deadhead-space-freight-tycoon/memory-bank/change-requests/CR-021-independent-pilots.md

184 lines
17 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-021 — Independent Pilots
> **Numbering note (added when recorded):** this request arrived titled "CR-019 — Independent Pilots" for "Phase 19". CR-019 (completable boards) and Phase 19 already existed, and CR-020 / Phase 20 went to the Bridge dashboard and HQs tab (recorded first, in another session), so it is stored as **CR-021** and done as **Phase 21**: every "19.n" below means **21.n**. (It was first recorded here as CR-020; its first five steps were committed as "Step 20.1"–"Step 20.5" before the clash was noticed, and are Steps 21.1–21.5.) Its references map to this repo as: "Phase 13 and CR-001 through CR-018" = Phase 13 and **CR-001 to CR-019**; "CR-001" (goals, Step 10A.5) and "CR-004" (pilot rest) are the same numbers; "CR-005 cargo goods" = **CR-007**; "CR-007 veteran pilot" (profit-share pay) = **CR-010**; "CR-011 local-first" = **CR-014**, and its "10K.6" resolution loop is **Step 14.6**; "CR-014 fixed map and fuel tanks" = **CR-015**; "CR-016" (the Pilots tab badge) = **CR-017**; "CR-017 fuel tuning" = **CR-018**; "CR-018 completable jobs" = **CR-019**. Its new decisions CR-019.D1–D9 are recorded as **0.5.122 Owner-operator, 0.5.123 Dispatch Office, 0.5.124 Modes and orders, 0.5.125 Job choice, 0.5.126 Player priority, 0.5.127 Fuel for pilots, 0.5.128 Idle behavior, 0.5.129 Deterministic catch-up, 0.5.130 No independent-trip notifications** (0.5.118–0.5.121 are the Bridge CR-020's).
>
> **On recording:** "HQ" means a player bay (plan 0.5 bays); the HQ screen is the app's HQ tab. "Game minutes" convert to real time by `timeCompression` (60 in this build), so the default 15-game-minute turnaround is 15 real seconds.
**Project:** Deadhead: Space Freight Tycoon
**Applies to:** a build with Phase 13 and CR-001 through CR-018 complete
**Do this:** as **Phase 19**
**Related docs:** `change-request-pilot-rest.md` (CR-004), `change-request-cargo-goods.md` (CR-005), `change-request-veteran-pilot.md` (CR-007), `change-request-local-first.md` (CR-011), `change-request-fixed-map-and-fuel-tanks.md` (CR-014), `change-request-fuel-tuning.md` (CR-017), `change-request-completable-jobs.md` (CR-018), `game-design-document.md`, `DECISIONS.md`
This change request is written for the local-first engine (CR-011). It turns the "NPC pilots" ideas from the design theorizing sessions into a first, buildable version.
---
## 1. Why
Today every job is dispatched by hand, so the fleet stops when the player puts the phone down. This change lets **hired pilots run jobs on their own**, so the company keeps earning while the player is away: closer to an idle game, without losing the hands-on dispatching.
Settled in the design sessions:
- **The player stays an owner-operator.** The avatar is never autonomous. The player still flies, buys and upgrades ships, and spends every pilot's skill points.
- **Hired pilots only fly jobs.** They don't buy ships, upgrade ships, or spend skill points. They still **earn XP** from the work they do; the player decides how to spend it.
- **Rest is the soft cap.** Independent pilots follow the existing duty and rest rules (CR-004), so a fleet left overnight works a shift, rests, and works again. No arbitrary offline timer.
- **The player gets first look.** New jobs on a board are the player's alone for a few minutes before pilots can take them, and first-contact (discovery) jobs are the player's only.
- **Everything is deterministic.** Pilots' decisions are replayed on the device when the app opens, from the same state and boards, so results are the same however the time is split up.
## 2. How it works
### Unlocking: the Dispatch Office
- A one-time company purchase from the HQ screen: **Dispatch Office** (price in content; start around the price of a Lighter). Until it's bought, pilots can't be set to Independent.
- It's also a money sink and a mid-game goal.
### Pilot modes
| Mode | Behavior |
|---|---|
| **Manual** (default) | Current behavior. The pilot only flies when the player dispatches them. |
| **Independent** | Whenever the pilot is idle, rested, and crewing an idle ship that isn't in the shop, they choose and fly a job from the board where they are, following their orders. |
- The avatar can't be set to Independent.
- The player can still dispatch an Independent pilot by hand when they're idle; the player's action always wins.
- Switching to Manual takes effect when the pilot's current trip ends.
### Orders (set per pilot by the player)
| Order | Default |
|---|---|
| Allowed destinations | All discovered locations |
| Allowed cargo classes | Standard, Heavy, High-Value, Dangerous (those the pilot is skilled for anyway) |
| Take Priority jobs | Yes |
| Minimum profit per job | 0 cr |
| Home HQ | The HQ where they were hired, or Earth |
### Choosing a job
When an Independent pilot can act, they look at the board at their location (CR-018: it only lists jobs the fleet can do) and consider jobs that:
- this pilot and ship can complete right now (all dispatch validations, including fuel after an allowed refuel, below);
- pass their orders;
- are **not first-contact jobs** (discovery is the player's);
- are **past the first-look window**: at least the configured number of real minutes (default 3) into the board period;
- aren't being viewed by the player at that moment (live play only, see §2 "While the app is open").
They pick the job with the highest **profit per hour**:
`profit per hour = (payout − fuel cost − pilot share) ÷ trip time in game hours`
Fuel cost uses the tank's average price for fuel already aboard and the location's price for fuel that must be bought. Ties go to the shorter trip, then the lower contract id.
### Fuel
- Pilots follow their ship's refuel policy (CR-017; Manual by default).
- If the best job needs more fuel than is aboard, the pilot buys **only what that leg needs**, as long as company credits stay above the **NPC spending floor** (a company setting, default 500 cr). Otherwise they consider only jobs they can fly on the fuel aboard.
- Pilots never use emergency fuel; that stays a player decision.
### When there's nothing to take
- The pilot **waits** at the location for the next board period (waiting doesn't add duty hours).
- After 3 empty periods in a row away from their home HQ, they **deadhead home** if the ship can reach it with the fuel rules above; otherwise they keep waiting.
### Rest and turnaround
- Duty and rest are unchanged (CR-004). A pilot over the duty limit rests on arrival, then resumes.
- Each new job starts after a **turnaround time** (default 15 game minutes) from the moment the pilot becomes able to act. This is the main balance knob for idle income.
### Pay and XP
- Profit-share pay (CR-007) and loan repayment (CR-001) apply as for any delivery.
- XP is earned as usual; unspent points show on the Pilots tab badge (CR-016).
### Catch-up and while the app is open
- **Catch-up:** when the app opens or resumes, the engine replays everything since the last saved time as an event queue: arrivals, rest endings, shop completions, board period starts, and pilot decision points, processed in time order (ties broken by pilot id). Board generation during catch-up uses the same deterministic rules as live play.
- **Splitting time doesn't change results:** catching up 8 hours at once gives exactly the same state as catching up in 1-hour pieces.
- **Limits:** catch-up covers at most 7 days of absence and runs in chunks so the UI stays responsive, with a "Catching up on your fleet…" indicator when it takes more than a moment.
- **While the app is open:** the resolution loop (CR-011) also runs pilot decisions. The client tells the engine which contract the player has open, and pilots skip it until it's closed.
- **Notifications:** independent trips aren't decided while the app is closed, so they can't be scheduled as notifications ahead of time. No per-trip notifications for independent pilots.
### The logbook
The "while you were away" screen gains a **per-pilot logbook**: jobs flown (good, route, payout, pilot share, fuel), deadheads, rests, XP gained, and where the pilot is now, with company totals at the top.
## 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`.
- **Pilots use the same commands as the player.** An independent dispatch goes through the normal dispatch validation and command path; nothing bypasses rules.
- **Job choice is a pure function** in `packages/sim` (state and board in, chosen action out), so it can be tested in isolation.
- Save migration: every pilot starts in Manual with default orders; the company has no Dispatch Office; the NPC spending floor is set to its default.
- Update `architecture.md` and `progress.md` as you go. Stop for the user's review at the end of Phase 19.
## 4. Decisions
Record these in `DECISIONS.md` and `implementation-plan.md` §0.5, using the next free numbers (labeled CR-019.D1–D9 here; note the mapping).
- **CR-019.D1 Owner-operator.** The avatar is never autonomous. Hired pilots never buy, upgrade, or spend points; they do earn XP.
- **CR-019.D2 Dispatch Office.** One-time company purchase required before any pilot can be Independent.
- **CR-019.D3 Modes and orders.** Manual (default) or Independent, with per-pilot orders per §2.
- **CR-019.D4 Job choice.** Highest profit per hour among eligible jobs, per §2.
- **CR-019.D5 Player priority.** First-look window (default 3 real minutes into each board period); first-contact jobs are player-only; jobs the player is viewing are skipped.
- **CR-019.D6 Fuel for pilots.** Buy only what the chosen leg needs, never below the NPC spending floor; never emergency fuel.
- **CR-019.D7 Idle behavior.** Wait for the next board; after 3 empty periods away from home HQ, deadhead home if possible.
- **CR-019.D8 Deterministic catch-up.** Event-queue replay on open/resume, at most 7 days, results independent of how time is split.
- **CR-019.D9 No independent-trip notifications.**
Removes "automation / standing routes and auto-accept rules" from the deferred list (`implementation-plan.md` §0.3); standing orders remain a possible later addition.
## 5. Steps — Phase 19
### Step 19.1 — Content and config
- Add: Dispatch Office price, first-look window (real minutes), turnaround time (game minutes), empty periods before heading home (3), maximum catch-up window (7 days), default NPC spending floor (500 cr).
- **Test:** Config validates; all values positive; the first-look window is shorter than the board period.
### Step 19.2 — Sim: eligibility and job choice
- Pure functions: which jobs on a board an Independent pilot may consider (§2 filters); profit per hour for a job given the ship's fuel and the location's price; the pilot's chosen action (take a job, refuel-then-take, wait, or deadhead home).
- **Test:** First-contact jobs are never chosen. Jobs inside the first-look window aren't chosen; the same job is after the window. Jobs outside the orders (destination, class, Priority, minimum profit) aren't chosen. The highest profit-per-hour job is chosen, with the documented tie-breaks. A pilot who can only afford fuel above the spending floor picks among jobs they can fly on fuel aboard. With nothing eligible, the result is wait; after 3 empty periods away from home, deadhead home when reachable.
### Step 19.3 — Engine: Dispatch Office, modes, orders
- Commands: buy Dispatch Office, set pilot mode, set pilot orders, set NPC spending floor. Save fields and migration per §3.
- **Test:** Setting Independent fails before the Dispatch Office is bought and succeeds after. The avatar can't be set to Independent. Orders save and load. An older save migrates with all pilots on Manual and no Dispatch Office.
### Step 19.4 — Engine: event-queue catch-up
- Extend the resolution loop (CR-011 Step 10K.6) into a time-ordered event queue covering arrivals, rest endings, shop completions, board period starts, and Independent pilots' decision points (each decision point is turnaround time after the pilot becomes able to act). Each pilot action is executed through the normal engine commands.
- Cap catch-up at 7 days; process in chunks with a progress indicator hook.
- **Test:** Determinism: from the same save, catching up 8 hours in one go and in eight 1-hour pieces gives identical saves. Two Independent pilots at the same location never take the same contract; the one whose decision comes first (then lower pilot id) gets it. A pilot over the duty limit rests on arrival and resumes after rest. Catch-up past 7 days stops at 7 days. No pilot is ever dispatched in a way that fails normal validation.
- **Amended by CR-024:** Independent pilots start no new work at or after the fleet's sleep time (the last manual decision plus the threshold); arrivals, rest and shop ends still resolve, and the next manual decision wakes them from that moment.
- **Amended by CR-026:** Independent pilots' trips get the same events, radio calls and defaults; the player can answer their calls too.
### Step 19.5 — Engine: live play
- While the app is open, the loop runs pilot decisions in real time. Add a client-to-engine signal for the contract currently open in the detail view; pilots skip it.
- **Test:** With a contract marked as being viewed, an eligible pilot picks a different job; once the view closes, the contract becomes eligible again.
### Step 19.6 — Client: Dispatch Office and pilot controls
- HQ screen: Dispatch Office purchase with a short explanation ("Lets your hired pilots run jobs on their own").
- Pilots screen: per-pilot Manual/Independent toggle (disabled with a reason before the Dispatch Office, hidden for the avatar), an orders editor (destinations, cargo classes, Priority, minimum profit, home HQ), and a status line ("Independent: flying Medical Supplies to the Moon", "Waiting for new contracts at Mars", "Resting until 14:20").
- Settings → Company: NPC spending floor.
- Fleet cards show an "Independent" badge on ships crewed by Independent pilots.
- **Test:** Component tests: the toggle is disabled with a reason before purchase and hidden for the avatar; orders edits call the engine; each status line renders from seeded state; the badge appears only for Independent crews.
### Step 19.7 — Client: board and logbook
- Contract cards inside the first-look window show a small chip: "Yours for 2:31".
- Catch-up shows "Catching up on your fleet…" when it takes more than half a second.
- The "while you were away" screen gains the per-pilot logbook from §2, with company totals.
- **Test:** Component tests: the chip counts down and disappears when the window ends; the indicator appears for slow catch-ups only; the logbook lists each pilot's jobs, deadheads, rests, XP, and current location, and totals match the sum of entries.
- **Amended by CR-024:** The "while you were away" screen opens with a banner when the fleet went quiet ("Your fleet went quiet on [date, time] after [24 h] without orders.") and a Back to work button; sleeping Independent pilots read "Waiting for orders".
- **Amended by CR-025:** The per-pilot logbook moves from the pop-up to the Logbook screen, built from logbook entries and covering every pilot (Step 25.4).
### Step 19.8 — Balance
- Extend the Phase 12 balance script with an "away" scenario: the player sets all hired pilots to Independent and closes the app for 8 real hours.
- Target: the independent fleet earns **40–80%** of what the Phase 12 active-play policy earns with the same fleet over the same time. Tune turnaround time first, then the first-look window.
- **Test:** The away scenario meets the target with default content. No simulated pilot ever buys fuel below the spending floor or gets stranded.
### Step 19.9 — Plan amendments and regression
- Apply the amendments in §6.
- Run the full suite.
- **Test:** CI is green. Manual check: buy the Dispatch Office, set Rosa to Independent with default orders, close the app for an hour, reopen, and read her logbook; then confirm a first-contact job stayed on the board for you.
## 6. Amendments
| Where | Change |
|---|---|
| Plan 0.3 | Remove automation/standing routes from deferred; standing orders, pilot traits, decision events, and a hiring market remain deferred. |
| Plan 0.5 | Add CR-019.D1–D9. |
| CR-001 goals | Optional later: a goal for buying the Dispatch Office (not part of this change). |
| CR-011 10K.6 | Resolution loop becomes the event-queue catch-up (19.4). |
| CR-018 | Pilots choose only from completable boards; no change to board rules. |
| Plan 12.1 | Away scenario added (19.8). |
| `game-design-document.md` | Pilots and automation sections updated by the designer. |
## 7. Out of scope
Standing orders (repeat routes), personality traits, decision events that wait for the player, a hiring market with varied candidates, pilots looking ahead to the destination's backhaul, pilots using emergency fuel, a premium contract tier, ship wear, and anything else in `implementation-plan.md` §0.3.