178 lines
5.2 KiB
GDScript
178 lines
5.2 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
|
|
|
|
## Типы комнат: подпись, цвет, ширина в ячейках и баланс ресурсов за цикл.
|
|
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)),
|
|
})
|