Step 3.1: add game clock conversion
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
241e2613f4
commit
f44789878e
|
|
@ -77,3 +77,8 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md`
|
|||
|
||||
- **Lookups return `undefined`** for an unknown id ("not found" without throwing). Callers decide whether a missing id is an error.
|
||||
- **The package also exports each data list and schema individually**, alongside the `content` bundle, so tests and the server can validate data with the schemas.
|
||||
|
||||
## Step 3.1
|
||||
|
||||
- **Times are Unix-millisecond numbers.** A real time is a wall-clock instant; a game time is the instant of the in-game date, so `new Date(gameTime)` gives the in-game date and can be passed straight to `astronomy-engine`.
|
||||
- **Every sim function takes the content it needs as arguments** (here the `GameConfig`), rather than importing the default bundle, so tests can pass custom fixtures.
|
||||
|
|
|
|||
|
|
@ -16,3 +16,24 @@
|
|||
- [x] **Step 2.4 — Cargo types and skills.** `src/data/cargo.ts`: Standard, Heavy, High-Value, Dangerous; skills Long Haul, Heavy Cargo, Expensive Cargo, Dangerous Cargo (max level 5). The location test now checks cargo ids against the real data.
|
||||
- [x] **Step 2.5 — Default game config.** `src/data/config.ts`. Courier + Earth bay ≈ 21 tutorial runs with the placeholder average payout. Money values rescaled ×10 so tutorial runs pay meaningful amounts; epoch moved from `.env.example` into GameConfig.
|
||||
- [x] **Step 2.6 — Single content bundle.** `content` in `src/bundle.ts` holds all data plus `location`, `shipModel`, `cargoType` and `skill` lookups that return `undefined` for unknown ids.
|
||||
|
||||
## Phase 3 — Simulation Package
|
||||
|
||||
- [x] **Step 3.1 — Game clock conversion.** `src/clock.ts`: `realToGame` / `gameToReal` using the config epoch and compression; times are Unix-ms numbers.
|
||||
- [ ] Step 3.2 — Seeded random number generator
|
||||
- [ ] Step 3.3 — Location positions
|
||||
- [ ] Step 3.4 — Route distance
|
||||
- [ ] Step 3.5 — Travel time
|
||||
- [ ] Step 3.6 — Fuel cost
|
||||
- [ ] Step 3.7 — Range and landing eligibility
|
||||
- [ ] Step 3.8 — Contract model and payout
|
||||
- [ ] Step 3.9 — Contract board generation
|
||||
- [ ] Step 3.10 — Tutorial contract board
|
||||
- [ ] Step 3.11 — Pilot eligibility
|
||||
- [ ] Step 3.12 — Dispatch validation
|
||||
- [ ] Step 3.13 — Dispatch record creation
|
||||
- [ ] Step 3.14 — Reposition dispatch
|
||||
- [ ] Step 3.15 — In-flight position and progress
|
||||
- [ ] Step 3.16 — XP and leveling
|
||||
- [ ] Step 3.17 — Dispatch resolution
|
||||
- [ ] Step 3.18 — Sim package public API and coverage
|
||||
|
|
|
|||
|
|
@ -7,5 +7,8 @@
|
|||
"types": "./src/index.ts",
|
||||
"scripts": {
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@deadhead/content": "workspace:*"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
import type { GameConfig } from "@deadhead/content";
|
||||
|
||||
/**
|
||||
* Times are milliseconds since the Unix epoch. A real time is a wall-clock
|
||||
* instant; a game time is the instant of the in-game (ephemeris) date, so
|
||||
* `new Date(gameTime)` is the in-game date.
|
||||
*/
|
||||
export type RealTime = number;
|
||||
export type GameTime = number;
|
||||
|
||||
export const MS_PER_MINUTE = 60_000;
|
||||
export const MS_PER_HOUR = 3_600_000;
|
||||
|
||||
/** Converts a real instant to the in-game instant it maps to. */
|
||||
export function realToGame(real: RealTime, config: GameConfig): GameTime {
|
||||
const realStart = Date.parse(config.epoch.realStart);
|
||||
const gameStart = Date.parse(config.epoch.ephemerisDate);
|
||||
return gameStart + (real - realStart) * config.timeCompression;
|
||||
}
|
||||
|
||||
/** Converts an in-game instant back to the real instant it maps to. */
|
||||
export function gameToReal(game: GameTime, config: GameConfig): RealTime {
|
||||
const realStart = Date.parse(config.epoch.realStart);
|
||||
const gameStart = Date.parse(config.epoch.ephemerisDate);
|
||||
return realStart + (game - gameStart) / config.timeCompression;
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
export {};
|
||||
export * from "./clock";
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
import { gameConfig } from "@deadhead/content";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { MS_PER_HOUR, MS_PER_MINUTE, gameToReal, realToGame } from "../src/index";
|
||||
|
||||
const realStart = Date.parse(gameConfig.epoch.realStart);
|
||||
const gameStart = Date.parse(gameConfig.epoch.ephemerisDate);
|
||||
|
||||
describe("game clock", () => {
|
||||
it("maps the real epoch to the game epoch", () => {
|
||||
expect(realToGame(realStart, gameConfig)).toBe(gameStart);
|
||||
});
|
||||
|
||||
it("round-trips real → game → real", () => {
|
||||
for (const real of [realStart, realStart + 12_345_678, Date.parse("2027-06-15T08:30:00Z")]) {
|
||||
expect(gameToReal(realToGame(real, gameConfig), gameConfig)).toBe(real);
|
||||
}
|
||||
});
|
||||
|
||||
it("advances one game hour per real minute at compression 60", () => {
|
||||
expect(gameConfig.timeCompression).toBe(60);
|
||||
expect(realToGame(realStart + MS_PER_MINUTE, gameConfig)).toBe(gameStart + MS_PER_HOUR);
|
||||
});
|
||||
});
|
||||
|
|
@ -48,7 +48,11 @@ importers:
|
|||
|
||||
packages/schema: {}
|
||||
|
||||
packages/sim: {}
|
||||
packages/sim:
|
||||
dependencies:
|
||||
'@deadhead/content':
|
||||
specifier: workspace:*
|
||||
version: link:../content
|
||||
|
||||
packages:
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue