mirror of
https://github.com/Threnklyn/dungeon-sheets.git
synced 2026-06-13 16:13:32 +02:00
Now creates GM sheet PDF, but with monsters only.
This commit is contained in:
@@ -238,19 +238,20 @@ class BardBattleMagic(Feature):
|
|||||||
source = "Bard (College of Valor)"
|
source = "Bard (College of Valor)"
|
||||||
|
|
||||||
|
|
||||||
# College of Glamour
|
# XgtE - College of Glamour:
|
||||||
class MantleOfInspiration(Feature):
|
class MantleOfInspiration(Feature):
|
||||||
"""When you join the College of Glamour at 3rd level, you gain the ability to
|
"""When you join the College of Glamour at 3rd level, you gain the
|
||||||
weave a song of fey magic that imbues your allies with vigor and speed. As
|
ability to weave a song of fey magic that imbues your allies with
|
||||||
a bonus action, you can expend one use of your Bardic Inspiration to grant
|
vigor and speed. As a bonus action, you can expend one use of your
|
||||||
yourself a wondrous appearance. When you do so, choose a number of
|
Bardic Inspiration to grant yourself a wondrous appearance. When
|
||||||
creatures you can see and that can see you within 60 feet of you, up to a
|
you do so, choose a number of creatures you can see and that can
|
||||||
number equal to your Charisma modifier (mini mum of one). Each of them
|
see you within 60 feet of you, up to a number equal to your
|
||||||
gains 5 temporary hit points. When a creature gains these temporary hit
|
Charisma modifier (minimum of one). Each of them gains 5 temporary
|
||||||
points, it can immediately use its reaction to move up to its speed,
|
hit points. When a creature gains these temporary hit points, it
|
||||||
without provoking opportunity attacks. The number of temporary hit points
|
can immediately use its reaction to move up to its speed, without
|
||||||
inoreases when you reach certain levels in this class, increasing to 8 at
|
provoking opportunity attacks. The number of temporary hit points
|
||||||
5th level, 11 at 10th level, and 14 at 15th level.
|
increases when you reach certain levels in this class, increasing
|
||||||
|
to 8 at 5th level, 11 at 10th level, and 14 at 15th level.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -402,13 +403,14 @@ class MastersFlourish(Feature):
|
|||||||
|
|
||||||
# College of Whispers
|
# College of Whispers
|
||||||
class PsychicBlades(Feature):
|
class PsychicBlades(Feature):
|
||||||
"""When you join the College of Whispers at 3rd level, you gain the ability to
|
"""When you join the College of Whispers at 3rd level, you gain the
|
||||||
make your weapon attacks magically toxic to a creature's mind. When you hit
|
ability to make your weapon attacks magically toxic to a
|
||||||
a creature with a weapon attack, you can expend one use ofyour Bardic
|
creature's mind. When you hit a creature with a weapon attack, you
|
||||||
Inspiration to deal an extra 2d6 psychic damage to that target. You can do
|
can expend one use ofyour Bardic Inspiration to deal an extra 2d6
|
||||||
so only once per round on your turn. The psychic damage increases when you
|
psychic damage to that target. You can do so only once per round
|
||||||
reach certain levels in this class, increasing to 3d6 at 5th level, 5d6
|
on your turn. The psychic damage increases when you reach certain
|
||||||
at 10th level, and 8d6 at 15th level.
|
levels in this class, increasing to 3d6 at 5th level, 5d6 at 10th
|
||||||
|
level, and 8d6 at 15th level.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
\section*{Monsters}
|
||||||
|
|
||||||
|
[% if use_dnd_decorations %]
|
||||||
|
[% for monster in monsters|sort(attribute='name') %]
|
||||||
|
\begin{DndMonster}{[[ monster.name ]]}
|
||||||
|
\DndMonsterType{[[ monster.description ]]}
|
||||||
|
|
||||||
|
% If you want to use commas in the key values, enclose the values in braces.
|
||||||
|
\DndMonsterBasics[
|
||||||
|
armor-class = {[[ monster.armor_class ]]},
|
||||||
|
hit-points = {[[ monster.hp_max ]] ([[ monster.hit_dice ]])},
|
||||||
|
speed = {[[ monster.speed ]] ft.[% if monster.swim_speed %], [[ monster.swim_speed ]]ft. swim[% endif %][% if monster.fly_speed %], [[ monster.fly_speed ]] ft. fly[% endif %]},
|
||||||
|
]
|
||||||
|
|
||||||
|
\DndMonsterAbilityScores[
|
||||||
|
str = [[ monster.strength.value ]],
|
||||||
|
dex = [[ monster.dexterity.value ]],
|
||||||
|
con = [[ monster.constitution.value ]],
|
||||||
|
int = [[ monster.intelligence.value ]],
|
||||||
|
wis = [[ monster.wisdom.value ]],
|
||||||
|
cha = [[ monster.charisma.value ]],
|
||||||
|
]
|
||||||
|
|
||||||
|
\DndMonsterDetails[
|
||||||
|
%saving-throws = {Str +0, Dex +0, Con +0, Int +0, Wis +0, Cha +0},
|
||||||
|
skills = {[[ monster.skills ]]},
|
||||||
|
%damage-vulnerabilities = {cold},
|
||||||
|
damage-resistances = {[[ monster.damage_resistance ]]},
|
||||||
|
%damage-immunities = {poison},
|
||||||
|
condition-immunities = {[[ monster.condition_immunities ]]},
|
||||||
|
senses = {[[ monster.senses ]]},
|
||||||
|
languages = {[% if monster.languages %][[ monster.languages ]][% else %] --- [% endif %]},
|
||||||
|
challenge = {[[ monster.challenge_rating ]]},
|
||||||
|
]
|
||||||
|
%\DndMonsterSection{Actions}
|
||||||
|
[[ monster.__doc__ | rst_to_latex(top_heading_level=2) ]]
|
||||||
|
\end{DndMonster}
|
||||||
|
[% endfor %]
|
||||||
|
|
||||||
|
[% else %]
|
||||||
|
[% for monster in monsters|sort(attribute='name') %]
|
||||||
|
{
|
||||||
|
\section*{[[ monster.name ]]}
|
||||||
|
[% if monster.description %]
|
||||||
|
\subsection*{[[ monster.description ]]}
|
||||||
|
[% endif %]
|
||||||
|
|
||||||
|
\begin{tabular}{c | c | c}
|
||||||
|
Armor Class & Hit Points & Speed \\
|
||||||
|
\hline
|
||||||
|
[[ monster.armor_class ]] &
|
||||||
|
[[ monster.hp_max ]] ([[ monster.hit_dice ]]) &
|
||||||
|
[[ monster.speed ]] \\
|
||||||
|
[% if monster.swim_speed %]
|
||||||
|
& & [[ monster.swim_speed ]] swim \\
|
||||||
|
[% endif %]
|
||||||
|
[% if monster.fly_speed %]
|
||||||
|
& & [[ monster.fly_speed ]] fly \\
|
||||||
|
[% endif %]
|
||||||
|
|
||||||
|
\end{tabular}
|
||||||
|
|
||||||
|
\vspace{0.2cm}
|
||||||
|
|
||||||
|
\begin{tabular}{c | c | c}
|
||||||
|
STR & DEX & CON \\
|
||||||
|
\hline
|
||||||
|
[[ monster.strength.value ]] ([[ monster.strength.modifier|mod_str ]]) &
|
||||||
|
[[ monster.dexterity.value ]] ([[ monster.dexterity.modifier|mod_str ]]) &
|
||||||
|
[[ monster.constitution.value ]] ([[ monster.constitution.modifier|mod_str ]]) \\
|
||||||
|
\end{tabular}
|
||||||
|
|
||||||
|
\vspace{0.2cm}
|
||||||
|
|
||||||
|
\begin{tabular}{p{0.1\textwidth} p{0.32\textwidth}}
|
||||||
|
\textbf{Skills:} & [[ monster.skills ]] \\
|
||||||
|
\textbf{Senses:} & [[ monster.senses ]] \\
|
||||||
|
\textbf{Languages:} & [[ monster.languages ]] \\
|
||||||
|
\textbf{Resistance:} & [[ monster.damage_resistance ]] \\
|
||||||
|
\textbf{Immunities:} & [[ monster.condition_immunities ]] \\
|
||||||
|
\end{tabular}
|
||||||
|
|
||||||
|
\vspace{0.2cm}
|
||||||
|
|
||||||
|
[[ monster.__doc__ | rst_to_latex(top_heading_level=2) ]]
|
||||||
|
|
||||||
|
} %\color
|
||||||
|
[% endfor %]
|
||||||
|
[% endif %]
|
||||||
@@ -51,4 +51,5 @@
|
|||||||
[% endraw %]
|
[% endraw %]
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\chapter*{Features, Magical Items and Spells}
|
|
||||||
|
\chapter*{[[ title ]]}
|
||||||
|
|||||||
+139
-31
@@ -9,11 +9,12 @@ import re
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from multiprocessing import Pool, cpu_count
|
from multiprocessing import Pool, cpu_count
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
from typing import Union, Mapping, Sequence
|
||||||
|
|
||||||
from jinja2 import Environment, PackageLoader
|
from jinja2 import Environment, PackageLoader
|
||||||
|
|
||||||
from dungeonsheets import character as _char, exceptions, readers, latex
|
from dungeonsheets import character as _char, exceptions, readers, latex, monsters
|
||||||
from dungeonsheets.stats import mod_str
|
from dungeonsheets.stats import mod_str, findattr
|
||||||
from dungeonsheets.fill_pdf_template import (
|
from dungeonsheets.fill_pdf_template import (
|
||||||
create_character_pdf_template,
|
create_character_pdf_template,
|
||||||
create_spells_pdf_template,
|
create_spells_pdf_template,
|
||||||
@@ -23,8 +24,6 @@ from dungeonsheets.character import Character
|
|||||||
"""Program to take character definitions and build a PDF of the
|
"""Program to take character definitions and build a PDF of the
|
||||||
character sheet."""
|
character sheet."""
|
||||||
|
|
||||||
PDFTK_CMD = "pdftk"
|
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
ORDINALS = {
|
ORDINALS = {
|
||||||
@@ -53,6 +52,10 @@ jinja_env.filters["mod_str"] = mod_str
|
|||||||
PDFTK_CMD = "pdftk"
|
PDFTK_CMD = "pdftk"
|
||||||
|
|
||||||
|
|
||||||
|
# Custom types
|
||||||
|
File = Union[Path, str]
|
||||||
|
|
||||||
|
|
||||||
def create_subclasses_tex(
|
def create_subclasses_tex(
|
||||||
character: Character,
|
character: Character,
|
||||||
use_dnd_decorations: bool = False,
|
use_dnd_decorations: bool = False,
|
||||||
@@ -77,6 +80,15 @@ def create_magic_items_tex(
|
|||||||
return template.render(character=character, use_dnd_decorations=use_dnd_decorations)
|
return template.render(character=character, use_dnd_decorations=use_dnd_decorations)
|
||||||
|
|
||||||
|
|
||||||
|
def create_monsters_tex(
|
||||||
|
monsters: Sequence[Union[monsters.Monster, str]],
|
||||||
|
use_dnd_decorations: bool = False,
|
||||||
|
) -> str:
|
||||||
|
# Convert strings to Monster objects
|
||||||
|
template = jinja_env.get_template("monsters_template.tex")
|
||||||
|
return template.render(monsters=monsters, use_dnd_decorations=use_dnd_decorations)
|
||||||
|
|
||||||
|
|
||||||
def create_spellbook_tex(
|
def create_spellbook_tex(
|
||||||
character: Character,
|
character: Character,
|
||||||
use_dnd_decorations: bool = False,
|
use_dnd_decorations: bool = False,
|
||||||
@@ -103,23 +115,15 @@ def create_druid_shapes_tex(
|
|||||||
return template.render(character=character, use_dnd_decorations=use_dnd_decorations)
|
return template.render(character=character, use_dnd_decorations=use_dnd_decorations)
|
||||||
|
|
||||||
|
|
||||||
def make_sheet(
|
def make_sheet(sheet_file: File,
|
||||||
character_file,
|
flatten: bool = False,
|
||||||
character=None,
|
fancy_decorations: bool = False,
|
||||||
flatten=False,
|
debug: bool = False):
|
||||||
latex_template=True,
|
"""Make a character or GM sheet into a PDF.
|
||||||
fancy_decorations=False,
|
|
||||||
debug=False,
|
|
||||||
):
|
|
||||||
"""Prepare a PDF character sheet from the given character file.
|
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
character_file : str
|
sheet_file
|
||||||
File (.py) to load character from. Will save PDF using same name
|
File (.py) to load character from. Will save PDF using same name
|
||||||
character : Character, optional
|
|
||||||
If provided, will not load from the character file, just use
|
|
||||||
file for PDF name
|
|
||||||
flatten : bool, optional
|
flatten : bool, optional
|
||||||
If true, the resulting PDF will look better and won't be
|
If true, the resulting PDF will look better and won't be
|
||||||
fillable form.
|
fillable form.
|
||||||
@@ -129,18 +133,120 @@ def make_sheet(
|
|||||||
debug : bool, optional
|
debug : bool, optional
|
||||||
Provide extra info and preserve temporary files.
|
Provide extra info and preserve temporary files.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# Parse the file
|
||||||
|
sheet_file = Path(sheet_file)
|
||||||
|
base_name = sheet_file.stem
|
||||||
|
sheet_props = readers.read_sheet_file(sheet_file)
|
||||||
|
# Create the sheet
|
||||||
|
if sheet_props.get("sheet_type", "") == "gm":
|
||||||
|
ret = make_gm_sheet(basename=base_name, gm_props=sheet_props,
|
||||||
|
fancy_decorations=fancy_decorations,
|
||||||
|
debug=debug)
|
||||||
|
else:
|
||||||
|
ret = make_character_sheet(
|
||||||
|
basename=base_name,
|
||||||
|
character_props=sheet_props,
|
||||||
|
flatten=flatten,
|
||||||
|
fancy_decorations=fancy_decorations,
|
||||||
|
debug=debug)
|
||||||
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def make_gm_sheet(
|
||||||
|
basename: str,
|
||||||
|
gm_props: Mapping,
|
||||||
|
fancy_decorations: bool = False,
|
||||||
|
debug: bool = False,
|
||||||
|
):
|
||||||
|
"""Prepare a PDF character sheet from the given character file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
basename
|
||||||
|
The basename for saving files.
|
||||||
|
gm_props
|
||||||
|
Properties for creating the GM notes.
|
||||||
|
fancy_decorations
|
||||||
|
Use fancy page layout and decorations for extra sheets, namely
|
||||||
|
the dnd style file: https://github.com/rpgtex/DND-5e-LaTeX-Template.
|
||||||
|
debug
|
||||||
|
Provide extra info and preserve temporary files.
|
||||||
|
|
||||||
|
"""
|
||||||
|
tex = [
|
||||||
|
jinja_env.get_template("preamble.tex").render(
|
||||||
|
use_dnd_decorations=fancy_decorations,
|
||||||
|
title=gm_props['session_title'],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
# Add the monsters
|
||||||
|
monsters_ = [findattr(monsters, m)() for m in gm_props.get("monsters", [])]
|
||||||
|
if len(monsters_) > 0:
|
||||||
|
tex.append(
|
||||||
|
create_monsters_tex(monsters_, use_dnd_decorations=fancy_decorations)
|
||||||
|
)
|
||||||
|
# Add the closing TeX
|
||||||
|
tex.append(
|
||||||
|
jinja_env.get_template("postamble.tex").render(
|
||||||
|
use_dnd_decorations=fancy_decorations
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Typeset combined LaTeX file
|
||||||
|
try:
|
||||||
|
if len(tex) > 2:
|
||||||
|
latex.create_latex_pdf(
|
||||||
|
tex="".join(tex),
|
||||||
|
basename=basename,
|
||||||
|
keep_temp_files=debug,
|
||||||
|
use_dnd_decorations=fancy_decorations,
|
||||||
|
)
|
||||||
|
except exceptions.LatexNotFoundError:
|
||||||
|
log.warning(
|
||||||
|
f"``pdflatex`` not available. Skipping {basename}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_character_sheet(
|
||||||
|
basename: str,
|
||||||
|
character_props: Mapping,
|
||||||
|
character: Character = None,
|
||||||
|
flatten: bool = False,
|
||||||
|
fancy_decorations: bool = False,
|
||||||
|
debug: bool = False,
|
||||||
|
):
|
||||||
|
"""Prepare a PDF character sheet from the given character file.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
basename
|
||||||
|
The basename for saving files (PDFs, etc).
|
||||||
|
character_props
|
||||||
|
Properties to load character from.
|
||||||
|
character
|
||||||
|
If provided, will not load from the character file, just use
|
||||||
|
file for PDF name
|
||||||
|
flatten
|
||||||
|
If true, the resulting PDF will look better and won't be
|
||||||
|
fillable form.
|
||||||
|
fancy_decorations
|
||||||
|
Use fancy page layout and decorations for extra sheets, namely
|
||||||
|
the dnd style file: https://github.com/rpgtex/DND-5e-LaTeX-Template.
|
||||||
|
debug
|
||||||
|
Provide extra info and preserve temporary files.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if character is None:
|
if character is None:
|
||||||
char_props = readers.read_sheet_file(character_file)
|
character = _char.Character.load(character_props)
|
||||||
character = _char.Character.load(char_props)
|
|
||||||
|
|
||||||
# Set the fields in the FDF
|
# Set the fields in the FDF
|
||||||
char_base = os.path.splitext(character_file)[0] + "_char"
|
char_base = basename + "_char"
|
||||||
sheets = [char_base + ".pdf"]
|
sheets = [char_base + ".pdf"]
|
||||||
pages = []
|
pages = []
|
||||||
tex = [
|
tex = [
|
||||||
jinja_env.get_template("preamble.tex").render(
|
jinja_env.get_template("preamble.tex").render(
|
||||||
use_dnd_decorations=fancy_decorations
|
use_dnd_decorations=fancy_decorations,
|
||||||
|
title="Features, Magical Items and Spells",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -151,14 +257,13 @@ def make_sheet(
|
|||||||
pages.append(char_pdf)
|
pages.append(char_pdf)
|
||||||
if character.is_spellcaster:
|
if character.is_spellcaster:
|
||||||
# Create spell sheet
|
# Create spell sheet
|
||||||
spell_base = "{:s}_spells".format(os.path.splitext(character_file)[0])
|
spell_base = "{:s}_spells".format(basename)
|
||||||
create_spells_pdf_template(
|
create_spells_pdf_template(
|
||||||
character=character, basename=spell_base, flatten=flatten
|
character=character, basename=spell_base, flatten=flatten
|
||||||
)
|
)
|
||||||
sheets.append(spell_base + ".pdf")
|
sheets.append(spell_base + ".pdf")
|
||||||
# end of PDF gen
|
# end of PDF gen
|
||||||
|
features_base = "{:s}_features".format(basename)
|
||||||
features_base = "{:s}_features".format(os.path.splitext(character_file)[0])
|
|
||||||
# Create a list of subcasses
|
# Create a list of subcasses
|
||||||
if character.subclasses:
|
if character.subclasses:
|
||||||
tex.append(
|
tex.append(
|
||||||
@@ -204,13 +309,13 @@ def make_sheet(
|
|||||||
try:
|
try:
|
||||||
if len(tex) > 2:
|
if len(tex) > 2:
|
||||||
latex.create_latex_pdf(
|
latex.create_latex_pdf(
|
||||||
"".join(tex),
|
tex="".join(tex),
|
||||||
features_base,
|
basename=features_base,
|
||||||
keep_temp_files=debug,
|
keep_temp_files=debug,
|
||||||
use_dnd_decorations=fancy_decorations,
|
use_dnd_decorations=fancy_decorations,
|
||||||
)
|
)
|
||||||
sheets.append(features_base + ".pdf")
|
sheets.append(features_base + ".pdf")
|
||||||
final_pdf = os.path.splitext(character_file)[0] + ".pdf"
|
final_pdf = f"{basename}.pdf"
|
||||||
merge_pdfs(sheets, final_pdf, clean_up=True)
|
merge_pdfs(sheets, final_pdf, clean_up=True)
|
||||||
except exceptions.LatexNotFoundError:
|
except exceptions.LatexNotFoundError:
|
||||||
log.warning(
|
log.warning(
|
||||||
@@ -250,7 +355,7 @@ def _build(filename, args) -> int:
|
|||||||
print(f"Processing {basename}...")
|
print(f"Processing {basename}...")
|
||||||
try:
|
try:
|
||||||
make_sheet(
|
make_sheet(
|
||||||
character_file=filename,
|
sheet_file=filename,
|
||||||
flatten=(not args.editable),
|
flatten=(not args.editable),
|
||||||
debug=args.debug,
|
debug=args.debug,
|
||||||
fancy_decorations=args.fancy_decorations,
|
fancy_decorations=args.fancy_decorations,
|
||||||
@@ -268,7 +373,7 @@ def _build(filename, args) -> int:
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main(args=None):
|
||||||
# Prepare an argument parser
|
# Prepare an argument parser
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Prepare Dungeons and Dragons character sheets as PDFs"
|
description="Prepare Dungeons and Dragons character sheets as PDFs"
|
||||||
@@ -307,7 +412,7 @@ def main():
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Provide verbose logging for debugging purposes.",
|
help="Provide verbose logging for debugging purposes.",
|
||||||
)
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args(args)
|
||||||
# Prepare logging if necessary
|
# Prepare logging if necessary
|
||||||
if args.debug:
|
if args.debug:
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
logging.basicConfig(level=logging.DEBUG)
|
||||||
@@ -326,6 +431,8 @@ def main():
|
|||||||
valid_files.extend(get_char_files(f, parse_dirs=args.recursive))
|
valid_files.extend(get_char_files(f, parse_dirs=args.recursive))
|
||||||
elif fpath.suffix in known_extensions:
|
elif fpath.suffix in known_extensions:
|
||||||
valid_files.append(fpath)
|
valid_files.append(fpath)
|
||||||
|
else:
|
||||||
|
log.info(f"Unhandled file: {str(fpath)}")
|
||||||
return valid_files
|
return valid_files
|
||||||
|
|
||||||
temp_filenames = []
|
temp_filenames = []
|
||||||
@@ -344,6 +451,7 @@ def main():
|
|||||||
# Process the requested files
|
# Process the requested files
|
||||||
if args.debug:
|
if args.debug:
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
|
print("building")
|
||||||
_build(filename, args)
|
_build(filename, args)
|
||||||
else:
|
else:
|
||||||
with Pool(cpu_count()) as p:
|
with Pool(cpu_count()) as p:
|
||||||
|
|||||||
@@ -8,3 +8,7 @@ monsters, etc.
|
|||||||
dungeonsheets_version = "0.14.0"
|
dungeonsheets_version = "0.14.0"
|
||||||
|
|
||||||
sheet_type = "gm"
|
sheet_type = "gm"
|
||||||
|
|
||||||
|
session_title = "Objects in Space"
|
||||||
|
|
||||||
|
monsters = ["wolf", "giant eagle"]
|
||||||
|
|||||||
+76
-2
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from dungeonsheets import features
|
from dungeonsheets import features, character
|
||||||
from dungeonsheets.features import create_feature, Feature, all_features
|
from dungeonsheets.features import create_feature, Feature, all_features, bard
|
||||||
|
|
||||||
|
|
||||||
class TestFeatures(TestCase):
|
class TestFeatures(TestCase):
|
||||||
@@ -31,3 +31,77 @@ class TestFeatures(TestCase):
|
|||||||
self.assertEqual(NewFeature.name, "Hello world")
|
self.assertEqual(NewFeature.name, "Hello world")
|
||||||
feature = NewFeature()
|
feature = NewFeature()
|
||||||
print(feature, feature.__class__, type(feature))
|
print(feature, feature.__class__, type(feature))
|
||||||
|
|
||||||
|
|
||||||
|
class BardTests(TestCase):
|
||||||
|
def test_bardic_inspiration(self):
|
||||||
|
# Level 1-4 Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[2])
|
||||||
|
bi = bard.BardicInspiration(owner=char)
|
||||||
|
self.assertEqual(bi.name, "Bardic Inspiration (1d6/LR)")
|
||||||
|
# Level 5-9 Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[5])
|
||||||
|
bi = bard.BardicInspiration(owner=char)
|
||||||
|
self.assertEqual(bi.name, "Bardic Inspiration (1d8/SR)")
|
||||||
|
# Level 10-14 Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[10])
|
||||||
|
bi = bard.BardicInspiration(owner=char)
|
||||||
|
self.assertEqual(bi.name, "Bardic Inspiration (1d10/SR)")
|
||||||
|
# Level 15+ Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[15])
|
||||||
|
bi = bard.BardicInspiration(owner=char)
|
||||||
|
self.assertEqual(bi.name, "Bardic Inspiration (1d12/SR)")
|
||||||
|
|
||||||
|
def test_song_of_rest(self):
|
||||||
|
# Level 1-8 Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[2])
|
||||||
|
sor = bard.SongOfRest(owner=char)
|
||||||
|
self.assertEqual(sor.name, "Song of Rest (1d6)")
|
||||||
|
# Level 9-12 Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[9])
|
||||||
|
sor = bard.SongOfRest(owner=char)
|
||||||
|
self.assertEqual(sor.name, "Song of Rest (1d8)")
|
||||||
|
# Level 13-16 Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[13])
|
||||||
|
sor = bard.SongOfRest(owner=char)
|
||||||
|
self.assertEqual(sor.name, "Song of Rest (1d10)")
|
||||||
|
# Level 17+ Bard
|
||||||
|
char = character.Character(classes=["bard"], levels=[17])
|
||||||
|
sor = bard.SongOfRest(owner=char)
|
||||||
|
self.assertEqual(sor.name, "Song of Rest (1d12)")
|
||||||
|
|
||||||
|
def test_mantle_of_inspiration(self):
|
||||||
|
for lvl in range(1, 5):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
moi = bard.MantleOfInspiration(owner=char)
|
||||||
|
self.assertEqual(moi.name, "Mantle of Inspiration (5HP)")
|
||||||
|
for lvl in range(5, 10):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
moi = bard.MantleOfInspiration(owner=char)
|
||||||
|
self.assertEqual(moi.name, "Mantle of Inspiration (8HP)")
|
||||||
|
for lvl in range(10, 15):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
moi = bard.MantleOfInspiration(owner=char)
|
||||||
|
self.assertEqual(moi.name, "Mantle of Inspiration (11HP)")
|
||||||
|
for lvl in range(15, 20):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
moi = bard.MantleOfInspiration(owner=char)
|
||||||
|
self.assertEqual(moi.name, "Mantle of Inspiration (14HP)")
|
||||||
|
|
||||||
|
def test_psychic_blades(self):
|
||||||
|
for lvl in range(1, 5):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
pb = bard.PsychicBlades(owner=char)
|
||||||
|
self.assertEqual(pb.name, "Psychic Blades (2d6)")
|
||||||
|
for lvl in range(5, 10):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
pb = bard.PsychicBlades(owner=char)
|
||||||
|
self.assertEqual(pb.name, "Psychic Blades (3d6)")
|
||||||
|
for lvl in range(10, 15):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
pb = bard.PsychicBlades(owner=char)
|
||||||
|
self.assertEqual(pb.name, "Psychic Blades (5d6)")
|
||||||
|
for lvl in range(15, 20):
|
||||||
|
char = character.Character(classes=["bard"], levels=[lvl])
|
||||||
|
pb = bard.PsychicBlades(owner=char)
|
||||||
|
self.assertEqual(pb.name, "Psychic Blades (8d6)")
|
||||||
|
|||||||
@@ -1,12 +1,55 @@
|
|||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from dungeonsheets import make_sheets, character
|
from dungeonsheets import make_sheets, character, monsters
|
||||||
from dungeonsheets.classes import monk
|
from dungeonsheets.classes import monk
|
||||||
|
|
||||||
|
EG_DIR = Path(__file__).parent.parent.resolve() / "examples"
|
||||||
|
CHARFILE = EG_DIR / "rogue1.py"
|
||||||
|
GMFILE = EG_DIR / "gm.py"
|
||||||
|
|
||||||
EG_DIR = os.path.abspath(os.path.join(os.path.split(__file__)[0], "../examples/"))
|
|
||||||
CHARFILE = os.path.join(EG_DIR, "rogue1.py")
|
class MakeSheetsTestCase(unittest.TestCase):
|
||||||
|
char_pdf = Path(f"{CHARFILE.stem}.pdf")
|
||||||
|
gm_pdf = Path(f"{GMFILE.stem}.pdf").resolve()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
if self.char_pdf.exists():
|
||||||
|
self.char_pdf.unlink()
|
||||||
|
if self.gm_pdf.exists():
|
||||||
|
self.gm_pdf.unlink()
|
||||||
|
|
||||||
|
def test_main(self):
|
||||||
|
make_sheets.main(args=[str(CHARFILE), "--debug"])
|
||||||
|
|
||||||
|
def test_make_sheets(self):
|
||||||
|
# Character PDF
|
||||||
|
make_sheets.make_sheet(sheet_file=CHARFILE)
|
||||||
|
# Was the PDF created?
|
||||||
|
self.assertTrue(self.char_pdf.exists(),
|
||||||
|
f"Character PDF ({self.char_pdf.resolve()}) not created.")
|
||||||
|
# GM PDF
|
||||||
|
make_sheets.make_sheet(sheet_file=GMFILE)
|
||||||
|
self.assertTrue(self.gm_pdf.exists)
|
||||||
|
# Was the PDF created?
|
||||||
|
self.assertTrue(self.gm_pdf.exists(),
|
||||||
|
f"GM PDF ({self.gm_pdf.resolve()}) not created.")
|
||||||
|
|
||||||
|
def test_make_fancy_sheets(self):
|
||||||
|
# Character PDF
|
||||||
|
make_sheets.make_sheet(sheet_file=CHARFILE,
|
||||||
|
fancy_decorations=True)
|
||||||
|
# Was the PDF created?
|
||||||
|
self.assertTrue(self.char_pdf.exists(),
|
||||||
|
f"Character PDF ({self.char_pdf.resolve()}) not created.")
|
||||||
|
# GM PDF
|
||||||
|
make_sheets.make_sheet(sheet_file=GMFILE,
|
||||||
|
fancy_decorations=True)
|
||||||
|
self.assertTrue(self.gm_pdf.exists)
|
||||||
|
# Was the PDF created?
|
||||||
|
self.assertTrue(self.gm_pdf.exists(),
|
||||||
|
f"GM PDF ({self.gm_pdf.resolve()}) not created.")
|
||||||
|
|
||||||
|
|
||||||
class PdfOutputTestCase(unittest.TestCase):
|
class PdfOutputTestCase(unittest.TestCase):
|
||||||
@@ -80,3 +123,8 @@ class TexCreatorTestCase(unittest.TestCase):
|
|||||||
tex = make_sheets.create_druid_shapes_tex(character=char)
|
tex = make_sheets.create_druid_shapes_tex(character=char)
|
||||||
self.assertIn(r"\section*{Known Beasts}", tex)
|
self.assertIn(r"\section*{Known Beasts}", tex)
|
||||||
self.assertIn(r"\section*{Crocodile}", tex)
|
self.assertIn(r"\section*{Crocodile}", tex)
|
||||||
|
|
||||||
|
def test_create_monsters_tex(self):
|
||||||
|
monsters_ = [monsters.GiantEagle()]
|
||||||
|
tex = make_sheets.create_monsters_tex(monsters=monsters_)
|
||||||
|
self.assertIn(r"Giant eagle", tex)
|
||||||
|
|||||||
Reference in New Issue
Block a user