fix(rules): document the burst center-to-center convention as a deferred SRD approximation - the SRD (aiming-a-spell.md) measures a burst from a grid INTERSECTION with the far-edge rule ('count from intersection to intersection'; a square is inside only 'if the far edge of a square is within the spell's area'), but the engine's burst origin is a CELL (target_pos, centered_on_caster, resolved target) counted center-to-center 5-10-5 in _creatures_in_range; matching RAW exactly needs an intersection coordinate added to AreaEffect/Action.target_pos/the observation area snapshot contract plus a per-caster corner choice (the far-edge boundary result depends entirely on which of the cell's four corners is the origin), so it is DEFERRED with a precise explanation in areas.py and a pointer from _creatures_in_range; no behavior change, golden untouched

This commit is contained in:
2026-09-20 00:16:18 +02:00
parent e6021a6dda
commit b775d46850
2 changed files with 37 additions and 0 deletions
+31
View File
@@ -32,6 +32,37 @@ PF1e (CRB, Magic — Aiming a Spell):
Bursts need no template here: they are circles around a center cell and are
resolved by ``CombatEngine._area_creatures`` (which additionally drops
creatures without line of effect from the burst's center).
BURST MEASUREMENT - DOCUMENTED DEFERRED APPROXIMATION (cell vs intersection).
The SRD ("Aiming a Spell - Area") measures a burst from a point of origin that
is ALWAYS a grid intersection: "instead of counting from the center of one
square to the center of the next, you count from intersection to intersection",
and a square is inside the area only "if the far edge of a square is within the
spell's area" (a square the area "only touches ... [at] the near edge" is
unaffected). The engine cannot apply that rule faithfully because its burst
origin is a CELL, not an intersection: ``Action.target_pos``, the
``centered_on_caster`` caster square and the resolved target's square are all
cells, and ``CombatEngine._creatures_in_range`` counts center-to-center 5-10-5
(``grid.distance(center, cell) <= radius_cells``). Two consequences make a
faithful fix out of scope for a contract-preserving pass:
1. A cell has four corner intersections, and which one is the point of origin
is a caster choice the engine never records. The far-edge rule's boundary
result (which diagonal squares are in vs out) depends entirely on that
choice, so there is no single "SRD-correct" square set to compute from a
cell center without first adding an intersection coordinate to the data
model.
2. Representing the origin as an intersection would change ``AreaEffect``,
``Action.target_pos`` and the observation ``area`` snapshot contract (a
documented Phase 2 LLM frontier), and would rework the burst line-of-effect
and Reflex-cover origins (``{aim}`` / ``{center}`` cells).
Until that representation change is made, bursts stay centered on the resolved
cell and measured center-to-center under 5-10-5 - a symmetric, conservative
approximation whose boundary squares can differ from RAW by one square on
diagonals (e.g. a 15/20-ft burst's far diagonal cells). This is the residual
recorded in docs/conformance-srd.md ("origin mesuré centre-à-centre plutôt
qu'intersection-à-intersection").
"""
from __future__ import annotations
+6
View File
@@ -7573,6 +7573,12 @@ class CombatEngine:
) -> list[CombatantState]:
"""Return creatures within radius_cells of center.
The burst center is a CELL and the radius is counted center-to-center
under 5-10-5 - a documented approximation of the SRD's
intersection-to-intersection far-edge measurement (see the
"BURST MEASUREMENT" note in ``areas.py`` for why a faithful
intersection origin is deferred).
``active_only`` (default) keeps only acting creatures; an area spell
passes False so its burst also catches dying/unconscious creatures
(SRD aiming-a-spell.md: a burst "affects whatever it catches in its