"""Cure spells damage undead instead of healing (Task 11 conformance). SRD "Cure Light Wounds" (spells/cure-light-wounds.md): "Since undead are powered by negative energy, this spell deals damage to them instead of curing their wounds. An undead creature can apply spell resistance, and can attempt a Will save to take half damage." The engine resolves SR upstream and the rolled Will save halves the damage here; living targets keep healing exactly as before. """ from __future__ import annotations from typing import TYPE_CHECKING from tests.test_spells import _make_caster, _make_engine, _make_target if TYPE_CHECKING: from pf1e_simulator.models import Combatant def _undead(cid: str = "skeleton", *, hp_max: int = 20) -> Combatant: return _make_target(cid=cid, hp_max=hp_max).model_copy( update={"creature_type": "undead"} ) def _caster() -> Combatant: return _make_caster().model_copy(update={"spells": ["Cure Light Wounds"]}) class TestCureDamagesUndead: """Given: a level-5 caster (Cure Light Wounds = 1d8+5) and an undead target When: the cure spell is delivered Then: the undead takes damage instead of healing. """ def test_failed_save_deals_full_damage(self) -> None: """Given: an undead at 20 hp When: cured and it fails the Will save (d20=1), the die rolls 8 Then: it takes the full 13 damage (8+5) instead of healing.""" engine, c, t = _make_engine( _caster(), _undead(), [1, 8], target_pos=(1, 0), target_side="players" ) engine._cast_spell(c, t, "Cure Light Wounds") assert t.hp == 7 assert any("13 damage (undead)" in line for line in engine._transcript) def test_successful_save_halves_the_damage(self) -> None: """Given: an undead at 20 hp When: cured and it succeeds the Will save (natural 20), the die rolls 8 Then: it takes half damage (13 // 2 = 6).""" engine, c, t = _make_engine( _caster(), _undead(), [20, 8], target_pos=(1, 0), target_side="players" ) engine._cast_spell(c, t, "Cure Light Wounds") assert t.hp == 14 assert any("6 damage (undead)" in line for line in engine._transcript) def test_undead_destroyed_by_cure_damage(self) -> None: """Given: an undead at 10 hp When: a failed-save cure deals 13 damage Then: the undead is destroyed (undead die at hp <= 0).""" engine, c, t = _make_engine( _caster(), _undead(hp_max=10), [1, 8], target_pos=(1, 0), target_side="players" ) engine._cast_spell(c, t, "Cure Light Wounds") assert t.dead is True def test_living_target_still_heals(self) -> None: """Given: a living ally at 5/20 hp When: cured with a die roll of 8 Then: it heals 13 hp (behavior unchanged for the living).""" engine, c, t = _make_engine( _caster(), _make_target(), [1, 8], target_pos=(1, 0), target_side="players" ) t.hp = 5 engine._cast_spell(c, t, "Cure Light Wounds") assert t.hp == 18 assert any("+13 HP" in line for line in engine._transcript) class TestCureUndeadAttrition: """Given: a cure spell delivered to an undead When: the negative-energy damage lands Then: it counts as damage in attrition (dealer + taker), while healing a living target records no damage either way.""" def test_undead_damage_recorded_for_both_sides(self) -> None: engine, c, t = _make_engine( _caster(), _undead(), [1, 8], target_pos=(1, 0), target_side="players" ) engine._cast_spell(c, t, "Cure Light Wounds") assert engine._stats["skeleton"].damage_taken == 13 assert engine._stats["caster"].damage_dealt == 13 def test_living_heal_records_no_damage(self) -> None: engine, c, t = _make_engine( _caster(), _make_target(), [1, 8], target_pos=(1, 0), target_side="players" ) t.hp = 5 engine._cast_spell(c, t, "Cure Light Wounds") assert engine._stats["caster"].damage_dealt == 0 assert engine._stats["target"].damage_taken == 0