Timers and Scheduling
create_timer dwell, hourly clock, spawn cooldown, time_scale, avoiding await spaghetti.
On this page
CanvasLayer. Hours advance via create_timer; keys 1–4 change Engine.time_scale.Patterns we use
One-shot dwell
await get_tree().create_timer(_dwell_duration(step)).timeout
Guards after await: still AT_STATION, same schedule step, not in conversation.
Hourly clock loop
while _clock_running:
await get_tree().create_timer(seconds_per_hour).timeout
if not _clock_running:
return
advance_hour()
stop_hourly_clock() flips the flag; the loop exits on the next iteration.
Spawn cooldown with generation token
Cancel stale awaits when midnight resets arrivals:
func cancel_pending_spawn() -> void:
_spawn_generation += 1
func _wait_and_spawn() -> void:
var generation := _spawn_generation
await get_tree().create_timer(_spawn_cooldown_seconds).timeout
if generation != _spawn_generation:
return
_spawn_next()
Game time vs real time
seconds_per_hourmaps real seconds → one in-game hour.- Periods (
TIME_PERIODS) maphour→morning/ etc. for dialog. Engine.time_scale(keys 1–4) speeds timers and physics; player speed is compensated.
Avoid giant await chains
Prefer:
- State + timer callback / await with guards
- Signals (
checked_in,dialog_closed,midnight_reached)
Over:
await walk()
await talk()
await wait()
await walk()
# brittle when player interrupts
Interrupts (talk while walking) are why guests use flags + signals instead of one linear await script.
Schedule data
JSON steps: { "station": "BAR", "dwell_seconds": 20 }. Front desk waits on dialog, not dwell.