feat(loaders): map 8-PC feats
This commit is contained in:
@@ -255,7 +255,7 @@ class Action:
|
||||
]
|
||||
target_id: str | None = None
|
||||
spell_name: str | None = None
|
||||
maneuver: str | None = None # "trip", "disarm", "bull_rush", "sunder"
|
||||
maneuver: str | None = None # "trip", "disarm", "bull_rush", "sunder", "grapple", "escape"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -915,7 +915,7 @@ class CombatEngine:
|
||||
if self.has_condition_flag(state, "cant_attack"):
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: wait (cannot attack)")
|
||||
return
|
||||
if maneuver not in ("trip", "disarm", "bull_rush", "sunder"):
|
||||
if maneuver not in ("trip", "disarm", "bull_rush", "sunder", "grapple", "escape"):
|
||||
return
|
||||
|
||||
# Calculate CMB and CMD
|
||||
@@ -974,11 +974,47 @@ class CombatEngine:
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: sunder vs {target.combatant.id} d20={roll}+{attacker_cmb}={total} CMD {target_cmd} -> SUCCESS ({dmg} damage to weapon)")
|
||||
else:
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: sunder vs {target.combatant.id} d20={roll}+{attacker_cmb}={total} CMD {target_cmd} -> FAILED")
|
||||
elif maneuver == "grapple":
|
||||
if success:
|
||||
# Initiate grapple
|
||||
target.active_conditions.append(
|
||||
ActiveCondition(
|
||||
condition=CONDITIONS_BY_NAME["grappled"],
|
||||
rounds_left=10, # Lasts until broken
|
||||
source=f"maneuver: grapple by {state.combatant.id}",
|
||||
)
|
||||
)
|
||||
target.conditions.append(CONDITIONS_BY_NAME["grappled"])
|
||||
target.grappled_with = state.combatant.id
|
||||
state.grappled_with = target.combatant.id
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: grapple vs {target.combatant.id} d20={roll}+{attacker_cmb}={total} CMD {target_cmd} -> SUCCESS (grappled)")
|
||||
else:
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: grapple vs {target.combatant.id} d20={roll}+{attacker_cmb}={total} CMD {target_cmd} -> FAILED")
|
||||
elif maneuver == "escape":
|
||||
# Escape uses CMB or Escape Artist skill
|
||||
escape_bonus = max(
|
||||
state.combatant.cmb or (state.combatant.bab + state.combatant.abilities.str_mod + self._size_mod(state)),
|
||||
self.resolve_skill(state, "escape_artist", 0).total if hasattr(state.combatant, 'skills') and 'escape_artist' in state.combatant.skills else 0
|
||||
)
|
||||
target_cmb = target.combatant.cmb or (target.combatant.bab + target.combatant.abilities.str_mod + self._size_mod(target))
|
||||
target_cmd = 10 + target_cmb + target.combatant.abilities.dex_mod
|
||||
roll = self._rng.d20()
|
||||
total = roll + escape_bonus
|
||||
success = roll != 1 and total >= target_cmd
|
||||
if success:
|
||||
# Remove grappled condition from both
|
||||
target.active_conditions = [ac for ac in target.active_conditions if ac.condition.name != "grappled"]
|
||||
target.conditions = [c for c in target.conditions if c.name != "grappled"]
|
||||
state.active_conditions = [ac for ac in state.active_conditions if ac.condition.name != "grappled"]
|
||||
state.conditions = [c for c in state.conditions if c.name != "grappled"]
|
||||
target.grappled_with = None
|
||||
state.grappled_with = None
|
||||
else:
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: escape vs {target.combatant.id} d20={roll}+{escape_bonus}={total} CMD {target_cmd} -> FAILED")
|
||||
|
||||
def _execute_attack(
|
||||
self, state: CombatantState, target: CombatantState | None, kind: str
|
||||
) -> None:
|
||||
if target is None or not target.active:
|
||||
return
|
||||
if self.has_condition_flag(state, "cant_attack"):
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: wait (cannot attack)")
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user