184 lines
5.7 KiB
GDScript
184 lines
5.7 KiB
GDScript
extends Node2D
|
|
## Игровой слой: убежище в разрезе, вид сбоку.
|
|
##
|
|
## Связывает три части: модель (VaultGrid), отрисовку (VaultView) и HUD.
|
|
## Здесь же живёт контракт сохранения — сцена лежит в группе "persistent".
|
|
##
|
|
## Управление: ЛКМ — построить или снести, ПКМ/СКМ — тащить вид,
|
|
## колесо — зум, WASD и стрелки — прокрутка.
|
|
|
|
## Строки сводки ресурсов: ключ из VaultGrid.totals() → подпись и цвет.
|
|
const STAT_ROWS := {
|
|
"power": {"label": "Питание", "color": "#f2b134"},
|
|
"water": {"label": "Вода", "color": "#4aa8f2"},
|
|
"food": {"label": "Еда", "color": "#f2704a"},
|
|
}
|
|
|
|
const HINT := "ЛКМ — строить · ПКМ — тащить вид · колесо — зум"
|
|
|
|
@onready var _saves: SaveManagerService = get_node("/root/SaveManager")
|
|
@onready var _vault: VaultView = $View
|
|
@onready var _camera: VaultCamera = $Camera
|
|
@onready var _stats: HBoxContainer = $Hud/Root/TopBar/Stats
|
|
@onready var _tools: HBoxContainer = $Hud/Root/BottomAnchor/Row/BuildBar/Panel/Tools
|
|
@onready var _status: Label = $Hud/Root/BottomAnchor/Row/BuildBar/Panel/Status
|
|
|
|
var _grid := VaultGrid.new()
|
|
var _selected_kind := "living"
|
|
var _demolish := false
|
|
var _stat_labels := {}
|
|
|
|
|
|
func _ready() -> void:
|
|
_grid.reset_to_starter()
|
|
_vault.grid = _grid
|
|
|
|
add_to_group(SaveManagerService.PERSIST_GROUP)
|
|
_saves.begin_session(scene_file_path)
|
|
|
|
_build_stats()
|
|
_build_tools()
|
|
_refresh_stats()
|
|
_status.text = HINT
|
|
|
|
|
|
func _process(_delta: float) -> void:
|
|
_vault.set_ghost(_selected_kind, _cell_under_mouse(), _demolish)
|
|
|
|
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
if not (event is InputEventMouseButton):
|
|
return
|
|
var button := event as InputEventMouseButton
|
|
if button.button_index != MOUSE_BUTTON_LEFT or not button.pressed:
|
|
return
|
|
_apply_at(_cell_under_mouse())
|
|
get_viewport().set_input_as_handled()
|
|
|
|
|
|
# --- Строительство ----------------------------------------------------------
|
|
|
|
func _cell_under_mouse() -> Vector2i:
|
|
return VaultGrid.cell_at(_vault.get_local_mouse_position())
|
|
|
|
|
|
func _apply_at(cell: Vector2i) -> void:
|
|
if _demolish:
|
|
if _grid.remove_at(cell.y, cell.x):
|
|
_after_change("Комната снесена.")
|
|
else:
|
|
_status.text = "Здесь нечего сносить."
|
|
return
|
|
|
|
if _grid.place(_selected_kind, cell.y, cell.x):
|
|
_after_change("Построено: %s" % VaultGrid.KINDS[_selected_kind]["label"])
|
|
else:
|
|
_status.text = "Здесь строить нельзя: занято или вплотную к шахте."
|
|
|
|
|
|
func _after_change(message: String) -> void:
|
|
_vault.queue_redraw()
|
|
_refresh_stats()
|
|
_status.text = message
|
|
|
|
|
|
# --- HUD --------------------------------------------------------------------
|
|
|
|
func _build_stats() -> void:
|
|
for key: String in STAT_ROWS:
|
|
var row := VBoxContainer.new()
|
|
row.add_theme_constant_override("separation", 0)
|
|
|
|
var caption := Label.new()
|
|
caption.text = String(STAT_ROWS[key]["label"])
|
|
caption.add_theme_font_size_override("font_size", 14)
|
|
caption.add_theme_color_override("font_color", Color("#8fa5ba"))
|
|
row.add_child(caption)
|
|
|
|
var value := Label.new()
|
|
value.add_theme_font_size_override("font_size", 24)
|
|
value.add_theme_color_override("font_color", Color(String(STAT_ROWS[key]["color"])))
|
|
row.add_child(value)
|
|
|
|
_stat_labels[key] = value
|
|
_stats.add_child(row)
|
|
|
|
|
|
func _refresh_stats() -> void:
|
|
var totals := _grid.totals()
|
|
for key: String in _stat_labels:
|
|
var amount := int(totals[key])
|
|
var label: Label = _stat_labels[key]
|
|
label.text = "%+d" % amount
|
|
# Дефицит важнее оформления: красим в тревожный цвет, а не в цвет ресурса.
|
|
if amount < 0:
|
|
label.add_theme_color_override("font_color", Color("#f2704a"))
|
|
else:
|
|
label.add_theme_color_override("font_color", Color(String(STAT_ROWS[key]["color"])))
|
|
|
|
|
|
func _build_tools() -> void:
|
|
var group := ButtonGroup.new()
|
|
|
|
for kind: String in VaultGrid.KINDS:
|
|
var button := Button.new()
|
|
button.text = String(VaultGrid.KINDS[kind]["label"])
|
|
button.toggle_mode = true
|
|
button.button_group = group
|
|
button.add_theme_font_size_override("font_size", 18)
|
|
button.button_pressed = kind == _selected_kind
|
|
button.pressed.connect(_on_kind_selected.bind(kind))
|
|
_tools.add_child(button)
|
|
|
|
var separator := Control.new()
|
|
separator.custom_minimum_size.x = 20
|
|
_tools.add_child(separator)
|
|
|
|
var demolish := Button.new()
|
|
demolish.text = "Снести"
|
|
demolish.toggle_mode = true
|
|
demolish.button_group = group
|
|
demolish.add_theme_font_size_override("font_size", 18)
|
|
demolish.pressed.connect(_on_demolish_selected)
|
|
_tools.add_child(demolish)
|
|
|
|
|
|
func _on_kind_selected(kind: String) -> void:
|
|
_selected_kind = kind
|
|
_demolish = false
|
|
_status.text = HINT
|
|
|
|
|
|
func _on_demolish_selected() -> void:
|
|
_demolish = true
|
|
_status.text = "Режим сноса: кликните по комнате."
|
|
|
|
|
|
# --- Сохранение -------------------------------------------------------------
|
|
|
|
func save_data() -> Dictionary:
|
|
return {
|
|
"rooms": _grid.to_array(),
|
|
"camera": {
|
|
"x": _camera.position.x,
|
|
"y": _camera.position.y,
|
|
"zoom": _camera.zoom.x,
|
|
},
|
|
}
|
|
|
|
|
|
func load_data(data: Dictionary) -> void:
|
|
_grid.from_array(data.get("rooms", []))
|
|
|
|
var camera_state: Dictionary = data.get("camera", {})
|
|
if not camera_state.is_empty():
|
|
_camera.position = Vector2(
|
|
float(camera_state.get("x", 0.0)), float(camera_state.get("y", 0.0))
|
|
)
|
|
var level := float(camera_state.get("zoom", 1.0))
|
|
_camera.zoom = Vector2(level, level)
|
|
|
|
_vault.queue_redraw()
|
|
_refresh_stats()
|
|
_status.text = "Убежище загружено."
|