save function now works for a character... need to add defaults if options dont exist

This commit is contained in:
Ben Cook
2018-12-20 10:35:50 -05:00
parent b93b1ac6d7
commit 2b1f5981b4
10 changed files with 116 additions and 50 deletions
+7 -1
View File
@@ -1,2 +1,8 @@
from . import weapons, character from . import weapons, character, features, race, background, spells
import os
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
__version__ = read('../VERSION')
+4
View File
@@ -7,6 +7,8 @@ class Shield():
def __str__(self): def __str__(self):
return self.name return self.name
def __repr__(self):
return "\"{:s}\"".format(self.name)
class WoodenShield(Shield): class WoodenShield(Shield):
name = 'Wooden shield' name = 'Wooden shield'
@@ -55,6 +57,8 @@ class Armor():
def __str__(self): def __str__(self):
return self.name return self.name
def __repr__(self):
return "\"{:s}\"".format(self.name)
class NoArmor(Armor): class NoArmor(Armor):
name = "No armor" name = "No armor"
+42 -7
View File
@@ -5,11 +5,14 @@ import os
import warnings import warnings
from . import exceptions from . import exceptions
import importlib.util import importlib.util
import jinja2
import subprocess
from .stats import Ability, Skill, findattr from .stats import Ability, Skill, findattr
from .dice import read_dice_str from .dice import read_dice_str
from . import (weapons, race, background, spells, armor, monsters, from . import (weapons, race, background, spells, armor, monsters,
exceptions, classes, features) exceptions, classes, features)
from .__init__ import __version__
from .weapons import Weapon from .weapons import Weapon
from .armor import Armor, NoArmor, Shield, NoShield from .armor import Armor, NoArmor, Shield, NoShield
@@ -49,6 +52,7 @@ class Character():
class_name = "" class_name = ""
player_name = "" player_name = ""
alignment = "Neutral" alignment = "Neutral"
dungeonsheets_version = __version__
class_list = [] class_list = []
race = None race = None
background = None background = None
@@ -111,7 +115,8 @@ class Character():
spells = tuple() spells = tuple()
spells_prepared = tuple() spells_prepared = tuple()
# Features IN MAJOR DEVELOPMENT # Features IN MAJOR DEVELOPMENT
other_features = () custom_features = ()
feature_choices = ()
def __init__(self, **attrs): def __init__(self, **attrs):
"""Takes a bunch of attrs and passes them to ``set_attrs``""" """Takes a bunch of attrs and passes them to ``set_attrs``"""
@@ -150,6 +155,11 @@ class Character():
@property @property
def class_initialized(self): def class_initialized(self):
return (self.num_classes > 0) return (self.num_classes > 0)
@property
def classes_levels(self):
return [c.class_name.lower() + ' ' + str(c.class_level)
for c in self.class_list]
@property @property
def primary_class(self): def primary_class(self):
@@ -176,7 +186,7 @@ class Character():
@property @property
def features(self): def features(self):
fts = set(self.other_features) fts = set(self.custom_features)
if not self.class_initialized: if not self.class_initialized:
return fts return fts
for c in self.class_list: for c in self.class_list:
@@ -190,6 +200,10 @@ class Character():
if self.background is not None: if self.background is not None:
fts |= set(getattr(self.background, 'features', ())) fts |= set(getattr(self.background, 'features', ()))
return tuple(fts) return tuple(fts)
@property
def custom_features_text(self):
return tuple([f.name for f in self.custom_features])
@property @property
def saving_throw_proficiencies(self): def saving_throw_proficiencies(self):
@@ -279,7 +293,7 @@ class Character():
name=f, source='Unknown', name=f, source='Unknown',
__doc__="""Unknown Feature. Add to features.py""")) __doc__="""Unknown Feature. Add to features.py"""))
warnings.warn(msg) warnings.warn(msg)
self.other_features = tuple(F() for F in _features) self.custom_features = tuple(F() for F in _features)
elif (attr == 'spells') or (attr == 'spells_prepared'): elif (attr == 'spells') or (attr == 'spells_prepared'):
# Create a list of actual spell objects # Create a list of actual spell objects
_spells = [] _spells = []
@@ -300,8 +314,8 @@ class Character():
# Instantiate them all for the spells list # Instantiate them all for the spells list
self.spells = tuple(S() for S in _spells) self.spells = tuple(S() for S in _spells)
else: else:
# this is a list of spell classes # Instantiate them all for the spells list
self.spells_prepared = tuple(_spells) self.spells_prepared = tuple(S() for S in _spells)
else: else:
if not hasattr(self, attr): if not hasattr(self, attr):
warnings.warn(f"Setting unknown character attribute {attr}", warnings.warn(f"Setting unknown character attribute {attr}",
@@ -401,7 +415,7 @@ class Character():
directly. directly.
""" """
if shield not in ('', None): if shield not in ('', 'None', None):
try: try:
NewShield = findattr(armor, shield) NewShield = findattr(armor, shield)
except AttributeError: except AttributeError:
@@ -519,7 +533,7 @@ class Character():
raise ValueError( raise ValueError(
'level was not recognizable as an int: {:s}'.format(lvl)) 'level was not recognizable as an int: {:s}'.format(lvl))
params = {} params = {}
params['feature_choices'] = char_props.pop('feature_choices', []) params['feature_choices'] = char_props.get('feature_choices', [])
class_list += [this_class(this_level, subclass=sub, **params)] class_list += [this_class(this_level, subclass=sub, **params)]
# accept backwards compatability for single-class characters # accept backwards compatability for single-class characters
if len(class_list) == 0: if len(class_list) == 0:
@@ -532,7 +546,28 @@ class Character():
char = cls(**char_props) char = cls(**char_props)
return char return char
def save(self, filename, template_file='character_template.txt'):
# Create the template context
context = dict(
char=self,
)
# Render the template
src_path = os.path.dirname(__file__)
text = jinja2.Environment(
loader=jinja2.FileSystemLoader(src_path or './')
).get_template(template_file).render(context)
# Save the file
with open(filename, mode='w') as f:
f.write(text)
def to_pdf(self, filename, **kwargs):
char_file = filename.replace('pdf', 'py')
self.save(char_file,
template_file=kwargs.get('template_file',
'character_template.txt'))
subprocess.call(['makesheets', char_file])
def read_character_file(filename): def read_character_file(filename):
"""Create a character object from the given definition file. """Create a character object from the given definition file.
+32 -25
View File
@@ -5,14 +5,13 @@ sheet by running ``makesheets`` from the command line.
""" """
dungeonsheets_version = "{{ dungeonsheets_version }}" dungeonsheets_version = "{{ char.dungeonsheets_version }}"
name = '{{ char.name }}' name = "{{ char.name }}"
character_class = '{{ char.class_name }}' classes_levels = {{ char.classes_levels }}
player_name = '{{ char.player_name }}' player_name = "{{ char.player_name }}"
background = "{{ char.background.name }}" background = "{{ char.background.name }}"
race = "{{ char.race.name }}" race = "{{ char.race.name }}"
level = {{ char.level }}
alignment = "{{ char.alignment }}" alignment = "{{ char.alignment }}"
xp = {{ char.xp }} xp = {{ char.xp }}
hp_max = {{ char.hp_max }} hp_max = {{ char.hp_max }}
@@ -24,10 +23,23 @@ constitution = {{ char.constitution.value }}
intelligence = {{ char.intelligence.value }} intelligence = {{ char.intelligence.value }}
wisdom = {{ char.wisdom.value }} wisdom = {{ char.wisdom.value }}
charisma = {{ char.charisma.value }} charisma = {{ char.charisma.value }}
# Select what skills you're proficient with
skill_proficiencies = {{ char.skill_proficiencies }} skill_proficiencies = {{ char.skill_proficiencies }}
# Named features / feats that aren't part of your classes,
# race, or background.
# Example:
# features = ('Tavern Brawler',) # take the optional Feat from PHB
features = {{ char.custom_features }}
# If selecting among multiple feature options: ex Fighting Style
# Example (Fighting Style):
# feature_choices = ('Archery',)
feature_choices = {{ char.feature_choices }}
# Proficiencies and languages # Proficiencies and languages
languages = "{{ char.languages }}" languages = """{{ char.languages }}"""
# Inventory # Inventory
# TODO: Get yourself some money # TODO: Get yourself some money
@@ -38,35 +50,30 @@ gp = 0
pp = 0 pp = 0
# TODO: Put your equipped weapons and armor here # TODO: Put your equipped weapons and armor here
weapons = () # Example: ('shortsword', 'longsword') weapons = {{ char.weapons }} # Example: ('shortsword', 'longsword')
armor = "" # Eg "light leather armor" armor = "{{ char.armor }}" # Eg "light leather armor"
shield = "" # Eg "shield" shield = "{{ char.shield }}" # Eg "shield"
equipment = "TODO: Describe your equipment from your {{ char.class_name }} class and {{ char.background.name }} background." equipment = """{{ char.equipment }}"""
attacks_and_spellcasting = "TODO: Describe specifics for how your {{ char.class_name }} attacks." attacks_and_spellcasting = """{{ char.attacks_and_spellcasting }}"""
# List of known spells # List of known spells
# Example: spells = ('magic missile', 'mage armor') # Example: spells = ('magic missile', 'mage armor')
spells = () # Todo: Learn some spells spells = {{ char.spells }} # Todo: Learn some spells
# Which spells have been prepared (not including cantrips) # Which spells have been prepared (not including cantrips)
spells_prepared = () spells_prepared = {{ char.spells_prepared }}
# Backstory # Backstory
# TODO: Describe your backstory here # Describe your backstory here
personality_traits = """I am a leaf on the wind, personality_traits = """{{ char.personality_traits}}"""
watch how I...
"""
ideals = """ ideals = """{{ char.ideals }}"""
"""
bonds = """ bonds = """{{ char.bonds }}"""
"""
flaws = """ flaws = """{{ char.flaws }}"""
"""
features_and_traits = """ features_and_traits = """{{ char.features_and_traits }}"""
"""
+1 -1
View File
@@ -51,4 +51,4 @@ class Ranger(CharClass):
# Custom Classes # Custom Classes
class Revisedranger(Ranger): class Revisedranger(Ranger):
class_name = 'Revised Ranger' class_name = 'Revisedranger'
+13 -13
View File
@@ -14,7 +14,7 @@ import subprocess
import npyscreen import npyscreen
import jinja2 import jinja2
from dungeonsheets import character, race, dice, background from dungeonsheets import character, race, dice, background, classes
def read_version(): def read_version():
@@ -24,18 +24,18 @@ def read_version():
char_classes = { char_classes = {
'Barbarian': character.Barbarian, 'Barbarian': classes.Barbarian,
'Bard': character.Bard, 'Bard': classes.Bard,
'Cleric': character.Cleric, 'Cleric': classes.Cleric,
'Druid': character.Druid, 'Druid': classes.Druid,
'Fighter': character.Fighter, 'Fighter': classes.Fighter,
'Monk': character.Monk, 'Monk': classes.Monk,
'Paladin': character.Paladin, 'Paladin': classes.Paladin,
'Ranger': character.Ranger, 'Ranger': classes.Ranger,
'Rogue': character.Rogue, 'Rogue': classes.Rogue,
'Sorceror': character.Sorceror, 'Sorceror': classes.Sorceror,
'Warlock': character.Warlock, 'Warlock': classes.Warlock,
'Wizard': character.Wizard 'Wizard': classes.Wizard
} }
races = { races = {
+3
View File
@@ -37,6 +37,9 @@ class Feature():
def __str__(self): def __str__(self):
return self.name return self.name
def __repr__(self):
return "\"{:s}\"".format(self.name)
def weapon_func(self, weapon: weapons.Weapon, **kwargs): def weapon_func(self, weapon: weapons.Weapon, **kwargs):
""" """
+1 -1
View File
@@ -175,7 +175,7 @@ def create_spells_pdf(char, basename, flatten=False):
prep_names = tuple(f'Check Box {i}' for i in prep_numbers[level]) prep_names = tuple(f'Check Box {i}' for i in prep_numbers[level])
for spell, field, chk_field in zip(spells, field_names, prep_names): for spell, field, chk_field in zip(spells, field_names, prep_names):
fields[field] = str(spell) fields[field] = str(spell)
is_prepared = any([isinstance(spell, Spl) is_prepared = any([spell == Spl
for Spl in char.spells_prepared]) for Spl in char.spells_prepared])
fields[chk_field] = CHECKBOX_ON if is_prepared else CHECKBOX_OFF fields[chk_field] = CHECKBOX_ON if is_prepared else CHECKBOX_OFF
# # Uncomment to post field names instead: # # Uncomment to post field names instead:
+7 -2
View File
@@ -14,7 +14,9 @@ def create_spell(**params):
NewSpell NewSpell
New spell class, subclass of ``Spell``, with given params. New spell class, subclass of ``Spell``, with given params.
""" """
NewSpell = type('UnknownSpell', (Spell,), params) NewSpell = Spell
NewSpell.name = params.get('name', 'Unknown Spell')
NewSpell.level = params.get('level', 9)
return NewSpell return NewSpell
@@ -41,8 +43,11 @@ class Spell():
return s return s
def __repr__(self): def __repr__(self):
return f'<{self.name}>' return "\"{:s}\"".format(self.name)
def __eq__(self, other):
return (self.name == other.name) and (self.level == other.level)
@property @property
def component_string(self): def component_string(self):
s = f'{", ".join(self.components)}' s = f'{", ".join(self.components)}'
+6
View File
@@ -24,6 +24,12 @@ class Weapon():
def is_ranged(self): def is_ranged(self):
return ('range' in self.properties.lower()) and ('thrown' not in self.properties.lower()) return ('range' in self.properties.lower()) and ('thrown' not in self.properties.lower())
def __str__(self):
return self.name
def __repr__(self):
return "\"{:s}\"".format(self.name)
class Club(Weapon): class Club(Weapon):
name = "Club" name = "Club"