class_name SimulationClock extends RefCounted var tick_interval: float var accumulator := 0.0 var elapsed_ticks := 0 var cycle_duration_seconds := 240.0 func _init(interval: float = 1.0) -> void: tick_interval = maxf(interval, 0.0001) func advance(delta: float, max_ticks: int = 0) -> int: accumulator += maxf(delta, 0.0) var ticks_due := 0 while accumulator >= tick_interval and (max_ticks <= 0 or ticks_due < max_ticks): accumulator -= tick_interval elapsed_ticks += 1 ticks_due += 1 return ticks_due func reset() -> void: accumulator = 0.0 elapsed_ticks = 0 func time_of_day() -> float: # A capped frame can leave several whole ticks in the accumulator. Those ticks # are backlog, not simulated time yet; only retain the sub-tick fraction for # smooth presentation until the manager consumes the remaining work. var fractional_progress := fmod(accumulator, tick_interval) var total_seconds := elapsed_ticks * tick_interval + fractional_progress return fmod(total_seconds / maxf(cycle_duration_seconds, 1.0), 1.0)