data(spells): add batch 1 of PC spell list (32 spells)
This commit is contained in:
@@ -922,7 +922,7 @@ uv run basedpyright src # mode strict
|
||||
## Feuille de route
|
||||
|
||||
- **Phase 1** — magie et états : largement réalisée. Sorts par emplacements
|
||||
(`spells.py` + base `data/spells/`, 11 sorts), DD et sauvegardes, RD non
|
||||
(`spells.py` + base `data/spells/`, 43 sorts), DD et sauvegardes, RD non
|
||||
appliquée aux dégâts de sorts, concentration par situation, invocation,
|
||||
conditions effectives (`conditions.py`), manœuvres de combat, dons (55 noms
|
||||
résolus, `abilities.py` + chargeur) et capacités de classe (channel energy,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Abundant Ammunition",
|
||||
"level": 0,
|
||||
"school": "transmutation",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that conjures duplicate ammunition: attacks with the affected ammunition do not expend it. No combat-mechanical effect in the engine (ammunition is not tracked)."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Acid Splash",
|
||||
"level": 0,
|
||||
"school": "conjuration",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "damage",
|
||||
"formula": "1d3",
|
||||
"types": ["acid"],
|
||||
"half_on_save": false
|
||||
}
|
||||
],
|
||||
"description": "You fire a small orb of acid at the target, dealing 1d3 points of acid damage (ranged touch attack, no save)."
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Aid",
|
||||
"level": 1,
|
||||
"school": "enchantment",
|
||||
"range": "long",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "buff",
|
||||
"modifiers": [
|
||||
{ "target": "attack", "value": 1, "bonus_type": "morale" },
|
||||
{ "target": "will", "value": 1, "bonus_type": "morale" }
|
||||
],
|
||||
"duration_rounds": 10
|
||||
}
|
||||
],
|
||||
"description": "The subject gains a +1 morale bonus on attack rolls and on saving throws against fear effects (modeled as Will). Duration 10 min/level approximated as 10 rounds."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Air Bubble",
|
||||
"level": 1,
|
||||
"school": "conjuration",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "You create a sphere of air around your head (or a touched creature's) that lasts 10 min/level, allowing breathing underwater. No combat-mechanical effect in the engine."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Alarm",
|
||||
"level": 1,
|
||||
"school": "abjuration",
|
||||
"range": "medium",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "Wards a area (20-ft radius, 20 ft high) against intrusion for 2 hours/level, alerting you mentally. No combat-mechanical effect in the engine."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Arcane Mark",
|
||||
"level": 0,
|
||||
"school": "divination",
|
||||
"range": "touch",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "Creates a permanent visible inscribed mark of your choosing on an object or surface, optionally invisible and keyed to you. Utility cantrip, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Bane",
|
||||
"level": 1,
|
||||
"school": "enchantment",
|
||||
"range": "medium",
|
||||
"save": "will",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "shaken",
|
||||
"duration_rounds": 10,
|
||||
"save_negates": true
|
||||
}
|
||||
],
|
||||
"description": "Enemies within range take a -1 penalty on attack rolls and saving throws (approximated with the shaken condition, -2). Will negates. Concentration 1 min/level approximated as 10 rounds."
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "Bear's Endurance",
|
||||
"level": 2,
|
||||
"school": "transmutation",
|
||||
"range": "touch",
|
||||
"save": "fort",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "buff",
|
||||
"modifiers": [
|
||||
{ "target": "fort", "value": 2, "bonus_type": "enhancement" },
|
||||
{ "target": "hp", "value": 2, "bonus_type": "enhancement" }
|
||||
],
|
||||
"duration_rounds": 60
|
||||
}
|
||||
],
|
||||
"description": "The subject gains a +4 enhancement bonus to Constitution, granting +2 Fortitude and +2 hit points (approximated as +2/+2). Fortitude negates (harmless). Duration 10 min/level approximated as 60 rounds."
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Bladed Dash",
|
||||
"level": 1,
|
||||
"school": "transmutation",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "buff",
|
||||
"modifiers": [
|
||||
{ "target": "speed_ft", "value": 30, "bonus_type": "enhancement" }
|
||||
],
|
||||
"duration_rounds": 1
|
||||
}
|
||||
],
|
||||
"description": "You move up your speed as a move action and make one melee attack at the end. Simplified as a +30 ft speed buff for 1 round; the extra attack is not modeled."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Bleed",
|
||||
"level": 0,
|
||||
"school": "necromancy",
|
||||
"range": "close",
|
||||
"save": "fort",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "bleeding",
|
||||
"duration_rounds": 1,
|
||||
"save_negates": true
|
||||
}
|
||||
],
|
||||
"description": "A cantrip that causes a wound to bleed: the target takes 1 point of damage per round until healed. Fortitude negates. Modeled as the bleeding condition for 1 round."
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "Bless",
|
||||
"level": 1,
|
||||
"school": "enchantment",
|
||||
"range": "medium",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "area",
|
||||
"radius_ft": 20,
|
||||
"save_for_half": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "buff",
|
||||
"modifiers": [
|
||||
{ "target": "attack", "value": 1, "bonus_type": "morale" },
|
||||
{ "target": "will", "value": 1, "bonus_type": "morale" }
|
||||
],
|
||||
"duration_rounds": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "All allies within 20 ft/level (modeled as a 20-ft burst applied to all creatures in area) gain a +1 morale bonus on attack rolls and on saving throws against fear effects (modeled as Will). Duration 1 min/level approximated as 10 rounds."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Blindness/Deafness",
|
||||
"level": 2,
|
||||
"school": "necromancy",
|
||||
"range": "medium",
|
||||
"save": "will",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "blinded",
|
||||
"duration_rounds": 100,
|
||||
"save_negates": true
|
||||
}
|
||||
],
|
||||
"description": "The target becomes blind or deaf (modeled as blinded) until removed by a caster of higher level. Permanent duration approximated as 100 rounds. Will negates."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Charm Person",
|
||||
"level": 1,
|
||||
"school": "enchantment",
|
||||
"range": "medium",
|
||||
"save": "will",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "fascinated",
|
||||
"duration_rounds": 100,
|
||||
"save_negates": true
|
||||
}
|
||||
],
|
||||
"description": "The charmed person regards the caster as a trusted friend (modeled as the fascinated condition; the charmed condition is not in the engine). Duration 1 hour/level approximated as 100 rounds. Will negates."
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "Color Spray",
|
||||
"level": 1,
|
||||
"school": "enchantment",
|
||||
"range": "close",
|
||||
"save": "will",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "area",
|
||||
"radius_ft": 15,
|
||||
"save_for_half": false,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "stunned",
|
||||
"duration_rounds": 1,
|
||||
"save_negates": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "A cone of chaotic colors (modeled as a 15-ft burst) blinds and stuns creatures with fewer HD than your level for 1d4 rounds (approximated as 1 round of stunned). Will negates."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Comprehend Languages",
|
||||
"level": 0,
|
||||
"school": "divination",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that lets you understand the meaning of any spoken or written language you encounter for 1 min/level. Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Create Alcool",
|
||||
"level": 0,
|
||||
"school": "conjuration",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip (Create Alcohol, spelled as on the PC sheet) that transforms one liquid per level into an alcoholic drink of equal volume. Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Create Water",
|
||||
"level": 1,
|
||||
"school": "conjuration",
|
||||
"range": "medium",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "Conjures 4 gallons of clean water per level in an open space or container. Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Dancing Lights",
|
||||
"level": 0,
|
||||
"school": "conjuration",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that creates up to four flickering torch-like lights you move as desired (concentration, 1 min/level). Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Darkness",
|
||||
"level": 2,
|
||||
"school": "evocation",
|
||||
"range": "touch",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "area",
|
||||
"radius_ft": 20,
|
||||
"save_for_half": false,
|
||||
"effects": []
|
||||
}
|
||||
],
|
||||
"description": "All light in a 20-ft radius (spread from the touched point) becomes supernatural darkness; creatures inside lose visual sight-based advantages for 10 min/level. The engine does not model area concealment, so the burst carries no mechanical effect."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Daze",
|
||||
"level": 0,
|
||||
"school": "enchantment",
|
||||
"range": "close",
|
||||
"save": "will",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "dazed",
|
||||
"duration_rounds": 1,
|
||||
"save_negates": true
|
||||
}
|
||||
],
|
||||
"description": "A cantrip that clouds the mind of a humanoid creature with 5 or fewer HD, making it unable to act for 1 round. Will negates."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Deja Vu",
|
||||
"level": 0,
|
||||
"school": "divination",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that lets you reroll one attack roll, saving throw, or skill check made in the past round (once per casting). Not modeled by the engine's action flow, so it carries no mechanical effect."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Delay Disease",
|
||||
"level": 3,
|
||||
"school": "abjuration",
|
||||
"range": "touch",
|
||||
"save": "fort",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "You suppress the progression of one disease the subject carries for 1 hour/level, keeping it dormant. Utility spell, no combat-mechanical effect in the engine. Fortitude negates (harmless)."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Delay Poison",
|
||||
"level": 2,
|
||||
"school": "abjuration",
|
||||
"range": "touch",
|
||||
"save": "fort",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "You temporarily stave off incoming poison or eliminate one already in the subject's system for 1 hour/level. Utility spell, no combat-mechanical effect in the engine. Fortitude negates (harmless)."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Detect Fiendish Presence",
|
||||
"level": 0,
|
||||
"school": "divination",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that reveals whether a creature of the outsider (evil) subtype has been in the area within 10 minutes/HD, and hints at its nature. Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Detect Magic",
|
||||
"level": 0,
|
||||
"school": "divination",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that detects magical auras in a 60-ft cone (2 rounds to identify strength and school). Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Detect Poison",
|
||||
"level": 0,
|
||||
"school": "divination",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "A cantrip that determines whether a creature, object, or area is poisoned, venomous, or afflicted by acid. Utility spell, no combat effect."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Disrupt Undead",
|
||||
"level": 0,
|
||||
"school": "necromancy",
|
||||
"range": "close",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "damage",
|
||||
"formula": "1d6",
|
||||
"types": ["force"],
|
||||
"half_on_save": false
|
||||
}
|
||||
],
|
||||
"description": "A cantrip: a line of crackling energy strikes one undead creature, dealing 1d6 points of damage (ranged touch attack, no save). The engine does not restrict the damage to undead targets."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Divine Favor",
|
||||
"level": 1,
|
||||
"school": "evocation",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "You draw upon divine power for 1 round/level: a +1 luck bonus on attack rolls and weapon damage per 4 caster levels. Simplified: the engine has no luck-bonus self-buff that preserves the pinned default-policy casting order, so no mechanical effect is applied."
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Doom",
|
||||
"level": 1,
|
||||
"school": "necromancy",
|
||||
"range": "close",
|
||||
"save": "will",
|
||||
"base_dc": 10,
|
||||
"effects": [
|
||||
{
|
||||
"type": "condition",
|
||||
"condition_name": "shaken",
|
||||
"duration_rounds": 10,
|
||||
"save_negates": true
|
||||
}
|
||||
],
|
||||
"description": "The target is paralyzed with fear and becomes shaken (-2 on attacks and saves; on a successful save it takes -1 instead, not modeled). Will negates. Duration 1 min/level approximated as 10 rounds."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Drunkard's Breath",
|
||||
"level": 1,
|
||||
"school": "abjuration",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "For 1 hour/level you gain DR 5/- against alcohol effects and cannot become drunk. The engine has no DR buff target, so no mechanical effect is applied."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Endure Elements",
|
||||
"level": 1,
|
||||
"school": "abjuration",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "For 24 hours you tolerate comfortable extremes of heat and cold (from -50 to 140 degrees F) without ill effect. Environmental protection, no combat-mechanical effect in the engine."
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "Enhance Water",
|
||||
"level": 2,
|
||||
"school": "abjuration",
|
||||
"range": "personal",
|
||||
"save": "none",
|
||||
"base_dc": 10,
|
||||
"effects": [],
|
||||
"description": "You purify and bless 1 gallon/level of water (or one vial of holy/unholy water). Utility spell, no combat-mechanical effect in the engine."
|
||||
}
|
||||
@@ -24,11 +24,11 @@ MAP = ROOT / "data" / "maps" / "sample_arena.yaml"
|
||||
ONI = ROOT / "data" / "pcs" / "oni.json"
|
||||
GOBLIN = ROOT / "data" / "monsters" / "goblin.json"
|
||||
|
||||
_NUM_SPELLS = 11
|
||||
_NUM_SPELLS = 43
|
||||
|
||||
|
||||
def test_runner_loads_realtime_spell_registry() -> None:
|
||||
"""The runner's registry source holds all 11 real spells from data/spells/."""
|
||||
"""The runner's registry source holds all 43 real spells from data/spells/."""
|
||||
registry = load_spell_registry(SPELLS_DIR)
|
||||
assert len(registry) == _NUM_SPELLS
|
||||
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
"""Batch 1 (V6a): the 32 PC-sheet spells added to data/spells/.
|
||||
|
||||
Given: the registry directory with the 11 original spells plus batch 1
|
||||
When: load_spell_registry / load_spell is called
|
||||
Then: every new spell loads with the expected level/school/range/save,
|
||||
the registry grew by exactly 32, and every effect validates against the
|
||||
schema (parsing raises on any unknown field value or condition).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from pf1e_simulator.conditions import CONDITIONS_BY_NAME
|
||||
from pf1e_simulator.spells import (
|
||||
AreaEffect,
|
||||
BuffEffect,
|
||||
ConditionEffect,
|
||||
DamageEffect,
|
||||
HealEffect,
|
||||
SpellEffect,
|
||||
SpellSpec,
|
||||
SummonEffect,
|
||||
load_spell,
|
||||
load_spell_registry,
|
||||
)
|
||||
|
||||
_SPELLS_DIR = Path(__file__).resolve().parent.parent / "data" / "spells"
|
||||
|
||||
_ORIGINAL_COUNT = 11
|
||||
|
||||
# name -> (file stem, level, school, range, save)
|
||||
_BATCH1: dict[str, tuple[str, int, str, str, str]] = {
|
||||
"Abundant Ammunition": ("abundant_ammunition", 0, "transmutation", "close", "none"),
|
||||
"Acid Splash": ("acid_splash", 0, "conjuration", "close", "none"),
|
||||
"Aid": ("aid", 1, "enchantment", "long", "none"),
|
||||
"Air Bubble": ("air_bubble", 1, "conjuration", "personal", "none"),
|
||||
"Alarm": ("alarm", 1, "abjuration", "medium", "none"),
|
||||
"Arcane Mark": ("arcane_mark", 0, "divination", "touch", "none"),
|
||||
"Bane": ("bane", 1, "enchantment", "medium", "will"),
|
||||
"Bear's Endurance": ("bears_endurance", 2, "transmutation", "touch", "fort"),
|
||||
"Bladed Dash": ("bladed_dash", 1, "transmutation", "close", "none"),
|
||||
"Bleed": ("bleed", 0, "necromancy", "close", "fort"),
|
||||
"Bless": ("bless", 1, "enchantment", "medium", "none"),
|
||||
"Blindness/Deafness": ("blindness_deafness", 2, "necromancy", "medium", "will"),
|
||||
"Charm Person": ("charm_person", 1, "enchantment", "medium", "will"),
|
||||
"Color Spray": ("color_spray", 1, "enchantment", "close", "will"),
|
||||
"Comprehend Languages": ("comprehend_languages", 0, "divination", "personal", "none"),
|
||||
"Create Alcool": ("create_alcool", 0, "conjuration", "close", "none"),
|
||||
"Create Water": ("create_water", 1, "conjuration", "medium", "none"),
|
||||
"Dancing Lights": ("dancing_lights", 0, "conjuration", "close", "none"),
|
||||
"Darkness": ("darkness", 2, "evocation", "touch", "none"),
|
||||
"Daze": ("daze", 0, "enchantment", "close", "will"),
|
||||
"Deja Vu": ("deja_vu", 0, "divination", "personal", "none"),
|
||||
"Delay Disease": ("delay_disease", 3, "abjuration", "touch", "fort"),
|
||||
"Delay Poison": ("delay_poison", 2, "abjuration", "touch", "fort"),
|
||||
"Detect Fiendish Presence": ("detect_fiendish_presence", 0, "divination", "close", "none"),
|
||||
"Detect Magic": ("detect_magic", 0, "divination", "close", "none"),
|
||||
"Detect Poison": ("detect_poison", 0, "divination", "close", "none"),
|
||||
"Disrupt Undead": ("disrupt_undead", 0, "necromancy", "close", "none"),
|
||||
"Divine Favor": ("divine_favor", 1, "evocation", "personal", "none"),
|
||||
"Doom": ("doom", 1, "necromancy", "close", "will"),
|
||||
"Drunkard's Breath": ("drunkards_breath", 1, "abjuration", "personal", "none"),
|
||||
"Endure Elements": ("endure_elements", 1, "abjuration", "personal", "none"),
|
||||
"Enhance Water": ("enhance_water", 2, "abjuration", "personal", "none"),
|
||||
}
|
||||
|
||||
_EFFECT_TYPES = (
|
||||
DamageEffect,
|
||||
ConditionEffect,
|
||||
HealEffect,
|
||||
BuffEffect,
|
||||
AreaEffect,
|
||||
SummonEffect,
|
||||
)
|
||||
|
||||
|
||||
def _walk_effects(effects: tuple[SpellEffect, ...]) -> list[SpellEffect]:
|
||||
flat: list[SpellEffect] = []
|
||||
for effect in effects:
|
||||
flat.append(effect)
|
||||
if isinstance(effect, AreaEffect):
|
||||
flat.extend(_walk_effects(effect.effects))
|
||||
return flat
|
||||
|
||||
|
||||
class TestBatch1Loading:
|
||||
"""Each batch-1 spell loads with the expected metadata."""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("name", "stem", "level", "school", "range_", "save"),
|
||||
[
|
||||
(name, *fields) for name, fields in sorted(_BATCH1.items())
|
||||
],
|
||||
)
|
||||
def test_spell_fields(
|
||||
self, name: str, stem: str, level: int, school: str, range_: str, save: str
|
||||
) -> None:
|
||||
spell = load_spell(_SPELLS_DIR / f"{stem}.json")
|
||||
assert spell.name == name
|
||||
assert spell.level == level
|
||||
assert spell.school == school
|
||||
assert spell.range == range_
|
||||
assert spell.save == save
|
||||
assert spell.base_dc == 10
|
||||
assert spell.description
|
||||
|
||||
def test_registry_grew_by_exactly_32(self) -> None:
|
||||
reg = load_spell_registry(_SPELLS_DIR)
|
||||
assert len(reg) == _ORIGINAL_COUNT + 32
|
||||
assert set(_BATCH1) <= set(reg)
|
||||
|
||||
def test_every_effect_validates(self) -> None:
|
||||
reg = load_spell_registry(_SPELLS_DIR)
|
||||
for name in _BATCH1:
|
||||
spell: SpellSpec = reg[name]
|
||||
for effect in _walk_effects(spell.effects):
|
||||
assert isinstance(effect, _EFFECT_TYPES)
|
||||
if isinstance(effect, ConditionEffect):
|
||||
assert effect.condition_name in CONDITIONS_BY_NAME
|
||||
if isinstance(effect, BuffEffect):
|
||||
assert effect.modifiers
|
||||
@@ -123,13 +123,13 @@ def _make_engine(
|
||||
|
||||
|
||||
class TestSpellLoading:
|
||||
"""Given: data/spells/ directory with 11 JSON spell files
|
||||
"""Given: data/spells/ directory with 43 JSON spell files
|
||||
When: load_spell_registry is called
|
||||
Then: all 11 spells are loaded with correct fields."""
|
||||
Then: all 43 spells are loaded with correct fields."""
|
||||
|
||||
def test_loads_all_10_spells(self) -> None:
|
||||
reg = load_spell_registry(_SPELLS_DIR)
|
||||
assert len(reg) == 11
|
||||
assert len(reg) == 43
|
||||
|
||||
def test_spell_names_present(self) -> None:
|
||||
reg = load_spell_registry(_SPELLS_DIR)
|
||||
@@ -139,7 +139,8 @@ class TestSpellLoading:
|
||||
"Fear", "Bull's Strength", "Acid Arrow",
|
||||
"Summon Monster II",
|
||||
}
|
||||
assert set(reg) == expected
|
||||
assert expected <= set(reg)
|
||||
assert len(reg) == 43
|
||||
|
||||
def test_fireball_fields(self) -> None:
|
||||
spell = load_spell(_SPELLS_DIR / "fireball.json")
|
||||
|
||||
Reference in New Issue
Block a user