195 lines
18 KiB
Markdown
195 lines
18 KiB
Markdown
# Change Request CR-015 — Fixed Map and Fuel Tanks
|
||
|
||
> **Numbering note (added when recorded):** this request arrived titled "CR-014 — Fixed Map and Fuel Tanks" for "Phase 14". CR-014 (local-first) and Phase 14 already existed, so it is stored as **CR-015** and done as **Phase 15**: every "14.n" below means **15.n**. Its references map to this repo as: "CR-006 ship upgrades" = **CR-009**, "CR-007 veteran pilot" = **CR-010**, "CR-009 new ships and range" = **CR-012** (its steps "10I.n" are **10L.n**), "CR-011 local-first" = **CR-014**; "CR-001" is CR-001. Decisions: its 0.5.9 and 0.5.12 are the same numbers; its "0.5.24" (deal loan repayment) is **0.5.32**; "0.5.49/0.5.50" (models stay distinct, Courier to Mars) are **0.5.67/0.5.68**; "0.5.60" (effective range from origin) is **0.5.78**; "0.5.61" (no-strand) is **0.5.79**; "0.5.63" (reach targets) is **0.5.81**. Its new decisions CR-014.D1–D7 are recorded as **0.5.93 Fixed map, 0.5.94 Fuel tanks, 0.5.95 Burn and reach, 0.5.96 Fuel prices, 0.5.97 Refueling, 0.5.98 Fuel cost of a leg, 0.5.99 Emergency fuel**. Error codes use this repo's kebab case (`not-enough-fuel`, `would-strand`).
|
||
>
|
||
> **User decision (2026-10-10), amending D3 (0.5.95):** a leg's fuel follows the same economic-distance curve as pay (decision 0.5.84), so long runs keep today's profitability: **fuel for a leg = burn rate × economic distance(distance ÷ origin range factor)**. Full-tank reach in plain million km is still exactly the old effective range from the origin, so §4 holds as written; only the fu burned per leg grows more slowly than the distance. Wherever §3–§4 below say "distance × burn rate ÷ origin range factor", read this formula.
|
||
|
||
**Project:** Deadhead: Space Freight Tycoon
|
||
**Applies to:** a build with Phase 13 and CR-001 through CR-013 complete
|
||
**Do this:** as **Phase 14**
|
||
**Related docs:** `implementation-plan.md`, `change-request-ship-upgrades.md` (CR-006), `change-request-veteran-pilot.md` (CR-007), `change-request-new-ships-and-range.md` (CR-009), `change-request-local-first.md` (CR-011), `game-design-document.md`, `DECISIONS.md`
|
||
|
||
This change request is written for the local-first engine (CR-011): "command" means an engine command, and "save field" means a field in the save document with a save migration.
|
||
|
||
---
|
||
|
||
## 1. Why
|
||
|
||
Two connected changes:
|
||
|
||
1. **The planets stop orbiting.** This is soft sci-fi, and in a trucking game part of the fun is learning the map: Earth to Ceres is always the same run. Moving planets make every route a lookup, make ETAs depend on launch date, and fight the new fuel system. Locations get **fixed positions**, so every distance, travel time, and payout for a route is constant.
|
||
2. **Ships carry fuel.** Instead of paying for exactly the fuel each trip needs, every ship has a **tank**. Legs burn fuel from the tank; ships **refuel instantly** at locations, ATS-style, at that location's **fixed fuel price** (cheap at Jupiter's refineries, expensive at Earth). Where you fill up becomes a real decision. By default ships top up automatically on landing; any ship can be switched to manual so the player can wait for cheaper fuel.
|
||
|
||
## 2. Fixed map
|
||
|
||
### Positions
|
||
Locations sit at fixed points, given in content as polar coordinates around the Sun (radius in million km, angle in degrees). Radii are the real average orbital radii. Angles are chosen so the reach rules in §4 work with clean margins. Only the angles **between** bodies matter; rotate the whole layout freely for looks.
|
||
|
||
| Location | Radius (M km) | Angle (suggested) | Notes |
|
||
|---|---|---|---|
|
||
| Earth | 150 | 210° | |
|
||
| Moon | — | — | Fixed offset from Earth (0.38M km), as now |
|
||
| Mars | 228 | 270° | |
|
||
| Ceres Station | 414 | 285° | |
|
||
| Jupiter Cloud Platform | 778 | 300° | At Jupiter |
|
||
|
||
Resulting distances (approximate, check with the sim): Earth–Mars ~201M km, Mars–Ceres ~202M km, Earth–Ceres ~402M km, Ceres–Jupiter ~393M km, Mars–Jupiter ~592M km, Earth–Jupiter ~792M km.
|
||
|
||
### What goes away
|
||
- `astronomy-engine` and all time-based positions.
|
||
- The ephemeris half of the game epoch (decision 0.5.9). The game clock stays for timers and the displayed game date.
|
||
- Every "on some dates", "at closest approach", and "sampled over N game years" rule. Each becomes a plain yes or no (§4).
|
||
|
||
### The map
|
||
- Planets and stations stay in place. Orbit rings are still drawn as static decoration.
|
||
- Routes are fixed straight lines; in-flight ships move along them as before.
|
||
- Map reachability text (CR-009 Step 10I.8) no longer mentions dates: "From Mars: Clipper, Freighter".
|
||
|
||
## 3. Fuel tanks
|
||
|
||
### The model
|
||
| Concept | Rule |
|
||
|---|---|
|
||
| Fuel units | One generic unit, shown as "fu". No fuel mass: fuel never affects cargo capacity or speed. |
|
||
| Tank capacity | Each ship model has a tank capacity in fu. The Extended Fuel Tanks upgrade (CR-006) raises capacity instead of range. |
|
||
| Burn rate | Each ship model burns fu per million km. |
|
||
| Fuel for a leg | `distance × burn rate ÷ origin range factor`. The gravity-well range factor (CR-009) now makes launches from low-gravity bodies more fuel-efficient. |
|
||
| Full-tank reach | `tank capacity ÷ burn rate × origin range factor`. This is exactly the old "effective range from origin", so the CR-009 reach rules carry over. |
|
||
| Flying a leg | Allowed only if fuel on board ≥ fuel for the leg. The leg's fuel is removed at departure. |
|
||
| Fuel cost of a leg | Fuel burned × the tank's **average purchase price** (the tank tracks a weighted average as fuel is added). Used for pilot pay (CR-007) and profit display. |
|
||
| New ships | Delivered with a full tank, included in the price. |
|
||
| Tutorial | Tutorial runs use no fuel (the company pays), as before. |
|
||
|
||
### Fuel prices (fixed per location, in content)
|
||
| Location | Price per fu (× base) | Why |
|
||
|---|---|---|
|
||
| Jupiter Cloud Platform | 0.6 | The system's refineries |
|
||
| Ceres Station | 0.9 | Belt ice cracked for fuel |
|
||
| Moon | 1.1 | Lunar LOX plants |
|
||
| Mars | 1.4 | Imported fuel, small market |
|
||
| Earth | 1.6 | Deep gravity well, high demand |
|
||
|
||
All five locations sell fuel. A `sellsFuel` flag exists in content for future locations that don't.
|
||
|
||
### Refueling
|
||
- **Instant**, like ATS. No time cost.
|
||
- **Refuel policy per ship:** *Auto top-up* (default) fills the tank on every landing at a location that sells fuel; *Manual* never refuels on its own.
|
||
- If credits can't cover a full top-up, auto top-up buys what the player can afford.
|
||
- **Manual refuel command:** fill to full, or buy a chosen amount, at the ship's current location.
|
||
- The dispatch flow offers **"Refuel here first"** when the ship doesn't have enough fuel for the leg.
|
||
|
||
### Stranding and emergency fuel
|
||
- **No-strand rule (CR-009 0.5.61), updated:** a dispatch is rejected with `would_strand` if, from the destination, the ship couldn't reach any other discovered location using the fuel it could have there: a full tank if the destination sells fuel, otherwise the fuel left after the leg. With all five locations selling fuel, this mostly guards future dry locations.
|
||
- **Emergency fuel:** if a ship is idle somewhere and its tank can't reach any other discovered location, and the player can't afford enough fuel, the player can order emergency fuel: enough for the cheapest reachable leg, at **2.5× the local price**, **added to the loan balance** instead of paid in credits. Repayment then works as for the deal loan (CR-001 0.5.24).
|
||
|
||
## 4. Reach rules, now yes/no
|
||
|
||
These replace the CR-009 §3 reach table. They are about full-tank reach for base ships (no upgrades), using the §2 distances and the CR-009 range factors (Earth 1.0, Mars 1.15, Moon 1.3, Ceres 1.3, Jupiter platform 1.0).
|
||
|
||
| Ship | Yes | No |
|
||
|---|---|---|
|
||
| Courier | Earth↔Moon | Earth→Mars, Moon→Mars |
|
||
| Lighter | Earth↔Moon | Earth→Mars, Moon→Mars |
|
||
| Clipper | Earth↔Moon, Earth↔Mars, Moon→Mars, Mars→Ceres | Earth→Ceres, Moon→Ceres, any leg to Jupiter |
|
||
| Freighter | Everything the Clipper can do, plus Earth↔Ceres and Ceres↔Jupiter | Earth→Jupiter, Moon→Jupiter, Mars→Jupiter |
|
||
| Bulk Hauler | Every leg between the five locations | — |
|
||
|
||
**Upgrades:** a Courier with maxed Extended Fuel Tanks **can** fly Earth→Mars; one at level 2 can't (replaces CR-006 decision 0.5.50 and its CR-009 amendment). All other CR-006/CR-009 distinctness caps still apply, restated as yes/no.
|
||
|
||
With the suggested angles, these hold with roughly: Courier and Lighter full-tank reach under 150M km; Clipper 210–300M km; Freighter 410–510M km; Bulk Hauler 800M km or more. Pick tank capacities and burn rates that land in those bands.
|
||
|
||
## 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`.
|
||
- Existing saves get a save migration: positions and distances are now static; every ship gets a tank, filled to full; every ship's refuel policy is Auto top-up. In-flight dispatches keep their stored arrival times.
|
||
- Update existing tests whose expectations change, including every date-sampling test (§7). Update `architecture.md`, `tech-stack.md`, and `progress.md` as you go.
|
||
- Stop for the user's review at the end of Phase 14.
|
||
|
||
## 6. Decisions
|
||
|
||
Record these in `DECISIONS.md` and `implementation-plan.md` §0.5. New decisions are labeled CR-014.D1–D7 here; give them the next free numbers in §0.5 and note the mapping in `DECISIONS.md`.
|
||
|
||
- **CR-014.D1 Fixed map.** Locations have fixed positions from content; distances never change. `astronomy-engine` is removed. Replaces the ephemeris part of 0.5.9.
|
||
- **CR-014.D2 Fuel tanks.** Ships carry fuel per §3. Fuel has no mass.
|
||
- **CR-014.D3 Burn and reach.** Fuel for a leg = distance × burn rate ÷ origin range factor. Replaces "effective range from origin" (0.5.60) as the range check; full-tank reach equals the old effective range.
|
||
- **CR-014.D4 Fuel prices.** Fixed per location (§3). Replaces the launch fuel-cost multiplier: launching from Earth is no longer charged extra, because Earth's fuel is the most expensive.
|
||
- **CR-014.D5 Refueling.** Instant. Per-ship policy: Auto top-up (default) or Manual. Auto buys what the player can afford.
|
||
- **CR-014.D6 Fuel cost of a leg.** Fuel burned × the tank's average purchase price. Pilot profit share (CR-007 0.5.12) uses this instead of the old per-trip fuel cost.
|
||
- **CR-014.D7 Emergency fuel.** Per §3: enough for the cheapest reachable leg, at 2.5× local price, added to the loan.
|
||
- **0.5.61 (amended) No-strand rule.** Uses fuel available at the destination per §3.
|
||
- **0.5.63 (amended) Reach targets.** The §4 yes/no table replaces the CR-009 §3 table.
|
||
- **0.5.50 (amended) Courier to Mars.** Maxed tanks: yes from Earth; level 2: no.
|
||
|
||
## 7. Steps — Phase 14
|
||
|
||
### Step 14.1 — Content: fixed positions
|
||
- Replace each location's astronomy body with fixed polar coordinates (§2). Remove the ephemeris epoch field from GameConfig. Remove `astronomy-engine` from the project.
|
||
- **Test:** Every location has coordinates; Moon is defined as an offset from Earth. Distances computed from the suggested coordinates match the §2 table within 2%. `astronomy-engine` is no longer in the lockfile.
|
||
|
||
### Step 14.2 — Sim: static positions and distances
|
||
- Location position and route distance no longer take a time argument. Travel time, payout, board generation, reachability, and map positions use static distances.
|
||
- Replace the Step 3.3 and 3.4 tests: positions are constant; distance A→B equals B→A; Earth→Moon is under 1M km. Remove the "Earth–Mars distance varies" test.
|
||
- **Test:** Calling position or distance for the same inputs always returns the same value. A contract's payout and travel time for a given route and ship are the same no matter when it's offered or dispatched.
|
||
|
||
### Step 14.3 — Content: fuel
|
||
- ShipModel: replace "fuel range" with **tank capacity** (fu) and **burn rate** (fu per million km); remove "fuel cost per million km". Location: add **fuel price** (§3) and `sellsFuel`; remove the launch fuel-cost multiplier. GameConfig: base fuel price, emergency fuel multiplier (2.5).
|
||
- Change the CR-006 Extended Fuel Tanks upgrade to raise tank capacity.
|
||
- Choose tank capacities and burn rates so the §4 table holds.
|
||
- **Test:** Content validates. Fuel prices are ordered Jupiter < Ceres < Moon < Mars < Earth. One test per cell of the §4 table using full-tank reach. The upgrade tests for 0.5.50 (amended) and the other CR-006/CR-009 caps pass in yes/no form.
|
||
|
||
### Step 14.4 — Sim: tank, burn, and refuel
|
||
- Pure functions: fuel needed for a leg; full-tank reach from an origin; refuel (adds fuel up to capacity, returns cost and the new weighted average price); affordable refuel (largest amount up to full that credits cover); leg fuel cost (burned × average price).
|
||
- Dispatch validation gains `not_enough_fuel` (fuel on board < fuel needed). Dispatch removes the leg's fuel at departure instead of charging credits.
|
||
- Resolution: on arrival, if the ship's policy is Auto top-up and the location sells fuel, apply the affordable refuel.
|
||
- Pilot pay (CR-007) uses leg fuel cost.
|
||
- **Test:** Fuel needed from the Moon is the Earth amount ÷ 1.3. Refueling 50 fu at price 1.0 into a tank holding 50 fu bought at 2.0 leaves an average of 1.5. A ship with too little fuel fails with `not_enough_fuel`; after refueling, it passes. Dispatch no longer changes credits. On arrival with Auto top-up, the tank is full and credits drop by the right amount; with Manual, nothing changes. With credits for only half a tank, Auto buys exactly what's affordable. Pilot pay for a leg uses burned fuel × average price. Tutorial dispatches don't touch fuel.
|
||
|
||
### Step 14.5 — Sim: stranding and emergency fuel
|
||
- Update the `would_strand` check per §3.
|
||
- Add an emergency fuel check (is it available for this ship?) and calculation (amount = fuel for the cheapest reachable leg minus fuel on board; cost = amount × local price × 2.5).
|
||
- **Test:** With all locations selling fuel, no base ship is ever blocked by `would_strand` on a §4 "yes" leg. A test fixture with a non-selling destination blocks a leg that would leave too little fuel to get out. Emergency fuel is offered only when the ship can't reach anywhere and the player can't afford the needed fuel, and its cost matches the formula.
|
||
|
||
### Step 14.6 — Engine: commands and save migration
|
||
- Save fields: per ship, fuel on board, tank average price, and refuel policy.
|
||
- Commands: refuel (full or amount), set refuel policy, order emergency fuel (adds fuel, adds cost to the loan balance).
|
||
- Save migration per §5.
|
||
- Buying a ship delivers it with a full tank at no extra cost.
|
||
- **Test:** An older save migrates with every ship full, on Auto top-up. Refuel deducts credits and fills correctly; refuel with no credits fails. Ordering emergency fuel when not available fails; when available, it adds fuel and increases the loan by exactly the cost. A new ship arrives full.
|
||
|
||
### Step 14.7 — Client: fuel in the UI
|
||
- **Fleet:** each ship card shows a fuel gauge (fu and %) and its refuel policy. Ship detail has Refuel (Fill / Amount), a policy toggle, and, when available, "Emergency fuel (added to loan)".
|
||
- **Dispatch sheet:** shows fuel needed, fuel on board, and fuel left after the leg. If short, the pair shows "Not enough fuel" with a "Refuel here first" button that fills the tank and keeps the sheet open. The cost line becomes "Fuel used (at your average price)".
|
||
- **Deadhead picker:** same fuel lines and refuel button.
|
||
- **Locations screen and map panels:** show each location's fuel price, highlighting the cheapest.
|
||
- **Delivery summaries:** show auto top-ups as their own line ("Topped up 120 fu at Jupiter, 72 cr").
|
||
- **Test:** Component tests: the gauge matches state; "Refuel here first" fills and enables the pair; the fuel-used line matches the sim; the cheapest fuel location is highlighted; an emergency fuel button appears only when available.
|
||
|
||
### Step 14.8 — Client: fixed map
|
||
- Remove all time-based position updates from the map. Draw static orbit rings for decoration. Remove date-based wording from reachability and Market reach lines (CR-009 Steps 10I.7–10I.8).
|
||
- **Test:** Component test: body screen positions don't change as the mocked clock advances. Reachability text contains no date-dependent wording.
|
||
|
||
### Step 14.9 — Plan amendments and regression
|
||
- Apply the amendments in §8.
|
||
- Run the full suite.
|
||
- **Test:** CI is green. The Phase 12 balance script re-checks all timing targets with fuel tanks and fixed distances, and confirms no simulated ship ever needs emergency fuel when the player follows Auto top-up with positive credits. Manual check: set the Freighter to Manual, fly to Jupiter, fill up cheaply, return, and confirm the next legs show a lower "fuel used" cost than fuel bought at Earth.
|
||
|
||
## 8. Amendments
|
||
|
||
| Where | Change |
|
||
|---|---|
|
||
| Plan 0.5 | Add CR-014.D1–D7 (next free numbers); amend 0.5.9, 0.5.50, 0.5.60, 0.5.61, 0.5.63, 0.5.12 per §6. |
|
||
| Plan 0.3 | Remove "fuel price variation" from deferred (now fixed per-location prices). Price drift and HQ fuel storage remain deferred. |
|
||
| Plan 2.1, 2.2 | Location gains coordinates, fuel price, `sellsFuel`; loses astronomy body and launch cost multiplier. ShipModel gains tank capacity and burn rate; loses fuel range and fuel cost. |
|
||
| Plan 3.3, 3.4 | Static positions and distances (14.2). |
|
||
| Plan 3.6 | Fuel cost per trip replaced by tank burn and refuel (14.4). |
|
||
| Plan 3.12 | Validation gains `not_enough_fuel` (14.4). |
|
||
| Plan 9.5, 9.6, 9.9, 10.x | Fuel UI and fixed map (14.7, 14.8). |
|
||
| CR-006 | Extended Fuel Tanks raises tank capacity; caps restated as yes/no. |
|
||
| CR-007 | Pilot pay uses leg fuel cost (CR-014.D6). |
|
||
| CR-009 | Reach table replaced by §4; no-strand rule amended; Market and map reach text without dates. |
|
||
| `tech-stack.md` | Remove `astronomy-engine` (Planetary Positions section superseded by fixed coordinates). |
|
||
|
||
## 9. Out of scope
|
||
|
||
Fuel price drift and depot shortages, fuel storage at HQ bays, fuel mass, throttle settings, refueling time, locations that don't sell fuel (the flag exists, but all five sell), a "refuel only below a price" policy, and anything else in `implementation-plan.md` §0.3.
|