54 lines
8.3 KiB
Markdown
54 lines
8.3 KiB
Markdown
# Progress
|
||
|
||
## Phase 1 — Repository Setup
|
||
|
||
- [x] **Step 1.1 — Create the monorepo.** pnpm workspace with `apps/web`, `apps/server`, `packages/sim`, `packages/schema`, `packages/content` (all `@deadhead/*`), strict `tsconfig.base.json` extended by each package. `pnpm install` + `pnpm typecheck` pass. Repo moved to `C:\Projects\Deadhead Space Freight Tycoon` (G: is exFAT, no symlinks).
|
||
- [x] **Step 1.2 — Linting and formatting.** ESLint 10 + typescript-eslint + Prettier at the root (`pnpm lint`, `pnpm format`). Purity rule bans `Math.random`, `Date.now` and argument-less `new Date()` in `packages/sim` and `packages/content`; verified it fails on a planted `Math.random()`.
|
||
- [x] **Step 1.3 — Test runner.** Vitest 5 with one project per workspace package (`pnpm test`). Trivial smoke test in `packages/sim/test/smoke.test.ts`; 1 passing.
|
||
- [x] **Step 1.4 — Continuous integration.** Repo pushed to Gitea; `.gitea/workflows/ci.yml` runs install, typecheck, lint and test on every push (runner `homelab-runner`). Run #1 failed because Prettier scanned the runner's in-workspace `.pnpm-store/`; ignored it, and run #2 is green.
|
||
- [x] **Step 1.5 — Local Postgres.** `docker compose up -d` starts Postgres 18 with `deadhead` (dev) and `deadhead_test` databases; connected to both with psql. `.env.example` lists the server's env vars.
|
||
|
||
## Phase 2 — Content Package
|
||
|
||
- [x] **Step 2.1 — Content schemas.** Zod 4 schemas in `packages/content/src/schemas.ts` for Location, ShipModel, CargoType, Skill and GameConfig, with inferred types. Tests check that each schema accepts a valid example and rejects it with any one field missing.
|
||
- [x] **Step 2.2 — The five locations.** `src/data/locations.ts`: Earth, Moon, Mars, Ceres Station, Jupiter Cloud Platform. Earth and Moon start discovered; Earth has the highest launch multiplier.
|
||
- [x] **Step 2.3 — The three ship models.** `src/data/ships.ts`: Courier, Freighter, Bulk Hauler, all atmospheric. Speed, capacity and price orderings tested; range rules are checked in Step 3.7.
|
||
- [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.
|
||
- [x] **Step 3.2 — Seeded RNG.** `src/rng.ts`: `createRng(seed)` (mulberry32) with `next`, `int`, `pick`; `deriveSeed(parent, label)` for child seeds.
|
||
- [x] **Step 3.3 — Location positions.** `src/positions.ts`: heliocentric ecliptic positions (million km) from `astronomy-engine` for Earth, Mars and Jupiter; Moon = Earth + fixed offset; Ceres on a circular orbit.
|
||
- [x] **Step 3.4 — Route distance.** `src/distance.ts`: `routeDistance(from, to, gameTime)`, straight-line million km.
|
||
- [x] **Step 3.5 — Travel time.** `src/travel.ts`: `travelTimeGameHours(ship, distance)` = cruise time + overhead.
|
||
- [x] **Step 3.6 — Fuel cost.** `src/fuel.ts`: `fuelCost(ship, origin, distance)` = distance × cost per million km × origin launch multiplier.
|
||
- [x] **Step 3.7 — Range and landing eligibility.** `src/eligibility.ts`: `checkRoute` returns `ok` or `cannot-land` / `out-of-range`; `canLandAt` ranks vacuum < atmospheric < hardened. Step 2.3 ranges confirmed against sampled real distances.
|
||
- [x] **Step 3.8 — Contract model and payout.** `src/contract.ts`: `Contract` interface and `contractPayout` (base × multiplier^tier × mass × distance × first-contact multiplier, rounded).
|
||
- [x] **Step 3.9 — Contract board generation.** `src/board.ts`: `generateContractBoard` (seeded per world, bay and period), plus `boardPeriodIndex`, `requiredLongHaulLevel` and the `SimContent` input type. First contact ≈10% over 1,000 boards.
|
||
- [x] **Step 3.10 — Tutorial contract board.** `src/tutorial.ts`: `generateTutorialBoard` (Earth↔Moon from the loaner's location, Standard cargo capped at Courier capacity, payout × tutorial share); `loanerModel`. `SimContent` gained `shipModels`.
|
||
- [x] **Step 3.11 — Pilot eligibility.** `src/pilot.ts`: `checkPilotEligibility(skills, contract)` returns `ok` or `cargo-skill-too-low` / `long-haul-too-low`.
|
||
- [x] **Step 3.12 — Dispatch validation.** `src/dispatch.ts`: `ShipState`, `PilotState`, `validateDispatch` (first failing reason, plan order), `tripFuelCost` (rounded). One test per failure reason plus tutorial cases.
|
||
- [x] **Step 3.13 — Dispatch record creation.** `createDispatchRecord` in `src/dispatch.ts`: `DispatchRecord` with arrival from travel time, fuel cost, departure distance, a `tutorial` flag and a per-dispatch seed.
|
||
- [x] **Step 3.14 — Reposition dispatch.** `validateReposition` and `createRepositionRecord` in `src/dispatch.ts`: empty flight to a player bay, range/landing/fuel only, zero payout. Zero XP is covered by resolution (Step 3.17).
|
||
- [x] **Step 3.15 — In-flight position and progress.** `src/flight.ts`: `dispatchProgress` (clamped 0–1) and `dispatchPosition` (straight line from origin at departure to destination at arrival).
|
||
- [x] **Step 3.16 — XP and leveling.** `src/xp.ts`: `deliveryXp`, `pilotLevel`, `skillPointsAvailable`.
|
||
- [x] **Step 3.17 — Dispatch resolution.** `src/resolution.ts`: `resolveDispatch` returns `in-flight` or a `Resolution` (credits after pilot pay, XP, new ship location, discovery).
|
||
- [x] **Step 3.18 — Public API and coverage.** `src/index.ts` exports an explicit list (content lookups and contract building stay internal). `pnpm coverage` (V8, `packages/sim/src`, 90% line threshold) reports 100% lines; CI now runs `pnpm coverage` instead of `pnpm test`.
|
||
|
||
## Phase 4 — Shared Schema Package
|
||
|
||
- [x] **Step 4.1 — API data shapes.** `packages/schema/src`: entity schemas (`entities.ts`), error format and codes (`errors.ts`), request/response bodies (`requests.ts`). Error codes include every sim validation reason; sim now exports `DISPATCH_FAILURES` / `REPOSITION_FAILURES`. `architecture.md` started with the API data model.
|
||
- [x] **Step 4.2 — API route catalog.** `packages/schema/src/routes.ts`: 13 typed endpoints under `/api` with request/response schemas and an `auth` flag, plus `RouteRequest` / `RouteResponse` helper types. Route table added to `architecture.md`.
|
||
|
||
## Phase 5 — Server Foundation
|
||
|
||
- [x] **Step 5.1 — Fastify skeleton.** `apps/server`: Zod-validated env config (`src/config.ts`), `buildApp` with pino JSON logging and `GET /api/health` (`src/app.ts`), entry point `src/main.ts` (loads repo-root `.env` if present). `pnpm --filter @deadhead/server dev|start` run it with tsx. Integration test starts the app on an ephemeral port and calls health over HTTP.
|
||
- [x] **Step 5.2 — Server time endpoint.** `GET /api/time` returns `{ realTime, gameTime }` from an injectable `Clock` (`src/clock.ts`, passed via `buildApp(config, { clock })`).
|
||
- [x] **Step 5.3 — Database schema and migrations.** Drizzle schema (`apps/server/src/db/schema.ts`, copied into `architecture.md`), first migration `drizzle/0000_init.sql`, `db:generate` / `db:migrate` scripts. Migration test runs against an emptied test database twice. CI now has a Postgres 18 service; server tests run one file at a time.
|
||
- [x] **Step 5.4 — Test database harness.** `apps/server/test/db.ts`: `useTestDatabase()` (migrate once, truncate all tables before each test) and `seedPlayer(db, "tutorial" | "owner")`. New-player creation lives in `src/game/new-player.ts` (`createPlayer`), shared with guest sign-up.
|
||
- [x] **Step 5.5 — Guest accounts.** Better Auth with the anonymous plugin (`src/auth.ts`, tables in `src/db/auth-schema.ts`, migration `0001_auth`). `POST /api/guest` creates a guest session and a tutorial player; `GET /api/state` returns player state for the session or `401 unauthorized`. Error handler sends the documented error format.
|
||
- [ ] Step 5.6 — Request validation from the route catalog
|