12 KiB
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_slowis the existingtoo-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/simstays pure, all numbers live inpackages/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.mdfor schema or API changes andprogress.mdafter 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-tanksvalues 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).
- Amended by CR-026: Hull Plating is added (+1 hull rating a level, three levels, the usual costs and shop times); upgrades may add a flat amount (
flatPerLevel) instead of a percent. - Amended by CR-027: Upgrades (one level a visit) and repairs can be booked from anywhere for an HQ; the work starts, and is charged, when the ship is next idle there.
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_slowinto 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_shopone second before in-shop-until and passes at it.
Step 10F.4 — Schema and database
- Add a
ship_upgradestable (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_shopfor 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 withnot_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 updategame-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.