- Every data/spells/*.json now carries a "components" field with the
V/S/M/F/DF tokens from its KB Components line (material/focus
descriptions dropped; M/DF and F/DF alternatives kept verbatim).
- 107/108 encoded. Create Alcool (data/spells/create_alcool.json) is
left without the field: it is a homebrew cantrip (level 0) with no KB
entry - the closest real spell, Enhance Water (Inner Sea Gods, V, S),
is already a separate registry entry.
- summon_monster_i/ii map to the KB's numeric files (summon-monster-1/2);
hidden_diplomacy maps to its KB name Egorian Diplomacy (same spell,
Spymaster's Handbook rename): V, S, M.
- tests/test_spell_failure.py: the placeholder asserting the registry was
not yet encoded is replaced by an encoded-state smoke test (all spells
but Create Alcool declare components; five spot-checks against the SRD
stat blocks). Golden transcripts unchanged: the component gates still
need deafened/ASF conditions no golden combat has.
Add attacker-alignment modifier conditions (attacker_is_evil/lawful/good/
chaotic) evaluated against an optional attacker context threaded through
_stat_modifiers/_modifier_condition_holds, _attack_ac_mods (resolve_attack
AC path), resolve_save and _area_save (spell save paths), plus the fear
aura. Without a source in scope the condition is False, so existing rolls
are untouched (golden transcripts bit-identical).
Give the four protection_from_evil/law(+communal) JSONs their SRD buff:
+2 deflection to AC and +2 resistance on fort/ref/will, gated on the
attacker/caster alignment (SRD: bonuses apply 'against attacks or effects
created by evil creatures'); duration 1 min/level approximated as 10
rounds (engine convention). Descriptions document the unmodeled mental-
control suppression and communal duration-split clauses. Level/range/
save/SR verified against the KB.
SpellSpec gains spell_resistance: bool = True (JSON 'spell_resistance'). When false, _spell_delivery_blocked skips the targeted SR check and _apply_area_effect skips the per-creature SR check (threaded through the effect chain). Verified each imported spell against the local KB and set false on the 28 whose SRD block says 'Spell Resistance: no' (Acid Arrow, Flurry of Snowballs, Web, Darkness, Sanctuary, the Protection from X spells, etc.). Added no-SR targeted/area tests and a high-SR control in test_spell_resistance.py, and the README.
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>