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:
@@ -8837,11 +8837,26 @@ def _find_offensive_spell(
|
||||
return None
|
||||
|
||||
|
||||
def _find_buff_spell(engine: CombatEngine, spells_known: list[str]) -> str | None:
|
||||
"""Find a buff spell."""
|
||||
def _find_buff_spell(
|
||||
engine: CombatEngine, spells_known: list[str]
|
||||
) -> str | None:
|
||||
"""Find a buff spell the policy will actually cast.
|
||||
|
||||
PF1e cantrips (level 0) 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 a cantrip buff such as Resistance must not 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).
|
||||
"""
|
||||
for name in spells_known:
|
||||
spell = engine.spell_registry.get(name)
|
||||
if spell and any(isinstance(e, BuffEffect) for e in spell.effects):
|
||||
if (
|
||||
spell
|
||||
and spell.level > 0
|
||||
and any(isinstance(e, BuffEffect) for e in spell.effects)
|
||||
):
|
||||
return name
|
||||
return None
|
||||
|
||||
|
||||
Reference in New Issue
Block a user