fix(rules): correct P1 PF1e conformance bugs (area saves, concentration, prone, slots, helpless, nauseated)
This commit is contained in:
@@ -108,7 +108,9 @@ def _cast_scene(
|
||||
(distance 0) but not foe_out (distance 5) nor the caster (distance 9).
|
||||
"""
|
||||
caster = make_state(
|
||||
make_combatant("caster", hp=100, spells=["Fireball"]), "players", (7, 0)
|
||||
make_combatant(
|
||||
"caster", hp=100, spells=["Fireball"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (7, 0)
|
||||
)
|
||||
foe_in = make_state(make_combatant("foe_in", hp=100), "monsters", (0, 4))
|
||||
foe_out = make_state(make_combatant("foe_out", hp=100), "monsters", (4, 7))
|
||||
@@ -187,7 +189,9 @@ def test_point_cast_without_line_of_effect_rejected() -> None:
|
||||
When: cast_spell at that unseen point
|
||||
Then: invalid_destination rejection without casting."""
|
||||
caster = make_state(
|
||||
make_combatant("caster", hp=100, spells=["Fireball"]), "players", (0, 0)
|
||||
make_combatant(
|
||||
"caster", hp=100, spells=["Fireball"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (0, 0)
|
||||
)
|
||||
foe = make_state(make_combatant("foe", hp=100), "monsters", (0, 6))
|
||||
eng = _engine([caster, foe], [], grid=_walled_grid())
|
||||
@@ -216,7 +220,9 @@ def test_point_cast_rolls_a_save_per_creature_in_area() -> None:
|
||||
When: a target-less point cast at (0,4)
|
||||
Then: no primary save is rolled (queue would shift otherwise) and EACH
|
||||
creature rolls its own: foe_a fails (2) full 24, foe_b saves (20) half 12."""
|
||||
caster = make_state(make_combatant("caster", hp=100, spells=["Fireball"]), "players", (7, 0))
|
||||
caster = make_state(make_combatant(
|
||||
"caster", hp=100, spells=["Fireball"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (7, 0))
|
||||
foe_a = make_state(make_combatant("foe_a", hp=100), "monsters", (0, 4))
|
||||
foe_b = make_state(make_combatant("foe_b", hp=100), "monsters", (0, 5))
|
||||
eng = _engine([caster, foe_a, foe_b], [2] + [3] * 8 + [20] + [3] * 8)
|
||||
@@ -233,7 +239,9 @@ def test_point_cast_no_save_area_spell_rolls_damage_dice_only() -> None:
|
||||
When: a target-less point cast
|
||||
Then: no saving throw is rolled at all — only the 8 damage dice."""
|
||||
caster = make_state(
|
||||
make_combatant("caster", hp=100, spells=["Spark Burst"]), "players", (7, 0)
|
||||
make_combatant(
|
||||
"caster", hp=100, spells=["Spark Burst"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (7, 0)
|
||||
)
|
||||
foe = make_state(make_combatant("foe", hp=100), "monsters", (0, 4))
|
||||
eng = _engine([caster, foe], [3] * 8)
|
||||
@@ -254,7 +262,9 @@ def test_creature_centered_cast_transcript_pinned() -> None:
|
||||
When: cast_spell with target_id and NO target_pos (the legacy path)
|
||||
Then: the transcript is the pinned creature-centered line — primary save
|
||||
rolled once, reused for the target in the area, 8d6 all 3s."""
|
||||
caster = make_state(make_combatant("caster", hp=100, spells=["Fireball"]), "players", (7, 0))
|
||||
caster = make_state(make_combatant(
|
||||
"caster", hp=100, spells=["Fireball"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (7, 0))
|
||||
foe = make_state(make_combatant("foe", hp=100), "monsters", (0, 4))
|
||||
eng = _engine([caster, foe], [2] + [3] * 8)
|
||||
result = eng._execute(
|
||||
@@ -276,13 +286,17 @@ def test_target_pos_at_target_cell_matches_creature_centered() -> None:
|
||||
target_pos set to the target's own cell (target_id kept)
|
||||
When: both casts resolve with identical scripted rolls
|
||||
Then: transcripts and hp outcomes are byte-identical."""
|
||||
caster = make_state(make_combatant("caster", hp=100, spells=["Fireball"]), "players", (7, 0))
|
||||
caster = make_state(make_combatant(
|
||||
"caster", hp=100, spells=["Fireball"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (7, 0))
|
||||
foe = make_state(make_combatant("foe", hp=100), "monsters", (0, 4))
|
||||
plain = _engine([caster, foe], [2] + [3] * 8)
|
||||
plain._execute(
|
||||
caster, Action(kind="cast_spell", target_id="foe", spell_name="Fireball")
|
||||
)
|
||||
caster2 = make_state(make_combatant("caster", hp=100, spells=["Fireball"]), "players", (7, 0))
|
||||
caster2 = make_state(make_combatant(
|
||||
"caster", hp=100, spells=["Fireball"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (7, 0))
|
||||
foe2 = make_state(make_combatant("foe", hp=100), "monsters", (0, 4))
|
||||
pointed = _engine([caster2, foe2], [2] + [3] * 8)
|
||||
pointed._execute(
|
||||
@@ -304,7 +318,9 @@ def test_non_area_spell_with_target_pos_resolves_on_target() -> None:
|
||||
Then: target_pos is ignored — the missile hits its creature (1d4+1 = 4)
|
||||
and the transcript is the ordinary creature-centered line."""
|
||||
caster = make_state(
|
||||
make_combatant("caster", hp=100, spells=["Magic Missile"]), "players", (0, 0)
|
||||
make_combatant(
|
||||
"caster", hp=100, spells=["Magic Missile"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (0, 0)
|
||||
)
|
||||
foe = make_state(make_combatant("foe", hp=100), "monsters", (0, 2))
|
||||
eng = _engine([caster, foe], [3])
|
||||
@@ -325,7 +341,9 @@ def test_non_area_point_cast_without_target_is_harmless_no_op() -> None:
|
||||
When: the action is dispatched with an empty roll queue
|
||||
Then: nothing crashes, nothing is cast, no die is rolled."""
|
||||
caster = make_state(
|
||||
make_combatant("caster", hp=100, spells=["Magic Missile"]), "players", (0, 0)
|
||||
make_combatant(
|
||||
"caster", hp=100, spells=["Magic Missile"], spell_slots={1: 9, 2: 9, 3: 9}
|
||||
), "players", (0, 0)
|
||||
)
|
||||
foe = make_state(make_combatant("foe", hp=100), "monsters", (0, 2))
|
||||
eng = _engine([caster, foe], [])
|
||||
|
||||
Reference in New Issue
Block a user