diff --git a/src/pf1e_simulator/combat.py b/src/pf1e_simulator/combat.py index 46317ee..cd491bc 100644 --- a/src/pf1e_simulator/combat.py +++ b/src/pf1e_simulator/combat.py @@ -1111,7 +1111,7 @@ ACTION_RESULT_REASONS: frozenset[str] = frozenset({ "no_line_of_effect", "no_slot", "insufficient_resource", "downgraded:staggered", "downgraded:nauseated", "downgraded:action_economy", "downgraded:cast_time", - "downgraded:surprise", + "downgraded:surprise", "downgraded:immediate_swift", "delayed", "already_delayed", "ready", "not_prone", "already_prone", "unreachable_destination", "weapon_unavailable", "invalid_destination", "cannot_combine", diff --git a/tests/test_immediate_actions.py b/tests/test_immediate_actions.py index 7fefcbd..460804c 100644 --- a/tests/test_immediate_actions.py +++ b/tests/test_immediate_actions.py @@ -10,7 +10,9 @@ from __future__ import annotations from typing import Literal +import pf1e_simulator.combat as combat_mod from pf1e_simulator.combat import ( + ACTION_RESULT_REASONS, Action, CombatantState, CombatEngine, @@ -497,3 +499,16 @@ def test_damage_taken_reaction_fires() -> None: result = engine.run() assert any("reactor: short bow vs caster" in line for line in result.transcript) assert caster_state.hp < 50 + + +def test_downgraded_immediate_swift_is_in_action_result_reasons() -> None: + """The reason vocabulary published to policies must contain every reason the + engine emits: downgraded:immediate_swift (swift dropped because an + in-turn immediate spent it) was emitted but missing from the frozenset.""" + assert "downgraded:immediate_swift" in ACTION_RESULT_REASONS + emitted = { + value + for name, value in vars(combat_mod).items() + if name.startswith("_REASON_") and isinstance(value, str) + } + assert emitted <= set(ACTION_RESULT_REASONS)