neon-sprawl/content/README.md

54 lines
12 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.

# Content data
Data-driven definitions (skills, items, recipes, quests) validated in CI with **JSON Schema**.
| Path | Purpose |
|------|---------|
| [`schemas/`](schemas/) | JSON Schema files (`*.schema.json`) |
| [`skills/`](skills/) | Skill catalogs; each row matches [`schemas/skill-def.schema.json`](schemas/skill-def.schema.json) — **stable `id`**, **`allowedXpSourceKinds`** for [E2.M1](../docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md) / [E2.M2](../docs/decomposition/modules/E2_M2_XpAwardAndLevelEngine.md) |
| [`mastery/`](mastery/) | Mastery catalogs; each file matches [`schemas/mastery-catalog.schema.json`](schemas/mastery-catalog.schema.json) — **stable `branchId` / `perkId`**, tier gates via skill level ([E2.M3](../docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md)) |
| [`items/`](items/) | Item catalogs; each row matches [`schemas/item-def.schema.json`](schemas/item-def.schema.json) — **stable `id`**, **`stackMax`**, **`inventorySlotKind`** for [E3.M3](../docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md) |
| [`recipes/`](recipes/) | Recipe catalogs; each row matches [`schemas/recipe-def.schema.json`](schemas/recipe-def.schema.json) — **stable `id`**, **`recipeKind`**, **`requiredSkillId`**, inputs/outputs for [E3.M2](../docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) |
| [`abilities/`](abilities/) | Combat ability catalogs; each row matches [`schemas/ability-def.schema.json`](schemas/ability-def.schema.json) — **stable `id`**, **`baseDamage`**, **`cooldownSeconds`** for [E5.M1](../docs/decomposition/modules/E5_M1_CombatRulesEngine.md) |
| [`npc-behaviors/`](npc-behaviors/) | NPC behavior archetype catalogs; each row matches [`schemas/npc-behavior-def.schema.json`](schemas/npc-behavior-def.schema.json) — **stable `id`**, **`archetypeKind`**, aggro/leash radii, telegraph + attack timings for [E5.M2](../docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md) |
| [`encounters/`](encounters/) | Encounter templates; each row matches [`schemas/encounter-def.schema.json`](schemas/encounter-def.schema.json) — **stable `id`**, completion criteria, **`rewardTableId`** for [E5.M3](../docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md) |
| [`reward-tables/`](reward-tables/) | Reward table catalogs; each row matches [`schemas/reward-table.schema.json`](schemas/reward-table.schema.json) — **stable `id`**, **`fixedGrants`** referencing frozen item ids for [E5.M3](../docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md) |
| [`quests/`](quests/) | Quest catalogs; each row matches [`schemas/quest-def.schema.json`](schemas/quest-def.schema.json) (steps via [`quest-step-def.schema.json`](schemas/quest-step-def.schema.json), objectives via [`quest-objective-def.schema.json`](schemas/quest-objective-def.schema.json)) — **stable `id`**, **`prerequisiteQuestIds`**, optional **`factionGateRules`**, step/objective graph for [E7.M1](../docs/decomposition/modules/E7_M1_QuestStateMachine.md) / [E7.M3](../docs/decomposition/modules/E7_M3_FactionReputationLedger.md) |
| [`factions/`](factions/) | Faction catalogs; each row matches [`schemas/faction-def.schema.json`](schemas/faction-def.schema.json) — **stable `id`**, standing band for [E7.M3](../docs/decomposition/modules/E7_M3_FactionReputationLedger.md) |
| [`contracts/`](contracts/) | Contract template catalogs; each row matches [`schemas/contract-template.schema.json`](schemas/contract-template.schema.json) — **stable `id`**, **`zoneDifficultyBand`**, **`encounterTemplateId`**, **`minFactionStanding`**, **`completionRewardBundle`** for [E7.M4](../docs/decomposition/modules/E7_M4_ContractMissionGenerator.md) |
| [`resource-nodes/`](resource-nodes/) | Resource node + yield catalogs; node rows match [`schemas/resource-node-def.schema.json`](schemas/resource-node-def.schema.json), yield rows match [`schemas/resource-yield-row.schema.json`](schemas/resource-yield-row.schema.json) — **stable `nodeDefId`**, **`gatherLens`**, **`maxGathers`** for [E3.M1](../docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) |
**Prototype Slice 1 (NEO-33):** CI expects **exactly three** skill rows with ids **`salvage`**, **`refine`**, **`intrusion`** (gather + process + tech). Each row must declare **`allowedXpSourceKinds`** (non-empty); see [E2.M1 — Designer note: `allowedXpSourceKinds`](../docs/decomposition/modules/E2_M1_SkillDefinitionRegistry.md#designer-note-allowedxpsourcekinds-and-grant-call-sites) for what each kind means for grant wiring.
**Prototype Slice 2 — resource nodes (NEO-57):** CI expects **exactly four** `nodeDefId` values aligned to [E3.M1 gather lens freeze](../docs/decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md#prototype-slice-2-freeze-e3m1-01) — one row per **`gatherLens`**; exactly one yield row per node referencing **`scrap_metal_bulk`** only. **Do not rename** `nodeDefId` after ship. See [E3M1-prototype-backlog.md](../docs/plans/E3M1-prototype-backlog.md) and [NEO-57 plan](../docs/plans/NEO-57-implementation-plan.md).
**Prototype Slice 1 — items (NEO-50):** CI expects **exactly six** item rows with ids **`scrap_metal_bulk`**, **`refined_plate_stock`**, **`field_stim_mk0`**, **`survey_drone_kit`**, **`contract_handoff_token`**, **`prototype_armor_shell`** — one row per **`prototypeRole`** (`material` through `equip_stub`). **Do not rename** ids after ship—change **`displayName`** only. See [E3.M3 — Designer note](../docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md#designer-note-stack-slot-and-v1-scope) and [freeze table](../docs/decomposition/modules/E3_M3_ItemizationAndInventorySchema.md#prototype-slice-1-freeze-neo-50).
**Prototype Slice 3 — recipes (E3M2-01):** CI expects **exactly eight** recipe ids aligned to [E3.M2 recipe freeze](../docs/decomposition/modules/E3_M2_RefinementAndRecipeExecution.md#prototype-slice-3-freeze-e3m2-01) — at least one **`process`** and one **`make`** row; all **`requiredSkillId`** values **`refine`**; every input/output **`itemId`** from the frozen item catalog. **Do not rename** recipe `id` after ship. See [E3M2-prototype-backlog.md](../docs/plans/E3M2-prototype-backlog.md).
**Prototype E5 Slice 1 — abilities (NEO-76):** CI expects **exactly four** ability ids aligned to [E5.M1 ability freeze](../docs/decomposition/modules/E5_M1_CombatRulesEngine.md#prototype-slice-1-freeze-e5m1-01) — **`prototype_pulse`**, **`prototype_guard`**, **`prototype_dash`**, **`prototype_burst`**. **Do not rename** ability `id` after ship—change **`displayName`** only. See [E5M1-prototype-backlog.md](../docs/plans/E5M1-prototype-backlog.md) and [NEO-76 plan](../docs/plans/NEO-76-implementation-plan.md).
**Prototype E5 Slice 2 — NPC behaviors (NEO-87):** CI expects **exactly three** npc behavior ids aligned to [E5.M2 behavior freeze](../docs/decomposition/modules/E5_M2_NpcAiAndBehaviorProfiles.md#prototype-slice-2-freeze-e5m2-01) — **`prototype_melee_pressure`**, **`prototype_ranged_control`**, **`prototype_elite_mini_boss`**. Each row requires **`leashRadius` > `aggroRadius`**. **Do not rename** behavior `id` after ship—change **`displayName`** only. See [E5M2-prototype-backlog.md](../docs/plans/E5M2-prototype-backlog.md) and [NEO-87 plan](../docs/plans/NEO-87-implementation-plan.md).
**Prototype E5 Slice 3 — encounters + rewards (NEO-100):** CI expects **exactly one** encounter id **`prototype_combat_pocket`** and one reward table id **`prototype_combat_pocket_clear`** aligned to [E5.M3 encounter freeze](../docs/decomposition/modules/E5_M3_EncounterAndRewardTables.md#prototype-slice-3-freeze-e5m3-01). **`requiredNpcInstanceIds`** must match the three E5.M2 NPC instance ids; **`fixedGrants`** reference frozen item ids only. **Do not rename** ids after ship—change **`displayName`** only. See [E5M3-prototype-backlog.md](../docs/plans/E5M3-prototype-backlog.md).
**Prototype E7 Slice 1 — quests (NEO-112):** CI expects **exactly four** quest ids aligned to [E7.M1 quest freeze](../docs/decomposition/modules/E7_M1_QuestStateMachine.md#prototype-slice-1-freeze-e7m1-01) — **`prototype_quest_gather_intro`**, **`prototype_quest_refine_intro`**, **`prototype_quest_combat_intro`**, **`prototype_quest_operator_chain`**. Objective **`itemId`** / **`recipeId`** / **`encounterId`** cross-refs must resolve to frozen item, recipe, and encounter catalogs; **`prerequisiteQuestIds`** must be acyclic and reference known quest ids; operator chain terminal step is **`inventory_has_item`** **`contract_handoff_token`** ×1. **Do not rename** quest `id` after ship—change **`displayName`** only. See [E7M1-prototype-backlog.md](../docs/plans/E7M1-prototype-backlog.md) and [NEO-112 plan](../docs/plans/NEO-112-implementation-plan.md).
**Prototype E7 Slice 2 — quest completion bundles (NEO-124):** Each frozen quest id must include **`completionRewardBundle`** with **`itemGrants`** / **`skillXpGrants`** per [E7.M2 completion freeze](../docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md#prototype-slice-2-freeze-e7m2-01). Bundle **`itemGrants[].itemId`** must resolve to the frozen item catalog; **`skillXpGrants[].skillId`** must resolve to a skill that lists **`mission_reward`** in **`allowedXpSourceKinds`**. Omit empty **`itemGrants`**. See [E7M2-prototype-backlog.md](../docs/plans/E7M2-prototype-backlog.md) and [NEO-124 plan](../docs/plans/NEO-124-implementation-plan.md).
**Prototype E7 Slice 3 — factions + gated quest line (NEO-133):** CI expects **exactly two** faction ids aligned to [E7.M3 faction freeze](../docs/decomposition/modules/E7_M3_FactionReputationLedger.md#prototype-slice-3-freeze-e7m3-01) — **`prototype_faction_grid_operators`**, **`prototype_faction_rust_collective`**. Quest roster expands to **five** ids (four E7.M1 onboarding + **`prototype_quest_grid_contract`**). **`factionGateRules[].factionId`** and **`completionRewardBundle.reputationGrants[].factionId`** must resolve to the frozen faction catalog; grid contract shape is frozen in CI. See [E7M3-pre-production-backlog.md](../docs/plans/E7M3-pre-production-backlog.md) and [NEO-133 plan](../docs/plans/NEO-133-implementation-plan.md).
**Prototype E7 Slice 4 — contract templates (NEO-144):** CI expects **exactly one** contract template id **`prototype_contract_clear_combat_pocket`** aligned to [E7.M4 contract freeze](../docs/decomposition/modules/E7_M4_ContractMissionGenerator.md#prototype-slice-4-freeze-e7m4-01). **`encounterTemplateId`** must resolve to **`prototype_combat_pocket`**; **`minFactionStanding.factionId`** must resolve to the frozen faction catalog; **`completionRewardBundle`** item/skill cross-refs and **`mission_reward`** allowlist match E7.M2 rules; band **1** economy caps (item qty ≤ **10**, skill XP ≤ **25**, rep ≤ **10** per grant row) are enforced in CI. Issue/audit shape: [`contract-seed.schema.json`](schemas/contract-seed.schema.json) (no catalog file yet). See [E7M4-pre-production-backlog.md](../docs/plans/E7M4-pre-production-backlog.md) and [NEO-144 plan](../docs/plans/NEO-144-implementation-plan.md).
**Prototype Slice 4 (NEO-45):** CI expects **exactly one** `MasteryTrack` for **`salvage`** only (`refine` / `intrusion` have no mastery rows yet). Tier 1 @ skill level **2** is a **mutually exclusive** branch pick (`scrap_efficiency` vs `bulk_haul`) with **no** perks; tier 2 @ level **4** unlocks one perk per branch path. **Do not rename** `branchId` / `perkId` after ship—change `displayName` only. See [E2.M3 — Designer note](../docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md#designer-note-tiers-branches-and-level-gates) and [freeze table](../docs/decomposition/modules/E2_M3_MasteryAndPerkUnlocks.md#prototype-slice-4-freeze--salvage-flagship-neo-45).
Server load path and hot-reload policy are TBD; keep files versioned here as the source of truth.
**Validate locally** (same as PR gate):
```bash
pip install -r scripts/requirements-content.txt
python scripts/validate_content.py
python scripts/check_decomposition_language.py
```