feat(rules): SRD Mage Armor spell + non-stacking armor bonus type

SRD spells/mage-armor.md: 'An invisible but tangible field of force
surrounds the subject of a mage armor spell, providing a +4 armor bonus
to AC' - 'School conjuration (creation) [force]', level 1, 'Casting Time
1 standard action', 'Range touch', 'Duration 1 hour/level (D)',
'Spell Resistance no'.

- data/spells/mage_armor.json: buff +4 armor to AC, 600 rounds (1
  hour/level modeled fixed, the shield.json convention). Description
  documents the known simplification: the engine bakes worn armor into
  the AC total and cannot see it, so the armor bonus stacks additively
  with that baked value (two mage armors still do not stack).
- effects.py: 'armor' added to BonusType; absent from _STACKING_TYPES it
  behaves as a keep-the-highest type (verified by resolve_modifiers
  tests).
- Spell-count pins 107 -> 108 (test_runner_spells, batch3, test_spells)
  and the README references.
- Golden transcript untouched (buff branch only fires when no offensive
  spell is selected; the lich still opens with Magic Missile).
This commit is contained in:
2026-09-16 15:36:43 +02:00
parent 881f2ef9b2
commit 3e19d2974e
7 changed files with 161 additions and 14 deletions
+3 -3
View File
@@ -476,9 +476,9 @@ Règles modélisées :
(seule `default_policy` s'en préserve : `_choose_spell_action` ne sélectionne
jamais un sort à temps long). Le moteur résout le code via sa registry
(`_cast_time_of`, repli `standard` pour un nom inconnu) et le passe aux
filtres purs sous forme de résolveur `CastTimeResolver`. Les 107 sorts
filtres purs sous forme de résolveur `CastTimeResolver`. Les 108 sorts
importés de `data/spells/` déclarent tous leur `cast_time` (codes normalisés,
backfillés depuis le SRD) : 95 `standard`, 6 `one_round` (Enhance Water,
backfillés depuis le SRD) : 96 `standard`, 6 `one_round` (Enhance Water,
Silence, Sleep, Summon Instrument, Summon Monster I/II), 2 `immediate`
(Grasp, Saving Finale) et 4 `long` (Make Whole, Masterwork Transformation,
Mending, Restoration Lesser). Hors du périmètre (hook futur, commenté dans
@@ -1724,7 +1724,7 @@ uv run basedpyright src # mode strict
## Feuille de route
- **Phase 1** — magie et états : largement réalisée. Sorts par emplacements
(`spells.py` + base `data/spells/`, 107 sorts), DD et sauvegardes, RD non
(`spells.py` + base `data/spells/`, 108 sorts), DD et sauvegardes, RD non
appliquée aux dégâts de sorts, concentration par situation, invocation,
conditions effectives (`conditions.py`), manœuvres de combat, dons (55 noms
résolus, `abilities.py` + chargeur) et capacités de classe (channel energy,