fix(rules): resistance is a +1 resistance bonus on fort/ref/will saves for 1 minute - SRD spells/resistance.md grants the subject a +1 resistance bonus on saving throws for 1 minute (Will negates, harmless); the resistance bonus type already exists in the stacking model (effects.py BonusType), so the buff is expressible as a BuffEffect and was only deferred because _find_buff_spell returns the FIRST BuffEffect in known-spell order and cantrips cost no slots - encoding it would have made default_policy cast Resistance every round (for Tammara it would preempt Divine Favor and never kill anything); resistance.json now carries a BuffEffect (+1 resistance on fort/ref/will, 10 rounds = 1 minute) and _find_buff_spell skips level-0 spells (cantrips have no spell slot in the engine's budget - spell_slots_left has no level-0 key - and the policy deliberately never auto-casts them, a cantrip buff would be cast every round degrading the policy), so Resistance cannot preempt a leveled buff (Divine Favor) in the known-spell order; leveled buffs keep the historical first-match behavior (if the first one's slots are exhausted the buff branch gives up - the golden pins that); the resistance bonus type does not stack with itself (resolve_modifiers keeps the higher value); +9 tests (test_resistance.py: buff shape, cast on a willing ally consumes no d20 and applies without a save roll, the bonus rides save rolls, no self-stacking, expiry at 10 rounds, _find_buff_spell skips the slot-less cantrip / returns None without a leveled buff, _choose_spell_action proposes Divine Favor and never Resistance); golden untouched; 1714 tests green

This commit is contained in:
2026-09-20 10:10:32 +02:00
parent 08f9af9e3e
commit 1238c40e43
3 changed files with 279 additions and 5 deletions
+12 -2
View File
@@ -7,6 +7,16 @@
"harmless": true,
"base_dc": 10,
"cast_time": "standard",
"effects": [],
"description": "SRD spells/resistance.md: a cantrip - the subject gains a +1 resistance bonus on saving throws for 1 minute. Will negates (harmless). Deferred: the resistance bonus type exists in the stacking model, so the buff is expressible - but _find_buff_spell returns the FIRST BuffEffect spell in known-spell order and cantrips cost no slots, so encoding it makes default_policy cast Resistance every round instead of attacking or leveling buffs (for Tammara it would preempt Divine Favor and never kill anything). A buff-priority policy is a redesign; no mechanical effect is applied."
"effects": [
{
"type": "buff",
"modifiers": [
{ "target": "fort", "value": 1, "bonus_type": "resistance" },
{ "target": "ref", "value": 1, "bonus_type": "resistance" },
{ "target": "will", "value": 1, "bonus_type": "resistance" }
],
"duration_rounds": 10
}
],
"description": "SRD spells/resistance.md: a cantrip - the subject gains a +1 resistance bonus on saving throws for 1 minute. Will negates (harmless). Modeled as a BuffEffect granting +1 resistance to fort/ref/will for 10 rounds (1 minute); the resistance bonus type does not stack with itself, so a second Resistance (or a cloak of resistance) keeps the higher value. default_policy never casts it: cantrips have no spell slot, and _find_buff_spell skips spells the caster has no slot for, so Resistance cannot preempt a leveled buff (Divine Favor) in the known-spell order."
}