Added more all wizard necromancy spells and a spell factory.

This commit is contained in:
Mark Wolfman
2018-04-09 23:58:56 -05:00
parent 82d59a70d9
commit e6bec1ce55
8 changed files with 348 additions and 24 deletions
+11 -8
View File
@@ -105,20 +105,23 @@ class Character():
elif attr == 'race':
MyRace = findattr(race, val)
self.race = MyRace()
elif attr == 'spells':
elif (attr == 'spells') or (attr == 'spells_prepared'):
# Create a list of actual spell objects
_spells = []
for spell_name in val:
try:
_spells.append(findattr(spells, spell_name)())
_spells.append(findattr(spells, spell_name))
except AttributeError:
msg = f'Spell "{spell_name}" not defined. Please add it to ``spells.py``'
raise AttributeError(msg)
self.spells = tuple(_spells)
elif attr == 'spells_prepared':
# Create a list of actual spell objects
self.spells_prepared = tuple(findattr(spells, spell_name)
for spell_name in val)
warnings.warn(msg)
# Create temporary spell
_spells.append(spells.create_spell(name=spell_name, level=9))
# raise AttributeError(msg)
if attr == 'spells':
# Instantiate them all for the spells list
self.spells = tuple(S() for S in _spells)
else:
self.spells_prepared = tuple(_spells)
else:
if not hasattr(self, attr):
warnings.warn(f"Setting unknown character attribute {attr}",
+318 -13
View File
@@ -1,3 +1,22 @@
def create_spell(**params):
"""Create a new subclass of ``Spell`` with given default parameters.
Useful for spells that haven't been entered into the ``spells.py``
file yet.
Parameters
----------
params : optional
Saved as attributes of the new class.
Returns
-------
NewSpell
New spell class, subclass of ``Spell``, with given params.
"""
NewSpell = type('UnknownSpell', (Spell,), params)
return NewSpell
class Spell():
"""A magical spell castable by a player character."""
level = 0
@@ -40,7 +59,6 @@ class AcidArrow(Spell):
class AcidSplash(Spell):
"""You hurl a bubble of acid. Choose one creature within range, or
choose two creatures within range that are within 5 feet of each
other. A target must succeed on a Dexterity saving throw or take
@@ -74,7 +92,7 @@ class Aid(Spell):
materials = "A tiny strip of white cloth"
duration = "8 hours"
magic_school = "Abjuration"
classes = ('Cleric', 'Paladin')
classes = ('Cleric', 'Paladin', )
class Alarm(Spell):
@@ -1311,6 +1329,57 @@ class ChillTouch(Spell):
classes = ('Sorceror', 'Warlock', 'Wizard')
class CircleOfDeath(Spell):
"""A sphere of negative energy ripples out in a 60-foot- radius sphere
from a point within range. Each creature in that area must make a
Constitution saving throw. A target takes 8d6 necrotic damage on a
failed save, or half as much damage on a successful one.
**At Higher Levels.** When you cast this spell using a spell slot of
7th level or higher, the damage increases by 2d6 for each slot
level above 6th.
"""
name = "Circle of Death"
level = 6
casting_time = "1 action"
casting_range = "150 feet"
components = ('V', 'S', 'M')
materials = "the powder of a crushed black pearl worth at least 500 gp"
duration = "Instantaneous"
magic_school = "Necromany"
classes = ('Sorceror', 'Warlock', 'Wizard')
class Clone(Spell):
"""This spell grows an inert duplicate of a living creature as a
safeguard against death. This clone forms inside a sealed vessel
and grows to full size and maturity after 120 days; you can also
choose to have the clone be a younger version of the same
creature. It remains inert and endures indefinitely, as long as
its vessel remains undisturbed.
At any time after the clone matures, if the original creature
dies, its soul transfers to the clone, provided that the soul is
free and willing to return. The clone is physically identical to
the original and has the same personality, memories, and
abilities, but none of the original's equipment. The original
creature's physical remains, if they still exist, become inert and
can't thereafter be restored to life, since the creature's soul is
elsewhere.
"""
name = "Clone"
level = 8
casting_time = "1 hour"
casting_range = "Touch"
components = ('V', 'S', "M")
materials = "a diamond worth at least 1,000 gp and at least 1 cubic inch of flesh of the creature that is to be cloned, which the spell consumes, and a vessel worth at least 2,000 gp that has a sealable lid and is large enough to hold a Medium creature, such as a huge urn, coffin, mud- filled cyst in the ground, or crystal container filled with salt water"
duration = "Instantaneous"
magic_school = "Necromancy"
classes = ('Wizard',)
class Command(Spell):
"""You speak a one-word command to a creature you can see within
range. The target must succeed on a Wisdom saving throw or follow
@@ -1432,6 +1501,50 @@ class Counterspell(Spell):
classes = ()
class CreateUndead(Spell):
"""You can cast this spell only at night. Choose up to three corpses
of Medium or Small humanoids within range. Each corpse becomes a
ghoul under your control. (The GM has game statistics for these
creatures.)
As a bonus action on each of your turns, you can mentally command
any creature you animated with this spell if the creature is
within 120 feet of you (if you control multiple creatures, you can
command any or all of them at the same time, issuing the same
command to each one). You decide what action the creature will
take and where it will move during its next turn, or you can issue
a general command, such as to guard a particular chamber or
corridor. If you issue no commands, the creature only defends
itself against hostile creatures. Once given an order, the
creature continues to follow it until its task is complete.
The creature is under your control for 24 hours, after which it
stops obeying any command you have given it. To maintain control
of the creature for another 24 hours, you must cast this spell on
the creature before the current 24-hour period ends. This use of
the spell reasserts your control over up to three creatures you
have animated with this spell, rather than animating new ones.
**At Higher Levels.** When you cast this spell using a 7th-level spell
slot, you can animate or reassert control over four ghouls. When
you cast this spell using an 8th-level spell slot, you can animate
or reassert control over five ghouls or two ghasts or wights. When
you cast this spell using a 9th-level spell slot, you can animate
or reassert control over six ghouls, three ghasts or wights, or
two mummy(ies).
"""
name = "Create Undead"
level = 6
casting_time = "1 minute"
casting_range = "10 feet"
components = ('V', 'S', 'M')
materials = "one clay pot filled with grave dirt, one clay pot filled with brackish water, and one 150 gp black onyx stone for each corpse"
duration = "Instantaneous"
magic_school = "Necromancy"
classes = ('Cleric', 'Warlock', 'Wizard', )
class CureWounds(Spell):
"""A creature you touch regains a number of hit points equal to 1d8 +
your spellcasting ability modifier. This spell has no effect on
@@ -1947,6 +2060,43 @@ class Etherealness(Spell):
classes = ()
class Eyebite(Spell):
"""For the spell's duration, your eyes become an inky void imbued with
dread power. One creature of your choice within 60 feet of you
that you can see must succeed on a Wisdom saving throw or be
affected by one of the following effects of your choice for the
duration. On each of your turns until the spell ends, you can use
your action to target another creature but can't target a creature
again if it has succeeded on a saving throw against this casting
of **eyebite**.
**Asleep.** The target falls unconscious. It wakes up if it takes
any damage or if another creature uses its action to shake the
sleeper awake.
**Panicked.** The target is frightened of you. On each of its
turns, the frightened creature must take the Dash action and
move away from you by the safest and shortest available route,
unless there is nowhere to move. If the target moves to a place
at least 60 feet away from you where it can no longer see you,
this effect ends.
**Sickened.** The target has disadvantage on attack rolls and
ability checks. At the end of each of its turns, it can make
another Wisdom saving throw. If it succeeds, the effect ends.
"""
name = "Eyebite"
level = 6
casting_time = "1 action"
casting_range = "Self"
components = ('V', 'S',)
materials = ""
duration = "1 minutes"
magic_school = "Necromancy"
classes = ('Bard', 'Sorceror', 'Warlock', 'Wizard', )
class FalseLife(Spell):
"""Bolstering yourself with a necromantic facsimile of life, you gain
1d4+4 temporary hit points for the duration.
@@ -2014,11 +2164,12 @@ class FingerOfDeath(Spell):
name = "Finger of Death"
level = 7
casting_time = "1 action"
casting_range = "60 feet"
components = ('V', 'S')
materials = ""
duration = "Instantaneous"
magic_school = "Necromancy"
classes = ()
classes = ('Sorceror', 'Warlock', 'Wizard', )
class FireBolt(Spell):
@@ -2273,6 +2424,26 @@ class Gate(Spell):
classes = ()
class GentleRepose(Spell):
"""You touch a corpse or other remains. For the duration, the target
is protected from decay and can't become undead.
The spell also effectively extends the time limit on raising the
target from the dead, since days spent under the influence of this
spell don't count against the time limit of spells such as raise
dead.
"""
name = "Gentle Repose"
level = 2
casting_time = "1 action"
casting_range = "Touch"
components = ('V', 'S', 'M')
materials = "a pinch of salt and one copper piece placed on each of the corpse's eyes, which must remain there for the duration"
duration = "10 days"
magic_school = "Necromancy"
classes = ('Cleric', 'Wizard')
class GlobeOfInvulnerability(Spell):
"""An immobile, faintly shimmering barrier springs into existence in a
10-foot radius around you and remains for the duration. Any spell
@@ -2859,6 +3030,65 @@ class MageHand(Spell):
classes = ('Bard', 'Sorceror', 'Warlock', 'Wizard', )
class MagicJar(Spell):
"""Your body falls into a catatonic state as your soul leaves it
and enters the container you used for the spell's material
component. While your soul inhabits the container, you are aware
of your surroundings as if you were in the container's space. You
can't move or use reactions. The only action you can take is to
project your soul up to 100 feet out of the container, either
returning to your living body (and ending the spell) or attempting to possess a humanoids body.
You can attempt to possess any humanoid within 100 feet of you
that you can see (creatures warded by a protection from evil and
good or magic circle spell can't be possessed). The target must
make a Charisma saving throw. On a failure, your soul moves into
the target's body, and the target's soul becomes trapped in the
container. On a success, the target resists your efforts to
possess it, and you can't attempt to possess it again for 24
hours.
Once you possess a creature's body, you control it. Your game
statistics are replaced by the statistics of the creature, though
you retain your alignment and your Intelligence, Wisdom, and
Charisma scores. You retain the benefit of your own class
features. If the target has any class levels, you can't use any of
its class features.
Meanwhile, the possessed creature's soul can perceive from the
container using its own senses, but it can't move or take actions
at all.
While possessing a body, you can use your action to return from
the host body to the container if it is within 100 feet of you,
returning the host creature's soul to its body. If the host body
dies while you're in it, the creature dies, and you must make a
Charisma saving throw against your own spellcasting DC. On a
success, you return to the container if it is within 100 feet of
you. Otherwise, you die.
If the container is destroyed or the spell ends, your soul
immediately returns to your body. If your body is more than 100
feet away from you or if your body is dead when you attempt to
return to it, you die. If another creature's soul is in the
container when it is destroyed, the creature's soul returns to its
body if the body is alive and within 100 feet. Otherwise, that
creature dies.
When the spell ends, the container is destroyed.
"""
name = "Magic Jar"
level = 6
casting_time = "1 minute"
casting_range = "Self"
components = ("V", "S", "M", )
materials = "a gem, crystal, reliquary, or some other ornamental container worth at least 500 gp)"
duration = "Until dispelled"
magic_school = "Necromancy"
classes = ('Wizard', )
class MagicMissile(Spell):
"""You create three glowing darts of magical force. Each dart hits a
creature of your choice that you can see within range. A dart
@@ -3055,6 +3285,29 @@ class Maze(Spell):
classes = ()
class MelfsAcidArrow(Spell):
"""A shimmering green arrow streaks toward a target within range and
burst in a spray of acid. Make a ranged spell attack against the
target. On a hit, the target takes 4d4 acid damage immediately and
2d4 acid damage at the end of its next turn. On a miss, the arrow
splashes the target for half as much of the initial damage and no
damage at the end of its next turn.
**At Higher Levels.** When you cast this spell using a spell slot
of 3rd level or higher, the damage (both initial and later)
increases by 1d4 for each slot level above 2nd.
"""
name = "Melf's Acid Arrow"
level = 2
casting_time = "1 action"
components = ('V', 'S', 'M', )
materials = "powdered rhubarb leaf and an adder's stomach"
duration = "Instantaneous"
magic_school = "Evocation"
classes = ('Wizard', )
class MeteorSwarm(Spell):
"""Blazing orbs of fire plummet to the ground at four different points
you can see within range. Each creature in a 40-foot-radius sphere
@@ -3313,29 +3566,57 @@ class RaiseDead(Spell):
"""You return a dead creature you touch to life, provided that it has
been dead no longer than 10 days. If the creatures soul is both
willing and at liberty to rejoin the body, the creature returns to
life with 1 hit point. This spell also neutralizes any poisons and
life with 1 hit point.
This spell also neutralizes any poisons and
cures nonmagical diseases that affected the creature at the time
it died. This spell doesnt, however, remove magical diseases,
curses, or similar effects; if these arent first removed prior to
casting the spell, they take effect when the creature returns to
life. The spell cant return an undead creature to life. This
spell closes all mortal wounds, but it doesnt restore missing
body parts. If the creature is lacking body parts or organs
integral for its survival—its head, for instance—the spell
automatically fails. Coming back from the dead is an ordeal. The
target takes a 4 penalty to all attack rolls, saving throws, and
ability checks. Every time the target finishes a long rest, the
penalty is reduced by 1 until it disappears.
life. The spell cant return an undead creature to life.
This spell closes all mortal wounds, but it doesnt restore
missing body parts. If the creature is lacking body parts or
organs integral for its survival—its head, for instance—the spell
automatically fails.
Coming back from the dead is an ordeal. The target takes a 4
penalty to all attack rolls, saving throws, and ability
checks. Every time the target finishes a long rest, the penalty is
reduced by 1 until it disappears.
"""
name = "Raise Dead"
level = 5
casting_time = "1 hour"
casting_range = "Touch"
components = ('V', 'S', 'M')
materials = "a diamond worth at least 500 gp, which the spell consumes"
duration = "Instantaneous"
magic_school = "Necromancy"
classes = ()
classes = ('Bard', 'Cleric', 'Paladin', )
class RayOfEnfeeblement(Spell):
"""A black beam of enervating energy springs from your finger toward a
creature within range. Make a ranged spell attack against the
target. On a hit, the target deals only half damage with weapon
attacks that use Strength until the spell ends.
At the end of each of the target's turns, it can make a
Constitution saving throw against the spell. On a success, the
spell ends.
"""
name = "Ray of Enfeeblement"
level = 2
casting_time = "1 action"
casting_range = "60 feet"
components = ('V', 'S', )
materials = ""
duration = "Concentration (1 minute)"
magic_school = "Necromancy"
classes = ('Warlock', 'Wizard', )
class RayOfFrost(Spell):
@@ -4072,6 +4353,30 @@ class TrueSeeing(Spell):
classes = ()
class VampiricTouch(Spell):
"""The touch of your shadow-wreathed hand can siphon life force from
others to heal your wounds. Make a melee spell attack against a
creature within your reach. On a hit, the target takes 3d6
necrotic damage, and you regain hit points equal to half the
amount of necrotic damage dealt. Until the spell ends, you can
make the attack again on each of your turns as an action.
**At Higher Levels.** When you cast this spell using a spell slot
of 4th level or higher, the damage increases by 1d6 for each slot
level above 3rd.
"""
name = "Vampiric Touch"
level = 3
casting_time = "1 action"
casting_range = "Self"
components = ('V', 'S', )
materials = ""
duration = "Concentration (1 minute)"
magic_school = "Necromancy"
classes = ('Warlock', 'Wizard', )
class WallOfFire(Spell):
"""You create a wall of fire on a solid surface within range. You can
make the wall up to 60 feet long, 20 feet high, and 1 foot thick,
+1 -1
View File
@@ -7,7 +7,7 @@ def findattr(obj, name):
"""
# Come up with several options
py_name = name.replace('-', '_').replace(' ', '_')
py_name = name.replace('-', '_').replace(' ', '_').replace("'", "")
camel_case = "".join([s.capitalize() for s in py_name.split('_')])
if hasattr(obj, py_name):
# Direct lookup
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2 -2
View File
@@ -44,10 +44,10 @@ equipment = (
spells = ('blindness deafness', 'burning hands', 'detect magic',
'false life', 'mage armor', 'mage hand', 'magic missile',
'prestidigitation', 'ray of frost', 'ray of sickness', 'shield',
'shocking grasp', 'sleep')
'shocking grasp', 'sleep', 'some other spell')
# Which spells have been prepared (not including cantrips)
spells_prepared = ('blindness deafness', 'false life', 'mage armor',
'ray of sickness', 'shield', 'sleep')
'ray of sickness', 'shield', 'sleep',)
# Backstory
personality_traits = """I use polysyllabic words that convey the impression of
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env python
from unittest import TestCase
from dungeonsheets.spells import create_spell, Spell
class TestSpells(TestCase):
"""Tests for spells and spell-related activities."""
def test_create_spell(self):
NewSpell = create_spell(name="Hello world")
self.assertTrue(issubclass(NewSpell, Spell))
self.assertEqual(NewSpell.name, 'Hello world')
spell = NewSpell()
print(spell, spell.__class__, type(spell))