Files
SpaceWanderer/docs/superpowers/specs/2026-10-01-crew-driven-actions-design.md
T

122 lines
12 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.
# Crew-Driven Actions & Skill Progression — Design Spec
- Date: 2026-10-01
- Status: approved
- Tracking: Gitea issues #43–#45 (created after spec approval)
- Repo rules: AGENTS.md change process applies to every issue below
## 1. Background
SpaceWanderer is a single-player, turn-based space adventure (dungeon-crawler flavor; reference: Out There). As of v0.9.12 the loop is jump → scan planets → collect hauls → spend on ship upgrades & research → take missions for generic rewards. Every action resolves instantly and costs exactly 1 turn (jumps cost hop count, 1–2 with NAV AI tech). Crew members carry a role and two traits but change nothing mechanically — ORCHESTRATOR.md's "manage crew of flawed humans" is unimplemented.
This iteration makes **crew the central resource**: every action requires an assigned team, takes variable duration based on that team's roles/skills/traits, and time advances only when the player presses NEXT TURN. Doing jobs builds skill levels, so crews specialise over a playthrough.
## 2. Goals / Non-goals
Goals:
1. Every game action (jump, explore, ship work, research) becomes a queued task with an assigned team (lead + optional helpers).
2. Duration is computed from work points ÷ team power, modified by role mismatch and the lead's traits — min 1 turn.
3. Manual time: NEXT TURN button (bottom-right) advances exactly 1 turn; empty queue = free skip.
4. Skill progression: 4 skills keyed by action type; XP from completed tasks; levels 0–5 with power bands; role-based starting specialisation; titles at L3/L5.
5. All 20 traits get mechanical meaning (duration modifiers now; two outcome effects in #43; three combat traits marked STANDBY for the future encounters iteration).
Non-goals (explicitly out of scope):
- Random encounters, combat, damage, death (next roadmap item per ORCHESTRATOR.md §7)
- Real-time mode — but the work-point model is time-independent by design to ease that migration later
- Crew injury/departure, supplies/hunger systems, task cancellation
## 3. Core Model: Queued Tasks + Turn Tick
- New `tasks` table (schema v7): one row per queued/active task. Fields: id, saveId, type (`JUMP|EXPLORE|SHIP_UPGRADE|SHIP_EXPAND|SHIP_BUILD|RESEARCH`), payload (JSON — targetSystemId / planetId / systemType+action / techSlug), leadCrewId, helpers (JSON array of crew ids), duration, remaining, status (`ACTIVE|PENDING|DONE`).
- **One active task per crew member** (as lead or helper). A crew member may hold a chain of PENDING tasks (FIFO pre-queueing); when their current task completes on a turn tick, the next pending auto-starts. Helper slots are fixed at queue time; a committed helper is busy for that task's duration and cannot be added elsewhere meanwhile.
- **Costs deduct at queue time** (ship work & research consume resources/parts immediately — prevents mid-turn affordability races); effects apply on completion. Jumps/explores have no resource cost.
- `save.turn` increments only via `POST /api/turn`. The tick: for every ACTIVE task, remaining−=1; tasks reaching 0 resolve (per-type resolvers reusing existing logic from save.ts / ship / research), grant XP (§6), write events, free the team, auto-start any FIFO successor, then run mission-completion checks for what just resolved.
- **In-transit rule:** while any JUMP task is ACTIVE, no new tasks can be queued (409). Pending jumps do not block; already-active/pending non-jump work continues through the jump and resolves against its target regardless of current system location. Only one JUMP may exist at a time (active or pending) — prevents two arrivals on one tick.
- **Mission accept becomes instant/free** (paperwork: no crew, no turn cost). Completion checks run when the satisfying task completes (jump arrival → DELIVER; scan → EXPLORE/FIND), same as today's hooks moved into tick resolution.
## 4. Duration Model: Work Points & Team Power
`duration = max(1, ceil(work(action) / totalPower) + mismatchPenalty + traitModifierSum)`
- `work(action)` — work points (balance knobs, §11): EXPLORE=4 · JUMP=hops×3 (single 3 / two-hop 6) · SHIP work=6 · RESEARCH=9. Durations are deliberately much longer than today; teams and skill levels claw back the time.
- `totalPower` — sum over lead + helpers of each member's power band in that action type's skill: L0–L2 → 1, L3–L4 → 2, L5 → 3. (Until #45 lands there is no skill table, so every crew member contributes power 1 — teamwork already works; progression layers on top.)
- `mismatchPenalty` — +1 turn if the LEAD's role is not a preferred role for the action type (preferred: JUMP → PILOT/NAVIGATOR · EXPLORE → SCIENTIST · SHIP work → ENGINEER/TECHNICIAN · RESEARCH → SCIENTIST). Helpers are never penalised. STEADY UNDER PRESSURE cancels it.
- `traitModifierSum` — sum of the LEAD's trait modifiers for this action type (§5 table). Helper traits do not apply.
Solo durations (power 1, no mismatch/traits): EXPLORE 4 · JUMP single 3 / two-hop 6 · SHIP work 6 · RESEARCH 9. A maxed specialist + helpers converges back to ~1–2 turns (§6 examples).
## 5. Trait Mechanics — all 20 traits
Duration modifiers (lead only, additive in §4 formula):
| Good trait | Effect |
|---|---|
| STEADY UNDER PRESSURE | Cancels role-mismatch penalty on any action |
| RECKLESS COURAGE | JUMP −1 turn |
| CALM IN A CRISIS | EXPLORE −1 turn |
| FIXES ANYTHING | SHIP work −1 turn |
| READS STARS BLIND | JUMP −1 turn |
| QUICK HANDS | Any action −1 turn |
| IRON WILL | RESEARCH −1 turn |
| Bad trait | Effect |
|---|---|
| RIGID — SLOW TO ADAPT | RESEARCH +1 turn |
| OVERCAUTIOUS | Any action +1 turn |
| SLOWS THE CREW DOWN | Any action +1 turn |
| CARELESS WITH FUSES | SHIP work +1 turn |
| HOARDS RATIONS | SHIP work +1 turn |
| FORGETS TO EAT | EXPLORE +1 turn |
| ARGUES WITH EVERYONE | RESEARCH +1 turn |
| DOUBTS THE CAPTAIN | JUMP +1 turn |
Outcome effects (#43): FINDS HIDDEN THINGS → exploration hauls gain +1 item (extra roll on the same seeded per-planet stream) · TALKS TO ANYONE → crew-recruit chance 18% → 30%.
Encounter-ready STANDBY (activate when random encounters land; shown as "STANDBY" in UI): SHARP SHOOTER, SHORT FUSE, JINXED IN A DOGFIGHT.
## 6. Skill Progression & Specialisation
- **Skills** — one per action type: NAVIGATION (jumps), SCANNING (exploration), ENGINEERING (ship work), RESEARCH. Stored in a new `crew_skills` table (schema v8): saveId, crewId, skill, level, xp — extensible for future COMBAT/MEDICAL skills.
- **Roles keep their names** — the existing 8 roles are unchanged; skills are a separate progression axis. A role matters only for: starting levels and the lead mismatch penalty.
- **Starting specialisation (role → L2 in its skill(s)):** PILOT/NAVIGATOR → NAVIGATION · SCIENTIST → SCANNING + RESEARCH · ENGINEER/TECHNICIAN → ENGINEERING · COMMANDER/MEDIC/GUNNER → all skills L0 (generalists; MEDIC/GUNNER become relevant with encounters).
- **Buildup by doing the job:** on task completion, every assigned crew member (lead and helpers) gains +1 XP in that action type's skill. Level thresholds (cumulative completions): L1=5 · L2=10 · L3=15 · L4=20 · L5=25 (cap).
- **Power bands:** L0–L2 → 1 · L3–L4 → 2 · L5 → 3 (§4).
- **Titles** (display only, role name preserved): highest skill level ≥5 → "MASTER <ROLE>", ≥3 → "SENIOR <ROLE>". Level-ups write an event ("BEN OKONKWO is now a SENIOR PILOT — NAVIGATION L3").
Duration examples (work values §4): EXPLORE solo rookie 4 turns → solo master 2 → master+rookie helper 1 · JUMP two-hop solo rookie 6 → solo master 2 → two masters 1 · SHIP work solo rookie 6 → solo master 2 · RESEARCH solo rookie 9 → solo master 3 (stays the long pole by design).
## 7. API Changes
- All action endpoints become queueing operations and gain crew params: `POST /api/jump {targetSystemId, leadCrewId, helperCrewIds[]}` · `POST /api/explore {planetId, ...}` · `POST /api/ship/{upgrade|expand|build} {...}` · `POST /api/research {techSlug, ...}`. Validation at queue time: unknown crew → 404; busy/committed crew → 409; in-transit or second jump → 409; unreachable target / unaffordable cost / build gate → existing error codes. No turn increment on queue.
- New `POST /api/turn` — advances exactly one turn (§3 tick), returns full snapshot.
- Snapshots (world/system/crew responses) gain an active+pending `tasks` array: {id, type, targetLabel, leadName, helperNames[], remaining} so the UI can render the task strip and disable busy crew. The admin CRUD panel picks up new tables automatically.
- OpenAPI spec in lib/openapi.ts updated accordingly; DDL in lib/db/client.ts kept in sync with lib/db/schema.ts (repo rule).
## 8. UI
- **NEXT TURN button fixed bottom-right** with current turn counter ("TURN 12") — the game's heartbeat; pressing it with an empty queue is a free skip.
- **Crew picker:** action buttons open an inline team builder — pick lead (radio), toggle helpers (checkboxes); live-computed duration updates as the team changes ("Lead AVA VANCE · PILOT L2 + 1 helper → 3 turns"); busy/committed members disabled with reason.
- **Active-tasks strip** (bottom-left): each task's type, target, team, remaining turns; pending FIFO tasks shown dimmed.
- **CREW tab:** per member — role (+title), traits, and the 4 skill levels; STANDBY traits labelled as encounter-ready.
- **Event log:** task-completion and level-up events.
## 9. Determinism & Testing
- Duration is a pure function `(actionType, hopCount, lead, helpers) → number` — unit-tested across work values × power bands × mismatch × trait matrix.
- XP/level-ups are deterministic (no rng). Trait outcome effects (#43) draw from the existing seeded per-planet streams (same save+planet → same haul), keeping everything reproducible.
- Unit tests (colocated *.test.ts): duration matrix; queue validation rules (busy 409, in-transit 409, second jump 409); tick resolution (completion effects, XP grant, level-up event, FIFO auto-start, mission checks); cost deduction at queue time.
- E2E (CI-only per AGENTS.md): assign team → NEXT TURN ×N → effect lands; parallel tasks complete independently; pre-queued FIFO starts after predecessor completes; in-transit queuing blocked; empty-queue skip works.
## 10. Delivery Plan — Gitea issues as TODO list
One issue = one feature branch/PR/version bump, per AGENTS.md loop (branch off master → version bump + CHANGELOG entry → local verify `tsc --noEmit` / `vitest run` / `playwright test --list` → push, CI auto-merge on green → tag → manual deploy → close issue with comment).
| Issue | Version | Scope |
|---|---|---|
| #44 Crew-driven actions + NEXT TURN engine | v0.10.0 | tasks table (schema v7; backfill creates empty table for existing saves), POST /api/turn tick, all action endpoints → queueing with lead+helpers, duration model (§4; power 1 for everyone until #45), trait duration modifiers (§5), UI: NEXT TURN button bottom-right + turn counter, crew picker with live duration, task strip. OpenAPI update. Rework existing unit/e2e assertions from instant-resolution to the queue+tick model |
| #45 Skill progression & specialisation | v0.11.0 | crew_skills table (schema v8; idempotent backfill seeds rows per role mapping for existing saves), XP on completion for all assigned crew, level-up events, power bands active (§6), UI: skill display in CREW tab + titles, picker shows per-member contribution |
| #43 Trait outcome effects | v0.12.0 | FINDS HIDDEN THINGS → hauls +1 item; TALKS TO ANYONE → recruit chance 30% |
## 11. Balance Knobs (single tuning surface)
All constants live in one new module `lib/game/crew-costs.ts` (alongside ship-costs.ts): WORK_POINTS per action type, POWER_BANDS, XP_THRESHOLDS, STARTING_LEVELS per role, recruit chance values. Tuning a number = editing one file; the duration formula and resolvers read from it.