NEO-76: decompose E5.M1 prototype backlog (NEO-76–NEO-86, NEO-44).
Add full-stack Epic 5 Slice 1 story backlog with Linear issues, blockedBy graph, and module/epic register cross-links.pull/110/head
parent
a38ab660b4
commit
510d71113b
|
|
@ -54,6 +54,8 @@ Deliver tab-target combat resolution, NPC behaviors and telegraphs, encounter te
|
|||
- Combat readable in fixed isometric camera; no silent failures on ability deny.
|
||||
- Telemetry hooks: `encounter_start`, `ability_used`, `player_death`, `enemy_defeat`.
|
||||
|
||||
**Linear backlog:** [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md) — [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); gig XP **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44).
|
||||
|
||||
### Slice 2 - NPC archetypes and telegraphs
|
||||
|
||||
- Scope: E5.M2 for three archetypes: melee pressure, ranged control, elite mini-boss.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
| **Epic** | [Epic 5 — PvE Combat](../epics/epic_05_pve_combat.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | Planned (see [dependency register](module_dependency_register.md)) |
|
||||
| **Linear** | Label **`E5.M1`** · [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md) **E5M1-01** [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → **E5M1-12** [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); gig XP **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44) |
|
||||
|
||||
## Purpose
|
||||
|
||||
|
|
@ -49,6 +50,25 @@ Core tab-target combat resolution: valid actions against a locked target, hit re
|
|||
|
||||
See Epic 5 **Slice 1 — Combat rules MVP**: target lock, basic attacks, 4–6 abilities, cooldowns/resources; telemetry `encounter_start`, `ability_used`, `player_death`, `enemy_defeat`.
|
||||
|
||||
## Linear backlog (decomposed)
|
||||
|
||||
Full story tables, kickoff defaults, and **`blockedBy` graph:** [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md).
|
||||
|
||||
| Slug | Linear |
|
||||
|------|--------|
|
||||
| E5M1-01 | [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) |
|
||||
| E5M1-02 | [NEO-77](https://linear.app/neon-sprawl/issue/NEO-77) |
|
||||
| E5M1-03 | [NEO-79](https://linear.app/neon-sprawl/issue/NEO-79) |
|
||||
| E5M1-04 | [NEO-78](https://linear.app/neon-sprawl/issue/NEO-78) |
|
||||
| E5M1-05 | [NEO-80](https://linear.app/neon-sprawl/issue/NEO-80) |
|
||||
| E5M1-06 | [NEO-81](https://linear.app/neon-sprawl/issue/NEO-81) |
|
||||
| E5M1-07 | [NEO-82](https://linear.app/neon-sprawl/issue/NEO-82) |
|
||||
| E5M1-08 | [NEO-83](https://linear.app/neon-sprawl/issue/NEO-83) |
|
||||
| E5M1-09 | [NEO-84](https://linear.app/neon-sprawl/issue/NEO-84) |
|
||||
| E5M1-10 | [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44) |
|
||||
| E5M1-11 | [NEO-85](https://linear.app/neon-sprawl/issue/NEO-85) |
|
||||
| E5M1-12 | [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86) |
|
||||
|
||||
## Prototype notes (NEO-28)
|
||||
|
||||
Epic 1 **Slice 3** exercises a minimal cast accept/deny surface on **`POST /game/players/{id}/ability-cast`** before the full engine exists: after hotbar/loadout validation ([E1.M4](E1_M4_AbilityInputScaffold.md)), the server requires **`targetId`** to match the persisted combat lock, resolve in the prototype target registry, and sit within horizontal lock radius against authoritative **`PositionState`**. Deny codes **`invalid_target`** and **`out_of_range`** reuse the same strings as E1.M3 **`TargetState.validity`** for vocabulary alignment. **`accepted: true`** still does not imply damage, cooldown commit, or a full **`CombatResolution`** payload — those remain E5.M1 scope.
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ Rows appear when work starts; default for unlisted modules is **Planned** / not
|
|||
| E3.M1 | Ready | **NEO-41 landed (prototype, superseded on interact by NEO-63):** inline XP on **`resource_node`** interact. **NEO-57–NEO-61** — content, registry, HTTP defs, depletion store (see prior alignment). **NEO-62 landed:** **`GatherOperations`** + **`GatherResult`**. **NEO-63 landed:** **`POST …/interact`** **`resource_node`** → gather engine; **four** **`PrototypeInteractableRegistry`** anchors; Bruno gather → inventory + depletion deny ([NEO-63](../../plans/NEO-63-implementation-plan.md), [`NEO-63` manual QA](../../manual-qa/NEO-63.md)); [server README — Gather via interact (NEO-63)](../../../server/README.md#gather-via-interact-neo-63). **NEO-64 landed:** comment-only **`resource_gathered`** / **`gather_node_depleted`** hook sites in **`GatherOperations.TryGather`** ([NEO-64](../../plans/NEO-64-implementation-plan.md), [`NEO-64` manual QA](../../manual-qa/NEO-64.md)); Epic 3 Slice 2 backlog **E3M1-01**–**E3M1-08** complete. **NEO-73 landed:** client gather feedback — **`skill_progression_client.gd`**, **`prototype_interactable_picker.gd`** (nearest in-range **R**), **`GatherFeedbackLabel`** + **`SkillProgressionLabel`**, auto inventory/skill refresh after successful gather ([NEO-73](../../plans/NEO-73-implementation-plan.md), [`NEO-73` manual QA](../../manual-qa/NEO-73.md)); `client/README.md` gather section. **NEO-75 landed:** capstone gather→refine→make loop in Godot — [`NEO-75` manual QA](../../manual-qa/NEO-75.md), `client/README.md` end-to-end section; Epic 3 Slice 5 client complete. | [NEO-41](../../plans/NEO-41-implementation-plan.md) … [NEO-64](../../plans/NEO-64-implementation-plan.md), [NEO-73](../../plans/NEO-73-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M1](E3_M1_ResourceNodeAndGatherLoop.md); `server/NeonSprawl.Server/Game/Gathering/`, `Game/Interaction/`, `client/scripts/skill_progression_client.gd`, `client/scripts/prototype_interactable_picker.gd` |
|
||||
| E3.M3 | In Progress | **NEO-50 landed:** frozen prototype six-item catalog in [`content/items/prototype_items.json`](../../../content/items/prototype_items.json); [`item-def.schema.json`](../../../content/schemas/item-def.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py). **NEO-51 landed:** fail-fast server load of `content/items/*_items.json` at startup — `server/NeonSprawl.Server/Game/Items/` ([NEO-51](../../plans/NEO-51-implementation-plan.md)); [server README — Item catalog](../../../server/README.md#item-catalog-contentitems-neo-51). **NEO-52 landed:** injectable **`IItemDefinitionRegistry`** + lookup tests ([NEO-52](../../plans/NEO-52-implementation-plan.md)). **NEO-53 landed:** **`GET /game/world/item-definitions`** — `ItemDefinitionsWorldApi` + DTOs in `Game/Items/` ([NEO-53](../../plans/NEO-53-implementation-plan.md), [`NEO-53` manual QA](../../manual-qa/NEO-53.md)); [server README — Item definitions (NEO-53)](../../../server/README.md#item-definitions-neo-53); Bruno `bruno/neon-sprawl-server/item-definitions/`. **NEO-54 landed:** **`IPlayerInventoryStore`** + **`PlayerInventoryOperations`** — 24 bag + 1 equipment slots, stack rules, `V005` migration ([NEO-54](../../plans/NEO-54-implementation-plan.md)). **NEO-55 landed:** **`GET`/`POST /game/players/{id}/inventory`** — `PlayerInventoryApi` + DTOs in `Game/Items/` ([NEO-55](../../plans/NEO-55-implementation-plan.md)); [server README — Player inventory (NEO-54 store, NEO-55 HTTP)](../../../server/README.md#player-inventory-neo-54-store-neo-55-http); Bruno `bruno/neon-sprawl-server/inventory/`. **NEO-56 landed:** comment-only **`item_created`** / **`inventory_transfer_denied`** telemetry hook sites in [`PlayerInventoryOperations`](../../../server/NeonSprawl.Server/Game/Items/PlayerInventoryOperations.cs) ([NEO-56](../../plans/NEO-56-implementation-plan.md), [`NEO-56` manual QA](../../manual-qa/NEO-56.md)); no E9.M1 ingest. **NEO-72 landed:** client inventory HUD — **`inventory_client.gd`**, **`item_definitions_client.gd`**, **`InventoryLabel`**, boot hydrate + **`I`** refresh ([NEO-72](../../plans/NEO-72-implementation-plan.md), [`NEO-72` manual QA](../../manual-qa/NEO-72.md)); `client/README.md` inventory HUD section. **NEO-75 landed:** **`prototype_economy_hud_section.gd`** collapse toggle + capstone manual QA ([NEO-75](../../plans/NEO-75-implementation-plan.md), [`NEO-75` manual QA](../../manual-qa/NEO-75.md)); Epic 3 Slice 5 client complete. | [NEO-50](../../plans/NEO-50-implementation-plan.md), [NEO-51](../../plans/NEO-51-implementation-plan.md), [NEO-52](../../plans/NEO-52-implementation-plan.md), [NEO-53](../../plans/NEO-53-implementation-plan.md), [NEO-54](../../plans/NEO-54-implementation-plan.md), [NEO-55](../../plans/NEO-55-implementation-plan.md), [NEO-56](../../plans/NEO-56-implementation-plan.md), [NEO-72](../../plans/NEO-72-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3M3-prototype-backlog](../../plans/E3M3-prototype-backlog.md), [E3_M3](E3_M3_ItemizationAndInventorySchema.md) |
|
||||
| E3.M2 | Ready | **NEO-65 landed:** frozen prototype eight-recipe catalog in [`content/recipes/prototype_recipes.json`](../../../content/recipes/prototype_recipes.json); [`recipe-def.schema.json`](../../../content/schemas/recipe-def.schema.json) + [`recipe-io-row.schema.json`](../../../content/schemas/recipe-io-row.schema.json); PR gate + [`validate_content.py`](../../../scripts/validate_content.py) Slice 3 gates ([NEO-65](../../plans/NEO-65-implementation-plan.md)). **NEO-66 landed:** fail-fast server load of `content/recipes/*_recipes.json` at startup — `server/NeonSprawl.Server/Game/Crafting/` ([NEO-66](../../plans/NEO-66-implementation-plan.md)); [server README — Recipe catalog](../../../server/README.md#recipe-catalog-contentrecipes-neo-66). **NEO-67 landed:** injectable **`IRecipeDefinitionRegistry`** + lookup tests ([NEO-67](../../plans/NEO-67-implementation-plan.md)). **NEO-68 landed:** **`GET /game/world/recipe-definitions`** — `RecipeDefinitionsWorldApi` + DTOs in `Game/Crafting/` ([NEO-68](../../plans/NEO-68-implementation-plan.md), [`NEO-68` manual QA](../../manual-qa/NEO-68.md)); [server README — Recipe definitions (NEO-68)](../../../server/README.md#recipe-definitions-neo-68); Bruno `bruno/neon-sprawl-server/recipe-definitions/`. **NEO-69 landed:** **`CraftOperations.TryCraft`** + **`CraftResult`** — pre-flight inputs/outputs, inventory mutation, refine XP ([NEO-69](../../plans/NEO-69-implementation-plan.md)); [server README — Craft engine (NEO-69)](../../../server/README.md#craft-engine-neo-69-and-craft-http-neo-70). **NEO-70 landed:** **`POST /game/players/{id}/craft`** — `PlayerCraftApi` + wire **`CraftResponse`**; Bruno gather→refine→make spine ([NEO-70](../../plans/NEO-70-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/craft/`. **NEO-71 landed:** comment-only **`item_crafted`** / **`craft_failed`** telemetry hook sites in **`CraftOperations.TryCraft`** ([NEO-71](../../plans/NEO-71-implementation-plan.md)); [server README — Craft telemetry hooks (NEO-71)](../../../server/README.md#craft-telemetry-hooks-neo-71). **NEO-42 landed (prep):** **`RefineActivitySkillXpGrant`** / **`RefineSkillXpConstants`** — wired from craft engine success path. Epic 3 Slice 3 server backlog **E3M2-01**–**E3M2-07** complete. Upstream: E3.M1 **Ready**, E3.M3 inventory landed. **NEO-74 landed:** client craft UI — **`recipe_definitions_client.gd`**, **`craft_client.gd`**, **`craft_recipe_panel.gd`**, **`CraftFeedbackLabel`**, **refine** skill row ([NEO-74](../../plans/NEO-74-implementation-plan.md), [`NEO-74` manual QA](../../manual-qa/NEO-74.md)); `client/README.md` craft section. **NEO-75 landed:** capstone playable gather→refine→make loop — [`NEO-75` manual QA](../../manual-qa/NEO-75.md), **`prototype_economy_hud_section.gd`**; Epic 3 Slice 5 client complete. | [NEO-42](../../plans/NEO-42-implementation-plan.md), [NEO-65](../../plans/NEO-65-implementation-plan.md), [NEO-66](../../plans/NEO-66-implementation-plan.md), [NEO-67](../../plans/NEO-67-implementation-plan.md), [NEO-68](../../plans/NEO-68-implementation-plan.md), [NEO-69](../../plans/NEO-69-implementation-plan.md), [NEO-70](../../plans/NEO-70-implementation-plan.md), [NEO-71](../../plans/NEO-71-implementation-plan.md), [NEO-74](../../plans/NEO-74-implementation-plan.md), [NEO-75](../../plans/NEO-75-implementation-plan.md), [E3M2-prototype-backlog](../../plans/E3M2-prototype-backlog.md), [E3S5-client-prototype-backlog](../../plans/E3S5-client-prototype-backlog.md), [E3_M2](E3_M2_RefinementAndRecipeExecution.md) |
|
||||
| E5.M1 | Planned | **Backlog decomposed:** Epic 5 Slice 1 — [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md) **E5M1-01** [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) through **E5M1-12** [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); gig XP **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). **Precursor landed:** E1.M4 cast funnel (NEO-28/31/32) — **`POST …/ability-cast`** accepts without **`CombatResolution`** yet. No implementation started. | [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1](E5_M1_CombatRulesEngine.md), [E1M4-prototype-backlog.md](../../plans/E1M4-prototype-backlog.md) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,8 @@ Fleshed-out scope, contracts, and integration notes live in **per-module documen
|
|||
| Module ID | Module Name | Depends On | Contract Needed | Phase Required | Status |
|
||||
|---|---|---|---|---|---|
|
||||
| E5.M1 | CombatRulesEngine | E1.M3, E2.M2 | CombatAction, CombatResolution, ThreatState | Prototype | Planned |
|
||||
|
||||
**E5.M1 note:** Epic 5 **Slice 1** backlog in Linear ([Epic 5 — PvE Combat and Encounter Content](https://linear.app/neon-sprawl/project/epic-5-pve-combat-and-encounter-content-cf3c94c3-5346-40e0-8aaf-281e846f2846)): [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) → [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86); label **`E5.M1`**. Gig XP on defeat: **E5M1-10** [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44). See [E5M1-prototype-backlog.md](../../plans/E5M1-prototype-backlog.md), [E5_M1_CombatRulesEngine.md](E5_M1_CombatRulesEngine.md). Builds on E1.M4 cast funnel (NEO-28/31/32); extends **`POST …/ability-cast`** with **`CombatResolution`**.
|
||||
| E5.M2 | NpcAiAndBehaviorProfiles | E5.M1 | NpcBehaviorDef, TelegraphEvent, AggroRule | Prototype | Planned |
|
||||
| E5.M3 | EncounterAndRewardTables | E5.M1, E3.M3, E7.M2 | EncounterDef, RewardTable, EncounterCompleteEvent | Prototype | Planned |
|
||||
| E5.M4 | GroupCombatScaling | E5.M3, E8.M1 | ScalingProfile, PartySizeModifier, CombatDifficultyBand | Pre-production | Planned |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,381 @@
|
|||
# E5.M1 — Prototype story backlog (CombatRulesEngine)
|
||||
|
||||
Working backlog for **Epic 5 — Slice 1** ([combat rules MVP](../decomposition/epics/epic_05_pve_combat.md#slice-1---combat-rules-mvp)). Decomposition and contracts: [E5.M1 — CombatRulesEngine](../decomposition/modules/E5_M1_CombatRulesEngine.md).
|
||||
|
||||
**Labels (Linear):** every issue **`E5.M1`**; add **`server`** or **`client`** + **`Story`** as listed.
|
||||
|
||||
**Precursor (do not re-scope):** [E1.M4](../decomposition/modules/E1_M4_AbilityInputScaffold.md) cast funnel — hotbar loadout ([NEO-29](https://linear.app/neon-sprawl/issue/NEO-29)), cast POST + client digits ([NEO-31](https://linear.app/neon-sprawl/issue/NEO-31)), target lock + range deny ([NEO-28](https://linear.app/neon-sprawl/issue/NEO-28)), global cooldown + snapshot ([NEO-32](https://linear.app/neon-sprawl/issue/NEO-32)), telemetry hook comments ([NEO-30](https://linear.app/neon-sprawl/issue/NEO-30)). **`POST …/ability-cast`** today returns **`accepted: true`** with **no** damage, **no** per-ability cooldown from data, and **no** **`CombatResolution`** payload — that gap is **E5.M1**.
|
||||
|
||||
**Upstream (must be landed):** [E1.M3](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md) targeting ([NEO-23](https://linear.app/neon-sprawl/issue/NEO-23)–[NEO-26](https://linear.app/neon-sprawl/issue/NEO-26)) — **`PrototypeTargetRegistry`** alpha/beta stubs remain combat dummies until [E5.M2](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md). [E2.M2](../decomposition/modules/E2_M2_XpAwardAndLevelEngine.md) skill XP engine is **not** the combat payout path — gig XP is [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44).
|
||||
|
||||
**Prototype combat spine (frozen in E5M1-01):** **four** `AbilityDef` rows — ids **`prototype_pulse`**, **`prototype_guard`**, **`prototype_dash`**, **`prototype_burst`** (same allowlist as today’s hardcoded [`PrototypeAbilityRegistry`](../../server/NeonSprawl.Server/Game/AbilityInput/PrototypeAbilityRegistry.cs)). **Two** combat dummies — **`prototype_target_alpha`** / **`prototype_target_beta`** with shared max HP. Player-as-target hostility and **E6.M1** PvP gates stay **stub deny** until Epic 6; prototype targets are NPC stubs only.
|
||||
|
||||
**Linear issues (created):** attach `docs/plans/NEO-*-implementation-plan.md` on the same branch as implementation work.
|
||||
|
||||
| Slug | Layer | Linear |
|
||||
|------|-------|--------|
|
||||
| E5M1-01 | server | [NEO-76](https://linear.app/neon-sprawl/issue/NEO-76) |
|
||||
| E5M1-02 | server | [NEO-77](https://linear.app/neon-sprawl/issue/NEO-77) |
|
||||
| E5M1-03 | server | [NEO-79](https://linear.app/neon-sprawl/issue/NEO-79) |
|
||||
| E5M1-04 | server | [NEO-78](https://linear.app/neon-sprawl/issue/NEO-78) |
|
||||
| E5M1-05 | server | [NEO-80](https://linear.app/neon-sprawl/issue/NEO-80) |
|
||||
| E5M1-06 | server | [NEO-81](https://linear.app/neon-sprawl/issue/NEO-81) |
|
||||
| E5M1-07 | server | [NEO-82](https://linear.app/neon-sprawl/issue/NEO-82) |
|
||||
| E5M1-08 | server | [NEO-83](https://linear.app/neon-sprawl/issue/NEO-83) |
|
||||
| E5M1-09 | server | [NEO-84](https://linear.app/neon-sprawl/issue/NEO-84) |
|
||||
| E5M1-10 | server | [NEO-44](https://linear.app/neon-sprawl/issue/NEO-44) |
|
||||
| E5M1-11 | client | [NEO-85](https://linear.app/neon-sprawl/issue/NEO-85) |
|
||||
| E5M1-12 | client | [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86) |
|
||||
|
||||
**Dependency graph in Linear:** E5M1-02 blocked by E5M1-01. E5M1-03 blocked by E5M1-02. E5M1-04 blocked by E5M1-03. E5M1-05 blocked by E5M1-03. E5M1-06 blocked by E5M1-05 and E5M1-03. E5M1-07 blocked by E5M1-06. E5M1-08 blocked by E5M1-06 (may parallel E5M1-07). E5M1-09 blocked by E5M1-07. E5M1-10 (NEO-44) blocked by E5M1-07. E5M1-11 blocked by E5M1-07 and E5M1-08. E5M1-12 blocked by E5M1-11 and E5M1-10.
|
||||
|
||||
**Board order:** estimates **1–12** matching slug order (E5M1-01 = 1 … E5M1-12 = 12). On the Epic 5 project board, sort by **Estimate** (ascending).
|
||||
|
||||
---
|
||||
|
||||
## Story order (recommended)
|
||||
|
||||
| Order | Slug | Layer | Depends on |
|
||||
|-------|------|-------|------------|
|
||||
| 1 | **E5M1-01** | server | Existing four prototype ability ids (hardcoded allowlist) |
|
||||
| 2 | **E5M1-02** | server | E5M1-01 |
|
||||
| 3 | **E5M1-03** | server | E5M1-02 |
|
||||
| 4 | **E5M1-04** | server | E5M1-03 |
|
||||
| 5 | **E5M1-05** | server | E5M1-03, `PrototypeTargetRegistry` ids |
|
||||
| 6 | **E5M1-06** | server | E5M1-05, E5M1-03 |
|
||||
| 7 | **E5M1-07** | server | E5M1-06 |
|
||||
| 8 | **E5M1-08** | server | E5M1-06 |
|
||||
| 9 | **E5M1-09** | server | E5M1-07 |
|
||||
| 10 | **E5M1-10** | server | E5M1-07 ([NEO-44](https://linear.app/neon-sprawl/issue/NEO-44)) |
|
||||
| 11 | **E5M1-11** | client | E5M1-07, E5M1-08 |
|
||||
| 12 | **E5M1-12** | client | E5M1-11, E5M1-10 |
|
||||
|
||||
**Downstream (separate modules):** [E5.M2](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) NPC AI + telegraphs; [E5.M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md) encounters + loot; [E1.M4](../decomposition/modules/E1_M4_AbilityInputScaffold.md) closes when **`CombatResolution`** lands; per-ability cooldown UI may refresh from extended cast responses or existing snapshot route.
|
||||
|
||||
---
|
||||
|
||||
## Kickoff decisions (decomposition defaults)
|
||||
|
||||
| Topic | Decision | Rationale |
|
||||
|-------|----------|-----------|
|
||||
| Ability count | **4** frozen defs | Matches existing hotbar allowlist; epic allows 4–6 |
|
||||
| Combat targets | **`prototype_target_alpha`** / **`beta`** only | Reuse E1.M3 registry until E5.M2 spawns real NPCs |
|
||||
| Max HP | **100** per dummy (constant in content or combat constants) | Enough casts to observe defeat without tuning TTK yet |
|
||||
| Hit resolution | **Deterministic** damage from `AbilityDef.baseDamage`; no miss/crit RNG | Readable prototype tests; RNG is pre-production |
|
||||
| Cooldown | **Per-ability** `cooldownSeconds` from catalog on successful **resolve** | Replaces global `AbilityPrototypeCooldown` when E5M1-07 lands |
|
||||
| Player HP / death | **Out of scope** Slice 1 | `player_death` telemetry gets a **reserved** hook site only |
|
||||
| `ThreatState` | **Stub** / comment-only in Slice 1 | Full threat/aggro is E5.M2 |
|
||||
| PvP | **Stub** deny path documented; no player targets in prototype registry | [pvp_combat_integration.md](../decomposition/modules/pvp_combat_integration.md) |
|
||||
| Cast wire | **Extend** `POST …/ability-cast` **`AbilityCastResponse` v1** | Avoid parallel combat POST in prototype; E1.M4 client keeps one route |
|
||||
| Target HP read | **`GET /game/world/combat-targets`** snapshot | Client HUD without trusting local damage math |
|
||||
| Gig XP | **[NEO-44](https://linear.app/neon-sprawl/issue/NEO-44)** on **`targetDefeated`** | Combat → **gig** XP only; no E2.M2 skill grant |
|
||||
|
||||
---
|
||||
|
||||
### E5M1-01 — Prototype AbilityDef catalog + schemas + CI
|
||||
|
||||
**Goal:** Lock content shape and CI validation for **four** frozen combat abilities before server load.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `content/schemas/ability-def.schema.json`.
|
||||
- `content/abilities/prototype_abilities.json` with stable **`id`** values matching today’s allowlist (`prototype_pulse`, `prototype_guard`, `prototype_dash`, `prototype_burst`).
|
||||
- Fields: **`displayName`**, **`baseDamage`** (non-negative int), **`cooldownSeconds`** (positive number), optional **`abilityKind`** enum for future UX (`attack` / `utility` / `movement`).
|
||||
- `scripts/validate_content.py`: schema validation, duplicate ids, exact four-id allowlist, non-empty display names.
|
||||
- Designer note in [E5_M1](../decomposition/modules/E5_M1_CombatRulesEngine.md) + `content/README.md`.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Server loader, combat engine, HTTP, Godot.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] PR gate validates ability JSON against schema.
|
||||
- [ ] Exactly four prototype ability ids; duplicate `id` fails CI.
|
||||
- [ ] Stable id list documented in module doc freeze box.
|
||||
|
||||
---
|
||||
|
||||
### E5M1-02 — Server ability catalog load (fail-fast)
|
||||
|
||||
**Goal:** Disk → host: startup load of `content/abilities/*.json` with CI-parity validation.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Loader + catalog types under `server/NeonSprawl.Server/Game/Combat/` (path finalized in plan).
|
||||
- Fail-fast on malformed files, duplicate ids, allowlist mismatch with CI.
|
||||
- Unit tests (AAA) for loader happy path and failure modes.
|
||||
- `server/README.md` section.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Injectable registry interface (E5M1-03), HTTP projection, combat resolution.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Host fails startup on invalid ability JSON (mirror CI rules).
|
||||
- [ ] Tests cover at least one happy path and one malformed catalog rejection.
|
||||
|
||||
---
|
||||
|
||||
### E5M1-03 — `IAbilityDefinitionRegistry` + DI
|
||||
|
||||
**Goal:** Injectable lookup for combat and hotbar validation; replace direct static allowlist reads over time.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `IAbilityDefinitionRegistry` + implementation backed by startup catalog.
|
||||
- DI registration in `Program.cs`.
|
||||
- Unit tests (AAA) for known-id lookup and unknown-id miss.
|
||||
- Migrate **`PrototypeAbilityRegistry`** call sites in hotbar/cast path to registry (keep normalize behavior).
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- HTTP GET route (E5M1-04), damage application.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Unknown ability id fails hotbar/cast validation same as today.
|
||||
- [ ] All four prototype ids resolve with expected **`baseDamage`** / **`cooldownSeconds`**.
|
||||
|
||||
---
|
||||
|
||||
### E5M1-04 — `GET /game/world/ability-definitions`
|
||||
|
||||
**Goal:** Versioned read-only projection of frozen ability defs for client/tooling.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `GET /game/world/ability-definitions` → list DTO (`schemaVersion`, `abilities[]` with id, displayName, baseDamage, cooldownSeconds, abilityKind).
|
||||
- API + integration tests (AAA); Bruno folder `bruno/neon-sprawl-server/ability-definitions/`.
|
||||
- `server/README.md`; manual QA stub if needed.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Godot HUD — client uses this optionally in E5M1-11; not required for Bruno proof.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] GET returns all four prototype abilities with stable field names.
|
||||
- [ ] Bruno happy path documents response shape.
|
||||
|
||||
**Client counterpart:** optional enrichment in [NEO-85](#e5m1-11--client-combat-feedback--target-hp-hud) — not a separate issue.
|
||||
|
||||
---
|
||||
|
||||
### E5M1-05 — Combat entity health store + prototype dummy init
|
||||
|
||||
**Goal:** Server-owned HP for combat dummies keyed by target id.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `ICombatEntityHealthStore` (or equivalent) with **`TryGet`**, **`ApplyDamage`**, **`TryResetToFull`** for prototype targets.
|
||||
- Initialize **`prototype_target_alpha`** and **`prototype_target_beta`** to catalog max HP at first access (in-memory; not persisted in Slice 1).
|
||||
- Unit tests (AAA) for damage, floor at zero, defeated flag.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Player HP, persistence migration, NPC spawn (E5.M2).
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Damaging alpha reduces stored HP; HP cannot go below zero.
|
||||
- [ ] Defeated target remains defeated until explicit reset policy (document prototype: server restart resets).
|
||||
|
||||
---
|
||||
|
||||
### E5M1-06 — `CombatOperations.TryResolve` + `CombatResult`
|
||||
|
||||
**Goal:** Pure server combat resolution: ability + target → damage + remaining HP + defeated.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `CombatOperations.TryResolve` (name TBD) using **`IAbilityDefinitionRegistry`** + **`ICombatEntityHealthStore`**.
|
||||
- Result type with **`damageDealt`**, **`targetRemainingHp`**, **`targetDefeated`**, stable deny **`reasonCode`** when target already defeated or unknown.
|
||||
- Unit tests (AAA) for happy path, zero-damage utility abilities, defeated-target deny.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- HTTP wiring (E5M1-07), gig XP (NEO-44), telemetry hooks.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] **`prototype_pulse`** against full HP dummy deals catalog damage deterministically.
|
||||
- [ ] Second resolve on defeated target returns structured deny (no silent no-op).
|
||||
|
||||
---
|
||||
|
||||
### E5M1-07 — Wire `ability-cast` into combat engine + extend cast response
|
||||
|
||||
**Goal:** Successful casts after E1.M4 gates invoke **`CombatOperations`** and return **`CombatResolution`** fields on the existing cast POST.
|
||||
|
||||
**In scope**
|
||||
|
||||
- After NEO-28/32 gates pass, call combat engine; commit per-ability cooldown from ability def (replace global constant).
|
||||
- Extend **`AbilityCastResponse` v1** with optional resolution block on accept: e.g. **`damageDealt`**, **`targetRemainingHp`**, **`targetDefeated`**, **`targetId`**, **`abilityId`** (exact names in plan).
|
||||
- JSON deny **`target_defeated`** (or aligned code) when firing at defeated dummy.
|
||||
- Integration tests + Bruno: lock target → cast → response shows damage; repeated casts until defeated then deny.
|
||||
- `server/README.md` cast section updated; cross-link E1.M4/E5.M1 deny vocabulary.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Godot presentation — **[NEO-85](#e5m1-11--client-combat-feedback--target-hp-hud)**.
|
||||
- Rich **`ThreatState`**, player damage, PvP eligibility (stub only).
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Accept path returns non-empty resolution payload with deterministic damage.
|
||||
- [ ] Cooldown duration matches ability catalog, not global constant.
|
||||
- [ ] Bruno documents lock → cast → damage → defeat deny spine.
|
||||
|
||||
**Client counterpart:** [NEO-85](https://linear.app/neon-sprawl/issue/NEO-85)
|
||||
|
||||
---
|
||||
|
||||
### E5M1-08 — `GET /game/world/combat-targets` snapshot
|
||||
|
||||
**Goal:** Read-only HP snapshot for prototype combat dummies for client HUD and manual QA.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `GET /game/world/combat-targets` → versioned list (`targetId`, `maxHp`, `currentHp`, `defeated`).
|
||||
- Seeds store entries for alpha/beta on first read (same init policy as E5M1-05).
|
||||
- API tests (AAA); Bruno folder; README section.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Godot wiring (E5M1-11), NPC movement.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] GET reflects damage applied via cast route without client-side HP math.
|
||||
- [ ] Defeated flag true after lethal cast sequence in Bruno.
|
||||
|
||||
**Client counterpart:** [NEO-85](https://linear.app/neon-sprawl/issue/NEO-85)
|
||||
|
||||
---
|
||||
|
||||
### E5M1-09 — Slice 1 combat telemetry hook sites
|
||||
|
||||
**Goal:** Comment-only hooks for Epic 5 Slice 1 product telemetry names; no E9.M1 ingest.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Hook placement in combat resolve / cast accept path for **`ability_used`**, **`enemy_defeat`**.
|
||||
- Reserved **`encounter_start`** / **`player_death`** comment sites (encounters and player HP deferred).
|
||||
- `TODO(E9.M1)` markers; README + module doc pointers.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Production logging, dashboards.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Hook names match [epic_05 Slice 1](../decomposition/epics/epic_05_pve_combat.md#slice-1---combat-rules-mvp) vocabulary.
|
||||
- [ ] **`enemy_defeat`** site fires only on transition to defeated (not every overkill cast).
|
||||
|
||||
---
|
||||
|
||||
### E5M1-10 — Gig XP on combat defeat ([NEO-44](https://linear.app/neon-sprawl/issue/NEO-44))
|
||||
|
||||
**Goal:** On prototype target defeat, award **gig XP** to the attacker's **main gig** — **not** E2.M2 skill XP.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Minimal gig progression store + grant helper (prototype main gig only — id TBD in kickoff plan, e.g. frozen **`breach`**).
|
||||
- Hook from combat defeat path in E5M1-07 engine.
|
||||
- **`GET`** (or extend existing snapshot) for gig progression visibility — scope in implementation plan.
|
||||
- Tests proving no **`POST …/skill-progression`** call on combat defeat.
|
||||
- `server/README.md`; manual QA steps.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Full nine-gig catalog UI, hub swap, sub-gig XP ([gigs.md](../game-design/gigs.md)).
|
||||
- Godot gig bar — optional in capstone [NEO-86](#e5m1-12--playable-tab-target-combat-capstone-godot).
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Defeating **`prototype_target_alpha`** grants gig XP once per defeat transition.
|
||||
- [ ] Skill progression snapshot unchanged by combat defeat in tests.
|
||||
- [ ] Documented invariant matches [progression.md](../game-design/progression.md).
|
||||
|
||||
**Note:** Issue pre-dates this backlog; slug **E5M1-10** maps to existing **[NEO-44](https://linear.app/neon-sprawl/issue/NEO-44)**.
|
||||
|
||||
---
|
||||
|
||||
### E5M1-11 — Client combat feedback + target HP HUD
|
||||
|
||||
**Goal:** Godot displays authoritative cast outcomes and dummy HP without local combat math.
|
||||
|
||||
**In scope**
|
||||
|
||||
- Extend **`ability_cast_client.gd`** (or sibling) to parse extended **`AbilityCastResponse`** resolution fields.
|
||||
- Update **`CastFeedbackLabel`** (or dedicated combat line) with damage / defeat messaging on accept.
|
||||
- **`combat_targets_client.gd`** + HUD label for locked target HP (poll or refresh after successful cast + on target lock change).
|
||||
- GdUnit tests with HTTP doubles ([testing-expectations.md](../../.cursor/rules/testing-expectations.md)).
|
||||
- `docs/manual-qa/NEO-85.md` — Godot steps (server + client running).
|
||||
- `client/README.md` combat HUD section.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- Final combat VFX, floating damage numbers art pass, player HP bar.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Player sees damage/de defeat feedback in HUD after digit-key cast (not Output-only).
|
||||
- [ ] Target HP label matches server snapshot after cast sequence.
|
||||
- [ ] Manual QA exercisable without Bruno.
|
||||
|
||||
**Client counterpart:** this issue (**NEO-85**).
|
||||
|
||||
---
|
||||
|
||||
### E5M1-12 — Playable tab-target combat capstone (Godot)
|
||||
|
||||
**Goal:** Prove Epic 5 Slice 1 acceptance **in Godot**: tab-target lock, cast abilities, readable deny reasons, defeat a prototype dummy — without Bruno.
|
||||
|
||||
**In scope**
|
||||
|
||||
- `docs/manual-qa/NEO-86.md` single-session script: spawn → Tab lock alpha → bind slot 0 **`prototype_pulse`** → cast until defeat → verify HUD + optional gig XP visibility if NEO-44 landed.
|
||||
- `client/README.md` end-to-end combat loop section.
|
||||
- Update [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) E5.M1 row when complete.
|
||||
|
||||
**Out of scope**
|
||||
|
||||
- NPC AI (E5.M2), encounters/loot (E5.M3), group scaling.
|
||||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] Human completes script with server + client; **`prototype_target_alpha`** shows defeated state in HUD.
|
||||
- [ ] Epic 5 Slice 1 AC: combat readable in fixed isometric camera; no silent cast failures.
|
||||
- [ ] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums.
|
||||
|
||||
**Client counterpart:** this issue (**NEO-86**).
|
||||
|
||||
---
|
||||
|
||||
## After this backlog
|
||||
|
||||
- **[E5.M2](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md)** Slice 2 — decompose when E5.M1 lands (NPC archetypes + telegraphs + client telegraph UX).
|
||||
- **[E1.M4](../decomposition/modules/E1_M4_AbilityInputScaffold.md)** module **Ready** when cast path returns real **`CombatResolution`** and client HUD reflects it.
|
||||
- Track delivery in Linear; keep `blockedBy` synchronized if scope changes.
|
||||
- For each issue kickoff: `docs/plans/{NEO-XX}-implementation-plan.md` per [story-kickoff](../../.cursor/rules/story-kickoff.md).
|
||||
- Add `docs/manual-qa/{NEO-XX}.md` for player-visible **client** stories when implementation lands.
|
||||
|
||||
## Decomposition complete checklist
|
||||
|
||||
- [x] Every player-visible AC has a **`client`** Linear issue (**NEO-85**, **NEO-86**)
|
||||
- [x] No “optional follow-up” / “future client” without NEO-XX
|
||||
- [x] Capstone client issue exists for playable Godot verification (**NEO-86**)
|
||||
- [x] [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) E5.M1 row updated when work starts
|
||||
- [x] [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) E5.M1 note updated when issues are created
|
||||
|
||||
## Related docs
|
||||
|
||||
- [E5_M1_CombatRulesEngine.md](../decomposition/modules/E5_M1_CombatRulesEngine.md)
|
||||
- [E1_M4_AbilityInputScaffold.md](../decomposition/modules/E1_M4_AbilityInputScaffold.md)
|
||||
- [epic_05_pve_combat.md](../decomposition/epics/epic_05_pve_combat.md)
|
||||
- [abilities.md](../game-design/abilities.md)
|
||||
- [gigs.md](../game-design/gigs.md)
|
||||
- [pvp_combat_integration.md](../decomposition/modules/pvp_combat_integration.md)
|
||||
- [client_server_authority.md](../decomposition/modules/client_server_authority.md)
|
||||
Loading…
Reference in New Issue