104 lines
8.6 KiB
Markdown
104 lines
8.6 KiB
Markdown
# E7.M1 — QuestStateMachine
|
||
|
||
## Summary
|
||
|
||
| Field | Value |
|
||
|--------|--------|
|
||
| **Module ID** | E7.M1 |
|
||
| **Epic** | [Epic 7 — Quest / Faction](../epics/epic_07_quest_faction.md) |
|
||
| **Stage target** | Prototype |
|
||
| **Status** | Planned — Slice 1 backlog [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md): **E7M1-01** [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) catalog **landed** (schemas + CI); **E7M1-02** [NEO-113](https://linear.app/neon-sprawl/issue/NEO-113) server load **landed**; **E7M1-03** [NEO-114](https://linear.app/neon-sprawl/issue/NEO-114) registry **landed**; **E7M1-04** [NEO-115](https://linear.app/neon-sprawl/issue/NEO-115) HTTP read **landed**; **E7M1-05** [NEO-116](https://linear.app/neon-sprawl/issue/NEO-116) player quest state store **landed**; **E7M1-06** [NEO-117](https://linear.app/neon-sprawl/issue/NEO-117) **`QuestStateOperations`** **landed**; **E7M1-07** [NEO-118](https://linear.app/neon-sprawl/issue/NEO-118) **`QuestObjectiveWiring`** **landed**; **E7M1-08** [NEO-119](https://linear.app/neon-sprawl/issue/NEO-119) per-player GET **landed**; accept + client from **E7M1-09** [NEO-120](https://linear.app/neon-sprawl/issue/NEO-120) → capstone **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) |
|
||
| **Linear** | Label **`E7.M1`** · [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md) |
|
||
|
||
## Purpose
|
||
|
||
Data-driven quest lifecycle: start, step progression, branching, failure/reset, and completion with durable persistence. Integrates crafting and combat outcomes so guided content ties together gather–craft–combat loops without duplicate rewards.
|
||
|
||
**Scope / party:** [Per-character state by default](quest_scope_and_party.md#persistence-per-character-by-default); optional [party credit and turn-in rules](quest_scope_and_party.md#quest-modes-content-driven) via `QuestDef` and [E8.M1](E8_M1_PartyAndMatchAssembly.md).
|
||
|
||
## Responsibilities
|
||
|
||
- Load and interpret `QuestDef`; track `QuestStepState` **per character**; synchronize party-credit advances per [quest_scope_and_party.md](quest_scope_and_party.md).
|
||
- Apply `QuestStateTransition` rules for advance, abandon, fail, and complete.
|
||
- Invoke or subscribe to [E3.M2](E3_M2_RefinementAndRecipeExecution.md) and [E5.M1](E5_M1_CombatRulesEngine.md) (or their events) for step completion predicates.
|
||
|
||
## Key contracts
|
||
|
||
| Contract | Role |
|
||
|----------|------|
|
||
| `QuestDef` | Steps, prerequisites, objectives, and branching metadata. |
|
||
| `QuestStepState` | Current step progress and internal flags. |
|
||
| `QuestStateTransition` | Valid transitions and side effects (before E7.M2 reward routing). |
|
||
|
||
## Module dependencies
|
||
|
||
- **E3.M2** — RefinementAndRecipeExecution: craft completion, item hand-ins, refine steps.
|
||
- **E5.M1** — CombatRulesEngine: defeat objectives, encounter-linked steps.
|
||
|
||
## Dependents (by design)
|
||
|
||
- **E7.M2** — [RewardAndUnlockRouter](E7_M2_RewardAndUnlockRouter.md): idempotent delivery after completion.
|
||
- **E7.M3** — [FactionReputationLedger](E7_M3_FactionReputationLedger.md): reputation-gated content (pre-production).
|
||
- **E5.M3** — [EncounterAndRewardTables](E5_M3_EncounterAndRewardTables.md): encounter completion and quest credit.
|
||
- **E6.M4** — [RewardParityEnforcer](E6_M4_RewardParityEnforcer.md): quest/PvP reward alignment.
|
||
|
||
## Related implementation slices
|
||
|
||
See Epic 7 **Slice 1 — Quest core and persistence**: 3–5 onboarding quests plus one chain across gather/craft/combat; telemetry `quest_start`, `quest_step_complete`, `quest_complete`, funnel times.
|
||
|
||
## Linear backlog (decomposed)
|
||
|
||
Full story tables, kickoff defaults, and **`blockedBy` graph:** [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md).
|
||
|
||
| Slug | Linear |
|
||
|------|--------|
|
||
| E7M1-01 | [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) |
|
||
| E7M1-02 | [NEO-113](https://linear.app/neon-sprawl/issue/NEO-113) |
|
||
| E7M1-03 | [NEO-114](https://linear.app/neon-sprawl/issue/NEO-114) |
|
||
| E7M1-04 | [NEO-115](https://linear.app/neon-sprawl/issue/NEO-115) |
|
||
| E7M1-05 | [NEO-116](https://linear.app/neon-sprawl/issue/NEO-116) |
|
||
| E7M1-06 | [NEO-117](https://linear.app/neon-sprawl/issue/NEO-117) |
|
||
| E7M1-07 | [NEO-118](https://linear.app/neon-sprawl/issue/NEO-118) |
|
||
| E7M1-08 | [NEO-119](https://linear.app/neon-sprawl/issue/NEO-119) |
|
||
| E7M1-09 | [NEO-120](https://linear.app/neon-sprawl/issue/NEO-120) |
|
||
| E7M1-10 | [NEO-121](https://linear.app/neon-sprawl/issue/NEO-121) |
|
||
| E7M1-11 | [NEO-122](https://linear.app/neon-sprawl/issue/NEO-122) |
|
||
| E7M1-12 | [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) |
|
||
|
||
## Prototype Slice 1 freeze (E7M1-01)
|
||
|
||
The **first shipped quest spine** is **frozen** for prototype tuning until a deliberate migration issue expands the roster.
|
||
|
||
| `QuestDef.id` | `displayName` | `prerequisiteQuestIds` | Steps (summary) |
|
||
|---------------|---------------|------------------------|-----------------|
|
||
| **`prototype_quest_gather_intro`** | Intro: Salvage Run | *(none)* | 1× **`gather_item`**: **`scrap_metal_bulk` ×3** |
|
||
| **`prototype_quest_refine_intro`** | Intro: Refine Stock | **`prototype_quest_gather_intro`** | 1× **`craft_recipe`**: **`refine_scrap_standard`** ×1 |
|
||
| **`prototype_quest_combat_intro`** | Intro: Clear the Pocket | *(none)* | 1× **`encounter_complete`**: **`prototype_combat_pocket`** |
|
||
| **`prototype_quest_operator_chain`** | Operator Chain | **`prototype_quest_gather_intro`**, **`prototype_quest_refine_intro`**, **`prototype_quest_combat_intro`** | 4 steps: gather **`scrap_metal_bulk` ×5** → craft **`refine_scrap_standard`** → craft **`make_field_stim_mk0`** → **`inventory_has_item`** **`contract_handoff_token` ×1** |
|
||
|
||
**CI enforcement (NEO-112):** `scripts/validate_content.py` requires **exactly** these four quest ids; objective cross-refs to frozen item/recipe/encounter catalogs; acyclic **`prerequisiteQuestIds`**; chain quest terminal step uses **`contract_handoff_token`** from [E5.M3 encounter loot](E5_M3_EncounterAndRewardTables.md#prototype-slice-3-freeze-e5m3-01).
|
||
|
||
**Server load (NEO-113):** Host fail-fast load of `content/quests/*_quests.json` with the same gates — [server README — Quest catalog](../../../server/README.md#quest-catalog-contentquests-neo-113).
|
||
|
||
**HTTP read model (NEO-115):** **`GET /game/world/quest-definitions`** — `QuestDefinitionsWorldApi` + DTOs in `Game/Quests/` ([NEO-115](../../plans/NEO-115-implementation-plan.md)); [server README — Quest definitions (NEO-115)](../../../server/README.md#quest-definitions-neo-115); Bruno `bruno/neon-sprawl-server/quest-definitions/`.
|
||
|
||
**Player quest state store (NEO-116):** **`IPlayerQuestStateStore`** — in-memory + Postgres (`V008__player_quest_progress.sql`) per-player rows keyed by `(playerId, questId)`; implicit `not_started` when missing ([NEO-116](../../plans/NEO-116-implementation-plan.md)); [server README — Quest progress store (NEO-116)](../../../server/README.md#quest-progress-store-neo-116).
|
||
|
||
**Quest state operations (NEO-117):** **`QuestStateOperations`** — accept, step advance, complete with **`QuestStateReasonCodes`**; prerequisite gate on accept; idempotent complete at operations layer ([NEO-117](../../plans/NEO-117-implementation-plan.md)); [server README — Quest state operations (NEO-117)](../../../server/README.md#quest-state-operations-neo-117). HTTP accept deferred to E7M1-09.
|
||
|
||
**Quest objective wiring (NEO-118):** **`QuestObjectiveWiring`** — gather/craft/encounter success hooks + **`inventory_has_item`** on accept/step advance/inventory mutations; auto advance/complete via **`QuestStateOperations`** ([NEO-118](../../plans/NEO-118-implementation-plan.md)); [server README — Quest objective wiring (NEO-118)](../../../server/README.md#quest-objective-wiring-neo-118).
|
||
|
||
**Per-player quest progress GET (NEO-119):** **`GET /game/players/{id}/quest-progress`** — `QuestProgressApi` + DTOs; registry + store merge; GET-side **`inventory_has_item`** refresh ([NEO-119](../../plans/NEO-119-implementation-plan.md)); [server README — Per-player quest progress (NEO-119)](../../../server/README.md#per-player-quest-progress-neo-119); Bruno `bruno/neon-sprawl-server/quest-progress/`.
|
||
|
||
**Reward policy (Slice 1):** Quest completion updates **`QuestStepState` only** — no duplicate item/XP grants. Gather/craft/encounter paths keep existing payouts; [E7.M2](E7_M2_RewardAndUnlockRouter.md) adds **`QuestRewardBundle`** apply in Slice 2.
|
||
|
||
## Risks and telemetry
|
||
|
||
- Scripting weight: keep `QuestDef` data-first; templates in decomposition/tooling path per epic.
|
||
- Double-claim: design for idempotency before E7.M2; lint bundles vs E6.M4 / E3.M5.
|
||
|
||
## Source anchors
|
||
|
||
- Master plan: [`neon_sprawl_vision.plan.md`](../../../neon_sprawl_vision.plan.md) — Core Epic Map, Epic 7.
|
||
- [Module dependency register](module_dependency_register.md)
|