feat(rules): allow a move action in place of a standard action
PF1e CRB (Actions in Combat): a move action can be taken instead of the standard one. The budget filter now converts a second move action onto the standard slot when the move slot is spent and the standard is still free, so (move, stand_up) and (move, move) are legal; a third move (or any further standard) is still surplus. The greedy walk is expressed with a spent-slot map to stay inside the C901 complexity ceiling. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -5900,32 +5900,38 @@ def _action_economy_filter(actions: tuple[Action, ...]) -> tuple[Action, ...]:
|
||||
Greedy in-order walk: each action is kept while its budget slot is free
|
||||
(one standard, one move, one swift; a full-round action is only legal
|
||||
while BOTH the standard and the move slot are still free and consumes
|
||||
them all). The kept sequence is therefore the earliest order-stable legal
|
||||
them all). PF1e (CRB, "Actions in Combat"): a move action can be taken
|
||||
"instead of" the standard one — when the move slot is already spent and
|
||||
the standard slot is still free, a further move action converts the
|
||||
standard slot (so (move, stand_up) and (move, move) are legal). The kept
|
||||
sequence is therefore the earliest order-stable legal
|
||||
prefix-subsequence; every skipped action is deterministic.
|
||||
"""
|
||||
kept: list[Action] = []
|
||||
standard_used = False
|
||||
move_used = False
|
||||
swift_used = False
|
||||
spent: dict[_ActionEconomySlot, bool] = {
|
||||
"standard": False,
|
||||
"move": False,
|
||||
"swift": False,
|
||||
"full_round": False,
|
||||
"free": False,
|
||||
}
|
||||
for action in actions:
|
||||
slot = _action_economy_slot(action.kind)
|
||||
if slot == "standard":
|
||||
if standard_used:
|
||||
if slot == "free":
|
||||
kept.append(action)
|
||||
continue
|
||||
if slot == "full_round":
|
||||
if spent["standard"] or spent["move"]:
|
||||
continue
|
||||
standard_used = True
|
||||
elif slot == "move":
|
||||
if move_used:
|
||||
continue
|
||||
move_used = True
|
||||
elif slot == "swift":
|
||||
if swift_used:
|
||||
continue
|
||||
swift_used = True
|
||||
elif slot == "full_round":
|
||||
if standard_used or move_used:
|
||||
continue
|
||||
standard_used = True
|
||||
move_used = True
|
||||
spent["standard"] = True
|
||||
spent["move"] = True
|
||||
kept.append(action)
|
||||
continue
|
||||
if slot == "move" and spent["move"]:
|
||||
slot = "standard" # conversion: a move action consumes the standard slot
|
||||
if spent[slot]:
|
||||
continue
|
||||
spent[slot] = True
|
||||
kept.append(action)
|
||||
return tuple(kept)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user