mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-06-14 00:23:30 +02:00
Added subclasses from PHB, SCAG, XGTE. Need to add features for all
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""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.8.0"
|
||||
|
||||
name = "Ben"
|
||||
classes_levels = ['barbarian 1', 'bard 1', 'cleric 1', 'druid 1', 'fighter 1', 'monk 1', 'ranger 1', 'rogue 1', 'sorceror 1', 'warlock 1', 'wizard 1', 'revised ranger 1']
|
||||
subclasses = ["Path of the Berserker", "College of Lore", "Arcana Domain", "Circle of the Land", "Arcane Archer", "Way of the Open Hand", <dungeonsheets.classes.ranger.Hunter object at 0x108e7ba20>, "Thief", "Draconic Bloodline", "The Archfey Patron", "School of Abjuration", '']
|
||||
player_name = "Ben"
|
||||
background = "Mercenary Veteran"
|
||||
race = "Hill Dwarf"
|
||||
alignment = "Neutral good"
|
||||
xp = 0
|
||||
hp_max = 10
|
||||
|
||||
# Ability Scores
|
||||
strength = 14
|
||||
dexterity = 15
|
||||
constitution = 15
|
||||
intelligence = 12
|
||||
wisdom = 11
|
||||
charisma = 8
|
||||
|
||||
# Select what skills you're proficient with
|
||||
skill_proficiencies = ('nature', 'animal handling', 'athletics', 'persuasion')
|
||||
|
||||
# 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, Dwarvish"""
|
||||
|
||||
# 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')
|
||||
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."""
|
||||
@@ -12,7 +12,8 @@ class Background():
|
||||
languages = ()
|
||||
|
||||
def __init__(self):
|
||||
self.features = tuple([f() for f in self.features])
|
||||
cls = type(self)
|
||||
self.features = tuple([f() for f in cls.features])
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
+31
-24
@@ -27,27 +27,27 @@ __version__ = read('../VERSION')
|
||||
dice_re = re.compile('(\d+)d(\d+)')
|
||||
|
||||
multiclass_spellslots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (0, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (0, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (0, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (0, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (0, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (0, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (0, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (0, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (0, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (0, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (0, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (0, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (0, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
6: (0, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
7: (0, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
8: (0, 4, 3, 3, 2, 0, 0, 0, 0, 0),
|
||||
9: (0, 4, 3, 3, 3, 1, 0, 0, 0, 0),
|
||||
10: (0, 4, 3, 3, 3, 2, 0, 0, 0, 0),
|
||||
11: (0, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
12: (0, 4, 3, 3, 3, 2, 1, 0, 0, 0),
|
||||
13: (0, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
14: (0, 4, 3, 3, 3, 2, 1, 1, 0, 0),
|
||||
15: (0, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
16: (0, 4, 3, 3, 3, 2, 1, 1, 1, 0),
|
||||
17: (0, 4, 3, 3, 3, 2, 1, 1, 1, 1),
|
||||
18: (0, 4, 3, 3, 3, 3, 1, 1, 1, 1),
|
||||
19: (0, 4, 3, 3, 3, 3, 2, 1, 1, 1),
|
||||
20: (0, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,10 @@ class Character():
|
||||
def class_name(self):
|
||||
return ' / '.join([f'{c.class_name} {c.class_level}'
|
||||
for c in self.class_list])
|
||||
|
||||
@property
|
||||
def subclasses(self):
|
||||
return list([c.subclass or '' for c in self.class_list])
|
||||
|
||||
@property
|
||||
def speed(self):
|
||||
@@ -555,16 +559,17 @@ class Character():
|
||||
assert len(classes_levels) == len(subclasses), (
|
||||
'the length of classes_levels {:d} does not match length of '
|
||||
'subclasses {:d}'.format(len(classes_levels), len(subclasses)))
|
||||
circle = char_props.pop('circle', None)
|
||||
class_list = []
|
||||
for cl, sub in zip(classes_levels, subclasses):
|
||||
try:
|
||||
c, lvl = cl.strip().split(' ') # " wizard 3 " => "wizard", "3"
|
||||
c, _, lvl = cl.strip().rpartition(' ') # " wizard 3 " => "wizard", "3"
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
'classes_levels not properly formatted. Each entry should '
|
||||
'be formatted \"class level\", but got {:s}'.format(cl))
|
||||
try:
|
||||
this_class = getattr(classes, c.capitalize())
|
||||
this_class = getattr(classes, c.title.replace(' ', ''))
|
||||
this_level = int(lvl)
|
||||
except AttributeError:
|
||||
raise AttributeError(
|
||||
@@ -572,6 +577,8 @@ class Character():
|
||||
except ValueError:
|
||||
raise ValueError(
|
||||
'level was not recognizable as an int: {:s}'.format(lvl))
|
||||
if issubclass(this_class, classes.Druid):
|
||||
sub = circle or sub
|
||||
params = {}
|
||||
params['feature_choices'] = char_props.get('feature_choices', [])
|
||||
class_list += [this_class(this_level, subclass=sub, **params)]
|
||||
@@ -606,7 +613,7 @@ class Character():
|
||||
self.save(filename,
|
||||
template_file=kwargs.get('template_file',
|
||||
'character_template.txt'))
|
||||
subprocess.call(['makesheets', filename)
|
||||
subprocess.call(['makesheets', filename])
|
||||
|
||||
|
||||
def read_character_file(filename):
|
||||
|
||||
@@ -9,6 +9,7 @@ dungeonsheets_version = "{{ char.dungeonsheets_version }}"
|
||||
|
||||
name = "{{ char.name }}"
|
||||
classes_levels = {{ char.classes_levels }}
|
||||
subclasses = {{ char.subclasses }}
|
||||
player_name = "{{ char.player_name }}"
|
||||
background = "{{ char.background.name }}"
|
||||
race = "{{ char.race.name }}"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
__all__ = ('CharClass', 'Barbarian', 'Bard', 'Cleric', 'Druid', 'Fighter',
|
||||
'Monk', 'Paladin', 'Ranger', 'Rogue', 'Sorceror', 'Warlock',
|
||||
'Wizard', 'Revisedranger', 'available_classes')
|
||||
'Wizard', 'RevisedRanger', 'available_classes')
|
||||
|
||||
from .classes import CharClass
|
||||
from .barbarian import Barbarian
|
||||
@@ -10,11 +10,11 @@ from .druid import Druid
|
||||
from .fighter import Fighter
|
||||
from .monk import Monk
|
||||
from .paladin import Paladin
|
||||
from .ranger import (Ranger, Revisedranger)
|
||||
from .ranger import (Ranger, RevisedRanger)
|
||||
from .rogue import Rogue
|
||||
from .sorceror import Sorceror
|
||||
from .warlock import Warlock
|
||||
from .wizard import Wizard
|
||||
|
||||
available_classes = [Barbarian, Bard, Cleric, Druid, Fighter, Monk, Ranger,
|
||||
Rogue, Sorceror, Warlock, Wizard, Revisedranger]
|
||||
Rogue, Sorceror, Warlock, Wizard, RevisedRanger]
|
||||
|
||||
@@ -1,8 +1,41 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (features, weapons)
|
||||
from .classes import (CharClass, SubClass)
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class BerserkerPath(SubClass):
|
||||
name = "Path of the Berserker"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class TotemWarriorPath(SubClass):
|
||||
name = "Path of the Totem Warrior"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# SCAG
|
||||
class BattleragerPath(SubClass):
|
||||
name = "Path of the Battlerager"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class AncestralGuardianPath(SubClass):
|
||||
name = "Path of the Ancestral Guardian"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class StormHeraldPath(SubClass):
|
||||
name = "Path of the Storm Herald"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class ZealotPath(SubClass):
|
||||
name = "Path of the Zealot"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Barbarian(CharClass):
|
||||
class_name = 'Barbarian'
|
||||
hit_dice_faces = 12
|
||||
@@ -12,4 +45,7 @@ class Barbarian(CharClass):
|
||||
weapon_proficiencies = (weapons.simple_weapons + weapons.martial_weapons)
|
||||
class_skill_choices = ('Animal Handling', 'Athletics',
|
||||
'Intimidation', 'Nature', 'Perception', 'Survival')
|
||||
|
||||
subclasses_available = (BerserkerPath, TotemWarriorPath, BattleragerPath,
|
||||
AncestralGuardianPath, StormHeraldPath, ZealotPath)
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class CollegeOfLore(SubClass):
|
||||
name = "College of Lore"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class CollegeOfValor(SubClass):
|
||||
name = "College of Valor"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class CollegeOfGlamour(SubClass):
|
||||
name = "College of Glamour"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class CollegeOfSwords(SubClass):
|
||||
name = "College of Swords"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class CollegeOfWhispers(SubClass):
|
||||
name = "College of Whispers"
|
||||
class_features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Bard(CharClass):
|
||||
@@ -20,6 +47,9 @@ class Bard(CharClass):
|
||||
'Religion', 'Sleight of Hand', 'Stealth',
|
||||
'Survival')
|
||||
num_skill_choices = 3
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (CollegeOfLore, CollegeOfValor, CollegeOfGlamour,
|
||||
CollegeOfSwords, CollegeOfWhispers)
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
|
||||
@@ -25,23 +25,56 @@ class CharClass():
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
self.class_level = level
|
||||
if subclass in [None, '', 'None']:
|
||||
self.subclass = None
|
||||
else:
|
||||
self.subclass = subclass
|
||||
# Instantiate the features
|
||||
self.features_by_level = defaultdict(list)
|
||||
cls = type(self)
|
||||
for i in range(1, 21):
|
||||
fs = [f() for f in cls.features_by_level[i]]
|
||||
self.features_by_level[i] = fs
|
||||
for k, v in params.items():
|
||||
setattr(self, k, v)
|
||||
# Instantiate the features
|
||||
|
||||
# Apply subclass
|
||||
self.subclass = self.select_subclass(subclass)
|
||||
if isinstance(self.subclass, SubClass):
|
||||
self.apply_subclass()
|
||||
|
||||
def select_subclass(self, subclass_str):
|
||||
"""
|
||||
Return a SubClass object corresponding to given string.
|
||||
|
||||
Intended to be replaced by classes so they can
|
||||
define their own methods of picking subclass by string.
|
||||
"""
|
||||
if subclass_str in ['', 'None', 'none', None]:
|
||||
return None
|
||||
for sc in self.subclasses_available:
|
||||
if subclass_str.lower() in sc.name.lower():
|
||||
return sc(level=self.class_level)
|
||||
return None
|
||||
|
||||
def apply_subclass(self):
|
||||
if self.subclass is None:
|
||||
return
|
||||
for i in range(1, 21):
|
||||
self.features_by_level[i] = [f() for f in self.features_by_level[i]]
|
||||
|
||||
self.features_by_level[i] += ([f() 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, ())
|
||||
setattr(self, attr, new_list)
|
||||
self.multiclass_weapon_proficiencies += (self.subclass.weapon_proficiencies)
|
||||
self._multiclass_proficiencies_text += (self._proficiencies_text)
|
||||
self.spellcasting_ability = (self.spellcasting_ability or
|
||||
self.subclass.spellcasting_ability)
|
||||
self.spell_slots_by_level = (self.spell_slots_by_level or
|
||||
self.subclass.spell_slots_by_level)
|
||||
|
||||
@property
|
||||
def features(self):
|
||||
features = ()
|
||||
for lvl in range(1, self.class_level+1):
|
||||
features += tuple(self.features_by_level[lvl])
|
||||
if self.subclass is not None and not isinstance(self.subclass, str):
|
||||
features += tuple(self.subclass.features_by_level[lvl])
|
||||
return features
|
||||
|
||||
@property
|
||||
@@ -55,3 +88,26 @@ class CharClass():
|
||||
return 0
|
||||
else:
|
||||
return self.spell_slots_by_level[self.class_level][spell_level]
|
||||
|
||||
|
||||
class SubClass():
|
||||
"""
|
||||
A generic subclass object
|
||||
"""
|
||||
name = ''
|
||||
features_by_level = defaultdict(list)
|
||||
weapon_proficiencies = ()
|
||||
_proficiencies_text = ()
|
||||
spellcasting_ability = None
|
||||
spell_slots_by_level = None
|
||||
spells_known = ()
|
||||
spells_prepared = ()
|
||||
|
||||
def __init__(self, level):
|
||||
self.class_level = level
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self):
|
||||
return "\"{:s}\"".format(self.name)
|
||||
|
||||
@@ -1,6 +1,58 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class KnowledgeDomain(SubClass):
|
||||
name = "Knowledge Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class LifeDomain(SubClass):
|
||||
name = "Life Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class LightDomain(SubClass):
|
||||
name = "Light Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class NatureDomain(SubClass):
|
||||
name = "Nature Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class TempestDomain(SubClass):
|
||||
name = "Tempest Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class TrickeryDomain(SubClass):
|
||||
name = "Trickery Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class WarDomain(SubClass):
|
||||
name = "War Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# SCAG
|
||||
class ArcanaDomain(SubClass):
|
||||
name = "Arcana Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class ForgeDomain(SubClass):
|
||||
name = "Forge Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class GraveDomain(SubClass):
|
||||
name = "Grave Domain"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Cleric(CharClass):
|
||||
@@ -12,6 +64,11 @@ class Cleric(CharClass):
|
||||
weapon_proficiencies = weapons.simple_weapons
|
||||
class_skill_choices = ('History', 'Insight', 'Medicine',
|
||||
'Persuasion', 'Religion')
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (KnowledgeDomain, LifeDomain, LightDomain,
|
||||
NatureDomain, TempestDomain, TrickeryDomain,
|
||||
WarDomain, ArcanaDomain, ForgeDomain,
|
||||
GraveDomain)
|
||||
spellcasting_ability = 'wisdom'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
|
||||
@@ -1,18 +1,42 @@
|
||||
from ..stats import findattr
|
||||
from .. import (weapons, monsters, exceptions)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, monsters, exceptions, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
import warnings
|
||||
import math
|
||||
|
||||
|
||||
# PHB
|
||||
class LandCircle(SubClass):
|
||||
name = "Circle of the Land"
|
||||
circle = "land"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class MoonCircle(SubClass):
|
||||
name = "Circle of the Moon"
|
||||
circle = "moon"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class DreamsCircle(SubClass):
|
||||
name = "Circle of Dreams"
|
||||
circle = "dreams"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class ShepherdCircle(SubClass):
|
||||
name = "Circle of the Shepherd"
|
||||
circle = "shepherd"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Druid(CharClass):
|
||||
class_name = 'Druid'
|
||||
circle = "" # moon, land
|
||||
_wild_shapes = ()
|
||||
hit_dice_faces = 8
|
||||
saving_throw_proficiencies = ('intelligence', 'wisdom')
|
||||
spellcasting_ability = 'wisdom'
|
||||
languages = 'Druidic'
|
||||
_proficiencies_text = (
|
||||
'Light armor', 'medium armor',
|
||||
@@ -26,6 +50,10 @@ class Druid(CharClass):
|
||||
class_skill_choices = ('Arcana', 'Animal Handling', 'Insight',
|
||||
'Medicine', 'Nature', 'Perception', 'Religion',
|
||||
'Survival')
|
||||
features_by_class = defaultdict(list)
|
||||
subclasses_available = (LandCircle, MoonCircle, DreamsCircle,
|
||||
ShepherdCircle)
|
||||
spellcasting_ability = 'wisdom'
|
||||
spell_slots_by_level = {
|
||||
1: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
@@ -49,17 +77,22 @@ class Druid(CharClass):
|
||||
20: (4, 4, 3, 3, 3, 3, 2, 2, 1, 1),
|
||||
}
|
||||
|
||||
def __init__(self, level, subclass=None, **params):
|
||||
if subclass is not None:
|
||||
sc = str(subclass).lower()
|
||||
if 'moon' in sc:
|
||||
self.circle = 'moon'
|
||||
params.pop('circle', '')
|
||||
elif 'land' in sc:
|
||||
self.circle = 'land'
|
||||
params.pop('circle', '')
|
||||
super().__init__(level, **params)
|
||||
|
||||
def select_subclass(self, subclass_str):
|
||||
if subclass_str in ['', 'None', 'none', None]:
|
||||
return None
|
||||
for sc in self.subclasses_available:
|
||||
if ((subclass_str.lower() == sc.circle.lower())
|
||||
or (subclass_str.lower() in sc.name.lower())):
|
||||
return sc(level=self.class_level)
|
||||
return None
|
||||
|
||||
@property
|
||||
def circle(self):
|
||||
if isinstance(self.subclass, SubClass):
|
||||
return self.subclass.circle.lower()
|
||||
else:
|
||||
return ''
|
||||
|
||||
@property
|
||||
def all_wild_shapes(self):
|
||||
"""Return all wild shapes, regardless of validity."""
|
||||
@@ -130,7 +163,7 @@ class Druid(CharClass):
|
||||
max_swim = None
|
||||
max_fly = None
|
||||
# Make adjustments for moon circle druids
|
||||
if self.circle.lower() == "moon":
|
||||
if self.circle == "moon":
|
||||
if 2 <= self.class_level < 6:
|
||||
max_cr = 1
|
||||
elif self.class_level >= 6:
|
||||
|
||||
@@ -1,8 +1,70 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class Champion(SubClass):
|
||||
name = "Champion"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class BattleMaster(SubClass):
|
||||
name = "Battle Master"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class EldritchKnight(SubClass):
|
||||
name = "Eldritch Knight"
|
||||
features_by_level = defaultdict(list)
|
||||
spellcasting_ability = 'intelligence'
|
||||
multiclass_spellslots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (2, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
7: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
9: (2, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
10: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
11: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
12: (3, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
13: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
14: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
15: (3, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
16: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
17: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
18: (3, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
19: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
20: (3, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
# SCAG
|
||||
class PurpleDragonKnight(SubClass):
|
||||
name = "Purple Dragon Knight"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class ArcaneArcher(SubClass):
|
||||
name = "Arcane Archer"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Cavalier(SubClass):
|
||||
name = "Cavalier"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Samurai(SubClass):
|
||||
name = "Samurai"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Fighter(CharClass):
|
||||
class_name = 'Fighter'
|
||||
hit_dice_faces = 10
|
||||
@@ -17,4 +79,7 @@ class Fighter(CharClass):
|
||||
class_skill_choices = ('Acrobatics', 'Animal Handling',
|
||||
'Athletics', 'History', 'Insight', 'Intimidation',
|
||||
'Perception', 'Survival')
|
||||
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (Champion, BattleMaster, EldritchKnight,
|
||||
PurpleDragonKnight, ArcaneArcher, Cavalier,
|
||||
Samurai)
|
||||
|
||||
@@ -1,10 +1,45 @@
|
||||
__all__ = ('Monk')
|
||||
|
||||
from .. import (features, weapons)
|
||||
from .classes import CharClass
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class OpenHandWay(SubClass):
|
||||
name = "Way of the Open Hand"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class ShadowWay(SubClass):
|
||||
name = "Way of Shadow"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class FourElementsWay(SubClass):
|
||||
name = "Way of the Four Elements"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class SunSoulWay(SubClass):
|
||||
name = "Way of the Sun Soul"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class LongDeathWay(SubClass):
|
||||
name = "Way of the Long Death"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class DrunkenMasterWay(SubClass):
|
||||
name = "Way of the Drunken Master"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class KenseiWay(SubClass):
|
||||
name = "Way of the Kensei"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Monk(CharClass):
|
||||
class_name = 'Monk'
|
||||
hit_dice_faces = 8
|
||||
@@ -15,7 +50,10 @@ class Monk(CharClass):
|
||||
weapon_proficiencies = (weapons.Shortsword, weapons.Unarmed) + weapons.simple_weapons
|
||||
class_skill_choices = ('Acrobatics', 'Athletics', 'History', 'Insight',
|
||||
'Religion', 'Stealth')
|
||||
subclasses_available = ('SunSoul', 'OpenHand')
|
||||
subclasses_available = (OpenHandWay, ShadowWay,
|
||||
FourElementsWay, SunSoulWay,
|
||||
LongDeathWay, DrunkenMasterWay,
|
||||
KenseiWay)
|
||||
features_by_level = defaultdict(list)
|
||||
features_by_level[1] = [features.UnarmoredDefense,
|
||||
features.MartialArts]
|
||||
@@ -25,19 +63,3 @@ class Monk(CharClass):
|
||||
for f in self.features_by_level[1]:
|
||||
if isinstance(f, features.MartialArts):
|
||||
f.level = self.class_level
|
||||
if subclass == 'sunsoul':
|
||||
self.subclass = SunSoul(level=self.class_level)
|
||||
else:
|
||||
self.subclass = None
|
||||
if self.subclass is not None:
|
||||
self._proficiencies_text += self.subclass._proficiencies_text
|
||||
self.weapon_proficiences += self.subclass.weapon_proficiencies
|
||||
|
||||
|
||||
class SunSoul:
|
||||
class_features_by_level = defaultdict(list)
|
||||
weapon_proficiencies = ()
|
||||
_profiencies_text = ()
|
||||
|
||||
def __init__(self, level):
|
||||
self.class_level = level
|
||||
|
||||
@@ -1,7 +1,37 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class OathOfDevotion(SubClass):
|
||||
name = "Oath of Devotion"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class OathOfAncients(SubClass):
|
||||
name = "Oath of The Ancients"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class OathOfVengance(SubClass):
|
||||
name = "Oath of Vengance"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class OathOfCrown(SubClass):
|
||||
name = "Oath of The Crown"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class OathOfConquest(SubClass):
|
||||
name = "Oath of Conquest"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class OathOfRedemption(SubClass):
|
||||
name = "Oath of Redemption"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Paladin(CharClass):
|
||||
class_name = 'Paladin'
|
||||
@@ -12,6 +42,9 @@ class Paladin(CharClass):
|
||||
weapon_proficiencies = weapons.simple_weapons + weapons.martial_weapons
|
||||
class_skill_choices = ("Athletics", 'Insight', 'Intimidation',
|
||||
'Medicine', 'Persuasion', 'Religion')
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (OathOfDevotion, OathOfAncients, OathOfVengance,
|
||||
OathOfCrown, OathOfConquest, OathOfRedemption)
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
|
||||
@@ -1,10 +1,37 @@
|
||||
__all__ = ('Ranger', 'Revisedranger')
|
||||
__all__ = ('Ranger', 'RevisedRanger')
|
||||
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class Hunter(SubClass):
|
||||
name = "Hunter"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class BeastMaster(SubClass):
|
||||
name = "Beast Master"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class GloomStalker(SubClass):
|
||||
name = "Gloom Stalker"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class HorizonWalker(SubClass):
|
||||
name = "Horizon Walker"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class MonsterSlayer(SubClass):
|
||||
name = "Monster Slayer"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Ranger(CharClass):
|
||||
class_name = 'Ranger'
|
||||
hit_dice_faces = 10
|
||||
@@ -16,8 +43,10 @@ class Ranger(CharClass):
|
||||
'Investigation', 'Nature', 'Perception', 'Stealth',
|
||||
'Survival')
|
||||
num_skill_choices = 3
|
||||
spellcasting_ability = 'wisdom'
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (Hunter, BeastMaster, GloomStalker,
|
||||
HorizonWalker, MonsterSlayer)
|
||||
spellcasting_ability = 'wisdom'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
@@ -49,6 +78,24 @@ class Ranger(CharClass):
|
||||
self.features_by_level[2].append(fighting_style)
|
||||
|
||||
|
||||
# Custom Classes
|
||||
class Revisedranger(Ranger):
|
||||
class_name = 'Revisedranger'
|
||||
# Revised Ranger
|
||||
class BeastConclave(SubClass):
|
||||
name = "Beast Conclave"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class HunterConclave(SubClass):
|
||||
name = "Hunter Conclave"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class DeepStalkerConclave(SubClass):
|
||||
name = "Deep Stalker Conclave"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class RevisedRanger(Ranger):
|
||||
class_name = 'Revised Ranger'
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (BeastConclave, HunterConclave, DeepStalkerConclave)
|
||||
|
||||
|
||||
@@ -1,6 +1,62 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class Thief(SubClass):
|
||||
name = "Thief"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Assassin(SubClass):
|
||||
name = "Assassin"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class ArcaneTrickster(SubClass):
|
||||
name = "Arcane Trickster"
|
||||
features_by_level = defaultdict(list)
|
||||
spellcasting_ability = 'intelligence'
|
||||
multiclass_spellslots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
1: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
2: (0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
3: (3, 2, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
4: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
5: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
6: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
7: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
8: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
9: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
|
||||
10: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
11: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
12: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
|
||||
13: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
14: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
15: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
|
||||
16: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
17: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
18: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
|
||||
19: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
20: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
|
||||
}
|
||||
|
||||
|
||||
# XGTE
|
||||
class Inquisitive(SubClass):
|
||||
name = "Inquisitive"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Mastermind(SubClass):
|
||||
name = "Mastermind"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Swashbuckler(SubClass):
|
||||
name = "Swashbuckler"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Rogue(CharClass):
|
||||
@@ -17,4 +73,6 @@ class Rogue(CharClass):
|
||||
'Insight', 'Intimidation', 'Investigation',
|
||||
'Perception', 'Performance', 'Persuasion',
|
||||
'Sleight of Hand', 'Stealth')
|
||||
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (Thief, Assassin, ArcaneTrickster,
|
||||
Inquisitive, Mastermind, Swashbuckler)
|
||||
|
||||
@@ -1,6 +1,33 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class DraconicBloodline(SubClass):
|
||||
name = "Draconic Bloodline"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class WildMagic(SubClass):
|
||||
name = "Wild Magic"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class DivineSoul(SubClass):
|
||||
name = "Divine Soul"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class ShadowMagic(SubClass):
|
||||
name = "Shadow Magic"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class StormSorcery(SubClass):
|
||||
name = "Storm Sorcery"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Sorceror(CharClass):
|
||||
@@ -14,6 +41,9 @@ class Sorceror(CharClass):
|
||||
weapons.LightCrossbow)
|
||||
class_skill_choices = ('Arcana', 'Deception', 'Insight',
|
||||
'Intimidation', 'Persuasion', 'Religion')
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (DraconicBloodline, WildMagic, DivineSoul,
|
||||
ShadowMagic, StormSorcery)
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
|
||||
@@ -1,6 +1,39 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class Archfey(SubClass):
|
||||
name = "The Archfey Patron"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Fiend(SubClass):
|
||||
name = "The Fiend Patron"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class GreatOldOne(SubClass):
|
||||
name = "The Great Old One Patron"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# SCAG
|
||||
class Undying(SubClass):
|
||||
name = "The Undying Patron"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class Celestial(SubClass):
|
||||
name = "The Celestial Patron"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Hexblade(SubClass):
|
||||
name = "Hexblade Patron"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Warlock(CharClass):
|
||||
@@ -12,6 +45,9 @@ class Warlock(CharClass):
|
||||
'Intimidation', 'Investigation', 'Nature',
|
||||
'Religion')
|
||||
weapon_proficiencies = weapons.simple_weapons
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (Archfey, Fiend, GreatOldOne, Undying, Celestial,
|
||||
Hexblade)
|
||||
spellcasting_ability = 'charisma'
|
||||
spell_slots_by_level = {
|
||||
1: (2, 1, 0, 0, 0, 0, 0, 0, 0, 0),
|
||||
|
||||
@@ -1,6 +1,59 @@
|
||||
from .. import (weapons)
|
||||
from .. import features as feats
|
||||
from .classes import CharClass
|
||||
from .. import (weapons, features)
|
||||
from .classes import CharClass, SubClass
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
# PHB
|
||||
class Abjuration(SubClass):
|
||||
name = "School of Abjuration"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Conjuration(SubClass):
|
||||
name = "School of Conjuration"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Divination(SubClass):
|
||||
name = "School of Divination"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Enchantment(SubClass):
|
||||
name = "School of Enchantment"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Evocation(SubClass):
|
||||
name = "School of Evocation"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Illusion(SubClass):
|
||||
name = "School of Illusion"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Necromancy(SubClass):
|
||||
name = "School of Necromancy"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Transmutation(SubClass):
|
||||
name = "School of Transmutation"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# SCAG
|
||||
class Bladeslinging(SubClass):
|
||||
name = "School of Bladeslinging"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
# XGTE
|
||||
class WarMagic(SubClass):
|
||||
name = "School of War Magic"
|
||||
features_by_level = defaultdict(list)
|
||||
|
||||
|
||||
class Wizard(CharClass):
|
||||
@@ -14,6 +67,10 @@ class Wizard(CharClass):
|
||||
weapons.LightCrossbow)
|
||||
class_skill_choices = ('Arcana', 'History', 'Investigation',
|
||||
'Medicine', 'Religion')
|
||||
features_by_level = defaultdict(list)
|
||||
subclasses_available = (Abjuration, Conjuration, Divination, Enchantment,
|
||||
Evocation, Illusion, Necromancy, Transmutation,
|
||||
Bladeslinging, WarMagic)
|
||||
spellcasting_ability = 'intelligence'
|
||||
spell_slots_by_level = {
|
||||
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
|
||||
|
||||
@@ -243,7 +243,10 @@ class SubclassForm(npyscreen.ActionForm):
|
||||
def __init__(self, newclass, level, num=1, **kwargs):
|
||||
self.class_num = num
|
||||
self.parent_class = newclass
|
||||
self.subclass_options = newclass.subclasses_available or ('None',)
|
||||
if len(newclass.subclasses_available) > 0:
|
||||
self.subclass_options = [sc.name for sc in newclass.subclasses_available]
|
||||
else:
|
||||
self.subclass_options = ('None',)
|
||||
self.level = level
|
||||
super().__init__(**kwargs)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ dungeonsheets_version = "{{ char.dungeonsheets_version }}"
|
||||
|
||||
name = "{{ char.name }}"
|
||||
classes_levels = {{ char.classes_levels }}
|
||||
subclasses = {{ char.subclasses }}
|
||||
player_name = "{{ char.player_name }}"
|
||||
background = "{{ char.background.name }}"
|
||||
race = "{{ char.race.name }}"
|
||||
|
||||
@@ -14,3 +14,4 @@ from .warlock import *
|
||||
from .wizard import *
|
||||
from .races import *
|
||||
from .backgrounds import *
|
||||
from .feats import *
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
\usepackage[dvipsnames]{color}
|
||||
\definecolor{mygrey}{gray}{0.7}
|
||||
|
||||
\title{Features and Traits}
|
||||
\title{Features and Subclass}
|
||||
\author{[[ character.name ]]}
|
||||
\date{}
|
||||
|
||||
@@ -13,6 +13,14 @@
|
||||
|
||||
\maketitle
|
||||
|
||||
[% for sc in character.subclasses if sc not in ['', None, 'None', 'none']%]
|
||||
|
||||
\section**{[[ sc.name ]]}
|
||||
|
||||
[[ sc.__doc__|rst_to_latex ]]
|
||||
|
||||
[% endfor %]
|
||||
|
||||
[% for feat in character.features %]
|
||||
|
||||
\section*{[[ feat.name ]]}
|
||||
|
||||
@@ -26,9 +26,13 @@ class Race():
|
||||
spells_prepared = ()
|
||||
|
||||
def __init__(self):
|
||||
self.features = tuple([f() for f in self.features])
|
||||
cls = type(self)
|
||||
# Instantiate the features
|
||||
self.features = tuple([f() for f in cls.features])
|
||||
self.features_by_level = defaultdict(list)
|
||||
for i in range(1, 21):
|
||||
self.features_by_level[i] = [f() for f in self.features_by_level[i]]
|
||||
self.features_by_level[i] = [f()for f in
|
||||
cls.features_by_level[i]]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@@ -23,6 +23,7 @@ setup(name='dungeonsheets',
|
||||
'dungeonsheets': ['blank-character-sheet-default.pdf',
|
||||
'blank-spell-sheet-default.pdf',
|
||||
'spellbook_template.tex', '../VERSION',
|
||||
'empty_template.txt',
|
||||
'character_template.txt', 'features_template.tex',
|
||||
'druid_shapes_template.tex']
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user