Features added and tested for all classes/subclasses in bens campaign

This commit is contained in:
Ben Cook
2018-12-26 18:58:25 -05:00
parent a83c49146c
commit b73e9848ef
34 changed files with 3612 additions and 313 deletions
+2
View File
@@ -105,3 +105,5 @@ ENV/
# mypy
.mypy_cache/
add_spell.sh
+1 -1
View File
@@ -1 +1 @@
0.9.0
0.9.2
BIN
View File
Binary file not shown.
-94
View File
@@ -1,94 +0,0 @@
"""This file describes the heroic adventurer Ben.
It's used primarily for saving characters from create-character,
where there will be many missing sections.
Modify this file as you level up and then re-generate the character
sheet by running ``makesheets`` from the command line.
"""
dungeonsheets_version = "0.9.0"
name = "Ben"
player_name = "Ben"
# Be sure to list Primary class first
classes = ['Bard', 'Paladin'] # ex: ['Wizard'] or ['Rogue', 'Fighter']
levels = [10, 2] # ex: [10] or [3, 2]
subclasses = ['', ''] # ex: ['Necromacy'] or ['Thief', None]
background = "Sailor"
race = "Half-Orc"
alignment = "Neutral good"
xp = 0
hp_max = 10
# Ability Scores
strength = 20
dexterity = 13
constitution = 14
intelligence = 12
wisdom = 10
charisma = 9
# Select what skills you're proficient with
# ex: skill_proficiencies = ('athletics', 'acrobatics', 'arcana')
skill_proficiencies = ('arcana', 'medicine', 'athletics', 'perception', 'intimidation')
# Named features / feats that aren't part of your classes, race, or background.
# Example:
# features = ('Tavern Brawler',) # take the optional Feat from PHB
features = ()
# If selecting among multiple feature options: ex Fighting Style
# Example (Fighting Style):
# feature_choices = ('Archery',)
feature_choices = ()
# Proficiencies and languages
languages = """Common, Orc"""
# Inventory
# TODO: Get yourself some money
cp = 0
sp = 0
ep = 0
gp = 0
pp = 0
# TODO: Put your equipped weapons and armor here
weapons = () # Example: ('shortsword', 'longsword')
magic_items = () # Example: ('ring of protection',)
armor = "" # Eg "light leather armor"
shield = "" # Eg "shield"
equipment = """TODO: list the equipment and magic items your character carries"""
attacks_and_spellcasting = """TODO: Describe how your character usually attacks
or uses spells."""
# List of known spells
# Example: spells_prepared = ('magic missile', 'mage armor')
spells_prepared = () # Todo: Learn some spells
# Which spells have not been prepared
__spells_unprepared = ()
# all spells known
spells = spells_prepared + __spells_unprepared
# Backstory
# Describe your backstory here
personality_traits = """TODO: How does your character behave? See the PHB for
examples of all the sections below"""
ideals = """TODO: What does your character believe in?"""
bonds = """TODO: Describe what debts your character has to pay,
and other commitments or ongoing quests they have."""
flaws = """TODO: Describe your characters interesting flaws.
"""
features_and_traits = """TODO: Describe other features and abilities your
character has."""
+91 -77
View File
@@ -60,9 +60,7 @@ class Character():
player_name = ""
alignment = "Neutral"
dungeonsheets_version = __version__
class_list = []
_level = 1 # Keep internal check of total level
_hit_dice_faces = 2
class_list = list()
_race = None
_background = None
xp = 0
@@ -77,13 +75,11 @@ class Character():
charisma = Ability()
armor_class = ArmorClass()
speed = Speed()
_saving_throw_proficiencies = []
other_weapon_proficiencies = tuple()
skill_proficiencies = tuple()
skill_expertise = tuple()
class_skill_choices = tuple()
num_skill_choices = 2
proficiencies_extra = tuple()
inspiration = 0
_saving_throw_proficiencies = tuple() # use to overwrite class proficiencies
other_weapon_proficiencies = tuple() # add to class/race proficiencies
skill_proficiencies = list()
skill_expertise = list()
languages = ""
# Skills
acrobatics = Skill(ability='dexterity')
@@ -118,30 +114,36 @@ class Character():
gp = 0
pp = 0
equipment = ""
weapons = [] # Replaced in __init__ constructor
magic_items = []
weapons = list()
magic_items = list()
armor = None
shield = None
_proficiencies_text = tuple()
_proficiencies_text = list()
# Magic
spellcasting_ability = None
_spells = tuple()
_spells_prepared = tuple()
_spells = list()
_spells_prepared = list()
# Features IN MAJOR DEVELOPMENT
custom_features = ()
feature_choices = ()
custom_features = list()
feature_choices = list()
def __init__(self, **attrs):
"""Takes a bunch of attrs and passes them to ``set_attrs``"""
self.clear()
# make sure class, race, background are set first
my_classes = attrs.pop('classes', [])
my_levels = attrs.pop('levels', [])
my_subclasses = attrs.pop('subclasses', [])
# backwards compatability
if (len(my_classes) == 0) and ('class' in attrs):
my_classes = [attrs.pop('class')]
my_levels = [attrs.pop('level', 1)]
my_subclasses = [attrs.pop('subclass', None)]
if (len(my_classes) == 0):
if ('class' in attrs):
my_classes = [attrs.pop('class')]
my_levels = [attrs.pop('level', 1)]
my_subclasses = [attrs.pop('subclass', None)]
else: # if no classes or levels given, default to Lvl 1 Fighter
my_classes = ['Fighter']
my_levels = [1]
my_subclasses = [None]
# Generate the list of class objects
self.add_classes(
my_classes, my_levels, my_subclasses,
@@ -152,6 +154,21 @@ class Character():
# parse all other attributes
self.set_attrs(**attrs)
def clear(self):
# reset class-definied items
self.class_list = list()
self.weapons = list()
self.magic_items = list()
self._saving_throw_proficiencies = tuple()
self.other_weapon_proficiencies = tuple()
self.skill_proficiencies = list()
self.skill_expertise = list()
self._proficiencies_text = list()
self._spells = list()
self._spells_prepared = list()
self.custom_features = list()
self.feature_choices = list()
def __str__(self):
return self.name
@@ -166,16 +183,15 @@ class Character():
cls = getattr(classes, cls)
except AttributeError:
raise AttributeError(
'class was not recognized from classes.py: {:s}'.format(c))
'class was not recognized from classes.py: {:s}'.format(cls))
if isinstance(level, str):
level = int(level)
params = {}
params['feature_choices'] = feature_choices
self.class_list.append(cls(level, owner=self,
subclass=subclass, **params))
subclass=subclass,
feature_choices=feature_choices))
def add_classes(self, classes_list=[], levels=[], subclasses=[],
feature_choices=[]):
feature_choices=[]):
if isinstance(classes_list, str):
classes_list = [classes_list]
if isinstance(levels, int) or isinstance(levels, float) or isinstance(levels, str):
@@ -265,20 +281,14 @@ class Character():
@property
def level(self):
if self.num_classes == 0:
return self._level
else:
return sum(c.level for c in self.class_list)
return sum(c.level for c in self.class_list)
@level.setter
def level(self, new_level):
if self.num_classes == 0:
self._level = new_level
else:
self.primary_class.level = new_level
if self.num_classes > 1:
warnings.warn("Unable to tell which level to set. Updating "
"level of primary class {:s}".format(self.primary_class.name))
self.primary_class.level = new_level
if self.num_classes > 1:
warnings.warn("Unable to tell which level to set. Updating "
"level of primary class {:s}".format(self.primary_class.name))
@property
def num_classes(self):
@@ -314,6 +324,10 @@ class Character():
def weapon_proficiencies(self, new_weapons):
self.other_weapon_proficiencies = tuple(new_weapons)
@property
def other_weapon_proficiencies_text(self):
return tuple(w.name for w in self.other_weapon_proficiencies)
@property
def features(self):
fts = set(self.custom_features)
@@ -335,6 +349,9 @@ class Character():
def custom_features_text(self):
return tuple([f.name for f in self.custom_features])
def has_feature(self, feat):
return any([isinstance(f, feat) for f in self.features])
@property
def saving_throw_proficiencies(self):
if self.primary_class is None:
@@ -419,18 +436,17 @@ class Character():
for mitem in val:
self.magic_items.append(mitem(owner=self))
elif attr == 'weapon_proficiencies':
self.other_weapon_proficiencies = tuple([findattr(weapons, w)
for w in val])
self.other_weapon_proficiencies = ()
wps = set([findattr(weapons, w) for w in val])
wps -= set(self.weapon_proficiencies)
self.other_weapon_proficiencies = list(wps)
elif attr == 'armor':
self.wear_armor(val)
elif attr == 'shield':
self.wield_shield(val)
elif attr == 'circle':
for c in self.class_list:
if isinstance(c, classes.Druid) and (c.circle == ''):
c.circle = val
self.circle = val
break
if hasattr(self, 'Druid'):
self.Druid.circle = val
elif attr == 'features':
if isinstance(val, str):
val = [val]
@@ -484,6 +500,17 @@ class Character():
ability_mod = getattr(self, class_type.spellcasting_ability).modifier
return (self.proficiency_bonus + ability_mod)
@property
def initiative(self) -> str:
ini = self.dexterity.modifier
if self.has_feature(features.QuickDraw):
ini += self.proficiency_bonus
ini = '{:+d}'.format(ini)
if self.has_feature(features.NaturalExplorerRevised):
ini += '(A)'
return ini
def is_proficient(self, weapon: Weapon):
"""Is the character proficient with this item?
@@ -516,7 +543,6 @@ class Character():
all_proficiencies += tuple(self.race.proficiencies_text)
if self.background is not None:
all_proficiencies += tuple(self.background.proficiencies_text)
all_proficiencies += tuple(self.proficiencies_extra)
# Create a single string out of all the proficiencies
for txt in all_proficiencies:
if not final_text:
@@ -536,7 +562,7 @@ class Character():
s = '\n\n--'.join([f.name + ("**" if f.needs_implementation else "")
for f in self.features])
if s != '':
s = '(See Features and Traits Page)\n\n--' + s
s = '(See Features Page)\n\n--' + s
s += '\n\n=================\n\n'
return s
@@ -593,7 +619,7 @@ class Character():
weapon_ = NewWeapon()
# check if features add any bonuses
for f in self.features:
weapon_ = f.weapon_func(weapon_, char=self)
weapon_ = f.weapon_func(weapon_)
# Set weapon attributes based on character
if weapon_.is_finesse:
ability_mod = max(self.strength.modifier, self.dexterity.modifier)
@@ -612,27 +638,19 @@ class Character():
"""What type and how many dice to use for re-gaining hit points.
To change, set hit_dice_num and hit_dice_faces."""
if self.num_classes == 0:
return '{:d}d{:d}'.format(self.level, self._hit_dice_faces)
else:
return ' + '.join([f'{c.level}d{c.hit_dice_faces}'
for c in self.class_list])
return ' + '.join([f'{c.level}d{c.hit_dice_faces}'
for c in self.class_list])
@property
def hit_dice_faces(self):
if self.num_classes == 0:
return self._hit_dice_faces
else: # Not a valid function if multiclass
if self.num_classes > 1:
warnings.warn("hit_dice_faces is not valid for multiclass characters")
return self.primary_class.hit_dice_faces
# Not a valid function if multiclass
if self.num_classes > 1:
warnings.warn("hit_dice_faces is not valid for multiclass characters")
return self.primary_class.hit_dice_faces
@hit_dice_faces.setter
def hit_dice_faces(self, faces):
if self.num_classes == 0:
self._hit_dice_faces = faces
else:
self.primary_class.hit_dice_faces = faces
self.primary_class.hit_dice_faces = faces
@property
def proficiency_bonus(self):
@@ -649,30 +667,26 @@ class Character():
return prof
def can_assume_shape(self, shape: monsters.Monster):
for c in self.class_list:
if isinstance(c, classes.Druid):
return c.can_assume_shape(shape)
return False
return hasattr(self, 'Druid') and self.Druid.can_assume_shape(shape)
@property
def all_wild_shapes(self):
for c in self.class_list:
if isinstance(c, classes.Druid):
return c.all_wild_shapes
return ()
if hasattr(self, 'Druid'):
return self.Druid.all_wild_shapes
else:
return ()
@property
def wild_shapes(self):
for c in self.class_list:
if isinstance(c, classes.Druid):
return c.wild_shapes
return ()
if hasattr(self, 'Druid'):
return self.Druid.wild_shapes
else:
return ()
@wild_shapes.setter
def wild_shapes(self, new_shapes):
for c in self.class_list:
if isinstance(c, classes.Druid):
c.wild_shapes = new_shapes
if hasattr(self, 'Druid'):
self.Druid.wild_shapes = new_shapes
@classmethod
def load(cls, character_file):
+31 -12
View File
@@ -47,11 +47,13 @@ class CharClass():
self.features_by_level[i] = fs
for k, v in params.items():
setattr(self, k, v)
self.spells_known = [S() for S in cls.spells_known]
self.spells_prepared = [S() for S in cls.spells_prepared]
# Apply subclass
self.subclass = self.select_subclass(subclass)
if isinstance(self.subclass, SubClass):
self.apply_subclass()
self.apply_subclass(feature_choices=feature_choices)
def select_subclass(self, subclass_str):
"""
@@ -67,23 +69,31 @@ class CharClass():
return sc(owner=self.owner)
return None
def apply_subclass(self):
if self.subclass is None:
def apply_subclass(self, feature_choices=[]):
if not isinstance(self.subclass, SubClass):
return
subcls = self.subclass
for i in range(1, 21):
self.features_by_level[i] += ([f(owner=self.owner) for f in
self.subclass.features_by_level[i]])
for attr in ('weapon_proficiencies', '_proficiencies_text',
'spells_known', 'spells_prepared'):
new_list = getattr(self, attr, ()) + getattr(self.subclass, attr, ())
fs = []
for f in subcls.features_by_level[i]:
if issubclass(f, FeatureSelector):
fs.append(f(owner=self.owner,
feature_choices=feature_choices))
elif issubclass(f, Feature):
fs.append(f(owner=self.owner))
self.features_by_level[i].extend(fs)
for attr in ('weapon_proficiencies', '_proficiencies_text'):
new_list = tuple(getattr(self, attr, ())) + tuple(getattr(self.subclass, attr, ()))
setattr(self, attr, new_list)
# All subclass proficiencies transfer, regardless of if this is primary class
self.multiclass_weapon_proficiencies += (self.subclass.weapon_proficiencies)
self._multiclass_proficiencies_text += (self._proficiencies_text)
self.multiclass_weapon_proficiencies += tuple(subcls.weapon_proficiencies)
self._multiclass_proficiencies_text += tuple(subcls._proficiencies_text)
self.spellcasting_ability = (self.spellcasting_ability or
self.subclass.spellcasting_ability)
subcls.spellcasting_ability)
self.spell_slots_by_level = (self.spell_slots_by_level or
self.subclass.spell_slots_by_level)
subcls.spell_slots_by_level)
self.spells_known.extend([S() for S in subcls.spells_known])
self.spells_prepared.extend([S() for S in subcls.spells_prepared])
@property
def features(self):
@@ -104,6 +114,15 @@ class CharClass():
else:
return self.spell_slots_by_level[self.level][spell_level]
def __str__(self):
s = 'Level {:d} {:s}'.format(self.level, self.name)
if isinstance(self.subclass, SubClass):
s += ' ({:s})'.format(str(self.subclass))
return s
def __repr__(self):
return '\"{:s}\"'.format(str(self))
class SubClass():
"""
+56 -11
View File
@@ -1,9 +1,31 @@
from .. import (weapons, features)
from .. import (weapons, features, spells)
from .classes import CharClass, SubClass
from collections import defaultdict
class KnowledgeDomain(SubClass):
class ClericDomain(SubClass):
name = "Generic Cleric Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
@property
def level(self):
return self.owner.Cleric.level
@property
def spells_known(self):
spells = []
for lvl, sps in self._domain_spells.items():
if self.level >= lvl:
spells.extend(sps)
return spells
# All Domain spells are both known and prepared
@property
def spells_prepared(self):
return self.spells_known
class KnowledgeDomain(ClericDomain):
"""The gods of knowledge—including Oghma, Boccob, Gilean, Aureon, and
Thoth—value learning and understanding above all. Some teach that knowledge
is to be gathered and shared in libraries and universities, or promote the
@@ -18,10 +40,11 @@ class KnowledgeDomain(SubClass):
"""
name = "Knowledge Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class LifeDomain(SubClass):
class LifeDomain(ClericDomain):
"""The Life domain focuses on the vibrant positive energy—one of the
fundamental forces of the universe— that sustains all life. The gods of
life promote vitality and health through healing the sick and wounded,
@@ -34,10 +57,11 @@ class LifeDomain(SubClass):
"""
name = "Life Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class LightDomain(SubClass):
class LightDomain(ClericDomain):
"""Gods of light—including Helm, Lathander, Pholtus, Branchala, the Silver
Flame, Belenus, Apollo, and Re-Horakhty—promote the ideals of rebirth and
renewal, truth, vigilance, and beauty, often using the symbol of the
@@ -51,10 +75,11 @@ class LightDomain(SubClass):
"""
name = "Light Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class NatureDomain(SubClass):
class NatureDomain(ClericDomain):
"""Gods of nature are as varied as the natural world itself, from inscrutable
gods of the deep forests (such as Silvanus, Obad-Hai, Chislev, Balinor, and
Pan) to friendly deities associated with particular springs and groves
@@ -68,10 +93,11 @@ class NatureDomain(SubClass):
"""
name = "Nature Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class TempestDomain(SubClass):
class TempestDomain(ClericDomain):
"""Gods whose portfolios include the Tempest domain - including Talos,
Umberlee, Kord, Zeboim, the Devourer, Zeus, and Thor — govern storms, sea,
and sky. They include gods of lightning and thunder, gods of earthquakes,
@@ -86,10 +112,22 @@ class TempestDomain(SubClass):
"""
name = "Tempest Domain"
_domain_spells = {1: [spells.FogCloud, spells.Thunderwave],
3: [spells.GustOfWind, spells.Shatter],
5: [spells.CallLightning, spells.SleetStorm],
7: [spells.ControlWater, spells.IceStorm],
9: [spells.DestructiveWave, spells.InsectPlague]}
weapon_proficiencies = (weapons.MartialWeapon,)
_proficiencies_text = ('martial weapons', 'heavy armor')
features_by_level = defaultdict(list)
features_by_level[1] = (features.WrathOfTheStorm,)
features_by_level[2] = (features.DestructiveWrath,)
features_by_level[6] = (features.ThunderboltStrike,)
features_by_level[8] = (features.DivineStrikeTempest,)
features_by_level[17] = (features.Stormborn,)
class TrickeryDomain(SubClass):
class TrickeryDomain(ClericDomain):
"""Gods of trickery—such as Tymora, Beshaba, Olidammara, the Traveler, Garl
Glittergold, and Loki—are mischief-makers and instigators who stand as a
constant challenge to the accepted order among both gods and
@@ -101,10 +139,11 @@ class TrickeryDomain(SubClass):
"""
name = "Trickery Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
class WarDomain(SubClass):
class WarDomain(ClericDomain):
"""War has many manifestations. It can make heroes of ordinary people. It can
be desperate and horrific, with acts of cruelty and cowardice eclipsing
instances of excellence and courage. In either case, the gods of war watch
@@ -120,11 +159,12 @@ class WarDomain(SubClass):
"""
name = "War Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
# SCAG
class ArcanaDomain(SubClass):
class ArcanaDomain(ClericDomain):
"""Magic is an energy that suffuses the multiverse and that fuels both
destruction and creation. Gods of the Arcana domain know the secrets and
potential of magic intimately. For some of these gods, magical knowledge
@@ -141,11 +181,12 @@ class ArcanaDomain(SubClass):
"""
name = "Arcana Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
# XGTE
class ForgeDomain(SubClass):
class ForgeDomain(ClericDomain):
"""The gods of the forge are patrons of artisans who work with metal, from a
humble blacksmith who keeps a village in horseshoes and plow blades to the
mighty elf artisan whose diamond-tipped arrows of mithral have felled demon
@@ -163,7 +204,7 @@ class ForgeDomain(SubClass):
features_by_level = defaultdict(list)
class GraveDomain(SubClass):
class GraveDomain(ClericDomain):
"""Gods of the grave watch over the line between life and death. To these
deities, death and the afterlife are a foundational part of the
multiverse. To desecrate the peace of the dead is an abomination. Deities
@@ -177,6 +218,7 @@ class GraveDomain(SubClass):
"""
name = "Grave Domain"
_domain_spells = {1: [], 3: [], 5: [], 7: [], 9: []}
features_by_level = defaultdict(list)
@@ -194,6 +236,9 @@ class Cleric(CharClass):
class_skill_choices = ('History', 'Insight', 'Medicine',
'Persuasion', 'Religion')
features_by_level = defaultdict(list)
features_by_level[2] = (features.ChannelDivinity, features.TurnUndead,)
features_by_level[5] = (features.DestroyUndead, )
features_by_level[10] = (features.DivineIntervention,)
subclasses_available = (KnowledgeDomain, LifeDomain, LightDomain,
NatureDomain, TempestDomain, TrickeryDomain,
WarDomain, ArcanaDomain, ForgeDomain,
+5
View File
@@ -0,0 +1,5 @@
from .. import (weapons, features, spells)
from collections import defaultdict
# Custom Classes
+26 -2
View File
@@ -13,6 +13,11 @@ class Champion(SubClass):
"""
name = "Champion"
features_by_level = defaultdict(list)
features_by_level[3] = [features.ImprovedCritical]
features_by_level[7] = [features.RemarkableAthelete]
features_by_level[10] = [features.AdditionalFightingStyle]
features_by_level[15] = [features.SuperiorCritical]
features_by_level[18] = [features.Survivor]
class BattleMaster(SubClass):
@@ -26,6 +31,9 @@ class BattleMaster(SubClass):
"""
name = "Battle Master"
features_by_level = defaultdict(list)
features_by_level[3] = [features.CombatSuperiority, features.StudentOfWar]
features_by_level[7] = [features.KnowYourEnemy]
features_by_level[15] = [features.Relentless]
class EldritchKnight(SubClass):
@@ -41,6 +49,12 @@ class EldritchKnight(SubClass):
"""
name = "Eldritch Knight"
features_by_level = defaultdict(list)
features_by_level[3] = [features.EldritchKnightSpellcasting,
features.WeaponBond]
features_by_level[7] = [features.WarMagic]
features_by_level[10] = [features.EldritchStrike]
features_by_level[15] = [features.ArcaneCharge]
features_by_level[18] = [features.ImprovedWarMagic]
spellcasting_ability = 'intelligence'
multiclass_spellslots_by_level = {
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
@@ -157,9 +171,15 @@ class Gunslinger(SubClass):
"""
name = "Gunslinger"
features_by_level = defaultdict(list)
weapon_proficiencies = (weapons.firearms)
_proficiencies_text = ('firearms')
_proficiencies_text = ('firearms', "tinker's tools")
features_by_level = defaultdict(list)
features_by_level[3] = [features.Gunsmith, features.AdeptMarksman]
features_by_level[7] = [features.QuickDraw]
features_by_level[10] = [features.RapidRepair]
features_by_level[15] = [features.LightningReload]
features_by_level[18] = [features.ViciousIntent,
features.HemorrhagingCritical]
class Fighter(CharClass):
@@ -179,6 +199,10 @@ class Fighter(CharClass):
'Athletics', 'History', 'Insight', 'Intimidation',
'Perception', 'Survival')
features_by_level = defaultdict(list)
features_by_level[1] = [features.FighterFightingStyle, features.SecondWind]
features_by_level[2] = [features.ActionSurge]
features_by_level[5] = [features.ExtraAttackFighter]
features_by_level[9] = [features.Indomitable]
subclasses_available = (Champion, BattleMaster, EldritchKnight,
PurpleDragonKnight, ArcaneArcher, Cavalier,
Samurai, Gunslinger)
+120 -8
View File
@@ -1,9 +1,31 @@
from .. import (weapons, features)
from .. import (weapons, features, spells)
from .classes import CharClass, SubClass
from collections import defaultdict
class OathOfDevotion(SubClass):
class PaladinOath(SubClass):
name = "Generic Paladin Oath"
_oath_spells = {3: [], 5: [], 9: [], 13: [], 17: []}
@property
def level(self):
return self.owner.Paladin.level
@property
def spells_known(self):
spells = []
for lvl, sps in self._oath_spells.items():
if self.level >= lvl:
spells.extend(sps)
return spells
# All Oath spells are both known and prepared
@property
def spells_prepared(self):
return self.spells_known
class OathOfDevotion(PaladinOath):
"""The Oath of Devotion binds a paladin to the loftiest ideals of justice,
virtue, and order. Sometim es called cavaliers, white knights, or holy
warriors, these paladins meet the ideal of the knight in shining armor,
@@ -35,10 +57,20 @@ class OathOfDevotion(SubClass):
"""
name = "Oath of Devotion"
_oath_spells = {3: [spells.ProtectionFromEvilAndGood,
spells.Sanctuary],
5: [spells.LesserRestoration, spells.ZoneOfTruth],
9: [spells.BeaconOfHope, spells.DispelMagic],
13: [spells.FreedomOfMovement, spells.GuardianOfFaith],
17: [spells.Commune, spells.FlameStrike]}
features_by_level = defaultdict(list)
features_by_level[3] = [features.SacredWeapon, features.TurnTheUnholy]
features_by_level[7] = [features.AuraOfDevotion]
features_by_level[15] = [features.PurityOfSpirit]
features_by_level[20] = [features.HolyNimbus]
class OathOfAncients(SubClass):
class OathOfAncients(PaladinOath):
"""The Oath of the Ancients is as old as the race of elves and the rituals of
the druids. Sometimes called fey knights, green knights, or horned knights,
paladins who swear this oath cast their lot with the side of the light in
@@ -69,10 +101,15 @@ class OathOfAncients(SubClass):
"""
name = "Oath of The Ancients"
_oath_spells = {3: [spells.EnsnaringStrike, spells.SpeakWithAnimals],
5: [spells.Moonbeam, spells.MistyStep],
9: [spells.PlantGrowth, spells.ProtectionFromEnergy],
13: [spells.IceStorm, spells.Stoneskin],
17: [spells.CommuneWithNature, spells.TreeStride]}
features_by_level = defaultdict(list)
class OathOfVengance(SubClass):
class OathOfVengance(PaladinOath):
"""The Oath of Vengeance is a solemn commitment to punish those who have
committed a grievous sin. When evil forces slaughter helpless villagers,
when an entire people turns against the will of the gods, when a thieves
@@ -103,10 +140,15 @@ class OathOfVengance(SubClass):
"""
name = "Oath of Vengance"
_oath_spells = {3: [spells.Bane, spells.HuntersMark],
5: [spells.HoldPerson, spells.MistyStep],
9: [spells.Haste, spells.ProtectionFromEnergy],
13: [spells.Banishment, spells.DimensionDoor],
17: [spells.HoldMonster, spells.Scrying]}
features_by_level = defaultdict(list)
class OathOfCrown(SubClass):
class OathOfCrown(PaladinOath):
"""The Oath of the Crown is sworn to the ideals of civilization, be it the
spirit of a nation, fealty to a sovereign, or service to a deity of law and
rulership. The paladins who swear this oath dedicate themselves to serving
@@ -137,10 +179,15 @@ class OathOfCrown(SubClass):
"""
name = "Oath of The Crown"
_oath_spells = {3: [spells.Command, spells.CompelledDuel],
5: [spells.WardingBond, spells.ZoneOfTruth],
9: [spells.AuraOfVitality, spells.SpiritGuardians],
13: [spells.Banishment, spells.GuardianOfFaith],
17: [spells.CircleOfPower, spells.Geas]}
features_by_level = defaultdict(list)
class OathOfConquest(SubClass):
class OathOfConquest(PaladinOath):
"""The Oath of Conquest calls to paladins who seek glory in battle and the
subjugation of their enemies. It isnt enough for these paladins to
establish order. They must crush the forces of chaos. Sometimes called
@@ -173,10 +220,15 @@ class OathOfConquest(SubClass):
"""
name = "Oath of Conquest"
_oath_spells = {3: [spells.ArmorOfAgathys, spells.Command],
5: [spells.HoldPerson, spells.SpiritualWeapon],
9: [spells.BestowCurse, spells.Fear],
13: [spells.DominateBeast, spells.Stoneskin],
17: [spells.Cloudkill, spells.DominatePerson]}
features_by_level = defaultdict(list)
class OathOfRedemption(SubClass):
class OathOfRedemption(PaladinOath):
"""The Oath of Redemption sets a paladin on a difficult path, one that requires
a holy warrior to use violence only as a last resort. Paladins who dedicate
themselves to this oath believe that any person can be redeemed and that
@@ -216,7 +268,56 @@ class OathOfRedemption(SubClass):
"""
name = "Oath of Redemption"
_oath_spells = {3: [spells.Sanctuary, spells.Sleep],
5: [spells.CalmEmotions, spells.HoldPerson],
9: [spells.Counterspell, spells.HypnoticPattern],
13: [spells.OtilukesResilientSphere, spells.Stoneskin],
17: [spells.HoldMonster, spells.WallOfForce]}
features_by_level = defaultdict(list)
features_by_level[3] = [features.EmissaryOfPeace,
features.RebukeTheViolent]
features_by_level[7] = [features.AuraOfTheGuardian]
features_by_level[15] = [features.ProtectiveSpirit]
features_by_level[20] = [features.EmissaryOfRedemption]
# Custom
class OathOfZor(PaladinOath):
"""The Oath of Zor
**Tenets of Zor**:
--Courage. Never fear to act, though caution is wise.
--Honesty. Dont lie or cheat. Let your word be your promise.
--Innocence. All people begin life in an innocent state, and it is their
environment or the influence of dark forces that drives them to evil. By
setting the proper example, and working to heal the wounds of a deeply
flawed world, you can set anyone on a righteous path.
--Restitution. If my foes wreak ruin on the world, it is because I failed
to stop them. I must help those harmed by their misdeeds.
--Patience. Change takes time. Those who have walked the path of the wicked
must be given reminders to keep them honest and true. Once you have planted
the seed of righteousness in a creature, you must work day after day to
allow that seed to survive and flourish.
"""
name = "Oath of Zor"
_oath_spells = {3: [spells.Sanctuary, spells.Sleep],
5: [spells.CalmEmotions, spells.HoldPerson],
9: [spells.Counterspell, spells.HypnoticPattern],
13: [spells.OtilukesResilientSphere, spells.Stoneskin],
17: [spells.HoldMonster, spells.WallOfForce]}
features_by_level = defaultdict(list)
features_by_level[3] = [features.EmissaryOfPeace,
features.RebukeTheViolent]
features_by_level[7] = [features.AuraOfTheGuardian]
features_by_level[15] = [features.ProtectiveSpirit]
features_by_level[20] = [features.EmissaryOfRedemption]
class Paladin(CharClass):
@@ -234,8 +335,19 @@ class Paladin(CharClass):
class_skill_choices = ("Athletics", 'Insight', 'Intimidation',
'Medicine', 'Persuasion', 'Religion')
features_by_level = defaultdict(list)
features_by_level[1] = [features.DivineSense, features.LayOnHands]
features_by_level[2] = [features.PaladinFightingStyle,
features.DivineSmite]
features_by_level[3] = [features.DivineHealth,
features.ChannelDivinityPaladin]
features_by_level[5] = [features.ExtraAttackPaladin]
features_by_level[6] = [features.AuraOfProtection]
features_by_level[10] = [features.AuraOfCourage]
features_by_level[11] = [features.ImprovedDivineSmite]
features_by_level[14] = [features.CleansingTouch]
subclasses_available = (OathOfDevotion, OathOfAncients, OathOfVengance,
OathOfCrown, OathOfConquest, OathOfRedemption)
OathOfCrown, OathOfConquest, OathOfRedemption,
OathOfZor)
spellcasting_ability = 'charisma'
spell_slots_by_level = {
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
+15 -6
View File
@@ -115,12 +115,6 @@ class Ranger(CharClass):
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)
# Revised Ranger
class BeastConclave(SubClass):
@@ -132,6 +126,11 @@ class BeastConclave(SubClass):
"""
name = "Beast Conclave"
features_by_level = defaultdict(list)
features_by_level[3] = [features.AnimalCompanion, features.CompanionsBond]
features_by_level[5] = [features.CoordinatedAttack]
features_by_level[7] = [features.BeastsDefense]
features_by_level[11] = [features.StormOfClawsAndFangs]
features_by_level[15] = [features.SuperiorBeastsDefense]
class HunterConclave(SubClass):
@@ -160,5 +159,15 @@ class DeepStalkerConclave(SubClass):
class RevisedRanger(Ranger):
name = 'Revised Ranger'
features_by_level = defaultdict(list)
features_by_level[1] = [features.FavoredEnemyRevised,
features.NaturalExplorerRevised]
features_by_level[2] = [features.RangerFightingStyle]
features_by_level[3] = [features.PrimevalAwarenessRevised]
features_by_level[6] = [features.GreaterFavoredEnemy]
features_by_level[8] = [features.FleetOfFoot]
features_by_level[10] = [features.HideInPlainSight]
features_by_level[14] = [features.Vanish]
features_by_level[18] = [features.FeralSenses]
features_by_level[20] = [features.FoeSlayer]
subclasses_available = (BeastConclave, HunterConclave, DeepStalkerConclave)
+17
View File
@@ -134,6 +134,23 @@ class Rogue(CharClass):
'Insight', 'Intimidation', 'Investigation',
'Perception', 'Performance', 'Persuasion',
'Sleight of Hand', 'Stealth')
num_skill_choices = 4
features_by_level = defaultdict(list)
features_by_level[1] = [features.Expertise, features.SneakAttack]
features_by_level[2] = [features.CunningAction]
features_by_level[5] = [features.UncannyDodge]
features_by_level[7] = [features.Evasion]
features_by_level[11] = [features.ReliableTalent]
features_by_level[14] = [features.BlindSense]
features_by_level[15] = [features.SlipperyMind]
features_by_level[18] = [features.Elusive]
features_by_level[20] = [features.StrokeOfLuck]
subclasses_available = (Thief, Assassin, ArcaneTrickster,
Inquisitive, Mastermind, Scout, Swashbuckler)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# slippery mind feature
if self.owner.Rogue.level >= 15:
self.saving_throw_proficiencies = ('dexterity', 'intelligence',
'wisdom')
+7
View File
@@ -30,6 +30,10 @@ class WildMagic(SubClass):
"""
name = "Wild Magic"
features_by_level = defaultdict(list)
features_by_level[1] = [features.WildMagicSurge, features.TidesOfChaos]
features_by_level[6] = [features.BendLuck]
features_by_level[14] = [features.ControlledChaos]
features_by_level[18] = [features.SpellBombardment]
# XGTE
@@ -107,6 +111,9 @@ class Sorceror(CharClass):
class_skill_choices = ('Arcana', 'Deception', 'Insight',
'Intimidation', 'Persuasion', 'Religion')
features_by_level = defaultdict(list)
features_by_level[2] = [features.FontOfMagic]
features_by_level[3] = [features.Metamagic]
features_by_level[20] = [features.SorcerousRestoration]
subclasses_available = (DraconicBloodline, WildMagic, DivineSoul,
ShadowMagic, StormSorcery)
spellcasting_ability = 'charisma'
+14
View File
@@ -31,6 +31,10 @@ class Fiend(SubClass):
"""
name = "The Fiend Patron"
features_by_level = defaultdict(list)
features_by_level[1] = [features.DarkOnesBlessing]
features_by_level[6] = [features.DarkOnesOwnLuck]
features_by_level[10] = [features.FiendishResilience]
features_by_level[14] = [features.HurlThroughHell]
class GreatOldOne(SubClass):
@@ -109,7 +113,13 @@ class Hexblade(SubClass):
"""
name = "Hexblade Patron"
weapon_proficiencies = (weapons.MartialWeapon,)
_proficiencies_text = ['martial weapons', 'medium armor', 'shields']
features_by_level = defaultdict(list)
features_by_level[1] = [features.HexbladesCurse, features.HexWarrior]
features_by_level[6] = [features.AccursedSpecter]
features_by_level[10] = [features.ArmorOfHexes]
features_by_level[14] = [features.MasterOfHexes]
class Warlock(CharClass):
@@ -126,6 +136,10 @@ class Warlock(CharClass):
multiclass_weapon_proficiencies = weapon_proficiencies
_multiclass_proficiencies_text = ('light armor', 'simple weapons')
features_by_level = defaultdict(list)
features_by_level[2] = [features.EldritchInvocation]
features_by_level[3] = [features.PactBoon]
features_by_level[11] = [features.MysticArcanum]
features_by_level[20] = [features.EldritchMaster]
subclasses_available = (Archfey, Fiend, GreatOldOne, Undying, Celestial,
Hexblade)
spellcasting_ability = 'charisma'
+2 -2
View File
@@ -486,7 +486,7 @@ class SkillForm(LinkedListForm):
self.update_remaining()
def update_remaining(self, widget=None):
num_choices = (self.parentApp.character.num_skill_choices +
num_choices = (self.parentApp.character.primary_class.num_skill_choices +
self.parentApp.character.race.num_skill_choices +
self.parentApp.character.background.num_skill_choices)
num_selected = len(self.skill_proficiencies.value)
@@ -507,7 +507,7 @@ class SkillForm(LinkedListForm):
value=0, editable=False)
self.skill_proficiencies = self.add(
npyscreen.TitleMultiSelect, name="Skill Proficiencies:",
values=self.parentApp.character.class_skill_choices,
values=(),
value_changed_callback=self.update_remaining)
def on_ok(self):
+164
View File
@@ -1,3 +1,167 @@
from .features import Feature
# Cleric Features
class ChannelDivinity(Feature):
"""At 2nd level, you gain the ability to channel divine energy directly from
your deity, using that energy to fuel magical effects. You start with two
such effects: Turn Undead and an effect determined by your domain. Some
domains grant you additional effects as you advance in levels, as noted in
the domain description.
When you use your Channel Divinity, you choose which effect to create. You
must then finish a short or long rest to use your Channel Divinity again.
Some Channel Divinity effects require saving throws. When you use such an
effect from this class, the DC equals your cleric spell save DC.
Beginning at 6th level, you can use your Channel Divinity twice between
rests, and beginning at 18th level you can use it three times between
rests. When you finish a short or long rest, you regain your expended uses.
"""
_name = "Channel Divinity"
source = "Cleric"
@property
def name(self):
level = self.owner.Cleric.level
if level < 6:
return "Channel Divinity (1x/SR)"
elif level < 18:
return "Channel Divinity (2x/SR)"
else:
return "Channel Divinity (3x/SR)"
class TurnUndead(Feature):
"""As an action, you present your holy symbol and speak a prayer censuring the
undead. Each undead that can see or hear you within 30 feet of you must
make a Wisdom saving throw. If the creature fails its saving throw, it is
turned for 1 minute or until it takes any damage.
A turned creature must spend its turns trying to move as far away from you
as it can, and it cant willingly move to a space within 30 feet of you. It
also cant take reactions. For its action, it can use only the Dash action
or try to escape from an effect that prevents it from moving. If theres
nowhere to move, the creature can use the Dodge action.
"""
name = "Channel Divinity: Turn Undead"
source = "Cleric"
class DestroyUndead(Feature):
"""Starting at 5th level, when an undead fails its saving throw against your
Turn Undead feature, the creature is instantly destroyed if its challenge
rating is at or below a certain threshold, as shown in the Destroy Undead
table.
"""
_name = "Destroy Undead"
source = "Cleric"
@property
def name(self):
level = self.owner.Cleric.level
name = self._name + ' (CR 1/2)'
if level >= 8:
name = self._name + ' (CR 1)'
if level >= 11:
name = self._name + ' (CR 2)'
if level >= 14:
name = self._name + ' (CR 3)'
if level >= 17:
name = self._name + ' (CR 4)'
return name
class DivineIntervention(Feature):
"""Beginning at 10th level, you can call on your deity to intervene on your
behalf when your need is great.
Imploring your deitys aid requires you to use your action. Describe the
assistance you seek, and roll percentile dice. If you roll a number equal
to or lower than your cleric level, your deity intervenes. The DM chooses
the nature of the intervention; the effect of any cleric spell or cleric
domain spell would be appropriate.
If your deity intervenes, you cant use this feature again for 7
days. Otherwise, you can use it again after you finish a long rest.
At 20th level, your call for intervention succeeds automatically, no roll
required.
"""
name = "Divine Intervention"
source = "Cleric"
# Tempest Domain
class WrathOfTheStorm(Feature):
"""Also at 1st level, you can thunderously rebuke attackers. When a creature
within 5 feet of you that you can see hits you with an attack, you can use
your reaction to cause the creature to make a Dexterity saving throw. The
creature takes 2d8 lightning or thunder damage (your choice) on a failed
saving throw, and half as much damage on a successful one.
You can use this feature a number of times equal to your Wisdom modifier (a
minimum of once). You regain all expended uses when you finish a long rest.
"""
_name = "Wrath of the Storm"
source = "Cleric (Tempest Domain)"
@property
def name(self):
num_uses = max(1, self.owner.wisdom.modifier)
return self._name + ' ({:d}x/LR)'.format(num_uses)
class DestructiveWrath(ChannelDivinity):
"""Starting at 2nd level, you can use your Channel Divinity to wield the power
of the storm with unchecked ferocity.
When you roll lightning or thunder damage, you can use your Channel
Divinity to deal maximum damage, instead of rolling.
"""
name = "Channel Divinity: Destructive Wrath"
source = "Cleric (Tempest Domain)"
class ThunderboltStrike(Feature):
"""At 6th level, when you deal lightning damage to a Large or smaller
creature, you can also push it up to 10 feet away from you.
"""
name = "Thunderbolt Strike"
source = "Cleric (Tempest Domain)"
class DivineStrikeTempest(Feature):
"""At 8th level, you gain the ability to infuse your weapon strikes with
divine energy. Once on each of your turns when you hit a creature with a
weapon attack, you can cause the attack to deal an extra 1d8 thunder damage
to the target. When you reach 14th level, the extra damage increases to
2d8.
"""
_name = "Divine Strike"
source = "Cleric (Tempest Domain)"
@property
def name(self):
level = self.owner.Cleric.level
if level < 14:
return self._name + " (1d8)"
else:
return self._name + " (2d8)"
class Stormborn(Feature):
"""At 17th level, you have a flying speed equal to your current walking speed
whenever you are not underground or indoors.
"""
name = "Stormborn"
source = "Cleric (Tempest Domain)"
+15 -10
View File
@@ -1,3 +1,4 @@
from .. import weapons
@@ -34,6 +35,9 @@ class Feature():
def __init__(self, owner=None):
self.owner = owner
cls = type(self)
self.spells_known = [S() for S in cls.spells_known]
self.spells_prepared = [S() for S in cls.spells_prepared]
def __eq__(self, other):
return (self.name == other.name) and (self.source == other.source)
@@ -72,16 +76,17 @@ class FeatureSelector(Feature):
A feature with multiple possible choices.
"""
options = dict()
name = ''
source = ''
def __init__(self, owner, feature_choices=[]):
self.owner = owner
keep_source = self.source
def __new__(t, owner, feature_choices=[]):
# Look for matching feature_choices
new_feat = Feature.__new__(Feature, owner=owner)
new_feat.__doc__ = t.__doc__
new_feat.name = t.name
new_feat.source = t.source
for selection in feature_choices:
if selection.lower() in self.options():
feature_choices.remove(selection)
new_feat = self.options[selection.lower()]
self.__dict__.update(new_feat.__dict__)
new_feat.__init__(self)
break
self.source = keep_source
if selection.lower() in t.options:
new_feat = t.options[selection.lower()](owner=owner)
new_feat.source = t.source
return new_feat
+751
View File
@@ -0,0 +1,751 @@
from .ranger import Archery, Defense, Dueling, TwoWeaponFighting
from .features import Feature, FeatureSelector
from .. import (weapons, armor)
# Features added for all PHB classes
# SCAG and XGTE needed
# PHB
class GreatWeaponFighting(Feature):
"""When you roll a 1 or 2 on a damage die for an attack you make with a melee
weapon that you are wielding with two hands, you can reroll the die and
must use the new roll, even if the new roll is a 1 or a 2. The weapon must
have the two-handed or versatile property for you to gain this benefit.
"""
name = "Fighting Style (Great Weapon Fighting)"
source = "Fighter"
class Protection(Feature):
"""When a creature you can see attacks a target other than you that is within
5 feet of you, you can use your reaction to impose disadvantage on the
attack roll. You must be wielding a shield.
"""
name = "Fighting Style (Protection)"
source = "Fighter"
class FighterFightingStyle(FeatureSelector):
"""
Select a Fighting Style by choosing in feature_choices:
archery
defense
dueling
great-weapon fighting
protection
two-weapon fighting
"""
options = {'archery': Archery,
'defense': Defense,
'dueling': Dueling,
'great': GreatWeaponFighting,
'great-weapon fighting': GreatWeaponFighting,
'projection': Protection,
'two-weapon fighting': TwoWeaponFighting,
'two-weapon': TwoWeaponFighting,
'dual wield': TwoWeaponFighting}
name = "Fighting Style (Select One)"
source = "Fighter"
class SecondWind(Feature):
"""You have a limited well of stamina that you can draw on to protect yourself
from harm. On your turn, you can use a bonus action to regain hit points
equal to 1d10 + your fighter level. Once you use this feature, you must
finish a short or long rest before you can use it again
"""
name = "Second Wind"
source = "Fighter"
class ActionSurge(Feature):
"""Starting at 2nd level, you can push yourself beyond your normal limits for a
moment. On your turn, you can take one additional action on top of your
regular action and a possible bonus action.
Once you use this feature, you must finish a short or long rest before you
can use it again. Starting at 17th level, you can use it twice before a
rest, but only once on the same turn.
"""
name = "Action Surge"
source = "Fighter"
class ExtraAttackFighter(Feature):
"""Beginning at 5th level, you can attack twice, instead of once, whenever you
take the Attack action on your turn. The number of attacks increases to
three when you reach 11th level in this class and to four when you reach
20th level in this class.
"""
_name = "Extra Attack"
source = "Fighter"
@property
def name(self):
level = self.owner.Fighter.level
if level < 11:
return self._name + ' (2x)'
elif level < 20:
return self._name + ' (3x)'
else:
return self._name + ' (4x)'
class Indomitable(Feature):
"""Beginning at 9th level, you can reroll a saving throw that you fail. If you
do so, you must use the new roll, and you cant use this feature again
until you finish a long rest.
You can use this feature twice between long rests starting at 13th level
and three times between long rests starting at 17th level.
"""
_name = "Indomitable"
source = "Fighter"
@property
def name(self):
level = self.owner.Fighter.level
if level < 13:
return self._name + ' (1x/LR)'
elif level < 17:
return self._name + ' (2x/LR)'
else:
return self._name + ' (3x/LR)'
# Champion
class ImprovedCritical(Feature):
"""Beginning when you choose this archetype at 3rd level, your weapon attacks
score a critical hit on a roll of 19 or 20.
"""
name = "Improved Critical"
source = "Fighter (Champion)"
class RemarkableAthelete(Feature):
"""Starting at 7th level, you can add half your proficiency bonus (round up)
to any Strength, Dexterity, or Constitution check you make that doesnt
already use your proficiency bonus.
In addition, when you make a running long jump, the distance you can cover
increases by a number of feet equal to your Strength modifier.
"""
name = "Remarkable Athelete"
source = "Fighter (Champion)"
class AdditionalFightingStyle(FeatureSelector):
"""
Select a Fighting Style by choosing in feature_choices:
archery 2
defense 2
dueling 2
great-weapon fighting 2
protection 2
two-weapon fighting 2
"""
options = {'archery 2': Archery,
'defense 2': Defense,
'dueling 2': Dueling,
'great 2': GreatWeaponFighting,
'great-weapon fighting 2': GreatWeaponFighting,
'projection 2': Protection,
'two-weapon fighting 2': TwoWeaponFighting,
'two-weapon 2': TwoWeaponFighting,
'dual wield 2': TwoWeaponFighting}
name = "Fighting Style (Select One)"
source = "Fighter (Champion)"
class SuperiorCritical(Feature):
"""Starting at 15th level, your weapon attacks score a critical hit on a roll
of 18-20 .
"""
name = "Superior Critical"
source = "Fighter (Champion)"
class Survivor(Feature):
"""At 18th level, you attain the pinnacle of resilience in battle. At the
start of each of your turns, you regain hit points equal to 5 + your
Constitution modifier if you have no more than half of your hit points
left. You dont gain this benefit if you have 0 hit points.
"""
name = "Survivor"
source = "Fighter (Champion)"
# Battle Master
class CombatSuperiority(Feature):
"""When you choose this archetype at 3rd level, you learn maneuvers that are
fueled by special dice called superiority dice.
**Maneuvers**: You learn three maneuvers of your choice, which are detailed
under Maneuvers below. Many maneuvers enhance an attack in some way. You
can use only one maneuver per attack. You learn two additional maneuvers
of your choice at 7th, 10th, and 15th level. Each time you learn new
maneuvers, you can also replace one maneuver you know with a different one.
**Superiority Dice**: You have four superiority dice, which are d8s. A
superiority die is expended when you use it. You regain all of your
expended superiority dice when you finish a short or long rest. You gain
another superiority die at 7th level and one more at 15th level.
**Saving Throws**: Some of your maneuvers require your target to make a
saving throw to resist the maneuvers effects. The saving throw DC is
calculated as follows:
Maneuver save DC = 8 + your proficiency bonus + your Strength or Dexterity
modifier (your choice)
"""
_name = "Combat Superiority"
@property
def name(self):
level = self.owner.Fighter.level
if level < 10:
return self._name + ' (d8)'
elif level < 18:
return self._name + ' (d10)'
else:
return self._name + ' (d12)'
class StudentOfWar(Feature):
"""At 3rd level, you gain proficiency with one type of artisans tools of your
choice.
"""
name = "Student of War"
source = "Fighter (Battle Master)"
class KnowYourEnemy(Feature):
"""Starting at 7th level, if you spend at least 1 minute observing or
interacting with another creature outside combat, you can learn certain
information about its capabilities compared to your own. The DM tells you
if the creature is your equal, superior, or inferior in regard to two of
the following characteristics of your choice:
--Strength score
--Dexterity score
--Constitution score
--Armor Class
--Current hit points
--Total class levels (if any)
--Fighter class levels (if any)
"""
name = "Know Your Enemy"
source = "Fighter (Battle Master)"
class Relentless(Feature):
"""Starting at 15th level, when you roll initiative and have no superiority
dice remaining, you regain 1 superiority die.
"""
name = "Relentless"
source = "Fighter (Battle Master)"
# Maneuvers
class Maneuver(Feature):
"""
A generic Maneuver
"""
name = "Maneuver"
source = "Fighter Maneuver (Battle Master)"
class CommandersStrike(Maneuver):
"""When you take the Attack action on your turn, you can forgo one of your
attacks and use a bonus action to direct one of your companions to
strike. When you do so, choose a friendly creature who can see or hear you
and expend one superiority die. That creature can immediately use its
reaction to make one weapon attack, adding the superiority die to the
attacks damage roll.
"""
name = "Commander's Strike"
class DisarmingStrike(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to attempt to disarm the target, forcing it to drop one
item o f your choice that its holding. You add the superiority die to the
attacks damage roll, and the target must make a Strength saving throw. On
a failed save, it drops the object you choose. The object lands at its
feet.
"""
name = "Disarming Strike"
class DistractingStrike(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to distract the creature, giving your allies an
opening. You add the superiority die to the attacks damage roll. The next
attack roll against the target by an attacker other than you has advantage
if the attack is made before the start of your next turn.
"""
name = "Distracting Strike"
class EvasiveFootwork(Maneuver):
"""When you move, you can expend one superiority die, rolling the die and
adding the number rolled to your AC until you stop moving.
"""
name = "Evasive Footwork"
class FeintingAttack(Maneuver):
"""You can expend one superiority die and use a bonus action on your turn to
feint, choosing one creature within 5 feet of you as your target. You have
advantage on your next attack roll against that creature. If that attack
hits, add the superiority die to the attacks damage roll.
"""
name = "Feinting Attack"
class GoadingAttack(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to attempt to goad the target into attacking you. You add
the superiority die to the attacks damage roll, and the target must make a
W isdom saving throw. On a failed save, the target has disadvantage on all
attack rolls against targets other than you until the end of your next
turn.
"""
name = "Goading Attack"
class LungingAttack(Maneuver):
"""When you make a melee weapon attack on your turn, you can expend one
superiority die to increase your reach for that attack by 5 feet. If you
hit, you add the superiority die to the attacks damage roll.
"""
name = "Lunging Attack"
class ManeuveringAttack(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to maneuver one o f your comrades into a more advantageous
position. You add the superiority die to the attacks damage roll, and you
choose a friendly creature who can see or hear you. That creature can use
its reaction to move up to half its speed without provoking opportunity
attacks from the target of your attack.
"""
name = "Maneuvering Attack"
class MenacingAttack(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to attempt to frighten the target. You add the superiority
die to the attacks damage roll, and the target must make a Wisdom saving
throw. On a failed save, it is frightened of you until the end o f your
next turn.
"""
name = "Menacing Attack"
class Parry(Maneuver):
"""When another creature damages you with a melee attack, you can use your
reaction and expend one superiority die to reduce the damage by the number
you roll on your superiority die + your Dexterity modifier.
"""
name = "Parry"
class PrecisionAttack(Maneuver):
"""When you make a weapon attack roll against a creature, you can expend one
superiority die to add it to the roll. You can use this maneuver before or
after making the attack roll, but before any effects of the attack are
applied
"""
name = "Precision Attack"
class PushingAttack(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to attempt to drive the target back. You add the
superiority die to the attack's damage roll, and if the target is Large or
smaller, it must make a Strength saving throw. On a failed save, you push
the target up to 15 feet away from you.
"""
name = "Pushing Attack"
class Rally(Maneuver):
"""On your turn, you can use a bonus action and expend one superiority die to
bolster the resolve of one of your companions. When you do so, choose a
friendly creature who can see or hear you. That creature gains temporary
hit points equal to the superiority die roll + your Charisma modifier.
"""
name = "Rally"
class Riposte(Maneuver):
"""When a creature misses you with a melee attack, you can use your reaction
and expend one superiority die to make a melee weapon attack against the
creature. If you hit, you add the superiority die to the attack's damage
roll
"""
name = "Riposte"
class SweepingAttack(Maneuver):
"""When you hit a creature with a melee weapon attack, you can expend one
superiority die to attempt to damage another creature with the same
attack. Choose another creature within 5 feet of the original target and
within your reach. If the original attack roll would hit the second
creature, it takes damage equal to the number you roll on your superiority
die. The damage is of the same type dealt by the original attack. Trip
"""
name = "Sweeping Attack"
class TripingAttack(Maneuver):
"""When you hit a creature with a weapon attack, you can expend one
superiority die to attempt to knock the target down. You add the
superiority die to the attacks damage roll, and if the target is Large or
smaller, it must make a Strength saving throw. On a failed save, you knock
the target prone
"""
name = "Triping Attack"
# Eldritch Knight
class EldritchKnightSpellcasting(Feature):
"""You know three 1st-level wizard spells of your choice, two of which you
must choose from the abjuration and evocation spells on the wizard spell
list.
The Spells Known column of the Eldritch Knight Spellcasting table shows
when you learn more wizard spells of 1st level or higher. Each of these
spells must be an abjuration or evocation spell of your choice, and must be
of a level for which you have spell slots. For instance, when you reach 7th
level in this class, you can learn one new spell of 1st or 2nd level.
The spells you learn at 8th, 14th, and 20th level can come from any school
of magic.
Whenever you gain a level in this class, you can replace one of the wizard
spells you know with another spell o f your choice from the wizard spell
list. The new spell must be of a level for which you have spell slots, and
it must be an abjuration or evocation spell, unless youre replacing the
spell you gained at 8th, 14th, or 20th level.
"""
name = "Spellcasting"
source = "Fighter (Eldritch Knight)"
class WeaponBond(Feature):
"""At 3rd level, you learn a ritual that creates a magical bond between
yourself and one weapon. You perform the ritual over the course of 1 hour,
which can be done during a short rest. The weapon must be within your reach
throughout the ritual, at the conclusion of which you touch the weapon and
forge the bond.
Once you have bonded a weapon to yourself, you cant be disarmed of that
weapon unless you are incapacitated. If it is on the same plane of
existence, you can summon that weapon as a bonus action on your turn,
causing it to teleport instantly to your hand.
You can have up to two bonded weapons, but can summon only one at a time
with your bonus action. If you attempt to bond with a third weapon, you
must break the bond with one of the other two.
"""
name = "Weapon Bond"
source = "Fighter (Eldritch Knight)"
class WarMagic(Feature):
"""Beginning at 7th level, when you use your action to cast a cantrip, you can
make one weapon attack as a bonus action.
"""
name = "War Magic"
source = "Fighter (Eldritch Knight)"
class EldritchStrike(Feature):
"""At 10th level, you learn how to make your weapon strikes undercut a
creatures resistance to your spells. When you hit a creature with a weapon
attack, that creature has disadvantage on the next saving throw it makes
against a spell you cast before the end of your next turn.
"""
name = "Eldritch Strike"
source = "Fighter (Eldritch Knight)"
class ArcaneCharge(Feature):
"""At 15th level, you gain the ability to teleport up to 30 feet to an
unoccupied space you can see when you use your Action Surge. You can
teleport before or after the additional action.
"""
name = "Arcane Charge"
source = "Fighter (Eldritch Knight)"
class ImprovedWarMagic(Feature):
"""Starting at 18th level, when you use your action to cast a spell, you can
make one weapon attack as a bonus action.
"""
name = "Improved War Magic"
source = "Fighter (Eldritch Knight)"
# Gunslinger
class Gunsmith(Feature):
"""Upon choosing this archetype at 3rd level, you gain proficiency with
Tinker's Tools. You may use them to craft ammunition at half the cost,
repair damaged firearms, or even draft and create new ones (DM's
discretion). Some extremely experimental and intricate firearms are only
available through crafting.
"""
name = "Gunsmith"
source = "Fighter (Gunslinger)"
class AdeptMarksman(Feature):
"""When you choose this archetype at 3rd level, you learn to perform powerful
trick shots to disable or damage your opponents using your firearms.
**Trick Shots**: You learn two trick shots of your choice, which are detailed under "Trick Shots" below.
If you have not already, add them by name to "features" in your character's .py file.
Many maneuvers enhance an attack in some way. Each use of a trick shot must be declared before the attack roll is made. You can use only one trick shot per attack.
You learn an additional trick shot of your choice at 7th, 10th, 15th, and
18th level. Each time you learn a new trick shot, you can also replace one
trick shot you know with a different one.
Grit. You gain a number of grit points equal to your Wisdom modifier
(minimum of 1). You regain 1 expended grit point each time you roll a 20 on
the d20 roll for an attack with a firearm, or deal a killing blow with a
firearm to a creature of significant threat (DMs discretion). You regain
all expended grit points after a short or long rest.
Saving Throws. Some of your trick shots require your targets to make a
saving throw to resist the trick shots effects. The saving throw DC is
calculated as follows:
Trick Shot save DC = 8 + your proficiency bonus + your Dexterity modifier
Firearm Properties Firearms are a new and volatile technology, and as such
bring their own unique set of weapon properties. Some properties are
followed by a number, and this number signifies an element of that property
(outlined below). These properties replace the optional ones presented in
the Dungeon Masters Guide. Firearms are ranged weapons.
Reload. The weapon can be fired a number of times equal to its Reload score
before you must spend 1 attack or 1 action to reload. You must have one
free hand to reload a firearm.
Misfire. Whenever you make an attack roll with a firearm, and the dice roll
is equal to or lower than the weapons Misfire score, the weapon
misfires. The attack misses, and the weapon cannot be used again until you
spend an action to try and repair it. To repair your firearm, you must make
a successful Tinkers Tools check (DC equal to 8 + misfire score). If your
check fails, the weapon is broken and must be mended out of combat at a
quarter of the cost of the firearm. Creatures who use a firearm without
being proficient increase the weapons misfire score by 1.
Explosive. Upon a hit, everything within 5 ft of the target must make a
Dexterity saving throw (DC equal to 8 + your proficiency bonus + your
Dexterity modifier) or suffer 1d8 fire damage. If the weapon misses, the
ammunition fails to detonate, or bounces away harmlessly before doing so.
Ammunition All firearms require ammunition to make an attack, and due to
their rare nature, ammunition may be near impossible to find or
purchase. However, if materials are gathered, you can craft ammunition
yourself using your Tinkers Tools at half the cost. Each firearm uses its
own unique ammunition and is generally sold or crafted in batches listed
below next to the price.
"""
name = "Adept Marksman"
source = "Fighter (Gunslinger"
class QuickDraw(Feature):
"""When you reach 7th level, you add your proficiency bonus to your
initiative. You can also stow a firearm, then draw another firearm as a
single object interaction on your turn.
"""
name = "Quick Draw"
source = "Fighter (Gunslinger)"
class RapidRepair(Feature):
"""Upon reaching 10th level, you learn how to quickly attempt to fix a jammed
gun. You can spend a grit point to attempt to repair a misfired (but not
broken) firearm as a bonus action.
"""
name = "Rapid Repair"
source = "Fighter (Gunslinger)"
class LightningReload(Feature):
"""Starting at 15th level, you can reload any firearm as a bonus action.
"""
name = "Lightning Repaid"
source = "Fighter (Gunslinger)"
class ViciousIntent(Feature):
"""At 18th level, your firearm attacks score a critical hit on a roll of 19-20,
and you regain a grit point on a roll of 19 or 20 on a d20 attack roll.
"""
name = "Vicious Intent"
source = "Fighter (Gunslinger)"
class HemorrhagingCritical(Feature):
"""Upon reaching 18th level, whenever you score a critical hit on an attack
with a firearm, the target additionally suffers half of the damage from the
attack at the end of its next turn.
"""
name = "Hemorrhaging Critical"
source = "Fighter (Gunslinger)"
class BullyingShot(Feature):
"""You can use the powerful blast and thundering sound of your firearm to shake
the resolve of a creature. You can expend one grit point while making a
Charisma (Intimidation) check to gain advantage on the roll.
"""
name = "Bullying Shot"
source = "Gunslinger (Trick Shot)"
class DazingShot(Feature):
"""When you make a firearm attack against a creature, you can expend one grit
point to attempt to dizzy your opponent. On a hit, the creature suffers
normal damage and must make a Constitution saving throw or suffer
disadvantage on attacks until the end of their next turn.
"""
name = "Dazing Shot"
source = "Gunslinger (Trick Shot)"
class DeadeyeShot(Feature):
"""When you make a firearm attack against a creature, you can expend one grit
point to gain advantage on the attack roll.
"""
name = "Deadeye Shot"
source = "Gunslinger (Trick Shot)"
class DisarmingShot(Feature):
"""When you make a firearm attack against a creature, you can expend one grit
point to attempt to shoot an object from their hands. On a hit, the
creature suffers normal damage and must succeed on a Strength saving throw
or drop 1 held object of your choice and have that object be pushed 10 feet
away from you.
"""
name = "Disarming Shot"
source = "Gunslinger (Trick Shot)"
class ForcefulShot(Feature):
"""When you make a firearm attack against a creature, you can expend one grit
point to attempt to trip them up and force them back. On a hit, the
creature suffers normal damage and must succeed on a Strength saving throw
or be pushed 15 feet away from you.
"""
name = "Forceful Shot"
source = "Gunslinger (Trick Shot)"
class PiercingShot(Feature):
"""When you make a firearm attack against a creature, you can expend one grit
point to attempt to fire through multiple opponents. The initial attack
gains a +1 to the firearms misfire score. On a hit, the creature suffers
normal damage and you make an attack roll with disadvantage against every
creature in a line directly behind the target within your first range
increment. Only the initial attack can misfire.
"""
name = "Piercing Shot"
source = "Gunslinger (Trick Shot)"
class WingingShot(Feature):
"""When you make a firearm attack against a creature, you can expend one grit
point to attempt to topple a moving target. On a hit, the creature suffers
normal damage and must make a Strength saving throw or be knocked prone.
"""
name = "Winging Shot"
source = "Gunslinger (Trick Shot)"
class ViolentShot(Feature):
"""When you make a firearm attack against a creature, you can expend one or
more grit points to enhance the volatility of the attack. For each grit
point expended, the attack gains a +2 to the firearms misfire score. If
the attack hits, you can roll one additional weapon damage die per grit
point spent when determining the damage.
"""
name = "Violent Shot"
source = "Gunslinger (Trick Shot)"
+12 -20
View File
@@ -44,30 +44,25 @@ class MartialArts(Feature):
source = 'Monk'
die = 'd4'
def __init__(self, owner):
self.owner = owner
self.level = owner.Monk.level
def weapon_func(self, weapon: weapons.Weapon, char=None, **kwargs):
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
"""
Update increasing damage dice and DEX mod of Monk weapons
"""
is_monk_weapon = any([isinstance(weapon, w)
for w in weapons.monk_weapons])
level = self.owner.Monk.level
if not is_monk_weapon:
return weapon
if char is None:
return weapon
self.die = 'd4'
if self.level >= 5:
if level >= 5:
self.die = 'd6'
if self.level >= 11:
if level >= 11:
self.die = 'd8'
if self.level >= 17:
if level >= 17:
self.die = 'd10'
# check if new damage is better than default
if self.die > int(weapon.base_damage.split('d')[-1]):
weapon.base_damage = '1d' + str(self.die)
if int(self.die[1:]) > int(weapon.base_damage.split('d')[-1]):
weapon.base_damage = '1' + str(self.die)
weapon.is_finesse = True
return weapon
@@ -84,19 +79,16 @@ class UnarmoredMovement(Feature):
name = "Unarmored Movement"
source = "Monk"
def __init__(self, owner):
self.owner = owner
self.level = owner.Monk.level
@property
def speed_bonus(self):
level = self.owner.Monk.level
_speed_bonus = 10
if self.level >= 6:
if level >= 6:
_speed_bonus = 15
if self.level >= 10:
if level >= 10:
_speed_bonus = 20
if self.level >= 14:
if level >= 14:
_speed_bonus = 25
if self.level >= 18:
if level >= 18:
_speed_bonus = 30
return _speed_bonus
+311
View File
@@ -0,0 +1,311 @@
from .features import Feature, FeatureSelector
from .ranger import Defense, Dueling
from .fighter import GreatWeaponFighting, Protection
from .. import (weapons, armor)
# PHB
class DivineSense(Feature):
"""The presence of strong evil registers on your senses like a noxious odor,
and powerful good rings like heavenly music in your ears. As an action, you
can open your awareness to detect such forces. Until the end of your next
turn, you know the location of any celestial, fiend, or undead within 60
feet of you that is not behind total cover. You know the type (celestial,
fiend, or undead) of any being whose presence you sense, but not its
identity (the vampire Count Strahd von Zarovich, for instance). Within the
same radius, you also detect the presence of any place or object that has
been consecrated or desecrated, as with the hallow spell.
You can use this feature a number of times equal to 1 + your Charisma
modifier. When you finish a long rest, you regain all expended uses.
"""
_name = "Divine Sense"
source = "Paladin"
@property
def name(self):
num_uses = max(1, 1+self.owner.charisma.modifier)
return self._name + ' ({:d}x/LR)'.format(num_uses)
class LayOnHands(Feature):
"""Your blessed touch can heal wounds. You have a pool of healing power that
replenishes when you take a long rest. With that pool, you can restore a
total number of hit points equal to your paladin level x 5.
As an action, you can touch a creature and draw power from the pool to
restore a number of hit points to that creature, up to the maximum amount
remaining in your pool.
Alternatively, you can expend 5 hit points from your pool of healing to
cure the target of one disease or neutralize one poison affecting it. You
can cure multiple diseases and neutralize multiple poisons with a single
use of Lay on Hands, expending hit points separately for each one.
This feature has no effect on undead and constructs
"""
_name = "Lay on Hands"
source = "Paladin"
@property
def name(self):
level = self.owner.Paladin.level
return self._name + " ({:d}HP/LR)".format(level*5)
class PaladinFightingStyle(FeatureSelector):
"""
Select a Fighting Style by choosing in feature_choices:
defense
dueling
great-weapon fighting
protection
"""
options = {'defense': Defense,
'dueling': Dueling,
'great': GreatWeaponFighting,
'great-weapon fighting': GreatWeaponFighting,
'projection': Protection}
name = "Fighting Style (Select One)"
source = "Paladin"
class DivineSmite(Feature):
"""Starting at 2nd level, when you hit a creature with a melee weapon attack,
you can expend one paladin spell slot to deal radiant damage to the target,
in addition to the weapons damage. The extra damage is 2d8 for a 1st-level
spell slot, plus 1d8 for each spell level higher than 1st, to a maximum of
5d8. The damage increases by 1d8 if the target is an undead or a fiend.
"""
name = "Divine Smite"
source = "Paladin"
class DivineHealth(Feature):
"""By 3rd level, the divine magic flowing through you makes you immune to
disease """
name = "Divine Health"
source = "Paladin"
class ExtraAttackPaladin(Feature):
"""Beginning at 5th level, you can attack twice, instead of once, whenever you
take the Attack action on your turn
"""
name = "Extra Attack (2x)"
source = "Paladin"
class AuraOfProtection(Feature):
"""Starting at 6th level, whenever you or a friendly creature within 10 feet
of you must make a saving throw, the creature gains a bonus to the saving
throw equal to your Charisma modifier (with a minimum bonus of +1). You
must be conscious to grant this bonus.
At 18th level, the range of this aura increases to 30 feet.
"""
name = "Aura of Protection"
source = "Paladin"
class AuraOfCourage(Feature):
"""Starting at 10th level, you and friendly creatures within 10 feet of you
cant be frightened while you are conscious.
At 18th level, the range of this aura increases to 30 feet
"""
name = "Aura of Courage"
source = "Paladin"
class ImprovedDivineSmite(Feature):
"""By 11th level, you are so suffused with righteous might that all your melee
weapon strikes carry divine power with them. Whenever you hit a creature
with a melee weapon, the creature takes an extra 1d8 radiant damage. If you
also use your Divine Smite with an attack, you add this damage to the extra
damage of your Divine Smite.
"""
name = "Improved Divine Smite"
source = "Paladin"
class CleansingTouch(Feature):
"""Beginning at 14th level, you can use your action to end one spell on
yourself or on one willing creature that you touch. You can use this
feature a number of times equal to your Charisma modifier (a minimum of
once). You regain expended uses when you finish a long rest.
"""
_name = "Cleansing Touch"
source = "Paladin"
@property
def name(self):
num_uses = max(1, 1+self.owner.charisma.modifier)
return self._name + ' ({:d}x/LR)'.format(num_uses)
class ChannelDivinityPaladin(Feature):
"""Your oath allows you to channel divine energy to fuel magical effects. Each
Channel Divinity option provided by your oath explains how to use it.
When you use your Channel Divinity, you choose which option to use. You
must then finish a short or long rest to use your Channel Divinity
again.
Some Channel Divinity effects require saving throws. When you use such an
effect from this class, the DC equals your paladin spell save DC.
"""
name = "Channel Divinity (1x/SR)"
source = "Paladin"
# Oath of Devotion
class SacredWeapon(Feature):
"""As an action, you can imbue one weapon that you are holding with positive
energy, using your Channel Divinity. For 1 minute, you add your Charisma
modifier to attack rolls made with that weapon (with a minimum bonus of
+1). The weapon also emits bright light in a 20-foot radius and dim light
20 feet beyond that. If the weapon is not already magical, it becomes
magical for the duration.
You can end this effect on your turn as part of any other action. If you
are no longer holding or carrying this weapon, or if you fall unconscious,
this effect ends.
"""
name = "Channel Divinity: Sacred Weapon"
source = "Paladin (Oath of Devotion)"
class TurnTheUnholy(Feature):
"""As an action, you present your holy symbol and speak a prayer censuring
fiends and undead, using your Channel Divinity. Each fiend or undead that
can see or hear you within 30 feet of you must make a W isdom saving
throw. If the creature fails its saving throw, it is turned for 1 minute or
until it takes damage.
A turned creature must spend its turns trying to move as far away from you
as it can, and it cant willingly move to a space within 30 feet of you. It
also cant take reactions. For its action, it can use only the Dash action
or try to escape from an effect that prevents it from moving. If theres
nowhere to move, the creature can use the Dodge action.
"""
name = "Channel Divinity: Turn the Unholy"
source = "Paladin (Oath of Devotion)"
class AuraOfDevotion(Feature):
"""Starting at 7th level, you and friendly creatures within 10 feet of you
cant be charmed while you are conscious. At 18th level, the range of this
aura increases to 30 feet.
"""
name = "Aura of Devotion"
source = "Paladin (Oath of Devotion)"
class PurityOfSpirit(Feature):
"""Beginning at 15th level, you are always under the effects of a protection
from evil and good spell.
"""
name = "Purity of Spirit"
source = "Paladin (Oath of Devotion)"
class HolyNimbus(Feature):
"""At 20th level, as an action, you can emanate an aura of sunlight. For 1
minute, bright light shines from you in a 30-foot radius, and dim light
shines 30 feet beyond that.
Whenever an enemy creature starts its turn in the bright light, the
creature takes 10 radiant damage.
In addition, for the duration, you have advantage on saving throws against
spells cast by fiends or undead. Once you use this feature, you cant use
it again until you finish a long rest.
"""
name = "Holy Nimbus"
source = "Paladin (Oath of Devotion)"
# Oath of the Ancients
# Oath of Redemption
class EmissaryOfPeace(Feature):
"""You can use your Channel Divinity to augment your presence with divine
power. As a bonus action, you grant yourself a +5 bonus to Charisma
(Persuasion) checks for the next 10 minutes.
"""
name = "Channel Divinity: Emissary of Peace"
source = "Paladin (Oath of Redemption)"
class RebukeTheViolent(Feature):
"""You can use your Channel Divinity to rebuke those who use
violence. Immediately after an attacker within 30 feet ofyou deals damage
with an attack against a creature other than you, you can use your reaction
to force the attacker to make a Wisdom saving throw. On a failed save, the
attacker takes radiant damage equal to the damage it just dealt. On a
successful save, it takes half as much damage.
"""
name = "Channel Divinity: Rebuke the Violent"
source = "Paladin (Oath of Redemption)"
class AuraOfTheGuardian(Feature):
"""Starting at 7th level, you can shield others from harm at the cost of your
own health. When a creature within 10 feet of you takes damage, you can use
your reaction to magically take that damage, instead of that creature tak
ing it. This feature doesn't transfer any other effects that might
accompany the damage, and this damage can't be reduced in any way. At 18th
level, the range of this aura increases to 30 feet.
"""
name = "Aura of the Guardian"
source = "Paladin (Oath of Redemption)"
class ProtectiveSpirit(Feature):
"""Starting at 15th level, a holy presence mends your wounds in battle. You
regain hit points equal to 1d6 + half your paladin level if you end your
turn in combat with fewer than half of your hit points remaining and you
aren't incapacitated.
"""
name = "Protective Spirit"
source = "Paladin (Oath of Redemption)"
class EmissaryOfRedemption(Feature):
"""At 20th level, you become an avatar of peace, which gives you two benefits:
--You have resistance to all damage dealt by other crea tures (their
attacks, spells, and other effects).
--Whenever a creature hits you with an attack, it takes radiant damage
equal to half the damage you take from the attack.
If you attack a creature, cast a spell on it, or deal dam- age to it by any
means but this feature, neither benefit works against that creature until
you finish a long rest
"""
name = "Emissary of Redemption"
source = "Paladin (Oath of Redemption)"
+281 -19
View File
@@ -2,24 +2,10 @@ from .features import Feature, FeatureSelector
from .. import (weapons, armor)
def select_ranger_fighting_style(char=None, feature_choices=[]):
lower_choices = [fc for fc in map(str.lower, feature_choices)]
if 'archery' in lower_choices:
return Archery(owner=char)
elif 'defense' in lower_choices:
return Defense(owner=char)
elif 'dueling' in lower_choices:
return Dueling(owner=char)
elif 'two-weapon fighting' in lower_choices:
return TwoWeaponFighting(owner=char)
else:
return RangerFightingStyle(owner=char)
class Archery(Feature):
"""
You gain a +2 bonus to attack rolls you make
with ranged weapons.
with ranged weapons (included in stats on Character Sheet).
"""
name = "Fighting Style (Archery)"
source = "Ranger"
@@ -28,14 +14,15 @@ class Archery(Feature):
"""
+2 attack roll bonus if weapon is ranged
"""
if weapon.is_ranged:
if isinstance(weapon, weapons.RangedWeapon):
weapon.attack_bonus += 2
return weapon
class Defense(Feature):
"""
While you are wearing armor, you gain a +1 bonus to AC.
While you are wearing armor, you gain a +1 bonus to AC (included in
stats on Character Sheet).
"""
name = "Fighting Style (Defense)"
source = "Ranger"
@@ -48,7 +35,15 @@ class Dueling(Feature):
"""
name = "Fighting Style (Dueling)"
source = "Ranger"
needs_implementation = True
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
"""
+2 attack roll bonus if melee weapon is not two handed
"""
if (isinstance(weapon, weapons.MeleeWeapon)
and "two-handed" in weapon.properties.lower()):
weapon.attack_bonus += 2
return weapon
class TwoWeaponFighting(Feature):
@@ -58,7 +53,6 @@ class TwoWeaponFighting(Feature):
"""
name = "Fighting Style (Two-Weapon Fighting)"
source = "Ranger"
needs_implementation = True
class RangerFightingStyle(FeatureSelector):
@@ -81,3 +75,271 @@ class RangerFightingStyle(FeatureSelector):
'dual wield': TwoWeaponFighting}
name = "Fighting Style (Select One)"
source = "Ranger"
# Revised Ranger
class FavoredEnemyRevised(Feature):
"""Beginning at 1st level, you have significant experience studying, tracking,
hunting, and even talking to a certain type of enemy commonly encountered
in the wilds.
Choose a type of favored enemy: beasts, fey, humanoids, monstrosities, or
undead. You gain a +2 bonus to damage rolls with weapon attacks against
creatures of the chosen type. Additionally, you have advantage on Wisdom
(Survival) checks to track your favored enemies, as well as on Intelligence
checks to recall information about them.
When you gain this feature, you also learn one language of your choice,
typically one spoken by your favored enemy or creatures associated with
it. However, you are free to pick any language you wish to learn
"""
name = "Favored Enemy"
source = "Revised Ranger"
class NaturalExplorerRevised(Feature):
"""You are a master of navigating the natural world, and you react with swift
and decisive action when attacked. This grants you the following benefits:
--You ignore difficult terrain.
--You have advantage on initiative rolls.
--On your first turn during combat, you have advantage on attack rolls
against creatures that have not yet acted.
In addition, you are skilled at navigating the wilderness. You gain the
following benefits when traveling for an hour or more:
--Difficult terrain doesnt slow your groups travel.
--Your group cant become lost except by magical means.
--Even when you are engaged in another activity while traveling (such as
foraging, navigating, or tracking), you remain alert to danger.
--If you are traveling alone, you can move stealthily at a normal pace.
--When you forage, you find twice as much food as you normally would.
--While tracking other creatures, you also learn their exact number, their
sizes, and how long ago they passed through the area.
"""
name = "Natural Explorer"
source = "Revised Ranger"
class PrimevalAwarenessRevised(Feature):
"""Beginning at 3rd level, your mastery of ranger lore allows you to establish
a powerful link to beasts and to the land around you.
You have an innate ability to communicate with beasts, and they recognize
you as a kindred spirit. Through sounds and gestures, you can communicate
simple ideas to a beast as an action, and can read its basic mood and
intent. You learn its emotional state, whether it is affected by magic of
any sort, its short-term needs (such as food or safety), and actions you
can take (if any) to persuade it to not attack.
You cannot use this ability against a creature that you have attacked
within the past 10 minutes.
Additionally, you can attune your senses to determine if any of your
favored enemies lurk nearby. By spending 1 uninterrupted minute in
concentration (as if you were concentrating on a spell), you can sense
whether any of your favored enemies are present within 5 miles of you. This
feature reveals which of your favored enemies are present, their numbers,
and the creatures general direction and distance (in miles) from you.
If there are multiple groups of your favored enemies within range, you
learn this information for each group.
"""
name = "Primeval Awareness"
source = "Revised Ranger"
class GreaterFavoredEnemy(Feature):
"""At 6th level, you are ready to hunt even deadlier game. Choose a type of
greater favored enemy: aberrations, celestials, constructs, dragons,
elementals, fiends, or giants. You gain all the benefits against this
chosen enemy that you normally gain against your favored enemy, including
an additional language. Your bonus to damage rolls against all your favored
enemies increases to +4.
Additionally, you have advantage on saving throws against the spells and
abilities used by a greater favored enemy.
"""
name = "Greated Favored Enemy"
source = "Revised Ranger"
class FleetOfFoot(Feature):
"""Beginning at 8th level, you can use the Dash action as a bonus action on
your turn.
"""
name = "Fleet of Foot"
source = "Revised Ranger"
class HideInPlainSight(Feature):
"""Starting at 10th level, you can remain perfectly still for long periods of
time to set up ambushes.
When you attempt to hide on your turn, you can opt to not move on that
turn. If you avoid moving, creatures that attempt to detect you take a 10
penalty to their Wisdom (Perception) checks until the start of your next
turn. You lose this benefit if you move or fall prone, either 4 voluntarily
or because of some external effect. You are still automatically detected if
any effect or action causes you to no longer be hidden.
If you are still hidden on your next turn, you can continue to remain
motionless and gain this benefit until you are detected
"""
name = "Hide in Plain Sight"
source = "Revised Ranger"
class Vanish(Feature):
"""Starting at 14th level, you can use the Hide action as a bonus action on
your turn. Also, you cant be tracked by nonmagical means, unless you
choose to leave a trail
"""
name = "Vanish"
source = "Revised Ranger"
class FeralSenses(Feature):
"""At 18th level, you gain preternatural senses that help you fight creatures
you cant see. When you attack a creature you cant see, your inability to
see it doesnt impose disadvantage on your attack rolls against it.
You are also aware of the location of any invisible creature within 30 feet
of you, provided that the creature isnt hidden from you and you arent
blinded or deafened
"""
name = "Feral Senses"
source = "Revised Ranger"
class FoeSlayer(Feature):
"""At 20th level, you become an unparalleled hunter. Once on each of your
turns, you can add your Wisdom modifier to the attack roll or the damage
roll of an attack you make. You can choose to use this feature before or
after the roll, but before any effects of the roll are applied
"""
name = "Foe Slayer"
source = "Revised Ranger"
class AnimalCompanion(Feature):
"""At 3rd level, you learn to use your magic to create a powerful bond with a
creature of the natural world.
With 8 hours of work and the expenditure of 50 gp worth of rare herbs and
fine food, you call forth an animal from the wilderness to serve as your
faithful companion. You normally select you companion from among the
following animals: an ape, a black bear, a boar, a giant badger, a giant
weasel, a mule, a panther, or a wolf. However, your DM might pick one of
these animals for you, based on the surrounding terrain and on what types
of creatures would logically be present in the area.
At the end of the 8 hours, your animal companion appears and gains all the
benefits of your Companions Bond ability. You can have only one animal
companion at a time.
If your animal companion is ever slain, the magical bond you share allows
you to return it to life. With 8 hours of work and the expenditure of 25 gp
worth of rare herbs and fine food, you call forth your companions spirit
and use your magic to create a new body for it. You can return an animal
companion to life in this manner even if you do not possess any part of its
body.
If you use this ability to return a former animal companion to life while
you have a current animal companion, your current companion leaves you and
is replaced by the restored companion
"""
name = "Animal Companion"
source = "Revised Ranger (Animal Companion)"
class CompanionsBond(Feature):
"""Your animal companion gains a variety of benefits while it is linked to
you. The animal companion loses its Multiattack action, if it has one.
The companion obeys your commands as best it can. It rolls for initiative
like any other creature, but you determine its actions, decisions,
attitudes, and so on. If you are incapacitated or absent, your companion
acts on its own. When using your Natural Explorer feature, you and your
animal companion can both move stealthily at a normal pace.
Your animal companion has abilities and game statistics determined in part
by your level. Your companion uses your proficiency bonus rather than its
own. In addition to the areas where it normally uses its proficiency bonus,
an animal companion also adds its proficiency bonus to its AC and to its
damage rolls.
Your animal companion gains proficiency in two skills of your choice. It
also becomes proficient with all saving throws. For each level you gain
after 3rd, your animal companion gains an additional hit die and increases
its hit points accordingly. Whenever you gain the Ability Score Improvement
class feature, your companions abilities also improve. Your companion can
increase one ability score of your choice by 2, or it can increase two
ability scores of your choice by 1. As normal, your companion cant
increase an ability score above 20 using this feature unless its
description specifies otherwise.
Your companion shares your alignment, and has a personality trait and a
flaw that you can roll for or select from the tables below. Your companion
shares your ideal, and its bond is always, \The ranger who travels with me
is a beloved companion for whom I would gladly give my life.\
"""
name = "Companions Bond"
source = "Revised Ranger (Beast Conclave)"
class CoordinatedAttack(Feature):
"""Beginning at 5th level, you and your animal companion form a more potent
fighting team. When you use the Attack action on your turn, if your
companion can see you, it can use its reaction to make a melee attack.
"""
name = "Coordinated Attack"
source = "Revised Ranger (Beast Conclave)"
class BeastsDefense(Feature):
"""At 7th level, while your companion can see you, it has advantage on all
saving throw
"""
name = "Beast's Defense"
source = "Revised Ranger (Beast Conclave)"
class StormOfClawsAndFangs(Feature):
"""At 11th level, your companion can use its action to make a melee attack
against each creature of its choice within 5 feet of it, with a separate
attack roll for each target
"""
name = "Storm of Claws and Fangs"
source = "Revised Ranger (Beast Conclave)"
class SuperiorBeastsDefense(Feature):
"""At 15th level, whenever an attacker that your companion can see hits it
with an attack, it can use its reaction to halve the attacks damage
against it.
"""
name = "Superior Beast's Defense"
source = "Revised Ranger (Beast Conclave)"
+126
View File
@@ -0,0 +1,126 @@
from .features import Feature
from math import ceil
# PHB
class Expertise(Feature):
"""At 1st level, choose two of your skill proficiencies, or one of your skill
proficiencies and your proficiency with thieves tools. Your proficiency
bonus is doubled for any ability check you make that uses either of the
chosen proficiencies.
At 6th level, you can choose two more of your
proficiencies (in skills or with thieves tools) to gain this benefit.
Add these skills to "skill_expertise" in your character.py file
"""
name = "Expertise"
source = "Rogue"
class SneakAttack(Feature):
"""Beginning at 1st level, you know how to strike subtly and exploit a foes
distraction. Once per turn, you can deal an extra 1d6 damage to one
creature you hit with an attack if you have advantage on the attack
roll. The attack must use a finesse or a ranged weapon.
You dont need advantage on the attack roll if another enemy of the target
is within 5 feet of it, that enemy isnt incapacitated, and you dont have
disadvantage on the attack roll.
The amount of the extra damage increases as you gain levels in this class,
as shown in the Sneak Attack column of the Rogue table.
"""
_name = "Sneak Attack"
source = "Rogue"
@property
def name(self):
level = self.owner.Rogue.level
dice = ceil(level / 2.)
name = self._name + " ({:d}d6)".format(dice)
return name
class CunningAction(Feature):
"""Starting at 2nd level, your quick thinking and agility allow you to move
and act quickly. You can take a bonus action on each of your turns in
combat. This action can be used only to take the Dash, Disengage, or Hide
action.
"""
name = "Cunning Action"
source = "Rogue"
class UncannyDodge(Feature):
"""Starting at 5th level, when an attacker that you can see hits you with an
attack, you can use your reaction to halve the attacks damage against you.
"""
name = "Uncanny Dodge"
source = "Rogue"
class Evasion(Feature):
"""Beginning at 7th level, you can nimbly dodge out o f the way of certain
area effects, such as a red dragons fiery breath or an ice storm
spell. When you are subjected to an effect that allows you to make a
Dexterity saving throw to take only half damage, you instead take no damage
if you succeed on the saving throw, and only half damage if you fail.
"""
name = "Evasion"
source = "Rogue"
class ReliableTalent(Feature):
"""By 11th level, you have refined your chosen skills until they approach
perfection. Whenever you make an ability check that lets you add your
proficiency bonus, you can treat a d20 roll of 9 or lower as a 10.
"""
name = "Reliable Talent"
source = "Rogue"
class BlindSense(Feature):
"""Starting at 14th level, if you are able to hear, you are aware of the
location of any hidden or invisible creature within 10 feet of you.
"""
name = "Blind Sense"
source = "Rogue"
class SlipperyMind(Feature):
"""By 15th level, you have acquired greater mental strength. You gain
proficiency in W isdom saving throws.
"""
name = 'Slippery Mind'
source = "Rogue"
class Elusive(Feature):
"""Beginning at 18th level, you are so evasive that attackers rarely gain the
upper hand against you. No attack roll has advantage against you while you
arent incapacitated.
"""
name = "Elusive"
source = "Rogue"
class StrokeOfLuck(Feature):
"""At 20th level, you have an uncanny knack for succeeding when you need
to. If your attack m isses a target within range, you can turn the miss
into a hit. Alternatively, if you fail an ability check, you can treat the
d20 roll as a 20.
Once you use this feature, you cant use it again until you finish a short
or long rest.
"""
name = "Stroke of Luck"
source = "Rogue"
+193
View File
@@ -1,6 +1,199 @@
from .features import Feature
# PHB
class FontOfMagic(Feature):
"""At 2nd level, you tap into a deep wellspring of magic within yourself. This
wellspring is represented by sorcery points, which allow you to create a
variety of magical effects.
**Sorcery Points**: You have sorcery points equal to your Sorceror
Level. You can never have more sorcery points than shown on the table for
your level. You regain all spent sorcery points when you finish a long
rest.
**Flexible Casting**: You can use your sorcery points to gain additional
spell slots, or sacrifice spell slots to gain additional sorcery
points. You learn other ways to use your sorcery points as you reach higher
levels. You can transform unexpended sorcery points into one spell slot as
a bonus action on your turn. The Creating Spell Slots table shows the cost
of creating a spell slot of a given level. You can create spell slots no
higher in level than 5th. As a bonus action on your turn, you can expend
one spell slot and gain a number of sorcery points equal to the slots
level.
1st Level Slot <--> 2 sorcery points
2nd Level Slot <--> 3 sorcery points
3rd Level Slot <--> 5 sorcery points
4th Level Slot <--> 6 sorcery points
5th Level Slot <--> 7 sorcery points
"""
name = "Font of Magic"
source = "Sorceror"
class Metamagic(Feature):
"""At 3rd level, you gain the ability to twist your spells to suit your
needs. You gain two of the following Metamagic options of your choice. You
gain another one at 10th and 17th level. You can use only one Metamagic
option on a spell when you cast it, unless otherwise noted
"""
name = "Metamagic"
source = "Sorceror (Metamagic)"
class SorcerousRestoration(Feature):
"""At 20th level, you regain 4 expended sorcery points whenever you finish a
short rest.
"""
# Metamagic
class CarefulSpell(Metamagic):
"""When you cast a spell that forces other creatures to make a saving throw,
you can protect some of those creatures from the spells full force. To do
so, you spend 1 sorcery point and choose a number o f those creatures up to
your Charisma modifier (minimum of one creature). A chosen creature
automatically succeeds on its saving throw against the spell.
"""
name = "Careful Spell"
class DistantSpell(Metamagic):
"""When you cast a spell that has a range of 5 feet or greater, you can spend
1 sorcery point to double the range of the spell. When you cast a spell
that has a range of touch, you can spend 1 sorcery point to make the range
of the spell 30 feet
"""
name = "Distant Spell"
class EmpoweredSpell(Metamagic):
"""When you roll damage for a spell, you can spend 1 sorcery point to reroll a
number of the damage dice up to your Charisma modifier (minimum of
one). You must use the new rolls. You can use Empowered Spell even if you
have already used a different Metamagic option during the casting of the
spell.
"""
name = "Empowered Spell"
class ExtendedSpell(Metamagic):
"""When you cast a spell that has a duration of 1 minute or longer, you can
spend 1 sorcery point to double its duration, to a maximum duration of 24
hours.
"""
name = "Extended Spell"
class HeightenedSpell(Metamagic):
"""When you cast a spell that forces a creature to make a saving throw to
resist its effects, you can spend 3 sorcery points to give one target of
the spell disadvantage on its first saving throw made against the spell
"""
name = "Heightened Spell"
class QuickenedSpell(Metamagic):
"""When you cast a spell that has a casting time of 1 action, you can spend 2
sorcery points to change the casting time to 1 bonus action for this
casting.
"""
name = "Quickened Spell"
class SubtleSpell(Metamagic):
"""When you cast a spell, you can spend 1 sorcery point to cast it without any
somatic or verbal components.
"""
name = "Subtle Spell"
class TwinnedSpell(Metamagic):
"""When you cast a spell that targets only one creature and doesnt have a
range of self, you can spend a number of sorcery points equal to the
spells level to target a second creature in range with the same spell (1
sorcery point if the spell is a cantrip)
"""
name = "Twinned Spell"
# Wild Magic
class WildMagicSurge(Feature):
"""Starting when you choose this origin at 1st level, your spellcasting can
unleash surges of untamed magic. Immediately after you cast a sorcerer
spell of 1st level or higher, the DM can have you roll a d20. If you roll a
1, roll on the Wild Magic Surge table to create a random magical effect.
"""
name = "Wild Magic Surge"
source = "Sorceror (Wild Magic)"
class TidesOfChaos(Feature):
"""Starting at 1st level, you can manipulate the forces of chance and chaos to
gain advantage on one attack roll, ability check, or saving throw. Once you
do so, you must finish a long rest before you can use this feature again.
Any time before you regain the use of this feature, the DM can have you
roll on the Wild Magic Surge table immediately after you cast a sorcerer
spell of 1st level or higher. You then regain the use of this feature.
"""
name = "Tides of Chaos"
source = "Sorceror (Wild Magic)"
class BendLuck(Feature):
"""Starting at 6th level, you have the ability to twist fate using your wild
magic. When another creature you can see makes an attack roll, an ability
check, or a saving throw, you can use your reaction and spend 2 sorcery
points to roll 1d4 and apply the number rolled as a bonus or penalty (your
choice) to the creatures roll. You can do so after the creature rolls but
before any effects of the roll occur.
"""
name = 'Bend Luck'
source = 'Sorceror (Wild Magic)'
class ControlledChaos(Feature):
"""At 14th level, you gain a modicum of control over the surges of your wild
magic. Whenever you roll on the Wild Magic Surge table, you can roll twice
and use either number.
"""
name = 'Controlled Chaos'
source = "Sorceror (Wild Magic)"
class SpellBombardment(Feature):
"""Beginning at 18th level, the harmful energy of your spells
intensifies. When you roll damage for a spell and roll the highest number
possible on any of the dice, choose one of those dice, roll it again and
add that roll to the damage. You can use the feature only once per turn.
"""
name = "Spell Bombardment"
source = "Sorceror (Wild Magic)"
# Draconic Ancestry
class DraconicResilience(Feature):
"""As magic flows through your body, it causes physical traits of your dragon
ancestors to emerge. At 1st level, your hit point maximum increases by 1
+469 -5
View File
@@ -1,5 +1,289 @@
from .features import Feature
from .. import spells
from .features import (Feature, FeatureSelector)
from .. import spells, weapons
# Features
class EldritchInvocation(Feature):
"""In your study of occult lore, you have unearthed eldritch invocations,
fragments of forbidden knowledge that imbue you with an abiding magical
ability.
At 2nd level, you gain two eldritch invocations of your choice. Your
invocation options are detailed at the end of the class description. When
you gain certain warlock levels, you gain additional invocations of your
choice, as shown in the Invocations Known column of the Warlock table.
Additionally, when you gain a level in this class, you can choose one of
the invocations you know and replace it with another invocation that you
could learn at that level.
"""
name = "Eldritch Invocations"
source = "Warlock"
class PactOfTheChain(Feature):
"""You learn the find familiar spell and can cast it as a ritual. The spell
doesnt count against your number of spells known.
When you cast the spell, you can choose one of the normal forms for your
familiar or one of the following special forms: imp, pseudodragon, quasit,
or sprite. Additionally, when you take the Attack action, you can forgo one
of your own attacks to allow your familiar to make one attack of its own.
"""
name = "Pact of the Chain"
source = "Warlock"
spells_known = spells_prepared = (spells.FindFamiliar,)
class PactOfTheBlade(Feature):
"""You can use your action to create a pact weapon in your empty hand. You can
choose the form that this melee weapon takes each time you create it (see
chapter 5 for weapon options). You are proficient with it while you wield
it. This weapon counts as magical for the purpose o f overcoming resistance
and immunity to nonmagical attacks and damage.
Your pact weapon disappears if it is more than 5 feet away from you for 1
minute or more. It also disappears if you use this feature again, if you
dismiss the weapon (no action required), or if you die.
You can transform one magic weapon into your pact weapon by performing a
special ritual while you hold the weapon. You perform the ritual over the
course of 1 hour, which can be done during a short rest. You can then
dismiss the weapon, shunting it into an extradimensional space, and it
appears whenever you create your pact weapon thereafter. You cant affect
an artifact or a sentient weapon in this way. The weapon ceases being your
pact weapon if you die, if you perform the 1-hour ritual on a different
weapon, or if you use a 1-hour ritual to break your bond to it. The weapon
appears at your feet if it is in the extradimensional space when the bond
breaks.
"""
name = "Pact of the Blade"
source = "Warlock"
class PactOfTheTome(Feature):
"""Your patron gives you a grimoire called a Book of Shadows. When you gain
this feature, choose three cantrips from any classs spell list. While the
book is on your person, you can cast those cantrips at will. They dont
count against your number of cantrips known.
If you lose your Book of Shadows, you can perform a 1-hour ceremony to
receive a replacement from your patron. This ceremony can be performed
during a short or long rest, and it destroys the previous book. The book
turns to ash when you die.
"""
name = "Pact of the Tome"
source = "Warlock"
class PactBoon(FeatureSelector):
"""Select a Pact Boon by choosing in feature_choices:
pact of the chain
pact of the blade
pact of the tome
"""
options = {'chain': PactOfTheChain,
'pact of the chain': PactOfTheChain,
'blade': PactOfTheBlade,
'pact of the blade': PactOfTheBlade,
'tome': PactOfTheTome,
'pact of the tome': PactOfTheTome}
name = "Pact Boon (Select One)"
source = "Warlock"
class MysticArcanum(Feature):
"""At 11th level, your patron bestows upon you a magical secret called an
arcanum. Choose one 6th-level spell from the warlock spell list as this
arcanum.
You can cast your arcanum spell once without expending a spell slot. You
must finish a long rest before you can do so again.
At higher levels, you gain more warlock spells of your choice that can be
cast in this way: one 7th-level spell at 13th level, one 8th-level spell at
15th level, and one 9th-level spell at 17th level. You regain all uses of
your Mystic Arcanum when you finish a long rest.
"""
name = "Mystic Arcanum"
source = "Warlock"
class EldritchMaster(Feature):
"""At 20th level, you can draw on your inner reserve of mystical power while
entreating your patron to regain expended spell slots. You can spend 1
minute entreating your patron for aid to regain all your expended spell
slots from your Pact Magic feature. Once you regain spell slots with this
feature, you must finish a long rest before you can do so again.
"""
name = "Eldritch Master"
source = "Warlock"
# The Fiend Patron
class DarkOnesBlessing(Feature):
"""Starting at 1st level, when you reduce a hostile creature to 0 hit points,
you gain temporary hit points equal to your Charisma modifier + your
warlock level (minimum of 1)
"""
name = "Dark One's Blessing"
source = "Warlock (The Fiend Patron)"
class DarkOnesOwnLuck(Feature):
"""Starting at 6th level, you can call on your patron to alter fate in your
favor. When you make an ability check or a saving throw, you can use this
feature to add a d10 to your roll. You can do so after seeing the initial
roll but before any o f the rolls effects occur.
Once you use this feature, you cant use it again until you finish a short
or long rest.
"""
name = "Dark One's Own Luck"
source = "Warlock (The Fiend Patron)"
class FiendishResilience(Feature):
"""Starting at 10th level, you can choose one damage type when you finish a
short or long rest. You gain resistance to that damage type until you
choose a different one with this feature. Damage from magical weapons or
silver weapons ignores this resistance.
"""
name = "Fiendish Resilience"
source = "Warlock (The Fiend Patron)"
class HurlThroughHell(Feature):
"""Starting at 14th level, when you hit a creature with an attack, you can use
this feature to instantly transport the target through the lower
planes. The creature disappears and hurtles through a nightmare landscape.
At the end of your next turn, the target returns to the space it previously
occupied, or the nearest unoccupied space. If the target is not a fiend, it
takes 10d10 psychic damage as it reels from its horrific experience.
Once you use this feature, you cant use it again until you finish a long
rest.
"""
name = "Hurl Through Hell"
source = "Warlock (The Fiend Patron)"
# Hexblade
class HexbladesCurse(Feature):
"""Starting at lst level, you gain the ability to place a bale— ful curse on
someone. As a bonus action, choose one creature you can see within 30 feet
of you. The target is cursed for 1 minute. The curse ends early if the
target dies, you die, or you are incapacitated. Until the curse ends, you
gain the following benefits:
-- You gain a bonus to damage rolls against the
cursed target. The bonus equals your proficiency bonus.
-- Any attack roll you make against the cursed target is a critical hit on
a roll of 19 or 20 on the d20.
--If the cursed target dies, you regain hit points equal to your warlock
level + your Charisma modifier (mini- mum of 1 hit point).
You cant use this feature again until you finish a short or long rest.
"""
name = "Hexblades Curse"
source = "Warlock (Hexblade)"
class HexWarrior(Feature):
"""At lst level, you acquire the training necessary to effec- tively arm
yourself for battle. You gain proficiency with medium armor, shields, and
martial weapons.
The influence of your patron also allows you to mystically channel your
will through a particular weapon. Whenever you finish a long rest, you can
touch one weapon that you are proficient with and that lacks the two-handed
property. When you attack with that weapon, you can use your Charisma
modifier, instead of Strength or Dexterity, for the attack and damage
rolls. This benefit lasts until you finish a long rest. If you later gain
the Pact of the Blade feature, this benefit extends to every pact weapon
you conjure with that feature, no matter the weapons type
"""
name = 'Hex Warrior'
source = "Warlock (Hexblade)"
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
"""
Swap the weapon's attack bonus modifier for Charisma if
it is higher than STR/DEX bonus
"""
if weapon.is_finesse:
existing_mod = max(self.owner.strength.modifier,
self.owner.dexterity.modifier)
else:
existing_mod = self.owner.strength.modifier
cha_mod = self.owner.charisma.modifier
if cha_mod > existing_mod:
weapon.attack_bonus += (cha_mod - existing_mod)
return weapon
class AccursedSpecter(Feature):
"""Starting at 6th level, you can curse the soul of a person you slay,
temporarily binding it to your service. When you slay a humanoid, you can
cause its Spirit to rise from its corpse as a specter, the statistics for
which are in the Monster Manual. When the specter appears, it gains
temporary hit points equal to halfyour warlock level. Roll initiative for
the specter, which has its own turns. It obeys your verbal commands, and it
gains a special bonus to its attack rolls equal to your Charisma modifier
(minimum of +0).
The specter remains in your service until the end of your next long rest,
at which point it vanishes to the afterlife.
Once you bind a specter with this feature, you cant use the feature again
until you finish a long rest.
"""
name = "Accursed Specter"
source = "Warlock (Hexblade)"
class ArmorOfHexes(Feature):
"""At 10th level, your hex grows more powerful. If the tar- get cursed by your
Hexblades Curse hits you with an attack roll, you can use your reaction to
roll a d6. On a 4 or higher, the attack instead misses you, regardless of
its roll.
"""
name = "Armor of Hexes"
source = "Warlock (Hexblade)"
class MasterOfHexes(Feature):
"""Starting at 14th level, you can spread your Hexblades Curse from a slain
creature to another creature. When the creature cursed by your Hexblades
Curse dies, you can apply the curse to a different creature you can see
within 30 feet of you, provided you arent incapacitated. When you apply
the curse in this way, you dont regain hit points from the death of the
previously cursed creature.
"""
name = "Master of Hexes"
source = "Warlock (Hexblade)"
# All Invocations
@@ -21,7 +305,8 @@ class Invocation(Feature):
self.spells_known += (s,)
self.spells_prepared += (s,)
def __init__(self):
def __init__(self, owner):
super().__init__(owner)
for s in self.at_will_spells:
self.cast_spell_at_will(s)
@@ -33,7 +318,6 @@ class AgonizingBlast(Invocation):
"""
name = "Agonizing Blast"
needs_implementation = True
class ArmorOfShadows(Invocation):
@@ -281,7 +565,6 @@ class ThiefOfFiveFates(Invocation):
"""
name = "Thief of Five Fates"
needs_implementation = True
class ThirstingBlade(Invocation):
@@ -333,3 +616,184 @@ class WitchSight(Invocation):
"""
name = "Witch Sight"
# XGTE
class AspectOfTheMoon(Invocation):
"""You no longer need to sleep and cant be forced to sleep by any means. To
gain the benefits of a long rest, you can spend all 8 hours doing light
activity, such as read- ing your Book of Shadows and keeping watch.
**Prerequisite**: Pact of the Tome
"""
name = "Aspect of the Moon"
class CloakOfFiles(Invocation):
"""As a bonus action, you can surround yourselfwith a magical aura that looks
like buzzing flies. The aura ex- tends 5 feet from you in every direction,
but not through total cover. It lasts until youre incapacitated or you
dismiss it as a bonus action.
The aura grants you advantage on Charisma
(Intimidation) checks but disadvantage on all other Charisma checks. Any
other creature that starts its turn in the aura takes poison damage equal
to your Charisma mod- ifier (minimum of O damage).
Once you use this invocation, you cant use it again until you finish a
short or long rest.
**Prerequisite**: 5th level
"""
name = "Cloak of Flies"
class EldritchSmite(Invocation):
"""Once per turn when you hit a creature with your pact weapon, you can expend
a warlock spell slot to deal an extra 1d8 force damage to the target, plus
another 1d8 per level of the spell slot, and you can knock the target
prone if it is Huge or smaller.
**Prerequisite**: 5th level, Pact of the Blade
"""
name = "Eldritch Smite"
class GhostlyGaze(Invocation):
"""As an action, you gain the ability to see through solid objects to a range
of 30 feet. Within that range, you have darkvision if you dont already
have it. This special sight lasts for 1 minute or until your concentration
ends (as if you were concentrating on a spell). During that time, you
perceive objects as ghostly, transparent images.
Once you use this invocation, you cant use it again until you finish a
short or long rest.
**Prerequisite**: 7th level
"""
name = "Ghostly Gaze"
class GiftOfTheDepths(Invocation):
"""You can breathe underwater, and you gain a swimming speed equal to your
walking speed. You can also cast water breathing once without ex- pending a
spell slot. You regain the ability to do so when you finish a long rest.
**Prerequisite**: 5th level
"""
name = "Gift of the Depths"
class GiftOfTheEverLivingOnes(Invocation):
"""Whenever you regain hit points while your familiar is within 100 feet
ofyou, treat any dice rolled to determine the hit points you regain as
having rolled their maxi- mum value for you.
**Prerequisite**: Pact of the Chain
"""
name = "Gift of the Ever-Living Ones"
class GraspOfHadar(Invocation):
"""Once on each ofyour turns when you hit a creature with your eldritcli
blast, you can move that creature in a straight line 10 feet closer to you
"""
name = "Grasp of Hadar"
class ImprovedPactWeapon(Invocation):
"""You can use any weapon you summon with your Pact of the Blade feature as a
spellcasting focus for your waru lock spells.
In addition, the weapon gains a +1 bonus to its attack and damage rolls,
unless it is a magic weapon that already has a bonus to those rolls.
Finally, the weapon you conjure can be a shortbow, longbow, light crossbow,
or heavy crossbow.
**Prerequisite**: Pact of the Blade
"""
name = "Improved Pact Weapon"
def weapon_func(self, weapon: weapons.Weapon, **kwargs):
"""
Add +1 to attack and damage if magic is not already magic
"""
if weapon.magic_bonus <= 1:
weapon.magic_bonus = 1
weapon.attack_bonus += 1
weapon.bonus_damage += 1
return weapon
class LanceOfLethargy(Invocation):
"""Once on each of your turns when you hit a creature with your eldritch
blast, you can reduce that creatures speed by 10 feet until the end ofyour
next turn.
"""
name = "Lance of Lethargy"
class MaddeningHex(Invocation):
"""As a bonus action, you cause a psychic disturbance around the target cursed
by your hex spell or by a warlock feature of yours, such as Hexblades
Curse or Sign of Ill Omen. When you do so, you deal psychic damage to the
cursed target and each creature of your choice that you can see within 5
feet of it. The psychic damage equals your Charisma modifier (minimum of 1
dam- age). To use this invocation, you must be able to see the cursed
target, and it must be within 30 feet ofyou.
**Prerequisite**: 5th level
"""
name = "Maddening Hex"
class RelentlessHex(Invocation):
"""Your curse creates a temporary bond between you and your target. As a bonus
action, you can magically telerport up to 30 feet to an unoccupied space
you can see within 5 feet of the target cursed by your hex spell or by a
warlock feature ofyours, such as Hexblades Curse or Sign of 111 Omen. To
teleport in this way, you must be able to see the cursed target.
"""
name = "Relentless Hex"
class ShroudOfShadow(Invocation):
"""You can cast invisibility at will, without expending a spell slot.
**Prerequisite**: 15th Level
"""
at_will_spells = (spells.Invisibility,)
name = "Shroud of Shadow"
class TombOfLevistus(Invocation):
"""As a reaction when you take damage, you can entomb yourself in ice, which
melts away at the end ofyour next turn. You gain 10 temporary hit points
per warlock level, which take as much of the triggering damage as
possible. Immediately after you take the damage, you gain vulnerability to
fire damage, your speed is reduced to 0, and you are incapacitated. These
effects, including any remaining temporary hit points, all end when the ice
melts.
Once you use this invocation, you cant use it again until you finish a
short or long rest.
**Prerequisite**: 5th Level
"""
name = "Tomb of Levistus"
class TrickstersEscape(Invocation):
"""You can cast freedom ofmovement once on yourself without expending a spell
slot. You regain the ability to do so when you finish a long rest
"""
name = "Tricksters Escape"
+19 -8
View File
@@ -17,8 +17,10 @@ subclasses = {{ char.subclasses }} # ex: ['Necromacy'] or ['Thief', None]
background = "{{ char.background.name }}"
race = "{{ char.race.name }}"
alignment = "{{ char.alignment }}"
xp = {{ char.xp }}
hp_max = {{ char.hp_max }}
inspiration = {{ char.inspiration }} # integer inspiration value
# Ability Scores
strength = {{ char.strength.value }}
@@ -32,10 +34,15 @@ charisma = {{ char.charisma.value }}
# ex: skill_proficiencies = ('athletics', 'acrobatics', 'arcana')
skill_proficiencies = {{ char.skill_proficiencies }}
# Named features / feats that aren't part of your classes,
# race, or background. Also include Eldritch Invocations.
# Any skills you have "expertise" (Bard/Rogue) in
skill_expertise = {{ char.skill_expertise }}
# Named features / feats that aren't part of your classes, race, or background.
# Also include Eldritch Invocations and features you make multiple selection of
# (like Maneuvers for Fighter, Metamagic for Sorcerors, Trick Shots for
# Gunslinger, etc.)
# Example:
# features = ('Tavern Brawler',) # take the optional Feat from PHB
# features = ('Tavern Brawler',) # take the optional Feat from PHB
features = {{ char.custom_features }}
# If selecting among multiple feature options: ex Fighting Style
@@ -43,6 +50,10 @@ features = {{ char.custom_features }}
# feature_choices = ('Archery',)
feature_choices = {{ char.feature_choices }}
# Weapons/other proficiencies not given by class/race/background
weapon_proficiencies = {{ char.other_weapon_proficiencies_text }}
_proficiencies_text = {{ char._proficiencies_text }}
# Proficiencies and languages
languages = """{{ char.languages }}"""
@@ -55,10 +66,10 @@ gp = 0
pp = 0
# TODO: Put your equipped weapons and armor here
weapons = {{ char.weapons }} # Example: ('shortsword', 'longsword')
magic_items = {{ char.magic_items }} # Example: ('ring of protection',)
armor = "{{ char.armor }}" # Eg "light leather armor"
shield = "{{ char.shield }}" # Eg "shield"
weapons = {{ char.weapons }} # Example: ('shortsword', 'longsword')
magic_items = {{ char.magic_items }} # Example: ('ring of protection',)
armor = "{{ char.armor }}" # Eg "light leather armor"
shield = "{{ char.shield }}" # Eg "shield"
equipment = """{{ char.equipment }}"""
@@ -66,7 +77,7 @@ attacks_and_spellcasting = """{{ char.attacks_and_spellcasting }}"""
# List of known spells
# Example: spells = ('magic missile', 'mage armor')
spells = {{ char.spells }} # Todo: Learn some spells
spells = {{ char.spells }}
# Which spells have been prepared (not including cantrips)
spells_prepared = {{ char.spells_prepared }}
+18 -6
View File
@@ -20,8 +20,10 @@ subclasses = {{ char.subclasses }} # ex: ['Necromacy'] or ['Thief', None]
background = "{{ char.background.name }}"
race = "{{ char.race.name }}"
alignment = "{{ char.alignment }}"
xp = {{ char.xp }}
hp_max = {{ char.hp_max }}
inspiration = 0 # integer inspiration value
# Ability Scores
strength = {{ char.strength.value }}
@@ -35,9 +37,15 @@ charisma = {{ char.charisma.value }}
# ex: skill_proficiencies = ('athletics', 'acrobatics', 'arcana')
skill_proficiencies = {{ char.skill_proficiencies }}
# Any skills you have "expertise" (Bard/Rogue) in
skill_expertise = ()
# Named features / feats that aren't part of your classes, race, or background.
# Also include Eldritch Invocations and features you make multiple selection of
# (like Maneuvers for Fighter, Metamagic for Sorcerors, Trick Shots for
# Gunslinger, etc.)
# Example:
# features = ('Tavern Brawler',) # take the optional Feat from PHB
# features = ('Tavern Brawler',) # take the optional Feat from PHB
features = ()
# If selecting among multiple feature options: ex Fighting Style
@@ -45,6 +53,10 @@ features = ()
# feature_choices = ('Archery',)
feature_choices = ()
# Weapons/other proficiencies not given by class/race/background
weapon_proficiencies = () # ex: ('shortsword', 'quarterstaff')
_proficiencies_text = () # ex: ("thieves' tools",)
# Proficiencies and languages
languages = """{{ char.languages }}"""
@@ -57,10 +69,10 @@ gp = 0
pp = 0
# TODO: Put your equipped weapons and armor here
weapons = () # Example: ('shortsword', 'longsword')
magic_items = () # Example: ('ring of protection',)
armor = "" # Eg "light leather armor"
shield = "" # Eg "shield"
weapons = () # Example: ('shortsword', 'longsword')
magic_items = () # Example: ('ring of protection',)
armor = "" # Eg "light leather armor"
shield = "" # Eg "shield"
equipment = """TODO: list the equipment and magic items your character carries"""
@@ -69,7 +81,7 @@ or uses spells."""
# List of known spells
# Example: spells_prepared = ('magic missile', 'mage armor')
spells_prepared = () # Todo: Learn some spells
spells_prepared = () # Todo: Learn some spells
# Which spells have not been prepared
__spells_unprepared = ()
+1 -1
View File
@@ -15,7 +15,7 @@
[% for sc in character.subclasses if sc not in ['', None, 'None', 'none']%]
\section*{[[ sc.name ]]}
\section*{Subclass: [[ sc.name ]]}
[[ sc.__doc__|rst_to_latex ]]
+2 -1
View File
@@ -200,6 +200,7 @@ def create_character_pdf(character, basename, flatten=False):
'Race ': str(character.race),
'Alignment': character.alignment,
'XP': str(character.xp),
'Inspiration': str(character.inspiration),
# Abilities
'ProfBonus': mod_str(character.proficiency_bonus),
'STRmod': str(character.strength.value),
@@ -215,7 +216,7 @@ def create_character_pdf(character, basename, flatten=False):
'CHamod': str(character.charisma.value),
'CHA': mod_str(character.charisma.modifier),
'AC': str(character.armor_class),
'Initiative': mod_str(character.dexterity.modifier),
'Initiative': str(character.initiative),
'Speed': str(character.speed),
'Passive': 10 + character.perception,
# Saving throws (proficiencies handled later)
+10 -10
View File
@@ -24,7 +24,6 @@ class Race():
charisma_bonus = 0
hit_point_bonus = 0
spells_known = ()
spells_prepared = ()
def __init__(self, owner=None):
self.owner = owner
@@ -35,12 +34,17 @@ class Race():
for i in range(1, 21):
self.features_by_level[i] = [f(owner=self.owner)for f in
cls.features_by_level[i]]
self.spells_known = [S() for S in cls.spells_known]
@property
def spells_prepared(self):
return self.spells_known
def __str__(self):
return self.name
def __repr__(self):
return f"<self.name>"
return "\"{:s}\"".format(self.name)
# Dwarves
@@ -107,8 +111,7 @@ class DarkElf(Elf):
charisma_bonus = 1
features = (feats.SuperiorDarkvision, feats.FeyAncestry, feats.Trance,
feats.SunlightSensitivity, feats.DrowMagic)
spells_known = (spells.DancingLights(),)
spells_prepared = (spells.DancingLights(),)
spells_known = (spells.DancingLights,)
# Halflings
@@ -174,8 +177,7 @@ class ForestGnome(Gnome):
dexterity_bonus = 1
features = Gnome.features + (feats.NaturalIllusionist,
feats.SpeakWithSmallBeasts)
spells_known = (spells.MinorIllusion(),)
spells_prepared = (spells.MinorIllusion(),)
spells_known = (spells.MinorIllusion,)
class RockGnome(Gnome):
@@ -242,8 +244,7 @@ class Aasimar(Race):
languages = ("Common", "Celestial")
features = (feats.Darkvision, feats.CelestialResistance,
feats.HealingHands, feats.LightBearer)
spells_known = (spells.Light(),)
spells_prepared = (spells.Light(),)
spells_known = (spells.Light,)
# Protector Aasimar
@@ -348,8 +349,7 @@ class Triton(Race):
features = (feats.Amphibious, feats.ControlAirAndWater,
feats.EmissaryOfTheSea, feats.GuardiansOfTheDepths)
languages = ("Common", "Primordial")
spells_known = (spells.FogCloud(),)
spells_prepared = (spells.FogCloud(),)
spells_known = (spells.FogCloud,)
# Aarakocra
+1
View File
@@ -5,3 +5,4 @@ from .spells_j_m import *
from .spells_n_r import *
from .spells_s_u import *
from .spells_v_z import *
from .unsorted_spells import *
+34
View File
@@ -1221,6 +1221,40 @@ class BurningHands(Spell):
classes = ('Wizard', )
class CallLightning(Spell):
"""A storm cloud appears in the shape of a cylinder that is 10 feet tall with a
60-foot radius, centered on a point you can see 100 feet directly above
you. The spell fails if you can't see a point in the air where the storm
cloud could appear (for example, if you are in a room that can't
accommodate the cloud).
When you cast the spell, choose a point you can see within range. A bolt of
lightning flashes down from the cloud to that point. Each creature within 5
feet of that point must make a Dexterity saving throw. A creature takes
3d10 lightning damage on a failed save, or half as much damage on a
successful one. On each of your turns until the spell ends, you can use
your action to call down lightning in this way again, targeting the same
point or a different one.
If you are outdoors in stormy conditions when you cast this spell, the
spell gives you control over the existing storm instead of creating a new
one. Under such conditions, the spell's damage increases by 1d10.
At Higher Levels: When you cast this spell using a spell slot of 4th level
or higher level, the damage increases by 1d10 for each slot level above
3rd.
"""
name = "Call Lightning"
level = 3
casting_time = "1 action"
components = ('V', 'S')
materials = ""
duration = "Up to 10 minutes"
magic_school = "Conjuration"
classes = ('Druid',)
class ChainLightning(Spell):
"""You create a bolt of lightning that arcs toward a target of your
choice that you can see within range. Three bolts then leap from
+793
View File
@@ -0,0 +1,793 @@
from .spells import Spell
class ControlWater(Spell):
"""Until the spell ends, you control any freestanding water inside an area you
choose that is a cube up to 100 feet on a side. You can choose from any of the
following effects when you cast this spell. As an action on your turn, you can
repeat the same effect or choose a different one.
Flood: You cause the water
level of all standing water in the area to rise by as much as 20 feet. If the
area includes a shore, the flooding water spills over onto dry land. If you
choose an area in a large body of water, you instead create a 20-foot tall wave
that travels from one side of the area to the other and then crashes down. Any
Huge or smaller vehicles in the wave's path are carried with it to the other
side. Any Huge or smaller vehicles struck by the wave have a 25 percent chance
of capsizing. The water level remains elevated until the spell ends or you
choose a different effect. If this effect produced a wave, the wave repeats on
the start of your next turn while the flood effect lasts.
Part Water: You cause
water in the area to move apart and create a trench. The trench extends across
the spell's area, and the separated water forms a wall to either side. The
trench remains until the spell ends or you choose a different effect. The water
then slowly fills in the trench over the course of the next round until the
normal water level is restored.
Redirect Flow: You cause flowing water in the
area to move in a direction you choose, even if the water has to flow over
obstacles, up walls, or in other unlikely directions. The water in the area
moves as you direct it, but once it moves beyond the spell's area, it resumes
its flow based on the terrain conditions. The water continues to move in the
direction you chose until the spell ends or you choose a different effect.
Whirlpool: This effect requires a body of water at least 50 feet square and 25
feet deep. You cause a whirlpool to form in the center of the area. The
whirlpool forms a vortex that is 5 feet wide at the base, up to 50 feet wide at
the top, and 25 feet tall. Any creature or object in the water and within 25
feet of the vortex is pulled 10 feet toward it. A creature can swim away from
the vortex by making a Strength (Athletics) check against your spell save DC.
When a creature enters the vortex for the first time on a turn or starts its
turn there, it must make a Strength saving throw. On a failed save, the creature
takes 2d8 bludgeoning damage and is caught in the vortex until the spell ends.
On a successful save, the creature takes half damage, and isn't caught in the
vortex. A creature caught in the vortex can use its action to try to swim away
from the vortex as described above, but has disadvantage on the Strength
(Athletics) check to do so. The first time each turn that an object enters the
vortex, the object takes 2d8 bludgeoning damage, this damage occurs each round
it remains in the vortex.
"""
name = "Control Water"
level = 4
casting_time = "1 action"
casting_range = "300 feet"
components = ('V', 'S', 'M')
materials = "A drop of water and a pinch of dust"
duration = "Concentration, up to 10 minutes"
magic_school = "Transmutation"
classes = ('Cleric', 'Druid', 'Wizard')
class SleetStorm(Spell):
"""Until the spell ends, freezing rain and sleet fall in a 20-foot-tall cylinder
with a 40-foot radius centered on a point you choose within range. The area is
heavily obscured, and exposed flames in the area are doused.
The ground in the
area is covered with slick ice, making it difficult terrain. When a creature
enters the spell's area for the first time on a turn or starts its turn there,
it must make a Dexterity saving throw. On a failed save, it falls prone.
If a
creature is concentrating in the spell's area, the creature must make a
successful Constitution saving throw against your spell save DC or lose
concentration.
"""
name = "Sleet Storm"
level = 3
casting_time = "1 action"
casting_range = "150 feet"
components = ('V', 'S', 'M')
materials = "A pinch of dust and a few drops of water"
duration = "Concentration, up to 1 minute"
magic_school = "Conjuration"
classes = ('Druid', 'Sorcerer', 'Wizard')
class DestructiveWave(Spell):
"""You strike the ground, creating a burst of divine energy that ripples
outward from you. Each creature you choose within 30 feet of you must
succeed on a Constitution saving throw or take 5d6 thunder damage, as well
as 5d6 radiant or necrotic damage (your choice), and be knocked prone. A
creature that succeeds on its saving throw takes half as much damage and
isnt knocked prone.
"""
name = "Destructive Wave"
level = 5
casting_time = "1 action"
casting_range = "Self (30 foot radius)"
components = ("V",)
magic_school = "Evocation"
classes = ("Paladin",)
class InsectPlague(Spell):
"""Swarming, biting locusts fill a 20-foot-radius sphere centered on a point you
choose within range. The sphere spreads around corners. The sphere remains for
the duration, and its area is lightly obscured. The sphere's area is difficult
terrain.
When the area appears, each creature in it must make a Constitution
saving throw. A creature takes 4d10 piercing damage on a failed save, or half as
much damage on a successful one. A creature must also make this saving throw
when it enters the spell's area for the first time on a turn or ends its turn
there.
At Higher Levels: When you cast this spell using a spell slot of 6th
level or higher, the damage increases by 1d10 for each slot level above 5th.
"""
name = "Insect Plague"
level = 5
casting_time = "1 action"
casting_range = "300 feet"
components = ('V', 'S', 'M')
materials = "A few grains of sugar, some kernels of grain, and a smear of fat"
duration = "Concentration, Up to 10 minutes"
magic_school = "Conjuration"
classes = ('Cleric', 'Druid', 'Sorcerer')
class FindFamiliar(Spell):
"""You gain the service of a familiar, a spirit that takes an animal form you
choose: bat, cat, crab, frog (toad), hawk, lizard, octopus, owl, poisonous
snake, fish (quipper), rat, raven, sea horse, spider, or weasel. Appearing in an
unoccupied space within range, the familiar has the statistics of the chosen
form, though it is a celestial, fey, or fiend (your choice) instead of a beast.
Your familiar acts independently of you, but it always obeys your commands. In
combat, it rolls its own initiative and acts on its own turn. A familiar can't
attack, but it can take other actions as normal.
When the familiar drops to 0
hit points, it disappears, leaving behind no physical form. It reappears after
you cast this spell again.
While your familiar is within 100 feet of you, you
can communicate with it telepathically. Additionally, as an action, you can see
through your familiar's eyes and hear what it hears until the start of your next
turn, gaining the benefits of any special senses that the familiar has. During
this time, you are deaf and blind with regard to your own senses.
As an action,
you can temporarily dismiss your familiar. It disappears into a pocket
dimension where it awaits your summons. Alternatively, you can dismiss it
forever. As an action while it is temporarily dismissed, you can cause it to
reappear in any unoccupied space within 30 feet of you.
You can't have more
than one familiar at a time. If you cast this spell while you already have a
familiar, you instead cause it to adopt a new form. Choose one of the forms from
the above list. Your familiar transforms into the chosen creature.
Finally,
when you cast a spell with a range of touch, your familiar can deliver the spell
as if it had cast the spell. Your familiar must be within 100 feet of you, and
it must use its reaction to deliver the spell when you cast it. If the spell
requires an attack roll, you use your attack modifier for the roll.
"""
name = "Find Familiar"
level = 1
casting_time = "1 hour"
casting_range = "10 feet"
components = ('V', 'S', 'M')
materials = ("10 gp worth of charcoal, incense, and herbs that must be"
"consumed by fire in a brass brazier")
duration = "Instantaneous"
magic_school = "Conjuration"
classes = ('Wizard',)
class ProtectionFromEvilAndGood(Spell):
"""Until the spell ends, one willing creature you touch is protected against
certain types of creatures - aberrations, celestials, elementals, fey, fiends,
and undead.
The protection grants several benefits. Creatures of those types
have disadvantage on attack rolls against the target. The target also can't be
charmed, frightened, or possessed by them. If the target is already charmed,
frightened, or possessed by such a creature, the target has advantage on any new
saving throw against the relevant effect.
"""
name = "Protection From Evil And Good"
level = 1
casting_time = "1 action"
casting_range = "Touch"
components = ('V', 'S', 'M')
materials = "Holy water or powdered silver and iron, which the spell consumes"
duration = "Concentration, Up to 10 minutes"
magic_school = "Abjuration"
classes = ('Cleric', 'Paladin', 'Warlock', 'Wizard')
class ZoneOfTruth(Spell):
"""You create a magical zone that guards against deception in a 15-foot-radius
sphere centered on a point of your choice within range. Until the spell ends, a
creature that enters the spell's area for the first time on a turn or starts its
turn there must make a Charisma saving throw. On a failed save, a creature
can't speak a deliberate lie while in the radius. You know whether each creature
succeeds or fails on its saving throw.
An affected creature is aware of the
spell and can thus avoid answering questions to which it would normally respond
with a lie. Such creatures can be evasive in its answers as long as it remains
within the boundaries of the truth.
"""
name = "Zone Of Truth"
level = 2
casting_time = "1 action"
casting_range = "60 feet"
components = ('V', 'S')
materials = ""
duration = "10 minutes"
magic_school = "Enchantment"
classes = ('Bard', 'Cleric', 'Paladin')
class EnsnaringStrike(Spell):
"""The next time you hit a creature with a weapon attack before this spell
ends, a writhing mass of thorny vines appears at the point of impact, and
the target must succeed on a Strength saving throw or be restrained by the
magical vines until the spell ends. A Large or larger creature has
advantage on this saving throw. If the target succeeds on the save, the
vines shrivel away.
While restrained by this spell, the target takes 1d6 piercing damage at the
start of each of its turns. A creature restrained by the vines or one that
can touch the creature can use its action to make a Strength check against
your spell save DC. On a success, the target is freed.
At Higher Level:
If you cast this spell using a spell slot of 2nd level or higher, the
damage increases by 1d6 for each slot level above 1st.
"""
name = "Ensnaring Strike"
level = 1
casting_time = '1 bonus action'
casting_range = 'Self'
components = ('V')
duration = 'Concentration, up to 1 minute'
magic_school = 'Conjuration'
classes = ("Ranger",)
class Moonbeam(Spell):
"""A silvery beam of pale light shines down in a 5-foot radius, 40-foot-high
cylinder centered on a point within range. Until the spell ends, dim light fills
the cylinder.
When a creature enters the spell's area for the first time on a
turn or starts its turn there, it is engulfed in ghostly flames that cause
searing pain, and it must make a Constitution saving throw. It takes 2d10
radiant damage on a failed save, or half as much damage on a successful one.
A
shapechanger makes its saving throw with disadvantage. If it fails, it also
instantly reverts to its original form and can't assume a different form until
it leaves the spell's light.
On each of your turns after you cast this spell,
you can use an action to move the beam 60 feet in any direction.
At Higher
Levels: When you cast this spell using a spell slot of 3rd level or higher, the
damage increases by 1d10 for each slot level above 2nd.
"""
name = "Moonbeam"
level = 2
casting_time = "1 action"
casting_range = "120 feet"
components = ('V', 'S', 'M')
materials = "Several seeds of any moonseed plant and a piece of opalescent feldspar"
duration = "Concentration, Up to 1 minute"
magic_school = "Evocation"
classes = ('Druid',)
class PlantGrowth(Spell):
"""This spell channels vitality into plants within a specific area. There are two
possible uses for the spell, granting either immediate or long-term benefits.
If you cast this spell using 1 action, choose a point within range. All normal
plants in a 100-foot radius centered on that point become thick and overgrown. A
creature moving through the area must spend 4 feet of movement for every 1 foot
it moves.
You can exclude one or more areas of any size within the spell's
area from being affected.
If you cast this spell over 8 hours, you enrich the
land. All plants in a half-mile radius centered on a point within range become
enriched for 1 year. The plants yield twice the normal amount of food when
harvested.
"""
name = "Plant Growth"
level = 3
casting_time = "1 action or 8 hours"
casting_range = "150 feet"
components = ('V', 'S')
materials = ""
duration = "Instantaneous"
magic_school = "Transmutation"
classes = ('Bard', 'Druid', 'Ranger')
class IceStorm(Spell):
"""A hail of rock-hard ice pounds to the ground in a 20-foot-radius, 40-foot-high
cylinder centered on a point within range. Each creature in the cylinder must
make a Dexterity saving throw. A creature takes 2d8 bludgeoning damage and 4d6
cold damage on a failed save, or half as much damage on a successful one.
Hailstones turn the storm's area of effect into difficult terrain until the end
of your next turn.
At Higher Levels: When you cast this spell using a spell
slot of 5th level or higher, the bludgeoning damage increases by 1d8 for each
slot level above 4th.
"""
name = "Ice Storm"
level = 4
casting_time = "1 action"
casting_range = "300 feet"
components = ('V', 'S', 'M')
materials = "A pinch of dust and a few drops of water"
duration = "Instantaneous"
magic_school = "Evocation"
classes = ('Druid', 'Sorcerer', 'Wizard')
class CommuneWithNature(Spell):
"""You briefly become one with nature and gain knowledge of the surrounding
territory. In the outdoors, the spell gives you knowledge of the land within 3
miles of you. In caves and other natural underground settings, the radius is
limited to 300 feet. The spell doesn't function where nature has been replaced
by construction, such as in dungeons and towns.
You instantly gain knowledge of
up to three facts of your choice about any of the following subjects as they
relate to the area - terrain and bodies of water; prevalent plants, minerals,
animals, or peoples; powerful celestials, fey, fiends, elementals, or undead;
influence from other planes of existence; buildings.
For example, you could
determine the location of powerful undead in the area, the location of major
sources of safe drinking water, and the location of any nearby towns.
"""
name = "Commune With Nature"
level = 5
casting_time = "1 minute"
casting_range = "Self"
components = ('V', 'S')
materials = ""
duration = "Instantaneous"
magic_school = "Divination"
classes = ('Druid', 'Ranger')
class TreeStride(Spell):
"""You gain the ability to enter a tree and move from inside it to inside another
tree of the same kind within 500 feet. Both trees must be living and at least
the same size as you. You must use 5 feet of movement to enter a tree. You
instantly know the location of all other trees of the same kind within 500 feet
and, as part of the move used to enter the tree, can either pass into one of
those trees or step out of the tree you're in. You appear in a spot of your
choice within 5 feet of the destination tree, using another 5 feet of movement.
If you have no movement left, you appear within 5 feet of the tree you entered.
You can use this transportation ability once per round for the duration. You
must end each turn outside a tree.
"""
name = "Tree Stride"
level = 5
casting_time = "1 action"
casting_range = "Self"
components = ('V', 'S')
materials = ""
duration = "Concentration, Up to 1 minute"
magic_school = "Conjuration"
classes = ('Druid', 'Ranger')
class HoldMonster(Spell):
"""Choose a creature that you can see within range. The target must succeed on a
Wisdom saving throw or be paralyzed for the duration. This spell has no effect
on undead. At the end of each of its turns, the target can make another Wisdom
saving throw. On a success, the spell ends on the target.
At Higher Levels:
When you cast this spell using a spell slot of 6th level or higher, you can
target on additional creature for each slot level above 5th. The creatures must
be within 30 feet of each other when you target them.
"""
name = "Hold Monster"
level = 5
casting_time = "1 action"
casting_range = "90 feet"
components = ('V', 'S', 'M')
materials = "A small, straight piece of iron"
duration = "Concentration, Up to 1 minute"
magic_school = "Enchantment"
classes = ('Bard', 'Sorcerer', 'Warlock', 'Wizard')
class Scrying(Spell):
"""You can see and hear a particular creature you choose that is on the same plane
of existence as you. The target must make a Wisdom saving throw, which is
modified by how well you know the target and the sort of physical connection you
have to it. If a target knows you're casting this spell, it can fail the saving
throw voluntarily if it wants to be observed.
Knowledge - Save Modifier
Secondhand (you have heard of the target) - +5
Firsthand (you have met the
target) +0
Familiar (you know the target well) - -5
Connection - Save Modifier
Likeness or picture - -2
Possession or garment - -4
Body part, lock of hair,
bit of nail, or the like - -10
On a successful save, the target isn't affected, and you can't use this
spell against it again for 24 hours.
On a failed save, the spell creates an invisible sensor within 10 feet of
the target. You can see and hear through the sensor as if you were
there. The sensor moves with the target, remaining within 10 feet of it for
the duration. A creature that can see invisible objects sees the sensor as
a luminous orb about the size of your fist.
Instead of targeting a creature, you can choose a location you
have seen before as the target of this spell. When you do, the sensor appears at
that location and doesn't move.
"""
name = "Scrying"
level = 5
casting_time = "10 minutes"
casting_range = "Self"
components = ('V', 'S', 'M')
materials = ("A focus worth at least 1,000 gp, such as a crystal ball, "
"a silver mirror, or a font filled with holy water")
duration = "Concentration, Up to 10 minutes"
magic_school = "Divination"
classes = ('Bard', 'Cleric', 'Druid', 'Warlock', 'Wizard')
class CompelledDuel(Spell):
"""You attempt to compel a creature into a duel. One creature that you can
see within range must make a Wisdom saving throw. On a failed save, the
creature is drawn to you, compelled by your divine demand. For the
duration, it has disadvantage on attack rolls against creatures other than
you, and must make a Wisdom saving throw each time it attempts to move to a
space that is more than 30 feet away from you; if it succeeds on this
saving throw, this spell doesnt restrict the targets movement for that
turn.
The spell ends if you attack any other creature, if you cast a spell that
targets a hostile creature other than the target, if a creature friendly to
you damages the target or casts a harmful spell on it, or if you end your
turn more than 30 feet away from the target.
"""
name = "Compelled Duel"
level = 1
casting_time = "1 bonus action"
casting_range = "30 feet"
components = ("V",)
duration = "Concentration, up to 10 minutes"
magic_school = "Enchantment"
classes = ('Paladin',)
class CircleOfPower(Spell):
"""Divine energy radiates from you, distorting and diffusing magical energy
within 30 feet of you. Until the spell ends, the sphere moves with you,
centered on you. For the duration, each friendly creature in the area
(including you) has advantage on saving throws against spells and other
magical effects.
Additionally, when an affected creature succeeds on a saving throw made
against a spell or magical effect that allows it to make a saving throw to
take only half damage, it instead takes no damage if it succeeds on the
saving throws. """
name = "Circle of Power"
level = 5
casting_time = '1 action'
casting_range = 'Self (30 foot radius)'
components = ('V',)
duration = "Concentration, up to 10 minutes"
magic_school = "Abjuration"
classes = ('Paladin',)
class Geas(Spell):
"""You place a magical command on a creature that you can see within range, forcing
it to carry out some service or refrain from some action or course of activity
as you decide. If the creature can understand you, it must succeed on a Wisdom
saving throw or become charmed by you for the duration. While the creature is
charmed by you, it takes 5d10 psychic damage each time it acts in a manner
directly counter to your instructions, but no more than once each day. A
creature that can't understand you is unaffected by the spell.
You can issue
any command you choose, short of an activity that would result in certain death.
Should you issue a suicidal command, the spell ends.
You can end the spell
early by using an action to dismiss it. A remove curse, greater restoration, or
wish spell also ends it.
At Higher Levels: When you cast this spell using a
spell slot of 7th or 8th level, the duration is 1 year. When you cast this spell
using a spell slot of 9th level, the spell lasts until it is ended by one of
the spells mentioned above.
"""
name = "Geas"
level = 5
casting_time = "1 minute"
casting_range = "60 feet"
components = ('V',)
materials = ""
duration = "30 days"
magic_school = "Enchantment"
classes = ('Bard', 'Cleric', 'Druid', 'Paladin', 'Wizard')
class BestowCurse(Spell):
"""You touch a creature, and that creature must succeed on a Wisdom saving throw or
become cursed for the duration of the spell. When you cast this spell, choose
the nature of the curse from the following options.
Choose one ability score.
While cursed, the target has disadvantage on ability checks and saving throws
made with that ability score.
While cursed, the target has disadvantage on
attack rolls against you.
While cursed, the target must make a Wisdom saving
throw at the start of each of its turns. If it fails, it wastes its action that
turn doing nothing.
While the target is cursed, your attacks and spells deal
an extra 1d8 necrotic damage to the target.
A remove curse spell ends this
effect. At the DM's option, you may choose an alternative curse effect, but it
should be no more powerful than those described above. The DM has final say on
such a curse's effect.
At Higher Levels: If you cast this spell using a spell
slot of 4th level or higher, the duration is concentration, up to 10 minutes. If
you use a spell slot of 5th level or higher, the duration is 8 hours. If you
use a spell slot of 7th level or higher, the duration is 24 hours. If you use a
9th level spell slot, the spell lasts until it is dispelled. Using a spell slot
of 5th level or higher grants a duration that doesn't require concentration.
"""
name = "Bestow Curse"
level = 3
casting_time = "1 action"
casting_range = "Touch"
components = ('V', 'S')
materials = ""
duration = "Concentration, Up to 1 minute"
magic_school = "Necromancy"
classes = ('Bard', 'Cleric', 'Wizard')
class Fear(Spell):
"""You project a phantasmal image of a creature's worst fears. Each creature in a
30-foot cone must succeed on a Wisdom saving throw or drop whatever it is
holding and become frightened for the duration.
While frightened by this spell,
a creature must take the Dash action and move away from you by the safest
available route on each of its turns, unless there is nowhere to move. If the
creature ends its turn in a location where it doesn't have line of sight to you,
the creature can make a Wisdom saving throw. On a successful save, the spell
ends for that creature.
"""
name = "Fear"
level = 3
casting_time = "1 action"
casting_range = "Self (30-foot radius)"
components = ('V', 'S', 'M')
materials = "A white feather or the heart of a hen"
duration = "Concentration, Up to 1 minute"
magic_school = "Illusion"
classes = ('Bard', 'Sorcerer', 'Warlock', 'Wizard')
class DominateBeast(Spell):
"""You attempt to beguile a beast that you can see within range. It must succeed on
a Wisdom saving throw or be charmed by you for the duration. If you or
creatures that are friendly to you are fighting it, it has advantage on the
saving throw.
While the beast is charmed, you have a telepathic link with it as
long as the two of you are on the same plane of existence. You can use this
telepathic link to issue commands to the creature while you are conscious (no
action required), which it does its best to obey. You can specify a simple and
general course of action, such as Attack that creature, Run over there, or Fetch
that object. If the creature completes the order and doesn't receive further
direction from you, it defends and preserves itself to the best of its ability.
You can use your action to take total and precise control of the target. Until
the end of your next turn, the creature takes only the actions you choose, and
doesn't do anything that you don't allow it to do. During this time, you can
also cause the creature to use a reaction, but this requires you to use your own
reaction as well.
Each time the target takes damage, it makes a new Wisdom
saving throw against the spell. If the saving throw succeeds, the spell ends.
At Higher Levels: When you cast this spell with a 5th-level spell slot, the
duration is concentration, up to 10 minutes. When you use a 6th-level spell
slot, the duration is concentration, up to 1 hour. When you use a spell slot of
7th level or higher, the duration is concentration, up to 8 hours.
"""
name = "Dominate Beast"
level = 4
casting_time = "1 action"
casting_range = "60 feet"
components = ('V', 'S')
materials = ""
duration = "Concentration, Up to 1 minute"
magic_school = "Enchantment"
classes = ('Druid', 'Sorcerer')
class Cloudkill(Spell):
"""You create a 20-foot-radius sphere of poisonous, yellow-green fog centered on a
point you choose within range. The fog spreads around corners. It lasts for the
duration or until strong wind disperses the fog, ending the spell. Its area is
heavily obscured.
When a creature enters the spell's area for the first time on
a turn or starts its turn there, that creature must make a Constitution saving
throw. The creature takes 5d8 poison damage on a failed save, or half as much
damage on a successful one. Creatures are affected even if they hold their
breath or don't need to breathe.
The fog moves 10 feet away from you at the
start of each of your turns, rolling along the surface of the ground. The
vapors, being heavier than air, sink to the lowest level of the land, even
pouring down openings.
At Higher Levels: When you cast this spell using a spell
slot of 6th level or higher, the damage increases by 1d8 for each slot level
above 5th.
"""
name = "Cloudkill"
level = 5
casting_time = "1 action"
casting_range = "120 feet"
components = ('V', 'S')
materials = ""
duration = "Concentration, Up to 10 minutes"
magic_school = "Conjuration"
classes = ('Sorcerer', 'Wizard')
class CalmEmotions(Spell):
"""You attempt to suppress strong emotions in a group of people. Each humanoid in a
20-foot-radius sphere centered on a point you choose within range must make a
Charisma saving throw a creature can choose to fail this saving throw if it
wishes. If a creature fails its saving throw, choose one of the following two
effects.
You can suppress any effect causing a target to be charmed or
frightened. When this spell ends, any suppressed effect resumes, provided that
its duration has not expired in the meantime.
Alternatively, you can make a
target indifferent about creatures of your choice that it is hostile toward.
This indifference ends if the target is attacked or harmed by a spell or if it
witnesses any of its friends being harmed. When the spell ends, the creature
becomes hostile again, unless the DM rules otherwise.
"""
name = "Calm Emotions"
level = 2
casting_time = "1 action"
casting_range = "60 feet"
components = ('V', 'S')
materials = ""
duration = "Concentration, Up to 1 minute"
magic_school = "Enchantment"
classes = ('Bard', 'Cleric')
class HypnoticPattern(Spell):
"""You create a twisting pattern of colors that weaves through the air inside a
30-foot cube within range. The pattern appears for a moment and vanishes. Each
creature in the area who sees the pattern must make a Wisdom saving throw. On a
failed save, the creature becomes charmed for the duration. While charmed by
this spell, the creature is incapacitated and has a speed of 0.
The spell ends
for an affected creature if it takes any damage or if someone else uses an
action to shake the creature out of its stupor.
"""
name = "Hypnotic Pattern"
level = 3
casting_time = "1 action"
casting_range = "120 feet"
components = ('S', 'M')
materials = "A glowing stick of incense or a crystal vial filled with phosphorescent material"
duration = "Concentration, Up to 1 minute"
magic_school = "Illusion"
classes = ('Bard', 'Sorcerer', 'Warlock', 'Wizard')
class OtilukesResilientSphere(Spell):
"""A sphere of shimmering force encloses a creature or object of Large size or
smaller within range. An unwilling creature must make a Dexterity saving
throw. On a failed save, the creature is enclosed for the duration.
Nothing---not physical objects, energy, or other spell effects---can pass
through the barrier, in or out, though a creature in the sphere can breathe
there. The sphere is immune to all damage, and a creature or object inside
cant be damaged by attacks or effects originating from outside, nor can a
creature inside the sphere damage anything outside it.
The sphere is weightless and just large enough to contain the creature or
object inside. An enclosed creature can use its action to push against the
spheres walls and thus roll the sphere at up to half the creatures
speed. Similarly, the globe can be picked up and moved by other creatures.
A disintegrate spell targeting the globe destroys it without harming
anything inside it. """
name = "Otiluke's Resilient Sphere"
level = 4
casting_time = "1 action"
casting_range = "30 feet"
components = ('V', 'S', 'M')
materials = ("a hemispherical piece of clear crystal and a matching "
"hemispherical piece of gum arabic")
duration = "Concentration, up to 1 minute"
magic_school = "Evocation"
classes = ('Wizard',)
class WallOfForce(Spell):
"""An invisible wall of force springs into existence at a point you choose within
range. The wall appears in any orientation you choose, as a horizontal or
vertical barrier or at an angle. It can be free floating or resting on a solid
surface. You can form it into a hemispherical dome or a sphere with a radius of
up to 10 feet, or you can shape a flat surface made up of ten 10-foot-by-10-foot
panels. Each panel must be contiguous with another panel. In any form, the wall
is 1/4 inch thick. It lasts for the duration. If the wall cuts through a
creature's space when it appears, the creature is pushed to one side of the wall
(your choice which side).
Nothing can physically pass through the wall. It is
immune to all damage and can't be dispelled by dispel magic. A disintegrate
spell destroys the wall instantly, however. The wall also extends into the
Ethereal Plane, blocking ethereal travel through the wall.
"""
name = "Wall Of Force"
level = 5
casting_time = "1 action"
casting_range = "120 feet"
components = ('V', 'S', 'M')
materials = "A pinch of powder made by crushing a clear gemstone"
duration = "Concentration, Up to 10 minutes"
magic_school = "Evocation"
classes = ('Wizard',)
+16 -8
View File
@@ -3,7 +3,8 @@ from collections import namedtuple
from .armor import NoArmor, NoShield, HeavyArmor
from .features import (UnarmoredDefenseMonk, UnarmoredDefenseBarbarian,
DraconicResilience, Defense, FastMovement,
UnarmoredMovement)
UnarmoredMovement, GiftOfTheDepths, RemarkableAthelete)
from math import ceil
def findattr(obj, name):
@@ -94,6 +95,10 @@ class Skill():
is_proficient = self.skill_name in character.skill_proficiencies
if is_proficient:
modifier += character.proficiency_bonus
elif any([isinstance(f, RemarkableAthelete) for f in character.features]):
if self.ability_name.lower() in ('strength',
'dexterity', 'constitution'):
modifier += ceil(character.proficiencies_bonus / 2.)
# Check for expertise
is_expert = self.skill_name in character.skill_expertise
if is_expert:
@@ -142,16 +147,19 @@ class Speed():
"""
def __get__(self, char, Character):
base_speed = char.race.speed
speed = char.race.speed
other_speed = ''
if isinstance(base_speed, str):
base_speed = int(base_speed[:2]) # ignore other speeds, like fly
other_speed = base_speed[2:]
if isinstance(speed, str):
other_speed = speed[2:]
speed = int(speed[:2]) # ignore other speeds, like fly
if any([isinstance(f, FastMovement) for f in char.features]):
if not isinstance(char.armor, HeavyArmor):
base_speed += 10
speed += 10
if isinstance(char.armor, NoArmor) or (char.armor is None):
for f in char.features:
if isinstance(f, UnarmoredMovement):
base_speed += f.speed_bonus
return '{:d}{:s}'.format(base_speed, other_speed)
speed += f.speed_bonus
if any([isinstance(f, GiftOfTheDepths) for f in char.features]):
if 'swim' not in other_speed:
other_speed += ' ({:d} swim)'.format(speed)
return '{:d}{:s}'.format(speed, other_speed)
+8 -11
View File
@@ -5,11 +5,16 @@ class Weapon():
bonus_damage = 0
damage_type = "p"
attack_bonus = 0
weight = 1 # In lbs
magic_bonus = 0
weight = 1 # In lbs
properties = "Light"
ability = 'strength'
is_finesse = False
def __init__(self):
self.attack_bonus += self.magic_bonus
self.bonus_damage += self.magic_bonus
@property
def damage(self):
dam_str = str(self.base_damage)
@@ -437,22 +442,14 @@ class Unarmed(MeleeWeapon):
# Custom weapons
class HeavyRight(MeleeWeapon):
class HeavyPunch(MeleeWeapon):
base_damage = "1d4"
name = "Heavy Right"
name = "Heavy Punch"
damage_type = 'b'
bonus_damage = 10 + 2 # Heavy weapon master + Dueling
attack_bonus = -5 # Heavy weapon master
class HeavyLeft(MeleeWeapon):
base_damage = "1d4"
name = "Heavy Left"
damage_type = 'b'
bonus_damage = 10 + 2 - 2 # No proficiency bonus
attack_bonus = -5 # Heavy weapon master
class Bite(MeleeWeapon):
name = "Bite"
base_damage = "1d4"