fix(combat): deed range gating
This commit is contained in:
@@ -1568,30 +1568,35 @@ class CombatEngine:
|
||||
"""Execute a gunslinger deed (up_close_and_deadly, quick_clear)."""
|
||||
if deed_name not in ("up_close_and_deadly", "quick_clear"):
|
||||
return
|
||||
if not state.spend_resource("grit", 1):
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: deed failed (no grit)"
|
||||
)
|
||||
return
|
||||
if deed_name == "up_close_and_deadly":
|
||||
# Up Close and Deadly: +1d6 damage within 30ft
|
||||
if target is None or not target.active:
|
||||
return
|
||||
dist_ft = self._grid.distance(state.pos, target.pos) * _SQUARE_FT
|
||||
if dist_ft <= _UP_CLOSE_RANGE_FT:
|
||||
if dist_ft > _UP_CLOSE_RANGE_FT:
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: deed Up Close "
|
||||
f"and Deadly - target out of range"
|
||||
)
|
||||
return
|
||||
if not state.spend_resource("grit", 1):
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: deed failed (no grit)"
|
||||
)
|
||||
return
|
||||
# Apply +1d6 damage to next attack this round
|
||||
state.deed_up_close_and_deadly = True
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: "
|
||||
f"deed Up Close and Deadly activated (+1d6 dmg within 30ft)"
|
||||
)
|
||||
else:
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: deed Up Close "
|
||||
f"and Deadly - target out of range"
|
||||
)
|
||||
elif deed_name == "quick_clear":
|
||||
# Quick Clear: swift action, clears misfire
|
||||
if not state.spend_resource("grit", 1):
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: deed failed (no grit)"
|
||||
)
|
||||
return
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: "
|
||||
f"deed Quick Clear - firearm cleared"
|
||||
@@ -2534,7 +2539,7 @@ def default_policy(engine: CombatEngine, state: CombatantState) -> tuple[Action,
|
||||
return spell_combat
|
||||
|
||||
# Deed (tammara) and inspire_courage (misty melee fallback): swift/free bonuses.
|
||||
swift_bonus = _swift_bonus_actions(state, target)
|
||||
swift_bonus = _swift_bonus_actions(engine, state, target)
|
||||
return (*swift_bonus, *normal) if swift_bonus else normal
|
||||
|
||||
|
||||
@@ -2597,13 +2602,17 @@ def _spell_combat_actions(
|
||||
|
||||
|
||||
def _swift_bonus_actions(
|
||||
state: CombatantState, target: CombatantState
|
||||
engine: CombatEngine, state: CombatantState, target: CombatantState
|
||||
) -> tuple[Action, ...]:
|
||||
"""Swift/free class-ability actions (deed, inspire_courage) to prepend to the turn."""
|
||||
bonus: list[Action] = []
|
||||
|
||||
# Deed (tammara): swift/free action, emitted alongside the normal action.
|
||||
if state.resources_left.get("grit", 0) > 0:
|
||||
# Deed (tammara): swift/free action, emitted alongside the normal action only
|
||||
# when the target is within Up Close and Deadly range (30 ft).
|
||||
if (
|
||||
state.resources_left.get("grit", 0) > 0
|
||||
and engine.grid.distance(state.pos, target.pos) * _SQUARE_FT <= _UP_CLOSE_RANGE_FT
|
||||
):
|
||||
bonus.append(
|
||||
Action(
|
||||
kind="deed",
|
||||
|
||||
@@ -192,6 +192,37 @@ def test_tammara_silent_when_grit_exhausted() -> None:
|
||||
assert not any(a.kind == "deed" for a in actions)
|
||||
|
||||
|
||||
def test_tammara_silent_when_target_out_of_deed_range() -> None:
|
||||
tammara = _combatant("tammara", resources={"grit": 3})
|
||||
goblin = _combatant("goblin", hp_max=6)
|
||||
engine, me, _ = _engine(tammara, [])
|
||||
far_goblin = _state(goblin, "monsters", (11, 11), hp=6)
|
||||
engine.states.append(far_goblin)
|
||||
|
||||
actions = default_policy(engine, me)
|
||||
|
||||
assert not any(a.kind == "deed" for a in actions)
|
||||
|
||||
|
||||
def test_tammara_deed_out_of_range_spends_no_grit() -> None:
|
||||
tammara = _combatant("tammara", resources={"grit": 3})
|
||||
goblin = _combatant("goblin", hp_max=6)
|
||||
engine, me, _ = _engine(tammara, [])
|
||||
far_goblin = _state(goblin, "monsters", (11, 11), hp=6)
|
||||
engine.states.append(far_goblin)
|
||||
|
||||
engine._execute(
|
||||
me,
|
||||
Action(
|
||||
kind="deed",
|
||||
target_id=far_goblin.combatant.id,
|
||||
deed_name="up_close_and_deadly",
|
||||
),
|
||||
)
|
||||
|
||||
assert me.resources_left["grit"] == 3
|
||||
|
||||
|
||||
# --- misty: spell_combat + inspire_courage ---
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user