deadhead-space-freight-tycoon/memory-bank/change-requests/CR-009-ship-upgrades.md

12 KiB
Raw Blame History

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).
  • 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.

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.