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

12 KiB
Raw Permalink Blame History

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 ", ≥3 → "SENIOR ". 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.