feat(rules): support cone and line spell areas
AreaEffect gains shape: burst|cone|line (CRB Magic - Aiming a Spell). Cones (90 deg, length = radius_ft) and lines (5 ft wide, Bresenham ray) originate at the caster's cell and are aimed at the resolved target or the U7b point-cast center; bursts are unchanged. Shared per-creature save + SR application; the observation area dict already exposes the shape. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -121,6 +121,7 @@ from dataclasses import asdict, dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Literal
|
||||
|
||||
from pf1e_simulator.areas import cone_cells, line_cells
|
||||
from pf1e_simulator.conditions import CONDITIONS_BY_NAME, Condition
|
||||
from pf1e_simulator.dice import dice_to_str
|
||||
from pf1e_simulator.effects import ActiveBuff, ActiveCondition, StatModifier, resolve_modifiers
|
||||
@@ -5888,16 +5889,21 @@ class CombatEngine:
|
||||
center_pos: tuple[int, int] | None = None,
|
||||
save_type: SaveType = "none",
|
||||
) -> str:
|
||||
"""Apply an area effect to all creatures within radius.
|
||||
"""Apply an area effect to all creatures within its template.
|
||||
|
||||
The burst is centered on ``center_pos`` when given (U7b point
|
||||
targeting), otherwise on the resolved target's position.
|
||||
PF1e (CRB, Magic - Aiming a Spell): a burst/spread centers on
|
||||
``center_pos`` when given (U7b point targeting), otherwise on the
|
||||
resolved target's position; a cone or line starts at the CASTER's
|
||||
square and is aimed at that same resolved cell (the cone "shoots
|
||||
away from you in a quarter-circle in the direction you designate",
|
||||
the line "shoots away from you in a line in the direction you
|
||||
designate"). Every creature whose occupied cells intersect the
|
||||
template is subject to the effect.
|
||||
"""
|
||||
radius_cells = effect.radius_ft // _SQUARE_FT
|
||||
center = _burst_center(target, center_pos)
|
||||
if center is None:
|
||||
return "no targets in area"
|
||||
affected = self._creatures_in_range(center, radius_cells)
|
||||
affected = self._area_creatures(effect, caster, center)
|
||||
|
||||
if not affected:
|
||||
return "no targets in area"
|
||||
@@ -5933,6 +5939,31 @@ class CombatEngine:
|
||||
f"{total_conditions} conditions, {total_buffs} buffs"
|
||||
)
|
||||
|
||||
def _area_creatures(
|
||||
self, effect: AreaEffect, caster: CombatantState, aim: tuple[int, int]
|
||||
) -> list[CombatantState]:
|
||||
"""Creatures intersecting the template of an area effect.
|
||||
|
||||
Burst: circle of ``radius_ft`` around the resolved center ``aim``
|
||||
(unchanged behaviour). Cone/line: the template originates at the
|
||||
caster's anchor cell and opens toward ``aim`` (PF1e CRB "Aiming a
|
||||
Spell" — see ``areas.py``); a creature is affected when ANY of its
|
||||
occupied cells lies inside the template.
|
||||
"""
|
||||
span_cells = effect.radius_ft // _SQUARE_FT
|
||||
if effect.shape == "burst":
|
||||
return self._creatures_in_range(aim, span_cells)
|
||||
origin = caster.pos
|
||||
if effect.shape == "cone":
|
||||
cells = cone_cells(self._grid, origin, aim, span_cells)
|
||||
else:
|
||||
cells = line_cells(self._grid, origin, aim, span_cells)
|
||||
return [
|
||||
state
|
||||
for state in self._states
|
||||
if state.active and state.occupied_cells() & cells
|
||||
]
|
||||
|
||||
def _creatures_in_range(
|
||||
self, center: tuple[int, int], radius_cells: int
|
||||
) -> list[CombatantState]:
|
||||
@@ -6010,6 +6041,7 @@ class CombatEngine:
|
||||
return BuffEffect(modifiers=effect.modifiers, duration_rounds=duration)
|
||||
if isinstance(effect, AreaEffect):
|
||||
return AreaEffect(
|
||||
shape=effect.shape,
|
||||
radius_ft=effect.radius_ft,
|
||||
effects=tuple(
|
||||
self._metamagic_transform_sub(sub, has_extend=has_extend)
|
||||
|
||||
Reference in New Issue
Block a user