From 4e11af7acd64b9c75dae140bdd71676622a374c5 Mon Sep 17 00:00:00 2001 From: Mark Wolfman Date: Mon, 21 Jun 2021 16:49:24 -0500 Subject: [PATCH] Added an option to put random tables into the GM notes. --- docs/gm_notes.rst | 15 + .../forms/random_tables_template.tex | 275 ++++++++++++++++++ dungeonsheets/make_sheets.py | 32 +- dungeonsheets/mechanics.py | 2 +- examples/gm-campaign-notes.py | 2 + tests/test_make_sheets.py | 5 + 6 files changed, 326 insertions(+), 5 deletions(-) create mode 100644 dungeonsheets/forms/random_tables_template.tex diff --git a/docs/gm_notes.rst b/docs/gm_notes.rst index e91d60b..bba5990 100644 --- a/docs/gm_notes.rst +++ b/docs/gm_notes.rst @@ -45,3 +45,18 @@ party. These will produce a summary table of the attributes of your party. .. _contributing: https://github.com/canismarko/dungeon-sheets/blob/master/CONTRIBUTING.rst + +Random Tables +============= + +Random tables can be used in-game to make decisions on-the-fly. These +tables can be included in the PDF using ``random_tables``. Currently the following random tables are available. + +- **"conjure animals"** - A list of options to choose from when a + player casts the *Conjure Animals* spell. + +.. code-block:: python + :caption: Example: + + random_tables = ["conjure animals"] + diff --git a/dungeonsheets/forms/random_tables_template.tex b/dungeonsheets/forms/random_tables_template.tex new file mode 100644 index 0000000..912763a --- /dev/null +++ b/dungeonsheets/forms/random_tables_template.tex @@ -0,0 +1,275 @@ +\section*{Random Tables} + +[% if conjure_animals %] + + %% https://the-azure-triskele.obsidianportal.com/wikis/conjure-animals-table + \subsection*{Conjure Animals} + + %% Which category of beasts to summon + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d4 & Number of Beasts \\ + [% else %] + \begin{tabular}{c | l} + 1d4 & Number of Beasts \\ + \hline\hline + [% endif %] + 1 & One beast of challenge rating 2 \\ + 2 & Two beasts of challenge rating 1 \\ + 3 & Four beasts of challenge rating 1/2 \\ + 4 & Eight beasts of challenge rating 1/4 or lower \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + + %% CR2 Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d20 & CR2 Beasts \\ + [% else %] + \begin{tabular}{c | l} + 1d20 & Challenge Rating 2 Beasts \\ + \hline\hline + [% endif %] + 1-2 & Allosaurus \\ + 3-4 & Giant Boar \\ + 5-6 & Giant Constrictor Snake \\ + 7-8 & Giant Elk \\ + 9-10 & Hunter Shark \\ + 11 & Plesiosaurus \\ + 12-13 & Polar Bear \\ + 14-15 & Rhinoceros \\ + 16-17 & Saber-toothed Tiger \\ + 18-19 & Swarm of Poisonous Snakes \\ + 20 & Roll on CR 1 Beast Table \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + + %% CR1 Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d12 & Challenge Rating 1 Beasts \\ + [% else %] + \begin{tabular}{c | l} + 1d12 & Challenge Rating 1 Beasts \\ + \hline\hline + [% endif %] + 1 & Brown Bear \\ + 2 & Dire Wolf \\ + 3 & Fire Snake \\ + 4 & Giant Eagle \\ + 5 & Giant Hyena \\ + 6 & Giant Octopus \\ + 7 & Giant Spider \\ + 8 & Giant Toad \\ + 9 & Giant Vulture \\ + 10 & Lion \\ + 11 & Tiger \\ + 12 & Roll on CR 1/2 Beast Table \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + %% CR1/2 Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d20 & Challenge Rating $\frac{1}{2}$ Beasts \\ + [% else %] + \begin{tabular}{c | l} + 1d20 & Challenge Rating $\frac{1}{2}$ Beasts \\ + \hline\hline + [% endif %] + 1-2 & Ape \\ + 3-4 & Black Bear \\ + 5-6 & Crocodile \\ + 7-8 & Giant Goat \\ + 9-10 & Giant Sea Horse \\ + 11-12 & Giant Wasp \\ + 13-14 & Reef Shark \\ + 15-16 & Swarm of Insects (below) \\ + 17-18 & Warhorse \\ + 19 & Worg \\ + 20 & Roll on Lesser Beast Menu Table \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + + %% Swarm of insects (mostly for flavor) + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d6 & Swarm of Insects \\ + [% else %] + \begin{tabular}{c | l} + 1d6 & Swarm of Insects \\ + \hline\hline + [% endif %] + 1 & Ant \\ + 2 & Beatles \\ + 3 & Centipedes \\ + 4 & Locusts \\ + 5 & Spiders \\ + 6 & Wasps \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + + %% Challenge Rating 1/4 and Lesser Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d6 & CR $\frac{1}{4}$ and Lesser Beast Menu \\ + [% else %] + \begin{tabular}{c | l} + 1d6 & CR $\frac{1}{4}$ and Lesser Beast Menu \\ + \hline\hline + [% endif %] + 1-2 & Menu A \\ + 3-4 & Menu B \\ + 5-6 & Menu C \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + + %% CR1/4 and Lesser Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d20 & Lesser Beast Menu A \\ + [% else %] + \begin{tabular}{c | l} + 1d12 & Lesser Beast Menu A \\ + \hline\hline + [% endif %] + 1 & Axe Beak \\ + 2 & Baboon \\ + 3 & Badger \\ + 4 & Bat \\ + 5 & Blood Hawk \\ + 6 & Boar \\ + 7 & Camel \\ + 8 & Cat \\ + 9 & Chicken* \\ + 10 & Constrictor Snake \\ + 11 & Crab \\ + 12 & Deer \\ + 13 & Draft Horse \\ + 14 & Eagle \\ + 15 & Elk \\ + 16 & Flying Snake \\ + 17 & Frog \\ + 18 & Giant Badger \\ + 19 & Giant Bat \\ + 20 & Giant Centipede \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + + \begin{description} + \item [*Chicken:] Raven stats with Advantage on checks to wake + up targets instead of mimicry + \end{description} + %% CR1/4 and Lesser Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d20 & Lesser Beast Menu B \\ + [% else %] + \begin{tabular}{c | l} + 1d12 & Lesser Beast Menu B \\ + \hline\hline + [% endif %] + 1 & Giant Crab \\ + 2 & Giant Fire Beetle \\ + 3 & Giant Frog \\ + 4 & Giant Lizard \\ + 5 & Giant Owl \\ + 6 & Giant Poisonous Snake \\ + 7 & Giant Rat \\ + 8 & Giant Weasel \\ + 9 & Giant Wolf Spider \\ + 10 & Goat \\ + 11 & Hawk \\ + 12 & Hyena \\ + 13 & Jackal \\ + 14 & Lemur* \\ + 15 & Lizard \\ + 16 & Mastiff \\ + 17 & Mule \\ + 18 & Newt** \\ + 19 & Octopus \\ + 20 & Octopus, Cascadian Tree*** \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + +\begin{description} + \item [*Lemur] Weasel stats with a common Climb speed instead of a + bite attack + \item [**Newt:] Lizard stats with Amphibious instead of a bite + attack + \item [***Octopus, Cascadian Tree:] Octopus stats with Amphibious + and a 10 ft land speed instead of camouflage +\end{description} + + %% CR1/4 and Lesser Beasts + [% if use_dnd_decorations %] + \begin{DndTable}{c l} + 1d20 & Lesser Beast Menu C \\ + [% else %] + \begin{tabular}{c | l} + 1d12 & Lesser Beast Menu C \\ + \hline\hline + [% endif %] + 1 & Owl \\ + 2 & Panther \\ + 3 & Poisonous Snake \\ + 4 & Pony \\ + 5 & Pteranodon \\ + 6 & Quipper \\ + 7 & Rat \\ + 8 & Raven \\ + 9 & Riding Horse \\ + 10 & Scorpion \\ + 11 & Sea Horse \\ + 12 & Shocker Lizard* \\ + 13 & Spider \\ + 14 & Swarm of Bats \\ + 15 & Swarm of Rats \\ + 16 & Swarm of Ravens \\ + 17 & Turtle** \\ + 18 & Vulture \\ + 19 & Weasel \\ + 20 & Wolf \\ + [% if use_dnd_decorations %] + \end{DndTable} + [% else %] + \end{tabular} + [% endif %] + +\begin{description} +\item [*Shocker Lizard] Lizard stats with Static Electricity ranged + attack of 1d6 Electricity damage Close/Medium. +\item [**Turtle] Lizard stats with 14 natural armor and no climb + speed. +\end{description} + +[% endif %] diff --git a/dungeonsheets/make_sheets.py b/dungeonsheets/make_sheets.py index 5a41eb4..456b807 100755 --- a/dungeonsheets/make_sheets.py +++ b/dungeonsheets/make_sheets.py @@ -130,6 +130,15 @@ def create_druid_shapes_tex( return template.render(character=character, use_dnd_decorations=use_dnd_decorations) +def create_random_tables_tex( + conjure_animals: bool, + use_dnd_decorations: bool = False, +) -> str: + template = jinja_env.get_template("random_tables_template.tex") + return template.render(conjure_animals=conjure_animals, + use_dnd_decorations=use_dnd_decorations) + + def make_sheet( sheet_file: File, flatten: bool = False, @@ -197,12 +206,12 @@ def make_gm_sheet( tex = [ jinja_env.get_template("preamble.tex").render( use_dnd_decorations=fancy_decorations, - title=gm_props["session_title"], + title=gm_props.pop("session_title", "GM Session Notes"), ) ] # Add the party stats table and session summary party = [] - for char_file in gm_props.get("party", []): + for char_file in gm_props.pop("party", []): # Resolve the file path char_file = Path(char_file) if not char_file.is_absolute(): @@ -212,7 +221,7 @@ def make_gm_sheet( character_props = readers.read_sheet_file(char_file) member = _char.Character.load(character_props) party.append(member) - summary = gm_props.get("summary", "") + summary = gm_props.pop("summary", "") tex.append( create_party_summary_tex( party, summary_rst=summary, use_dnd_decorations=fancy_decorations @@ -220,7 +229,7 @@ def make_gm_sheet( ) # Add the monsters monsters_ = [] - for monster in gm_props.get("monsters", []): + for monster in gm_props.pop("monsters", []): if isinstance(monster, monsters.Monster): # It's already a monster, so just add it new_monster = monster @@ -238,12 +247,27 @@ def make_gm_sheet( tex.append( create_monsters_tex(monsters_, use_dnd_decorations=fancy_decorations) ) + # Add the random tables + random_tables = [s.replace(" ", "_").lower() for s in gm_props.pop("random_tables")] + tex.append( + create_random_tables_tex( + conjure_animals=("conjure_animals" in random_tables), + use_dnd_decorations=fancy_decorations, + ) + ) # Add the closing TeX tex.append( jinja_env.get_template("postamble.tex").render( use_dnd_decorations=fancy_decorations ) ) + # Warn about any unhandled sheet properties + gm_props.pop("dungeonsheets_version") + gm_props.pop("sheet_type") + if len(gm_props.keys()) > 0: + msg = f"Unhandled attributes in '{str(gm_file)}': {','.join(gm_props.keys())}" + log.warn(msg) + warnings.warn(msg) # Typeset combined LaTeX file try: if len(tex) > 2: diff --git a/dungeonsheets/mechanics.py b/dungeonsheets/mechanics.py index 3e62620..70f39bd 100644 --- a/dungeonsheets/mechanics.py +++ b/dungeonsheets/mechanics.py @@ -4,7 +4,7 @@ game mechanics.""" from dungeonsheets.spells import Spell from dungeonsheets.features import Feature from dungeonsheets.infusions import Infusion -from dungeonsheets.weapons import Weapon, MeleeWeapon, RangedWeapon, SimpleWeapon, MartialWeapon, +from dungeonsheets.weapons import Weapon, MeleeWeapon, RangedWeapon, SimpleWeapon, MartialWeapon from dungeonsheets.armor import Armor, Shield from dungeonsheets.magic_items import MagicItem from dungeonsheets.monsters import Monster diff --git a/examples/gm-campaign-notes.py b/examples/gm-campaign-notes.py index 678c0da..1655fd1 100644 --- a/examples/gm-campaign-notes.py +++ b/examples/gm-campaign-notes.py @@ -12,3 +12,5 @@ sheet_type = "gm" session_title = "Objects in Space" party = ["rogue1.py", "paladin2.py"] + +random_tables = ["conjure animals"] diff --git a/tests/test_make_sheets.py b/tests/test_make_sheets.py index 574d551..bc18c41 100644 --- a/tests/test_make_sheets.py +++ b/tests/test_make_sheets.py @@ -185,3 +185,8 @@ class TexCreatorTestCase(unittest.TestCase): # Check that the RST is parsed self.assertIn(r"\emph{adventure}", tex) + def test_random_tables_tex(self): + tex = make_sheets.create_random_tables_tex( + conjure_animals=True, + ) + self.assertIn(r"\subsection*{Conjure Animals}", tex)