From 68a03a34f672139733e5ffa9c70a084532c1681c Mon Sep 17 00:00:00 2001 From: ctan Date: Wed, 16 Sep 2026 19:30:08 +0200 Subject: [PATCH] fix(rules): confused creatures make no attacks of opportunity (simplification documented) --- src/pf1e_simulator/conditions.py | 6 +++++- tests/test_aoo_defense.py | 22 ++++++++++++++++++++++ tests/test_bleeding_confused.py | 2 +- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/pf1e_simulator/conditions.py b/src/pf1e_simulator/conditions.py index f643240..ec973a8 100644 --- a/src/pf1e_simulator/conditions.py +++ b/src/pf1e_simulator/conditions.py @@ -317,9 +317,13 @@ BLEEDING = Condition( # Confused: at the beginning of each turn the subject rolls d% to act randomly # (CRB). The d% behavior is engine logic in ``_take_turn``; simplification: # the "if attacked, attack the attacker" clause is reduced to "nearest -# creature". No stat modifiers or behavioral flags. +# creature". SRD "Conditions—Confused": a confused creature "will not make +# attacks of opportunity against anything that it is not already devoted to +# attacking" — simplified to NO AoOs at all (cant_aoo), since the engine has +# no "already devoted" concept. No stat modifiers. CONFUSED = Condition( name="confused", + cant_aoo=True, ) # ── Registry ───────────────────────────────────────────────────────────────── diff --git a/tests/test_aoo_defense.py b/tests/test_aoo_defense.py index aba7738..682e1ac 100644 --- a/tests/test_aoo_defense.py +++ b/tests/test_aoo_defense.py @@ -154,3 +154,25 @@ def test_nauseated_guard_makes_no_aoo() -> None: assert engine._aoo_used.get("guard", 0) == 0 assert len(engine._rng._queue) == 2 assert runner_s.hp == 20 + + +def test_confused_guard_makes_no_aoo() -> None: + """Given: the guard is confused (SRD: "will not make attacks of + opportunity against anything that it is not already devoted to + attacking" — simplified to no AoOs at all, the engine has no + "already devoted" concept) + When: the runner provokes an AoO + Then: no opportunity is taken — no die rolled, no budget spent.""" + runner = make_combatant("runner", hp=20, ac=12) + guard = make_combatant("guard", hp=20, ac=15, attack_bonus=5) + states = [ + make_state(runner, "players", (2, 1)), + make_state(guard, "monsters", (2, 0)), + ] + engine = CombatEngine(ScriptedRng([15, 3]), _grid(("....", "....")), states) + runner_s, guard_s = engine._states + _add_condition(guard_s, "confused") + assert engine._resolve_aoo(guard_s, runner_s) is False + assert engine._aoo_used.get("guard", 0) == 0 + assert len(engine._rng._queue) == 2 + assert runner_s.hp == 20 diff --git a/tests/test_bleeding_confused.py b/tests/test_bleeding_confused.py index c15860f..3f2558c 100644 --- a/tests/test_bleeding_confused.py +++ b/tests/test_bleeding_confused.py @@ -320,4 +320,4 @@ def test_snapshot_exposes_bleed_damage() -> None: defs = dict(snap["condition_definitions"]) assert len(defs) == len(CONDITIONS_BY_NAME) assert defs["bleeding"]["flags"] == [] - assert defs["confused"]["flags"] == [] + assert defs["confused"]["flags"] == ["cant_aoo"]