fix(rules): expose healing_done in per-combatant stats and the CLI report - the channel/spell healing deliberately removed from damage_dealt (2e2873f) was invisible; CombatantStats/_LiveStats gain a healing_done counter booked with the hp actually restored (capped at the target's hp_max, overheal uncounted) by _channel_heal and by spell HealEffect on living targets, while cure-as-damage on undead stays in damage_dealt and the damage counters keep their exact semantics; the runner aggregates and averages the new field and the French report prints it as 'soins prodigués'; +4 tests (channel heal, cure spell, undead cure guard, run_encounter aggregate) +1 CLI assertion
This commit is contained in:
@@ -287,6 +287,88 @@ def test_oni_channel_harm_is_counted_as_damage_dealt() -> None:
|
||||
assert engine._stats["lich"].damage_taken == dealt
|
||||
|
||||
|
||||
def test_oni_channel_heal_is_counted_as_healing_done() -> None:
|
||||
"""The healing deliberately removed from damage_dealt (commit 2e2873f) is
|
||||
visible in its own counter: the goblin restored 2 -> 6 books 4 hp of
|
||||
healing_done on the channeler, damage counters untouched."""
|
||||
oni = _combatant("oni", resources={"channel_energy": 6}, hp_max=30)
|
||||
goblin = _combatant("goblin", hp_max=6)
|
||||
engine, me, enemy = _engine(oni, [goblin], rng_queue=[5])
|
||||
enemy[0].hp = 2
|
||||
|
||||
engine._execute(
|
||||
me,
|
||||
Action(kind="channel_energy", target_id=me.combatant.id, channel_mode="heal"),
|
||||
)
|
||||
|
||||
assert enemy[0].hp == 6
|
||||
assert engine._stats["oni"].healing_done == 4 # 1+5=6 rolled, capped at hp_max
|
||||
assert engine._stats["oni"].damage_dealt == 0
|
||||
assert engine._stats["goblin"].damage_taken == 0
|
||||
|
||||
|
||||
def test_cure_spell_healing_is_counted_as_healing_done() -> None:
|
||||
"""A Cure Light Wounds cast on a wounded ally books the restored hp to the
|
||||
caster's healing_done (willing target: NO SAVE consumes no die; the single
|
||||
scripted d8 is the heal roll)."""
|
||||
cleric = _combatant(
|
||||
"cleric", spells=["Cure Light Wounds"], spell_slots={1: 1}, hp_max=20
|
||||
)
|
||||
goblin = _combatant("goblin", hp_max=6)
|
||||
hurt_ally = _state(_combatant("ally", hp_max=20), "players", (5, 4), hp=5)
|
||||
me = _state(cleric, "players", (5, 5))
|
||||
enemy = _state(goblin, "monsters", (5, 6))
|
||||
engine = CombatEngine(
|
||||
# die 1: the goblin's cast-provoked AoO (d20=3+3=6, misses AC 15);
|
||||
# die 2: the heal d8.
|
||||
ScriptedRng([3, 3]),
|
||||
_grid(),
|
||||
[me, enemy, hurt_ally],
|
||||
spell_registry=load_spell_registry(SPELLS_DIR),
|
||||
)
|
||||
|
||||
engine._execute(
|
||||
me,
|
||||
Action(
|
||||
kind="cast_spell", target_id="ally", spell_name="Cure Light Wounds"
|
||||
),
|
||||
)
|
||||
|
||||
healed = hurt_ally.hp - 5
|
||||
assert healed == 8 # 1d8=3 + level-5 scaling_flat +5
|
||||
assert engine._stats["cleric"].healing_done == healed
|
||||
assert engine._stats["cleric"].damage_dealt == 0
|
||||
|
||||
|
||||
def test_cure_damage_on_undead_is_not_healing() -> None:
|
||||
"""A cure cast on an undead deals damage instead (SRD cure-light-wounds):
|
||||
it stays booked as damage_dealt, never as healing_done."""
|
||||
cleric = _combatant("cleric", hp_max=20)
|
||||
engine, me, _ = _engine(
|
||||
cleric,
|
||||
[_combatant("zombie", hp_max=20, creature_type="undead")],
|
||||
rng_queue=[3], # the single d8 heal roll
|
||||
)
|
||||
target = engine.states[1]
|
||||
|
||||
result = engine._apply_heal_effect(
|
||||
HealEffect(formula=parse_dice("1d8")),
|
||||
target,
|
||||
caster_level=1,
|
||||
empower=False,
|
||||
maximize=False,
|
||||
saved=False,
|
||||
healer=me,
|
||||
caster_live=engine._stats["cleric"],
|
||||
target_live=engine._stats["zombie"],
|
||||
)
|
||||
|
||||
assert result == "3 damage (undead)"
|
||||
assert engine._stats["cleric"].healing_done == 0
|
||||
assert engine._stats["cleric"].damage_dealt == 3
|
||||
assert engine._stats["zombie"].damage_taken == 3
|
||||
|
||||
|
||||
def test_oni_emits_channel_harm_only_for_grouped_undead() -> None:
|
||||
"""Channel harm is emitted only when at least 2 undead enemies are grouped at 30ft
|
||||
— living enemies are never harmed by positive energy."""
|
||||
|
||||
Reference in New Issue
Block a user