fix(rules): exclude the caster from a line spell's area
CRB Magic - Aiming a Spell: a line 'starts from any corner of your square', so the caster's own square is outside the template (SRD Lightning Bolt: 'The bolt begins at your fingertips' - it leaves the square, it does not fill it). line_cells now drops the origin and takes the length_cells squares beyond it; a degenerate aim affects nothing. Updated the Lightning Bolt and Line Sparks tests (caster no longer caught) and the README. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -197,8 +197,9 @@ def test_line_hits_the_straight_ray_only() -> None:
|
||||
"""Given: caster (6,2) aiming east at foe_ray (6,5); foe_off (5,5) sits a
|
||||
row away, foe_west (6,0) is behind the caster
|
||||
When: cast the 120-ft line aimed at foe_ray
|
||||
Then: every creature ON the ray (the caster's own cell — the line starts
|
||||
at your fingertips — and foe_ray) takes damage; the off-row and the
|
||||
Then: every creature ON the ray beyond the caster's own square (foe_ray)
|
||||
takes damage; the caster's own cell is NOT part of the line (CRB: it
|
||||
"starts from any corner of your square"), and the off-row and the
|
||||
behind-caster foes are outside the 5-ft-wide template."""
|
||||
caster = _caster()
|
||||
foe_ray = make_state(make_combatant("foe_ray", hp=100), "monsters", (6, 5))
|
||||
@@ -210,7 +211,7 @@ def test_line_hits_the_straight_ray_only() -> None:
|
||||
)
|
||||
assert result.ok
|
||||
assert foe_ray.hp == 100 - 4
|
||||
assert caster.hp == 100 - 4 # the line's first square is the caster's own
|
||||
assert caster.hp == 100 # the caster's own square is outside the line
|
||||
assert foe_off.hp == 100
|
||||
assert foe_west.hp == 100
|
||||
|
||||
|
||||
@@ -462,22 +462,24 @@ class TestLightningBoltSave:
|
||||
1d6 per caster level max 10d6, ref save for half) cast by the level-5
|
||||
fixture at a target straight ahead
|
||||
When: the primary target succeeds its save
|
||||
Then: damage is halved — and the caster, whose own square the line
|
||||
starts in ("the bolt begins at your fingertips"), is caught as well."""
|
||||
Then: damage is halved — and the caster is NOT caught by their own line
|
||||
(CRB, Magic — Aiming a Spell: the line "starts from any corner of your
|
||||
square", so the caster's own square is outside the template)."""
|
||||
|
||||
def test_successful_save_still_halves(self) -> None:
|
||||
caster = _make_caster()
|
||||
target = _make_target(hp_max=200)
|
||||
# primary save (target) + secondary save (caster, same line) +
|
||||
# 5 damage dice each (1d6/level, level 5)
|
||||
# primary save (target, SAVED on a natural 20) + 5 damage dice
|
||||
# (1d6/level, level 5); the caster is no longer in the line so no
|
||||
# secondary save and no caster damage dice are rolled.
|
||||
engine, c, t = _make_engine(
|
||||
caster, target, [20, 20] + [3] * 10
|
||||
caster, target, [20] + [3] * 5
|
||||
)
|
||||
hp_before = t.hp
|
||||
engine._cast_spell(c, t, "Lightning Bolt")
|
||||
full = 3 + 3 + 3 + 3 + 3
|
||||
assert t.hp == hp_before - (full // 2)
|
||||
assert c.hp == 20 - (full // 2)
|
||||
assert c.hp == 20 # the caster's own square is outside the line
|
||||
|
||||
|
||||
class TestDamageEffectHalfOnSaveDefault:
|
||||
|
||||
Reference in New Issue
Block a user