58db53227c
- Install zustand@^5.0.0 + break_eternity.js@^2.1.3 as dependencies - BigQty immutable wrapper around Decimal (D-31): factories, arithmetic, comparison, JSON round-trip, saturating coercion - formatHumanReadable for K/M/B/T/scientific HUD readouts (UX-11) - Clock interface + wallClock + FakeClock — only file in src/sim/ allowed to read Date.now() (D-33) - drainTicks fixed-timestep accumulator: refuses negative deltas (CORE-11), clamps at MAX_OFFLINE_MS=24h (CORE-03), TICK_MS=200 - computeOfflineCatchup pure descriptor for offline boundaries - SimState root shape with BLOCKER 3 split: lastTickAt (wall-clock, app-layer-only) + tickCount (sim-internal monotonic) - 52 tests across big-qty / format / clock / tick / catchup all green
41 lines
1.5 KiB
TypeScript
41 lines
1.5 KiB
TypeScript
/**
|
|
* SimState — root shape of the in-memory sim world. Structurally
|
|
* compatible with V1Payload from src/save/migrations.ts (a SimState
|
|
* round-trips to a V1Payload via the application layer).
|
|
*
|
|
* Wave 0 ships placeholder unknown[] for tiles/plants — Wave 1 (Plan 02-02)
|
|
* fleshes them out with real interfaces in src/sim/garden/types.ts.
|
|
*
|
|
* BLOCKER 3 invariant — two distinct time fields with strict separation:
|
|
* - lastTickAt: wall-clock milliseconds. Written ONLY by the application
|
|
* layer at saveSync time (src/PhaserGame.tsx). The sim NEVER writes
|
|
* this field. computeOfflineCatchup reads it as wall-clock ms.
|
|
* - tickCount: monotonically-increasing sim-internal counter (one per
|
|
* simulate() call). Used for STRY-10 narrative gating that must be
|
|
* immune to wall-clock manipulation. The sim DOES write this field.
|
|
* The application layer reads it but never writes it.
|
|
*/
|
|
export interface SimState {
|
|
garden: { tiles: unknown[] };
|
|
plants: unknown[];
|
|
harvestedFragmentIds: string[];
|
|
/** Wall-clock milliseconds at last save. Written ONLY at saveSync. */
|
|
lastTickAt: number;
|
|
/** Monotonic sim tick counter. Incremented by the sim; used for STRY-10. */
|
|
tickCount: number;
|
|
unlockedPlantTypes: string[];
|
|
luraBeatProgress: {
|
|
arrived: boolean;
|
|
mid: boolean;
|
|
farewell: boolean;
|
|
pending: 'arrival' | 'mid' | 'farewell' | null;
|
|
};
|
|
offlineEvents: unknown | null;
|
|
settings: {
|
|
musicVolume: number;
|
|
ambientVolume: number;
|
|
sfxVolume: number;
|
|
persistenceToastShown: boolean;
|
|
};
|
|
}
|