fix(rules): correct metamagic slots, touch-spell delivery, and defensive/grapple casting

This commit is contained in:
2026-09-11 00:06:06 +02:00
parent 9aee4f6f6b
commit 890b7959d4
4 changed files with 691 additions and 52 deletions
+12 -6
View File
@@ -114,9 +114,10 @@ def _make_engine(
caster_pos: tuple[int, int] = (0, 0),
target_pos: tuple[int, int] = (3, 0),
spell_registry: dict[str, SpellSpec] | None = None,
target_side: str = "monsters",
) -> tuple[CombatEngine, CombatantState, CombatantState]:
c = CombatantState(combatant=caster, side="players", pos=caster_pos, hp=caster.hp_max)
t = CombatantState(combatant=target, side="monsters", pos=target_pos, hp=target.hp_max)
t = CombatantState(combatant=target, side=target_side, pos=target_pos, hp=target.hp_max)
legend = {".": TerrainType(type="floor", move_cost=1)}
spec = MapSpec(name="test", terrain=tuple(["." * 12] * 12), legend=legend)
grid = Grid.from_spec(spec)
@@ -435,7 +436,8 @@ class TestCureLightWounds:
def test_heals_damage(self) -> None:
caster = _make_caster()
target = _make_target(hp_max=30)
engine, c, t = _make_engine(caster, target, [3, 3, 5], target_pos=(1, 0))
engine, c, t = _make_engine(caster, target, [3, 5], target_pos=(1, 0),
target_side="players")
t.hp = 10
engine._cast_spell(c, t, "Cure Light Wounds")
assert t.hp == 10 + (5 + 1)
@@ -443,7 +445,8 @@ class TestCureLightWounds:
def test_heal_capped_at_max(self) -> None:
caster = _make_caster()
target = _make_target(hp_max=30)
engine, c, t = _make_engine(caster, target, [3, 3, 6], target_pos=(1, 0))
engine, c, t = _make_engine(caster, target, [3, 3, 6], target_pos=(1, 0),
target_side="players")
t.hp = 28
engine._cast_spell(c, t, "Cure Light Wounds")
assert t.hp == 30
@@ -457,7 +460,8 @@ class TestBullsStrength:
def test_applies_buff_effects(self) -> None:
caster = _make_caster()
target = _make_target()
engine, c, t = _make_engine(caster, target, [3, 3], target_pos=(1, 0))
engine, c, t = _make_engine(caster, target, [3, 3], target_pos=(1, 0),
target_side="players")
engine._cast_spell(c, t, "Bull's Strength")
buff_mods = [m for b in t.active_buffs for m in b.modifiers]
atk_mods = [e for e in buff_mods if e.target == "attack"]
@@ -470,7 +474,8 @@ class TestBullsStrength:
def test_transcript_shows_buff(self) -> None:
caster = _make_caster()
target = _make_target()
engine, c, t = _make_engine(caster, target, [3, 3], target_pos=(1, 0))
engine, c, t = _make_engine(caster, target, [3, 3], target_pos=(1, 0),
target_side="players")
engine._cast_spell(c, t, "Bull's Strength")
assert any("buff" in line for line in engine._transcript)
@@ -614,7 +619,8 @@ class TestActiveIsSingleSource:
def test_legacy_lists_stay_empty_active_lists_populated(self) -> None:
caster = _make_caster()
target = _make_target()
engine, c, t = _make_engine(caster, target, [2, 3, 3], target_pos=(1, 0))
engine, c, t = _make_engine(caster, target, [2, 3, 3], target_pos=(1, 0),
target_side="players")
engine._cast_spell(c, t, "Hold Person")
engine._cast_spell(c, t, "Bull's Strength")
assert t.conditions == []