fix(combat): _find_ally_needing_heal uses hp*2 < hp_max (strictly below half) - the floored hp < hp_max//2 was off by one for odd hp_max (5 -> threshold 2, so 2/5 hp counted healthy); + threshold regression test
This commit is contained in:
@@ -8311,9 +8311,14 @@ def _find_heal_spell(engine: CombatEngine, spells_known: list[str]) -> str | Non
|
|||||||
|
|
||||||
|
|
||||||
def _find_ally_needing_heal(engine: CombatEngine, caster: CombatantState) -> CombatantState | None:
|
def _find_ally_needing_heal(engine: CombatEngine, caster: CombatantState) -> CombatantState | None:
|
||||||
"""Find an ally below 50% HP."""
|
"""Find an 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.
|
||||||
|
"""
|
||||||
for ally_state in engine.states:
|
for ally_state in engine.states:
|
||||||
below_half = ally_state.hp < ally_state.combatant.hp_max // 2
|
below_half = ally_state.hp * 2 < ally_state.combatant.hp_max
|
||||||
if ally_state.side == caster.side and ally_state.active and below_half:
|
if ally_state.side == caster.side and ally_state.active and below_half:
|
||||||
return ally_state
|
return ally_state
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -127,6 +127,23 @@ def test_oni_emits_channel_energy_when_ally_hurt() -> None:
|
|||||||
assert any(a.kind == "channel_energy" for a in actions)
|
assert any(a.kind == "channel_energy" for a in actions)
|
||||||
|
|
||||||
|
|
||||||
|
def test_oni_channel_heal_triggers_below_half_of_odd_hp_max() -> None:
|
||||||
|
"""Strictly-below-half threshold: hp_max 5 at hp 2 satisfies 2*2 < 5 but
|
||||||
|
failed the floored old check hp < hp_max // 2 (2 < 2), leaving a 40%-HP
|
||||||
|
ally untreated."""
|
||||||
|
oni = _combatant("oni", resources={"channel_energy": 6}, hp_max=30)
|
||||||
|
goblin = _combatant("goblin", hp_max=6)
|
||||||
|
engine, me, _ = _engine(oni, [goblin])
|
||||||
|
hurt_ally = _state(_combatant("halfling", hp_max=5), "players", (5, 5), hp=2)
|
||||||
|
engine.states.append(hurt_ally)
|
||||||
|
|
||||||
|
actions = default_policy(engine, me)
|
||||||
|
|
||||||
|
assert any(
|
||||||
|
a.kind == "channel_energy" and a.channel_mode == "heal" for a in actions
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_oni_silent_when_channel_energy_exhausted() -> None:
|
def test_oni_silent_when_channel_energy_exhausted() -> None:
|
||||||
oni = _combatant("oni", resources={"channel_energy": 0}, hp_max=30)
|
oni = _combatant("oni", resources={"channel_energy": 0}, hp_max=30)
|
||||||
goblin = _combatant("goblin", hp_max=6)
|
goblin = _combatant("goblin", hp_max=6)
|
||||||
|
|||||||
Reference in New Issue
Block a user