diff --git a/DECISIONS.md b/DECISIONS.md index 75a7d80..badc608 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -611,3 +611,9 @@ Implementation decisions not spelled out in `memory-bank/implementation-plan.md` - **The first contract of each band is a guaranteed job:** Standard cargo (no skill) to an already-discovered location, never first contact. A new pilot has no skill points (tutorial XP does not reach level 1), so without this a Courier-sized job could still need a skill. The other slots are drawn as before. First contact now applies to 5 of 8 slots, about 6% of contracts, inside the 5–15% the Step 3.9 test expects. - **Boards list contracts band by band** (small to large), so a new player sees Courier-sized work first. - **Existing stored boards are unchanged** until their period ends (at most an hour); new boards use the bands. + +## CR-002 — Open Markets, Bays as HQs (Phase 10B) + +- **Change request CR-002 is stored in `memory-bank/change-requests/`** and inserted as Phase 10B, before Phase 11. +- **Decision numbering:** the CR's new 0.5.27 (Ship delivery) and 0.5.28 (Undiscovered locations) are recorded as **0.5.35** and **0.5.36**, since 27–34 were already in use. Decision 0.5.5 is replaced and the glossary gains "Contract board" and "Deadhead". +- **Error code:** the CR's `location_undiscovered` is the existing `location-undiscovered`. diff --git a/apps/web/public/assets/map/bg.png b/apps/web/public/assets/map/bg.png new file mode 100644 index 0000000..2aaea66 Binary files /dev/null and b/apps/web/public/assets/map/bg.png differ diff --git a/apps/web/public/assets/map/body-ceres.png b/apps/web/public/assets/map/body-ceres.png new file mode 100644 index 0000000..0bd1a3b Binary files /dev/null and b/apps/web/public/assets/map/body-ceres.png differ diff --git a/apps/web/public/assets/map/body-earth.png b/apps/web/public/assets/map/body-earth.png new file mode 100644 index 0000000..46a93ec Binary files /dev/null and b/apps/web/public/assets/map/body-earth.png differ diff --git a/apps/web/public/assets/map/body-jupiter.png b/apps/web/public/assets/map/body-jupiter.png new file mode 100644 index 0000000..2933fbd Binary files /dev/null and b/apps/web/public/assets/map/body-jupiter.png differ diff --git a/apps/web/public/assets/map/body-mars.png b/apps/web/public/assets/map/body-mars.png new file mode 100644 index 0000000..c50c16f Binary files /dev/null and b/apps/web/public/assets/map/body-mars.png differ diff --git a/apps/web/public/assets/map/body-moon.png b/apps/web/public/assets/map/body-moon.png new file mode 100644 index 0000000..c38dae5 Binary files /dev/null and b/apps/web/public/assets/map/body-moon.png differ diff --git a/apps/web/public/assets/map/body-sun.png b/apps/web/public/assets/map/body-sun.png new file mode 100644 index 0000000..115bd91 Binary files /dev/null and b/apps/web/public/assets/map/body-sun.png differ diff --git a/apps/web/public/assets/map/ship-bulk-hauler.png b/apps/web/public/assets/map/ship-bulk-hauler.png new file mode 100644 index 0000000..d2c07b3 Binary files /dev/null and b/apps/web/public/assets/map/ship-bulk-hauler.png differ diff --git a/apps/web/public/assets/map/ship-company-courier.png b/apps/web/public/assets/map/ship-company-courier.png new file mode 100644 index 0000000..de3a138 Binary files /dev/null and b/apps/web/public/assets/map/ship-company-courier.png differ diff --git a/apps/web/public/assets/map/ship-courier.png b/apps/web/public/assets/map/ship-courier.png new file mode 100644 index 0000000..be141ad Binary files /dev/null and b/apps/web/public/assets/map/ship-courier.png differ diff --git a/apps/web/public/assets/map/ship-freighter.png b/apps/web/public/assets/map/ship-freighter.png new file mode 100644 index 0000000..eca0daf Binary files /dev/null and b/apps/web/public/assets/map/ship-freighter.png differ diff --git a/memory-bank/change-requests/CR-002-open-markets.md b/memory-bank/change-requests/CR-002-open-markets.md new file mode 100644 index 0000000..843f638 --- /dev/null +++ b/memory-bank/change-requests/CR-002-open-markets.md @@ -0,0 +1,126 @@ +# Change Request CR-002 — Open Markets, Bays as HQs + +**Project:** Deadhead: Space Freight Tycoon +**Applies to:** a build with Phases 1–10 and CR-001 (Faster Start) complete +**Do this:** as **Phase 10B**, after Phase 10A and before Phase 11 +**Related docs:** `implementation-plan.md`, `change-request-faster-start.md`, `game-design-document.md`, `DECISIONS.md` + +> **Numbering note (added when implemented):** the plan's §0.5 already used 27 (pilot location storage), 28 (Git hosting) and 29–34 (CR-001). The new decisions below numbered 0.5.27 and 0.5.28 are recorded as **0.5.35 Ship delivery** and **0.5.36 Undiscovered locations**. The error code `location_undiscovered` is the existing `location-undiscovered`. + +--- + +## 1. Why + +Contracts are currently only offered at locations where the player has a bay, and repositioning is only allowed to and from bay locations. A ship that delivers to a location without a bay (for example the Moon) is stranded: there is no contract board there, and it cannot reposition home. + +The fix follows the American/Euro Truck Simulator model: + +- **Every discovered location has a contract board (the freight market).** Any idle ship can pick up work wherever it is, which makes backhaul work. +- **Bays are HQs (like ATS garages).** You need a bay to hire pilots there, to take delivery of newly bought ships there, and to unassign pilots there. Bays no longer control where contracts are available. +- **Discovery still gates expansion.** An undiscovered location has no board until the player delivers there. + +## 2. Rules for this change + +- All rules in `implementation-plan.md` §0.1 still apply: steps in order, one commit per step, each step's test must pass, `packages/sim` stays pure, all numbers live in `packages/content`. +- **Do not write data migrations for old contract boards.** Resetting the dev and test databases is fine. Schema migrations are still required. +- Update existing tests whose expectations change. Do not delete a test unless §5 says the step is replaced. +- Update `architecture.md` for every schema or API change and `progress.md` after every step. +- Stop for the user's review at the end of Phase 10B. + +## 3. Decision changes + +Record these in `DECISIONS.md` and in `implementation-plan.md` §0.4 and §0.5. + +**Glossary changes (§0.4)** +- **Bay** → A docking bay the player leases at a discovered location. It is an HQ: pilots are hired there, newly bought ships are delivered there, and pilots can be unassigned there. A bay does **not** control where contracts are offered. +- **Contract board** (new) → The freight market at a discovered location. Every discovered location has one per player per board period, whether or not the player has a bay there. +- **Deadhead** (new) → A reposition: a ship flying empty to another location. Fuel only, no payout, no XP. + +**Replaced** +- **0.5.5 (Contract boards are stored)** → A board exists for every **discovered location**, per player, per period. It is generated on the first request in a period and stored; later requests in that period return the stored board. Every contract expires at the end of its board period. +- **Step 3.14 rule (reposition only to a bay)** → An idle ship can deadhead from **any** location to **any discovered** location other than its current one, subject to the normal range and landing checks. + +**New** +- **0.5.27 Ship delivery.** Buying a ship requires choosing one of the player's bays. The new ship appears there, idle and uncrewed. +- **0.5.28 Undiscovered locations.** Requesting the board of an undiscovered location fails with a `location_undiscovered` error. Deadheading to an undiscovered location is not allowed; only a contract delivery discovers a location. + +**Unchanged (confirm, don't modify)** +- **0.5.2 Pilot locations.** Hiring happens at a player bay; unassigning is allowed only at a location with a player bay. +- **CR-001 deal.** The deal still includes the Earth bay. + +## 4. Steps — Phase 10B + +### Step 10B.1 — Sim: boards at any location +- Change the contract board generator's input from "the bay's location" to "the board's location". No other generation rules change. +- Rename any sim types or functions that say "bay board" to "location board". +- **Test:** The Step 3.9 tests still pass with a location input. Generating a board for the Moon with no bay data supplied anywhere works and is deterministic. + +### Step 10B.2 — Sim: deadhead validation +- Reposition validation allows any origin. The destination must be discovered, must differ from the origin, and must pass the range and landing checks. The bay requirement is removed. +- Update the Step 3.14 tests. +- **Test:** A Courier idle at the Moon with no Moon bay can deadhead to Earth. Deadheading to an undiscovered location fails with `location_undiscovered`. Deadheading to the ship's own location fails. Deadheading out of range fails with the out-of-range reason. A deadhead record still has zero payout and zero XP. + +### Step 10B.3 — Database: boards keyed by location +- Change `contract_boards` to be keyed by player, location, and board period (the tutorial board keeps its own key). +- Update `architecture.md`. +- **Test:** Migrations run on a fresh database and are a no-op the second time. Two boards for the same player and period at different locations can coexist; a second board for the same player, location, and period is rejected by a unique constraint. + +### Step 10B.4 — Server: board endpoint for any discovered location +- Replace "get contract board for a bay" with "get contract board for a location" in the route catalog and server. Any discovered location works; an undiscovered location returns `location_undiscovered`. +- Update the Step 6.9 tests. +- **Test:** The Moon's board is returned for a player with no Moon bay. Two requests in the same period return the same board. Requesting Mars's board before discovery fails with `location_undiscovered`; after a delivery discovers Mars, it succeeds. + +### Step 10B.5 — Server: no more stranded ships +- Contract dispatch works from any discovered location where the ship is idle (it already validates that the ship is at the contract's origin; confirm no bay check remains). +- Deadhead dispatch uses the Step 10B.2 rules. +- Update the Step 6.10 and 6.11 tests. +- **Test (the original bug):** A ship delivers Earth→Moon with no Moon bay. After arrival, the Moon's board is available, a Moon→Earth contract can be dispatched with that ship, and it resolves normally. Separately, the same ship can instead deadhead Moon→Earth. + +### Step 10B.6 — Server: ship delivery to a bay +- Buying a ship takes a bay id. The ship appears idle and uncrewed at that bay's location. Buying without a bay, or with a bay the player doesn't own, fails. +- Update the Step 6.5 tests. +- **Test:** With bays at Earth and the Moon, buying a Freighter for the Moon bay places it at the Moon. Buying with no bay id fails with a validation error. Buying with another player's bay id fails and changes nothing. + +### Step 10B.7 — Client: Contracts screen by location +- The Contracts screen's selector lists all discovered locations instead of bays. Each entry shows a badge with the number of idle ships there. +- Default selection: the location of the most recently arrived idle ship; otherwise Earth. +- Fleet screen: each idle ship gets a "Find work here" action that opens its location's board. +- Update the Step 9.4 tests. +- **Test:** Component test: the selector lists every discovered location and no undiscovered ones; idle-ship badges match state; the default is the most recently arrived idle ship's location; "Find work here" opens the right board. + +### Step 10B.8 — Client: deadhead action +- Rename the Fleet screen's "Reposition" action to "Deadhead (fly empty)". It is available for every idle ship. The destination picker lists discovered locations; ones out of range or not landable are shown disabled with the reason. Show fuel cost and trip time for each. +- Update the Step 9.6 tests. +- **Test:** Component test: an idle ship at the Moon with no Moon bay shows the action; the picker excludes undiscovered locations and the ship's current location; an out-of-range destination is disabled with its reason; fuel and time match the sim. + +### Step 10B.9 — Client: bays as HQs +- Market screen: "Buy" opens a bay picker ("Deliver to which HQ?"). With only one bay, preselect it. +- Locations screen and map panels: every discovered location has a "View contracts" link. The "Lease bay" action explains what a bay does: "Open an HQ here: hire pilots and take delivery of new ships at this location." +- Update the Step 9.8, 9.9, and 10.3 tests. +- **Test:** Component test: Buy shows the bay picker and sends the chosen bay id; with one bay it is preselected. Every discovered location shows "View contracts", with or without a bay. The lease action shows the HQ explanation. + +### Step 10B.10 — Plan amendments and regression +- Apply the amendments in §5 to `implementation-plan.md`, and update `game-design-document.md` §4 and §10 to match §1 of this document. +- Run the full suite. +- **Test:** CI is green. Manual check: on a fresh account after the CR-001 deal, deliver to the Moon, pick up a Moon→Earth contract with the same ship, and deliver it back to Earth without leasing a Moon bay. + +## 5. Amendments to `implementation-plan.md` + +| Step | Change | +|---|---| +| 0.4, 0.5 | Apply §3 of this document. | +| 3.9 | Input is a location, not a bay (10B.1). | +| 3.14 | Deadhead rules replace reposition-to-bay (10B.2). | +| 5.3 | `contract_boards` keyed by player, location, and period (10B.3). | +| 6.5 | Ship purchase takes a bay id (10B.6). | +| 6.9 | Boards for any discovered location (10B.4). | +| 6.10, 6.11 | No bay checks on dispatch; deadhead rules (10B.5). | +| 9.4 | Selector lists discovered locations (10B.7). | +| 9.6 | "Deadhead (fly empty)" for every idle ship (10B.8). | +| 9.8, 9.9, 10.3 | Bay picker on purchase; "View contracts" everywhere; HQ explanation (10B.9). | +| 12.1 | The simulated player picks up contracts at whatever location its ships are at, and only deadheads when no eligible contract exists there. Add a check: across the whole run, no ship is ever idle with no eligible contract **and** no reachable deadhead destination. | +| 12.5 | After Mars is discovered, also confirm Mars's contract board is available without a Mars bay. | + +## 6. Out of scope + +Garage-style bay capacity or slot limits, bay upgrades, recurring bay rent, pilots that pick their own jobs from their home bay (ATS-style AI drivers), and anything else in `implementation-plan.md` §0.3. diff --git a/memory-bank/implementation-plan.md b/memory-bank/implementation-plan.md index 6206246..5859093 100644 --- a/memory-bank/implementation-plan.md +++ b/memory-bank/implementation-plan.md @@ -42,7 +42,9 @@ Random events, reputation, fuel price variation, throttle setting, multi-leg rou - **Location** — a place a ship can land (planet, moon, station, platform). - **Discovered** — a location the player has delivered to at least once (Earth and Moon start discovered). -- **Bay** — a docking bay the player rents at a discovered location. Contracts are offered only at bays (plus the tutorial board). +- **Bay** — a docking bay the player leases at a discovered location. It is an HQ: pilots are hired there, newly bought ships are delivered there, and pilots can be unassigned there. A bay does **not** control where contracts are offered. *(Changed by CR-002.)* +- **Contract board** — the freight market at a discovered location. Every discovered location has one per player per board period, whether or not the player has a bay there. *(CR-002.)* +- **Deadhead** — a reposition: a ship flying empty to another location. Fuel only, no payout, no XP. *(CR-002.)* - **Contract** — a job: cargo of a type and mass, from origin to destination, with a payout and skill requirements. - **Dispatch** — a ship + pilot sent on a trip, either carrying a contract or repositioning empty. - **Game time** — the in-world clock, derived from real time × compression factor. @@ -58,7 +60,7 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple 2. **Pilot locations.** An assigned pilot is wherever their ship is. An unassigned pilot stays where they were unassigned. Pilots travel only by flying a ship. Unassigning is allowed only at a location with a player bay. New hires appear at a player bay the player chooses. 3. **Tutorial movement.** The avatar and loaner move with each tutorial run. The tutorial board starts at the loaner's current location (Earth→Moon or Moon→Earth). The company pays fuel and pilot pay; the player receives only the tutorial share. *(Amended by CR-001: the board has three contracts of different masses (0.5.31), and the loaner is a dedicated content ship (0.5.29).)* 4. **Tutorial end.** *(Replaced by CR-001.)* The tutorial ends when the player accepts the deal (0.5.30). Buying a ship and leasing an Earth bay no longer end it. -5. **Contract boards are stored.** A bay's board is generated on the first request in a period and stored. Later requests in that period return the stored board, so a discovery mid-period does not change it. Every contract expires at the end of its board period; nothing carries over. +5. **Contract boards are stored.** *(Replaced by CR-002.)* A board exists for every **discovered location**, per player, per period. It is generated on the first request in a period and stored; later requests in that period return the stored board. Every contract expires at the end of its board period. 6. **Cargo tier.** Standard is tier 0. A non-Standard contract has a tier from 1 to 5, and that tier is the required level in the cargo's skill. 7. **Long Haul.** A config list of distance cut-offs, one per Long Haul level. The required Long Haul level is the number of cut-offs the route distance exceeds. 8. **Config-driven tuning.** First-contact chance, first-contact multiplier, contract mass range, and Long Haul cut-offs all live in GameConfig. *(Amended after the CR-001 playtest: the single mass range is replaced by per-ship-size mass bands, `contractMassBands`.)* @@ -88,6 +90,8 @@ These answer the pre-build review. Log each one in `DECISIONS.md` as it is imple 32. **Loan** (CR-001). The company has a loan balance. When a dispatch resolves with positive credits (after pilot pay), the lender takes min(balance, credits × repayment share). The player can pay down any amount up to the balance at any time. No interest, no missed payments, no fail state. 33. **Scripted tutorial contracts** (CR-001). Content can define special tutorial contracts keyed to a tutorial run number. A scripted contract replaces one board slot on that run and can carry a custom name, pay multiplier, and a flavor message shown after delivery. 34. **Onboarding goals** (CR-001). A fixed, ordered list of goals in content, each with a condition evaluated from player state and a one-time credit reward the player claims. Goals appear after the tutorial ends. +35. **Ship delivery** (CR-002). Buying a ship requires choosing one of the player's bays. The new ship appears there, idle and uncrewed. +36. **Undiscovered locations** (CR-002). Requesting the board of an undiscovered location fails with a `location-undiscovered` error. Deadheading to an undiscovered location is not allowed; only a contract delivery discovers a location. --- @@ -467,6 +471,12 @@ Steps 10A.1–10A.18 are specified in `memory-bank/change-requests/CR-001-faster --- +## Phase 10B — Open Markets, Bays as HQs (CR-002) + +Steps 10B.1–10B.10 are specified in `memory-bank/change-requests/CR-002-open-markets.md` §4, with its §5 amendments to earlier steps. Done after Phase 10A and before Phase 11; stop for review at the end. + +--- + ## Phase 11 — PWA ### Step 11.1 — Installable PWA diff --git a/memory-bank/progress.md b/memory-bank/progress.md index b01eb92..9f44534 100644 --- a/memory-bank/progress.md +++ b/memory-bank/progress.md @@ -129,3 +129,18 @@ Spec: `memory-bank/change-requests/CR-001-faster-start.md`. Decisions recorded a - [x] HQ tutorial hint when enough runs are done but the loaner is away from Earth; dispatch sheet shows job details and lists every problem with numbers; onboarding test race fixed. - [x] Contract size bands (user's choice): `contractMassBands` gives every board jobs for each ship size, each band starting with a no-skill Standard job to a discovered place. Smoke test: guest → 4 runs → deal → a Courier job resolves with a loan repayment. + +## Phase 10B — Open Markets, Bays as HQs (CR-002) + +Spec: `memory-bank/change-requests/CR-002-open-markets.md`. New decisions recorded as plan §0.5.35–0.5.36. + +- [ ] Step 10B.1 — Sim: boards at any location +- [ ] Step 10B.2 — Sim: deadhead validation +- [ ] Step 10B.3 — Database: boards keyed by location +- [ ] Step 10B.4 — Server: board endpoint for any discovered location +- [ ] Step 10B.5 — Server: no more stranded ships +- [ ] Step 10B.6 — Server: ship delivery to a bay +- [ ] Step 10B.7 — Client: Contracts screen by location +- [ ] Step 10B.8 — Client: deadhead action +- [ ] Step 10B.9 — Client: bays as HQs +- [ ] Step 10B.10 — Plan amendments and regression