133 lines
11 KiB
Markdown
133 lines
11 KiB
Markdown
# Change Request CR-004 — Pilot Duty and Rest
|
||
|
||
**Project:** Deadhead: Space Freight Tycoon
|
||
**Applies to:** a build with Phases 1–10, CR-001, CR-002, and CR-003 complete
|
||
**Do this:** as **Phase 10D**, after Phase 10C and before Phase 11
|
||
**Related docs:** `implementation-plan.md`, `change-request-faster-start.md`, `change-request-open-markets.md`, `change-request-map-art.md`, `DECISIONS.md`
|
||
|
||
> **Numbering note (added when implemented):** the plan's §0.5 already uses 34–41. This CR's decisions 0.5.34–0.5.38 are recorded as **0.5.42 Duty clock**, **0.5.43 Duty limit**, **0.5.44 Finish the job**, **0.5.45 Rest** and **0.5.46 Rest covers a board refresh**. The error code `pilot_resting` is `pilot-resting` (the API's kebab-case convention).
|
||
|
||
---
|
||
|
||
## 1. Why
|
||
|
||
Pilots can currently fly back-to-back forever, so there is no rhythm and no reason to run more than one pilot per ship. When a location's board has nothing useful, the only options are to wait with nothing to show for it or to deadhead.
|
||
|
||
This adds an ATS-style hours-of-service rule:
|
||
|
||
- Each pilot has a **duty clock** that fills up as they fly.
|
||
- When it's full, the pilot must **rest** for a fixed time on arrival. The ship stays put unless another pilot takes it.
|
||
- The player can also **rest a pilot early**. Rest always lasts at least one contract board period, so **a rested pilot always wakes up to a fresh board.**
|
||
- The **Long Haul** skill extends a pilot's duty limit, so leveling it changes the math, not just which jobs unlock.
|
||
|
||
## 2. How it works
|
||
|
||
| Rule | Detail |
|
||
|---|---|
|
||
| Duty clock | Each pilot tracks duty hours (game hours flown since their last rest). |
|
||
| What counts | Every contract and deadhead trip adds its full travel time. Tutorial runs do not count. |
|
||
| Duty limit | Base limit from config, plus a bonus per Long Haul level. |
|
||
| Starting a trip | Allowed whenever the pilot is not resting, even if the trip will take them over the limit. Pilots always finish the job they're on. |
|
||
| Mandatory rest | If duty hours are at or over the limit when a trip resolves, rest starts automatically at the arrival time. |
|
||
| Voluntary rest | The player can rest any idle pilot who isn't in flight, at any duty level. |
|
||
| Rest | Lasts the configured rest duration. When it starts, duty hours reset to zero; the pilot can't fly until it ends. Rest happens wherever the pilot is; no bay is needed. |
|
||
| Crew swap | A resting pilot can be unassigned (at a bay, per decision 0.5.2), and a rested pilot can be assigned to the same ship. Rest blocks the pilot, not the ship. |
|
||
| Fresh board | The rest duration in real time is at least the contract board period, so any rest crosses at least one board refresh. |
|
||
|
||
Example with defaults (12-hour limit, 1 real minute = 1 game hour): a pilot flies three ~4-hour Moon runs, rests about 15 real minutes, and wakes to a new board. A Mars run (60+ game hours) always ends in a rest, which is negligible next to the trip itself.
|
||
|
||
## 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`.
|
||
- Rest is derived from stored timestamps, like dispatches. **No background job is needed to end a rest**: a pilot is resting if the current time is before their rest-ends-at time.
|
||
- 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 10D.
|
||
|
||
## 4. Decisions
|
||
|
||
Record these in `DECISIONS.md`.
|
||
|
||
- **0.5.34 Duty clock.** Pilots accrue duty hours equal to each trip's travel time in game hours, for contracts and deadheads. Tutorial runs accrue nothing.
|
||
- **0.5.35 Duty limit.** Base duty limit plus a per-level Long Haul bonus, both in config.
|
||
- **0.5.36 Finish the job.** Dispatch is blocked only while a pilot is resting. A trip that will exceed the limit is allowed; mandatory rest starts on arrival.
|
||
- **0.5.37 Rest.** Starting rest sets duty hours to zero and sets rest-ends-at to start time plus the rest duration. Mandatory rest starts at the trip's arrival time, so it is correct even if resolution happens later (offline).
|
||
- **0.5.38 Rest covers a board refresh.** The rest duration converted to real time must be at least the contract board period. This is enforced by a content test.
|
||
|
||
## 5. Steps — Phase 10D
|
||
|
||
### Step 10D.1 — Content config
|
||
- Add GameConfig fields: base duty limit (game hours, default 12), Long Haul duty bonus per level (game hours, default 2), rest duration (game hours; default equal to the board period converted to game hours).
|
||
- **Test:** Config validates. A test converts the rest duration to real time with the current compression and asserts it is at least the board period. Both duty numbers are positive.
|
||
|
||
### Step 10D.2 — Sim: duty and rest functions
|
||
- Add pure functions:
|
||
- Duty limit for a pilot, from their Long Haul level.
|
||
- Whether a pilot is resting at a given real time.
|
||
- Duty hours after a trip (current duty plus trip travel time, unless it's a tutorial run).
|
||
- Whether a pilot needs mandatory rest after a trip.
|
||
- Rest window from a start time (rest-ends-at).
|
||
- **Test:** Duty limit rises by the bonus for each Long Haul level. A pilot is resting one second before rest-ends-at and not resting at it. A tutorial trip leaves duty unchanged. A pilot at 10 of 12 hours who flies a 4-hour trip needs rest; one at 4 of 12 who flies a 4-hour trip does not. For many random start times, a rest window always contains at least one contract board period boundary.
|
||
|
||
### Step 10D.3 — Sim: validation and resolution
|
||
- Dispatch validation gains a `pilot_resting` reason, checked before the other pilot checks.
|
||
- Dispatch resolution returns the pilot's new duty hours and, if mandatory rest applies, a rest-ends-at time computed from the arrival time.
|
||
- **Test:** A resting pilot fails validation with `pilot_resting`. A pilot whose duty is over the limit but not resting can still be dispatched. Resolving a trip that pushes duty over the limit returns duty zero and rest-ends-at equal to arrival time plus rest duration, even when resolved hours after arrival. Deadheads accrue duty; tutorial runs don't.
|
||
|
||
### Step 10D.4 — Schema and database
|
||
- Add to pilots: duty hours and rest-ends-at (nullable).
|
||
- Add the `pilot_resting` error code and a "rest pilot" endpoint to the route catalog.
|
||
- Player state shows, per pilot: duty hours, duty limit, resting (yes/no), and rest-ends-at.
|
||
- Dispatch summaries include a "rest started" note when a trip ended in mandatory rest.
|
||
- 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 10D.5 — Server: apply duty and rest
|
||
- Resolution (lazy and job-based) writes the pilot's new duty hours and rest-ends-at.
|
||
- The rest endpoint starts rest now for an idle pilot who is not in flight and not already resting. It works at any duty level.
|
||
- Dispatch returns `pilot_resting` for a resting pilot.
|
||
- Assigning and unassigning ignore rest (crew swaps are allowed); only flying is blocked.
|
||
- **Test:** A pilot who chains Moon runs until over the limit is resting after the last one resolves, and dispatch with them fails with `pilot_resting`. After the rest window passes, the same dispatch succeeds. Resting a pilot by hand sets duty zero and the correct rest-ends-at; resting an in-flight or already-resting pilot fails. A resting pilot can be unassigned at a bay, and a rested pilot assigned to that ship can dispatch immediately.
|
||
|
||
### Step 10D.6 — Client: pilot duty on the Pilots screen
|
||
- Each pilot shows a duty bar (hours used of limit). Resting pilots show "Resting" with a countdown instead.
|
||
- Idle, non-resting pilots have a "Rest now" action with a confirmation that says rest lasts at least one board refresh.
|
||
- **Test:** Component test: the duty bar matches state; a resting pilot shows the countdown and no Rest now action; Rest now calls the endpoint.
|
||
|
||
### Step 10D.7 — Client: duty in the dispatch flow
|
||
- In the dispatch sheet, pairs with a resting pilot are disabled with "Resting — mm:ss".
|
||
- For other pairs, show the pilot's duty after the trip. If it reaches the limit, show "Will need rest on arrival".
|
||
- **Test:** Component test: a resting pilot's pair is disabled with the countdown; a trip that takes a pilot over the limit shows the warning; one that doesn't, doesn't. Numbers match the sim.
|
||
|
||
### Step 10D.8 — Client: board refresh and the "nothing here" case
|
||
- Every contract board shows "New contracts in mm:ss" until the next board period.
|
||
- If a location has an idle ship but no contract that ship and its pilot are eligible for, show a short prompt with both options: "Rest [pilot] — wakes to a new board" and "Deadhead (fly empty)".
|
||
- **Test:** Component test: the countdown matches the next period boundary for the mocked time; with no eligible contracts, the prompt shows both actions and each calls the right endpoint or opens the deadhead picker.
|
||
|
||
### Step 10D.9 — Client: rest in summaries and on HQ
|
||
- The "while you were away" modal shows "Pilot X is resting until …" for trips that ended in mandatory rest.
|
||
- HQ shows the count of resting pilots and the next pilot to wake up, with a countdown.
|
||
- **Test:** Component test with seeded state: the modal line appears for a summary with rest started; HQ shows the correct count and the soonest wake time.
|
||
|
||
### Step 10D.10 — Plan amendments and regression
|
||
- Apply the amendments in §6 to `implementation-plan.md`.
|
||
- Run the full suite.
|
||
- **Test:** CI is green. Manual check: on a fresh account after the CR-001 deal, chain Moon runs with the avatar until mandatory rest starts; confirm the ship's location board shows a new set of contracts when the avatar wakes.
|
||
|
||
## 6. Amendments to `implementation-plan.md`
|
||
|
||
| Step | Change |
|
||
|---|---|
|
||
| 0.5 | Add decisions 0.5.34–0.5.38. |
|
||
| 2.1 | GameConfig gains duty limit, Long Haul duty bonus, and rest duration (10D.1). |
|
||
| 3.12 | Validation gains `pilot_resting` (10D.3). |
|
||
| 3.17 | Resolution returns duty hours and rest-ends-at (10D.3). |
|
||
| 5.3 | Pilots table gains duty hours and rest-ends-at (10D.4). |
|
||
| 9.5, 9.7 | Duty and rest in the dispatch sheet and Pilots screen (10D.6, 10D.7). |
|
||
| 12.1 | The simulated player follows rest rules: it rests a pilot when no eligible contract exists at its location and deadheading isn't better. Re-check all CR-001 timing targets with rest in place; adjust content values, not thresholds. Add a check that no pilot is ever dispatched while resting. |
|
||
| 12.6 | After leveling Long Haul, confirm the pilot's duty limit increased. |
|
||
|
||
## 7. Out of scope
|
||
|
||
Fatigue penalties, accidents, or slower flying when tired; rest facilities or bay amenities that shorten rest; pilot morale; and anything else in `implementation-plan.md` §0.3.
|