feat(combat): resolve all natural melee weapons in full_attack
This commit is contained in:
@@ -2295,13 +2295,13 @@ class CombatEngine:
|
||||
return
|
||||
if target is None:
|
||||
return
|
||||
weapon = self.weapon_for(state, target)
|
||||
if weapon is None:
|
||||
return
|
||||
if kind == "attack":
|
||||
weapon = self.weapon_for(state, target)
|
||||
if weapon is None:
|
||||
return
|
||||
self._single_attack(state, target, weapon)
|
||||
elif kind == "full_attack":
|
||||
self._full_attack(state, target, weapon)
|
||||
self._full_attack(state, target)
|
||||
def _execute_charge(self, state: CombatantState, target: CombatantState | None) -> None:
|
||||
if target is None or not target.active:
|
||||
return
|
||||
@@ -2819,8 +2819,30 @@ class CombatEngine:
|
||||
self._resolve_swing(attacker, target, weapon, label=weapon.name)
|
||||
|
||||
def _full_attack(
|
||||
self, attacker: CombatantState, target: CombatantState
|
||||
) -> None:
|
||||
melee_weapons = [
|
||||
w
|
||||
for w in attacker.usable_attacks()
|
||||
if w.kind == "melee" and self._melee_weapon_usable(attacker, target, w)
|
||||
]
|
||||
if not melee_weapons:
|
||||
# No melee weapon reaches: fall back to weapon_for (ranged/touch),
|
||||
# preserving the pre-L3 single-weapon behavior.
|
||||
weapon = self.weapon_for(attacker, target)
|
||||
if weapon is None:
|
||||
return
|
||||
self._full_attack_weapon(attacker, target, weapon)
|
||||
return
|
||||
for weapon in melee_weapons:
|
||||
if not attacker.active or not target.active:
|
||||
return
|
||||
self._full_attack_weapon(attacker, target, weapon)
|
||||
|
||||
def _full_attack_weapon(
|
||||
self, attacker: CombatantState, target: CombatantState, weapon: AttackSpec
|
||||
) -> None:
|
||||
"""Resolve one weapon's full-attack swings (count swings or BAB iteratives)."""
|
||||
if weapon.count > 1:
|
||||
self._attack(attacker, target, weapon)
|
||||
return
|
||||
@@ -2831,6 +2853,8 @@ class CombatEngine:
|
||||
for i in range(n):
|
||||
if not attacker.active:
|
||||
return
|
||||
if not target.active:
|
||||
return
|
||||
if self._check_provoked_aoo(attacker, weapon):
|
||||
return
|
||||
label = weapon.name if n == 1 else f"{weapon.name} #{i + 1}"
|
||||
@@ -2840,6 +2864,20 @@ class CombatEngine:
|
||||
):
|
||||
return
|
||||
|
||||
def _melee_weapon_usable(
|
||||
self, attacker: CombatantState, target: CombatantState, weapon: AttackSpec
|
||||
) -> bool:
|
||||
"""True if ``weapon`` (a melee weapon) can reach ``target`` with clear LoE."""
|
||||
if not has_line_of_effect_cells(
|
||||
self._grid, attacker.occupied_cells(), target.occupied_cells()
|
||||
):
|
||||
return False
|
||||
dist_ft = self.creature_distance(attacker, target) * _SQUARE_FT
|
||||
effective_reach_ft = _effective_reach_ft(attacker, weapon)
|
||||
if weapon.reach_ft > _SQUARE_FT:
|
||||
return _SQUARE_FT < dist_ft <= effective_reach_ft
|
||||
return dist_ft <= effective_reach_ft
|
||||
|
||||
def _step5(self, state: CombatantState, target: CombatantState) -> None:
|
||||
if state.moved_this_turn:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user