fix(spells): make the bleed spell bleed
PF1e (SRD spells/bleed.md): "Saving Throw Will negates" and the target takes 1 point of damage per round. Two divergences: the JSON declared a Fortitude save, and the bleeding condition never set the engine's bleed_damage field, so the spell was a no-op. Bleed now saves Will; _apply_condition_effect sets bleed_damage = 1 when a spell applies bleeding, and _decrement_durations lands the final tick as the last bleeding condition expires before clearing the field (heal-path clearing already existed in _stop_bleeding). README updated. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -42,7 +42,7 @@ _BATCH1: dict[str, tuple[str, int, str, str, str]] = {
|
||||
"Bane": ("bane", 1, "enchantment", "medium", "will"),
|
||||
"Bear's Endurance": ("bears_endurance", 2, "transmutation", "touch", "fort"),
|
||||
"Bladed Dash": ("bladed_dash", 1, "transmutation", "close", "none"),
|
||||
"Bleed": ("bleed", 0, "necromancy", "close", "fort"),
|
||||
"Bleed": ("bleed", 0, "necromancy", "close", "will"),
|
||||
"Bless": ("bless", 1, "enchantment", "medium", "none"),
|
||||
"Blindness/Deafness": ("blindness_deafness", 2, "necromancy", "medium", "will"),
|
||||
"Charm Person": ("charm_person", 1, "enchantment", "medium", "will"),
|
||||
|
||||
@@ -396,6 +396,38 @@ class TestAreaSavesPerSpellSaveType:
|
||||
assert not any(ac.condition.name == "stunned" for ac in t.active_conditions)
|
||||
|
||||
|
||||
class TestBleedCantrip:
|
||||
"""Given: the Bleed cantrip (SRD spells/bleed.md: Will negates; the target
|
||||
takes 1 point of bleed damage per round)
|
||||
When: the target fails / succeeds its Will save
|
||||
Then: failed — bleeding condition, 1 hp lost at the start of its turn;
|
||||
saved — no condition, no bleed damage."""
|
||||
|
||||
def test_failed_save_deals_one_bleed_damage_per_round(self) -> None:
|
||||
caster = _make_caster()
|
||||
target = _make_target(hp_max=30)
|
||||
# Will save d20=2 (+2) vs DC 14 -> fail.
|
||||
engine, c, t = _make_engine(caster, target, [2])
|
||||
engine._cast_spell(c, t, "Bleed")
|
||||
assert any(ac.condition.name == "bleeding" for ac in t.active_conditions)
|
||||
assert t.bleed_damage == 1
|
||||
# The target's next turn start: the 1-round condition expires and its
|
||||
# final bleed tick lands (1 hp).
|
||||
engine._decrement_durations(t)
|
||||
assert t.hp == 30 - 1
|
||||
assert t.bleed_damage == 0
|
||||
|
||||
def test_successful_save_does_not_bleed(self) -> None:
|
||||
caster = _make_caster()
|
||||
target = _make_target(hp_max=30)
|
||||
# Will save natural 20 -> auto-success (SRD: Will negates).
|
||||
engine, c, t = _make_engine(caster, target, [20])
|
||||
engine._cast_spell(c, t, "Bleed")
|
||||
assert not any(ac.condition.name == "bleeding" for ac in t.active_conditions)
|
||||
assert t.bleed_damage == 0
|
||||
assert t.hp == 30
|
||||
|
||||
|
||||
class TestTargetedSpellSaveNegates:
|
||||
"""Given: a targeted spell with a save (half_on_save=False)
|
||||
When: the target succeeds / fails its save
|
||||
|
||||
Reference in New Issue
Block a user