Files
g-world/scripts/game/vault_grid.gd
T

182 lines
5.6 KiB
GDScript

class_name VaultGrid
extends RefCounted
## Модель убежища: сетка этажей и комнаты на ней.
##
## Здесь нет ни узлов, ни отрисовки — только данные и правила размещения.
## Вид (vault_view.gd) читает эту модель, а сохранение сериализует её целиком.
## Размер одной ячейки сетки в пикселях мира.
const CELL := Vector2(140.0, 120.0)
## Колонка 0 — лифтовая шахта, комнаты занимают 1..COLUMNS-1.
const COLUMNS := 9
const FLOORS := 6
const ELEVATOR_COLUMN := 0
## Насколько верхний этаж утоплен под поверхность. y = 0 — уровень земли,
## выше — небо. Смещение живёт здесь, а не в отрисовке, чтобы вместе с
## картинкой уехали и попадание мышью, и границы камеры.
const DEPTH := 360.0
## Типы комнат: подпись, цвет, ширина в ячейках и баланс ресурсов за цикл.
const KINDS := {
"living": {
"label": "Жилой блок", "color": "#2ee6c5", "width": 2,
"power": -2, "water": 0, "food": 0,
},
"power": {
"label": "Генератор", "color": "#f2b134", "width": 2,
"power": 8, "water": 0, "food": 0,
},
"water": {
"label": "Водоочистка", "color": "#4aa8f2", "width": 2,
"power": -3, "water": 6, "food": 0,
},
"diner": {
"label": "Столовая", "color": "#f2704a", "width": 2,
"power": -3, "water": 0, "food": 6,
},
"storage": {
"label": "Склад", "color": "#9b8cf5", "width": 1,
"power": -1, "water": 0, "food": 0,
},
}
## Комнаты: [{"kind": String, "floor": int, "column": int}, ...]
var rooms: Array[Dictionary] = []
## Стартовая застройка: без неё убежище выглядит пустой ямой.
func reset_to_starter() -> void:
rooms.clear()
place("living", 0, 1)
place("power", 0, 3)
place("diner", 1, 1)
place("water", 1, 3)
# --- Правила размещения -----------------------------------------------------
static func kind_width(kind: String) -> int:
return int(KINDS[kind]["width"])
func in_bounds(a_floor: int, column: int, width: int) -> bool:
if a_floor < 0 or a_floor >= FLOORS:
return false
if column <= ELEVATOR_COLUMN:
return false
return column + width <= COLUMNS
func can_place(kind: String, a_floor: int, column: int) -> bool:
if not KINDS.has(kind):
return false
var width := kind_width(kind)
if not in_bounds(a_floor, column, width):
return false
for offset in width:
if room_index_at(a_floor, column + offset) >= 0:
return false
return true
func place(kind: String, a_floor: int, column: int) -> bool:
if not can_place(kind, a_floor, column):
return false
rooms.append({"kind": kind, "floor": a_floor, "column": column})
return true
## Индекс комнаты, накрывающей ячейку, или -1.
func room_index_at(a_floor: int, column: int) -> int:
for index in rooms.size():
var room: Dictionary = rooms[index]
if int(room["floor"]) != a_floor:
continue
var start := int(room["column"])
if column >= start and column < start + kind_width(String(room["kind"])):
return index
return -1
func remove_at(a_floor: int, column: int) -> bool:
var index := room_index_at(a_floor, column)
if index < 0:
return false
rooms.remove_at(index)
return true
# --- Геометрия --------------------------------------------------------------
static func cell_rect(a_floor: int, column: int, width: int = 1) -> Rect2:
return Rect2(
Vector2(column * CELL.x, DEPTH + a_floor * CELL.y),
Vector2(CELL.x * width, CELL.y)
)
static func room_rect(room: Dictionary) -> Rect2:
return cell_rect(int(room["floor"]), int(room["column"]), kind_width(String(room["kind"])))
## Ячейка под точкой в координатах вида. Может оказаться за пределами сетки —
## проверяйте in_bounds().
static func cell_at(local_position: Vector2) -> Vector2i:
return Vector2i(
int(floor(local_position.x / CELL.x)),
int(floor((local_position.y - DEPTH) / CELL.y))
)
## Прямоугольник всего убежища в координатах вида.
static func bounds() -> Rect2:
return Rect2(Vector2(0.0, DEPTH), Vector2(COLUMNS * CELL.x, FLOORS * CELL.y))
# --- Сводка по ресурсам -----------------------------------------------------
func totals() -> Dictionary:
var result := {"power": 0, "water": 0, "food": 0}
for room: Dictionary in rooms:
var kind: Dictionary = KINDS[String(room["kind"])]
for key: String in result:
result[key] = int(result[key]) + int(kind[key])
return result
func count_of(kind: String) -> int:
var total := 0
for room: Dictionary in rooms:
if String(room["kind"]) == kind:
total += 1
return total
# --- Сериализация -----------------------------------------------------------
func to_array() -> Array:
var data := []
for room: Dictionary in rooms:
data.append({
"kind": String(room["kind"]),
"floor": int(room["floor"]),
"column": int(room["column"]),
})
return data
func from_array(data: Array) -> void:
rooms.clear()
for entry: Variant in data:
if not (entry is Dictionary):
continue
var room: Dictionary = entry
var kind := String(room.get("kind", ""))
if not KINDS.has(kind):
continue
rooms.append({
"kind": kind,
"floor": int(room.get("floor", 0)),
"column": int(room.get("column", 1)),
})