Files
gogobee/internal/plugin/dnd_spells_data.go
prosolis c48e12a296 UX S2: caster onboarding — strip reaction defaults, deterministic healing_word, route hints
B2: Removed shield/counterspell from Mage/Sorcerer defaults and hellish_rebuke/counterspell
    from Warlock — reactions are EffectReaction and combat has no reaction window yet, so
    auto-granting them just litters the spellbook with dead entries. Substituted L1/L3
    staples (sleep, fly, burning_hands, chromatic_orb where class-tagged appropriately).

B3: Replaced the stale "Mage, Cleric, Ranger, and Arcane Trickster (L5+)" enumeration in
    !cast/!spells learn/!prepare error paths with class-aware route hints via a new
    spellRouteHintFor() helper.

R15: Removed the duplicate hand-authored healing_word_spell entry; Cleric default now
     references the canonical SRD healing_word (cleric/druid/bard tagged). parseSpell
     and lookupSpell now resolve "healing word" deterministically.

R16: !cast --drop now appends "(slot refunded)" when applicable.

R17: Prep-cap message rewritten to "Your prepared list is full (N/N)."

R19: Removed shillelagh from Druid default — the overlay flags it cleric/ranger only and
     it has no real attack profile yet (BuffSelf, no DamageDice).

Bonus correctness: dndSpellRegistry now unions Classes on overlay collision instead of
replacing wholesale. The hand-authored buildSpellList() entries were tagged Mage-only,
so every Sorcerer/Warlock/Bard/Druid spell that collided with the SRD silently lost its
class list — meaning auto-granted defaults were rejected at the classOK gate. Union
merge restores SRD coverage without forcing a hand-edit of every overlay entry.

Tests:
- TestDefaultKnownSpellsHaveNoReactions covers B2.
- TestDefaultKnownSpellsAreCastableByClass guards the overlay-narrow regression.
- TestHealingWordResolvesDeterministically covers R15.
- TestCastNonCasterErrorHasNoClassEnumeration covers B3.
- Existing dnd_prepare_test.go updated to reference healing_word.
2026-05-14 21:30:15 -07:00

