Added spell sheet for casters, but not spells yet.

This commit is contained in:
Mark Wolfman
2018-03-28 23:56:11 -05:00
parent cafde3465d
commit 8a53b7c2be
9 changed files with 231 additions and 28 deletions
Binary file not shown.
+68
View File
@@ -74,6 +74,8 @@ class Character():
equipment = ""
weapons = [] # Replaced in __init__ constructor
_proficiencies_text = tuple()
# Magic
spellcasting_ability = None
def __init__(self, **attrs):
"""Takes a bunch of attrs and passes them to ``set_attrs``"""
@@ -108,6 +110,25 @@ class Character():
# Lookup general attributes
setattr(self, attr, val)
@property
def is_spellcaster(self):
result = (self.spellcasting_ability is not None)
return result
@property
def spell_save_dc(self):
ability_mod = getattr(self, self.spellcasting_ability).modifier
return (8 + self.proficiency_bonus + ability_mod)
@property
def spell_attack_bonus(self):
ability_mod = getattr(self, self.spellcasting_ability).modifier
return (self.proficiency_bonus + ability_mod)
def spell_slots(self, spell_level):
"""How many spells slots are available for this spell level."""
return self.spell_slots_by_level[self.level][spell_level]
def is_proficient(self, weapon: Weapon):
"""Is the character proficient with this item?
@@ -308,6 +329,29 @@ class Warlock(Character):
saving_throw_proficiencies = ('wisdom', 'charisma')
_proficiencies_text = ("light Armor", "simple weapons")
weapon_proficiencies = weapons.simple_weapons
spellcasting_ability = 'charisma'
spell_slots_by_level = {
1: (2, 1, 0, 0, 0, 0, 0, 0, 0, 0),
2: (2, 2, 0, 0, 0, 0, 0, 0, 0, 0),
3: (2, 0, 2, 0, 0, 0, 0, 0, 0, 0),
4: (3, 0, 2, 0, 0, 0, 0, 0, 0, 0),
5: (3, 0, 0, 3, 0, 0, 0, 0, 0, 0),
6: (3, 0, 0, 3, 0, 0, 0, 0, 0, 0),
7: (3, 0, 0, 0, 2, 0, 0, 0, 0, 0),
8: (3, 0, 0, 0, 2, 0, 0, 0, 0, 0),
9: (3, 0, 0, 0, 0, 2, 0, 0, 0, 0),
10: (4, 0, 0, 0, 0, 2, 0, 0, 0, 0),
11: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
12: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
13: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
14: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
15: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
16: (4, 0, 0, 0, 0, 3, 0, 0, 0, 0),
17: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
18: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
19: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
20: (4, 0, 0, 0, 0, 4, 0, 0, 0, 0),
}
class Wizard(Character):
@@ -319,3 +363,27 @@ class Wizard(Character):
weapon_proficiencies = (weapons.Dagger, weapons.Dart,
weapons.Sling, weapons.Quarterstaff,
weapons.LightCrossbow)
spellcasting_ability = 'intelligence'
spell_slots_by_level = {
# char_lvl: (cantrips, 1st, 2nd, 3rd, ...)
1: (3, 2, 0, 0, 0, 0, 0, 0, 0, 0),
2: (3, 3, 0, 0, 0, 0, 0, 0, 0, 0),
3: (3, 4, 2, 0, 0, 0, 0, 0, 0, 0),
4: (4, 4, 3, 0, 0, 0, 0, 0, 0, 0),
5: (4, 4, 3, 2, 0, 0, 0, 0, 0, 0),
6: (4, 4, 3, 3, 0, 0, 0, 0, 0, 0),
7: (4, 4, 3, 3, 1, 0, 0, 0, 0, 0),
8: (4, 4, 3, 3, 2, 0, 0, 0, 0, 0),
9: (4, 4, 3, 3, 3, 1, 0, 0, 0, 0),
10: (5, 4, 3, 3, 3, 2, 0, 0, 0, 0),
11: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
12: (5, 4, 3, 3, 3, 2, 1, 0, 0, 0),
13: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
14: (5, 4, 3, 3, 3, 2, 1, 1, 0, 0),
15: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
16: (5, 4, 3, 3, 3, 2, 1, 1, 1, 0),
17: (5, 4, 3, 3, 3, 2, 1, 1, 1, 1),
18: (5, 4, 3, 3, 3, 3, 1, 1, 1, 1),
19: (5, 4, 3, 3, 3, 3, 2, 1, 1, 1),
20: (5, 4, 3, 3, 3, 3, 2, 2, 1, 1),
}
+61 -16
View File
@@ -52,7 +52,32 @@ def load_character_file(filename):
return char_props
def create_fdf(character, fdfname):
def create_spells_pdf(character, basename, flatten=False):
class_level = (character.class_name + ' ' + str(character.level))
spell_level = lambda x : (x or '')
fields = (
('Spellcasting Class 2', class_level),
("SpellcastingAbility 2", character.spellcasting_ability.capitalize()),
('SpellSaveDC 2', character.spell_save_dc),
('SpellAtkBonus 2', mod_str(character.spell_attack_bonus)),
# Number of spell slots
('SlotsTotal 19', spell_level(character.spell_slots(1))),
('SlotsTotal 20', spell_level(character.spell_slots(2))),
('SlotsTotal 21', spell_level(character.spell_slots(3))),
('SlotsTotal 22', spell_level(character.spell_slots(4))),
('SlotsTotal 23', spell_level(character.spell_slots(5))),
('SlotsTotal 24', spell_level(character.spell_slots(6))),
('SlotsTotal 25', spell_level(character.spell_slots(7))),
('SlotsTotal 26', spell_level(character.spell_slots(8))),
('SlotsTotal 27', spell_level(character.spell_slots(9))),
)
# Make the actual pdf
dirname = os.path.dirname(os.path.abspath(__file__))
src_pdf = os.path.join(dirname, 'blank-spell-sheet-default.pdf')
make_pdf(fields, src_pdf=src_pdf, basename=basename, flatten=flatten)
def create_character_pdf(character, basename, flatten=False):
# Prepare the list of fields
class_level = (character.class_name + ' ' + str(character.level))
fields = [
@@ -173,11 +198,29 @@ def create_fdf(character, fdfname):
prof_text = "Proficiencies:\n" + text_box(character.proficiencies_text)
prof_text += "\n\nLanguages:\n" + text_box(character.languages)
fields.append(('ProficienciesLang', prof_text))
# Prepare the actual PDF
dirname = os.path.dirname(os.path.abspath(__file__))
src_pdf = os.path.join(dirname, 'blank-character-sheet-default.pdf')
return make_pdf(fields, src_pdf=src_pdf, basename=basename, flatten=flatten)
def make_pdf(fields, src_pdf, basename, flatten=False):
# Create the actual FDF file
fdfname = basename + '.fdf'
fdf = forge_fdf("", fields, [], [], [])
fdf_file = open(fdfname, "wb")
fdf_file.write(fdf)
fdf_file.close()
# Build the final flattened PDF documents
dest_pdf = basename + '.pdf'
popenargs = [
'pdftk', src_pdf, 'fill_form', fdfname, 'output', dest_pdf,
]
if flatten:
popenargs.append('flatten')
subprocess.call(popenargs)
# Clean up temporary files
os.remove(fdfname)
def make_sheet(character_file, flatten=False):
@@ -195,28 +238,30 @@ def make_sheet(character_file, flatten=False):
CharClass = getattr(character, class_name)
char = CharClass(**char_props)
# Set the fields in the FDF
fdfname = os.path.splitext(character_file)[0] + '.fdf'
create_fdf(character=char, fdfname=fdfname)
# Build the final flattened PDF document
dirname = os.path.dirname(os.path.abspath(__file__))
src_pdf = os.path.join(dirname, 'blank-character-sheet-default.pdf')
dest_pdf = os.path.splitext(character_file)[0] + '.pdf'
popenargs = [
'pdftk', src_pdf, 'fill_form', fdfname, 'output', dest_pdf,
]
if flatten:
popenargs.append('flatten')
char_base = os.path.splitext(character_file)[0] + '_char'
sheets = [char_base + '.pdf']
create_character_pdf(character=char, basename=char_base, flatten=flatten)
if char.is_spellcaster:
spell_base = os.path.splitext(character_file)[0] + '_spells'
create_spells_pdf(character=char, basename=spell_base, flatten=flatten)
sheets.append(spell_base + '.pdf')
# Combine sheets into final pdf
final_pdf = os.path.splitext(character_file)[0] + '.pdf'
popenargs = ('pdftk', *sheets, 'cat', 'output', final_pdf)
subprocess.call(popenargs)
# Clean up temporary files
os.remove(fdfname)
# Remove temporary files
for sheet in sheets:
os.remove(sheet)
def main():
# Prepare an argument parser
parser = argparse.ArgumentParser(
description='Prepare Dungeons and Dragons character sheets as PDFs')
parser.add_argument('filename', type=str, nargs="?", help="Python file with character definition")
parser.add_argument('--flatten', '-F', action="store_true", help="Remove the PDF fields once processed.")
parser.add_argument('filename', type=str, nargs="?",
help="Python file with character definition")
parser.add_argument('--flatten', '-F', action="store_true",
help="Remove the PDF fields once processed.")
args = parser.parse_args()
# Process the requested files
if args.filename is None: