neon-sprawl/docs/plans/NEO-98-implementation-plan.md

173 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# NEO-98 — Implementation plan
## Story reference
| Field | Value |
|--------|--------|
| **Key** | NEO-98 |
| **Title** | E5M2-12: Playable NPC telegraph combat capstone (Godot) |
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-98/e5m2-12-playable-npc-telegraph-combat-capstone-godot |
| **Module** | [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) · Epic 5 Slice 2 · backlog **E5M2-12** |
| **Branch** | `NEO-98-e5m2-12-playable-npc-telegraph-combat-capstone` |
| **Server deps** | [NEO-92](https://linear.app/neon-sprawl/issue/NEO-92) aggro + leash (**Done**); [NEO-93](https://linear.app/neon-sprawl/issue/NEO-93) runtime state machine (**Done**); [NEO-94](https://linear.app/neon-sprawl/issue/NEO-94) runtime snapshot GET (**Done**); [NEO-95](https://linear.app/neon-sprawl/issue/NEO-95) NPC attack resolve + player combat HP (**Done**) |
| **Client deps** | [NEO-97](https://linear.app/neon-sprawl/issue/NEO-97) telegraph HUD + archetype markers (**Done**); E5.M1 cast + combat-target HUD ([NEO-85](https://linear.app/neon-sprawl/issue/NEO-85), [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86)) (**Done**) |
| **Pattern** | Capstone integration — [NEO-86](https://linear.app/neon-sprawl/issue/NEO-86) docs + manual QA primary; minimal client gap-fill only if capstone QA fails on `main` |
| **Server counterpart** | NEO-92NEO-95 server spine; Bruno-only verification is not prototype-complete per [full-stack epic decomposition](../../.cursor/rules/full-stack-epic-decomposition.md) |
## Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|--------|----------|----------------------|--------|
| **Implementation scope** | Docs-only vs client code? | **Docs + manual QA primary**; fix integration gaps only if capstone QA fails on `main` — [NEO-86](NEO-86-implementation-plan.md) precedent. | **User:** docs-primary; client fixes only if QA fails. |
| **Session baseline** | Fresh server vs fixture POST? | **Fresh server restart** before Godot **F5** — resets NPC HP, aggro/runtime rows, and player combat HP (NEO-86 / NEO-97 precedent); zero Bruno/curl in main checklist. | **User:** fresh restart; no Bruno/curl in checklist. |
| **Archetype order** | Order in single-session script? | **Melee → Ranged → Elite** — matches E5M2-12 backlog goal prose (“aggro a melee NPC… defeat all three archetypes”). | **User:** melee → ranged → elite. |
| **Verification depth** | Full telegraph spine per archetype? | **Full telegraph + player HP on Melee**; Ranged + Elite **defeat + telegraph visible** with documented windup/cooldown — keeps one-session script tractable while satisfying Slice 2 AC. | **User:** melee full; ranged/elite light. |
| **NEO-97 relationship** | Supersede component QA? | **NEO-98 supersedes as capstone**; keep [NEO-97](../manual-qa/NEO-97.md) as component-level regression reference (NEO-85 / NEO-86 pattern). | **Adopted** (repo precedent; not asked). |
## Goal, scope, and out-of-scope
**Goal:** Prove Epic 5 Slice 2 acceptance **in Godot**: aggro a melee NPC, observe readable telegraph timing before damage, take incoming NPC damage, defeat all three archetypes — without Bruno.
**In scope (from Linear + [E5M2-12](E5M2-prototype-backlog.md#e5m2-12--playable-npc-telegraph-combat-capstone-godot)):**
- **`docs/manual-qa/NEO-98.md`**: numbered **single-session** capstone script — fresh dev player, **Melee → Ranged → Elite**, zero Bruno/curl steps in the main checklist.
- **`client/README.md`**: **End-to-end NPC telegraph combat loop** section — integration checklist (boot → lock → cast → telegraph → player HP → defeat ×3); cross-links NEO-92NEO-97.
- **Module alignment** (on story completion): update [`documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) E5.M2 row + E5M2-12 backlog checkboxes; note Epic 5 Slice 2 client capstone complete.
- **Integration fixes only if capstone QA fails** on current `main` wiring (unexpected deny, HUD refresh gap, poll gate, etc.).
**Out of scope (from Linear + backlog):**
- Encounters/loot ([E5.M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md)), spawn ecology ([E4.M2](../decomposition/modules/E4_M2_SpawnEcologyController.md)).
- Final VFX art, dodge mechanics, player death flow.
- Bruno-only verification as prototype-complete proof.
- New server APIs or behavior catalog changes.
## Acceptance criteria checklist
- [x] Human completes **`docs/manual-qa/NEO-98.md`** with server + client; telegraph timing readable before damage on the melee spine.
- [x] Epic 5 Slice 2 AC: telegraph timing matches UI feedback; aggro rules feel deterministic in manual QA.
- [x] All three archetypes defeated in one session; **`CombatTargetHpLabel`** shows defeated for each.
- [x] Re-read [epic_05 Definition of Done](../decomposition/epics/epic_05_pve_combat.md#definition-of-done) for prototype minimums.
## Implementation reconciliation (shipped)
- **`docs/manual-qa/NEO-98.md`** — single-session capstone (Melee full telegraph spine → Ranged/Elite defeat + telegraph visible); zero Bruno/curl in main checklist.
- **`client/README.md`** — **End-to-end NPC telegraph combat loop (NEO-98)** section with flow table and cross-links.
- **`docs/plans/E5M2-prototype-backlog.md`**, **`E5_M2_NpcAiAndBehaviorProfiles.md`**, **`documentation_and_implementation_alignment.md`** — E5M2-12 landed; E5.M2 **Ready**; Epic 5 Slice 2 client capstone complete.
- **Integration fix (capstone QA):** defeated NPCs continued attacking — **`NpcRuntimeOperations.TryStopOnTargetDefeat`** on cast accept + **`AdvanceOne`** defeated guard; server tests added.
## Technical approach
### 1. Capstone manual QA script (`docs/manual-qa/NEO-98.md`)
**Preconditions:** Stop any running server; start fresh (`dotnet run`) so in-memory **`dev-local-1`** NPC HP, aggro/runtime rows, and player combat HP reset. Godot **F5** with **no** prior curl/Bruno seeding (hotbar **`prototype_pulse`** bind is automatic — NEO-85 dev bootstrap).
**Frozen catalog math** (from [E5_M2 freeze box](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01); player casts **`prototype_pulse`** **25** dmg, ~**3 s** cooldown):
| Archetype | Instance id | Marker | maxHp | Casts to defeat | attackDamage | windup | cooldown |
|-----------|-------------|--------|-------|-----------------|--------------|--------|----------|
| Melee | `prototype_npc_melee` | orange west **`(-3, -3)`** | 100 | **4** | 15 | 1.5 s | 3.0 s |
| Ranged | `prototype_npc_ranged` | purple SE **`(3, 3)`** | 80 | **4** | 12 | 2.0 s | 4.0 s |
| Elite | `prototype_npc_elite` | gold origin **`(0, 0)`** | 200 | **8** | 25 | 2.5 s | 5.0 s |
**Melee full spine (telegraph + player HP):**
1. Walk to **Melee** marker; **Tab** lock **`prototype_npc_melee`** — **`Validity: ok`**.
2. Press **1** (damaging cast): **`NpcStateLabel`** → **`aggro`** (~1 Hz poll); **`PlayerCombatHpLabel`** → **`100/100`**.
3. Wait **≥ 4.5 s** from first cast (3.0 s cooldown + 1.5 s windup): **`NpcStateLabel`** → **`telegraph_windup`**; **`TelegraphLabel`** shows countdown (**`Telegraph: Melee … · ~1.x s (melee)`**) ticking between polls.
4. After windup completes: **`PlayerCombatHpLabel`** → **`85/100`** (15 damage).
5. Press **1** three more times (respect cooldown) until **`CombatTargetHpLabel`** → **`0/100 (defeated)`** and cast feedback shows defeat.
**Ranged light (defeat + telegraph visible):**
6. Walk to **Ranged** marker; lock **`prototype_npc_ranged`**.
7. First damaging cast → aggro; wait for **`telegraph_windup`** + readable **`TelegraphLabel`** (2.0 s windup after 4.0 s cooldown from first cast).
8. Cast until defeated (**4** pulses); verify deny on re-cast (**`target_defeated`**).
**Elite light (defeat + telegraph visible):**
9. Walk to **Elite** marker; lock **`prototype_npc_elite`**.
10. First damaging cast → aggro; wait for **`telegraph_windup`** + readable **`TelegraphLabel`** (2.5 s windup after 5.0 s cooldown).
11. Cast until defeated (**8** pulses); verify deny on re-cast.
**Regression spot-checks (end of session):**
- Cast without lock → **`ability_cast_denied: invalid_target`** (NEO-28).
- **`CombatTargetHpLabel`** + cooldown HUD still work (NEO-85 / NEO-32).
- Optional deeper telegraph/HP checks: [NEO-97 manual QA](../manual-qa/NEO-97.md) (elite incoming-threat row).
**Notes section (not main checklist steps):**
- Optional mid-session reset (Development host): `POST /game/__dev/combat-targets-fixture` — see [NEO-97](../manual-qa/NEO-97.md) / [server README — dev fixture](../../../server/README.md#dev-combat-target-fixture-brunomanual-qa).
- Capstone baseline prefers **server restart** over fixture POST.
### 2. README integration checklist
New **`## End-to-end NPC telegraph combat loop (NEO-98)`** section after [NEO-97 NPC runtime + telegraph HUD](NEO-97-implementation-plan.md) subsection in `client/README.md`:
- Flow in prose: fresh server → boot hotbar sync → **Melee** lock → cast → telegraph HUD → player HP drop → defeat → repeat **Ranged** + **Elite**.
- Table mirroring capstone steps (lock, telegraph labels, defeat cast counts).
- Cross-links: NEO-92 (aggro), NEO-94 (runtime snapshot), NEO-95 (player combat HP), NEO-97 (HUD poll clients).
- Pointer to **`docs/manual-qa/NEO-98.md`** as authoritative capstone script.
- Note NEO-97 manual QA as component-level regression.
### 3. Module alignment (implementation batch or story end)
When acceptance criteria pass:
- **`documentation_and_implementation_alignment.md`**: E5.M2 — note **NEO-98 landed**, Epic 5 Slice 2 client capstone complete.
- **`E5M2-prototype-backlog.md`**: E5M2-12 acceptance checkboxes + landed note.
- **`E5_M2_NpcAiAndBehaviorProfiles.md`**: E5M2-12 row + capstone cross-link if not already marked.
### 4. Integration fixes (conditional)
Run capstone dry-run on **`main`** before doc-only work; if any step fails, fix minimal client bug in this branch and note in plan **Decisions**. Expected path: docs + README only (NEO-97 HUD wiring landed in PR #136).
## Files to add
| Path | Purpose |
|------|---------|
| `docs/plans/NEO-98-implementation-plan.md` | This plan. |
| `docs/manual-qa/NEO-98.md` | Capstone single-session manual QA (zero Bruno/curl in main checklist). |
## Files to modify
| Path | Rationale |
|------|-----------|
| `client/README.md` | End-to-end NPC telegraph combat loop section; capstone manual QA link; cross-links NEO-92NEO-97. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E5.M2 row — NEO-98 landed + Slice 2 client capstone complete (on story completion). |
| `docs/plans/E5M2-prototype-backlog.md` | E5M2-12 acceptance checkboxes + landed note (on story completion). |
| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | E5M2-12 capstone landed note + manual QA link (on story completion). |
| `server/NeonSprawl.Server/Game/Npc/NpcRuntimeOperations.cs` | **`TryStopOnTargetDefeat`** + defeated guard in **`AdvanceOne`**; **`AdvanceAll`** takes **`ICombatEntityHealthStore`**. |
| `server/NeonSprawl.Server/Game/Npc/NpcRuntimeSnapshotWorldApi.cs` | Pass combat health store into **`AdvanceAll`**. |
| `server/NeonSprawl.Server/Game/AbilityInput/AbilityCastApi.cs` | Stop NPC combat on **`TargetDefeated`**. |
| `server/README.md` | Threat table — **Defeat clear** rule. |
**Conditional (only if capstone QA fails on client):**
| Path | Rationale |
|------|-----------|
| `client/scripts/main.gd` | Minimal HUD / poll / sync fix for failing capstone step. |
| Other client scripts under `client/scripts/` | Only if root cause is outside `main.gd` (document in plan **Decisions**). |
## Tests
| Path | Change |
|------|--------|
| `server/NeonSprawl.Server.Tests/Game/Npc/NpcRuntimeOperationsTests.cs` | **`TryStopOnTargetDefeat`** + defeated **`AdvanceAll`** guard tests. |
| `server/NeonSprawl.Server.Tests/Game/AbilityInput/AbilityCastApiTests.cs` | **`PostAbilityCast_ShouldStopNpcCombat_WhenTargetDefeated`** integration test. |
**If integration fix adds/changes client behavior:** add or extend the smallest GdUnit test in the matching NEO-97 suite (`npc_combat_hud_refresh_test.gd`, etc.) — AAA layout per [testing-expectations.md](../../.cursor/rules/testing-expectations.md).
**Manual verification:** [`docs/manual-qa/NEO-98.md`](../manual-qa/NEO-98.md) — server + Godot; melee telegraph spine + three-archetype defeat without curl.
## Open questions / risks
| Question or risk | Agent recommendation | Status |
|------------------|----------------------|--------|
| **Capstone dry-run on `main`** | Capstone QA found defeated melee still attacking; server fix clears aggro + runtime on defeat. | **adopted** |
| **Session length (elite ×8 casts)** | Document cast counts + cooldown reminders; elite section is defeat + telegraph visibility only (kickoff depth decision). | **adopted** |
| **Player HP drift across three fights** | Script asserts melee **85/100** only; later archetype hits optional in Notes — do not require exact final HP in AC. | **adopted** |
| **Overlap with NEO-97 manual QA** | NEO-98 supersedes as capstone; NEO-97 stays component regression (elite incoming-threat row). | **adopted** |
| **NEO-97 merge dependency** | **NEO-97 merged** to `main` (PR #136) — capstone can proceed. | **adopted** |