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 ## Типы комнат: подпись, цвет, ширина в ячейках и баланс ресурсов за цикл. 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, 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 / CELL.y)) ) ## Прямоугольник всего убежища в координатах вида. static func bounds() -> Rect2: return Rect2(Vector2.ZERO, 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)), })