mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 00:32:40 +00:00
Phase A1: jargon-free flavor on all reachable spell Descriptions
The audit plan flagged jargon leaks in the SRD spell prose surfaced by
!cast and !spellbook. Verifying against HEAD turned up two corrections
to the plan's framing:
- The overlay in dnd_spells_data.go wins on ID collision, so for the
~76 hand-authored spells the *overlay* Descriptions are what players
actually see — and those were crunchier than the SRD ("Heal 1d8 +
WIS mod HP. Touch.", "8d6 fire in 20 ft radius. DEX save half.",
etc.). Touching only the generated SRD file would have missed the
bigger leak.
- The Upcast field is never displayed at runtime (only the import
generator reads it), so testing it against the regex would test
dead data.
So this commit:
- rewrites every overlay Description into playful, jargon-free flavor
in the charm_person voice ("Sweet-talk a humanoid…") — no dice, no
saves, no AC math, no ability-mod references
- adds 31 overlay shims for SRD-only spells whose generated text was
broken (placeholder gaps from the Open5e classifier — "no larger
than a.", "up to to", "in a in range"), empty ("You touch a
creature."), typo-laden ("magicou", "undeadou", "diseasesou"), or
jargon-heavy (false_life's "1d4+4 temporary hit points")
- adds TestSpellDescriptionsAreJargonFree, a regex sweep over the
merged registry that bans "saving throw", "spell slot of",
"ability modifier", "hit points equal to", and \bNdN\b dice
notation. Currently passing; will fail-loud on regressions or new
SRD imports that need overlay shims.
The generated dnd_spells_srd_data.go is left untouched — overlay is
the regen-safe surface per its own header comment.
This commit is contained in:
@@ -1,9 +1,19 @@
|
||||
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.
|
||||
// Hand-authored spell overlay. Loaded after the Open5e SRD dump in
|
||||
// buildSRDSpellList() and merged on top — overlay entries replace the SRD
|
||||
// struct entirely (Classes is unioned). The original ~76 entries are tuned
|
||||
// for Effect/DamageDice/Upcast; the 2026-05 pass rewrote every Description
|
||||
// into playful, jargon-free flavor (no dice, no saves, no ability-mod math)
|
||||
// and added overlay shims for the SRD-only spells whose generated text was
|
||||
// broken, empty, or jargon-laden.
|
||||
//
|
||||
// Voice template: charm_person — "Sweet-talk a humanoid into treating you
|
||||
// as their new favourite acquaintance. Wears off; they may notice."
|
||||
//
|
||||
// Mechanics still live in Effect/DamageDice/SaveStat/Concentration fields;
|
||||
// the engine reads those, not the prose. The prose is what the player sees
|
||||
// in !cast confirmations and the !spellbook list.
|
||||
|
||||
func buildSpellList() []SpellDefinition {
|
||||
mage := []DnDClass{ClassMage}
|
||||
@@ -19,119 +29,119 @@ func buildSpellList() []SpellDefinition {
|
||||
{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.",
|
||||
Description: "Flick a mote of fire at a foe. Small, hot, surprisingly rude.",
|
||||
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."},
|
||||
Description: "A funeral bell rings only the target can hear — and the wounded hear it louder."},
|
||||
{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."},
|
||||
Description: "A spectral hand clutches a foe — bandages refuse to stick for a moment."},
|
||||
{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."},
|
||||
Description: "A green puff from your palm. Hold your breath; they probably won't."},
|
||||
{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."},
|
||||
Description: "Lightning leaps from your touch. They flinch hard; their reaction is gone."},
|
||||
{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."},
|
||||
Description: "A column of holy light drops on a foe. Cover does not help."},
|
||||
{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)."},
|
||||
Description: "A faint helpful hum settles on an ally. Their next tricky task feels a touch less tricky."},
|
||||
{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."},
|
||||
Description: "Your club or staff wakes up grumpy. Hits harder and listens to your wisdom now."},
|
||||
{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."},
|
||||
Description: "A vine of thorns lashes out, snags, and yanks a foe closer. Undignified."},
|
||||
{ID: "mending", Name: "Mending", Level: 0, School: "transmutation",
|
||||
Classes: allCasters, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Repair one non-magical item. Out of combat only."},
|
||||
Description: "Knit a small break back together — a snapped key, a torn cloak, a sulking wineskin. 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."},
|
||||
Description: "Whisper to someone across the room. They can whisper back. Nobody else hears a thing."},
|
||||
{ID: "minor_illusion", Name: "Minor Illusion", Level: 0, School: "illusion",
|
||||
Classes: mage, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Create a sound or image."},
|
||||
Description: "Conjure a quick sound or harmless image. Looks real until someone tries to touch it."},
|
||||
|
||||
// ── 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.",
|
||||
Description: "Three darts of pure magic streak from your fingers. They never miss; they never tire of it.",
|
||||
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"},
|
||||
Description: "A short, loud BOOM rolls out from you. Nearby foes get the message and stumble back.",
|
||||
Upcast: "louder 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."},
|
||||
Description: "Wrap yourself in an invisible glove of force. Hits glance off; movement stays free."},
|
||||
{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"},
|
||||
Description: "Splay your fingers; a short cone of flame washes over the front rank.",
|
||||
Upcast: "hotter 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)."},
|
||||
Description: "Your senses sharpen — anything enchanted nearby softly glows in your mind's eye. Ritual; no cost."},
|
||||
{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."},
|
||||
Description: "A bank of fog rolls in. Aim becomes a guessing game; everyone misses more."},
|
||||
{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."},
|
||||
Description: "A patch of greasy floor blooms underfoot. Walking is a comedy; running, a tragedy."},
|
||||
{ID: "shield", Name: "Shield", Level: 1, School: "abjuration",
|
||||
Classes: mage, Effect: EffectReaction, CastTime: CastReaction,
|
||||
Description: "+5 AC as Reaction until next turn. (Phase 11)"},
|
||||
Description: "Throw up a slab of force in an eyeblink — the blow you were about to take skids off harmlessly."},
|
||||
{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"},
|
||||
Description: "A drowsy fog rolls over nearby foes. The weakest drop where they stand and snore.",
|
||||
Upcast: "more snoring 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.",
|
||||
Description: "A floating orb in the colour of your chosen element. Pick a flavour of pain and hurl.",
|
||||
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"},
|
||||
Description: "Lay a glowing hand on an ally; a stack of fresh wounds quietly close up. Bruises survive.",
|
||||
Upcast: "stronger 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."},
|
||||
Description: "Drop a quiet blessing on up to three allies. They swing a little truer; their nerve holds."},
|
||||
{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"},
|
||||
Description: "Press a withered palm to a foe and a deep, ugly wound opens under your touch.",
|
||||
Upcast: "uglier 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"},
|
||||
Description: "A blazing javelin of light streaks home — and leaves the target glowing, easy to hit next.",
|
||||
Upcast: "brighter 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."},
|
||||
Description: "A shimmering halo settles around an ally. Blades skid off it. Looks holy; works."},
|
||||
// `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
|
||||
@@ -139,252 +149,373 @@ func buildSpellList() []SpellDefinition {
|
||||
{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.",
|
||||
Description: "Snap a single word at a foe — Halt! Drop! Flee! — and watch them obey before they've thought about it.",
|
||||
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."},
|
||||
Description: "Ward an ally against things that aren't supposed to be here — fiends, fey, the undead. They flinch from the touch."},
|
||||
|
||||
// ── 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."},
|
||||
Description: "Tag a quarry. You'll feel where they are, and your shots will know where to land."},
|
||||
{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."},
|
||||
Description: "Your next hit sprouts thorny vines that wrap a foe in place. They writhe; the thorns bite."},
|
||||
{ID: "speak_with_animals", Name: "Speak with Animals", Level: 1, School: "divination",
|
||||
Classes: ranger, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Description: "Communicate with beasts. 10 min."},
|
||||
Description: "For a while, animals make sense and you make sense to them. Most of what they say is about food."},
|
||||
{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)"},
|
||||
Description: "Catch the worst of an incoming blast of fire, frost, or lightning on your sleeve and shrug it off."},
|
||||
|
||||
// ── 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.",
|
||||
Description: "Three darting jets of fire spit from your hand. Each one finds skin.",
|
||||
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."},
|
||||
Description: "Three flickering duplicates of you appear. Foes pick the wrong one; you grin from the right one."},
|
||||
{ID: "misty_step", Name: "Misty Step", Level: 2, School: "conjuration",
|
||||
Classes: mage, Effect: EffectBuffSelf, CastTime: CastBonusAction,
|
||||
Description: "Bonus-action teleport up to 30 ft."},
|
||||
Description: "Vanish in a curl of silver mist and reappear a short hop away. Very dramatic; very useful."},
|
||||
{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.",
|
||||
Description: "A foe locks rigid mid-step. Anything you hit them with next lands devastatingly well.",
|
||||
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"},
|
||||
Description: "An invisible chord rings, then snaps — everything brittle nearby cracks at once.",
|
||||
Upcast: "louder 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."},
|
||||
Description: "Your outline smears. Foes swing at where you almost are; they almost hit."},
|
||||
{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)."},
|
||||
Description: "Sticky cobwebs bloom across the floor. Foes go in fast and come out slowly, swearing."},
|
||||
{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."},
|
||||
Description: "Lift a target gently off the floor. They flail like a kicked cat; the ground rolls past."},
|
||||
{ID: "knock", Name: "Knock", Level: 2, School: "transmutation",
|
||||
Classes: mage, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Open any mundane lock or magically sealed door."},
|
||||
Description: "Any mundane lock — and a few proud magical ones — clicks open at a sharp word. Loudly. Sorry."},
|
||||
|
||||
// ── 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.",
|
||||
Description: "A glowing spectral mace floats up beside you and swings at foes whenever you fancy.",
|
||||
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)."},
|
||||
Description: "Lift a single nasty condition from an ally — blinded, poisoned, paralysed, deaf. They breathe out."},
|
||||
{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."},
|
||||
Description: "Murmur a prayer over a clustered party. Bruises fade; spirits lift. 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."},
|
||||
Description: "A bubble of dead quiet drops over an area. No shouting, no chanting, no spells with words."},
|
||||
{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"},
|
||||
Description: "Three allies stand a little taller, feel a little tougher, last a little longer. All day.",
|
||||
Upcast: "more vigour 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."},
|
||||
Description: "Ask the next half-hour a yes-or-no question. TwinBee relays the omen, mostly straight."},
|
||||
|
||||
// ── 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."},
|
||||
Description: "A veil of hush settles over the party. Footprints fade; you slip past sentries like a rumour."},
|
||||
{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."},
|
||||
Description: "The ground sprouts thorny barbs across a wide patch. Anyone crossing leaves a blood trail."},
|
||||
{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."},
|
||||
Description: "Borrow a beast's eyes and ears for a while. You smell things you'd rather not have smelled."},
|
||||
{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."},
|
||||
Description: "Plant four arrows in the dirt; they wait. Anyone uninvited steps near and the arrows leap."},
|
||||
{ID: "find_traps", Name: "Find Traps", Level: 2, School: "divination",
|
||||
Classes: ranger, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Detect presence of traps within 120 ft."},
|
||||
Description: "The traps in your sight start to look like traps. They've always been there; now you see them."},
|
||||
|
||||
// ── 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"},
|
||||
Description: "A bead of flame streaks out and blossoms into a roaring bonfire. Foes inside have a brief, terrible moment.",
|
||||
Upcast: "hotter 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"},
|
||||
Description: "A line of furious lightning blasts straight through everything stupid enough to be standing in it.",
|
||||
Upcast: "louder 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)"},
|
||||
Description: "Catch another caster mid-incantation and pluck the spell out of the air. Polite smile optional."},
|
||||
{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."},
|
||||
Description: "An ally lifts off and zips around like an angry hummingbird. Try not to lose concentration up there."},
|
||||
{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."},
|
||||
Description: "A swirling kaleidoscope hangs in the air. Foes who look stop fighting and stare, mouths agape."},
|
||||
{ID: "dispel_magic", Name: "Dispel Magic", Level: 3, School: "abjuration",
|
||||
Classes: mageCleric, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "End one magical effect on target."},
|
||||
Description: "Snip the threads of one active spell on a target. The magic frays and falls away."},
|
||||
{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."},
|
||||
Description: "Time thickens around your foes. They wade. They paddle. They are very late to the fight."},
|
||||
{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."},
|
||||
Description: "Three nearby corpses sit up and look at you for orders. Frowned upon at parties; useful in dungeons."},
|
||||
|
||||
// ── 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"},
|
||||
Description: "Ghostly guardians swarm around you, slashing at anything hostile that comes close.",
|
||||
Upcast: "more guardians 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."},
|
||||
Description: "Coax a recently-dead ally back across the threshold. Burns up a small, expensive diamond doing it."},
|
||||
{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."},
|
||||
Description: "Bark a healing word across the battlefield. Up to six allies find themselves slightly less dead."},
|
||||
{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."},
|
||||
Description: "A warm glow settles over your party. Fear loosens; the dying cling on; healing lands at its kindest."},
|
||||
{ID: "remove_curse", Name: "Remove Curse", Level: 3, School: "abjuration",
|
||||
Classes: cleric, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "End one curse on target or object."},
|
||||
Description: "Lift a curse from a creature or object. The bad thing it was making them do quietly stops."},
|
||||
|
||||
// ── 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."},
|
||||
Description: "Your next arrow turns into a forking bolt. It strikes; nearby foes flinch from the splash."},
|
||||
{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."},
|
||||
Description: "Hurl one weapon; a phantom volley follows, raining down the same kind of pain across a cone."},
|
||||
{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."},
|
||||
Description: "Up to ten allies stride across water, mud, snow, or worse as if it were a polite pavement."},
|
||||
{ID: "plant_growth", Name: "Plant Growth", Level: 3, School: "transmutation",
|
||||
Classes: ranger, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "100 ft difficult terrain; or enhance crops."},
|
||||
Description: "Brambles and undergrowth thrash to life around you. The forest objects to anyone hurrying through."},
|
||||
{ID: "nondetection", Name: "Nondetection", Level: 3, School: "abjuration",
|
||||
Classes: ranger, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Target undetectable by divination magic. 8 hr."},
|
||||
Description: "A target falls off every scrying mirror in the realm. Whoever's watching frowns at static."},
|
||||
|
||||
// ── 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."},
|
||||
Description: "Yank a creature back where they belong. Demons hate this; the rest just look confused and gone."},
|
||||
{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."},
|
||||
Description: "Turn a foe into a small, harmless beast. They still know who they are. They are furious about it."},
|
||||
{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."},
|
||||
Description: "Hail the size of bricks crashes down across a wide area. The footing afterwards is a problem too."},
|
||||
{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."},
|
||||
Description: "A long curtain of flame roars up from the ground. Anyone trying to cross regrets it loudly."},
|
||||
{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."},
|
||||
Description: "An ally goes properly invisible — they can swing, cast, and shout, and stay unseen the whole time."},
|
||||
{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."},
|
||||
Description: "A towering spectral knight appears and stands its ground. Anything hostile approaching gets a smiting."},
|
||||
{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."},
|
||||
Description: "Lay a single saved-life on an ally. The next mortal blow ends in a stagger, not a funeral."},
|
||||
{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."},
|
||||
Description: "An ally shrugs off mud, vines, manacles, and most of the laws of friction for an hour."},
|
||||
|
||||
// ── 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"},
|
||||
Description: "A blast of arctic air rolls out from your palms. Frost on eyelashes; foes very, very cold.",
|
||||
Upcast: "colder 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."},
|
||||
Description: "Lock any creature — beast, fiend, dragon — rigid in place. Their eyes still glare; that's all."},
|
||||
{ID: "wall_of_force", Name: "Wall of Force", Level: 5, School: "evocation",
|
||||
Classes: mage, Effect: EffectBuffSelf, CastTime: CastAction,
|
||||
Concentration: true,
|
||||
Description: "Invisible impenetrable wall."},
|
||||
Description: "Drop a sheet of invisible, unbreakable force exactly where you want it. Nothing gets through."},
|
||||
{ID: "scrying", Name: "Scrying", Level: 5, School: "divination",
|
||||
Classes: mageCleric, Effect: EffectUtility, CastTime: CastAction,
|
||||
Concentration: true, SaveStat: "WIS",
|
||||
Description: "Observe target at any distance."},
|
||||
Description: "Peer through a quiet pool or mirror and watch a chosen target wherever they are. Long-distance gossip."},
|
||||
{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."},
|
||||
Description: "A wave of healing rolls out and washes up to six bleeding allies clean of their worst injuries."},
|
||||
{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."},
|
||||
Description: "A column of divine fire roars down from above. Half flame, half holy fury, all very loud."},
|
||||
{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."},
|
||||
Description: "Speak a word from the language of creation. The weak fall; the strong stagger; the worst things go elsewhere."},
|
||||
{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."},
|
||||
Description: "Coax an ally back from up to ten days dead. They wake up tired and missing a diamond."},
|
||||
|
||||
// ── SRD-only Description cleanups ─────────────────────────────────────
|
||||
// Shims that overlay SRD entries whose generated Description was
|
||||
// broken, empty, typo-laden ("magicou"), or jargon-heavy. Fields
|
||||
// duplicated from buildSRDSpellList() so the overlay replacement
|
||||
// preserves Effect/CastTime/etc. Classes are unioned at merge time.
|
||||
{ID: "false_life", Name: "False Life", Level: 1, School: "necromancy",
|
||||
Classes: []DnDClass{ClassMage, ClassSorcerer}, Effect: EffectBuffSelf, CastTime: CastAction,
|
||||
DamageDice: "1d4+4",
|
||||
Description: "Wrap yourself in a thin shell of necromantic vigour. Hits land on the shell first; you'll thank yourself later.",
|
||||
Upcast: "tougher shell per slot above 1st"},
|
||||
{ID: "druidcraft", Name: "Druidcraft", Level: 0, School: "transmutation",
|
||||
Classes: []DnDClass{ClassDruid}, Effect: EffectUtility, CastTime: CastAction, AOE: true,
|
||||
Description: "Whisper to the spirits and they whisper back — usually about tomorrow's weather, sometimes a tiny gust, sometimes a sapling. Mostly harmless theatre."},
|
||||
{ID: "light", Name: "Light", Level: 0, School: "evocation",
|
||||
Classes: []DnDClass{ClassMage, ClassCleric, ClassBard, ClassSorcerer}, Effect: EffectUtility, CastTime: CastAction, AOE: true,
|
||||
Description: "Tap an object and it glows like a friendly torch. Easy to carry, harder to lose."},
|
||||
{ID: "resistance", Name: "Resistance", Level: 0, School: "abjuration",
|
||||
Classes: []DnDClass{ClassCleric, ClassDruid}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Concentration: true,
|
||||
Description: "A brief shimmer of luck settles on an ally. The next hard knock lands a little softer."},
|
||||
{ID: "spare_the_dying", Name: "Spare the Dying", Level: 0, School: "necromancy",
|
||||
Classes: []DnDClass{ClassCleric}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Lay hands on someone who's just dropped. They're stable now — not better, but not getting worse."},
|
||||
{ID: "thaumaturgy", Name: "Thaumaturgy", Level: 0, School: "transmutation",
|
||||
Classes: []DnDClass{ClassCleric}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Bark, boom, flicker, tremor — manifest a small showy miracle on cue. Crowds appreciate it; cats less so."},
|
||||
{ID: "true_strike", Name: "True Strike", Level: 0, School: "divination",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassSorcerer, ClassWarlock}, Effect: EffectBuffSelf, CastTime: CastAction,
|
||||
Concentration: true,
|
||||
Description: "Point at a foe and read their tells. Your next swing knows exactly where they'll flinch."},
|
||||
{ID: "mage_hand", Name: "Mage Hand", Level: 0, School: "conjuration",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassSorcerer, ClassWarlock}, Effect: EffectBuffSelf, CastTime: CastAction,
|
||||
Description: "A spectral hand pops into being and does small chores for you — fetch, lift, poke. Mute, helpful, slightly creepy."},
|
||||
{ID: "prestidigitation", Name: "Prestidigitation", Level: 0, School: "transmutation",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassSorcerer, ClassWarlock}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "A grab-bag of party tricks — small sparks, faint scents, a chilled drink, a tidied cloak. Showy and useless and brilliant."},
|
||||
{ID: "bane", Name: "Bane", Level: 1, School: "enchantment",
|
||||
Classes: []DnDClass{ClassCleric, ClassBard, ClassPaladin}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Concentration: true,
|
||||
Description: "Up to three foes feel suddenly cursed — their swings wobble, their nerve wavers. Petty and effective.",
|
||||
Upcast: "+1 target per slot above 1st"},
|
||||
{ID: "comprehend_languages", Name: "Comprehend Languages", Level: 1, School: "divination",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassSorcerer, ClassWarlock}, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Description: "For a while, every spoken language sounds like your own. Idioms and sarcasm still your problem."},
|
||||
{ID: "create_or_destroy_water", Name: "Create or Destroy Water", Level: 1, School: "transmutation",
|
||||
Classes: []DnDClass{ClassCleric, ClassDruid}, Effect: EffectUtility, CastTime: CastAction, AOE: true,
|
||||
Description: "Conjure a small flood out of nowhere, or politely uncreate one. Useful for drinks, fires, and floors.",
|
||||
Upcast: "more water per slot above 1st"},
|
||||
{ID: "detect_evil_and_good", Name: "Detect Evil and Good", Level: 1, School: "divination",
|
||||
Classes: []DnDClass{ClassCleric, ClassPaladin}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Concentration: true,
|
||||
Description: "Sweep the area for things that don't belong on the Material Plane — fiends, fey, the undead, the worse. Locations included."},
|
||||
{ID: "detect_poison_and_disease", Name: "Detect Poison and Disease", Level: 1, School: "divination",
|
||||
Classes: []DnDClass{ClassCleric, ClassRanger, ClassDruid, ClassPaladin}, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Concentration: true,
|
||||
Description: "Sniff out anything that wants to make you sick — venoms, plagues, the cook's questionable stew."},
|
||||
{ID: "floating_disk", Name: "Floating Disk", Level: 1, School: "conjuration",
|
||||
Classes: []DnDClass{ClassMage}, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Description: "Summon a hovering disc of force that follows you around. Pack the loot on top; it never tires, never complains."},
|
||||
{ID: "goodberry", Name: "Goodberry", Level: 1, School: "transmutation",
|
||||
Classes: []DnDClass{ClassRanger, ClassDruid}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "A handful of magic berries appears. Each is a small meal and a small heal in one bite. Tastes faintly of summer."},
|
||||
{ID: "jump", Name: "Jump", Level: 1, School: "transmutation",
|
||||
Classes: []DnDClass{ClassMage, ClassRanger, ClassDruid, ClassSorcerer}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Touch an ally and their next leap clears walls, fences, and most regrets."},
|
||||
{ID: "longstrider", Name: "Longstrider", Level: 1, School: "transmutation",
|
||||
Classes: []DnDClass{ClassMage, ClassRanger, ClassDruid, ClassBard}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "An ally's stride lengthens. They cover more ground all day and barely notice.",
|
||||
Upcast: "+1 target per slot above 1st"},
|
||||
{ID: "purify_food_and_drink", Name: "Purify Food and Drink", Level: 1, School: "transmutation",
|
||||
Classes: []DnDClass{ClassCleric, ClassDruid, ClassPaladin}, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Description: "Whatever's on the table is suddenly safe to eat. Poisons gone, plagues gone, flavour mostly intact."},
|
||||
{ID: "sanctuary", Name: "Sanctuary", Level: 1, School: "abjuration",
|
||||
Classes: []DnDClass{ClassCleric, ClassPaladin}, Effect: EffectUtility, CastTime: CastBonusAction,
|
||||
Description: "Mark an ally with a quiet ward. Foes who try to swing at them think twice — and usually pick someone else."},
|
||||
{ID: "silent_image", Name: "Silent Image", Level: 1, School: "illusion",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassSorcerer}, Effect: EffectBuffSelf, CastTime: CastAction,
|
||||
Concentration: true, AOE: true,
|
||||
Description: "Project a silent illusion you can shift around at will. Looks real until someone tries to touch it."},
|
||||
{ID: "unseen_servant", Name: "Unseen Servant", Level: 1, School: "conjuration",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassWarlock}, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Description: "An invisible little helper appears at your elbow — fetches, carries, opens doors. Mute, tireless, suggestible."},
|
||||
{ID: "barkskin", Name: "Barkskin", Level: 2, School: "transmutation",
|
||||
Classes: []DnDClass{ClassCleric, ClassRanger, ClassDruid}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Concentration: true,
|
||||
Description: "An ally's skin roughens into bark. Less expressive; much harder to dent."},
|
||||
{ID: "enthrall", Name: "Enthrall", Level: 2, School: "enchantment",
|
||||
Classes: []DnDClass{ClassBard, ClassWarlock}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Spin a rapid-fire patter so distracting that listeners lose track of everything else around them."},
|
||||
{ID: "gentle_repose", Name: "Gentle Repose", Level: 2, School: "necromancy",
|
||||
Classes: []DnDClass{ClassMage, ClassCleric}, Effect: EffectUtility, CastTime: CastRitual,
|
||||
Description: "Hold time off a corpse for a while. Rot pauses; resurrection windows stay open."},
|
||||
{ID: "protection_from_poison", Name: "Protection from Poison", Level: 2, School: "abjuration",
|
||||
Classes: []DnDClass{ClassCleric, ClassRanger, ClassDruid, ClassPaladin}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Drain the poison from an ally and ward them against the next dose. Briefly venom-proof."},
|
||||
{ID: "magic_weapon", Name: "Magic Weapon", Level: 2, School: "transmutation",
|
||||
Classes: []DnDClass{ClassMage, ClassCleric, ClassPaladin}, Effect: EffectBuffAlly, CastTime: CastBonusAction,
|
||||
Concentration: true,
|
||||
Description: "A plain weapon hums with a faint magical edge. Hits land truer; ghosts stop being a problem."},
|
||||
{ID: "major_image", Name: "Major Image", Level: 3, School: "illusion",
|
||||
Classes: []DnDClass{ClassMage, ClassBard, ClassSorcerer, ClassWarlock}, Effect: EffectBuffSelf, CastTime: CastAction,
|
||||
Concentration: true, DamageType: "thunder", AOE: true,
|
||||
Description: "Spin up a full-sensory illusion — sound, smell, the lot. Move it around like a puppeteer."},
|
||||
{ID: "tiny_hut", Name: "Tiny Hut", Level: 3, School: "evocation",
|
||||
Classes: []DnDClass{ClassMage, ClassBard}, Effect: EffectUtility, CastTime: CastRitual, AOE: true,
|
||||
Description: "Drop an invisible dome around the party for the night. Weather stays out; sleep stays in."},
|
||||
{ID: "compulsion", Name: "Compulsion", Level: 4, School: "enchantment",
|
||||
Classes: []DnDClass{ClassBard}, Effect: EffectControl, CastTime: CastAction,
|
||||
Concentration: true, SaveStat: "WIS",
|
||||
Description: "Foes who can hear you find themselves walking the direction you point. They're not happy about it."},
|
||||
{ID: "hallucinatory_terrain", Name: "Hallucinatory Terrain", Level: 4, School: "illusion",
|
||||
Classes: []DnDClass{ClassMage, ClassDruid, ClassBard, ClassWarlock}, Effect: EffectUtility, CastTime: CastAction, AOE: true,
|
||||
Description: "Reskin a whole patch of landscape. A meadow becomes a swamp; a cliff becomes a road. Disorienting."},
|
||||
{ID: "stone_shape", Name: "Stone Shape", Level: 4, School: "transmutation",
|
||||
Classes: []DnDClass{ClassMage, ClassCleric, ClassDruid}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Description: "Mould a chunk of stone like wet clay. Doorways appear; walls grow handles; the dwarves disapprove."},
|
||||
{ID: "antilife_shell", Name: "Antilife Shell", Level: 5, School: "abjuration",
|
||||
Classes: []DnDClass{ClassDruid}, Effect: EffectUtility, CastTime: CastAction,
|
||||
Concentration: true, AOE: true,
|
||||
Description: "A shimmering bubble travels with you. Living things bounce off it; the undead saunter through."},
|
||||
}
|
||||
}
|
||||
|
||||
45
internal/plugin/dnd_spells_prose_test.go
Normal file
45
internal/plugin/dnd_spells_prose_test.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestSpellDescriptionsAreJargonFree scans every reachable spell Description
|
||||
// in the merged registry (SRD + hand-authored overlay) for D&D jargon that
|
||||
// leaks into player-facing chat via dnd_cast.go's !cast confirmation and the
|
||||
// !spellbook list view. Per feedback_accessibility_over_dnd_crunch, the
|
||||
// player surface should be verb/outcome-led — no math, no dice, no saves.
|
||||
//
|
||||
// Failures usually mean either (a) a new SRD entry was added without an
|
||||
// overlay shim in dnd_spells_data.go, or (b) an overlay Description was
|
||||
// edited back into crunch. Fix in dnd_spells_data.go; do NOT touch the
|
||||
// generated dnd_spells_srd_data.go directly (it's regenerated from Open5e).
|
||||
//
|
||||
// Upcast strings are NOT tested — they're never displayed (grep
|
||||
// `\.Upcast` across the codebase to confirm; only the import generator
|
||||
// reads them as of 2026-05-15).
|
||||
func TestSpellDescriptionsAreJargonFree(t *testing.T) {
|
||||
bans := []struct {
|
||||
name string
|
||||
re *regexp.Regexp
|
||||
}{
|
||||
{"saving throw", regexp.MustCompile(`(?i)saving throw`)},
|
||||
{"spell slot of", regexp.MustCompile(`(?i)spell slot of`)},
|
||||
{"ability modifier", regexp.MustCompile(`(?i)ability modifier`)},
|
||||
{"hit points equal to", regexp.MustCompile(`(?i)hit points equal to`)},
|
||||
{"NdN dice notation", regexp.MustCompile(`\b\d+d\d+\b`)},
|
||||
}
|
||||
|
||||
for id, s := range dndSpellRegistry {
|
||||
if s.Description == "" {
|
||||
continue
|
||||
}
|
||||
for _, b := range bans {
|
||||
if b.re.MatchString(s.Description) {
|
||||
t.Errorf("spell %q Description leaks %q jargon: %q",
|
||||
id, b.name, s.Description)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user