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:
@@ -296,3 +296,74 @@ def test_run_encounter_splits_duplicate_summons() -> None:
|
||||
assert "celestial_wolf" in report.attrition
|
||||
assert "celestial_wolf-2" in report.attrition
|
||||
assert report.attrition["celestial_wolf-2"].damage_dealt >= 0.0
|
||||
|
||||
|
||||
def _make_runner_healer() -> Combatant:
|
||||
attack = AttackSpec(
|
||||
id="healer-w",
|
||||
name="mace",
|
||||
kind="melee",
|
||||
attack_bonus=2,
|
||||
damage=[DamageComponent(formula=parse_dice("1d6"), types=["bludgeoning"])],
|
||||
)
|
||||
return Combatant(
|
||||
id="healer",
|
||||
name="healer",
|
||||
level=3,
|
||||
size="Medium",
|
||||
abilities=AbilityScores(
|
||||
str_score=10, dex_score=12, con_score=14,
|
||||
int_score=10, wis_score=16, cha_score=10,
|
||||
),
|
||||
hp_max=40,
|
||||
ac=ACProfile(total=14, touch=13, flat_footed=12),
|
||||
bab=2,
|
||||
initiative_mod=2,
|
||||
speed_land_ft=30,
|
||||
saves=Saves(fort=2, ref=3, will=5),
|
||||
attacks=[attack],
|
||||
casting_ability="wis",
|
||||
spells=["Cure Light Wounds"],
|
||||
spell_slots={1: 3},
|
||||
)
|
||||
|
||||
|
||||
def _self_healing_policy(
|
||||
engine: CombatEngine, state: CombatantState
|
||||
) -> tuple[Action, ...]:
|
||||
if (
|
||||
state.combatant.id.startswith("healer")
|
||||
and state.hp < state.combatant.hp_max
|
||||
and state.spell_slots_left.get(1, 0) > 0
|
||||
):
|
||||
return (
|
||||
Action(
|
||||
kind="cast_spell",
|
||||
target_id=state.combatant.id,
|
||||
spell_name="Cure Light Wounds",
|
||||
),
|
||||
)
|
||||
return default_policy(engine, state)
|
||||
|
||||
|
||||
def test_run_encounter_reports_healing_done() -> None:
|
||||
"""Spell healing is visible through the aggregation as the average
|
||||
healing_done per combatant - the counter deliberately removed from
|
||||
damage_dealt (commit 2e2873f) was invisible in the report before."""
|
||||
spec = EncounterSpec(
|
||||
map=make_map(),
|
||||
sides=(
|
||||
Side(name="players", combatants=(_make_runner_healer(),), zone="A"),
|
||||
Side(
|
||||
name="monsters",
|
||||
combatants=(
|
||||
make_combatant("gob", hp=30, attack_bonus=10, damage="1d6"),
|
||||
),
|
||||
zone="B",
|
||||
),
|
||||
),
|
||||
round_cap=10,
|
||||
)
|
||||
report = run_encounter(spec, runs=5, seed=11, policy=_self_healing_policy)
|
||||
assert report.attrition["healer"].healing_done > 0.0
|
||||
assert report.attrition["healer"].damage_taken > 0.0
|
||||
|
||||
Reference in New Issue
Block a user