391 lines
24 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package plugin
// Phase 9 spell registry data. All 76 in-scope spells from
// gogobee_spell_system.md. Reaction spells (Shield, Counterspell,
// Absorb Elements) are included with EffectReaction so the registry is
// complete; SP2's !cast refuses them with a "Phase 11" note.
func buildSpellList() []SpellDefinition {
mage := []DnDClass{ClassMage}
cleric := []DnDClass{ClassCleric}
ranger := []DnDClass{ClassRanger}
mageCleric := []DnDClass{ClassMage, ClassCleric}
clericRanger := []DnDClass{ClassCleric, ClassRanger}
rangerCleric := []DnDClass{ClassRanger, ClassCleric}
allCasters := []DnDClass{ClassMage, ClassCleric, ClassRanger}
return []SpellDefinition{
// ── Cantrips (level 0) ────────────────────────────────────────────────
{ID: "fire_bolt", Name: "Fire Bolt", Level: 0, School: "evocation",
Classes: mage, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "1d10", DamageType: "fire",
Description: "Hurl a mote of fire at a target.",
Upcast: "2d10 at L5, 3d10 at L11"},
{ID: "toll_the_dead", Name: "Toll the Dead", Level: 0, School: "necromancy",
Classes: mageCleric, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "WIS", DamageDice: "1d8", DamageType: "necrotic",
Description: "1d8 necrotic; 1d12 if target is missing HP."},
{ID: "chill_touch", Name: "Chill Touch", Level: 0, School: "necromancy",
Classes: mageCleric, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "1d8", DamageType: "necrotic",
Description: "Spectral hand — target can't heal until next turn."},
{ID: "poison_spray", Name: "Poison Spray", Level: 0, School: "conjuration",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "CON", DamageDice: "1d12", DamageType: "poison",
Description: "Puff of noxious gas; CON save or full damage."},
{ID: "shocking_grasp", Name: "Shocking Grasp", Level: 0, School: "evocation",
Classes: mage, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "1d8", DamageType: "lightning",
Description: "Lightning leaps from your touch; target loses Reaction."},
{ID: "sacred_flame", Name: "Sacred Flame", Level: 0, School: "evocation",
Classes: cleric, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "1d8", DamageType: "radiant",
Description: "Flame-like radiance descends; ignores cover."},
{ID: "guidance", Name: "Guidance", Level: 0, School: "divination",
Classes: clericRanger, Effect: EffectUtility, CastTime: CastAction,
Concentration: true,
Description: "+1d4 to one ability check (self or ally)."},
{ID: "shillelagh", Name: "Shillelagh", Level: 0, School: "transmutation",
Classes: rangerCleric, Effect: EffectBuffSelf, CastTime: CastBonusAction,
Concentration: true,
Description: "Club/staff uses WIS for attack+damage; 1d8."},
{ID: "thorn_whip", Name: "Thorn Whip", Level: 0, School: "transmutation",
Classes: ranger, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "1d6", DamageType: "piercing",
Description: "Vine of thorns; pull target 10 ft."},
{ID: "mending", Name: "Mending", Level: 0, School: "transmutation",
Classes: allCasters, Effect: EffectUtility, CastTime: CastAction,
Description: "Repair one non-magical item. Out of combat only."},
{ID: "message", Name: "Message", Level: 0, School: "transmutation",
Classes: mage, Effect: EffectUtility, CastTime: CastAction,
Description: "Whisper to target up to 120 ft."},
{ID: "minor_illusion", Name: "Minor Illusion", Level: 0, School: "illusion",
Classes: mage, Effect: EffectUtility, CastTime: CastAction,
Description: "Create a sound or image."},
// ── 1st level — Mage ──────────────────────────────────────────────────
{ID: "magic_missile", Name: "Magic Missile", Level: 1, School: "evocation",
Classes: mage, Effect: EffectDamageAuto, CastTime: CastAction,
DamageDice: "3d4+3", DamageType: "force",
Description: "Three darts × 1d4+1 force; auto-hit.",
Upcast: "+1 dart per slot above 1st"},
{ID: "thunderwave", Name: "Thunderwave", Level: 1, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "CON", DamageDice: "2d8", DamageType: "thunder", AOE: true,
Description: "Wave of force in 15 ft cube; push 10 ft.",
Upcast: "+1d8 per slot above 1st"},
{ID: "mage_armor", Name: "Mage Armor", Level: 1, School: "abjuration",
Classes: mage, Effect: EffectBuffSelf, CastTime: CastAction,
Description: "AC = 13 + DEX mod (no armor required) for 8 hours."},
{ID: "burning_hands", Name: "Burning Hands", Level: 1, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "3d6", DamageType: "fire", AOE: true,
Description: "Cone of fire; DEX save half.",
Upcast: "+1d6 per slot above 1st"},
{ID: "detect_magic", Name: "Detect Magic", Level: 1, School: "divination",
Classes: mage, Effect: EffectUtility, CastTime: CastRitual,
Concentration: true,
Description: "Sense magic within 30 ft. Ritual castable (no slot)."},
{ID: "fog_cloud", Name: "Fog Cloud", Level: 1, School: "conjuration",
Classes: []DnDClass{ClassMage, ClassRanger}, Effect: EffectBuffSelf, CastTime: CastAction,
Concentration: true,
Description: "20 ft heavily obscured radius. Attacks inside have disadvantage."},
{ID: "grease", Name: "Grease", Level: 1, School: "conjuration",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
SaveStat: "DEX", AOE: true,
Description: "10 ft square; DEX save or Prone. Difficult terrain."},
{ID: "shield", Name: "Shield", Level: 1, School: "abjuration",
Classes: mage, Effect: EffectReaction, CastTime: CastReaction,
Description: "+5 AC as Reaction until next turn. (Phase 11)"},
{ID: "sleep", Name: "Sleep", Level: 1, School: "enchantment",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
AOE: true,
Description: "Incapacitate creatures totaling 5d8 HP (weakest first).",
Upcast: "+2d8 per slot above 1st"},
{ID: "chromatic_orb", Name: "Chromatic Orb", Level: 1, School: "evocation",
Classes: mage, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "3d8", DamageType: "varies",
Description: "3d8 of chosen damage type.",
Upcast: "+1d8 per slot above 1st"},
// ── 1st level — Cleric ────────────────────────────────────────────────
{ID: "cure_wounds", Name: "Cure Wounds", Level: 1, School: "evocation",
Classes: clericRanger, Effect: EffectSpellHeal, CastTime: CastAction,
DamageDice: "1d8",
Description: "Heal 1d8 + WIS mod HP. Touch.",
Upcast: "+1d8 per slot above 1st"},
{ID: "bless", Name: "Bless", Level: 1, School: "enchantment",
Classes: cleric, Effect: EffectBuffAlly, CastTime: CastAction,
Concentration: true,
Description: "Up to 3 targets: +1d4 to attack rolls and saves. 1 min."},
{ID: "inflict_wounds", Name: "Inflict Wounds", Level: 1, School: "necromancy",
Classes: cleric, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "3d10", DamageType: "necrotic",
Description: "3d10 necrotic. Melee spell attack.",
Upcast: "+1d10 per slot above 1st"},
{ID: "guiding_bolt", Name: "Guiding Bolt", Level: 1, School: "evocation",
Classes: cleric, Effect: EffectDamageAttack, CastTime: CastAction,
AttackRoll: true, DamageDice: "4d6", DamageType: "radiant",
Description: "Next attack on target has advantage.",
Upcast: "+1d6 per slot above 1st"},
{ID: "shield_of_faith", Name: "Shield of Faith", Level: 1, School: "abjuration",
Classes: cleric, Effect: EffectBuffAlly, CastTime: CastBonusAction,
Concentration: true,
Description: "+2 AC to one target. 10 min."},
// `healing_word_spell` was a hand-authored alias for `healing_word`
// that collided on display name with the SRD entry. Removed to make
// parseSpell("healing word") deterministic; Cleric default now points
// at the canonical SRD `healing_word` (cleric/druid/bard tagged).
{ID: "command", Name: "Command", Level: 1, School: "enchantment",
Classes: cleric, Effect: EffectControl, CastTime: CastAction,
SaveStat: "WIS",
Description: "One-word command (Flee/Grovel/Halt). 1 turn.",
Upcast: "+1 target per slot above 1st"},
{ID: "protection_from_evil", Name: "Protection from Evil and Good", Level: 1, School: "abjuration",
Classes: cleric, Effect: EffectBuffAlly, CastTime: CastAction,
Concentration: true,
Description: "Aberrations/fiends/undead have disadvantage vs. target."},
// ── 1st level — Ranger ────────────────────────────────────────────────
{ID: "hunters_mark", Name: "Hunter's Mark", Level: 1, School: "divination",
Classes: ranger, Effect: EffectBuffSelf, CastTime: CastBonusAction,
Concentration: true,
Description: "+1d6 damage to marked target; track as bonus action. 1 hr."},
{ID: "ensnaring_strike", Name: "Ensnaring Strike", Level: 1, School: "conjuration",
Classes: ranger, Effect: EffectControl, CastTime: CastBonusAction,
Concentration: true, SaveStat: "STR",
Description: "On hit: STR save or Restrained; 1d6/turn."},
{ID: "speak_with_animals", Name: "Speak with Animals", Level: 1, School: "divination",
Classes: ranger, Effect: EffectUtility, CastTime: CastRitual,
Description: "Communicate with beasts. 10 min."},
{ID: "absorb_elements", Name: "Absorb Elements", Level: 1, School: "abjuration",
Classes: ranger, Effect: EffectReaction, CastTime: CastReaction,
Description: "Reaction: resistance to incoming elemental damage. (Phase 11)"},
// ── 2nd level — Mage ──────────────────────────────────────────────────
{ID: "scorching_ray", Name: "Scorching Ray", Level: 2, School: "evocation",
Classes: mage, Effect: EffectDamageAuto, CastTime: CastAction,
DamageDice: "6d6", DamageType: "fire",
Description: "3 rays × 2d6 fire; each is a separate auto-hit.",
Upcast: "+1 ray per slot above 2nd"},
{ID: "mirror_image", Name: "Mirror Image", Level: 2, School: "illusion",
Classes: mage, Effect: EffectBuffSelf, CastTime: CastAction,
Description: "3 duplicates absorb hits."},
{ID: "misty_step", Name: "Misty Step", Level: 2, School: "conjuration",
Classes: mage, Effect: EffectBuffSelf, CastTime: CastBonusAction,
Description: "Bonus-action teleport up to 30 ft."},
{ID: "hold_person", Name: "Hold Person", Level: 2, School: "enchantment",
Classes: mageCleric, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "WIS",
Description: "Paralyzed; auto-crit melee while held.",
Upcast: "+1 target per slot above 2nd"},
{ID: "shatter", Name: "Shatter", Level: 2, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "CON", DamageDice: "3d8", DamageType: "thunder", AOE: true,
Description: "3d8 thunder in 10 ft sphere.",
Upcast: "+1d8 per slot above 2nd"},
{ID: "blur", Name: "Blur", Level: 2, School: "illusion",
Classes: mage, Effect: EffectBuffSelf, CastTime: CastAction,
Concentration: true,
Description: "All attacks vs. you have disadvantage."},
{ID: "web", Name: "Web", Level: 2, School: "conjuration",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "STR", AOE: true,
Description: "20 ft cube; Restrained (STR DC to escape)."},
{ID: "levitate", Name: "Levitate", Level: 2, School: "transmutation",
Classes: mage, Effect: EffectUtility, CastTime: CastAction,
Concentration: true, SaveStat: "CON",
Description: "Float up to 20 ft; immune to ground-based effects."},
{ID: "knock", Name: "Knock", Level: 2, School: "transmutation",
Classes: mage, Effect: EffectUtility, CastTime: CastAction,
Description: "Open any mundane lock or magically sealed door."},
// ── 2nd level — Cleric ────────────────────────────────────────────────
{ID: "spiritual_weapon", Name: "Spiritual Weapon", Level: 2, School: "evocation",
Classes: cleric, Effect: EffectBuffSelf, CastTime: CastBonusAction,
DamageDice: "1d8",
Description: "Bonus-action spectral weapon attacks 1d8+WIS each turn. 1 min.",
Upcast: "+1d8 per 2 slots above 2nd"},
{ID: "lesser_restoration", Name: "Lesser Restoration", Level: 2, School: "abjuration",
Classes: clericRanger, Effect: EffectUtility, CastTime: CastAction,
Description: "Remove one condition (Blinded/Deafened/Paralyzed/Poisoned)."},
{ID: "prayer_of_healing", Name: "Prayer of Healing", Level: 2, School: "evocation",
Classes: cleric, Effect: EffectSpellHeal, CastTime: CastAction,
DamageDice: "2d8",
Description: "Up to 6 targets: 2d8 + WIS mod HP. Out of combat only."},
{ID: "silence", Name: "Silence", Level: 2, School: "illusion",
Classes: clericRanger, Effect: EffectUtility, CastTime: CastAction,
Concentration: true,
Description: "No sound in 20 ft sphere; no verbal spells."},
{ID: "aid", Name: "Aid", Level: 2, School: "abjuration",
Classes: cleric, Effect: EffectBuffAlly, CastTime: CastAction,
Description: "3 targets: +5 max HP and current HP. 8 hr.",
Upcast: "+5 HP per slot above 2nd"},
{ID: "augury", Name: "Augury", Level: 2, School: "divination",
Classes: cleric, Effect: EffectUtility, CastTime: CastRitual,
Description: "Omen about action in next 30 min. TwinBee delivers."},
// ── 2nd level — Ranger ────────────────────────────────────────────────
{ID: "pass_without_trace", Name: "Pass Without Trace", Level: 2, School: "abjuration",
Classes: ranger, Effect: EffectBuffSelf, CastTime: CastAction,
Concentration: true,
Description: "+10 to Stealth; can't be tracked magically."},
{ID: "spike_growth", Name: "Spike Growth", Level: 2, School: "transmutation",
Classes: ranger, Effect: EffectDamageAuto, CastTime: CastAction,
Concentration: true, DamageDice: "2d4", DamageType: "piercing", AOE: true,
Description: "20 ft radius; 2d4 per 5 ft moved through. Difficult terrain."},
{ID: "beast_sense", Name: "Beast Sense", Level: 2, School: "divination",
Classes: ranger, Effect: EffectUtility, CastTime: CastRitual,
Concentration: true,
Description: "See/hear through a beast's senses."},
{ID: "cordon_of_arrows", Name: "Cordon of Arrows", Level: 2, School: "transmutation",
Classes: ranger, Effect: EffectBuffSelf, CastTime: CastAction,
Description: "4 arrows guard area; fire on intruders (1d6+DEX). 8 hr."},
{ID: "find_traps", Name: "Find Traps", Level: 2, School: "divination",
Classes: ranger, Effect: EffectUtility, CastTime: CastAction,
Description: "Detect presence of traps within 120 ft."},
// ── 3rd level — Mage ──────────────────────────────────────────────────
{ID: "fireball", Name: "Fireball", Level: 3, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "8d6", DamageType: "fire", AOE: true,
Description: "8d6 fire in 20 ft radius. DEX save half.",
Upcast: "+1d6 per slot above 3rd"},
{ID: "lightning_bolt", Name: "Lightning Bolt", Level: 3, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "8d6", DamageType: "lightning", AOE: true,
Description: "8d6 lightning in 100 ft line. DEX save half.",
Upcast: "+1d6 per slot above 3rd"},
{ID: "counterspell", Name: "Counterspell", Level: 3, School: "abjuration",
Classes: mage, Effect: EffectReaction, CastTime: CastReaction,
Description: "Reaction: cancel a spell of 3rd level or lower. (Phase 11)"},
{ID: "fly", Name: "Fly", Level: 3, School: "transmutation",
Classes: mage, Effect: EffectBuffAlly, CastTime: CastAction,
Concentration: true,
Description: "Target gains 60 ft fly speed. 10 min."},
{ID: "hypnotic_pattern", Name: "Hypnotic Pattern", Level: 3, School: "illusion",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "WIS", AOE: true,
Description: "Creatures in 30 ft cube: Incapacitated, speed 0."},
{ID: "dispel_magic", Name: "Dispel Magic", Level: 3, School: "abjuration",
Classes: mageCleric, Effect: EffectUtility, CastTime: CastAction,
Description: "End one magical effect on target."},
{ID: "slow", Name: "Slow", Level: 3, School: "transmutation",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "WIS", AOE: true,
Description: "Up to 6 targets: halve speed, -2 AC, no reactions."},
{ID: "animate_dead", Name: "Animate Dead", Level: 3, School: "necromancy",
Classes: mageCleric, Effect: EffectUtility, CastTime: CastAction,
Description: "Animate up to 3 corpses as skeletons/zombies. 24 hr."},
// ── 3rd level — Cleric ────────────────────────────────────────────────
{ID: "spirit_guardians", Name: "Spirit Guardians", Level: 3, School: "conjuration",
Classes: cleric, Effect: EffectDamageSave, CastTime: CastAction,
Concentration: true, SaveStat: "WIS", DamageDice: "3d8",
DamageType: "radiant", AOE: true,
Description: "15 ft aura: 3d8/turn to enemies. WIS save half.",
Upcast: "+1d8 per slot above 3rd"},
{ID: "revivify", Name: "Revivify", Level: 3, School: "necromancy",
Classes: cleric, Effect: EffectUtility, CastTime: CastAction,
MaterialCost: 300,
Description: "Restore a creature dead <1 min to 1 HP. 300-coin diamond."},
{ID: "mass_healing_word", Name: "Mass Healing Word", Level: 3, School: "evocation",
Classes: cleric, Effect: EffectSpellHeal, CastTime: CastBonusAction,
DamageDice: "1d4",
Description: "Up to 6 targets: 1d4 + WIS mod HP."},
{ID: "beacon_of_hope", Name: "Beacon of Hope", Level: 3, School: "abjuration",
Classes: cleric, Effect: EffectBuffAlly, CastTime: CastAction,
Concentration: true,
Description: "Targets have advantage on WIS saves and death saves; max healing."},
{ID: "remove_curse", Name: "Remove Curse", Level: 3, School: "abjuration",
Classes: cleric, Effect: EffectUtility, CastTime: CastAction,
Description: "End one curse on target or object."},
// ── 3rd level — Ranger ────────────────────────────────────────────────
{ID: "lightning_arrow", Name: "Lightning Arrow", Level: 3, School: "transmutation",
Classes: ranger, Effect: EffectDamageSave, CastTime: CastBonusAction,
SaveStat: "DEX", DamageDice: "4d8", DamageType: "lightning", AOE: true,
Description: "Ranged: 4d8 lightning on hit; 2d8 to creatures within 10 ft."},
{ID: "conjure_barrage", Name: "Conjure Barrage", Level: 3, School: "conjuration",
Classes: ranger, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "3d8", DamageType: "varies", AOE: true,
Description: "3d8 of weapon type in 60 ft cone. DEX save half."},
{ID: "water_walk", Name: "Water Walk", Level: 3, School: "transmutation",
Classes: ranger, Effect: EffectUtility, CastTime: CastRitual,
Description: "Up to 10 targets walk on liquid surfaces. 1 hr."},
{ID: "plant_growth", Name: "Plant Growth", Level: 3, School: "transmutation",
Classes: ranger, Effect: EffectUtility, CastTime: CastAction,
Description: "100 ft difficult terrain; or enhance crops."},
{ID: "nondetection", Name: "Nondetection", Level: 3, School: "abjuration",
Classes: ranger, Effect: EffectUtility, CastTime: CastAction,
Description: "Target undetectable by divination magic. 8 hr."},
// ── 4th level (Mage / Cleric) ─────────────────────────────────────────
{ID: "banishment", Name: "Banishment", Level: 4, School: "abjuration",
Classes: cleric, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "CHA",
Description: "Send extraplanar creature to home plane temporarily."},
{ID: "polymorph", Name: "Polymorph", Level: 4, School: "transmutation",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "WIS",
Description: "Transform target into beast. Max CR = target's level."},
{ID: "ice_storm", Name: "Ice Storm", Level: 4, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "6d6", DamageType: "cold", AOE: true,
Description: "2d8 bludgeoning + 4d6 cold in 20 ft cylinder."},
{ID: "wall_of_fire", Name: "Wall of Fire", Level: 4, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
Concentration: true, SaveStat: "DEX", DamageDice: "5d8",
DamageType: "fire", AOE: true,
Description: "60 ft wall; 5d8 fire to those passing through."},
{ID: "greater_invisibility", Name: "Greater Invisibility", Level: 4, School: "illusion",
Classes: mage, Effect: EffectBuffSelf, CastTime: CastAction,
Concentration: true,
Description: "Target invisible; attacks have advantage; can't be targeted. 1 min."},
{ID: "guardian_of_faith", Name: "Guardian of Faith", Level: 4, School: "conjuration",
Classes: cleric, Effect: EffectBuffSelf, CastTime: CastAction,
Description: "Spectral guardian deals 20 radiant to approaching enemies."},
{ID: "death_ward", Name: "Death Ward", Level: 4, School: "abjuration",
Classes: cleric, Effect: EffectBuffAlly, CastTime: CastAction,
Description: "Next time target would drop to 0 HP: drops to 1 instead. 8 hr."},
{ID: "freedom_of_movement", Name: "Freedom of Movement", Level: 4, School: "abjuration",
Classes: clericRanger, Effect: EffectBuffAlly, CastTime: CastAction,
Description: "Target ignores Difficult terrain, paralysis, restraint. 1 hr."},
// ── 5th level (Mage / Cleric) ─────────────────────────────────────────
{ID: "cone_of_cold", Name: "Cone of Cold", Level: 5, School: "evocation",
Classes: mage, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "CON", DamageDice: "8d8", DamageType: "cold", AOE: true,
Description: "8d8 cold in 60 ft cone. CON save half.",
Upcast: "+1d8 per slot above 5th"},
{ID: "hold_monster", Name: "Hold Monster", Level: 5, School: "enchantment",
Classes: mage, Effect: EffectControl, CastTime: CastAction,
Concentration: true, SaveStat: "WIS",
Description: "Paralyze any creature type."},
{ID: "wall_of_force", Name: "Wall of Force", Level: 5, School: "evocation",
Classes: mage, Effect: EffectBuffSelf, CastTime: CastAction,
Concentration: true,
Description: "Invisible impenetrable wall."},
{ID: "scrying", Name: "Scrying", Level: 5, School: "divination",
Classes: mageCleric, Effect: EffectUtility, CastTime: CastAction,
Concentration: true, SaveStat: "WIS",
Description: "Observe target at any distance."},
{ID: "mass_cure_wounds", Name: "Mass Cure Wounds", Level: 5, School: "evocation",
Classes: cleric, Effect: EffectSpellHeal, CastTime: CastAction,
DamageDice: "3d8",
Description: "Up to 6 targets: 3d8 + WIS mod HP."},
{ID: "flame_strike", Name: "Flame Strike", Level: 5, School: "evocation",
Classes: cleric, Effect: EffectDamageSave, CastTime: CastAction,
SaveStat: "DEX", DamageDice: "8d6", DamageType: "fire", AOE: true,
Description: "4d6 fire + 4d6 radiant in 10 ft cylinder."},
{ID: "divine_word", Name: "Divine Word", Level: 5, School: "evocation",
Classes: cleric, Effect: EffectControl, CastTime: CastBonusAction,
SaveStat: "WIS",
Description: "Targets suffer effects based on HP. Kills below 20 HP."},
{ID: "raise_dead", Name: "Raise Dead", Level: 5, School: "necromancy",
Classes: cleric, Effect: EffectUtility, CastTime: CastAction,
MaterialCost: 500,
Description: "Restore a creature dead up to 10 days. 500-coin diamond."},
}
}