Merge pull request #127 from ViPro-Technologies/NEO-90-npc-behavior-definitions-get
NEO-90: GET /game/world/npc-behavior-definitionspull/129/head
commit
cb65612f03
|
|
@ -0,0 +1,67 @@
|
|||
meta {
|
||||
name: GET npc behavior definitions
|
||||
type: http
|
||||
seq: 1
|
||||
}
|
||||
|
||||
get {
|
||||
url: {{baseUrl}}/game/world/npc-behavior-definitions
|
||||
body: none
|
||||
auth: none
|
||||
}
|
||||
|
||||
tests {
|
||||
test("returns 200 JSON with schema v1 and npcBehaviors array", function () {
|
||||
expect(res.getStatus()).to.equal(200);
|
||||
expect(res.getHeader("content-type")).to.contain("application/json");
|
||||
const body = res.getBody();
|
||||
expect(body.schemaVersion).to.equal(1);
|
||||
expect(body.npcBehaviors).to.be.an("array");
|
||||
expect(body.npcBehaviors.length).to.equal(3);
|
||||
});
|
||||
|
||||
test("npcBehaviors are ascending by id (ordinal)", function () {
|
||||
const body = res.getBody();
|
||||
const ids = body.npcBehaviors.map((x) => x.id);
|
||||
const sorted = [...ids].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
||||
expect(ids).to.eql(sorted);
|
||||
});
|
||||
|
||||
test("frozen prototype three matches registry id order", function () {
|
||||
const body = res.getBody();
|
||||
const ids = body.npcBehaviors.map((x) => x.id);
|
||||
expect(ids).to.eql([
|
||||
"prototype_elite_mini_boss",
|
||||
"prototype_melee_pressure",
|
||||
"prototype_ranged_control",
|
||||
]);
|
||||
});
|
||||
|
||||
test("prototype_melee_pressure row matches catalog", function () {
|
||||
const body = res.getBody();
|
||||
const row = body.npcBehaviors.find((x) => x.id === "prototype_melee_pressure");
|
||||
expect(row).to.be.an("object");
|
||||
expect(row.displayName).to.equal("Melee Pressure");
|
||||
expect(row.archetypeKind).to.equal("melee_pressure");
|
||||
expect(row.maxHp).to.equal(100);
|
||||
expect(row.aggroRadius).to.equal(8);
|
||||
expect(row.leashRadius).to.equal(16);
|
||||
expect(row.telegraphWindupSeconds).to.equal(1.5);
|
||||
expect(row.attackDamage).to.equal(15);
|
||||
expect(row.attackCooldownSeconds).to.equal(3);
|
||||
});
|
||||
|
||||
test("prototype_elite_mini_boss row matches catalog", function () {
|
||||
const body = res.getBody();
|
||||
const row = body.npcBehaviors.find((x) => x.id === "prototype_elite_mini_boss");
|
||||
expect(row).to.be.an("object");
|
||||
expect(row.displayName).to.equal("Elite Mini-Boss");
|
||||
expect(row.archetypeKind).to.equal("elite_mini_boss");
|
||||
expect(row.maxHp).to.equal(200);
|
||||
expect(row.aggroRadius).to.equal(8);
|
||||
expect(row.leashRadius).to.equal(18);
|
||||
expect(row.telegraphWindupSeconds).to.equal(2.5);
|
||||
expect(row.attackDamage).to.equal(25);
|
||||
expect(row.attackCooldownSeconds).to.equal(5);
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
meta {
|
||||
name: npc-behavior-definitions
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
| **Module ID** | E5.M2 |
|
||||
| **Epic** | [Epic 5 — PvE Combat](../epics/epic_05_pve_combat.md) |
|
||||
| **Stage target** | Prototype |
|
||||
| **Status** | In Progress — Slice 2 backlog [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md): **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) catalog + CI landed · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) server load landed · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) registry + DI landed → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
| **Linear** | Label **`E5.M2`** · [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md) **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
| **Status** | In Progress — Slice 2 backlog [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md): **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) catalog + CI landed · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) server load landed · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) registry + DI landed · **E5M2-04** [NEO-90](https://linear.app/neon-sprawl/issue/NEO-90) behavior-definitions GET landed → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
| **Linear** | Label **`E5.M2`** · [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md) **E5M2-01** [NEO-87](https://linear.app/neon-sprawl/issue/NEO-87) · **E5M2-02** [NEO-88](https://linear.app/neon-sprawl/issue/NEO-88) · **E5M2-03** [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) · **E5M2-04** [NEO-90](https://linear.app/neon-sprawl/issue/NEO-90) → **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
|
||||
## Purpose
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ Enemy archetype behavior loops, aggro logic, and telegraph scheduling on top of
|
|||
|
||||
## Related implementation slices
|
||||
|
||||
Epic 5 **Slice 2 — NPC archetypes and telegraphs**: three archetypes (melee pressure, ranged control, elite mini-boss); telemetry `telegraph_fired`, `npc_state_transition`.
|
||||
Epic 5 **Slice 2 — NPC archetypes and telegraphs**: three archetypes (melee pressure, ranged control, elite mini-boss); telemetry `telegraph_fired`, `npc_state_transition`. **HTTP read model (NEO-90):** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/`; Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`.
|
||||
|
||||
## Linear backlog (decomposed)
|
||||
|
||||
|
|
@ -75,11 +75,13 @@ The **first shipped three-archetype NPC spine** under `content/npc-behaviors/*.j
|
|||
|
||||
**NPC instance ids (E5M2-05):** **`prototype_npc_melee`**, **`prototype_npc_ranged`**, **`prototype_npc_elite`** — each binds one behavior def + world anchor (replaces **`prototype_target_alpha` / `beta`**).
|
||||
|
||||
**Rules:** Do **not** rename frozen behavior **`id`** values without a migration issue. CI ([`scripts/validate_content.py`](../../../scripts/validate_content.py)) and server startup ([NEO-88](../../plans/NEO-88-implementation-plan.md)) enforce **exactly** these three behavior ids, duplicate-`id` rejection, and **`leashRadius` > `aggroRadius`** per row. Relaxing or changing that gate belongs in a new Linear issue once the catalog intentionally grows. Catalog + CI plan: [NEO-87 implementation plan](../../plans/NEO-87-implementation-plan.md). **`INpcBehaviorDefinitionRegistry`** + DI **landed** ([NEO-89](../../plans/NEO-89-implementation-plan.md), E5M2-03) — game code and HTTP read models should inject the registry, not **`NpcBehaviorDefinitionCatalog`**.
|
||||
**Rules:** Do **not** rename frozen behavior **`id`** values without a migration issue. CI ([`scripts/validate_content.py`](../../../scripts/validate_content.py)) and server startup ([NEO-88](../../plans/NEO-88-implementation-plan.md)) enforce **exactly** these three behavior ids, duplicate-`id` rejection, and **`leashRadius` > `aggroRadius`** per row. Relaxing or changing that gate belongs in a new Linear issue once the catalog intentionally grows. Catalog + CI plan: [NEO-87 implementation plan](../../plans/NEO-87-implementation-plan.md). **`INpcBehaviorDefinitionRegistry`** + DI **landed** ([NEO-89](../../plans/NEO-89-implementation-plan.md), E5M2-03) — game code and HTTP read models should inject the registry, not **`NpcBehaviorDefinitionCatalog`**. Read-only HTTP projection: **`GET /game/world/npc-behavior-definitions`** ([NEO-90](../../plans/NEO-90-implementation-plan.md), E5M2-04).
|
||||
|
||||
**Server load (NEO-88):** On host startup, `server/NeonSprawl.Server/Game/Npc/` loads `content/npc-behaviors/*_npc_behaviors.json` with the same validation gates as CI and **refuses to listen** when the catalog is invalid. Config and discovery: [server README — NPC behavior catalog](../../../server/README.md#npc-behavior-catalog-contentnpc-behaviors-neo-88). Plan: [NEO-88 implementation plan](../../plans/NEO-88-implementation-plan.md).
|
||||
|
||||
**NPC behavior definition registry (NEO-89):** **`INpcBehaviorDefinitionRegistry`** in `server/NeonSprawl.Server/Game/Npc/` wraps the startup-loaded catalog for read-only lookup by stable behavior `id` (`TryGetDefinition`), trim/lowercase validation (`TryNormalizeKnown`), and ordered enumeration (`GetDefinitionsInIdOrder`). Stable prototype id constants: **`PrototypeNpcBehaviorRegistry`**. **NEO-90+** (HTTP definitions, instance registry, runtime tick) should inject the interface rather than `NpcBehaviorDefinitionCatalog`. Plan: [NEO-89 implementation plan](../../plans/NEO-89-implementation-plan.md).
|
||||
**NPC behavior definition registry (NEO-89):** **`INpcBehaviorDefinitionRegistry`** in `server/NeonSprawl.Server/Game/Npc/` wraps the startup-loaded catalog for read-only lookup by stable behavior `id` (`TryGetDefinition`), trim/lowercase validation (`TryNormalizeKnown`), and ordered enumeration (`GetDefinitionsInIdOrder`). Stable prototype id constants: **`PrototypeNpcBehaviorRegistry`**. **NEO-91+** (instance registry, runtime tick) should inject the interface rather than `NpcBehaviorDefinitionCatalog`. Plan: [NEO-89 implementation plan](../../plans/NEO-89-implementation-plan.md).
|
||||
|
||||
**NPC behavior definitions HTTP (NEO-90):** **`GET /game/world/npc-behavior-definitions`** — versioned read-only projection (`schemaVersion` **1**, **`npcBehaviors`**) backed by **`INpcBehaviorDefinitionRegistry`**. Plan: [NEO-90 implementation plan](../../plans/NEO-90-implementation-plan.md); [server README — NPC behavior definitions (NEO-90)](../../../server/README.md#npc-behavior-definitions-neo-90); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`.
|
||||
|
||||
## Risks and telemetry
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -78,7 +78,7 @@ Fleshed-out scope, contracts, and integration notes live in **per-module documen
|
|||
|
||||
**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`**. **NEO-76 landed:** frozen four-ability catalog + CI ([NEO-76 plan](../../plans/NEO-76-implementation-plan.md)). **NEO-77 landed:** fail-fast server load of `content/abilities/*_abilities.json` ([NEO-77 plan](../../plans/NEO-77-implementation-plan.md)). **NEO-79 landed:** injectable **`IAbilityDefinitionRegistry`** + DI; hotbar/cast use **`TryNormalizeKnown`** ([NEO-79 plan](../../plans/NEO-79-implementation-plan.md)). **NEO-78 landed:** **`GET /game/world/ability-definitions`** — `AbilityDefinitionsWorldApi` + DTOs in `Game/Combat/` ([NEO-78 plan](../../plans/NEO-78-implementation-plan.md)); Bruno `bruno/neon-sprawl-server/ability-definitions/`. **NEO-80 landed:** **`ICombatEntityHealthStore`** / **`InMemoryCombatEntityHealthStore`** — lazy prototype dummy HP at **`PrototypeCombatConstants.MaxPrototypeTargetHp` (100)**; DI via **`AddCombatEntityHealthStore()`** ([NEO-80 plan](../../plans/NEO-80-implementation-plan.md)); [server README — Combat entity health (NEO-80)](../../../server/README.md#combat-entity-health-neo-80). **NEO-81 landed:** **`CombatOperations.TryResolve`** + **`CombatResult`** + **`CombatReasonCodes`** — defeated pre-check deny vocabulary (`target_defeated`, `unknown_ability`, `unknown_target`) ([NEO-81 plan](../../plans/NEO-81-implementation-plan.md)); [server README — Combat engine (NEO-81)](../../../server/README.md#combat-engine-neo-81). **NEO-82 landed:** **`AbilityCastApi`** invokes **`CombatOperations.TryResolve`** after E1.M4 gates; nested wire **`combatResolution`** on accept; per-ability catalog **`cooldownSeconds`** on successful resolve; **`target_defeated`** JSON cast deny without cooldown ([NEO-82 plan](../../plans/NEO-82-implementation-plan.md)); [server README — Ability cast (NEO-82)](../../../server/README.md#ability-cast-neo-31-neo-82); Bruno `bruno/neon-sprawl-server/ability-cast/` defeat spine. **NEO-83 landed:** **`GET /game/world/combat-targets`** — `CombatTargetsWorldApi` + DTOs; authoritative HP snapshot from **`ICombatEntityHealthStore`** for client HUD ([NEO-83 plan](../../plans/NEO-83-implementation-plan.md)); [server README — Combat targets snapshot (NEO-83)](../../../server/README.md#combat-targets-snapshot-neo-83); Bruno `bruno/neon-sprawl-server/combat-targets/`. **NEO-44 landed:** **`CombatDefeatGigXpGrant`** on cast **`targetDefeated`** — **25** gig XP to **`breach`** via **`IPlayerGigProgressionStore`** (V007); **`GET …/gig-progression`** ([NEO-44 plan](../../plans/NEO-44-implementation-plan.md)); [server README — Gig progression (NEO-44)](../../../server/README.md#gig-progression-snapshot-neo-44). **NEO-84 landed:** comment-only **`ability_used`** / **`enemy_defeat`** telemetry hook sites in **`CombatOperations.TryResolve`**; reserved **`encounter_start`** / **`player_death`** ([NEO-84 plan](../../plans/NEO-84-implementation-plan.md)); [server README — Combat telemetry hooks (NEO-84)](../../../server/README.md#combat-telemetry-hooks-neo-84). **NEO-85 landed:** client combat HUD — **`combat_targets_client.gd`**, **`CombatTargetHpLabel`**, **`CastFeedbackLabel`** resolution copy; event-driven **`GET /game/world/combat-targets`** refresh ([NEO-85 plan](../../plans/NEO-85-implementation-plan.md), [`NEO-85` manual QA](../../manual-qa/NEO-85.md)); [client README — Combat feedback + target HP HUD (NEO-85)](../../../client/README.md#combat-feedback--target-hp-hud-neo-85). **NEO-86 landed:** playable combat capstone — **`gig_progression_client.gd`**, **`GigXpLabel`**, defeat-triggered gig GET refresh; capstone manual QA [`NEO-86`](../../manual-qa/NEO-86.md) ([NEO-86 plan](../../plans/NEO-86-implementation-plan.md)); [client README — End-to-end combat loop (NEO-86)](../../../client/README.md#end-to-end-combat-loop-neo-86). **Epic 5 Slice 1 complete — register row Ready.**
|
||||
|
||||
**E5.M2 note:** Epic 5 **Slice 2** 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-87](https://linear.app/neon-sprawl/issue/NEO-87) → [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98); label **`E5.M2`**. Client capstone **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98). See [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md), [E5_M2_NpcAiAndBehaviorProfiles.md](E5_M2_NpcAiAndBehaviorProfiles.md). Upstream **E5.M1 Ready**. **NEO-87 landed:** frozen three-behavior catalog + CI ([NEO-87 plan](../../plans/NEO-87-implementation-plan.md)). **NEO-88 landed:** fail-fast server load of `content/npc-behaviors/*_npc_behaviors.json` ([NEO-88 plan](../../plans/NEO-88-implementation-plan.md)); [server README — NPC behavior catalog](../../../server/README.md#npc-behavior-catalog-contentnpc-behaviors-neo-88). **NEO-89 landed:** injectable **`INpcBehaviorDefinitionRegistry`** + DI ([NEO-89 plan](../../plans/NEO-89-implementation-plan.md)). Replaces **`prototype_target_alpha` / `beta`** with three NPC archetype instances; owns **`ThreatState`**, **`TelegraphEvent`**, session **`IPlayerCombatHealthStore`**.
|
||||
**E5.M2 note:** Epic 5 **Slice 2** 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-87](https://linear.app/neon-sprawl/issue/NEO-87) → [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98); label **`E5.M2`**. Client capstone **E5M2-12** [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98). See [E5M2-prototype-backlog.md](../../plans/E5M2-prototype-backlog.md), [E5_M2_NpcAiAndBehaviorProfiles.md](E5_M2_NpcAiAndBehaviorProfiles.md). Upstream **E5.M1 Ready**. **NEO-87 landed:** frozen three-behavior catalog + CI ([NEO-87 plan](../../plans/NEO-87-implementation-plan.md)). **NEO-88 landed:** fail-fast server load of `content/npc-behaviors/*_npc_behaviors.json` ([NEO-88 plan](../../plans/NEO-88-implementation-plan.md)); [server README — NPC behavior catalog](../../../server/README.md#npc-behavior-catalog-contentnpc-behaviors-neo-88). **NEO-89 landed:** injectable **`INpcBehaviorDefinitionRegistry`** + DI ([NEO-89 plan](../../plans/NEO-89-implementation-plan.md)). **NEO-90 landed:** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/` ([NEO-90 plan](../../plans/NEO-90-implementation-plan.md)); [server README — NPC behavior definitions (NEO-90)](../../../server/README.md#npc-behavior-definitions-neo-90); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`. Replaces **`prototype_target_alpha` / `beta`** with three NPC archetype instances; owns **`ThreatState`**, **`TelegraphEvent`**, session **`IPlayerCombatHealthStore`**.
|
||||
|
||||
### Epic 6 — PvP Security
|
||||
|
||||
|
|
|
|||
|
|
@ -165,8 +165,10 @@ Working backlog for **Epic 5 — Slice 2** ([NPC archetypes and telegraphs](../d
|
|||
|
||||
**Acceptance criteria**
|
||||
|
||||
- [ ] GET returns all three defs in ascending `id` order with `schemaVersion` **1**.
|
||||
- [ ] Bruno happy GET passes in CI Bruno step.
|
||||
- [x] GET returns all three defs in ascending `id` order with `schemaVersion` **1**.
|
||||
- [x] Bruno happy GET passes in CI Bruno step.
|
||||
|
||||
**Landed ([NEO-90](https://linear.app/neon-sprawl/issue/NEO-90)):** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/`; plan [NEO-90-implementation-plan.md](NEO-90-implementation-plan.md); Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/`.
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,125 @@
|
|||
# NEO-90 — Implementation plan
|
||||
|
||||
## Story reference
|
||||
|
||||
| Field | Value |
|
||||
|--------|--------|
|
||||
| **Key** | NEO-90 |
|
||||
| **Title** | E5M2-04: GET `/game/world/npc-behavior-definitions` |
|
||||
| **Linear** | https://linear.app/neon-sprawl/issue/NEO-90/e5m2-04-get-gameworldnpc-behavior-definitions |
|
||||
| **Module** | [E5.M2 — NpcAiAndBehaviorProfiles](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) · Epic 5 Slice 2 · backlog **E5M2-04** |
|
||||
| **Branch** | `NEO-90-npc-behavior-definitions-get` |
|
||||
| **Precursor** | [NEO-89](https://linear.app/neon-sprawl/issue/NEO-89) — `INpcBehaviorDefinitionRegistry` + DI (**Done** on `main`) |
|
||||
| **Pattern** | [NEO-78](https://linear.app/neon-sprawl/issue/NEO-78) — `GET /game/world/ability-definitions`; [NEO-68](https://linear.app/neon-sprawl/issue/NEO-68) — recipe definitions world GET |
|
||||
| **Client counterpart** | None (server-only); player-visible labels/HUD land in [NEO-97](https://linear.app/neon-sprawl/issue/NEO-97) / capstone [NEO-98](https://linear.app/neon-sprawl/issue/NEO-98) |
|
||||
|
||||
## Kickoff clarifications
|
||||
|
||||
| Topic | Question | Agent recommendation | Answer |
|
||||
|--------|----------|----------------------|--------|
|
||||
| **Response array key** | Top-level JSON array name? | **`npcBehaviors`** — matches `content/npc-behaviors/*.json` catalog envelope. | **User:** `npcBehaviors`. |
|
||||
| **Manual QA doc** | Add `docs/manual-qa/NEO-90.md`? | **Skip** — server-only HTTP; Bruno + integration tests (NEO-78/89 pattern). | **User:** skip. |
|
||||
|
||||
No other blocking decisions — route path, `schemaVersion` **1**, registry injection, row fields (all required schema fields), and frozen three-id order are specified in [E5M2-04](E5M2-prototype-backlog.md#e5m2-04--get-gameworldnpc-behavior-definitions) and the [E5.M2 freeze box](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01).
|
||||
|
||||
## Goal, scope, and out-of-scope
|
||||
|
||||
**Goal:** Expose a stable, read-only JSON endpoint that returns all three loaded prototype NPC behavior definitions for Bruno, tooling, and future client labels ([NEO-97](https://linear.app/neon-sprawl/issue/NEO-97)) — backed by **`INpcBehaviorDefinitionRegistry`** without duplicating catalog truth.
|
||||
|
||||
**In scope (from Linear + [E5M2-04](E5M2-prototype-backlog.md#e5m2-04--get-gameworldnpc-behavior-definitions)):**
|
||||
|
||||
- **`GET /game/world/npc-behavior-definitions`** with versioned envelope (`schemaVersion` **1**, **`npcBehaviors`** array).
|
||||
- Row fields: **`id`**, **`displayName`**, **`archetypeKind`**, **`maxHp`**, **`aggroRadius`**, **`leashRadius`**, **`telegraphWindupSeconds`**, **`attackDamage`**, **`attackCooldownSeconds`** (all required in v1; no optional omissions).
|
||||
- Behaviors ordered by **`id`** ordinal, matching **`GetDefinitionsInIdOrder()`**.
|
||||
- Bruno folder `bruno/neon-sprawl-server/npc-behavior-definitions/`.
|
||||
- `server/README.md` route section.
|
||||
- API integration tests (AAA).
|
||||
|
||||
**Out of scope (from Linear + backlog):**
|
||||
|
||||
- Runtime state, telegraphs, aggro, NPC instance registry ([NEO-91+](E5M2-prototype-backlog.md)).
|
||||
- Godot client wiring ([NEO-97](https://linear.app/neon-sprawl/issue/NEO-97)).
|
||||
- `docs/manual-qa/NEO-90.md` (kickoff decision).
|
||||
|
||||
## Acceptance criteria checklist
|
||||
|
||||
- [x] GET returns all **three** prototype behavior defs with **`schemaVersion`** **1** and stable field names.
|
||||
- [x] Rows appear in ascending **`id`** order (ordinal).
|
||||
- [x] Bruno happy GET passes in CI Bruno step.
|
||||
|
||||
## Technical approach
|
||||
|
||||
1. **Route:** **`GET /game/world/npc-behavior-definitions`** — parameterless read; inject **`INpcBehaviorDefinitionRegistry`** only (not **`NpcBehaviorDefinitionCatalog`**).
|
||||
|
||||
2. **Response shape:** Top-level **`schemaVersion`** (`NpcBehaviorDefinitionsListResponse.CurrentSchemaVersion` = **1**) plus **`npcBehaviors`** array. Each row maps from **`NpcBehaviorDefRow`**: all nine schema fields with camelCase JSON names matching existing world-definition APIs.
|
||||
|
||||
3. **Ordering:** Build list from **`registry.GetDefinitionsInIdOrder()`** (already ordinal by id). Tests assert exact id sequence (frozen three from [E5.M2 freeze box](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01)):
|
||||
|
||||
`prototype_elite_mini_boss`, `prototype_melee_pressure`, `prototype_ranged_control`
|
||||
|
||||
(ordinal sort of the three ids — same order as **`NpcBehaviorDefinitionRegistryTests`** / **`PrototypeNpcBehaviorRegistry`** constants).
|
||||
|
||||
4. **Implementation:** New **`NpcBehaviorDefinitionsWorldApi`** + **`NpcBehaviorDefinitionsListDtos.cs`** in `Game/Npc/` (mirror [`AbilityDefinitionsWorldApi`](../../server/NeonSprawl.Server/Game/Combat/AbilityDefinitionsWorldApi.cs)). Wire **`app.MapNpcBehaviorDefinitionsWorldApi()`** from **`Program.cs`** after other world definition APIs (near **`MapAbilityDefinitionsWorldApi()`**).
|
||||
|
||||
5. **Bruno:** `bruno/neon-sprawl-server/npc-behavior-definitions/` with **`Get npc behavior definitions.bru`** + **`folder.bru`**. Tests: 200, JSON, **`schemaVersion` 1**, **`npcBehaviors.length === 3`**, ascending id order, **exact frozen-three registry sequence**, spot-check **`prototype_melee_pressure`** and **`prototype_elite_mini_boss`** metadata.
|
||||
|
||||
6. **Docs:** Add **`server/README.md`** subsection under NPC behavior catalog documenting GET + curl example. Update [E5_M2](E5_M2_NpcAiAndBehaviorProfiles.md) **Related implementation slices** and [documentation_and_implementation_alignment.md](documentation_and_implementation_alignment.md) E5.M2 row when landed.
|
||||
|
||||
### Expected prototype row values (from content)
|
||||
|
||||
| `id` | `displayName` | `archetypeKind` | `maxHp` | `aggroRadius` | `leashRadius` | `telegraphWindupSeconds` | `attackDamage` | `attackCooldownSeconds` |
|
||||
|------|---------------|-----------------|---------|---------------|---------------|--------------------------|----------------|-------------------------|
|
||||
| `prototype_melee_pressure` | Melee Pressure | `melee_pressure` | 100 | 8.0 | 16.0 | 1.5 | 15 | 3.0 |
|
||||
| `prototype_ranged_control` | Ranged Control | `ranged_control` | 80 | 10.0 | 20.0 | 2.0 | 12 | 4.0 |
|
||||
| `prototype_elite_mini_boss` | Elite Mini-Boss | `elite_mini_boss` | 200 | 8.0 | 18.0 | 2.5 | 25 | 5.0 |
|
||||
|
||||
## Files to add
|
||||
|
||||
| Path | Purpose |
|
||||
|------|---------|
|
||||
| `server/NeonSprawl.Server/Game/Npc/NpcBehaviorDefinitionsWorldApi.cs` | `Map*` extension registering GET route; maps registry → JSON. |
|
||||
| `server/NeonSprawl.Server/Game/Npc/NpcBehaviorDefinitionsListDtos.cs` | Versioned response + row DTOs for JSON serialization. |
|
||||
| `server/NeonSprawl.Server.Tests/Game/Npc/NpcBehaviorDefinitionsWorldApiTests.cs` | HTTP integration: 200, schema v1, three ids in order, spot-check rows. |
|
||||
| `bruno/neon-sprawl-server/npc-behavior-definitions/Get npc behavior definitions.bru` | Manual / Bruno verification against dev server. |
|
||||
| `bruno/neon-sprawl-server/npc-behavior-definitions/folder.bru` | Bruno folder metadata (match `ability-definitions`). |
|
||||
| `docs/plans/NEO-90-implementation-plan.md` | This plan. |
|
||||
|
||||
## Files to modify
|
||||
|
||||
| Path | Rationale |
|
||||
|------|-----------|
|
||||
| `server/NeonSprawl.Server/Program.cs` | Register `MapNpcBehaviorDefinitionsWorldApi()` alongside other game world definition APIs. |
|
||||
| `server/README.md` | Document `GET /game/world/npc-behavior-definitions`, curl example, links to plan/Bruno. |
|
||||
| `docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md` | **Related implementation slices** — HTTP read model bullet (NEO-90). |
|
||||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E5.M2 row — note NEO-90 HTTP projection when landed. |
|
||||
| `docs/plans/E5M2-prototype-backlog.md` | E5M2-04 acceptance checkboxes + landed note when complete. |
|
||||
|
||||
## Tests
|
||||
|
||||
| File | Coverage |
|
||||
|------|----------|
|
||||
| `server/NeonSprawl.Server.Tests/Game/Npc/NpcBehaviorDefinitionsWorldApiTests.cs` | **Integration:** `GET /game/world/npc-behavior-definitions` via **`InMemoryWebApplicationFactory`**; **`ReadFromJsonAsync`**; assert **`schemaVersion` 1**; **`npcBehaviors`** count **3**; ordered **`id`** list equals frozen three in ordinal id order; spot-check **`prototype_melee_pressure`** (maxHp 100, attackDamage 15, archetypeKind `melee_pressure`) and **`prototype_elite_mini_boss`** (maxHp 200, attackDamage 25, telegraphWindupSeconds 2.5). **AAA** per [csharp-style](../../.cursor/rules/csharp-style.md). |
|
||||
|
||||
Bruno scripts complement automated tests for manual dev-server verification. No `docs/manual-qa/NEO-90.md` (server-only per kickoff).
|
||||
|
||||
## Open questions / risks
|
||||
|
||||
| Question / risk | Agent recommendation | Status |
|
||||
|-----------------|---------------------|--------|
|
||||
| **Linear blockedBy NEO-89** | Branch from **`main`** where NEO-89 is merged (confirmed at kickoff). | **adopted** |
|
||||
| **Optional fields later** | When content adds new optional behavior-def fields, bump **`schemaVersion`** or extend v1 in a follow-up issue. | **deferred** |
|
||||
| **Client not in this story** | Cross-link **NEO-97** / **NEO-98**; Bruno is not prototype-complete per [full-stack epic decomposition](../../.cursor/rules/full-stack-epic-decomposition.md). | **adopted** |
|
||||
| **Numeric JSON types** | Emit radii/timings as JSON **number** (double); HP/damage as **integer**; tests assert numeric equality. | **adopted** |
|
||||
|
||||
## Decisions (kickoff)
|
||||
|
||||
- **`npcBehaviors`** top-level array key — matches content catalog envelope (user confirmed).
|
||||
- **No manual QA doc** — server-only HTTP; Bruno + automated tests (user confirmed).
|
||||
- **Registry-only injection** — HTTP layer depends on **`INpcBehaviorDefinitionRegistry`**, not **`NpcBehaviorDefinitionCatalog`**.
|
||||
|
||||
## Reconciliation (implementation)
|
||||
|
||||
- **`NpcBehaviorDefinitionsWorldApi`** + **`NpcBehaviorDefinitionsListDtos`** registered via **`MapNpcBehaviorDefinitionsWorldApi()`** in **`Program.cs`**; injects **`INpcBehaviorDefinitionRegistry`** only.
|
||||
- **`NpcBehaviorDefinitionsWorldApiTests`** — integration test for schema v1, frozen-three id order, **`prototype_melee_pressure`** / **`prototype_elite_mini_boss`** spot-checks.
|
||||
- **Bruno:** `bruno/neon-sprawl-server/npc-behavior-definitions/Get npc behavior definitions.bru` with response-shape tests.
|
||||
- **`server/README.md`**, **E5.M2** module doc, **alignment register**, and **E5M2 backlog** updated.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# Code review — NEO-90 GET `/game/world/npc-behavior-definitions`
|
||||
|
||||
**Date:** 2026-05-25
|
||||
**Scope:** Branch `NEO-90-npc-behavior-definitions-get` · commits `c757b7a`–`d3e765f` vs `origin/main`
|
||||
**Base:** `origin/main`
|
||||
|
||||
## Verdict
|
||||
|
||||
**Approve with nits**
|
||||
|
||||
## Summary
|
||||
|
||||
NEO-90 delivers **E5M2-04**: a read-only **`GET /game/world/npc-behavior-definitions`** projection backed solely by **`INpcBehaviorDefinitionRegistry`**, mirroring the NEO-78 ability-definitions pattern. Implementation adds `NpcBehaviorDefinitionsWorldApi`, versioned DTOs (`schemaVersion` **1**, **`npcBehaviors`**), Bruno smokes, integration test, and reconciled plan/backlog/module/alignment docs. The HTTP layer maps all nine schema fields from `GetDefinitionsInIdOrder()` without duplicating catalog truth. One new AAA integration test passes; **29** Npc-filter tests green. No runtime, instance registry, or client surface — appropriately scoped for a server-only HTTP read model ahead of NEO-97/NEO-98. Risk is low.
|
||||
|
||||
## Documentation checked
|
||||
|
||||
| Document | Result |
|
||||
|----------|--------|
|
||||
| [`docs/plans/NEO-90-implementation-plan.md`](../plans/NEO-90-implementation-plan.md) | **Matches** — kickoff decisions (`npcBehaviors` key, no manual QA), scope, acceptance checklist, reconciliation section. |
|
||||
| [`docs/plans/E5M2-prototype-backlog.md`](../plans/E5M2-prototype-backlog.md) · **E5M2-04** | **Matches** — AC checkboxes + landed note. |
|
||||
| [`docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md`](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) | **Matches** — Status, Related slices, NEO-90 implementation blurb, **Linear** summary row, and freeze **Rules** cross-link updated (review follow-up). |
|
||||
| [`docs/decomposition/modules/documentation_and_implementation_alignment.md`](../decomposition/modules/documentation_and_implementation_alignment.md) · **E5.M2** | **Matches** — NEO-90 landed note with README + Bruno links. |
|
||||
| [`docs/decomposition/modules/module_dependency_register.md`](../decomposition/modules/module_dependency_register.md) · **E5.M2 note** | **Matches** — NEO-90 HTTP projection note added (review follow-up). |
|
||||
| [`docs/decomposition/modules/client_server_authority.md`](../decomposition/modules/client_server_authority.md) | **N/A** — read-only world definition GET; no runtime state or client mutation. |
|
||||
| [`server/README.md`](../../server/README.md) | **Matches** — NPC behavior definitions section with curl example and cross-links. |
|
||||
| [`server/NeonSprawl.Server/Game/Combat/AbilityDefinitionsWorldApi.cs`](../../server/NeonSprawl.Server/Game/Combat/AbilityDefinitionsWorldApi.cs) (pattern reference) | **Matches** — structurally identical registry → DTO → `Results.Json` mapping. |
|
||||
| Full-stack epic decomposition | **Matches** — server-only; kickoff defers Godot labels to NEO-97/NEO-98; no false prototype-complete claim. |
|
||||
| Manual QA | **N/A** — plan documents skip: Bruno + integration tests only. |
|
||||
|
||||
Register/tracking: alignment table E5.M2 **In Progress** with NEO-90 note is correct; module register **E5.M2 note** extended (review follow-up).
|
||||
|
||||
## Blocking issues
|
||||
|
||||
None.
|
||||
|
||||
## Suggestions
|
||||
|
||||
1. ~~**Update `module_dependency_register.md` E5.M2 note** — Append **NEO-90 landed:** **`GET /game/world/npc-behavior-definitions`** — `NpcBehaviorDefinitionsWorldApi` + DTOs in `Game/Npc/` with link to [NEO-90 plan](../plans/NEO-90-implementation-plan.md), [server README — NPC behavior definitions (NEO-90)](../../server/README.md#npc-behavior-definitions-neo-90), and Bruno `bruno/neon-sprawl-server/npc-behavior-definitions/` (same pattern as E5.M1 note after NEO-78).~~ **Done.**
|
||||
|
||||
2. ~~**E5.M2 Linear summary row** — In [`E5_M2_NpcAiAndBehaviorProfiles.md`](../decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md), extend the **Linear** field (Summary table line 11) to cite **E5M2-04** [NEO-90](https://linear.app/neon-sprawl/issue/NEO-90) so it matches the updated **Status** row and the decomposed backlog table below.~~ **Done.**
|
||||
|
||||
## Nits
|
||||
|
||||
- Nit: Neither C# nor Bruno tests assert **`prototype_ranged_control`** row metadata — acceptable per plan (melee + elite spot-checks only); optional ranged row assertion if NEO-97 client labels want a third anchor.
|
||||
|
||||
- Nit: **`FrozenThreeInIdOrder`** in tests uses **`PrototypeNpcBehaviorRegistry`** constants (good); Bruno hard-codes string literals — same established split as ability-definitions Bruno vs C# tests.
|
||||
|
||||
- ~~Nit: Freeze **Rules** paragraph cites registry injection (NEO-89) but not the NEO-90 GET endpoint by name — the new NEO-90 blurb below covers it; optional one-line cross-link in Rules for skimmers.~~ **Done.**
|
||||
|
||||
## Verification
|
||||
|
||||
```bash
|
||||
cd /home/don/neon-sprawl/server
|
||||
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --filter "FullyQualifiedName~NpcBehaviorDefinitions"
|
||||
dotnet test NeonSprawl.Server.Tests/NeonSprawl.Server.Tests.csproj --filter "FullyQualifiedName~NpcBehavior"
|
||||
```
|
||||
|
||||
Confirmed during review: **1** NpcBehaviorDefinitions test passed; **29** Npc-filter tests passed.
|
||||
|
||||
Optional: run Bruno `Get npc behavior definitions.bru` against a dev server (`curl` example in `server/README.md`).
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using NeonSprawl.Server.Game.Npc;
|
||||
using Xunit;
|
||||
|
||||
namespace NeonSprawl.Server.Tests.Game.Npc;
|
||||
|
||||
public class NpcBehaviorDefinitionsWorldApiTests
|
||||
{
|
||||
/// <summary>Frozen prototype three in registry id order (ordinal). Keep in sync with Bruno.</summary>
|
||||
public static readonly string[] FrozenThreeInIdOrder =
|
||||
[
|
||||
PrototypeNpcBehaviorRegistry.PrototypeEliteMiniBoss,
|
||||
PrototypeNpcBehaviorRegistry.PrototypeMeleePressure,
|
||||
PrototypeNpcBehaviorRegistry.PrototypeRangedControl,
|
||||
];
|
||||
|
||||
[Fact]
|
||||
public async Task GetNpcBehaviorDefinitions_ShouldReturnSchemaV1_WithFrozenThreeInIdOrder()
|
||||
{
|
||||
// Arrange
|
||||
await using var factory = new InMemoryWebApplicationFactory();
|
||||
var client = factory.CreateClient();
|
||||
// Act
|
||||
var response = await client.GetAsync("/game/world/npc-behavior-definitions");
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
var body = await response.Content.ReadFromJsonAsync<NpcBehaviorDefinitionsListResponse>();
|
||||
Assert.NotNull(body);
|
||||
Assert.Equal(NpcBehaviorDefinitionsListResponse.CurrentSchemaVersion, body!.SchemaVersion);
|
||||
Assert.NotNull(body.NpcBehaviors);
|
||||
Assert.Equal(3, body.NpcBehaviors.Count);
|
||||
var ids = body.NpcBehaviors.Select(static b => b.Id).ToList();
|
||||
Assert.Equal(FrozenThreeInIdOrder, ids);
|
||||
|
||||
var melee = body.NpcBehaviors.Single(b => b.Id == PrototypeNpcBehaviorRegistry.PrototypeMeleePressure);
|
||||
Assert.Equal("Melee Pressure", melee.DisplayName);
|
||||
Assert.Equal("melee_pressure", melee.ArchetypeKind);
|
||||
Assert.Equal(100, melee.MaxHp);
|
||||
Assert.Equal(8.0, melee.AggroRadius);
|
||||
Assert.Equal(16.0, melee.LeashRadius);
|
||||
Assert.Equal(1.5, melee.TelegraphWindupSeconds);
|
||||
Assert.Equal(15, melee.AttackDamage);
|
||||
Assert.Equal(3.0, melee.AttackCooldownSeconds);
|
||||
|
||||
var elite = body.NpcBehaviors.Single(b => b.Id == PrototypeNpcBehaviorRegistry.PrototypeEliteMiniBoss);
|
||||
Assert.Equal("Elite Mini-Boss", elite.DisplayName);
|
||||
Assert.Equal("elite_mini_boss", elite.ArchetypeKind);
|
||||
Assert.Equal(200, elite.MaxHp);
|
||||
Assert.Equal(8.0, elite.AggroRadius);
|
||||
Assert.Equal(18.0, elite.LeashRadius);
|
||||
Assert.Equal(2.5, elite.TelegraphWindupSeconds);
|
||||
Assert.Equal(25, elite.AttackDamage);
|
||||
Assert.Equal(5.0, elite.AttackCooldownSeconds);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace NeonSprawl.Server.Game.Npc;
|
||||
|
||||
/// <summary>JSON body for <c>GET /game/world/npc-behavior-definitions</c> (NEO-90).</summary>
|
||||
public sealed class NpcBehaviorDefinitionsListResponse
|
||||
{
|
||||
public const int CurrentSchemaVersion = 1;
|
||||
|
||||
[JsonPropertyName("schemaVersion")]
|
||||
public int SchemaVersion { get; init; } = CurrentSchemaVersion;
|
||||
|
||||
/// <summary>Loaded behavior defs ordered by stable <c>id</c> (ordinal), matching <see cref="INpcBehaviorDefinitionRegistry.GetDefinitionsInIdOrder"/>.</summary>
|
||||
[JsonPropertyName("npcBehaviors")]
|
||||
public required IReadOnlyList<NpcBehaviorDefinitionJson> NpcBehaviors { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>One row in the read-only NPC behavior definition projection.</summary>
|
||||
public sealed class NpcBehaviorDefinitionJson
|
||||
{
|
||||
[JsonPropertyName("id")]
|
||||
public required string Id { get; init; }
|
||||
|
||||
[JsonPropertyName("displayName")]
|
||||
public required string DisplayName { get; init; }
|
||||
|
||||
[JsonPropertyName("archetypeKind")]
|
||||
public required string ArchetypeKind { get; init; }
|
||||
|
||||
[JsonPropertyName("maxHp")]
|
||||
public required int MaxHp { get; init; }
|
||||
|
||||
[JsonPropertyName("aggroRadius")]
|
||||
public required double AggroRadius { get; init; }
|
||||
|
||||
[JsonPropertyName("leashRadius")]
|
||||
public required double LeashRadius { get; init; }
|
||||
|
||||
[JsonPropertyName("telegraphWindupSeconds")]
|
||||
public required double TelegraphWindupSeconds { get; init; }
|
||||
|
||||
[JsonPropertyName("attackDamage")]
|
||||
public required int AttackDamage { get; init; }
|
||||
|
||||
[JsonPropertyName("attackCooldownSeconds")]
|
||||
public required double AttackCooldownSeconds { get; init; }
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
namespace NeonSprawl.Server.Game.Npc;
|
||||
|
||||
/// <summary>Maps <c>GET /game/world/npc-behavior-definitions</c> (NEO-90).</summary>
|
||||
public static class NpcBehaviorDefinitionsWorldApi
|
||||
{
|
||||
public static WebApplication MapNpcBehaviorDefinitionsWorldApi(this WebApplication app)
|
||||
{
|
||||
app.MapGet(
|
||||
"/game/world/npc-behavior-definitions",
|
||||
(INpcBehaviorDefinitionRegistry registry) =>
|
||||
{
|
||||
var defs = registry.GetDefinitionsInIdOrder();
|
||||
var npcBehaviors = new List<NpcBehaviorDefinitionJson>(defs.Count);
|
||||
foreach (var d in defs)
|
||||
{
|
||||
npcBehaviors.Add(
|
||||
new NpcBehaviorDefinitionJson
|
||||
{
|
||||
Id = d.Id,
|
||||
DisplayName = d.DisplayName,
|
||||
ArchetypeKind = d.ArchetypeKind,
|
||||
MaxHp = d.MaxHp,
|
||||
AggroRadius = d.AggroRadius,
|
||||
LeashRadius = d.LeashRadius,
|
||||
TelegraphWindupSeconds = d.TelegraphWindupSeconds,
|
||||
AttackDamage = d.AttackDamage,
|
||||
AttackCooldownSeconds = d.AttackCooldownSeconds,
|
||||
});
|
||||
}
|
||||
|
||||
return Results.Json(
|
||||
new NpcBehaviorDefinitionsListResponse
|
||||
{
|
||||
SchemaVersion = NpcBehaviorDefinitionsListResponse.CurrentSchemaVersion,
|
||||
NpcBehaviors = npcBehaviors,
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ app.MapSkillDefinitionsWorldApi();
|
|||
app.MapItemDefinitionsWorldApi();
|
||||
app.MapRecipeDefinitionsWorldApi();
|
||||
app.MapAbilityDefinitionsWorldApi();
|
||||
app.MapNpcBehaviorDefinitionsWorldApi();
|
||||
app.MapCombatTargetsWorldApi();
|
||||
app.MapResourceNodeDefinitionsWorldApi();
|
||||
app.MapPlayerInventoryApi();
|
||||
|
|
|
|||
|
|
@ -106,6 +106,14 @@ On startup the host loads every **`*_npc_behaviors.json`** under the npc-behavio
|
|||
|
||||
On success, **Information** logs include the resolved npc-behaviors directory path, distinct behavior count, and catalog file count. Game code should use **`INpcBehaviorDefinitionRegistry`** for lookups (`TryGetDefinition`, `TryNormalizeKnown`, `GetDefinitionsInIdOrder`; NEO-89). The catalog singleton remains for fail-fast startup only; do not inject **`NpcBehaviorDefinitionCatalog`** in new game code. Stable prototype behavior id constants: **`PrototypeNpcBehaviorRegistry`**.
|
||||
|
||||
## NPC behavior definitions (NEO-90)
|
||||
|
||||
**`GET /game/world/npc-behavior-definitions`** returns a versioned JSON body (`schemaVersion` **1**, **`npcBehaviors`**) backed by **`INpcBehaviorDefinitionRegistry`** — the same prototype rows loaded at startup (no second source of truth). Each row includes **`id`**, **`displayName`**, **`archetypeKind`**, **`maxHp`**, **`aggroRadius`**, **`leashRadius`**, **`telegraphWindupSeconds`**, **`attackDamage`**, and **`attackCooldownSeconds`**. Plan: [NEO-90 implementation plan](../../docs/plans/NEO-90-implementation-plan.md); Bruno: `bruno/neon-sprawl-server/npc-behavior-definitions/`.
|
||||
|
||||
```bash
|
||||
curl -sS -i "http://localhost:5253/game/world/npc-behavior-definitions"
|
||||
```
|
||||
|
||||
## Ability definitions (NEO-78)
|
||||
|
||||
**`GET /game/world/ability-definitions`** returns a versioned JSON body (`schemaVersion` **1**, **`abilities`**) backed by **`IAbilityDefinitionRegistry`** — the same prototype rows loaded at startup (no second source of truth). Each row includes **`id`**, **`displayName`**, **`baseDamage`**, **`cooldownSeconds`**, and **`abilityKind`** when present in content. Plan: [NEO-78 implementation plan](../../docs/plans/NEO-78-implementation-plan.md); Bruno: `bruno/neon-sprawl-server/ability-definitions/`.
|
||||
|
|
|
|||
Loading…
Reference in New Issue