4.3 KiB
4.3 KiB
Global Feat Injection — Specification
Status Snapshot
Current state as of 2026-05-24:
- Current code reads class feat injection policy from
topdata.class_feat_injectionsinnwn-tool.yaml. global_featsinjects concretefeat:*references into eachclasses/feats/*.jsontable, unless the class already authors the same feat.global_featssupportsrequire_presentandunless_presentconditions against other feat references already present in the class feat table.class_skill_masterfeatsexpands configuredmasterfeats:*rows once for each class skill.- If a project omits
topdata.class_feat_injections, the toolkit keeps the former hardcoded default injections for compatibility.
Objective
Ensure the native class feat generation pipeline produces functionally identical output to the legacy system by applying deterministic global feat injections.
Requirements
R1 — Skill-based master feats
For every class skill, inject:
masterfeats:skillfocusmasterfeats:greaterskillfocus
With properties:
{
"GrantedOnLevel": "-1",
"List": "1",
"OnMenu": "0"
}
For the current implementation, `masterfeats:greaterskillfocus` is injected with
`GrantedOnLevel = 12`, not `-1`.
---
### R2 — Spellcasting master feats
If class core defines:
- `"SpellCaster" = 1`
Then inject:
- `masterfeats:spellfocus`
- `masterfeats:greaterspellfocus`
Historical note:
- This rule is not currently implemented as an automatic global injection in
`native.go`.
- Spell-focus expansion is supported when the class feat data explicitly
references `masterfeats:spellfocus`.
If automatic spellcaster-driven injection is still required, it remains
outstanding work.
With properties:
```json
{
"GrantedOnLevel": "-1",
"List": "1",
"OnMenu": "0"
}
R3 — Literacy rule
Inject:
feat:literate
Only if:
feat:illiterateis NOT present
With properties:
{
"GrantedOnLevel": "1",
"List": "3",
"OnMenu": "0"
}
R4 — Default combat/menu feats
Always inject:
feat:special_attacksfeat:throwfeat:grapplefeat:offensive_fightingfeat:defensive_fightingfeat:horse_menu
With properties:
{
"GrantedOnLevel": "1",
"List": "3",
"OnMenu": "1"
}
R5 — Conditional combat feats
Inject:
feat:power_attackfeat:combat_expertise
With properties:
{
"GrantedOnLevel": "-1",
"List": "0",
"OnMenu": "1"
}
R6 — Override precedence
- Explicitly authored class feat rows take precedence over injected rows.
- Injection MUST NOT replace explicit class feat data.
- Dataset overrides are applied before class feat injection in the native dataset merge pipeline; author a class feat row directly to override or suppress the injected equivalent for a class.
Behavioral Rules
B1 — Output equivalence
- The resulting feat set MUST match legacy output in substance.
- Exact row ordering and numeric IDs are NOT significant.
B2 — Deterministic inclusion
- All injections MUST occur consistently for identical inputs.
- No randomness or order-dependent behavior is allowed.
B3 — Conditional correctness
feat:literateMUST NOT be injected iffeat:illiterateexists.- Spell focus feats MUST ONLY be injected when
"SpellCaster" = 1.
B4 — Integration with pipeline
- Injected feats MUST flow through the same processing stages as authored feats.
- They MUST be subject to:
- overrides
- deduplication
- final emission rules
Acceptance Criteria
An implementation is considered correct if:
-
Parity
- For any given class input, native output produces the same effective feats as legacy.
-
Injection coverage
- All required feats (R1–R5) appear when conditions are met.
-
Exclusion correctness
- No invalid injections occur (e.g., literacy conflict).
-
Override dominance
- Any override affecting injected feats is correctly applied.
-
Stability
- Repeated runs with identical input produce identical output.
Non-Negotiables
- This is parity-critical, not a best-effort feature.
- Native pipeline MUST be capable of fully replacing legacy output.
- There MUST be zero dependency on legacy systems at runtime.
- Any deviation from legacy-equivalent output is a defect.