Created
September 16, 2020 23:32
-
-
Save tdhsmith/c299b2913f8dc8a29063e0fa95957bb5 to your computer and use it in GitHub Desktop.
Pathfinder 2e Chronicle Generator
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
\documentclass[]{article} | |
\usepackage{wallpaper, array} | |
\usepackage{calc} | |
\usepackage{etoolbox} | |
\usepackage{tikz} | |
\usetikzlibrary{patterns} | |
\usetikzlibrary{calc} | |
\usepackage[absolute,overlay]{textpos} | |
\begin{document} | |
% quick way to define the YYYY-MM-DD date format without a package | |
\def\ISOdate{\leavevmode\hbox{\the\year-\twodigits\month-\twodigits\day}} | |
\def\twodigits#1{\ifnum#1<10 0\fi\the#1} | |
% some repeated text block templates with relative positioning arguments | |
\newcommand{\rightsidebox}[2] { | |
\begin{textblock*}{1.32in}(6.65in,3.835in+#1) | |
\centering | |
\Large \csname#2\endcsname | |
\end{textblock*}} | |
\newcommand{\bottomrowbox}[3] { | |
\begin{textblock*}{#1}(0.55in+#2,10.3in-8pt) | |
\centering | |
\small \csname#3\endcsname | |
\end{textblock*}} | |
% defaults for running this file WITHOUT the accompanying script | |
\providecommand{\pfsEventDate}[0]{\ISOdate} | |
\providecommand{\pfsFameGain}[0]{1} | |
\providecommand{\pfsXPgain}[0]{1} | |
\providecommand{\pfsFactionAGain}[0]{1} | |
\providecommand{\PFSplayerName}[0]{(Somebody)} | |
% add the existing PDF to the background | |
\ThisCenterWallPaper{1}{blankchronicle} | |
% ===== main identification ===== | |
\begin{textblock*}{1.4in}(0.605in,1.75in-16pt) | |
\centering | |
\large \csname pfsPlayerName\endcsname | |
\end{textblock*} | |
\begin{textblock*}{1.445in}(2.35in,1.75in-16pt) | |
\centering | |
\large \csname pfsCharacterName\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.935in}(3.89in,1.75in-10pt) | |
\centering | |
\csname pfsPlayerNumber\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.22in}(5.05in,1.75in-10pt) | |
\raggedleft\csname pfsCharacterNumber\endcsname | |
\end{textblock*} | |
% ===== chronicle and faction ===== | |
\begin{textblock*}{1.165in}(6.795in,0.565in) | |
\centering | |
\large \csname pfsChronicleNumber\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.8in}(6.08in,1.5in-6pt) | |
\centering | |
\scriptsize \csname pfsFactionA\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.23in}(7.46in,1.5in-8pt) | |
\centering | |
\small \csname pfsFactionAGain\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.23in}(7.7in,1.5in-8pt) | |
\centering | |
\small \csname pfsFactionAFinal\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.8in}(6.08in,1.78in-6pt) | |
\centering | |
\scriptsize \csname pfsFactionB\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.23in}(7.46in,1.78in-8pt) | |
\centering | |
\small \csname pfsFactionBGain\endcsname | |
\end{textblock*} | |
\begin{textblock*}{0.23in}(7.7in,1.78in-8pt) | |
\centering | |
\small \csname pfsFactionBFinal\endcsname | |
\end{textblock*} | |
% \begin{textblock*}{0.8in}(6.08in,2.07in-6pt) | |
% \centering | |
% \scriptsize \csname pfsFactionC\endcsname | |
% \end{textblock*} | |
% \begin{textblock*}{0.23in}(7.46in,2.07in-8pt) | |
% \centering | |
% \small \csname pfsFactionCGain\endcsname | |
% \end{textblock*} | |
% \begin{textblock*}{0.23in}(7.7in,2.07in-8pt) | |
% \centering | |
% \small \csname pfsFactionCFinal\endcsname | |
% \end{textblock*} | |
% ===== right column boxes ===== | |
\rightsidebox{0in}{pfsXPstart} | |
\rightsidebox{0.59in}{pfsXPgain} | |
\rightsidebox{1.11in}{pfsXPfinal} | |
\rightsidebox{1.635in}{pfsGPstart} | |
\rightsidebox{2.145in}{pfsGPgain} | |
\rightsidebox{2.66in}{pfsGPincome} | |
\rightsidebox{3.185in}{pfsGPsold} | |
\rightsidebox{3.7in}{pfsGPspent} | |
\rightsidebox{4.22in}{pfsGPfinal} | |
\rightsidebox{4.745in}{pfsFameStart} | |
\rightsidebox{5.255in}{pfsFameGain} | |
\rightsidebox{5.77in}{pfsFameFinal} | |
% ===== bottom boxes ===== | |
\bottomrowbox{1.55in}{0in}{pfsEventName} | |
\bottomrowbox{0.76in}{1.67in}{pfsEventNumber} | |
\bottomrowbox{1.08in}{2.635in}{pfsEventDate} | |
\bottomrowbox{1.43in}{5.925in}{pfsGMnumber} | |
% ===== decorations ===== | |
\ifdefined\pfsLowTierDisable | |
\begin{textblock*}{2.22in}(0.7in,5.32in) | |
\noindent\begin{tikzpicture}[x=1in,y=1in] | |
\fill[pattern=crosshatch, pattern color=black] (0,0) rectangle (2.22,1.8); | |
\end{tikzpicture} | |
\end{textblock*} | |
\fi | |
\ifdefined\pfsHighTierDisable | |
\providecommand{\pfsLowTierDisable}[0]{lol} | |
\begin{textblock*}{2.22in}(0.7in,7.12in) | |
\noindent\begin{tikzpicture}[x=1in,y=1in] | |
\fill[pattern=crosshatch, pattern color=black] (0,0) rectangle (2.22,1.8); | |
\end{tikzpicture} | |
\end{textblock*} | |
\fi | |
\end{document} |
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
# This tool wasn't really designed for the widest audience, but rather cobbled | |
# together from other tools I had on hand, however I did try to err to the side | |
# of more-common stuff when there were multiple obvious options. | |
# It definitely requires: | |
# - a LaTeX distro with pdflatex, and pdfjam if using the last-page grabber | |
# (I'm running it on the MacTex 2020) | |
# - LaTeX packages tikz, calc, etoolbox, wallpaper, array, & textpos | |
# - bash v4+ (notable because Macs still ship with a 3.x by default!) | |
# | |
# I think that should be it. I mean it's a pretty dumb script after all. | |
# | |
# Intended use is: | |
# ./chronicler.sh <adventure.pdf> | |
# It will automatically extract the last page. If the chronicle you want is NOT | |
# on the last page, then manually extract it to "blankchronicle.pdf" in the | |
# same directory and run the script without an argument. | |
# | |
# The rest of the script is interactive prompts. Things in [brackets] usually | |
# indicate defaults if you enter nothing. Generally nothing is required, though | |
# if you want to skip fields with defaults, you will need to enter a space. | |
# | |
# The crosshatching option is more experimental than the others -- I dunno if it | |
# will line up well across adventures. Some have more cutom layouts. | |
# | |
# If the template isn't lining up with the chronicle you have, feel free to | |
# tweak the TeX file. It's not crazy hard if you're just replacing numbers. | |
# Alternatively feel free to pay me money to fix it. | |
# | |
# If you want me to come work with you at Paizo / Wizards / the next great VTT, | |
# email me at <contact (AT) tdhsmith.com>, though I hardly feel this is | |
# representative of my professional work! | |
# add values here with your name etc! | |
DEFAULT_GM_NAME="Sandra Example" | |
DEFAULT_GM_NUMBER="5555555" | |
DEFAULT_EVENT_NAME="Our Game Night" | |
DEFAULT_EVENT_NUMBER="0000000" | |
# Required to accept input with spaces as-is! | |
IFS= | |
echo "GM Name: [$DEFAULT_GM_NAME]" | |
read GM_NAME | |
GM_NAME=${GM_NAME:-$DEFAULT_GM_NAME} | |
echo "GM Number: [$DEFAULT_GM_NUMBER]" | |
read GM_NUMBER | |
GM_NUMBER=${GM_NUMBER:-$DEFAULT_GM_NUMBER} | |
echo "Event Name: [$DEFAULT_EVENT_NAME]" | |
read EVENT_NAME | |
EVENT_NAME=${EVENT_NAME:-$DEFAULT_EVENT_NAME} | |
echo "Event Number: [$DEFAULT_EVENT_NUMBER]" | |
read EVENT_NUMBER | |
EVENT_NUMBER=${EVENT_NUMBER:-$DEFAULT_EVENT_NUMBER} | |
echo "Event Date: [today]" | |
read EVENT_DATE | |
echo "Base Prestige (used as default for fame/XP/rep): [1]" | |
read BASE_PRESTIGE | |
BASE_PRESTIGE=${BASE_PRESTIGE:-1} | |
# ========= | |
echo "Player name:" | |
read PLAYER_NAME | |
echo "Character name:" | |
read CHARACTER_NAME | |
NAME_FOR_FILE=${CHARACTER_NAME:-unknown} | |
echo "Organized play ID:" | |
read PLAYER_NUMBER | |
echo "Character number (without '2' prefix): [001]" | |
read CHARACTER_NUMBER | |
CHARACTER_NUMBER=${CHARACTER_NUMBER:-001} | |
echo "Chronicle number:" | |
read CHRONICLE_NUMBER | |
declare -A factions | |
factions[EA]="Envoy's Alliance" | |
factions[GA]="Grand Archive" | |
factions[HH]="Horizon Hunters" | |
factions[RO]="Radiant Oath" | |
factions[VW]="Verdant Wheel" | |
factions[VS]="Vigilant Seal" | |
echo "Primary faction (shortcuts EA/GA/HH/RO/VW/VS):" | |
read FACTION_A_SHORT | |
if [ -n "$FACTION_A_SHORT" ]; then | |
FACTION_A=${factions[$FACTION_A_SHORT]-$FACTION_A_SHORT} | |
echo "$FACTION_A reputation gain: [$BASE_PRESTIGE]" | |
read FACTION_A_REP_GAIN | |
FACTION_A_REP_GAIN=${FACTION_A_REP_GAIN:-$BASE_PRESTIGE} | |
echo "$FACTION_A final reputation:" | |
read FACTION_A_REP_FINAL | |
fi | |
echo "Secondary faction (shortcuts EA/GA/HH/RO/VW/VS):" | |
read FACTION_B_SHORT | |
if [ -n "$FACTION_B_SHORT" ]; then | |
FACTION_B=${factions[$FACTION_B_SHORT]-$FACTION_B_SHORT} | |
echo "$FACTION_B reputation gain: [2]" | |
read FACTION_B_REP_GAIN | |
FACTION_B_REP_GAIN=${FACTION_B_REP_GAIN:-2} | |
echo "$FACTION_B final reputation:" | |
read FACTION_B_REP_FINAL | |
fi | |
echo "XP initial:" | |
read XP_START | |
echo "XP gained: [$BASE_PRESTIGE]" | |
read XP_GAIN | |
XP_GAIN=${XP_GAIN:-$BASE_PRESTIGE} | |
echo "XP final:" | |
read XP_FINAL | |
echo "GP initial:" | |
read GP_START | |
echo "GP gained:" | |
read GP_GAIN | |
echo "GP from earn income:" | |
read GP_INCOME | |
echo "GP from items sold:" | |
read GP_ITEMS_SOLD | |
echo "GP spent:" | |
read GP_SPENT | |
echo "GP final:" | |
read GP_FINAL | |
echo "Fame initial:" | |
read FAME_START | |
echo "Fame gained: [$BASE_PRESTIGE]" | |
read FAME_GAIN | |
FAME_GAIN=${FAME_GAIN:-$BASE_PRESTIGE} | |
echo "Fame final:" | |
read FAME_FINAL | |
echo "Disable tier rewards? L for low, H for high, B for both" | |
read TIER_DISABLE | |
function texDefiner() | |
{ | |
if [ -n "$2" ]; then | |
echo "\\newcommand{\\$1}[0]{$2}" >> data.tex | |
fi | |
} | |
echo -n "" > data.tex | |
texDefiner pfsGMnumber "$GM_NUMBER" | |
texDefiner pfsEventNumber "$EVENT_NUMBER" | |
texDefiner pfsEventName "$EVENT_NAME" | |
texDefiner pfsEventDate "$EVENT_DATE" | |
texDefiner pfsPlayerName "$SPLAYER_NAME" | |
texDefiner pfsCharacterName "$CHARACTER_NAME" | |
texDefiner pfsPlayerNumber "$PLAYER_NUMBER" | |
texDefiner pfsCharacterNumber "$CHARACTER_NUMBER" | |
texDefiner pfsChronicleNumber "$CHRONICLE_NUMBER" | |
texDefiner pfsFactionA "$FACTION_A" | |
texDefiner pfsFactionAGain "$FACTION_A_REP_GAIN" | |
texDefiner pfsFactionAFinal "$FACTION_A_REP_FINAL" | |
texDefiner pfsFactionB "$FACTION_B" | |
texDefiner pfsFactionBGain "$FACTION_B_REP_GAIN" | |
texDefiner pfsFactionBFinal "$FACTION_B_REP_FINAL" | |
texDefiner pfsXPstart "$XP_START" | |
texDefiner pfsXPgain "$XP_GAIN" | |
texDefiner pfsXPfinal "$XP_FINAL" | |
texDefiner pfsGPstart "$GP_START" | |
texDefiner pfsGPgain "$GP_GAIN" | |
texDefiner pfsGPincome "$GP_INCOME" | |
texDefiner pfsGPsold "$GP_ITEMS_SOLD" | |
texDefiner pfsGPspent "$GP_SPENT" | |
texDefiner pfsGPfinal "$GP_FINAL" | |
texDefiner pfsFameStart "$FAME_START" | |
texDefiner pfsFameGain "$FAME_GAIN" | |
texDefiner pfsFameFinal "$FAME_FINAL" | |
if [ $TIER_DISABLE == "L" ] || [ $TIER_DISABLE == "B" ]; then | |
texDefiner pfsLowTierDisable 1 | |
fi | |
if [ $TIER_DISABLE == "H" ] || [ $TIER_DISABLE == "B" ]; then | |
texDefiner pfsHighTierDisable 1 | |
fi | |
if [ -n "$1" ]; then | |
pdfjam "$1" $((`pdfinfo "$1" | grep Pages | sed 's/[^0-9]*//'`)) -o blankchronicle.pdf | |
texDefiner sourcePDF "blankchronicle.pdf" | |
fi | |
FILENAME="chronicle_$NAME_FOR_FILE" | |
pdflatex -jobname="$FILENAME" "\input{data} \input{chronicle_template}" | |
rm data.tex | |
# TODO: this would be a great place to loop if I implemented that | |
open "$FILENAME.pdf" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment