fix(rules): use Dexterity for the CMB of Tiny and smaller creatures

PF1e (SRD Combat Maneuvers): "Creatures that are size Tiny or smaller use their Dexterity modifier in place of their Strength modifier to determine their CMB." _maneuver_cmb now swaps Str for Dex when the size is Tiny, Diminutive or Fine. True 0-reach and co-occupancy for Tiny-or-smaller stay unmodelled (5-ft grid abstraction), documented in the README.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-09-12 08:44:44 +02:00
parent 9d583a04c0
commit 47010c6e00
3 changed files with 56 additions and 7 deletions
+13 -1
View File
@@ -271,6 +271,10 @@ _SIZE_MODIFIERS: dict[str, int] = {
"medium": 0, "large": 1, "huge": 2, "gargantuan": 4, "colossal": 8,
}
# PF1e (SRD Combat Maneuvers): creatures Tiny or smaller use their Dexterity
# modifier in place of their Strength modifier to determine their CMB.
_TINY_OR_SMALLER_SIZES: frozenset[str] = frozenset({"tiny", "diminutive", "fine"})
# 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] = {
@@ -4033,9 +4037,17 @@ class CombatEngine:
)
def _maneuver_cmb(self, state: CombatantState) -> int:
# PF1e (SRD Combat Maneuvers): "Creatures that are size Tiny or
# smaller use their Dexterity modifier in place of their Strength
# modifier to determine their CMB."
ability_mod = (
state.combatant.abilities.dex_mod
if state.combatant.size.lower() in _TINY_OR_SMALLER_SIZES
else state.combatant.abilities.str_mod
)
return (
state.combatant.cmb
or (state.combatant.bab + state.combatant.abilities.str_mod + self._size_mod(state))
or (state.combatant.bab + ability_mod + self._size_mod(state))
)
def _maneuver_cmd(self, target: CombatantState) -> int: