Created
September 21, 2017 11:07
-
-
Save zhengyangchoong/24847454a12dee2203619657db8801e5 to your computer and use it in GitHub Desktop.
monster statblock generator (with fuzzy search)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Writes to main.tex with \\latex code for monster manuals | |
# argparse to get monster names. Write to latex file with statblocks, and compile | |
# uses Evan Bergeron's awesome latex library. https://github.com/evanbergeron/DND-5e-LaTeX-Template | |
# and also a compiled json file of dnd stats by /u/droiddruid https://www.reddit.com/r/dndnext/comments/43a09o/srd_monsters_in_json_format/?ref=share&ref_source=link | |
import argparse | |
import subprocess | |
import json | |
import string | |
import os, sys | |
from fuzzywuzzy import fuzz | |
from fuzzywuzzy import process | |
import copy | |
def write_preamble(f): | |
preamble = "\\documentclass[10pt,twoside,twocolumn,openany]{article} \n \\usepackage[a4paper]{geometry} \n \\usepackage[bg-print]{dnd} \n \\usepackage[english]{babel} \n \\usepackage[utf8]{inputenc} \n \\begin{document} \n \\fontfamily{ppl}\\selectfont \n" | |
f.write(preamble) | |
return f | |
def main(fileOutput, monsterlist): | |
monsterlist_indices = [] | |
mm = open("5e-SRD-Monsters.json", "r") | |
_mm = json.load(mm) | |
f = open("latex/"+fileOutput + ".tex", "w") | |
f = write_preamble(f) | |
monster_index = [string.lower(_mm[i]["name"]) for i in xrange(len(_mm) - 1)] | |
""" the very last one is the copyright info which doesn't have the name info """ | |
for i in monsterlist: | |
try: | |
monsterlist_indices.append(monster_index.index(i)) | |
except ValueError: | |
a = [fuzz.ratio(i,monster_index[_]) for _ in xrange(len(monster_index))] | |
print max(a) | |
monsterlist_indices.append(a.index(max(a))) | |
# generate the monster box in latex | |
x = "" | |
for i in monsterlist_indices: | |
z =_mm[i] # im lazy | |
#print z["name"] | |
x += "\\begin{{monsterbox}}{{{}}}\n".format(z["name"]) | |
# size subtype type alignment | |
x += "\\textit{{{0} {1} ({2}), {3}}}\\\\\n".format(z["size"], z["subtype"], z["type"], z["alignment"]) | |
# AC, hp, speed | |
x += "\\hline\n\\basics[armorclass={},hitpoints=\\dice{{{}}}, speed = {}]\n\\hline\n".format(z["armor_class"], z["hit_dice"], z["speed"]) | |
# stats | |
x += "\\stats[STR = \\stat{{{}}}, DEX = \\stat{{{}}}, CON = \\stat{{{}}}, INT = \\stat{{{}}}, WIS = \\stat{{{}}}, CHA = \\stat{{{}}}]\n\\hline\n".format(z["strength"], z["dexterity"], z["constitution"], z["intelligence"], z["wisdom"], z["charisma"]) | |
# saving throws, immunities, skills, vulnerabilities | |
zz = copy.deepcopy(z) | |
def extra_details(zz): | |
try: | |
zz.pop('actions', None) | |
zz.pop('special_abilities', None) | |
zz.pop('legendary_actions', None) | |
zz.pop('name', None) | |
for i in ['charisma', 'intelligence', 'dexterity', 'constitution', 'wisdom', 'strength', 'languages', 'hit_points', 'hit_dice', 'subtype', 'senses', 'speed', 'alignment', 'armor_class', 'type', 'challenge_rating', 'size']: | |
zz.pop(i, None) | |
except: | |
pass | |
saves = [x for x in zz.keys() if "save" in x] | |
condition_stuff = [x for x in zz.keys() if "immunities" in x or "damage" in x] | |
_x = "" | |
if len(saves) > 0: | |
_x += "savingthrows={" | |
for i in saves: | |
_x += ("{} {}, ".format(i[:3].upper(), zz[i])) | |
_x += "}, " | |
def addthing(zz, thing): | |
_x = "" | |
if len(zz[thing]) > 0: | |
_x += "{} = ".format(''.join(thing.split("_"))) | |
_x += "{" | |
_x += "{}".format(zz[thing]) | |
_x += "}, " | |
return _x | |
for i in condition_stuff: | |
_x += addthing(zz, i) | |
for i in (saves + condition_stuff): | |
zz.pop(i) | |
if len(zz) > 0: # more than one skill. zz is a dictionary. | |
_x += "skills={" | |
for i in zz: | |
_x += "{} {}, ".format(i.title(), zz[i]) | |
_x += "}," | |
return _x | |
extrastuff = extra_details(zz) | |
# details | |
x += "\\details[senses = {{{}}}, challenge={}, languages={{{}}}, {}]\n\\hline\\\\[1mm]".format(z["senses"], z["challenge_rating"], z["languages"], extrastuff) | |
try: # special abilities. sa is a dictionary | |
for sa in z["special_abilities"]: | |
x += "\\begin{{monsteraction}}[{}]\n".format(sa["name"]) | |
x += "{}\n\\end{{monsteraction}}\n\n".format(sa["desc"]) | |
except KeyError: | |
pass | |
# monster actions | |
x += "\\monstersection{Actions}\n" | |
for ab in z["actions"]: | |
x += "\\begin{{monsteraction}}[{}]\n".format(ab["name"]) | |
x += "{}\n\\end{{monsteraction}}\n\n".format(ab["desc"]) | |
try: | |
_ = z["legendary_actions"] | |
x += "\\monstersection{Legendary Actions}\n" | |
for la in z["legendary_actions"]: | |
x += "\\begin{{monsteraction}}[{}]\n".format(la["name"]) | |
x += "{}\n\\end{{monsteraction}}\n\n".format(la["desc"]) | |
except KeyError: | |
pass | |
x += "\\end{monsterbox}\n" | |
x += ("\\end{document}\n") | |
#print x | |
f.write(x) | |
f.close() | |
os.chdir("latex") | |
subprocess.Popen(["pdflatex","-interaction=nonstopmode", fileOutput]) | |
#print monsterlist | |
parser = argparse.ArgumentParser(description="Get monster list") | |
parser.add_argument("fileOutput", type = str, help = "name of output file (no .tex behind)") | |
parser.add_argument("monsters", nargs="+", help= "type in the monster name from the 5e rulebook. if it has spaces, enclose it with quotation marks. separate monsters by spaces") | |
c = parser.parse_args() | |
#print c.fileOutput | |
main(c.fileOutput, c.monsters) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment