mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-05-19 04:33:26 +02:00
reorganized spells by name, and added directory for features for each class"
This commit is contained in:
+29
-16
@@ -183,6 +183,10 @@ class Character():
|
||||
fts |= set(c.features)
|
||||
if self.race is not None:
|
||||
fts |= set(getattr(self.race, 'features', ()))
|
||||
# some races have level-based features (Ex: Aasimar)
|
||||
if hasattr(self.race, 'features_by_level'):
|
||||
for lvl in range(1, self.level+1):
|
||||
fts |= set(self.race.features_by_level[lvl])
|
||||
if self.background is not None:
|
||||
fts |= set(getattr(self.background, 'features', ()))
|
||||
return tuple(fts)
|
||||
@@ -266,12 +270,16 @@ class Character():
|
||||
_features = []
|
||||
for f in val:
|
||||
try:
|
||||
_features.append(findattr(features, f)())
|
||||
_features.append(findattr(features, f))
|
||||
except AttributeError:
|
||||
msg = (f'Feature "{f}" not defined. '
|
||||
f'Please add it to ``features.py``')
|
||||
# create temporary feature
|
||||
_features.append(features.create_feature(
|
||||
name=f, source='Unknown',
|
||||
__doc__="""Unknown Feature. Add to features.py"""))
|
||||
warnings.warn(msg)
|
||||
self.other_features = tuple(_features)
|
||||
self.other_features = tuple(F() for F in _features)
|
||||
elif (attr == 'spells') or (attr == 'spells_prepared'):
|
||||
# Create a list of actual spell objects
|
||||
_spells = []
|
||||
@@ -358,9 +366,10 @@ class Character():
|
||||
|
||||
@property
|
||||
def features_text(self):
|
||||
s = '\n\n*'.join([f.name for f in self.features])
|
||||
s = '\n\n--'.join([f.name + ("**" if f.needs_implementation else "")
|
||||
for f in self.features])
|
||||
if s != '':
|
||||
s = '(See Features Details Page)\n\n*' + s
|
||||
s = '(See Features and Traits Page)\n\n--' + s
|
||||
s += '\n\n=================\n\n'
|
||||
return s
|
||||
|
||||
@@ -415,6 +424,9 @@ class Character():
|
||||
except AttributeError:
|
||||
raise AttributeError(f'Weapon "{weapon}" is not defined')
|
||||
weapon_ = NewWeapon()
|
||||
# check if features add any bonuses
|
||||
for f in self.features:
|
||||
weapon_ = f.weapon_func(weapon_, char=self)
|
||||
# Set weapon attributes based on character
|
||||
if weapon_.is_finesse:
|
||||
ability_mod = max(self.strength.modifier, self.dexterity.modifier)
|
||||
@@ -425,11 +437,6 @@ class Character():
|
||||
# Check for prifiency
|
||||
if self.is_proficient(weapon_):
|
||||
weapon_.attack_bonus += self.proficiency_bonus
|
||||
# check if features add any bonuses
|
||||
for f in self.features:
|
||||
a_bonus, d_bonus = f.weapon_func(weapon_)
|
||||
weapon_.attack_bonus += a_bonus
|
||||
weapon_.bonus_damage += d_bonus
|
||||
# Save it to the array
|
||||
self.weapons.append(weapon_)
|
||||
|
||||
@@ -456,12 +463,8 @@ class Character():
|
||||
return prof
|
||||
|
||||
@property
|
||||
def armor_class(self):
|
||||
def default_AC(self):
|
||||
"""Armor class, including contributions from worn armor and shield."""
|
||||
# ## TODO:
|
||||
# Implement AC functions by class
|
||||
if hasattr(self, 'force_AC'):
|
||||
return self.force_AC
|
||||
# Retrieve current armor (or a generic armor substitute)
|
||||
armor = self.armor if self.armor is not None else NoArmor()
|
||||
shield = self.shield if self.shield is not None else NoShield()
|
||||
@@ -471,7 +474,15 @@ class Character():
|
||||
else:
|
||||
modifier = min(self.dexterity.modifier, armor.dexterity_mod_max)
|
||||
# Calculate final armor class
|
||||
ac = [armor.base_armor_class + shield.base_armor_class + modifier]
|
||||
ac = armor.base_armor_class + shield.base_armor_class + modifier
|
||||
return ac
|
||||
|
||||
@property
|
||||
def armor_class(self):
|
||||
"""Armor class, including any applicable features"""
|
||||
if hasattr(self, 'force_AC'):
|
||||
return self.force_AC
|
||||
ac = [self.default_AC]
|
||||
ac += [f.AC_func(self) for f in self.features]
|
||||
return max(ac)
|
||||
|
||||
@@ -507,7 +518,9 @@ class Character():
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
'level was not recognizable as an int: {:s}'.format(lvl))
|
||||
class_list += [this_class(this_level, subclass=sub)]
|
||||
params = {}
|
||||
params['feature_choices'] = char_props.pop('feature_choices', [])
|
||||
class_list += [this_class(this_level, subclass=sub, **params)]
|
||||
# accept backwards compatability for single-class characters
|
||||
if len(class_list) == 0:
|
||||
class_name = char_props.pop('character_class').lower().capitalize()
|
||||
|
||||
@@ -1,523 +0,0 @@
|
||||
__all__ = ('Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter', 'Monk',
|
||||
'Paladin', 'Ranger', 'Rogue', 'Sorceror', 'Warlock', 'Wizard',
|
||||
'Revisedranger')
|
||||
|
||||
from .stats import findattr
|
||||
from . import (weapons, monsters, exceptions)
|
||||
from . import features as feats
|
||||
import math
|
||||
import warnings
|
||||
|
||||
|
||||
class CharClass():
|
||||
"""
|
||||
A generic Character Class (not to be confused with builtin class)
|
||||
"""
|
||||
class_name = ""
|
||||
class_level = 1
|
||||
hit_dice_faces = None
|
||||
weapon_proficiencies = ()
|
||||
_proficiencies_text = ()
|
||||
multiclass_weapon_proficiencies = ()
|
||||
_multiclass_proficiencies_text = ()
|
||||
features = ()
|
||||
languages = ()
|
||||
class_skill_choices = ()
|
||||
num_skill_choices = 2
|
||||
spellcasting_ability = None
|
||||
spell_slots_by_level = None
|
||||
subclass = None
|
||||
class_features_by_level = {lvl: () for lvl in range(1, 21)}
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
self.class_level = level
|
||||
self.subclass = subclass
|
||||
for k, v in params:
|
||||
setattr(self, k, v)
|
||||
|
||||
@property
|
||||
def class_features(self):
|
||||
features = ()
|
||||
for lvl in range(1, self.class_level+1):
|
||||
features += tuple(self.class_features_by_level[lvl])
|
||||
if self.subclass is not None:
|
||||
features += tuple(self.subclass.features_by_level[lvl])
|
||||
return features
|
||||
|
||||
@property
|
||||
def is_spellcaster(self):
|
||||
result = (self.spellcasting_ability is not None)
|
||||
return result
|
||||
|
||||
def spell_slots(self, spell_level):
|
||||
"""How many spells slots are available for this spell level."""
|
||||
if self.spell_slots_by_level is None:
|
||||
return 0
|
||||
else:
|
||||
return self.spell_slots_by_level[self.class_level][spell_level]
|
||||
|
||||
|
||||
class Barbarian(CharClass):
|
||||
class_name = 'Barbarian'
|
||||
hit_dice_faces = 12
|
||||
saving_throw_proficiencies = ('strength', 'constitution')
|
||||
_proficiencies_text = ('light armor', 'medium armor', 'shields',
|
||||
'simple weapons', 'martial weapons')
|
||||
weapon_proficiencies = (weapons.simple_weapons + weapons.martial_weapons)
|
||||
class_skill_choices = ('Animal Handling', 'Athletics',
|
||||
'Intimidation', 'Nature', 'Perception', 'Survival')
|
||||
|
||||
|
||||
class Bard(CharClass):
|
||||
class_name = 'Bard'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('dexterity', 'charisma')
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'simple weapons', 'hand crossbows', 'longswords',
|
||||
'rapiers', 'shortswords', 'three musical instruments of your choice')
|
||||
weapon_proficiencies = ((weapons.HandCrossbow, weapons.Longsword,
|
||||
weapons.Rapier, weapons.Shortsword) +
|
||||
weapons.simple_weapons)
|
||||
class_skill_choices = ('Acrobatics', 'Animal Handling', 'Arcana',
|
||||
'Athletics', 'Deception', 'History', 'Insight',
|
||||
'Intimidation', 'Investigation', 'Medicine',
|
||||
'Nature', 'Perception', 'Performance', 'Persuasion',
|
||||
'Religion', 'Sleight of Hand', 'Stealth',
|
||||
'Survival')
|
||||
num_skill_choices = 3
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (3, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (4, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (4, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (4, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (4, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (4, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
class Cleric(CharClass):
|
||||
class_name = 'Cleric'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ('light armor', 'medium armor', 'shields',
|
||||
'all simple weapons')
|
||||
weapon_proficiencies = weapons.simple_weapons
|
||||
class_skill_choices = ('History', 'Insight', 'Medicine',
|
||||
'Persuasion', 'Religion')
|
||||
spellcasting_ability = 'wisdom'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (3, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (5, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (5, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (5, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (5, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (5, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
class Druid(CharClass):
|
||||
class_name = 'Druid'
|
||||
circle = "" # moon, land
|
||||
_wild_shapes = ()
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
spellcasting_ability = 'wisdom'
|
||||
languages = 'Druidic'
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'medium armor',
|
||||
'shields (druids will not wear armor or use shields made of metal)',
|
||||
'clubs', 'daggers', 'darts', 'javelins', 'maces', 'quarterstaffs',
|
||||
'scimitars', 'sickles', 'slings', 'spears')
|
||||
weapon_proficiencies = (weapons.Club, weapons.Dagger, weapons.Dart,
|
||||
weapons.Javelin, weapons.Mace,
|
||||
weapons.Quarterstaff, weapons.Scimitar,
|
||||
weapons.Sickle, weapons.Sling, weapons.Spear)
|
||||
class_skill_choices = ('Arcana', 'Animal Handling', 'Insight',
|
||||
'Medicine', 'Nature', 'Perception', 'Religion',
|
||||
'Survival')
|
||||
spell_slots_by_level = {
|
||||
1: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (3, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (3, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (4, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (4, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (4, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (4, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (4, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
if subclass is not None:
|
||||
sc = str(subclass).lower()
|
||||
if 'moon' in sc:
|
||||
self.circle = 'moon'
|
||||
params.pop('circle', '')
|
||||
elif 'land' in sc:
|
||||
self.circle = 'land'
|
||||
params.pop('circle', '')
|
||||
super().__init__(level, **params)
|
||||
|
||||
@property
|
||||
def all_wild_shapes(self):
|
||||
"""Return all wild shapes, regardless of validity."""
|
||||
return self._wild_shapes
|
||||
|
||||
@property
|
||||
def wild_shapes(self):
|
||||
"""Return a list of valid wild shapes for this Druid."""
|
||||
valid_shapes = []
|
||||
for shape in self._wild_shapes:
|
||||
# Check if shape can be transformed into
|
||||
if self.can_assume_shape(shape):
|
||||
valid_shapes.append(shape)
|
||||
return valid_shapes
|
||||
|
||||
@wild_shapes.setter
|
||||
def wild_shapes(self, new_shapes):
|
||||
actual_shapes = []
|
||||
# Retrieve the actual monster classes if possible
|
||||
for shape in new_shapes:
|
||||
if isinstance(shape, monsters.Monster):
|
||||
# Already a monster shape so just add it as is
|
||||
new_shape = shape
|
||||
else:
|
||||
# Not already a monster so see if we can find one
|
||||
try:
|
||||
NewMonster = findattr(monsters, shape)
|
||||
new_shape = NewMonster()
|
||||
except AttributeError:
|
||||
msg = f'Wild shape "{shape}" not found. Please add it to ``monsters.py``'
|
||||
raise exceptions.MonsterError(msg)
|
||||
actual_shapes.append(new_shape)
|
||||
# Save the updated list for later
|
||||
self._wild_shapes = actual_shapes
|
||||
|
||||
def can_assume_shape(self, shape: monsters.Monster)-> bool:
|
||||
"""Determine if a given shape meets the requirements for transforming.
|
||||
|
||||
See Pg 66 of player's handbook.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
shape
|
||||
A monster that the Druid wishes to transform into.
|
||||
|
||||
Returns
|
||||
=======
|
||||
can_assume
|
||||
True if the monster meets the C/R, swim and flying speed
|
||||
restrictions.
|
||||
|
||||
"""
|
||||
# Determine acceptable states based on druid level
|
||||
if self.class_level < 2:
|
||||
max_cr = -1
|
||||
max_swim = 0
|
||||
max_fly = 0
|
||||
elif self.class_level < 4:
|
||||
max_cr = 1/4
|
||||
max_swim = 0
|
||||
max_fly = 0
|
||||
elif self.class_level < 8:
|
||||
max_cr = 1/2
|
||||
max_swim = None
|
||||
max_fly = 0
|
||||
else:
|
||||
max_cr = 1
|
||||
max_swim = None
|
||||
max_fly = None
|
||||
# Make adjustments for moon circle druids
|
||||
if self.circle.lower() == "moon":
|
||||
if 2 <= self.class_level < 6:
|
||||
max_cr = 1
|
||||
elif self.class_level >= 6:
|
||||
max_cr = math.floor(self.class_level / 3)
|
||||
# Check if the beast shape can be assumed
|
||||
valid_cr = (max_cr is None or shape.challenge_rating <= max_cr)
|
||||
valid_swim = (max_swim is None or shape.swim_speed <= max_swim)
|
||||
valid_fly = (max_fly is None or shape.fly_speed <= max_fly)
|
||||
can_assume = shape.is_beast and valid_cr and valid_swim and valid_fly
|
||||
return can_assume
|
||||
|
||||
@property
|
||||
def spells(self):
|
||||
return tuple(S() for S in self.spells_prepared)
|
||||
|
||||
@spells.setter
|
||||
def spells(self, val):
|
||||
if len(val) > 0:
|
||||
warnings.warn("Druids cannot learn spells, "
|
||||
"use ``spells_prepared`` instead.",
|
||||
RuntimeWarning)
|
||||
|
||||
|
||||
class Fighter(CharClass):
|
||||
class_name = 'Fighter'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ('strength', 'constitution')
|
||||
_proficiencies_text = ('All armor', 'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
multiclass_weapon_proficiencies = weapon_proficiencies
|
||||
_multiclass_proficiencies_text = ('light armor', 'medium armor',
|
||||
'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
class_skill_choices = ('Acrobatics', 'Animal Handling',
|
||||
'Athletics', 'History', 'Insight', 'Intimidation',
|
||||
'Perception', 'Survival')
|
||||
|
||||
|
||||
class Monk(CharClass):
|
||||
class_name = 'Monk'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('strength', 'dexterity')
|
||||
_proficiencies_text = (
|
||||
'simple weapons', 'shortswords', 'unarmed',
|
||||
"one type of artisan's tools or one musical instrument")
|
||||
weapon_proficiencies = (weapons.Shortsword, weapons.Unarmed) + weapons.simple_weapons
|
||||
class_skill_choices = ('Acrobatics', 'Athletics', 'History', 'Insight',
|
||||
'Religion', 'Stealth')
|
||||
|
||||
|
||||
class Paladin(CharClass):
|
||||
class_name = 'Paladin'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ('All armor', 'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
class_skill_choices = ("Athletics", 'Insight', 'Intimidation',
|
||||
'Medicine', 'Persuasion', 'Religion')
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
7: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
9: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
10: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
11: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
12: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
13: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
14: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
15: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
16: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
17: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
18: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
19: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
20: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
class Ranger(CharClass):
|
||||
class_name = 'Ranger'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ('strength', 'dexterity')
|
||||
_proficiencies_text = ("light armor", "medium armor", "shields",
|
||||
"simple weapons", "martial weapons")
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
class_skill_choices = ('Animal Handling', 'Athletics', 'Insight',
|
||||
'Investigation', 'Nature', 'Perception', 'Stealth',
|
||||
'Survival')
|
||||
num_skill_choices = 3
|
||||
spellcasting_ability = 'wisdom'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
7: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
9: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
10: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
11: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
12: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
13: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
14: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
15: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
16: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
17: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
18: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
19: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
20: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
class Rogue(CharClass):
|
||||
class_name = 'Rogue'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('dexterity', 'intelligence')
|
||||
_proficiencies_text = (
|
||||
'light armor', 'simple weapons', 'hand crossbows', 'longswords',
|
||||
'rapiers', 'shortswords', "thieves' tools")
|
||||
weapon_proficiencies = weapons.simple_weapons + (
|
||||
weapons.HandCrossbow, weapons.Longsword, weapons.Rapier,
|
||||
weapons.Shortsword)
|
||||
class_skill_choices = ('Acrobatics', 'Athletics', 'Deception',
|
||||
'Insight', 'Intimidation', 'Investigation',
|
||||
'Perception', 'Performance', 'Persuasion',
|
||||
'Sleight of Hand', 'Stealth')
|
||||
|
||||
|
||||
class Sorceror(CharClass):
|
||||
class_name = 'Sorceror'
|
||||
hit_dice_faces = 6
|
||||
saving_throw_proficiencies = ('constitution', 'charisma')
|
||||
_proficiencies_text = ('daggers', 'darts', 'slings',
|
||||
'quarterstaffs', 'light crossbows')
|
||||
weapon_proficiencies = (weapons.Dagger, weapons.Dart,
|
||||
weapons.Sling, weapons.Quarterstaff,
|
||||
weapons.LightCrossbow)
|
||||
class_skill_choices = ('Arcana', 'Deception', 'Insight',
|
||||
'Intimidation', 'Persuasion', 'Religion')
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (4, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (4, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (4, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (5, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (5, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (5, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (5, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (5, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (5, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (6, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (6, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (6, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (6, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (6, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (6, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (6, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (6, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (6, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (6, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (6, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
class Warlock(CharClass):
|
||||
class_name = 'Warlock'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ("light Armor", "simple weapons")
|
||||
class_skill_choices = ('Arcana', 'Deception', 'History',
|
||||
'Intimidation', 'Investigation', 'Nature',
|
||||
'Religion')
|
||||
weapon_proficiencies = weapons.simple_weapons
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
1: (2, 1, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 0, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 0, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 0, 0, 3, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 0, 0, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 0, 0, 0, 2, 0, 0, 0, 0, 0),
|
||||
8: (3, 0, 0, 0, 2, 0, 0, 0, 0, 0),
|
||||
9: (3, 0, 0, 0, 0, 2, 0, 0, 0, 0),
|
||||
10: (4, 0, 0, 0, 0, 2, 0, 0, 0, 0),
|
||||
11: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
12: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
13: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
14: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
15: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
16: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
17: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
18: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
19: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
20: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
class Wizard(CharClass):
|
||||
class_name = 'Wizard'
|
||||
hit_dice_faces = 6
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
_proficiencies_text = ('daggers', 'darts', 'slings',
|
||||
'quarterstaffs', 'light crossbows')
|
||||
weapon_proficiencies = (weapons.Dagger, weapons.Dart,
|
||||
weapons.Sling, weapons.Quarterstaff,
|
||||
weapons.LightCrossbow)
|
||||
class_skill_choices = ('Arcana', 'History', 'Investigation',
|
||||
'Medicine', 'Religion')
|
||||
spellcasting_ability = 'intelligence'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (3, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (5, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (5, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (5, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (5, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (5, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
# Custom Classes
|
||||
class Revisedranger(Ranger):
|
||||
class_name = 'Revisedranger'
|
||||
@@ -0,0 +1,17 @@
|
||||
__all__ = ('CharClass', 'Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter',
|
||||
'Monk', 'Paladin', 'Ranger', 'Rogue', 'Sorceror', 'Warlock',
|
||||
'Wizard', 'Revisedranger')
|
||||
|
||||
from .classes import CharClass
|
||||
from .barbarian import Barbarian
|
||||
from .bard import Bard
|
||||
from .cleric import Cleric
|
||||
from .druid import Druid
|
||||
from .fighter import Fighter
|
||||
from .monk import Monk
|
||||
from .paladin import Paladin
|
||||
from .ranger import (Ranger, Revisedranger)
|
||||
from .rogue import Rogue
|
||||
from .sorceror import Sorceror
|
||||
from .warlock import Warlock
|
||||
from .wizard import Wizard
|
||||
@@ -0,0 +1,15 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Barbarian(CharClass):
|
||||
class_name = 'Barbarian'
|
||||
hit_dice_faces = 12
|
||||
saving_throw_proficiencies = ('strength', 'constitution')
|
||||
_proficiencies_text = ('light armor', 'medium armor', 'shields',
|
||||
'simple weapons', 'martial weapons')
|
||||
weapon_proficiencies = (weapons.simple_weapons + weapons.martial_weapons)
|
||||
class_skill_choices = ('Animal Handling', 'Athletics',
|
||||
'Intimidation', 'Nature', 'Perception', 'Survival')
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Bard(CharClass):
|
||||
class_name = 'Bard'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('dexterity', 'charisma')
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'simple weapons', 'hand crossbows', 'longswords',
|
||||
'rapiers', 'shortswords', 'three musical instruments of your choice')
|
||||
weapon_proficiencies = ((weapons.HandCrossbow, weapons.Longsword,
|
||||
weapons.Rapier, weapons.Shortsword) +
|
||||
weapons.simple_weapons)
|
||||
class_skill_choices = ('Acrobatics', 'Animal Handling', 'Arcana',
|
||||
'Athletics', 'Deception', 'History', 'Insight',
|
||||
'Intimidation', 'Investigation', 'Medicine',
|
||||
'Nature', 'Perception', 'Performance', 'Persuasion',
|
||||
'Religion', 'Sleight of Hand', 'Stealth',
|
||||
'Survival')
|
||||
num_skill_choices = 3
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (3, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (4, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (4, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (4, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (4, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (4, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class CharClass():
|
||||
"""
|
||||
A generic Character Class (not to be confused with builtin class)
|
||||
"""
|
||||
class_name = ""
|
||||
class_level = 1
|
||||
hit_dice_faces = None
|
||||
weapon_proficiencies = ()
|
||||
_proficiencies_text = ()
|
||||
multiclass_weapon_proficiencies = ()
|
||||
_multiclass_proficiencies_text = ()
|
||||
languages = ()
|
||||
class_skill_choices = ()
|
||||
num_skill_choices = 2
|
||||
spellcasting_ability = None
|
||||
spell_slots_by_level = None
|
||||
subclass = None
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
self.class_level = level
|
||||
self.subclass = subclass
|
||||
for k, v in params.items():
|
||||
setattr(self, k, v)
|
||||
|
||||
@property
|
||||
def features(self):
|
||||
features = ()
|
||||
for lvl in range(1, self.class_level+1):
|
||||
features += tuple(self.features_by_level[lvl])
|
||||
if self.subclass is not None and not isinstance(self.subclass, str):
|
||||
features += tuple(self.subclass.features_by_level[lvl])
|
||||
return features
|
||||
|
||||
@property
|
||||
def is_spellcaster(self):
|
||||
result = (self.spellcasting_ability is not None)
|
||||
return result
|
||||
|
||||
def spell_slots(self, spell_level):
|
||||
"""How many spells slots are available for this spell level."""
|
||||
if self.spell_slots_by_level is None:
|
||||
return 0
|
||||
else:
|
||||
return self.spell_slots_by_level[self.class_level][spell_level]
|
||||
@@ -0,0 +1,39 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Cleric(CharClass):
|
||||
class_name = 'Cleric'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ('light armor', 'medium armor', 'shields',
|
||||
'all simple weapons')
|
||||
weapon_proficiencies = weapons.simple_weapons
|
||||
class_skill_choices = ('History', 'Insight', 'Medicine',
|
||||
'Persuasion', 'Religion')
|
||||
spellcasting_ability = 'wisdom'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (3, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (5, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (5, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (5, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (5, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (5, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
from ..stats import findattr
|
||||
from .. import (weapons, monsters, exceptions)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
import warnings
|
||||
import math
|
||||
|
||||
|
||||
class Druid(CharClass):
|
||||
class_name = 'Druid'
|
||||
circle = "" # moon, land
|
||||
_wild_shapes = ()
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
spellcasting_ability = 'wisdom'
|
||||
languages = 'Druidic'
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'medium armor',
|
||||
'shields (druids will not wear armor or use shields made of metal)',
|
||||
'clubs', 'daggers', 'darts', 'javelins', 'maces', 'quarterstaffs',
|
||||
'scimitars', 'sickles', 'slings', 'spears')
|
||||
weapon_proficiencies = (weapons.Club, weapons.Dagger, weapons.Dart,
|
||||
weapons.Javelin, weapons.Mace,
|
||||
weapons.Quarterstaff, weapons.Scimitar,
|
||||
weapons.Sickle, weapons.Sling, weapons.Spear)
|
||||
class_skill_choices = ('Arcana', 'Animal Handling', 'Insight',
|
||||
'Medicine', 'Nature', 'Perception', 'Religion',
|
||||
'Survival')
|
||||
spell_slots_by_level = {
|
||||
1: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (3, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (3, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (4, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (4, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (4, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (4, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (4, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (4, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (4, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (4, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
if subclass is not None:
|
||||
sc = str(subclass).lower()
|
||||
if 'moon' in sc:
|
||||
self.circle = 'moon'
|
||||
params.pop('circle', '')
|
||||
elif 'land' in sc:
|
||||
self.circle = 'land'
|
||||
params.pop('circle', '')
|
||||
super().__init__(level, **params)
|
||||
|
||||
@property
|
||||
def all_wild_shapes(self):
|
||||
"""Return all wild shapes, regardless of validity."""
|
||||
return self._wild_shapes
|
||||
|
||||
@property
|
||||
def wild_shapes(self):
|
||||
"""Return a list of valid wild shapes for this Druid."""
|
||||
valid_shapes = []
|
||||
for shape in self._wild_shapes:
|
||||
# Check if shape can be transformed into
|
||||
if self.can_assume_shape(shape):
|
||||
valid_shapes.append(shape)
|
||||
return valid_shapes
|
||||
|
||||
@wild_shapes.setter
|
||||
def wild_shapes(self, new_shapes):
|
||||
actual_shapes = []
|
||||
# Retrieve the actual monster classes if possible
|
||||
for shape in new_shapes:
|
||||
if isinstance(shape, monsters.Monster):
|
||||
# Already a monster shape so just add it as is
|
||||
new_shape = shape
|
||||
else:
|
||||
# Not already a monster so see if we can find one
|
||||
try:
|
||||
NewMonster = findattr(monsters, shape)
|
||||
new_shape = NewMonster()
|
||||
except AttributeError:
|
||||
msg = f'Wild shape "{shape}" not found. Please add it to ``monsters.py``'
|
||||
raise exceptions.MonsterError(msg)
|
||||
actual_shapes.append(new_shape)
|
||||
# Save the updated list for later
|
||||
self._wild_shapes = actual_shapes
|
||||
|
||||
def can_assume_shape(self, shape: monsters.Monster)-> bool:
|
||||
"""Determine if a given shape meets the requirements for transforming.
|
||||
|
||||
See Pg 66 of player's handbook.
|
||||
|
||||
Parameters
|
||||
==========
|
||||
shape
|
||||
A monster that the Druid wishes to transform into.
|
||||
|
||||
Returns
|
||||
=======
|
||||
can_assume
|
||||
True if the monster meets the C/R, swim and flying speed
|
||||
restrictions.
|
||||
|
||||
"""
|
||||
# Determine acceptable states based on druid level
|
||||
if self.class_level < 2:
|
||||
max_cr = -1
|
||||
max_swim = 0
|
||||
max_fly = 0
|
||||
elif self.class_level < 4:
|
||||
max_cr = 1/4
|
||||
max_swim = 0
|
||||
max_fly = 0
|
||||
elif self.class_level < 8:
|
||||
max_cr = 1/2
|
||||
max_swim = None
|
||||
max_fly = 0
|
||||
else:
|
||||
max_cr = 1
|
||||
max_swim = None
|
||||
max_fly = None
|
||||
# Make adjustments for moon circle druids
|
||||
if self.circle.lower() == "moon":
|
||||
if 2 <= self.class_level < 6:
|
||||
max_cr = 1
|
||||
elif self.class_level >= 6:
|
||||
max_cr = math.floor(self.class_level / 3)
|
||||
# Check if the beast shape can be assumed
|
||||
valid_cr = (max_cr is None or shape.challenge_rating <= max_cr)
|
||||
valid_swim = (max_swim is None or shape.swim_speed <= max_swim)
|
||||
valid_fly = (max_fly is None or shape.fly_speed <= max_fly)
|
||||
can_assume = shape.is_beast and valid_cr and valid_swim and valid_fly
|
||||
return can_assume
|
||||
|
||||
@property
|
||||
def spells(self):
|
||||
return tuple(S() for S in self.spells_prepared)
|
||||
|
||||
@spells.setter
|
||||
def spells(self, val):
|
||||
if len(val) > 0:
|
||||
warnings.warn("Druids cannot learn spells, "
|
||||
"use ``spells_prepared`` instead.",
|
||||
RuntimeWarning)
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Fighter(CharClass):
|
||||
class_name = 'Fighter'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ('strength', 'constitution')
|
||||
_proficiencies_text = ('All armor', 'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
multiclass_weapon_proficiencies = weapon_proficiencies
|
||||
_multiclass_proficiencies_text = ('light armor', 'medium armor',
|
||||
'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
class_skill_choices = ('Acrobatics', 'Animal Handling',
|
||||
'Athletics', 'History', 'Insight', 'Intimidation',
|
||||
'Perception', 'Survival')
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
__all__ = ('Monk')
|
||||
|
||||
from .. import (features, weapons)
|
||||
from .classes import CharClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class Monk(CharClass):
|
||||
class_name = 'Monk'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('strength', 'dexterity')
|
||||
_proficiencies_text = (
|
||||
'simple weapons', 'shortswords', 'unarmed',
|
||||
"one type of artisan's tools or one musical instrument")
|
||||
weapon_proficiencies = (weapons.Shortsword, weapons.Unarmed) + weapons.simple_weapons
|
||||
class_skill_choices = ('Acrobatics', 'Athletics', 'History', 'Insight',
|
||||
'Religion', 'Stealth')
|
||||
|
||||
features_by_level = defaultdict(list)
|
||||
martial_arts = features.MartialArts()
|
||||
features_by_level[1] = [features.UnarmoredDefense(),
|
||||
martial_arts]
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
super().__init__(level, subclass=subclass, **params)
|
||||
self.martial_arts.level = self.class_level
|
||||
if subclass == 'sunsoul':
|
||||
self.subclass = SunSoul(level=self.class_level)
|
||||
else:
|
||||
self.subclass = None
|
||||
if self.subclass is not None:
|
||||
self._proficiencies_text += self.subclass._proficiencies_text
|
||||
self.weapon_proficiences += self.subclass.weapon_proficiencies
|
||||
|
||||
|
||||
class SunSoul:
|
||||
class_features_by_level = defaultdict(list)
|
||||
weapon_proficiencies = ()
|
||||
_profiencies_text = ()
|
||||
|
||||
def __init__(self, level):
|
||||
self.class_level = level
|
||||
@@ -0,0 +1,38 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Paladin(CharClass):
|
||||
class_name = 'Paladin'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ('All armor', 'shields', 'simple weapons',
|
||||
'martial weapons')
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
class_skill_choices = ("Athletics", 'Insight', 'Intimidation',
|
||||
'Medicine', 'Persuasion', 'Religion')
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
7: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
9: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
10: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
11: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
12: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
13: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
14: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
15: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
16: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
17: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
18: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
19: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
20: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
__all__ = ('Ranger', 'Revisedranger')
|
||||
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class Ranger(CharClass):
|
||||
class_name = 'Ranger'
|
||||
hit_dice_faces = 10
|
||||
saving_throw_proficiencies = ('strength', 'dexterity')
|
||||
_proficiencies_text = ("light armor", "medium armor", "shields",
|
||||
"simple weapons", "martial weapons")
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
class_skill_choices = ('Animal Handling', 'Athletics', 'Insight',
|
||||
'Investigation', 'Nature', 'Perception', 'Stealth',
|
||||
'Survival')
|
||||
num_skill_choices = 3
|
||||
spellcasting_ability = 'wisdom'
|
||||
features_by_level = defaultdict(list)
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
7: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
9: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
10: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
11: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
12: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
13: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
14: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
15: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
16: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
17: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
18: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
19: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
20: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
super().__init__(level, subclass=subclass, **params)
|
||||
fighting_style = features.select_ranger_fighting_style(
|
||||
feature_choices=params.get('feature_choices', []))
|
||||
self.features_by_level[2].append(fighting_style)
|
||||
|
||||
|
||||
# Custom Classes
|
||||
class Revisedranger(Ranger):
|
||||
class_name = 'Revised Ranger'
|
||||
@@ -0,0 +1,20 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Rogue(CharClass):
|
||||
class_name = 'Rogue'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('dexterity', 'intelligence')
|
||||
_proficiencies_text = (
|
||||
'light armor', 'simple weapons', 'hand crossbows', 'longswords',
|
||||
'rapiers', 'shortswords', "thieves' tools")
|
||||
weapon_proficiencies = weapons.simple_weapons + (
|
||||
weapons.HandCrossbow, weapons.Longsword, weapons.Rapier,
|
||||
weapons.Shortsword)
|
||||
class_skill_choices = ('Acrobatics', 'Athletics', 'Deception',
|
||||
'Insight', 'Intimidation', 'Investigation',
|
||||
'Perception', 'Performance', 'Persuasion',
|
||||
'Sleight of Hand', 'Stealth')
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Sorceror(CharClass):
|
||||
class_name = 'Sorceror'
|
||||
hit_dice_faces = 6
|
||||
saving_throw_proficiencies = ('constitution', 'charisma')
|
||||
_proficiencies_text = ('daggers', 'darts', 'slings',
|
||||
'quarterstaffs', 'light crossbows')
|
||||
weapon_proficiencies = (weapons.Dagger, weapons.Dart,
|
||||
weapons.Sling, weapons.Quarterstaff,
|
||||
weapons.LightCrossbow)
|
||||
class_skill_choices = ('Arcana', 'Deception', 'Insight',
|
||||
'Intimidation', 'Persuasion', 'Religion')
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (4, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (4, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (4, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (5, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (5, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (5, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (5, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (5, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (5, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (6, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (6, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (6, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (6, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (6, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (6, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (6, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (6, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (6, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (6, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (6, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Warlock(CharClass):
|
||||
class_name = 'Warlock'
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('wisdom', 'charisma')
|
||||
_proficiencies_text = ("light Armor", "simple weapons")
|
||||
class_skill_choices = ('Arcana', 'Deception', 'History',
|
||||
'Intimidation', 'Investigation', 'Nature',
|
||||
'Religion')
|
||||
weapon_proficiencies = weapons.simple_weapons
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
1: (2, 1, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 0, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 0, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 0, 0, 3, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 0, 0, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 0, 0, 0, 2, 0, 0, 0, 0, 0),
|
||||
8: (3, 0, 0, 0, 2, 0, 0, 0, 0, 0),
|
||||
9: (3, 0, 0, 0, 0, 2, 0, 0, 0, 0),
|
||||
10: (4, 0, 0, 0, 0, 2, 0, 0, 0, 0),
|
||||
11: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
12: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
13: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
14: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
15: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
16: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
|
||||
17: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
18: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
19: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
20: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
|
||||
|
||||
class Wizard(CharClass):
|
||||
class_name = 'Wizard'
|
||||
hit_dice_faces = 6
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
_proficiencies_text = ('daggers', 'darts', 'slings',
|
||||
'quarterstaffs', 'light crossbows')
|
||||
weapon_proficiencies = (weapons.Dagger, weapons.Dart,
|
||||
weapons.Sling, weapons.Quarterstaff,
|
||||
weapons.LightCrossbow)
|
||||
class_skill_choices = ('Arcana', 'History', 'Investigation',
|
||||
'Medicine', 'Religion')
|
||||
spellcasting_ability = 'intelligence'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (3, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (5, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (5, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (5, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (5, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (5, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
from . import weapons, armor
|
||||
|
||||
|
||||
class Feature():
|
||||
"""
|
||||
Provide full text of rules in documentation
|
||||
"""
|
||||
name = "Generic Feature"
|
||||
source = '' # race, class, background, etc.
|
||||
|
||||
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
|
||||
"""
|
||||
Return the attack/damage bonus from having this feature
|
||||
|
||||
Parameters
|
||||
----------
|
||||
weapon
|
||||
The weapon to be tested for special bonuses
|
||||
kwargs
|
||||
Any other key-word arguments the function may require
|
||||
|
||||
Returns
|
||||
-------
|
||||
attack bonus : integer attack bonus
|
||||
damage bonus : integer attack bonus
|
||||
|
||||
"""
|
||||
return (0, 0)
|
||||
|
||||
def AC_func(self, char, **kwargs):
|
||||
"""
|
||||
Return the alternative AC from having this feat
|
||||
|
||||
The character will take max AC from all available feats / standard AC,
|
||||
so the default is to output very low AC
|
||||
|
||||
Parameters
|
||||
----------
|
||||
char
|
||||
Character object, to check for necessary abilities, etc.
|
||||
kwargs
|
||||
Any other key-word arguments the function may require
|
||||
|
||||
Returns
|
||||
-------
|
||||
AC : integer armor class from this feature
|
||||
"""
|
||||
return -100
|
||||
|
||||
|
||||
class Archery(Feature):
|
||||
"""
|
||||
You gain a +2 bonus to attack rolls you make
|
||||
with ranged weapons.
|
||||
"""
|
||||
name = "Archery"
|
||||
source = 'Revised Ranger'
|
||||
|
||||
def weapon_func(self, weapon: weapons.Weapon):
|
||||
"""
|
||||
+2 attack roll bonus if weapon is ranged
|
||||
"""
|
||||
return (2, 0) if weapon.is_ranged else (0, 0)
|
||||
|
||||
|
||||
class UnarmoredDefense(Feature):
|
||||
"""Beginning at 1st level, while you are wearing no armor and not wearing a
|
||||
shield, your AC equals 10 + your Dexterity modifier + your Wisdom modifier.
|
||||
|
||||
"""
|
||||
name = "Unarmored Defense"
|
||||
source = "Monk"
|
||||
|
||||
def AC_func(self, char):
|
||||
no_armor = ((char.armor is None)
|
||||
or (isinstance(char.armor, armor.NoAmor)))
|
||||
no_shield = ((char.shield is None)
|
||||
or (isinstance(char.shield, armor.NoShield)))
|
||||
if no_armor and no_shield:
|
||||
return 10 + char.dexterity.modifier + char.wisdom.modifier
|
||||
else:
|
||||
return -100
|
||||
@@ -0,0 +1,16 @@
|
||||
from .features import Feature, create_feature
|
||||
|
||||
from .barbarian import *
|
||||
from .bard import *
|
||||
from .cleric import *
|
||||
from .druid import *
|
||||
from .fighter import *
|
||||
from .monk import *
|
||||
from .paladin import *
|
||||
from .ranger import *
|
||||
from .rogue import *
|
||||
from .sorceror import *
|
||||
from .warlock import *
|
||||
from .wizard import *
|
||||
from .races import *
|
||||
from .backgrounds import *
|
||||
@@ -0,0 +1,80 @@
|
||||
from .. import weapons
|
||||
|
||||
|
||||
def create_feature(**params):
|
||||
"""Create a new subclass of ``Feature`` with given default parameters.
|
||||
|
||||
Useful for features that haven't been entered into the ``features.py``
|
||||
file yet.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
params : optional
|
||||
Saved as attributes of the new class.
|
||||
|
||||
Returns
|
||||
-------
|
||||
NewFeature
|
||||
New feature class, subclass of ``Feature``, with given params.
|
||||
"""
|
||||
NewFeature = type('UnknownFeature', (Feature,), params)
|
||||
return NewFeature
|
||||
|
||||
|
||||
class Feature():
|
||||
"""
|
||||
Provide full text of rules in documentation
|
||||
"""
|
||||
name = "Generic Feature"
|
||||
source = '' # race, class, background, etc.
|
||||
needs_implementation = False # Set to True if need to find way to compute stats
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.name == other.name) and (self.source == other.source)
|
||||
|
||||
def __hash__(self):
|
||||
return 0
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
|
||||
"""
|
||||
Updates weapon based on the Feature property
|
||||
|
||||
Parameters
|
||||
----------
|
||||
weapon
|
||||
The weapon to be tested for special bonuses
|
||||
kwargs
|
||||
Any other key-word arguments the function may require
|
||||
|
||||
Returns
|
||||
-------
|
||||
weapon
|
||||
Updated weapon (perhaps changed damage bonus, etc.)
|
||||
|
||||
"""
|
||||
return weapon
|
||||
|
||||
def AC_func(self, char, **kwargs):
|
||||
"""
|
||||
Return the alternative AC from having this feat
|
||||
|
||||
The character will take max AC from all available feats / standard AC,
|
||||
so the default is to output very low AC
|
||||
|
||||
Parameters
|
||||
----------
|
||||
char
|
||||
Character object, to check for necessary abilities, etc.
|
||||
kwargs
|
||||
Any other key-word arguments the function may require
|
||||
|
||||
Returns
|
||||
-------
|
||||
AC : integer armor class from this feature
|
||||
"""
|
||||
return -100
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
from .features import Feature
|
||||
from .. import (weapons, armor)
|
||||
|
||||
|
||||
class UnarmoredDefense(Feature):
|
||||
"""Beginning at 1st level, while you are wearing no armor and not wearing a
|
||||
shield, your AC equals 10 + your Dexterity modifier + your Wisdom modifier.
|
||||
|
||||
"""
|
||||
name = "Unarmored Defense"
|
||||
source = 'Monk'
|
||||
|
||||
def AC_func(self, char):
|
||||
no_armor = ((char.armor is None)
|
||||
or (isinstance(char.armor, armor.NoAmor)))
|
||||
no_shield = ((char.shield is None)
|
||||
or (isinstance(char.shield, armor.NoShield)))
|
||||
if no_armor and no_shield:
|
||||
return 10 + char.dexterity.modifier + char.wisdom.modifier
|
||||
else:
|
||||
return -100
|
||||
|
||||
|
||||
class MartialArts(Feature):
|
||||
"""At 1st level, your practice of martial arts gives you mastery of combat
|
||||
styles that use unarmed strikes and monk weapons, which are shortswords and
|
||||
any simple melee weapons that don’t have the two-handed or heavy
|
||||
property. You gain the following benefits while you are unarmed or wielding
|
||||
only monk weapons and you aren’t wearing armor or wielding a shield:
|
||||
|
||||
-- You can use Dexterity instead of Strength for the attack and damage rolls
|
||||
of your unarmed strikes and monk weapons.
|
||||
|
||||
-- You can roll a d4 in place of the normal damage of your unarmed strike or
|
||||
monk weapon. This die changes as you gain monk levels, as shown in the
|
||||
Martial Arts column of the Monk table.
|
||||
|
||||
-- When you use the Attack action with an unarmed strike or a monk weapon on
|
||||
your turn, you can make one unarmed strike as a bonus action. For example,
|
||||
if you take the Attack action and attack with a quarter- staff, you can
|
||||
also make an unarmed strike as a bonus action, assuming you haven't already
|
||||
taken a bonus action this turn.
|
||||
|
||||
Certain monasteries use specializepd forms of the monk weapons. For
|
||||
example, you might use a club that is two lengths of w ood connected by a
|
||||
short chain (called a nunchaku) or a sickle with a shorter, straighter
|
||||
blade (called a kama). Whatever name you use for a monk weapon, you can use
|
||||
the game statistics provided for
|
||||
|
||||
"""
|
||||
name = "Martial Arts"
|
||||
source = 'Monk'
|
||||
die_by_level = {1: 'd4', 2: 'd4', 3: 'd4', 4: 'd4',
|
||||
5: 'd6', 6: 'd6', 7: 'd6', 8: 'd6',
|
||||
9: 'd6', 10: 'd6', 11: 'd8', 12: 'd8',
|
||||
13: 'd8', 14: 'd8', 15: 'd8', 16: 'd8',
|
||||
17: 'd10', 18: 'd10', 19: 'd10', 20: 'd10'}
|
||||
level = 1
|
||||
|
||||
def weapon_func(self, weapon: weapons.Weapon, char=None, **kwargs):
|
||||
"""
|
||||
Update increasing damage dice and DEX mod of Monk weapons
|
||||
"""
|
||||
is_monk_weapon = any([isinstance(weapon, w)
|
||||
for w in weapons.monk_weapons])
|
||||
if not is_monk_weapon:
|
||||
return weapon
|
||||
if char is None:
|
||||
return weapon
|
||||
# check if new damage is better than default
|
||||
new_die = int(self.die_by_level[self.level].strip('d'))
|
||||
if new_die > int(weapon.base_damage.split('d')[-1]):
|
||||
weapon.base_damage = '1d' + str(new_die)
|
||||
weapon.is_finesse = True
|
||||
return weapon
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
from .features import Feature
|
||||
from .. import (weapons, armor)
|
||||
|
||||
|
||||
def select_ranger_fighting_style(feature_choices=[]):
|
||||
lower_choices = [fc for fc in map(str.lower, feature_choices)]
|
||||
if 'archery' in lower_choices:
|
||||
return Archery()
|
||||
elif 'defense' in lower_choices:
|
||||
return Defense()
|
||||
elif 'dueling' in lower_choices:
|
||||
return Dueling()
|
||||
elif 'two-weapon fighting' in lower_choices:
|
||||
return TwoWeaponFighting()
|
||||
else:
|
||||
return RangerFightingStyle()
|
||||
|
||||
|
||||
class RangerFightingStyle(Feature):
|
||||
"""
|
||||
Select a Fighting Style by choosing in feature_choices:
|
||||
|
||||
archery
|
||||
|
||||
defense
|
||||
|
||||
dueling
|
||||
|
||||
two-weapon fighting
|
||||
"""
|
||||
name = "Fighting Style (Select One)"
|
||||
source = "Ranger"
|
||||
|
||||
|
||||
class Archery(Feature):
|
||||
"""
|
||||
You gain a +2 bonus to attack rolls you make
|
||||
with ranged weapons.
|
||||
"""
|
||||
name = "Fighting Style (Archery)"
|
||||
source = "Ranger"
|
||||
|
||||
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
|
||||
"""
|
||||
+2 attack roll bonus if weapon is ranged
|
||||
"""
|
||||
if weapon.is_ranged:
|
||||
weapon.attack_bonus += 2
|
||||
return weapon
|
||||
|
||||
|
||||
class Defense(Feature):
|
||||
"""
|
||||
While you are wearing armor, you gain a +1 bonus to AC.
|
||||
"""
|
||||
name = "Fighting Style (Defense)"
|
||||
source = "Ranger"
|
||||
|
||||
def AC_func(self, char, **kwargs):
|
||||
"""
|
||||
Apply a +1 bonus if wearing armor
|
||||
"""
|
||||
if (char.armor is None) or (isinstance(char.armor, armor.NoArmor)):
|
||||
return char.default_AC
|
||||
else:
|
||||
return char.default_AC + 1
|
||||
|
||||
|
||||
class Dueling(Feature):
|
||||
"""When you are wielding a melee weapon in one hand and no other weapons, you
|
||||
gain a +2 bonus to damage rolls with that weapon.
|
||||
|
||||
"""
|
||||
name = "Fighting Style (Dueling)"
|
||||
source = "Ranger"
|
||||
needs_implementation = True
|
||||
|
||||
|
||||
class TwoWeaponFighting(Feature):
|
||||
"""When you engage in two-weapon fighting, you can add your ability modifier
|
||||
to the damage of the second attack.
|
||||
|
||||
"""
|
||||
name = "Fighting Style (Two-Weapon Fighting)"
|
||||
source = "Ranger"
|
||||
needs_implementation = True
|
||||
@@ -16,10 +16,14 @@
|
||||
[% for feat in character.features %]
|
||||
|
||||
\section*{[[ feat.name ]]}
|
||||
|
||||
|
||||
\noindent
|
||||
\textbf{Source:} [[ feat.source ]] \\
|
||||
|
||||
[% if feat.needs_implementation %] %
|
||||
\textbf{**Not included in stats on Character Sheet} %
|
||||
[% endif %] %
|
||||
|
||||
[[ feat.__doc__|rst_to_latex ]]
|
||||
|
||||
[% endfor %]
|
||||
|
||||
@@ -37,6 +37,9 @@ def rst_to_latex(rst):
|
||||
tex = it_re.sub(r'\\textit{\1}', tex)
|
||||
tex = dice_re.sub(r'\\texttt{\1}', tex)
|
||||
tex = tt_re.sub(r'\\texttt{\1}', tex)
|
||||
for c in ['\\', '#', '$', '%', '&', '~', '_', '^',
|
||||
'{', '}', '(', ')', '[', ']']:
|
||||
tex = tex.replace(c, '\\' + c)
|
||||
return tex
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
from .spells import Spell, create_spell
|
||||
from .spells_a_d import *
|
||||
from .spells_e_i import *
|
||||
from .spells_j_m import *
|
||||
from .spells_n_r import *
|
||||
from .spells_s_u import *
|
||||
from .spells_v_z import *
|
||||
@@ -0,0 +1,60 @@
|
||||
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
|
||||
name = "Unknown spell"
|
||||
casting_time = "1 action"
|
||||
casting_range = "60 ft"
|
||||
components = ()
|
||||
materials = ""
|
||||
duration = "instantaneous"
|
||||
ritual = False
|
||||
magic_school = ""
|
||||
classes = ()
|
||||
|
||||
def __str__(self):
|
||||
s = self.name + ' ({:s}) '.format(','.join(self.components))
|
||||
# Indicate if this is a ritual or a concentration
|
||||
indicators = [('R', self.ritual), ('C', self.concentration), ('$', self.special_material)]
|
||||
indicators = tuple(letter for letter, is_active in indicators if is_active)
|
||||
if len(indicators):
|
||||
s += f' ({", ".join(indicators)})'
|
||||
return s
|
||||
|
||||
def __repr__(self):
|
||||
return f'<{self.name}>'
|
||||
|
||||
@property
|
||||
def component_string(self):
|
||||
s = f'{", ".join(self.components)}'
|
||||
if "M" in self.components:
|
||||
s += f' ({self.materials})'
|
||||
return s
|
||||
|
||||
@property
|
||||
def concentration(self):
|
||||
return ('concentration' in self.duration.lower())
|
||||
|
||||
@property
|
||||
def special_material(self):
|
||||
return ('worth at least' in self.materials.lower())
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,546 @@
|
||||
from .spells import Spell
|
||||
|
||||
|
||||
class Knock(Spell):
|
||||
"""Choose an object that you can see within range. The object can be a
|
||||
door, a box, a chest, a set of manacles, a padlock, or another
|
||||
object that contains a mundane or magical means that prevents
|
||||
access. A target that is held shut by a mundane lock or that is
|
||||
stuck or barred becomes unlocked, unstuck, or unbarred. If the
|
||||
object has multiple locks, only one of them is unlocked. If you
|
||||
choose a target that is held shut with arcane lock, that spell is
|
||||
suppressed for 10 minutes, during which time the target can be
|
||||
opened and shut normally. When you cast the spell, a loud knock,
|
||||
audible from as far away as 300 feet, emanates from the target
|
||||
object.
|
||||
|
||||
"""
|
||||
name = "Knock"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class LesserRestoration(Spell):
|
||||
"""You touch a creature and can end either one disease or one
|
||||
condition afflicting it. The condition can be blinded, deafened,
|
||||
paralyzed, or poisoned.
|
||||
|
||||
"""
|
||||
name = "Lesser Restoration"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Levitate(Spell):
|
||||
"""One creature or object of your choice that you can see within range
|
||||
rises vertically, up to 20 feet, and remains suspended there for
|
||||
the duration. The spell can levitate a target that weighs up to
|
||||
500 pounds. An unwilling creature that succeeds on a Constitution
|
||||
saving throw is unaffected. The target can move only by pushing or
|
||||
pulling against a fixed object or surface within reach (such as a
|
||||
wall or a ceiling), which allows it to move as if it were
|
||||
climbing. You can change the target’s altitude by up to 20 feet in
|
||||
either direction on your turn. If you are the target, you can move
|
||||
up or down as part of your move. Otherwise, you can use your
|
||||
action to move the target, which must remain within the spell’s
|
||||
range. When the spell ends, the target floats gently to the ground
|
||||
if it is still aloft.
|
||||
|
||||
"""
|
||||
name = "Levitate"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "either a small leather loop or a piece of golden wire bent into a cup shape with a long shank on one end"
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Light(Spell):
|
||||
"""You touch one object that is no larger than 10 feet in any
|
||||
dimension. Until the spell ends, the object sheds bright light in
|
||||
a 20-foot radius and dim light for an additional 20 feet. The
|
||||
light can be colored as you like. Completely covering the object
|
||||
with something opaque blocks the light. The spell ends if you cast
|
||||
it again or dismiss it as an action. If you target an object held
|
||||
or worn by a hostile creature, that creature must succeed on a
|
||||
Dexterity saving throw to avoid the spell.
|
||||
|
||||
"""
|
||||
name = "Light"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'M')
|
||||
materials = "a firefly or phosphorescent moss"
|
||||
duration = "1 hour"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class LightningBolt(Spell):
|
||||
"""A stroke of lightning forming a line 100 feet long and 5 feet wide
|
||||
blasts out from you in a direction you choose. Each creature in
|
||||
the line must make a Dexterity saving throw. A creature takes 8d6
|
||||
lightning damage on a failed save, or half as much damage on a
|
||||
successful one. The lightning ignites flammable objects in the
|
||||
area that aren’t being worn or carried. 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 = "Lightning Bolt"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a bit of fur and a rod of amber, crystal, or glass"
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class LocateCreature(Spell):
|
||||
"""Describe or name a creature that is familiar to you. You sense the
|
||||
direction to the creature’s location, as long as that creature is
|
||||
within 1,000 feet of you. If the creature is moving, you know the
|
||||
direction of its movement. The spell can locate a specific
|
||||
creature known to you, or the nearest creature of a specific kind
|
||||
(such as a human or a unicorn), so long as you have seen such a
|
||||
creature up close—within 30 feet—at least once. If the creature
|
||||
you described or named is in a different form, such as being under
|
||||
the effects of a polymorph spell, this spell doesn’t locate the
|
||||
creature. This spell can’t locate a creature if running water at
|
||||
least 10 feet wide blocks a direct path between you and the
|
||||
creature.
|
||||
|
||||
"""
|
||||
name = "Locate Creature"
|
||||
level = 4
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a bit of fur from a bloodhound"
|
||||
duration = "Concentration, up to 1 hour"
|
||||
magic_school = "Divination"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MageArmor(Spell):
|
||||
"""You touch a willing creature who isn't wearing armor, and a
|
||||
protective magical force surrounds it until the spell ends. The
|
||||
target's base AC becomes 13 + its Dexterity modifier. The spell
|
||||
ends it if the target dons armor or if you dismiss the spell as an
|
||||
action.
|
||||
|
||||
"""
|
||||
name = "Mage Armor"
|
||||
level = 1
|
||||
casting_time = "1 action"
|
||||
casting_range = "Touch"
|
||||
components = ("V", "S", "M")
|
||||
materials = "A piece of cured leather"
|
||||
duration = "8 hours"
|
||||
magic_school = "Abjuration"
|
||||
classes = ('Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
class MageHand(Spell):
|
||||
"""A spectral, floating hand appears at a point you choose within
|
||||
range. The hand lasts for the duration or until you dismiss it as
|
||||
an action. The hand vanishes if it is ever more than 30 feet away
|
||||
from you or if you cast this spell again.
|
||||
|
||||
You can use your action to control the hand. You can use the hand
|
||||
to manipulate an object, open an unlocked door or container, stow
|
||||
or retrieve an item from an open container, or pour the contents
|
||||
out of a vial. You can move the hand up to 30 feet each time you
|
||||
use it.
|
||||
|
||||
The hand can't attack, activate magical items, or carry more than
|
||||
10 pounds.
|
||||
|
||||
"""
|
||||
name = "Mage Hand"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
casting_range = "30 feet"
|
||||
components = ("V", "S", )
|
||||
duration = "1 minute"
|
||||
magic_school = "Conjuration"
|
||||
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
|
||||
deals 1d4+1 force damage to its target. The darts all strike
|
||||
simultaneously and you can direct them to hit one creature or
|
||||
several.
|
||||
|
||||
At Higher Levels: When you cast this spell using a spell slot of
|
||||
2nd level or higher, the spell creates one more dart for each slot
|
||||
above 1st.
|
||||
|
||||
"""
|
||||
name = "Magic Missile"
|
||||
level = 1
|
||||
casting_time = "1 action"
|
||||
casting_range = "120 feet"
|
||||
components = ("V", "S", )
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ('Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
|
||||
class MagicWeapon(Spell):
|
||||
"""You touch a nonmagical weapon. Until the spell ends, that weapon
|
||||
becomes a magic weapon with a +1 bonus to attack rolls and damage
|
||||
rolls. At Higher Levels. When you cast this spell using a spell
|
||||
slot of 4th level or higher, the bonus increases to +2. When you
|
||||
use a spell slot of 6th level or higher, the bonus increases to
|
||||
+3.
|
||||
|
||||
"""
|
||||
name = "Magic Weapon"
|
||||
level = 2
|
||||
casting_time = "1 bonus action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Concentration, up to 1 hour"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
|
||||
class MajorImage(Spell):
|
||||
"""You create the image of an object, a creature, or some other
|
||||
visible phenomenon that is no larger than a 20-foot cube. The
|
||||
image appears at a spot that you can see within range and lasts
|
||||
for the duration. It seems completely real, including sounds,
|
||||
smells, and temperature appropriate to the thing depicted. You
|
||||
can’t create sufficient heat or cold to cause damage, a sound loud
|
||||
enough to deal thunder damage or deafen a creature, or a smell
|
||||
that might sicken a creature (like a troglodyte’s stench). As long
|
||||
as you are within range of the illusion, you can use your action
|
||||
to cause the image to move to any other spot within range. As the
|
||||
image changes location, you can alter its appearance so that its
|
||||
movements appear natural for the image. For example, if you create
|
||||
an image of a creature and move it, you can alter the image so
|
||||
that it appears to be walking. Similarly, you can cause the
|
||||
illusion to make different sounds at different times, even making
|
||||
it carry on a conversation, for example. Physical interaction with
|
||||
the image reveals it to be an illusion, because things can pass
|
||||
through it. A creature that uses its action to examine the image
|
||||
can determine that it is an illusion with a successful
|
||||
Intelligence (Investigation) check against your spell save DC. If
|
||||
a creature discerns the illusion for what it is, the creature can
|
||||
see through the image, and its other sensory qualities become
|
||||
faint to the creature. At Higher Levels. When you cast this spell
|
||||
using a spell slot of 6th level or higher, the spell lasts until
|
||||
dispelled, without requiring your concentration.
|
||||
|
||||
"""
|
||||
name = "Major Image"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a bit of fleece"
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Illusion"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MassCureWounds(Spell):
|
||||
"""A wave of healing energy washes out from a point of your choice
|
||||
within range. Choose up to six creatures in a 30-foot-radius
|
||||
sphere centered on that point. Each target regains hit points
|
||||
equal to 3d8 + your spellcasting ability modifier. This spell has
|
||||
no effect on undead or constructs. At Higher Levels. When you cast
|
||||
this spell using a spell slot of 6th level or higher, the healing
|
||||
increases by 1d8 for each slot level above 5th.
|
||||
|
||||
"""
|
||||
name = "Mass Cure Wounds"
|
||||
level = 5
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MassHeal(Spell):
|
||||
"""A flood of healing energy flows from you into injured creatures
|
||||
around you. You restore up to 700 hit points, divided as you
|
||||
choose among any number of creatures that you can see within
|
||||
range. Creatures healed by this spell are also cured of all
|
||||
diseases and any effect making them blinded or deafened. This
|
||||
spell has no effect on undead or constructs.
|
||||
|
||||
"""
|
||||
name = "Mass Heal"
|
||||
level = 9
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MassHealingWord(Spell):
|
||||
"""As you call out words of restoration, up to six creatures of your
|
||||
choice that you can see within range regain hit points equal to
|
||||
1d4 + your spellcasting ability modifier. This spell has no effect
|
||||
on undead or constructs. At Higher Levels. When you cast this
|
||||
spell using a spell slot of 4th level or higher, the healing
|
||||
increases by 1d4 for each slot level above 3rd.
|
||||
|
||||
"""
|
||||
name = "Mass Healing Word"
|
||||
level = 3
|
||||
casting_time = "1 bonus action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MassSuggestion(Spell):
|
||||
"""You suggest a course of activity (limited to a sentence or two) and
|
||||
magically influence up to twelve creatures of your choice that you
|
||||
can see within range and that can hear and understand
|
||||
you. Creatures that can’t be charmed are immune to this
|
||||
effect. The suggestion must be worded in such a manner as to make
|
||||
the course of action sound reasonable. Asking the creature to stab
|
||||
itself, throw itself onto a spear, immolate itself, or do some
|
||||
other obviously harmful act automatically negates the effect of
|
||||
the spell. Each target must make a Wisdom saving throw. On a
|
||||
failed save, it pursues the course of action you described to the
|
||||
best of its ability. The suggested course of action can continue
|
||||
for the entire duration. If the suggested activity can be
|
||||
completed in a shorter time, the spell ends when the subject
|
||||
finishes what it was asked to do. You can also specify conditions
|
||||
that will trigger a special activity during the duration. For
|
||||
example, you might suggest that a group of soldiers give all their
|
||||
money to the first beggar they meet. If the condition isn’t met
|
||||
before the spell ends, the activity isn’t performed. If you or any
|
||||
of your companions damage a creature affected by this spell, the
|
||||
spell ends for that creature. At Higher Levels. When you cast this
|
||||
spell using a 7th-level spell slot, the duration is 10 days. When
|
||||
you use an 8th-level spell slot, the duration is 30 days. When you
|
||||
use a 9th-level spell slot, the duration is a year and a day.
|
||||
|
||||
"""
|
||||
name = "Mass Suggestion"
|
||||
level = 6
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'M')
|
||||
materials = "a snake’s tongue and either a bit of honeycomb or a drop of sweet oil"
|
||||
duration = "24 hours"
|
||||
magic_school = "Enchantment"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Maze(Spell):
|
||||
"""You banish a creature that you can see within range into a
|
||||
labyrinthine demiplane. The target remains there for the duration
|
||||
or until it escapes the maze. The target can use its action to
|
||||
attempt to escape. When it does so, it makes a DC 20 Intelligence
|
||||
check. If it succeeds, it escapes, and the spell ends (a minotaur
|
||||
or goristro demon automatically succeeds). When the spell ends,
|
||||
the target reappears in the space it left or, if that space is
|
||||
occupied, in the nearest unoccupied space.
|
||||
|
||||
"""
|
||||
name = "Maze"
|
||||
level = 8
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Conjuration"
|
||||
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
|
||||
centered on each point you choose must make a Dexterity saving
|
||||
throw. The sphere spreads around corners. A creature takes 20d6
|
||||
fire damage and 20d6 bludgeoning damage on a failed save, or half
|
||||
as much damage on a successful one. A creature in the area of more
|
||||
than one fiery burst is affected only once. The spell damages
|
||||
objects in the area and ignites flammable objects that aren’t
|
||||
being worn or carried.
|
||||
|
||||
"""
|
||||
name = "Meteor Swarm"
|
||||
level = 9
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MinorIllusion(Spell):
|
||||
"""You create a sound or an image of an object within range that lasts
|
||||
for the duration. The illusion also ends if you dismiss it as an
|
||||
action or cast this spell again. If you create a sound, its volume
|
||||
can range from a whisper to a scream. It can be your voice,
|
||||
someone else’s voice, a lion’s roar, a beating of drums, or any
|
||||
other sound you choose. The sound continues unabated throughout
|
||||
the duration, or you can make discrete sounds at different times
|
||||
before the spell ends. If you create an image of an object—such as
|
||||
a chair, muddy footprints, or a small chest—it must be no larger
|
||||
than a 5-foot cube. The image can’t create sound, light, smell, or
|
||||
any other sensory effect. Physical interaction with the image
|
||||
reveals it to be an illusion, because things can pass through
|
||||
it. If a creature uses its action to examine the sound or image,
|
||||
the creature can determine that it is an illusion with a
|
||||
successful Intelligence (Investigation) check against your spell
|
||||
save DC. If a creature discerns the illusion for what it is, the
|
||||
illusion becomes faint to the creature.
|
||||
|
||||
"""
|
||||
name = "Minor Illusion"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('S', 'M')
|
||||
materials = "a bit of fleece"
|
||||
duration = "1 minute"
|
||||
magic_school = "Illusion"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MistyStep(Spell):
|
||||
"""Briefly surrounded by silvery mist, you teleport up to 30 feet to
|
||||
an unoccupied space that you can see.
|
||||
|
||||
"""
|
||||
name = "Misty Step"
|
||||
level = 2
|
||||
casting_time = "1 bonus action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class MordenkainensSword(Spell):
|
||||
"""You create a sword-shaped plane of force that hovers within
|
||||
range. It lasts for the duration. When the sword appears, you make
|
||||
a melee spell attack against a target of your choice within 5 feet
|
||||
of the sword. On a hit, the target takes 3d10 force damage. Until
|
||||
the spell ends, you can use a bonus action on each of your turns
|
||||
to move the sword up to 20 feet to a spot you can see and repeat
|
||||
this attack against the same target or a different one.
|
||||
|
||||
"""
|
||||
name = "Mordenkainen's Sword"
|
||||
level = 7
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a miniature platinum sword with a grip and pommel of copper and zinc, worth 250 gp"
|
||||
duration = "Concentration, up to 1 minute"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
@@ -0,0 +1,417 @@
|
||||
from .spells import Spell
|
||||
|
||||
|
||||
class OttosIrresistibleDance(Spell):
|
||||
"""Choose one creature that you can see within range. The target
|
||||
begins a comic dance in place: shuffling, tapping its feet, and
|
||||
capering for the duration. Creatures that can’t be charmed are
|
||||
immune to this spell. A dancing creature must use all its movement
|
||||
to dance without leaving its space and has disadvantage on
|
||||
Dexterity saving throws and attack rolls. While the target is
|
||||
affected by this spell, other creatures have advantage on attack
|
||||
rolls against it. As an action, a dancing creature makes a Wisdom
|
||||
saving throw to regain control of itself. On a successful save,
|
||||
the spell ends.
|
||||
|
||||
"""
|
||||
name = "Otto's Irresistible Dance"
|
||||
level = 6
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Concentration, up to 1 minute"
|
||||
magic_school = "Enchantment"
|
||||
classes = ('Bard', 'Wizard')
|
||||
|
||||
|
||||
class Passwall(Spell):
|
||||
"""A passage appears at a point of your choice that you can see on a
|
||||
wooden, plaster, or stone surface (such as a wall, a ceiling, or a
|
||||
floor) within range, and lasts for the duration. You choose the
|
||||
opening’s dimensions: up to 5 feet wide, 8 feet tall, and 20 feet
|
||||
deep. The passage creates no instability in a structure
|
||||
surrounding it. When the opening disappears, any creatures or
|
||||
objects still in the passage created by the spell are safely
|
||||
ejected to an unoccupied space nearest to the surface on which you
|
||||
cast the spell.
|
||||
|
||||
"""
|
||||
name = "Passwall"
|
||||
level = 5
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a pinch of sesame seeds"
|
||||
duration = "1 hour"
|
||||
magic_school = "Transmutation"
|
||||
classes = ('Wizard',)
|
||||
|
||||
|
||||
class PhantasmalForce(Spell):
|
||||
"""You craft an illusion that takes root in the mind of a creature
|
||||
that you can see within range. The target must make an
|
||||
Intelligence saving throw. On a failed save, you create a
|
||||
phantasmal object, creature or other visible phenomenon of your
|
||||
choice that is no larger than a 10-foot cube and that is
|
||||
perceivable only to the target for the duration. This spell has no
|
||||
effect on undead or constructs.
|
||||
|
||||
The phantasm includes sound, temperature, and other stimuli, also
|
||||
evident only to the creature. The target can use its action to
|
||||
examine the phantasm with an Intelligence (Investigation) check
|
||||
against your spell save DC. If the check succeeds, the target
|
||||
realizes that the phantasm is an illusion, and the spell
|
||||
ends. While a target is affected by the spell, the target treats
|
||||
the phantasm as if it were real. The target rationalizes any
|
||||
illogical outcomes from interacting with the phantasm. For
|
||||
example, a target attempting to walk across a phantasmal bridge
|
||||
that spans a chasm falls once it steps onto the bridge. If the
|
||||
target survives the fall, it still believes that the bridge exists
|
||||
and comes up with some other explanation for its fall-it was
|
||||
pushed, it slipped, or a strong wind might have knocked it off.
|
||||
|
||||
An affected target is so convinced of the phantasm's reality that
|
||||
it can even take damage from the illusion. A phantasm created to
|
||||
appear as a creature can attack the target. Similarly, a phantasm
|
||||
created to appear as fire, a pool of acid, or lava can burn the
|
||||
target. Each round on your turn, the phantasm can deal 1d6 psychic
|
||||
damage to the target if it is in the phantasm's area or within 5
|
||||
feet of the phantasm, provided that the illusion is of a creature
|
||||
or hazard that could logically deal damage, such as by
|
||||
attacking. The target perceives the damage as a type appropriate
|
||||
to the illusion.
|
||||
|
||||
"""
|
||||
name = "Phantasmal Force"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
casting_range = "60 feet"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "A bit of fleece"
|
||||
duration = "Concentration, up to 1 minute"
|
||||
magic_school = "Illusion"
|
||||
classes = ('Bard', 'Sorceror', 'Wizard')
|
||||
|
||||
|
||||
class PoisonSpray(Spell):
|
||||
"""You extend your hand toward a creature you can see within range and
|
||||
project a puff of noxious gas from your palm. The creature must
|
||||
succeed on a Constitution saving throw or take ``1d12`` poison
|
||||
damage. This spell’s damage increases by ``1d12`` when you reach
|
||||
5th level (``2d12``), 11th level (``3d12``), and 17th level
|
||||
(``4d12``).
|
||||
|
||||
"""
|
||||
name = "Poison Spray"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class PowerWordKill(Spell):
|
||||
"""You utter a word of power that can compel one creature you can see
|
||||
within range to die instantly. If the creature you choose has 100
|
||||
hit points or fewer, it dies. Otherwise, the spell has no
|
||||
effect.
|
||||
|
||||
"""
|
||||
name = "Power Word Kill"
|
||||
level = 9
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Enchantment"
|
||||
classes = ('Bard', 'Wizard', 'Sorceror', 'Warlock')
|
||||
|
||||
|
||||
class PowerWordStun(Spell):
|
||||
"""You speak a word of power that can overwhelm the mind of one
|
||||
creature you can see within range, leaving it dumbfounded. If the
|
||||
target has 150 hit points or fewer, it is stunned. Otherwise, the
|
||||
spell has no effect. The stunned target must make a Constitution
|
||||
saving throw at the end of each of its turns. On a successful
|
||||
save, this stunning effect ends.
|
||||
|
||||
"""
|
||||
name = "Power Word Stun"
|
||||
level = 8
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Enchantment"
|
||||
classes = ()
|
||||
|
||||
|
||||
class PrayerOfHealing(Spell):
|
||||
"""Up to six creatures of your choice that you can see within range
|
||||
each regain hit points equal to 2d8 + your spellcasting ability
|
||||
modifier. This spell has no effect on undead or constructs. At
|
||||
Higher Levels. When you cast this spell using a spell slot of 3rd
|
||||
level or higher, the healing increases by 1d8 for each slot level
|
||||
above 2nd.
|
||||
|
||||
"""
|
||||
name = "PrayerOfHealing"
|
||||
level = 2
|
||||
casting_time = "10 minutes"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Prestidigitation(Spell):
|
||||
"""This spell is a minor magical trick that novice spellcasters use
|
||||
for practice. You create one of the following magical effects
|
||||
within range.
|
||||
|
||||
- You create an instantaneous, harmless sensory effect, such as a
|
||||
shower of sparks, a puff of wind, faint musical notes, or an odd
|
||||
odor.
|
||||
- You instantaneously light or snuff out a candle, a torch, or a
|
||||
small campfire.
|
||||
- You instantaneously clean or soil an object no larger than 1
|
||||
cubic foot.
|
||||
- You chill, warm, or flavor up to 1 cubic foot of nonliving
|
||||
material for 1 hour.
|
||||
- You make a color, a small mark, or a symbol appear on an object
|
||||
or a surface for 1 hour.
|
||||
- You create a nonmagical trinket or an illusory image that can
|
||||
fit in your hand and that lasts until the end of your next turn.
|
||||
|
||||
If you cast this spell multiple times, you can have up to three of
|
||||
its non-instantaneous effects active at a time, and you can
|
||||
dismiss such an effect as an action.
|
||||
|
||||
"""
|
||||
name = "Prestidigitation"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
casting_range = "10 feet"
|
||||
components = ("V", "S", )
|
||||
duration = "1 hour"
|
||||
magic_school = "Transmutation"
|
||||
classes = ('Bard', 'Sorceror', 'Warlock', 'Wizard', )
|
||||
|
||||
|
||||
class ProtectionFromEnergy(Spell):
|
||||
"""For the duration, the willing creature you touch has resistance to
|
||||
one damage type of your choice: acid, cold, fire, lightning, or
|
||||
thunder.
|
||||
|
||||
"""
|
||||
name = "Protection from Energy"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Concentration, up to 1 hour"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
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 creature’s 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
|
||||
cures nonmagical diseases that affected the creature at the time
|
||||
it died. This spell doesn’t, however, remove magical diseases,
|
||||
curses, or similar effects; if these aren’t first removed prior to
|
||||
casting the spell, they take effect when the creature returns to
|
||||
life. The spell can’t return an undead creature to life.
|
||||
|
||||
This spell closes all mortal wounds, but it doesn’t 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 = ('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):
|
||||
"""A frigid beam of blue-white light streaks toward a creature within
|
||||
range. Make a ranged spell attack against the target. On a hit, it
|
||||
takes 1d8 cold damage, and its speed is reduced by 10 feet until
|
||||
the start of your next turn.
|
||||
|
||||
The spell's damage increases by 1d8 when you reach 5th level
|
||||
(2d8), 11th level (3d8), and 17th level (4d8).
|
||||
|
||||
"""
|
||||
name = "Ray of Frost"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
casting_range = "60 feet"
|
||||
components = ("V", "S", )
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ('Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
class RayOfSickness(Spell):
|
||||
"""A ray of sickening greenish energy lashes out toward a creature
|
||||
within range. Make a ranged spell attack against the target. On a
|
||||
hit, the target takes 2d8 poison damage and must make a
|
||||
Constitution saving throw. On a failed save, it is also poisoned
|
||||
until the end of your next turn.
|
||||
|
||||
At Higher Levels. When you cast this spell using a spell slot of
|
||||
2nd level or higher, the damage increases by 1d8 for each slot
|
||||
level above 1st.
|
||||
|
||||
"""
|
||||
name = "Ray of Sickness"
|
||||
level = 1
|
||||
casting_time = "1 action"
|
||||
casting_range = "60 feet"
|
||||
components = ("V", "S", )
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Necromancy"
|
||||
classes = ('Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
class Regenerate(Spell):
|
||||
"""You touch a creature and stimulate its natural healing ability. The
|
||||
target regains 4d8 + 15 hit points. For the duration of the spell,
|
||||
the target regains 1 hit point at the start of each of its turns
|
||||
(10 hit points each minute). The target’s severed body members
|
||||
(fingers, legs, tails, and so on), if any, are restored after 2
|
||||
minutes. If you have the severed part and hold it to the stump,
|
||||
the spell instantaneously causes the limb to knit to the stump.
|
||||
|
||||
"""
|
||||
name = "Regenerate"
|
||||
level = 7
|
||||
casting_time = "1 minute"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a prayer wheel and holy water"
|
||||
duration = "1 hour"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class RemoveCurse(Spell):
|
||||
"""At your touch, all curses affecting one creature or object end. If
|
||||
the object is a cursed magic item, its curse remains, but the
|
||||
spell breaks its owner’s attunement to the object so it can be
|
||||
removed or discarded.
|
||||
|
||||
"""
|
||||
name = "Remove Curse"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Resistance(Spell):
|
||||
"""You touch one willing creature. Once before the spell ends, the
|
||||
target can roll a d4 and add the number rolled to one saving throw
|
||||
of its choice. It can roll the die before or after making the
|
||||
saving throw. The spell then ends.
|
||||
|
||||
"""
|
||||
name = "Resistance"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a miniature cloak"
|
||||
duration = "Concentration, up to 1 minute"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Resurrection(Spell):
|
||||
"""You touch a dead creature that has been dead for no more than a
|
||||
century, that didn’t die of old age, and that isn’t undead. If its
|
||||
soul is free and willing, the target returns to life with all its
|
||||
hit points. This spell neutralizes any poisons and cures normal
|
||||
diseases afflicting the creature when it died. It doesn’t,
|
||||
however, remove magical diseases, curses, and the like; if such
|
||||
effects aren’t removed prior to casting the spell, they afflict
|
||||
the target on its return to life. This spell closes all mortal
|
||||
wounds and restores any missing body parts. 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. Casting this spell to restore life to a creature that
|
||||
has been dead for one year or longer taxes you greatly. Until you
|
||||
finish a long rest, you can’t cast spells again, and you have
|
||||
disadvantage on all attack rolls, ability checks, and saving
|
||||
throws.
|
||||
|
||||
"""
|
||||
name = "Resurrection"
|
||||
level = 7
|
||||
casting_time = "1 hour"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a diamond worth at least 1,000 gp, which the spell consumes"
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Necromancy"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Revivify(Spell):
|
||||
"""You touch a creature that has died within the last minute. That
|
||||
creature returns to life with 1 hit point. This spell can’t return
|
||||
to life a creature that has died of old age, nor can it restore
|
||||
any missing body parts.
|
||||
|
||||
"""
|
||||
name = "Revivify"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "diamonds worth 300 gp, which the spell consumes"
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
@@ -0,0 +1,664 @@
|
||||
from .spells import Spell
|
||||
|
||||
|
||||
class SacredFlame(Spell):
|
||||
"""Flame-like radiance descends on a creature that you can see within
|
||||
range. The target must succeed on a Dexterity saving throw or take
|
||||
1d8 radiant damage. The target gains no benefit from cover for
|
||||
this saving throw. The spell’s damage increases by 1d8 when you
|
||||
reach 5th level (2d8), 11th level (3d8), and 17th level (4d8).
|
||||
|
||||
"""
|
||||
name = "Sacred Flame"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Sanctuary(Spell):
|
||||
"""You ward a creature within range against attack. Until the spell
|
||||
ends, any creature who targets the warded creature with an attack
|
||||
or a harmful spell must first make a Wisdom saving throw. On a
|
||||
failed save, the creature must choose a new target or lose the
|
||||
attack or spell. This spell doesn’t protect the warded creature
|
||||
from area effects, such as the explosion of a fireball. If the
|
||||
warded creature makes an attack or casts a spell that affects an
|
||||
enemy creature, this spell ends.
|
||||
|
||||
"""
|
||||
name = "Sanctuary"
|
||||
level = 1
|
||||
casting_time = "1 bonus action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a small silver mirror"
|
||||
duration = "1 minute"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Shillelagh(Spell):
|
||||
"""The wood of a club or quarterstaff you are holding is imbued with
|
||||
nature's power. For the duration, you can use your spellcasting
|
||||
ability instead of Strength for the attack and damage rolls of
|
||||
melee attacks using that weapon, and the weapon's damage die
|
||||
becomes a ``d8``. The weapon also becomes magical, if it isn't
|
||||
already. The spell ends if you cast it again or if you let go of
|
||||
the weapon.
|
||||
|
||||
"""
|
||||
level = 0
|
||||
name = "Shillelagh"
|
||||
casting_time = "1 bonus action"
|
||||
casting_range = "Touch"
|
||||
components = ("V", "S", "M")
|
||||
materials = "mistletoe, a shamrock leaf, and a club or quarterstaff"
|
||||
duration = "1 minute"
|
||||
ritual = False
|
||||
magic_school = "Transmutation"
|
||||
classes = ('Druid')
|
||||
|
||||
|
||||
class Shatter(Spell):
|
||||
"""A sudden loud ringing noise, painfully intense, erupts from a point
|
||||
of your choice within range. Each creature in a 10-foot-radius
|
||||
sphere centered on that point must make a Constitution saving
|
||||
throw. A creature takes 3d8 thunder damage on a failed save, or
|
||||
half as much damage on a successful one. A creature made of
|
||||
inorganic material such as stone, crystal, or metal has
|
||||
disadvantage on this saving throw. A nonmagical object that isn’t
|
||||
being worn or carried also takes the damage if it’s in the spell’s
|
||||
area. At Higher Levels. When you cast this spell using a spell
|
||||
slot of 3rd level or higher, the damage increases by 1d8 for each
|
||||
slot level above 2nd.
|
||||
|
||||
"""
|
||||
name = "Shatter"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a chip of mica"
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Shield(Spell):
|
||||
"""An invisible barrier of magical force appears and protects
|
||||
you. Until the start of your next turn, you have a +5 bonus to AC,
|
||||
including against the triggering attack, and you take no damage
|
||||
from magic missile.
|
||||
|
||||
"""
|
||||
name = "Shield"
|
||||
level = 1
|
||||
casting_time = "1 reaction"
|
||||
casting_range = "Self"
|
||||
components = ("V", "S", )
|
||||
duration = "1 round"
|
||||
magic_school = "Abjuration"
|
||||
classes = ('Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
class ShieldOfFaith(Spell):
|
||||
"""A shimmering field appears and surrounds a creature of your choice
|
||||
within range, granting it a +2 bonus to AC for the duration.
|
||||
|
||||
"""
|
||||
name = "Shield of Faith"
|
||||
level = 1
|
||||
casting_time = "1 bonus action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a small parchment with a bit of holy text written on it"
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class ShockingGrasp(Spell):
|
||||
"""Lightning springs from your hand to deliver a shock to a creature
|
||||
you try to touch. Make a melee spell attack against the
|
||||
target. You have advantage on the attack roll if the target is
|
||||
wearing armor made of metal. On a hit, the target takes 1d8
|
||||
lightning damage, and it can't take reactions until the start of
|
||||
its next turn.
|
||||
|
||||
The spell's damage increases by 1d8 when you reach 5th level
|
||||
(2d8), 11th level (3d8), and 17th level (4d8).
|
||||
|
||||
"""
|
||||
name = "Shocking Grasp"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
casting_range = "Touch"
|
||||
components = ("V", "S", )
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ('Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
class Silence(Spell):
|
||||
"""For the duration, no sound can be created within or pass through a
|
||||
20-foot-radius sphere centered on a point you choose within
|
||||
range. Any creature or object entirely inside the sphere is immune
|
||||
to thunder damage, and creatures are deafened while entirely
|
||||
inside it. Casting a spell that includes a verbal component is
|
||||
impossible there.
|
||||
|
||||
"""
|
||||
name = "Silence"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Illusion"
|
||||
classes = ()
|
||||
|
||||
|
||||
class SilentImage(Spell):
|
||||
"""You create the image of an object, a creature, or some other
|
||||
visible phenomenon that is no larger than a 15-foot cube. The
|
||||
image appears at a spot within range and lasts for the
|
||||
duration. The image is purely visual; it isn’t accompanied by
|
||||
sound, smell, or other sensory effects. You can use your action to
|
||||
cause the image to move to any spot within range. As the image
|
||||
changes location, you can alter its appearance so that its
|
||||
movements appear natural for the image. For example, if you create
|
||||
an image of a creature and move it, you can alter the image so
|
||||
that it appears to be walking. Physical interaction with the image
|
||||
reveals it to be an illusion, because things can pass through
|
||||
it. A creature that uses its action to examine the image can
|
||||
determine that it is an illusion with a successful Intelligence
|
||||
(Investigation) check against your spell save DC. If a creature
|
||||
discerns the illusion for what it is, the creature can see through
|
||||
the image.
|
||||
|
||||
"""
|
||||
name = "Silent Image"
|
||||
level = 1
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a bit of fleece"
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Illusion"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Sleep(Spell):
|
||||
"""This spell sends creatures into a magical slumber. Roll 5d8, the
|
||||
total is how many hit points of creatures this spell can
|
||||
affect. Creatures within 20 feet of a point you choose within
|
||||
range are affected in ascending order of their current hit points
|
||||
(ignoring unconscious creatures).
|
||||
|
||||
Starting with the creature that has the lowest current hit points,
|
||||
each creature affected by this spell falls unconscious until the
|
||||
spell ends, the sleeper takes damage, or someone uses an action to
|
||||
shake or slap the sleeper awake. Subtract each creature's hit
|
||||
points from the total before moving on to the creature with the
|
||||
next lowest hit points. A creature's hit points must be equal to
|
||||
or less than the remaining total for that creature to be affected.
|
||||
|
||||
Undead and creatures immune to being charmed aren't affected by
|
||||
this spell.
|
||||
|
||||
At Higher Levels: When you cast this spell using a spell slot of
|
||||
2nd level or higher, roll an additional 2d8 for each slot level
|
||||
above 1st.
|
||||
|
||||
"""
|
||||
name = "Sleep"
|
||||
level = 1
|
||||
casting_time = "1 action"
|
||||
casting_range = "90 feet"
|
||||
components = ("V", "S", "M", )
|
||||
materials = "A pinch of fine sand, rose petals, or a cricket"
|
||||
duration = "1 minutes"
|
||||
magic_school = "Enchantment"
|
||||
classes = ('Bard', 'Sorceror', 'Wizard', )
|
||||
|
||||
|
||||
class SpareTheDying(Spell):
|
||||
"""You touch a living creature that has 0 hit points. The creature
|
||||
becomes stable. This spell has no effect on undead or
|
||||
constructs.
|
||||
|
||||
"""
|
||||
name = "Spare the Dying"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Necromancy"
|
||||
classes = ()
|
||||
|
||||
|
||||
class SpeakWithAnimals(Spell):
|
||||
"""You gain the ability to comprehend and verbally communicate with
|
||||
beasts for the duration. The knowledge and awareness of many
|
||||
beasts is limited by their intelligence, but at minimum, beasts
|
||||
can give you information about nearby locations and monsters,
|
||||
including whatever they can perceive or have perceived within the
|
||||
past day. You might be able to persuade a beast to perform a small
|
||||
favor for you, at the GM's discretion.
|
||||
|
||||
"""
|
||||
level = 1
|
||||
name = "Speak with Animals"
|
||||
casting_time = "1 action"
|
||||
casting_range = "Self"
|
||||
components = ("V", "S")
|
||||
duration = "10 minutes"
|
||||
ritual = True
|
||||
magic_school = "Divination"
|
||||
classes = ('Bard', 'Druid', 'Ranger')
|
||||
|
||||
|
||||
class SpeakWithDead(Spell):
|
||||
"""You grant the semblance of life and intelligence to a corpse of
|
||||
your choice within range, allowing it to answer the questions you
|
||||
pose. The corpse must still have a mouth and can’t be undead. The
|
||||
spell fails if the corpse was the target of this spell within the
|
||||
last 10 days. Until the spell ends, you can ask the corpse up to
|
||||
five questions. The corpse knows only what it knew in life,
|
||||
including the languages it knew. Answers are usually brief,
|
||||
cryptic, or repetitive, and the corpse is under no compulsion to
|
||||
offer a truthful answer if you are hostile to it or it recognizes
|
||||
you as an enemy. This spell doesn’t return the creature’s soul to
|
||||
its body, only its animating spirit. Thus, the corpse can’t learn
|
||||
new information, doesn’t comprehend anything that has happened
|
||||
since it died, and can’t speculate about future events.
|
||||
|
||||
"""
|
||||
name = "Speak with Dead"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "burning incense"
|
||||
duration = "10 minutes"
|
||||
magic_school = "Necromancy"
|
||||
classes = ()
|
||||
|
||||
|
||||
class SpiderClimb(Spell):
|
||||
"""Until the spell ends, one willing creature you touch gains the
|
||||
ability to move up, down, and across vertical surfaces and upside
|
||||
down along ceilings, while leaving its hands free. The target also
|
||||
gains a climbing speed equal to its walking speed.
|
||||
|
||||
"""
|
||||
name = "Spider Climb"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a drop of bitumen and a spider"
|
||||
duration = "Concentration, up to 1 hour"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class SpiritGuardians(Spell):
|
||||
"""You call forth spirits to protect you. They flit around you to a
|
||||
distance of 15 feet for the duration. If you are good or neutral,
|
||||
their spectral form appears angelic or fey (your choice). If you
|
||||
are evil, they appear fiendish. When you cast this spell, you can
|
||||
designate any number of creatures you can see to be unaffected by
|
||||
it. An affected creature’s speed is halved in the area, and when
|
||||
the creature enters the area for the first time on a turn or
|
||||
starts its turn there, it must make a Wisdom saving throw. On a
|
||||
failed save, the creature takes 3d8 radiant damage (if you are
|
||||
good or neutral) or 3d8 necrotic damage (if you are evil). On a
|
||||
successful save, the creature takes half as much damage. At Higher
|
||||
Levels. When you cast this spell using a spell slot of 4th level
|
||||
or higher, the damage increases by 1d8 for each slot level above
|
||||
3rd.
|
||||
|
||||
"""
|
||||
name = "Spirit Guardians"
|
||||
level = 3
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a holy symbol"
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class SpiritualWeapon(Spell):
|
||||
"""You create a floating, spectral weapon within range that lasts for
|
||||
the duration or until you cast this spell again. When you cast the
|
||||
spell, you can make a melee spell attack against a creature within
|
||||
5 feet of the weapon. On a hit, the target takes force damage
|
||||
equal to 1d8 + your spellcasting ability modifier. As a bonus
|
||||
action on your turn, you can move the weapon up to 20 feet and
|
||||
repeat the attack against a creature within 5 feet of it. The
|
||||
weapon can take whatever form you choose. Clerics of deities who
|
||||
are associated with a particular weapon (as St. Cuthbert is known
|
||||
for his mace and Thor for his hammer) make this spell’s effect
|
||||
resemble that weapon. At Higher Levels. When you cast this spell
|
||||
using a spell slot of 3rd level or higher, the damage increases by
|
||||
1d8 for every two slot levels above the 2nd.
|
||||
|
||||
"""
|
||||
name = "Spiritual Weapon"
|
||||
level = 2
|
||||
casting_time = "1 bonus action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "1 minute"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Stoneskin(Spell):
|
||||
"""This spell turns the flesh of a willing creature you touch as hard
|
||||
as stone. Until the spell ends, the target has resistance to
|
||||
nonmagical bludgeoning, piercing, and slashing damage.
|
||||
|
||||
"""
|
||||
name = "Stoneskin"
|
||||
level = 4
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "diamond dust worth 100 gp, which the spell consumes"
|
||||
duration = "Concentration, up to 1 hour"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Suggestion(Spell):
|
||||
"""You suggest a course of activity (limited to a sentence or two) and
|
||||
magically influence a creature you can see within range that can
|
||||
hear and understand you. Creatures that can’t be charmed are
|
||||
immune to this effect. The suggestion must be worded in such a
|
||||
manner as to make the course of action sound reasonable. Asking
|
||||
the creature to stab itself, throw itself onto a spear, immolate
|
||||
itself, or do some other obviously harmful act ends the spell.
|
||||
|
||||
The target must make a Wisdom saving throw. On a failed save, it
|
||||
pursues the course of action you described to the best of its
|
||||
ability. The suggested course of action can continue for the
|
||||
entire duration. If the suggested activity can be completed in a
|
||||
shorter time, the spell ends when the subject finishes what it was
|
||||
asked to do.
|
||||
|
||||
You can also specify conditions that will trigger a special
|
||||
activity during the duration. For example, you might suggest that
|
||||
a knight give her warhorse to the first beggar she meets. If the
|
||||
condition isn’t met before the spell expires, the activity isn’t
|
||||
performed.
|
||||
|
||||
If you or any of your companions damage the target, the spell
|
||||
ends.
|
||||
|
||||
"""
|
||||
name = "Suggestion"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'M')
|
||||
materials = "a snake’s tongue and either a bit of honeycomb or a drop of sweet oil"
|
||||
duration = "Concentration, up to 8 hours"
|
||||
magic_school = "Enchantment"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Sunburst(Spell):
|
||||
"""Brilliant sunlight flashes in a 60-foot radius centered on a point
|
||||
you choose within range. Each creature in that light must make a
|
||||
Constitution saving throw. On a failed save, a creature takes 12d6
|
||||
radiant damage and is blinded for 1 minute. On a successful save,
|
||||
it takes half as much damage and isn’t blinded by this
|
||||
spell. Undead and oozes have disadvantage on this saving throw.
|
||||
|
||||
A creature blinded by this spell makes another Constitution saving
|
||||
throw at the end of each of its turns. On a successful save, it is
|
||||
no longer blinded.
|
||||
|
||||
This spell dispels any darkness in its area that was created by a
|
||||
spell.
|
||||
|
||||
"""
|
||||
name = "Sunburst"
|
||||
level = 8
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "fire and a piece of sunstone"
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Teleport(Spell):
|
||||
"""This spell instantly transports you and up to eight willing
|
||||
creatures of your choice that you can see within range, or a
|
||||
single object that you can see within range, to a destination you
|
||||
select. If you target an object, it must be able to fit entirely
|
||||
inside a 10-foot cube, and it can’t be held or carried by an
|
||||
unwilling creature.
|
||||
|
||||
The destination you choose must be known to you, and it must be on
|
||||
the same plane of existence as you. Your familiarity with the
|
||||
destination determines whether you arrive there successfully. The
|
||||
DM rolls d100 and consults the table.
|
||||
|
||||
================= ====== ============ ========== =========
|
||||
Familiarity Mishap Similar Area Off Target On Target
|
||||
================= ====== ============ ========== =========
|
||||
Permanent circle -- -- -- 01-100
|
||||
Associated object -- -- -- 01-100
|
||||
Very familiar 01–05 06–13 14–24 25–100
|
||||
Seen casually 01–33 34–43 44–53 54–100
|
||||
Viewed once 01–43 44–53 54–73 74–100
|
||||
Description 01–43 44–53 54–73 74–100
|
||||
False destination 01–50 51–100 -- --
|
||||
================= ====== ============ ========== =========
|
||||
|
||||
**Familiarity** “Permanent circle” means a permanent teleportation
|
||||
circle whose sigil sequence you know. “Associated object” means
|
||||
that you possess an object taken from the desired destination
|
||||
within the last six months, such as a book from a wizard’s
|
||||
library, bed linen from a royal suite, or a chunk of marble from a
|
||||
lich’s secret tomb.
|
||||
|
||||
“Very familiar” is a place you have been very often, a place you
|
||||
have carefully studied, or a place you can see when you cast the
|
||||
spell. “Seen casually” is someplace you have seen more than once
|
||||
but with which you aren’t very familiar. “Viewed once” is a place
|
||||
you have seen once, possibly using magic. “Description” is a place
|
||||
whose location and appearance you know through someone else’s
|
||||
description, perhaps from a map.
|
||||
|
||||
“False destination” is a place that doesn’t exist. Perhaps you
|
||||
tried to scry an enemy’s sanctum but instead viewed an illusion,
|
||||
or you are attempting to teleport to a familiar location that no
|
||||
longer exists.
|
||||
|
||||
**On Target.** You and
|
||||
your group (or the target object) appear where you want to.
|
||||
|
||||
**Off Target.** You and your group (or the target object) appear a
|
||||
random distance away from the destination in a random
|
||||
direction. Distance off target is 1d10 × 1d10 percent of the
|
||||
distance that was to be traveled. For example, if you tried to
|
||||
travel 120 miles, landed off target, and rolled a 5 and 3 on the
|
||||
two d10s, then you would be off target by 15 percent, or 18
|
||||
miles. The DM determines the direction off target randomly by
|
||||
rolling a d8 and designating 1 as north, 2 as northeast, 3 as
|
||||
east, and so on around the points of the compass. If you were
|
||||
teleporting to a coastal city and wound up 18 miles out at sea,
|
||||
you could be in trouble.
|
||||
|
||||
**Similar Area.** You and your group (or the target object) wind
|
||||
up in a different area that’s visually or thematically similar to
|
||||
the target area. If you are heading for your home laboratory, for
|
||||
example, you might wind up in another wizard’s laboratory or in an
|
||||
alchemical supply shop that has many of the same tools and
|
||||
implements as your laboratory. Generally, you appear in the
|
||||
closest similar place, but since the spell has no range limit, you
|
||||
could conceivably wind up anywhere on the plane.
|
||||
|
||||
**Mishap.** The spell’s unpredictable magic results in a difficult
|
||||
journey. Each teleporting creature (or the target object) takes
|
||||
3d10 force damage, and the DM rerolls on the table to see where
|
||||
you wind up (multiple mishaps can occur, dealing damage each
|
||||
time).
|
||||
|
||||
"""
|
||||
name = "Teleport"
|
||||
level = 7
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Thaumaturgy(Spell):
|
||||
"""You manifest a minor wonder, a sign of supernatural power, within
|
||||
range. You create one of the following magical effects within
|
||||
range:
|
||||
|
||||
- Your voice booms up to three times as loud as normal for 1 minute.
|
||||
- You cause flames to flicker, brighten, dim, or change color for 1 minute.
|
||||
- You cause harmless tremors in the ground for 1 minute.
|
||||
- You create an instantaneous sound that originates from a point
|
||||
of your choice within range, such as a rumble of thunder, the
|
||||
cry of a raven, or omi- nous whispers.
|
||||
- You instantaneously cause an unlocked door or win- dow to fly
|
||||
open or slam shut.
|
||||
- You alter the appearance of your eyes for 1 minute.
|
||||
|
||||
If you cast this spell multiple times, you can have up to three of
|
||||
its 1-minute effects active at a time, and you can dismiss such an
|
||||
effect as an action.
|
||||
|
||||
"""
|
||||
name = "Thaumaturgy"
|
||||
level = 0
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Up to 1 minute"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Thunderwave(Spell):
|
||||
"""A wave of thunderous force sweeps out from you. Each creature in a
|
||||
15-foot cube originating from you must make a Constitution saving
|
||||
throw. On a failed save, a creature takes 2d8 thunder damage and
|
||||
is pushed 10 feet away from you. On a successful save, the
|
||||
creature takes half as much damage and isn’t pushed. In addition,
|
||||
unsecured objects that are completely within the area of effect
|
||||
are automatically pushed 10 feet away from you by the spell’s
|
||||
effect, and the spell emits a thunderous boom audible out to 300
|
||||
feet. At Higher Levels. When you cast this spell using a spell
|
||||
slot of 2nd level or higher, the damage increases by 1d8 for each
|
||||
slot level above 1st.
|
||||
|
||||
"""
|
||||
name = "Thunderwave"
|
||||
level = 1
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S')
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class TimeStop(Spell):
|
||||
"""You briefly stop the flow of time for everyone but yourself. No
|
||||
time passes for other creatures, while you take 1d4 + 1 turns in a
|
||||
row, during which you can use actions and move as normal.
|
||||
|
||||
This spell ends if one of the actions you use during this period,
|
||||
or any effects that you create during this period, affects a
|
||||
creature other than you or an object being worn or carried by
|
||||
someone other than you. In addition, the spell ends if you move to
|
||||
a place more than 1,000 feet from the location where you cast it.
|
||||
|
||||
"""
|
||||
name = "Time Stop"
|
||||
level = 9
|
||||
casting_time = "1 action"
|
||||
components = ('V',)
|
||||
materials = ""
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Transmutation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class TrueResurrection(Spell):
|
||||
"""You touch a creature that has been dead for no longer than 200
|
||||
years and that died for any reason except old age. If the
|
||||
creature’s soul is free and willing, the creature is restored to
|
||||
life with all its hit points. This spell closes all wounds,
|
||||
neutralizes any poison, cures all diseases, and lifts any curses
|
||||
affecting the creature when it died. The spell replaces damaged or
|
||||
missing organs and limbs. The spell can even provide a new body if
|
||||
the original no longer exists, in which case you must speak the
|
||||
creature’s name. The creature then appears in an unoccupied space
|
||||
you choose within 10 feet of you.
|
||||
|
||||
"""
|
||||
name = "True Resurrection"
|
||||
level = 9
|
||||
casting_time = "1 hour"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a sprinkle of holy water and diamonds worth at least 25,000 gp, which the spell consumes"
|
||||
duration = "Instantaneous"
|
||||
magic_school = "Necromancy"
|
||||
classes = ()
|
||||
|
||||
|
||||
class TrueSeeing(Spell):
|
||||
"""This spell gives the willing creature you touch the ability to see
|
||||
things as they actually are. For the duration, the creature has
|
||||
truesight, notices secret doors hidden by magic, and can see into
|
||||
the Ethereal Plane, all out to a range of 120 feet.
|
||||
|
||||
"""
|
||||
name = "True Seeing"
|
||||
level = 6
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "an ointment for the eyes that costs 25 gp; is made from mushroom powder, saffron, and fat; and is consumed by the spell"
|
||||
duration = "1 hour"
|
||||
magic_school = "Divination"
|
||||
classes = ()
|
||||
|
||||
|
||||
class UnseenServant(Spell):
|
||||
"""This spell creates an invisible, mindless, shapeless force that performs
|
||||
simple tasks at your command until the spell ends. The servant springs into
|
||||
existence in an unoccupied space on the ground within range. It has AC 10,
|
||||
1 hit point, and a Strength of 2, and it can’t attack. If it drops to 0 hit
|
||||
points, the spell ends. Once on each of your turns as a bonus action, you
|
||||
can mentally command the servant to move up to 15 feet and inteact with an
|
||||
object. The servant can perform simple tasks that a human servant could do,
|
||||
such as fetching things, cleaning, mending, folding clothes, lighting
|
||||
fires, serving food, and pouring wine. Once you give the command, the
|
||||
servant performs the task to the best of its ability until it completes the
|
||||
task, then waits for your next command. If you command the servant to
|
||||
perform a task that would move it more than 60 feet away from you, the
|
||||
spell ends.
|
||||
|
||||
"""
|
||||
name = "Unseen Servant"
|
||||
level = 1
|
||||
casting_time = '1 action'
|
||||
components = ('V', 'S', 'M')
|
||||
materials = 'a piece of string and a bit of wood'
|
||||
duration = '1 hour'
|
||||
casting_rage = '60 feet'
|
||||
magic_school = 'Conjuration'
|
||||
ritual = True
|
||||
classes = ('Bard', 'Warlock', 'Wizard')
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
from .spells import Spell
|
||||
|
||||
|
||||
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,
|
||||
or a ringed wall up to 20 feet in diameter, 20 feet high, and 1
|
||||
foot thick. The wall is opaque and lasts for the duration. When
|
||||
the wall appears, each creature within its area must make a
|
||||
Dexterity saving throw. On a failed save, a creature takes 5d8
|
||||
fire damage, or half as much damage on a successful save. One side
|
||||
of the wall, selected by you when you cast this spell, deals 5d8
|
||||
fire damage to each creature that ends its turn within 10 feet of
|
||||
that side or inside the wall. A creature takes the same damage
|
||||
when it enters the wall for the first time on a turn or ends its
|
||||
turn there. The other side of the wall deals no damage. At Higher
|
||||
Levels. When you cast this spell using a spell slot of 5th level
|
||||
or higher, the damage increases by 1d8 for each slot level above
|
||||
4th.
|
||||
|
||||
"""
|
||||
name = "Wall of Fire"
|
||||
level = 4
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a small piece of phosphorus"
|
||||
duration = "Concentration, up to 1 minute"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class WallOfStone(Spell):
|
||||
"""A nonmagical wall of solid stone springs into existence at a point
|
||||
you choose within range. The wall is 6 inches thick and is
|
||||
composed of ten 10-foot-by-10-foot panels. Each panel must be
|
||||
contiguous with at least one other panel. Alternatively, you can
|
||||
create 10-foot-by-20-foot panels that are only 3 inches thick. If
|
||||
the wall cuts through a creature’s space when it appears, the
|
||||
creature is pushed to one side of the wall (your choice). If a
|
||||
creature would be surrounded on all sides by the wall (or the wall
|
||||
and another solid surface), that creature can make a Dexterity
|
||||
saving throw. On a success, it can use its reaction to move up to
|
||||
its speed so that it is no longer enclosed by the wall. The wall
|
||||
can have any shape you desire, though it can’t occupy the same
|
||||
space as a creature or object. The wall doesn’t need to be
|
||||
vertical or rest on any firm foundation. It must, however, merge
|
||||
with and be solidly supported by existing stone. Thus, you can use
|
||||
this spell to bridge a chasm or create a ramp. If you create a
|
||||
span greater than 20 feet in length, you must halve the size of
|
||||
each panel to create supports. You can crudely shape the wall to
|
||||
create crenellations, battlements, and so on. The wall is an
|
||||
object made of stone that can be damaged and thus breached. Each
|
||||
panel has AC 15 and 30 hit points per inch of thickness. Reducing
|
||||
a panel to 0 hit points destroys it and might cause connected
|
||||
panels to collapse at the DM’s discretion. If you maintain your
|
||||
concentration on this spell for its whole duration, the wall
|
||||
becomes permanent and can’t be dispelled. Otherwise, the wall
|
||||
disappears when the spell ends.
|
||||
|
||||
"""
|
||||
name = "Wall of Stone"
|
||||
level = 5
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a small block of granite"
|
||||
duration = "Concentration, up to 10 minutes"
|
||||
magic_school = "Evocation"
|
||||
classes = ()
|
||||
|
||||
|
||||
class WardingBond(Spell):
|
||||
"""This spell wards a willing creature you touch and creates a mystic
|
||||
connection between you and the target until the spell ends. While
|
||||
the target is within 60 feet of you, it gains a +1 bonus to AC and
|
||||
saving throws, and it has resistance to all damage. Also, each
|
||||
time it takes damage, you take the same amount of damage. The
|
||||
spell ends if you drop to 0 hit points or if you and the target
|
||||
become separated by more than 60 feet. It also ends if the spell
|
||||
is cast again on either of the connected creatures. You can also
|
||||
dismiss the spell as an action.
|
||||
|
||||
"""
|
||||
name = "Warding Bond"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a pair of platinum rings worth at least 50 gp each, which you and the target must wear for the duration"
|
||||
duration = "1 hour"
|
||||
magic_school = "Abjuration"
|
||||
classes = ()
|
||||
|
||||
|
||||
class Web(Spell):
|
||||
"""You conjure a mass of thick, sticky webbing at a point of your
|
||||
choice within range. The webs fill a 20-foot cube from that point
|
||||
for the duration. The webs are difficult terrain and lightly
|
||||
obscure their area. If the webs aren’t anchored between two solid
|
||||
masses (such as walls or trees) or layered across a floor, wall,
|
||||
or ceiling, the conjured web collapses on itself, and the spell
|
||||
ends at the start of your next turn. Webs layered over a flat
|
||||
surface have a depth of 5 feet. Each creature that starts its turn
|
||||
in the webs or that enters them during its turn must make a
|
||||
Dexterity saving throw. On a failed save, the creature is
|
||||
restrained as long as it remains in the webs or until it breaks
|
||||
free. A creature restrained by the webs can use its action to make
|
||||
a Strength check against your spell save DC. If it succeeds, it is
|
||||
no longer restrained. The webs are flammable. Any 5-foot cube of
|
||||
webs exposed to fire burns away in 1 round, dealing 2d4 fire
|
||||
damage to any creature that starts its turn in the fire.
|
||||
|
||||
"""
|
||||
name = "Web"
|
||||
level = 2
|
||||
casting_time = "1 action"
|
||||
components = ('V', 'S', 'M')
|
||||
materials = "a bit of spiderweb"
|
||||
duration = "Concentration, up to 1 hour"
|
||||
magic_school = "Conjuration"
|
||||
classes = ()
|
||||
@@ -422,20 +422,18 @@ class Unarmed(Weapon):
|
||||
|
||||
|
||||
# Custom weapons
|
||||
class MonkUnarmed(Unarmed):
|
||||
name = "Monk Unarmed"
|
||||
class HeavyRight(Weapon):
|
||||
base_damage = "1d4"
|
||||
bonus_damage = 2 # from Dueling
|
||||
|
||||
|
||||
class HeavyRight(MonkUnarmed):
|
||||
name = "Heavy Right"
|
||||
damage_type = 'b'
|
||||
bonus_damage = 10 + 2 # Heavy weapon master + Dueling
|
||||
attack_bonus = -5 # Heavy weapon master
|
||||
|
||||
|
||||
class HeavyLeft(MonkUnarmed):
|
||||
class HeavyLeft(Weapon):
|
||||
base_damage = "1d4"
|
||||
name = "Heavy Left"
|
||||
damage_type = 'b'
|
||||
bonus_damage = 10 + 2 - 2 # No proficiency bonus
|
||||
attack_bonus = -5 # Heavy weapon master
|
||||
|
||||
@@ -505,4 +503,7 @@ martial_ranged_weapons = (Blowgun, HandCrossbow, HeavyCrossbow,
|
||||
Longbow, Net)
|
||||
martial_weapons = martial_melee_weapons + martial_ranged_weapons
|
||||
|
||||
firearms = (Firearm)
|
||||
monk_weapons = (Shortsword, Unarmed, Club, Dagger, Handaxe, Javelin,
|
||||
LightHammer, Mace, Quarterstaff, Sickle, Spear)
|
||||
|
||||
firearms = (Firearm, Blunderbuss, Pistol, Musket)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
from setuptools import setup
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
def read(fname):
|
||||
return open(os.path.join(os.path.dirname(__file__), fname)).read()
|
||||
@@ -18,12 +18,12 @@ setup(name='dungeonsheets',
|
||||
license='GPLv3',
|
||||
url='https://github.com/canismarko/dungeon-sheets',
|
||||
download_url = 'https://github.com/canismarko/dungeon-sheets/archive/master.zip',
|
||||
packages=['dungeonsheets'],
|
||||
packages=find_packages(),
|
||||
package_data={
|
||||
'dungeonsheets': ['blank-character-sheet-default.pdf',
|
||||
'blank-spell-sheet-default.pdf',
|
||||
'spellbook_template.tex', '../VERSION',
|
||||
'character_template.txt',
|
||||
'character_template.txt', 'features_template.tex',
|
||||
'druid_shapes_template.tex']
|
||||
},
|
||||
install_requires=[
|
||||
|
||||
Reference in New Issue
Block a user