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:
+20
-12
@@ -11,8 +11,11 @@ PF1e (CRB, Magic — Aiming a Spell):
|
||||
limit of its range [...]. A line-shaped spell affects all creatures in
|
||||
squares through which the line passes." — modeled here as the 5-ft-wide
|
||||
Bresenham ray from the origin through the aim cell, truncated to the
|
||||
line's length (the origin cell counts as the first square: the bolt
|
||||
"begins at your fingertips", SRD Lightning Bolt).
|
||||
line's length. The line "starts from any corner of your square", so the
|
||||
CASTER's own square is NOT one of the affected squares: the affected
|
||||
squares are the ones beyond the origin along the ray (SRD Lightning Bolt:
|
||||
"The bolt begins at your fingertips" — it leaves your square, it does not
|
||||
fill it).
|
||||
|
||||
Bursts need no template here: they are circles around a center cell and are
|
||||
resolved by ``CombatEngine._creatures_in_range`` (unchanged).
|
||||
@@ -74,23 +77,28 @@ def line_cells(
|
||||
|
||||
The line runs along the Bresenham ray from the origin toward ``aim``,
|
||||
extended to ``length_cells`` squares (CRB: it "extends to the limit of
|
||||
its range"). The origin cell is the first square of the line. Out-of-bounds
|
||||
cells simply drop out of the template (creatures only exist in bounds).
|
||||
A degenerate aim (origin == aim) affects only the origin cell.
|
||||
its range"). The line "starts from any corner of your square" (CRB, Magic
|
||||
— Aiming a Spell), so the CASTER's own square is NOT affected: the
|
||||
``length_cells`` affected squares are the ones *beyond* the origin along
|
||||
the ray. Out-of-bounds cells simply drop out of the template (creatures
|
||||
only exist in bounds). A degenerate aim (origin == aim) affects nothing.
|
||||
"""
|
||||
if length_cells <= 0:
|
||||
return frozenset()
|
||||
d_row = aim[0] - origin[0]
|
||||
d_col = aim[1] - origin[1]
|
||||
if d_row == 0 and d_col == 0:
|
||||
return frozenset({origin}) if grid.in_bounds(origin) else frozenset()
|
||||
# Extend the aim vector so the ray spans `length_cells` squares (the
|
||||
# dominant axis step count of a Bresenham line is max(|d_row|, |d_col|)).
|
||||
scale = length_cells / max(abs(d_row), abs(d_col))
|
||||
return frozenset()
|
||||
# Extend the aim vector so the ray reaches `length_cells` squares BEYOND
|
||||
# the origin (the dominant-axis step count of a Bresenham line is
|
||||
# max(|d_row|, |d_col|)); the +1 accounts for the origin square we drop.
|
||||
scale = (length_cells + 1) / max(abs(d_row), abs(d_col))
|
||||
end = (origin[0] + round(d_row * scale), origin[1] + round(d_col * scale))
|
||||
# The line is `length_cells` squares LONG: the origin square counts as
|
||||
# the first of them (120 ft / 5 ft = 24 squares for Lightning Bolt).
|
||||
return frozenset(_bresenham(origin, end)[:length_cells])
|
||||
# The line is `length_cells` squares LONG starting just past the caster:
|
||||
# 120 ft / 5 ft = 24 squares, none of them the caster's own (SRD Lightning
|
||||
# Bolt: "The bolt begins at your fingertips" — it leaves your square).
|
||||
ray = _bresenham(origin, end)
|
||||
return frozenset(cell for cell in ray[1 : length_cells + 1] if grid.in_bounds(cell))
|
||||
|
||||
|
||||
def _bresenham(start: Pos, end: Pos) -> list[Pos]:
|
||||
|
||||
Reference in New Issue
Block a user