deadhead-space-freight-tycoon/memory-bank/change-requests/CR-012-lighter-clipper-rang...

160 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Change Request CR-012 — Lighter and Clipper, Per-Leg Range, Gravity-Well Range
> **Numbering note (added when implemented):** this request arrived titled "CR-009 — Lighter and Clipper, Per-Leg Range, Gravity-Well Range" for "Phase 10I", applying to "CR-001 through CR-008". CR-009 to CR-011 and Phases 10I–10K already existed, so it is stored as **CR-012** and done as **Phase 10L**: every "10I.n" below means **10L.n**. Its decisions 0.5.59–0.5.63 are recorded as **0.5.77 Per-leg range, 0.5.78 Gravity-well range factor, 0.5.79 No-strand rule, 0.5.80 New ships, 0.5.81 Reach targets**. It amends "0.5.49 Models stay distinct" and "0.5.50 Courier to Mars", which in this repo are **0.5.67** and **0.5.68** (from the ship upgrades CR). Its "CR-006 (ship upgrades)" is this repo's **CR-009**, and its "CR-008" (settings) is **CR-011**. The error code `would_strand` is `would-strand`.
**Project:** Deadhead: Space Freight Tycoon
**Applies to:** a build with Phases 1–10 and CR-001 through CR-008 complete
**Do this:** as **Phase 10I**, after Phase 10H and before Phase 11
**Related docs:** `implementation-plan.md`, `change-request-open-markets.md` (CR-002), `change-request-map-art.md` (CR-003), `change-request-ship-upgrades.md` (CR-006), `game-design-document.md`, `DECISIONS.md`
---
## 1. Why
Three connected changes:
1. **Two new ships between the Courier and the Freighter.** The jump from Courier to Freighter is too big. Instead of two more rungs on a ladder, the new ships are a fork: the **Lighter** works harder close to home (heavy Earth–Moon loads), and the **Clipper** goes farther, faster (the cheapest way to Mars, built for Priority and small high-value cargo).
2. **Range is per leg.** The sim already checks range from wherever the ship departs, but the design targets and several tests say "from Earth". With open markets (CR-002), ships hop: Mars and Ceres are real stepping stones (Mars→Ceres at a good alignment is shorter than Earth→Ceres at its best; Ceres→Jupiter is roughly half of Earth→Jupiter). Targets and tests must describe reach per leg and by hopping.
3. **Gravity-well range factor.** Launching from a low-gravity body stretches a ship's range, so a Moon or Ceres HQ becomes a real staging base for long runs.
A **no-strand rule** is added so hopping can never leave a ship stuck somewhere it can't fly out of.
## 2. The ships
| | Courier | **Lighter** (new) | **Clipper** (new) | Freighter | Bulk Hauler |
|---|---|---|---|---|---|
| Role | Starter | Short-haul heavy lifter | Fast long-legged runner | All-rounder | Long-haul bulk |
| Speed | Fast | Slow (faster than Bulk Hauler) | **Fastest in the game** | Medium | Slowest |
| Capacity | Small | Near Freighter (75–90% of it) | About 1.5× Courier | Large | Largest |
| Takeoff/landing overhead | Low | Low | Low | Normal | High |
| Handling time per tonne | Normal | **Fast** (big cargo doors) | Normal | Normal | Normal |
| Fuel cost per million km | Low | Medium | **High** | Medium | High |
| Landing class | Atmospheric | Atmospheric | Atmospheric | Atmospheric | Atmospheric |
| Price | Cheapest | About 40% of the way from Courier to Freighter | About 60% of the way | — | Highest |
Ids: `lighter`, `clipper`. Sprite ids: `ship-lighter`, `ship-clipper`.
## 3. Range rules
### Gravity-well range factor (per location, in content)
| Location | Range factor |
|---|---|
| Earth | 1.0 |
| Mars | 1.15 |
| Moon | 1.3 |
| Ceres Station | 1.3 |
| Jupiter Cloud Platform | 1.0 (platforms float high above the deep gravity well) |
**Effective range from an origin** = ship's effective range (model plus upgrades, CR-006) × origin's range factor. A leg is in range if its distance at departure is no more than the effective range from that origin.
### Reach targets (base ships, no upgrades)
"Always" = in range on every sampled date. "Some" = in range on at least one sampled date and out of range on at least one. "Never" = out of range on every sampled date. Distances come from the sim; approximate values for reference: Earth–Mars 55–400M km, Mars–Ceres 165–660M km, Earth–Ceres 240–595M km, Ceres–Jupiter 350–1,230M km, Earth–Jupiter 590–970M km, Mars–Jupiter 490–1,040M km.
| Ship | Leg targets | Furthest reach by hopping |
|---|---|---|
| Courier | Earth↔Moon **always**. Earth→Mars and Moon→Mars **never**. | Moon |
| Lighter | Earth↔Moon **always**. Earth→Mars and Moon→Mars **never**. | Moon |
| Clipper | Earth→Mars **some** (in range on at least 30% of dates). Moon→Mars **some**, on more dates than Earth→Mars. Mars→Ceres **some**. Earth→Ceres and Moon→Ceres **never**. Any leg to Jupiter **never**. | Ceres, via Mars |
| Freighter | Earth→Mars **always**. Earth→Ceres **some** (most dates). Ceres→Jupiter **some**. Earth→Jupiter, Moon→Jupiter, and Mars→Jupiter **never**. | Jupiter, via Ceres |
| Bulk Hauler | Earth→Jupiter **always**. Every other leg between the five locations **always**. | Anywhere |
Upgrades (CR-006) may push a ship past a "never" target; that is an intended upgrade payoff. The distinctness caps in §4 still apply.
### No-strand rule
A dispatch (contract or deadhead) is rejected with `would_strand` if, at the arrival time plus a configured margin (default 48 game hours), the ship could not reach **any** other discovered location from the destination with its effective range from that destination. This prevents hopping into a place the ship can't leave.
## 4. Decisions
Record these in `DECISIONS.md` and `implementation-plan.md` §0.5.
- **0.5.59 Per-leg range.** Ship range is checked per leg, from the departure location, at departure time. Design targets and tests must name their origin explicitly.
- **0.5.60 Gravity-well range factor.** Each location has a launch range factor (§3). Effective range from an origin = ship effective range × origin factor. This is separate from the existing launch fuel-cost multiplier.
- **0.5.61 No-strand rule.** As §3. New validation reason `would_strand`.
- **0.5.62 New ships.** Lighter and Clipper per §2, purchasable, all atmospheric.
- **0.5.63 Reach targets.** The §3 table is the source of truth for ship range values. Ranges are chosen so every cell holds; adjust ship ranges (not location factors) to satisfy it.
- **0.5.49 (amended) Models stay distinct.** In addition to the existing three caps: a maxed Clipper's capacity is below a base Freighter's; a maxed Courier's speed is below a base Clipper's; a maxed Lighter can't reach Mars from Earth or the Moon on any sampled date. The existing "only Bulk Haulers reach Jupiter at its farthest" cap now reads "from Earth".
- **0.5.50 (amended) Courier to Mars.** The maxed-tanks Courier target is measured **from Earth**. From the Moon it will reach Mars more often; that's fine.
## 5. 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`.
- **Ship stat values are chosen to satisfy tests, not by hand.** Where a test fails, change ship values (range first, then the others) and record the change in `DECISIONS.md`.
- Date-sampling tests sample monthly over **16 game years** (long enough for Ceres–Jupiter alignments to cycle).
- Until the user provides art for the new ships, they use the CR-003 fallback placeholder.
- 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 10I.
## 6. Steps — Phase 10I
### Step 10I.1 — Content: range factors and new ships
- Add a launch range factor to the Location schema and set the §3 values.
- Add the Lighter and Clipper ship models with sprite keys and a one-line role description for each ship model (all five), shown in the Market.
- Add the no-strand margin to GameConfig.
- **Test:** Content validates. Every location has a range factor greater than zero. Both new ships are purchasable and atmospheric. Price ordering: Courier < Lighter < Clipper < Freighter < Bulk Hauler. Speed: Clipper is the fastest model; Bulk Hauler is the slowest; Lighter is slower than Freighter. Capacity: Courier < Clipper < Lighter < Freighter < Bulk Hauler. Lighter handling time per tonne is the lowest of all models. Clipper fuel cost per million km is higher than Courier's and Freighter's. These replace the Step 2.3 ordering tests.
### Step 10I.2 — Sim: effective range from origin
- The range check uses effective range from the origin (decision 0.5.60) everywhere: contract dispatch, deadhead, dispatch-sheet eligibility, and reachability lists.
- **Test:** A ship with base range 100 has effective range 130 from the Moon and 115 from Mars. A leg that fails from Earth passes from the Moon when its distance is between 100 and 130. With all factors set to 1.0 in a test fixture, results match the pre-change range check exactly.
### Step 10I.3 — Sim: no-strand rule
- Add the `would_strand` check (decision 0.5.61) to dispatch validation, after range and landing checks.
- **Test:** A fixture ship whose effective range from the destination can't reach any other discovered location at arrival plus margin is rejected with `would_strand`. The same trip passes once another reachable location is discovered. No base ship on any sampled date is ever stranded by a trip it is allowed to take between the five locations (exhaustive over sampled dates and origin/destination pairs).
### Step 10I.4 — Content tuning: reach table
- Choose ship ranges (and adjust other stats only if needed) so every cell of the §3 reach table holds.
- Rewrite Step 3.7's range tests to name their origin explicitly.
- **Test:** One test per cell of the §3 table using monthly samples over 16 game years, plus the hop tests: a Clipper can't reach Ceres from Earth on any date but can from Mars on some dates; a Freighter can reach Jupiter from Ceres on some dates but never directly from Earth.
### Step 10I.5 — Upgrade caps for the new lineup
- Apply decisions 0.5.49 (amended) and 0.5.50 (amended); set per-model upgrade caps for the Lighter and Clipper as needed.
- **Test:** All distinctness inequalities in 0.5.49 hold, including the three new ones. The maxed-tanks Courier target from Earth still holds; a level-2-tanks Courier never reaches Mars from Earth.
### Step 10I.6 — Schema and server
- Add the `would_strand` error code. The Market and buy-ship endpoint accept the new models. No other server changes.
- **Test:** Buying a Lighter and a Clipper succeeds and places them at the chosen HQ. A dispatch that would strand returns `would_strand` and changes nothing.
### Step 10I.7 — Client: Market shows roles and reach
- Each ship in the Market shows its role line and a "Reach" line computed with the sim: legs it can fly from the player's HQs right now, and its furthest reach by hopping from §3 (for example "From Earth now: Moon, Mars · By hopping: Ceres").
- **Test:** Component test: the Clipper's reach line lists Mars from Earth only on a mocked date when Mars is in range, and always lists Ceres under "By hopping"; the Lighter's lists only the Moon.
### Step 10I.8 — Client: reachability from where ships actually are
- Replace "reachable from Earth" text (map panels, CR-001 Step 10A.16) with per-origin text: for an undiscovered location, list each discovered location that has a player ship or HQ, and which ship models can reach it from there right now, for example "From Mars: Clipper, Freighter · From Earth: Freighter".
- **Test:** Component test with a mocked date and a ship at Mars: Ceres's panel lists "From Mars: Clipper" when Mars→Ceres is in range for a Clipper, and does not list Clipper under Earth.
### Step 10I.9 — Client: stranding reason
- In the dispatch sheet and the deadhead picker, a would-strand option is disabled with "Would be stranded — can't fly out of [destination] from there".
- **Test:** Component test: a would-strand pair or destination is disabled with that text.
### Step 10I.10 — Art
- Add `ship-lighter` and `ship-clipper` to the CR-003 manifest. If the user's files are present, run the asset script; otherwise the fallback placeholder is used and the missing assets are noted in `progress.md`.
- **Test:** The CR-003 manifest tests pass (every ship model has a sprite key, every manifest file exists or is listed as pending in `DECISIONS.md`).
### Step 10I.11 — Plan amendments and regression
- Apply the amendments in §7 to `implementation-plan.md` and earlier change requests.
- Run the full suite.
- **Test:** CI is green. The balance script (once it exists) runs two policies, Lighter-first and Clipper-first for the second ship; both reach "own three ships" within 25% of each other's time. Manual check: buy a Clipper, discover Mars, deadhead or take work to Mars, then take a first-contact contract to Ceres from the Mars board on a date when it's in range.
- **Amended by CR-013:** The second-ship balance check (Lighter-first vs Clipper-first) is replaced by CR-013's first-purchase check: Lighter-first vs Freighter-first, both reaching "own four ships" within 25 % of each other's time (Step 10M.5).
## 7. Amendments
| Where | Change |
|---|---|
| Plan 0.2 | Ships: 5 models (Courier, Lighter, Clipper, Freighter, Bulk Hauler). |
| Plan 0.5 | Add 0.5.59–0.5.63; amend 0.5.49 and 0.5.50. |
| Plan 2.1 | Location gains launch range factor; ShipModel gains a role line; GameConfig gains the no-strand margin. |
| Plan 2.3 | Ordering tests replaced by 10I.1. |
| Plan 3.7 | Range check uses effective range from origin; tests name origins (10I.2, 10I.4). |
| Plan 3.12 | Validation gains `would_strand` (10I.3). |
| Plan 9.8 | Market shows role and reach (10I.7). |
| Plan 12.1 | Two second-ship policies, Lighter-first and Clipper-first (10I.11). |
| CR-001 10A.16 | Reachability text is per origin (10I.8). |
| CR-003 | Manifest gains `ship-lighter` and `ship-clipper`. |
| CR-006 | Caps amended (10I.5). |
## 8. Out of scope
Refueling stops or fuel stations as separate mechanics, multi-leg routes booked as one dispatch, automatic route planning across hops, orbital transfer windows beyond what real distances already produce, and anything else in `implementation-plan.md` §0.3.