feat(combat): area-of-effect spells

This commit is contained in:
2026-09-04 22:55:43 +02:00
parent 09062b37ea
commit 8ae344fd16
4 changed files with 155 additions and 16 deletions
+24 -4
View File
@@ -54,7 +54,22 @@ class BuffEffect:
duration_rounds: int
SpellEffect = DamageEffect | ConditionEffect | HealEffect | BuffEffect
@dataclass(frozen=True)
class AreaEffect:
"""Area of effect spell (burst only for Phase 0).
- shape: only "burst" supported (circle around target point)
- radius_ft: radius in feet (e.g., 20 for fireball)
- effects: the effects applied to each creature in the area
- save_for_half: if True, successful save halves damage
"""
shape: Literal["burst"] = "burst"
radius_ft: int = 20
effects: tuple[SpellEffect, ...] = ()
save_for_half: bool = True
SpellEffect = DamageEffect | ConditionEffect | HealEffect | BuffEffect | AreaEffect
@dataclass(frozen=True)
@@ -111,10 +126,15 @@ def _parse_effect(data: dict[str, Json]) -> SpellEffect:
)
for m in cast("list[dict[str, Json]]", data["modifiers"])
),
duration_rounds=int(data["duration_rounds"]), # type: ignore[arg-type]
duration_rounds=int(data.get("duration_rounds", 0)), # type: ignore[arg-type]
)
msg = f"Unknown effect type: {effect_type}"
raise ValueError(msg)
if effect_type == "area":
return AreaEffect(
radius_ft=int(data.get("radius_ft", 20)), # type: ignore[arg-type]
effects=tuple(_parse_effect(e) for e in data["effects"]), # type: ignore[index]
save_for_half=bool(data.get("save_for_half", True)),
)
raise ValueError(f"Unknown effect type: {effect_type}")
def load_spell(path: str | Path) -> SpellSpec: