diff --git a/src/pf1e_simulator/combat.py b/src/pf1e_simulator/combat.py index 8af6ccd..f6dc0f2 100644 --- a/src/pf1e_simulator/combat.py +++ b/src/pf1e_simulator/combat.py @@ -8311,15 +8311,21 @@ def _find_heal_spell(engine: CombatEngine, spells_known: list[str]) -> str | Non def _find_ally_needing_heal(engine: CombatEngine, caster: CombatantState) -> CombatantState | None: - """Find an ally below half HP. + """Find a living ally below half HP. ``hp * 2 < hp_max`` is strictly-below-half for every hp_max; the old ``hp < hp_max // 2`` floored the threshold (hp_max 5 -> 2), so a level-1 character at 2/5 hp counted as healthy. + + Living-only (SRD Channel Energy: heal mode heals living creatures; undead + take damage from positive energy, never healing): an undead ally below + half must not trigger the heal paths - channel heal would spend the + channel without helping it, and a cure spell would hurt it. """ for ally_state in engine.states: below_half = ally_state.hp * 2 < ally_state.combatant.hp_max - if ally_state.side == caster.side and ally_state.active and below_half: + living = ally_state.combatant.creature_type == "living" + if ally_state.side == caster.side and ally_state.active and living and below_half: return ally_state return None diff --git a/tests/test_class_abilities.py b/tests/test_class_abilities.py index bec977f..b5deea8 100644 --- a/tests/test_class_abilities.py +++ b/tests/test_class_abilities.py @@ -156,6 +156,24 @@ def test_oni_silent_when_channel_energy_exhausted() -> None: assert not any(a.kind == "channel_energy" for a in actions) +def test_oni_no_channel_heal_trigger_for_undead_ally() -> None: + """SRD Channel Energy: heal mode heals living creatures. An undead ally + below half cannot benefit (positive energy harms undead), so it must not + trigger the heal mode - the channel would be spent healing nobody.""" + oni = _combatant("oni", resources={"channel_energy": 6}, hp_max=30) + goblin = _combatant("goblin", hp_max=6) + engine, me, _ = _engine(oni, [goblin]) + undead_ally = _state( + _combatant("ghoul", hp_max=20, creature_type="undead"), + "players", (5, 5), hp=5, + ) + engine.states.append(undead_ally) + + actions = default_policy(engine, me) + + assert not any(a.kind == "channel_energy" for a in actions) + + def test_oni_channel_energy_spends_resource() -> None: oni = _combatant("oni", resources={"channel_energy": 6}, hp_max=30) goblin = _combatant("goblin", hp_max=6)