import type { RealTime } from "@deadhead/sim"; /** The engine's only source of time (CR-014): injected, so tests use a fixed one. */ export interface Clock { now(): RealTime; } /** * The device clock (decision 0.5.88): device time plus a dev-only offset, * clamped so it never runs backward. Setting the device clock back leaves * the game at the last time it saw; setting it forward is accepted. */ export class DeviceClock implements Clock { #lastSeen: RealTime; #offsetMs: number; constructor( /** Device time; Date.now outside tests. */ private readonly deviceNow: () => RealTime = () => Date.now(), options: { lastTimeSeen?: RealTime; offsetMs?: number } = {}, ) { this.#lastSeen = options.lastTimeSeen ?? 0; this.#offsetMs = options.offsetMs ?? 0; } now(): RealTime { this.#lastSeen = Math.max(this.deviceNow() + this.#offsetMs, this.#lastSeen); return this.#lastSeen; } /** The latest time handed out, stored in the save (CR-014 ยง2). */ get lastTimeSeen(): RealTime { return this.#lastSeen; } /** After loading a save: never hand out a time before the one it last saw. */ restore(lastTimeSeen: RealTime): void { this.#lastSeen = Math.max(this.#lastSeen, lastTimeSeen); } get offsetMs(): number { return this.#offsetMs; } /** Developer tool: move the game forward (never back). */ advance(ms: number): void { if (ms < 0) throw new RangeError("The clock only moves forward"); this.#offsetMs += ms; } }