127 lines
11 KiB
Markdown
127 lines
11 KiB
Markdown
# Change Request CR-009 — Ship Upgrades
|
||
|
||
> **Numbering note (added when implemented):** this request arrived titled "CR-006 — Ship Upgrades", for "Phase 10F", applying to "CR-001 through CR-005". CR-006 to CR-008 and Phases 10F–10H already existed, so it is stored as **CR-009** and done as **Phase 10I**: every "10F.n" below means **10I.n**. Its decisions 0.5.45–0.5.50 are recorded as **0.5.63 Effective stats, 0.5.64 Where and when, 0.5.65 One level at a time, 0.5.66 Shop time, 0.5.67 Models stay distinct, 0.5.68 Courier to Mars** (0.5.45–0.5.62 were in use). Error codes use the API's kebab-case and existing codes where one already means the same thing: `not_at_hq` → `not-at-hq`, `ship_busy` → `ship-not-idle`, `ship_in_shop` → `ship-in-shop`, `upgrade_maxed` → `upgrade-maxed`, `insufficient_funds` → `insufficient-funds`, `tutorial_active` → `tutorial-active`, `not_upgradable` → `not-upgradable`; `too_slow` is the existing `too-slow`.
|
||
|
||
**Project:** Deadhead: Space Freight Tycoon
|
||
**Applies to:** a build with Phases 1–10 and CR-001 through CR-005 complete
|
||
**Do this:** as **Phase 10F**, after Phase 10E and before Phase 11
|
||
**Related docs:** `implementation-plan.md`, the earlier change requests, `game-design-document.md`, `DECISIONS.md`
|
||
|
||
---
|
||
|
||
## 1. Why
|
||
|
||
A ship's stats are fixed by its model, so the only way to improve the fleet is to buy a different ship. There is nothing to spend money on between ship purchases and no way to make a favorite ship better.
|
||
|
||
This adds an ATS-style tuning shop:
|
||
|
||
- Five upgrades, each with **three levels**, each improving one ship stat.
|
||
- Upgrades are installed **at an HQ (bay)**, which gives bays a second purpose besides hiring pilots.
|
||
- The ship is **out of service** while it's in the shop: start an upgrade, put the phone down, come back to a better ship.
|
||
- **Caps keep ship models distinct.** A maxed Courier is still a small, fast ship, never a hauler. One deliberate exception: a Courier with maxed fuel tanks can reach Mars around closest approach, as an ambitious early goal.
|
||
|
||
## 2. Upgrades
|
||
|
||
All numbers are starting values in `packages/content`. Percentages are of the ship model's base stat and add up across levels (level 3 engines = +30% speed).
|
||
|
||
| Upgrade id | Name | Improves | Per level | Max level |
|
||
|---|---|---|---|---|
|
||
| `engines` | Engine Tuning | Cruise speed | +10% | 3 |
|
||
| `cargo-bay` | Cargo Bay Expansion | Cargo capacity | +15% | 3 |
|
||
| `fuel-tanks` | Extended Fuel Tanks | Fuel range | +20% | 3 (see 10F.1 for the Courier) |
|
||
| `landing-thrusters` | Landing Thrusters | Takeoff/landing overhead | −15% | 3 |
|
||
| `handling-gear` | Cargo Handling Gear | Handling time per tonne | −15% | 3 |
|
||
|
||
| Level | Cost (% of ship model's purchase price) | Time in shop (real minutes) |
|
||
|---|---|---|
|
||
| 1 | 10% | 10 |
|
||
| 2 | 20% | 20 |
|
||
| 3 | 35% | 30 |
|
||
|
||
Hull plating is left out until random events exist, because hull rating has no effect yet.
|
||
|
||
## 3. Rules for this change
|
||
|
||
- All rules in `implementation-plan.md` §0.1 apply: steps in order, one commit per step, each step's test must pass, `packages/sim` stays pure, all numbers live in `packages/content`.
|
||
- Every place that reads a ship's speed, capacity, range, overhead, or handling time must use the ship's **effective stats** (model plus upgrades), never the model's stats directly.
|
||
- Resetting the dev and test databases is fine. Schema migrations are still required.
|
||
- Update existing tests whose expectations change. Update `architecture.md` for schema or API changes and `progress.md` after every step.
|
||
- Stop for the user's review at the end of Phase 10F.
|
||
|
||
## 4. Decisions
|
||
|
||
Record these in `DECISIONS.md` and `implementation-plan.md` §0.5.
|
||
|
||
- **0.5.45 Effective stats.** A ship's effective stats are its model's base stats adjusted by its installed upgrade levels. Percentage bonuses are of the base stat and add up linearly.
|
||
- **0.5.46 Where and when.** An upgrade can be started only on an owned ship that is idle (not in flight, not already in the shop) at a location with a player bay, with tutorial mode off. The Company Courier loaner can't be upgraded.
|
||
- **0.5.47 One level at a time.** Each purchase raises one upgrade by exactly one level. A ship can be in the shop for one upgrade at a time.
|
||
- **0.5.48 Shop time.** Starting an upgrade deducts the cost, raises the level immediately, and sets the ship's in-shop-until time. The ship can't be dispatched until then (`ship_in_shop`). Because dispatch is blocked, there is nothing to complete later: no background job is needed. The assigned pilot stays assigned and can rest meanwhile.
|
||
- **0.5.49 Models stay distinct.** Content caps guarantee: a maxed Courier's capacity is below a base Freighter's; a maxed Bulk Hauler's speed is below a base Freighter's; a maxed Freighter's range is below a base Bulk Hauler's (so only Bulk Haulers reach Jupiter at its farthest).
|
||
- **0.5.50 Courier to Mars.** A Courier with maxed fuel tanks can reach Mars at and around closest approach, but not at Mars's farthest distance. Base and lower-level Couriers still can't reach Mars at all.
|
||
|
||
## 5. Steps — Phase 10F
|
||
|
||
### Step 10F.1 — Content: upgrades
|
||
- Add an Upgrade schema: id, name, stat affected, change per level (percent), max level, and optional per-model max level overrides.
|
||
- Add the five upgrades from §2, and config for cost percent and shop time per level.
|
||
- Set the Courier's `fuel-tanks` values so decision 0.5.50 holds (adjust the Courier's per-level bonus or cap, not the Courier's base range, and don't break any Step 3.7 range test).
|
||
- **Test:** All upgrades validate. Every upgrade affects a stat that exists on ShipModel. Tests for decision 0.5.49 (all three inequalities). Tests for decision 0.5.50: sampling Earth–Mars distance monthly over two game years, a maxed Courier is in range on at least one sample and out of range on at least one sample; a Courier with fuel tanks at level 2 is never in range.
|
||
- **Amended by CR-012:** Caps follow the amended 0.5.49/0.5.50 (this plan's 0.5.67/0.5.68): three more distinctness inequalities, Courier-to-Mars measured from Earth over 16 game years; the Courier's tanks are 450 % per level (Step 10L.5).
|
||
|
||
### Step 10F.2 — Sim: effective stats
|
||
- Add a pure function that returns a ship's effective stats from its model and installed upgrade levels.
|
||
- Change travel time, fuel range checks, capacity checks, handling time, and Priority window checks to take effective stats.
|
||
- **Test:** With no upgrades, effective stats equal the model's stats exactly, and all existing Phase 3 tests pass unchanged. Each upgrade changes only its own stat. Level 3 engines give exactly +30% speed. A cargo-bay upgrade turns a contract that failed with the too-heavy reason into a valid one. An engines upgrade turns a contract that failed with `too_slow` into a valid one.
|
||
|
||
### Step 10F.3 — Sim: upgrade validation and cost
|
||
- Add pure functions: next-level cost and shop time for a ship and upgrade; validation for starting an upgrade per decisions 0.5.46 and 0.5.47, with reasons: `not_at_hq`, `ship_busy` (in flight), `ship_in_shop`, `upgrade_maxed`, `insufficient_funds`, `tutorial_active`, `not_upgradable` (the loaner).
|
||
- Dispatch validation gains `ship_in_shop`.
|
||
- **Test:** One test per reason. Cost for level 1 on a Freighter equals 10% of the Freighter's price; level 3 equals 35%. A ship in the shop fails dispatch validation with `ship_in_shop` one second before in-shop-until and passes at it.
|
||
|
||
### Step 10F.4 — Schema and database
|
||
- Add a `ship_upgrades` table (ship, upgrade id, level) and an in-shop-until time on ships.
|
||
- Add a "start upgrade" endpoint (ship id, upgrade id) to the route catalog, and the new error codes.
|
||
- Player state includes, per ship: installed upgrade levels, effective stats, and in-shop-until.
|
||
- Update `architecture.md`.
|
||
- **Test:** Migrations run on a fresh database and are a no-op the second time. New and changed schemas accept valid examples and reject invalid ones. The route catalog test still passes.
|
||
|
||
### Step 10F.5 — Server: start upgrade
|
||
- In one transaction with the company row lock (Step 6.14): validate, deduct the cost, raise the level by one, set in-shop-until.
|
||
- Dispatch uses effective stats and returns `ship_in_shop` for ships in the shop.
|
||
- **Test:** Starting a level 1 engines upgrade on an idle Freighter at the Earth HQ deducts the right cost, sets the level to 1 and the correct in-shop-until. Dispatching it before then fails with `ship_in_shop`; after, it succeeds and its trip time reflects the faster speed. Starting an upgrade at a location without a bay fails with `not_at_hq`. Two simultaneous upgrade requests the player can afford only one of produce exactly one upgrade and no negative balance.
|
||
|
||
### Step 10F.6 — Client: upgrade shop
|
||
- Each ship on the Fleet screen opens a detail view with an **Upgrades** panel listing all five upgrades: current level, next level's effect as before → after (for example "Speed 180 → 198 km/h"), cost, and shop time.
|
||
- Disabled upgrades show the reason (not at an HQ, in flight, in the shop, maxed, can't afford).
|
||
- Confirming starts the upgrade. Ships in the shop show "In the shop — mm:ss" on their Fleet card.
|
||
- **Test:** Component test: before → after values match the sim's effective stats; each disabled reason displays correctly; confirming calls the endpoint; an in-shop ship shows the countdown.
|
||
|
||
### Step 10F.7 — Client: effective stats everywhere
|
||
- Contract card badges (TOO HEAVY, TOO SLOW), the dispatch sheet, the deadhead picker, the Market's ship comparison for owned ships, and map reachability text all use effective stats.
|
||
- **Test:** Component test: after a seeded cargo-bay upgrade, a contract that showed TOO HEAVY for that ship no longer does; the deadhead picker enables a destination that only the upgraded range reaches.
|
||
|
||
### Step 10F.8 — Client: shop in summaries and on HQ
|
||
- The "while you were away" modal shows "[Ship] is out of the shop: Engine Tuning 2" for upgrades that finished while the player was away.
|
||
- HQ shows ships currently in the shop with countdowns.
|
||
- **Test:** Component test with seeded state: the modal line appears for an upgrade that finished since the last visit; HQ lists in-shop ships with correct countdowns.
|
||
|
||
### Step 10F.9 — Plan amendments and regression
|
||
- Apply the amendments in §6 to `implementation-plan.md`, and update `game-design-document.md` §8 (done by the designer; confirm it matches).
|
||
- Run the full suite.
|
||
- **Test:** CI is green. Manual check: on a fresh account after the CR-001 deal, earn enough to buy Cargo Bay Expansion 1 for the Courier at the Earth HQ, confirm it's unavailable for the shop time, then confirm it can take a contract that was too heavy before.
|
||
|
||
## 6. Amendments to `implementation-plan.md`
|
||
|
||
| Step | Change |
|
||
|---|---|
|
||
| 0.5 | Add decisions 0.5.45–0.5.50. |
|
||
| 2.1 | Content gains the Upgrade schema and upgrade cost/time config (10F.1). |
|
||
| 3.5, 3.7, 3.12 | Use effective stats; validation gains `ship_in_shop` (10F.2, 10F.3). |
|
||
| 5.3 | `ship_upgrades` table and in-shop-until on ships (10F.4). |
|
||
| 9.5, 9.6, 9.8 | Effective stats in dispatch, Fleet, and Market; upgrade shop on Fleet (10F.6, 10F.7). |
|
||
| 12.1 | The simulated player may buy upgrades: when it can't yet afford its next planned ship, it buys the cheapest available upgrade on its most-used ship, preferring cargo bay and engines. Re-check all timing targets; adjust content values, not thresholds. |
|
||
|
||
## 7. Out of scope
|
||
|
||
Hull plating (until random events exist), selling or removing upgrades, ship resale value, changing a ship's landing class, cosmetic liveries, ship renaming, and anything else in `implementation-plan.md` §0.3.
|