fix(combat): _choose_spell_action heal branch gates on spell range + line of effect - a touch-range Cure on a distant ally was rejected by _cast_spell (OUT OF RANGE / NO LINE OF EFFECT) and wasted the caster's whole turn; extracted the offensive branch's reach check as _spell_reaches and reused it (the duplicated below-half check in the branch was a no-op copy of _find_ally_needing_heal, dropped) + 3 policy tests
This commit is contained in:
@@ -8319,11 +8319,27 @@ def _find_ally_needing_heal(engine: CombatEngine, caster: CombatantState) -> Com
|
||||
return None
|
||||
|
||||
|
||||
def _spell_reaches(
|
||||
engine: CombatEngine,
|
||||
caster: CombatantState,
|
||||
target: CombatantState,
|
||||
spell: SpellSpec,
|
||||
) -> bool:
|
||||
"""True if the spell's range covers caster -> target with line of effect
|
||||
(the same gate `_cast_spell` applies; a proposed cast failing it wastes
|
||||
the whole turn on an OUT OF RANGE / NO LINE OF EFFECT rejection)."""
|
||||
dist_ft = engine.creature_distance(caster, target) * _SQUARE_FT
|
||||
return dist_ft <= spell_range_ft(spell, caster.combatant.level) and (
|
||||
has_line_of_effect_cells(
|
||||
engine.grid, caster.occupied_cells(), target.occupied_cells()
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _find_offensive_spell(
|
||||
engine: CombatEngine, spells_known: list[str], caster: CombatantState, target: CombatantState
|
||||
) -> str | None:
|
||||
"""Find an offensive spell that can reach the target."""
|
||||
dist_ft = engine.creature_distance(caster, target) * _SQUARE_FT
|
||||
for name in spells_known:
|
||||
spell = engine.spell_registry.get(name)
|
||||
if not spell:
|
||||
@@ -8332,10 +8348,7 @@ def _find_offensive_spell(
|
||||
has_damage = any(isinstance(e, DamageEffect) for e in spell.effects)
|
||||
if not has_damage:
|
||||
continue
|
||||
max_range = spell_range_ft(spell, caster.combatant.level)
|
||||
if dist_ft <= max_range and has_line_of_effect_cells(
|
||||
engine.grid, caster.occupied_cells(), target.occupied_cells()
|
||||
):
|
||||
if _spell_reaches(engine, caster, target, spell):
|
||||
return name
|
||||
return None
|
||||
|
||||
@@ -8375,7 +8388,9 @@ def _choose_spell_action(
|
||||
if not spells_known:
|
||||
return None
|
||||
|
||||
# 1. Heal spell if ally below 50% HP
|
||||
# 1. Heal spell if ally below 50% HP (within range and line of effect:
|
||||
# a touch-range Cure on a distant ally is rejected by _cast_spell and
|
||||
# would waste the whole turn).
|
||||
heal_spell = _find_heal_spell(engine, spells_known)
|
||||
if (
|
||||
heal_spell
|
||||
@@ -8383,7 +8398,10 @@ def _choose_spell_action(
|
||||
and _has_slot(engine, caster, heal_spell)
|
||||
):
|
||||
ally = _find_ally_needing_heal(engine, caster)
|
||||
if ally and ally.hp < ally.combatant.hp_max // 2:
|
||||
spell = engine.spell_registry.get(heal_spell)
|
||||
if ally is not None and spell is not None and _spell_reaches(
|
||||
engine, caster, ally, spell
|
||||
):
|
||||
return (Action(kind="cast_spell", target_id=ally.combatant.id, spell_name=heal_spell),)
|
||||
|
||||
# 2. Offensive spell if enemy in range
|
||||
|
||||
Reference in New Issue
Block a user