Game Clock Singleton

Clock is the deterministic game clock used by all gameplay code in place of performance.now() / Date.now(). It lives at engine/core/clock.ts and exposes a tiny surface: now(), uniqueNow(), advanceSimStep(), reset().

Surface

  • Clock.now(): number — current sim time in milliseconds since the last reset(). Deterministic: same inputs produce the same output. Used for cooldowns, weapon timers, spawner schedules, visual pulses, artifact timers, flame heat buildup, and telemetry timestamps that need to align with sim state.
  • Clock.uniqueNow(): number — sim time plus a monotonically-incrementing sub-counter (_subCounter * 0.001). Used by call sites that need a unique timestamp within the same sim step (e.g. crate IDs).
  • Clock.advanceSimStep(): void — advances _simTimeMs by 1000 / 60 (≈ 16.666 ms) and resets _subCounter to 0. Called only from loop.ts::stepSimulation(). There is no advance(dt) — the step size is fixed.
  • Clock.reset(): void — sets sim time and sub-counter back to 0. Called from loop.ts::startNewGame() and from bridge.ts::sandboxResetForTest so every test run begins with an identical clock.

Units

Milliseconds, matching performance.now()’s return type so call sites can be swapped 1:1 without math changes.

Determinism contract

Sim time advances only when the sim steps. Wall-clock time (performance.now(), Date.now()) is reserved for scheduler concerns: setTimeout delays, telemetry log timestamps for human reading, render-perf diagnostics. Game state — weapons, spawners, visual pulses, artifact timers, crate IDs — reads from Clock.now() so runs are byte-identical given the same input seed.

Distinct from game.time

Clock.now() is raw sim time. game.time (simulation time scaled by timeDilation) is the time-dilated view used by some gameplay systems that want to slow or speed up in-world time. The Clock is the substrate; time-dilated game time is derived from it.

Lifecycle

EventAction
New game startsClock.reset() (from loop.ts::startNewGame)
Sim tickClock.advanceSimStep() (from loop.ts::stepSimulation)
Sandbox test resetClock.reset() (from bridge.ts::sandboxResetForTest)