extends CanvasLayer ## Top-bar HUD: speed/pause buttons, tick counter, and save/load controls. ## ## Buttons call Sim.set_speed(); active button is yellow-tinted. ## Tick label updates on every EventBus.sim_tick signal. ## Keyboard shortcuts (pause / speed_normal / speed_fast / speed_ultra) are ## handled here so the bar is the single owner of speed-input logic. ## ## Phase 16: SaveBtn → SaveSystem.save_to_slot(&"manual"). ## LoadBtn → opens the LoadMenu CanvasLayer (mounted by main.gd). const ACTIVE_MODULATE := Color(1.2, 1.2, 0.8) const IDLE_MODULATE := Color.WHITE @onready var pause_btn : Button = $Anchor/ButtonRow/PauseBtn @onready var normal_btn : Button = $Anchor/ButtonRow/NormalBtn @onready var fast_btn : Button = $Anchor/ButtonRow/FastBtn @onready var ultra_btn : Button = $Anchor/ButtonRow/UltraBtn @onready var save_btn : Button = $Anchor/ButtonRow/SaveBtn @onready var load_btn : Button = $Anchor/ButtonRow/LoadBtn @onready var tick_label : Label = $Anchor/TickLabel @onready var clock_label : Label = $Anchor/ClockLabel @onready var season_label : Label = $Anchor/SeasonLabel # Maps Speed enum value → the corresponding Button node. var _speed_buttons: Dictionary = {} # Early-out cache: only set label text when the string changes. var _last_clock_text: String = "" var _last_season_text: String = "" ## Injected by main.gd after mount so we don't walk the tree with get_node. var load_menu: CanvasLayer = null func _ready() -> void: pause_btn.text = Strings.t(&"speed.pause") normal_btn.text = Strings.t(&"speed.normal") fast_btn.text = Strings.t(&"speed.fast") ultra_btn.text = Strings.t(&"speed.ultra") save_btn.text = Strings.t(&"ui.save") load_btn.text = Strings.t(&"ui.load") tick_label.text = "(boot)" clock_label.text = Strings.t(&"clock.format").format({"d": 1, "t": "06:00"}) season_label.text = Strings.t(&"season.format").format({"s": Strings.t(&"season.spring"), "d": 1}) _speed_buttons = { Sim.Speed.PAUSE: pause_btn, Sim.Speed.NORMAL: normal_btn, Sim.Speed.FAST: fast_btn, Sim.Speed.ULTRA: ultra_btn, } pause_btn.pressed.connect(func() -> void: Sim.set_speed(Sim.Speed.PAUSE)) normal_btn.pressed.connect(func() -> void: Sim.set_speed(Sim.Speed.NORMAL)) fast_btn.pressed.connect(func() -> void: Sim.set_speed(Sim.Speed.FAST)) ultra_btn.pressed.connect(func() -> void: Sim.set_speed(Sim.Speed.ULTRA)) save_btn.pressed.connect(_on_save_pressed) load_btn.pressed.connect(_on_load_pressed) EventBus.speed_changed.connect(_on_speed_changed) EventBus.sim_tick.connect(_on_sim_tick) EventBus.sim_tick.connect(_on_clock_refresh) EventBus.sim_tick.connect(_on_season_refresh) EventBus.save_started.connect(_on_save_started) EventBus.save_finished.connect(_on_save_finished) # Reflect the initial speed state without emitting a signal. _apply_highlight(Sim.current_speed) func _unhandled_input(event: InputEvent) -> void: if event.is_action_pressed("pause"): Sim.set_speed(Sim.Speed.PAUSE) elif event.is_action_pressed("speed_normal"): Sim.set_speed(Sim.Speed.NORMAL) elif event.is_action_pressed("speed_fast"): Sim.set_speed(Sim.Speed.FAST) elif event.is_action_pressed("speed_ultra"): Sim.set_speed(Sim.Speed.ULTRA) func _on_speed_changed(new_speed: int) -> void: _apply_highlight(new_speed as Sim.Speed) func _on_sim_tick(tick_number: int) -> void: tick_label.text = Strings.t(&"hud.tick").format({"n": tick_number}) func _on_clock_refresh(_n: int) -> void: var t: String = Strings.t(&"clock.format").format({"d": Clock.current_day(), "t": Clock.time_string()}) if t != _last_clock_text: _last_clock_text = t clock_label.text = t func _on_season_refresh(_n: int) -> void: var season_key: StringName = &"season." + String(Clock.current_season()) var t: String = Strings.t(&"season.format").format({ "s": Strings.t(season_key), "d": Clock.day_of_season() + 1, # display as 1-indexed }) if t != _last_season_text: _last_season_text = t season_label.text = t func _apply_highlight(speed: Sim.Speed) -> void: for s: int in _speed_buttons: _speed_buttons[s].modulate = ACTIVE_MODULATE if s == speed else IDLE_MODULATE func _on_save_pressed() -> void: SaveSystem.save_to_slot(&"manual") Audit.log("top_bar", "manual save triggered") func _on_load_pressed() -> void: if load_menu != null and load_menu.has_method("open"): load_menu.open() else: Audit.log("top_bar", "LoadMenu not mounted — skipping open()") func _on_save_started(_slot: StringName) -> void: save_btn.disabled = true save_btn.text = Strings.t(&"ui.saving") func _on_save_finished(_slot: StringName, _ok: bool) -> void: save_btn.disabled = false save_btn.text = Strings.t(&"ui.save")