From 6981c7e9bc732929671ff5e5675ca7be140d98fd Mon Sep 17 00:00:00 2001 From: Rijad Zuzo Date: Thu, 9 Jul 2026 00:25:57 +0200 Subject: [PATCH] feat: simulation speed control with bracket keys Press [ to slow down, ] to speed up through 6 levels: 0.25x, 0.5x, 1x, 2x, 4x, 10x. Clock delta is multiplied by the current speed, so time_of_day advances proportionally and the visual day-night cycle stays synced. Current speed shown in the village panel header. 10x makes a full day cycle watchable in ~24 seconds. Speed changes are non-destructive to simulation determinism. --- simulation/SimulationManager.gd | 24 +++++++++++++++++++++++- world/ui/ui.gd | 13 ++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/simulation/SimulationManager.gd b/simulation/SimulationManager.gd index a560702..7023f03 100644 --- a/simulation/SimulationManager.gd +++ b/simulation/SimulationManager.gd @@ -31,6 +31,9 @@ var latest_decisions: Dictionary = {} var action_selector := ActionSelectionSystem.new() var action_executor := ActionExecutionSystem.new() var target_resolver := ActionTargetResolver.new() +var speed_index := 2 +const SPEED_LEVELS := [0.25, 0.5, 1.0, 2.0, 4.0, 10.0] +signal speed_changed(multiplier: float) var names := ["Amina", "Tarik", "Jasmin", "Elma", "Mirza", "Lejla"] @@ -58,11 +61,26 @@ func _ready() -> void: func _process(delta: float) -> void: - var ticks_due := clock.advance(delta) + var ticks_due := clock.advance(delta * SPEED_LEVELS[speed_index]) for tick in ticks_due: simulate_tick() +func _input(event: InputEvent) -> void: + if not event is InputEventKey or not event.pressed or event.echo: + return + if event.keycode == KEY_BRACKETLEFT: + speed_index = maxi(speed_index - 1, 0) + speed_changed.emit(SPEED_LEVELS[speed_index]) + if debug_logs: + print("[SimulationManager] Speed: %.2fx" % SPEED_LEVELS[speed_index]) + elif event.keycode == KEY_BRACKETRIGHT: + speed_index = mini(speed_index + 1, SPEED_LEVELS.size() - 1) + speed_changed.emit(SPEED_LEVELS[speed_index]) + if debug_logs: + print("[SimulationManager] Speed: %.2fx" % SPEED_LEVELS[speed_index]) + + func generate_npcs() -> void: var profession_ids := SimulationDefinitions.get_profession_ids() for i in range(names.size()): @@ -568,6 +586,10 @@ func get_recent_events(max_count: int = 5) -> Array[EconomicEventRecord]: return results +func get_current_speed() -> String: + return "%.2fx" % SPEED_LEVELS[speed_index] + + func _initialize_storage() -> void: if not storage_states.has(SimulationIds.STORAGE_VILLAGE_PANTRY): storage_states[SimulationIds.STORAGE_VILLAGE_PANTRY] = (StorageStateRecord.create( diff --git a/world/ui/ui.gd b/world/ui/ui.gd index 62a9f14..6ecc9ed 100644 --- a/world/ui/ui.gd +++ b/world/ui/ui.gd @@ -27,6 +27,8 @@ func _ready() -> void: push_error("VillageUI: SimulationManager has no npc_decision_recorded signal") if simulation_manager.has_signal("state_restored"): simulation_manager.state_restored.connect(_on_state_restored) + if simulation_manager.has_signal("speed_changed"): + simulation_manager.speed_changed.connect(_on_speed_changed) if "village" in simulation_manager: _on_village_changed(simulation_manager.village) @@ -63,9 +65,12 @@ func _on_village_changed(village: SimVillage) -> void: event_text = "" else: event_text = "\n" + event_text + var speed_text := "" + if simulation_manager.has_method("get_current_speed"): + speed_text = simulation_manager.get_current_speed() village_stats_label.text = ( """ - Village + Village [%s] Food: %s Wood: %s @@ -79,6 +84,7 @@ func _on_village_changed(village: SimVillage) -> void: %s """ % [ + speed_text, round(village.food), round(village.wood), round(village.safety), @@ -101,6 +107,11 @@ func _on_state_restored() -> void: _refresh_npc_inspector() +func _on_speed_changed(_multiplier: float) -> void: + if simulation_manager != null and "village" in simulation_manager: + _on_village_changed(simulation_manager.village) + + func _refresh_npc_inspector() -> void: if not debug_overlay_visible: return