Skip to content

Instantly share code, notes, and snippets.

@tdhsmith
Created September 16, 2020 23:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tdhsmith/c299b2913f8dc8a29063e0fa95957bb5 to your computer and use it in GitHub Desktop.
Save tdhsmith/c299b2913f8dc8a29063e0fa95957bb5 to your computer and use it in GitHub Desktop.
Pathfinder 2e Chronicle Generator
\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 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