fix(rules): classify run as a full-round action
PF1e CRB: running is a full-round action, not a move action. The budget filter now classifies run as full-round (consumes the standard AND the move slot), _FULL_ROUND_KINDS includes it for the staggered filter, and the nauseated filter drops it (run is no longer move-like). The moved_this_turn/running flags are unchanged; the _execute_run moved_this_turn early return stays as defense in depth. default_policy never emits run, so the golden transcripts are untouched. Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -3393,6 +3393,10 @@ class CombatEngine:
|
||||
"""
|
||||
kind = "run"
|
||||
if state.moved_this_turn:
|
||||
# Defense in depth, mirroring the _move/_5foot_step no-op-ok
|
||||
# convention. Unreachable through the filtered path: run is a
|
||||
# full-round action, so no move-slot action can precede it in a
|
||||
# kept sequence (the budget filter would have dropped one).
|
||||
return ActionResult(kind=kind, ok=True, reason=_REASON_OK)
|
||||
if self.has_condition_flag(state, "cant_move"):
|
||||
self._log(f"round {self._current_round} {state.combatant.id}: wait (cannot move)")
|
||||
@@ -5781,7 +5785,9 @@ def _choose_spell_action(
|
||||
return None
|
||||
|
||||
|
||||
_FULL_ROUND_KINDS: frozenset[str] = frozenset({"full_attack", "charge", "withdraw", "full_round"})
|
||||
_FULL_ROUND_KINDS: frozenset[str] = frozenset(
|
||||
{"full_attack", "charge", "withdraw", "full_round", "run"}
|
||||
)
|
||||
_SWIFT_BONUS_KINDS: frozenset[str] = frozenset({"deed", "inspire_courage", "swift"})
|
||||
# PF1e (CRB): a nauseated creature can take only a single move action per
|
||||
# turn — no standard, no full-round, no attacks, no spellcasting.
|
||||
@@ -5843,12 +5849,11 @@ def _nauseated_filter(actions: tuple[Action, ...]) -> tuple[Action, ...]:
|
||||
# SpellSpec has no cast_time field, so every spell casts as a standard),
|
||||
# maneuver, channel_energy, hex, delay, ready, total_defense, aid_another
|
||||
# - move: move, stand_up, retrieve_weapon (drawing a weapon is a move action),
|
||||
# repair_weapon, run (a full-round action in RAW, but this engine models it
|
||||
# as move machinery that sets moved_this_turn and already excludes it from
|
||||
# _FULL_ROUND_KINDS — kept consistent, see README)
|
||||
# repair_weapon
|
||||
# - swift: swift, deed, inspire_courage (the engine's swift bonus kinds)
|
||||
# - full-round: full_attack, charge, withdraw, full_round, spell_combat
|
||||
# (spell + full attack in one round); consumes the standard AND the move slot
|
||||
# (spell + full attack in one round), run (4x-speed movement, PF1e full-round);
|
||||
# consumes the standard AND the move slot
|
||||
# - free/conditional (everything else, no budget): wait, free, immediate
|
||||
# (off-turn; it consumes the NEXT swift via immediate_used, not this turn's
|
||||
# slots), 5foot_step (free step, mutually exclusive with other movement via
|
||||
@@ -5860,13 +5865,13 @@ _ACTION_ECONOMY_STANDARD_KINDS: frozenset[str] = frozenset({
|
||||
"delay", "ready", "total_defense", "aid_another",
|
||||
})
|
||||
_ACTION_ECONOMY_MOVE_KINDS: frozenset[str] = frozenset({
|
||||
"move", "stand_up", "retrieve_weapon", "repair_weapon", "run",
|
||||
"move", "stand_up", "retrieve_weapon", "repair_weapon",
|
||||
})
|
||||
_ACTION_ECONOMY_SWIFT_KINDS: frozenset[str] = frozenset(
|
||||
{"swift", "deed", "inspire_courage"}
|
||||
)
|
||||
_ACTION_ECONOMY_FULL_ROUND_KINDS: frozenset[str] = frozenset({
|
||||
"full_attack", "charge", "withdraw", "full_round", "spell_combat",
|
||||
"full_attack", "charge", "withdraw", "full_round", "spell_combat", "run",
|
||||
})
|
||||
_ACTION_ECONOMY_FREE_KINDS: frozenset[str] = frozenset({
|
||||
"wait", "free", "immediate", "5foot_step", "drop_prone",
|
||||
|
||||
Reference in New Issue
Block a user