mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-05-31 09:58:28 +02:00
Added spell sheet for casters, but not spells yet.
This commit is contained in:
Binary file not shown.
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user