neon-sprawl/docs/plans/E5M1-prototype-backlog.md

23 KiB
Raw Blame History

E5.M1 — Prototype story backlog (CombatRulesEngine)

Working backlog for Epic 5 — Slice 1 (combat rules MVP). Decomposition and contracts: E5.M1 — CombatRulesEngine.

Labels (Linear): every issue E5.M1; add server or client + Story as listed.

Precursor (do not re-scope): E1.M4 cast funnel — hotbar loadout (NEO-29), cast POST + client digits (NEO-31), target lock + range deny (NEO-28), global cooldown + snapshot (NEO-32), telemetry hook comments (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 targeting (NEO-23NEO-26) — PrototypeTargetRegistry alpha/beta stubs remain combat dummies until E5.M2. E2.M2 skill XP engine is not the combat payout path — gig XP is NEO-44.

Prototype combat spine (frozen in E5M1-01): four AbilityDef rows — ids prototype_pulse, prototype_guard, prototype_dash, prototype_burst (same allowlist as todays hardcoded PrototypeAbilityRegistry). 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
E5M1-02 server NEO-77
E5M1-03 server NEO-79
E5M1-04 server NEO-78
E5M1-05 server NEO-80
E5M1-06 server NEO-81
E5M1-07 server NEO-82
E5M1-08 server NEO-83
E5M1-09 server NEO-84
E5M1-10 server NEO-44
E5M1-11 client NEO-85
E5M1-12 client 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 112 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)
11 E5M1-11 client E5M1-07, E5M1-08
12 E5M1-12 client E5M1-11, E5M1-10

Downstream (separate modules): E5.M2 NPC AI + telegraphs; E5.M3 encounters + loot; E1.M4 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 46
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
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 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 todays 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 + 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.

Landed (NEO-76): content/abilities/prototype_abilities.json, ability-def.schema.json, CI gates in validate_content.py; plan NEO-76-implementation-plan.md.


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.

Landed (NEO-77): fail-fast server load of content/abilities/*_abilities.jsonserver/NeonSprawl.Server/Game/Combat/ (NEO-77-implementation-plan.md); server README — Ability catalog.


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.

Landed (NEO-79): IAbilityDefinitionRegistry + DI; hotbar/cast migrated — NEO-79-implementation-plan.md.


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; no manual QA (server-only per NEO-78 kickoff).

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.

Landed (NEO-78): GET /game/world/ability-definitionsAbilityDefinitionsWorldApi + DTOs in Game/Combat/ (NEO-78-implementation-plan.md); Bruno bruno/neon-sprawl-server/ability-definitions/.

Client counterpart: optional enrichment in NEO-85 — 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).

Landed (NEO-80): ICombatEntityHealthStore + InMemoryCombatEntityHealthStore — lazy init for prototype_target_alpha / prototype_target_beta at PrototypeCombatConstants.MaxPrototypeTargetHp (100) (NEO-80-implementation-plan.md); server README — Combat entity health (NEO-80).


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).

Landed (NEO-81): CombatOperations.TryResolve + CombatResult + CombatReasonCodes — deterministic catalog damage, defeated pre-check deny, unknown ability/target validation (NEO-81-implementation-plan.md); server README — Combat engine (NEO-81).


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.
  • 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.

Landed (NEO-82): cast POST invokes CombatOperations.TryResolve; nested combatResolution on accept; per-ability catalog cooldown; target_defeated deny (NEO-82-implementation-plan.md); Bruno defeat spine.

Client counterpart: 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.

Landed (NEO-83): GET /game/world/combat-targetsCombatTargetsWorldApi + DTOs; cast→GET damage + defeat spine (NEO-83-implementation-plan.md); Bruno bruno/neon-sprawl-server/combat-targets/.

Client counterpart: 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 vocabulary.
  • enemy_defeat site fires only on transition to defeated (not every overkill cast).

Landed (NEO-84): comment-only ability_used / enemy_defeat hook sites in CombatOperations.TryResolve; reserved encounter_start / player_death (NEO-84-implementation-plan.md); server README — Combat telemetry hooks.


E5M1-10 — Gig XP on combat defeat (NEO-44)

Goal: On prototype target defeat, award gig XP to the attacker's main gignot 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).
  • Godot gig bar — optional in capstone NEO-86.

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.

Landed (NEO-44): IPlayerGigProgressionStore + CombatDefeatGigXpGrant on cast accept when targetDefeated; GET …/gig-progression; main gig breach, 25 XP per defeat; plan NEO-44-implementation-plan.md; manual QA 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).
  • 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/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.

Landed (NEO-85): ability_cast_client.gd parses combatResolution; combat_targets_client.gd + CombatTargetHpLabel; event-driven GET refresh; plan NEO-85-implementation-plan.md; manual QA NEO-85.

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 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 for prototype minimums.

Landed (NEO-86): gig_progression_client.gd + GigXpLabel; defeat-triggered gig GET refresh; capstone manual QA NEO-86; plan NEO-86-implementation-plan.md; client/README.md end-to-end combat loop section. Epic 5 Slice 1 client capstone complete.

Client counterpart: this issue (NEO-86).


After this backlog

  • E5.M2 Slice 2 — decomposed in E5M2-prototype-backlog.md (NEO-87NEO-98).
  • E1.M4 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.
  • Add docs/manual-qa/{NEO-XX}.md for player-visible client stories when implementation lands.

Decomposition complete checklist