Files
dungeon-sheets/dungeonsheets/classes/classes.py
T
2018-12-23 03:22:47 -05:00

130 lines
4.3 KiB
Python

from collections import defaultdict
from ..features import Feature, FeatureSelector
class CharClass():
"""
A generic Character Class (not to be confused with builtin class)
"""
name = "Default"
level = 1
hit_dice_faces = 2
subclass_select_level = 3
weapon_proficiencies = ()
_proficiencies_text = ()
multiclass_weapon_proficiencies = ()
_multiclass_proficiencies_text = ()
saving_throw_proficiencies = ()
primary_abilities = ()
languages = ()
class_skill_choices = ()
num_skill_choices = 2
spellcasting_ability = None
spell_slots_by_level = None
spells_known = ()
spells_prepared = ()
subclass = None
subclasses_available = ()
features_by_level = defaultdict(list)
def __init__(self, level, owner=None, subclass=None, feature_choices=[],
**params):
self.level = level
self.owner = owner
# For ex: add "char.Monk" attribute
setattr(self.owner, self.name, self)
# Instantiate the features
self.features_by_level = defaultdict(list)
cls = type(self)
for i in range(1, 21):
fs = []
for f in cls.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] = fs
for k, v in params.items():
setattr(self, k, v)
# 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(owner=self.owner)
return None
def apply_subclass(self):
if self.subclass is None:
return
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, ())
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.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.level+1):
features += tuple(self.features_by_level[lvl])
return features
@property
def is_spellcaster(self):
result = (self.spellcasting_ability is not None)
return result
def spell_slots(self, spell_level):
"""How many spells slots are available for this spell level."""
if self.spell_slots_by_level is None:
return 0
else:
return self.spell_slots_by_level[self.level][spell_level]
class SubClass():
"""
A generic subclass object. Add more detail in the __doc__ attribute.
"""
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, owner):
self.owner = owner
self.__doc__ = self.__doc__ or SubClass.__doc__
def __str__(self):
return self.name
def __repr__(self):
return "\"{:s}\"".format(self.name)