fix(rules): restrict maneuvers to targets within one size category
PF1e (SRD Trip / Bull Rush / Overrun): "You can only [affect] an opponent who is no more than one size category larger than you." _execute_maneuver now rejects trip, bull_rush and overrun against a target two or more size categories larger: no roll, no provoked AoO, logged rejection. A _SIZE_ORDER ranking (Fine..Colossal, unknown -> Medium) backs the check; other maneuvers keep no size cap. README updated. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -262,6 +262,7 @@ _RUN_SPEED_FACTOR = 4 # PF1e: running is a full-round action at 4x speed
|
||||
_OVERRUN_PRONE_MARGIN = 5 # PF1e: overrun knocks prone only on a 5+ CMB margin
|
||||
_BULL_RUSH_EXTRA_PER_MARGIN = 5 # PF1e: +5 ft push per 5 points above CMD
|
||||
_TRIP_RETALIATION_MARGIN = 10 # PF1e: trip failing by 10+ knocks the attacker prone
|
||||
_MANEUVER_MAX_SIZE_DIFF = 2 # PF1e: 2+ size categories larger = unmaneuverable
|
||||
_SAVE_DESCRIPTOR_CONDITIONS = frozenset({
|
||||
"fear", "cold", "electricity", "fey", "divination", "poison", "disease",
|
||||
"mind_affecting",
|
||||
@@ -277,6 +278,24 @@ _SIZE_MODIFIERS: dict[str, int] = {
|
||||
# modifier in place of their Strength modifier to determine their CMB.
|
||||
_TINY_OR_SMALLER_SIZES: frozenset[str] = frozenset({"tiny", "diminutive", "fine"})
|
||||
|
||||
# PF1e size order, smallest first (SRD "Size").
|
||||
_SIZE_ORDER: tuple[str, ...] = (
|
||||
"fine", "diminutive", "tiny", "small", "medium", "large", "huge",
|
||||
"gargantuan", "colossal",
|
||||
)
|
||||
|
||||
# PF1e (SRD Trip / Bull Rush / Overrun): "You can only [affect] an opponent
|
||||
# who is no more than one size category larger than you."
|
||||
_SIZE_RESTRICTED_MANEUVERS: frozenset[str] = frozenset({"trip", "bull_rush", "overrun"})
|
||||
|
||||
|
||||
def _size_rank(size: str) -> int:
|
||||
"""Index in the PF1e size order; unknown sizes rank as Medium."""
|
||||
try:
|
||||
return _SIZE_ORDER.index(size.lower())
|
||||
except ValueError:
|
||||
return _SIZE_ORDER.index("medium")
|
||||
|
||||
# PF1e natural reach for tall creatures (simplification: no long/quadruped
|
||||
# distinction). Unknown sizes fall back to 5 ft (the historical behavior).
|
||||
_SIZE_NATURAL_REACH_FT: dict[str, int] = {
|
||||
@@ -3947,6 +3966,18 @@ class CombatEngine:
|
||||
"overrun", "dirty_trick", "steal", "reposition", "drag",
|
||||
):
|
||||
return
|
||||
# PF1e (SRD Trip / Bull Rush / Overrun): "You can only [affect] an
|
||||
# opponent who is no more than one size category larger than you."
|
||||
# The attempt is illegal outright: no roll, no provoked AoO.
|
||||
if maneuver in _SIZE_RESTRICTED_MANEUVERS and (
|
||||
_size_rank(target.combatant.size) - _size_rank(state.combatant.size)
|
||||
>= _MANEUVER_MAX_SIZE_DIFF
|
||||
):
|
||||
self._log(
|
||||
f"round {self._current_round} {state.combatant.id}: {maneuver} vs "
|
||||
f"{target.combatant.id} rejected (target too large)"
|
||||
)
|
||||
return
|
||||
|
||||
# PF1e (CRB, Combat Maneuver): attempting the maneuver provokes an
|
||||
# attack of opportunity from the target — resolved BEFORE the CMB
|
||||
|
||||
Reference in New Issue
Block a user