Storyteller: restore sim speed after auto_pause event dismissal

The auto_pause flag on THREAT events paused Sim when the modal fired, but
resolve_current() never resumed it. Player dismisses the modal expecting play
to continue; sim stays paused; pawns appear stuck. (Surfaced by first real PC
playtest after the controls patch.)

Now: capture Sim.current_speed before paying the pause, restore it on
resolve if the player hasn't manually changed speed during the modal
(current_speed != PAUSE skips restore so the player's choice wins). Field
round-trips via save_dict for the save-during-modal edge case.

Verified MCP runtime: lone_wolf modal fires at boot, speed=0, dismissal
restores speed=1 and pawns immediately walk toward their next job.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-12 12:17:15 +01:00
parent c8c9fcbb33
commit 335ccf52b2

View file

@ -51,6 +51,11 @@ var _category_last_fired: Dictionary = {} # StringName → int
## Target day_index for the ghost-state wanderer auto-fire. -1 = not scheduled.
var _ghost_wanderer_target_day: int = -1
## Sim speed captured before an auto_pause event paused the sim. -1 when no
## restore is pending. Restored by resolve_current() so the player doesn't have
## to manually un-pause every threat modal dismissal.
var _speed_before_auto_pause: int = -1
func _ready() -> void:
Clock.phase_changed.connect(_on_phase_changed)
@ -90,6 +95,13 @@ func resolve_current(choice_index: int = 0) -> void:
_current_event.on_resolve.call(choice_index)
EventBus.storyteller_event_resolved.emit(_current_event, choice_index)
_current_event = null
# Restore the sim speed if this dismissal closes an auto_pause event AND
# the player hasn't manually changed speed during the modal. (If they did,
# current_speed won't be PAUSE and we leave their choice alone.)
if _speed_before_auto_pause >= 0:
if Sim.current_speed == Sim.Speed.PAUSE:
Sim.set_speed(_speed_before_auto_pause)
_speed_before_auto_pause = -1
# ── save / load ───────────────────────────────────────────────────────────────
@ -109,6 +121,7 @@ func save_dict() -> Dictionary:
"event_last_fired": event_fired_str,
"category_last_fired": cat_fired_str,
"ghost_wanderer_target_day": _ghost_wanderer_target_day,
"speed_before_auto_pause": _speed_before_auto_pause,
}
@ -117,6 +130,7 @@ func apply_dict(d: Dictionary) -> void:
ghost_state = d.get("ghost_state", false)
_last_rolled_day_index = int(d.get("last_rolled_day_index", -1))
_ghost_wanderer_target_day = int(d.get("ghost_wanderer_target_day", -1))
_speed_before_auto_pause = int(d.get("speed_before_auto_pause", -1))
# Restore StringName keyed dicts.
_event_last_fired.clear()
for k: String in d.get("event_last_fired", {}):
@ -246,8 +260,13 @@ func _fire(def: EventDef, focus_tile: Vector2i = Vector2i(-1, -1)) -> void:
tension = minf(100.0, tension + 15.0)
EventBus.storyteller_tension_changed.emit(tension)
# Auto-pause for modal events that require it.
# Auto-pause for modal events that require it. Capture the prior speed so
# resolve_current() can restore it on dismissal — without this the sim
# stays paused after every threat modal, which the player reads as "pawns
# stopped working for no reason."
if def.auto_pause:
if Sim.current_speed != Sim.Speed.PAUSE:
_speed_before_auto_pause = int(Sim.current_speed)
Sim.set_speed(Sim.Speed.PAUSE)
# For banners with no choices, resolve immediately after a short delay is