reorganized spells by name, and added directory for features for each class"

This commit is contained in:
Ben Cook
2018-12-20 09:09:38 -05:00
parent 0ed7262fc8
commit b93b1ac6d7
52 changed files with 5768 additions and 5458 deletions
+29 -16
View File
@@ -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()
-523
View File
@@ -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'
+17
View File
@@ -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
+15
View File
@@ -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')
+46
View File
@@ -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),
}
+48
View File
@@ -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]
+39
View File
@@ -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),
}
+155
View File
@@ -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)
+20
View File
@@ -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')
+42
View File
@@ -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
+38
View File
@@ -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),
}
+54
View File
@@ -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'
+20
View File
@@ -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')
+40
View File
@@ -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),
}
+37
View File
@@ -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),
}
+42
View File
@@ -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),
}
-82
View File
@@ -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
+16
View File
@@ -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 *
View File
View File
View File
View File
+80
View File
@@ -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
View File
+77
View File
@@ -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 dont have the two-handed or heavy
property. You gain the following benefits while you are unarmed or wielding
only monk weapons and you arent 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
View File
View File
+86
View File
@@ -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
View File
View File
View File
View File
+5 -1
View File
@@ -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 %]
+3
View File
@@ -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
+7
View File
@@ -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 *
+60
View File
@@ -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
+546
View File
@@ -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 targets 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 spells
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 arent 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 creatures 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 closewithin 30 feetat 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 doesnt locate the
creature. This spell cant 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
cant 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 troglodytes 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 cant 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 isnt met
before the spell ends, the activity isnt 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 snakes 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 arent
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 elses voice, a lions 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 objectsuch as
a chair, muddy footprints, or a small chestit must be no larger
than a 5-foot cube. The image cant 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 = ()
+417
View File
@@ -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 cant 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
openings 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 spells 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 creatures soul is both
willing and at liberty to rejoin the body, the creature returns to
life with 1 hit point.
This spell also neutralizes any poisons and
cures nonmagical diseases that affected the creature at the time
it died. This spell doesnt, however, remove magical diseases,
curses, or similar effects; if these arent first removed prior to
casting the spell, they take effect when the creature returns to
life. The spell cant return an undead creature to life.
This spell closes all mortal wounds, but it doesnt restore
missing body parts. If the creature is lacking body parts or
organs integral for its survivalits head, for instancethe 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 targets 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 owners 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 didnt die of old age, and that isnt 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 doesnt,
however, remove magical diseases, curses, and the like; if such
effects arent 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 cant 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 cant 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 = ()
+664
View File
@@ -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 spells 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 doesnt 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 isnt
being worn or carried also takes the damage if its in the spells
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 isnt 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 cant 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 doesnt return the creatures soul to
its body, only its animating spirit. Thus, the corpse cant learn
new information, doesnt comprehend anything that has happened
since it died, and cant 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 creatures 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 spells 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 cant 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 isnt met before the spell expires, the activity isnt
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 snakes 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 isnt 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 cant 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 0105 0613 1424 25100
Seen casually 0133 3443 4453 54100
Viewed once 0143 4453 5473 74100
Description 0143 4453 5473 74100
False destination 0150 51100 -- --
================= ====== ============ ========== =========
**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 wizards
library, bed linen from a royal suite, or a chunk of marble from a
lichs 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 arent 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 elses
description, perhaps from a map.
False destination is a place that doesnt exist. Perhaps you
tried to scry an enemys 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 thats visually or thematically similar to
the target area. If you are heading for your home laboratory, for
example, you might wind up in another wizards 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 spells 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 isnt pushed. In addition,
unsecured objects that are completely within the area of effect
are automatically pushed 10 feet away from you by the spells
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
creatures 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
creatures 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 cant 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')
+143
View File
@@ -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 creatures 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 cant occupy the same
space as a creature or object. The wall doesnt 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 DMs discretion. If you maintain your
concentration on this spell for its whole duration, the wall
becomes permanent and cant 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 arent 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 = ()
+9 -8
View File
@@ -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.
+3 -3
View File
@@ -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=[