%% basic-stats.sty % % Basic stat blocks for the Role-Playing Game Module class % % Copyright 2016 Michael C. Davis % % LICENSE FOR THE WORK % % This work consists of the following files: % rpg-module.cls % basic-stats.sty % basic-stats.def % doc/rpg-module.tex % % This work may be distributed and/or modified under the conditions of the LaTeX % Project Public License, either version 1.3 of this license or (at your option) % any later version. The latest version of this license can be found at: % http://www.latex-project.org/lppl.txt % and version 1.3 or later is part of all distributions of LaTeX version % 2005/12/01 or later. % % This work has the LPPL maintenance status `author-maintained'. % % The Author and Maintainer of this work is Michael C. Davis % % % LICENSE FOR COMPILED WORKS % % You may distribute compiled works generated using the work as specified in % Clause 3 of the LaTeX Project Public License. If you incorporate Open Gaming % Content into the compiled work, you must also comply with the terms of that % license. % % % USAGE % % See the file rpg-module.pdf (source file doc/rpg-module.tex) for documentation. % There are a number of worked examples in the examples/ directory. % % Technical support is provided on Dragonsfoot Forums: % % http://www.dragonsfoot.org/forums/viewtopic.php?f=87&t=73823 \ProvidesPackage{basic-stats}[2016/04/25 Basic Stat Blocks for the Role-Playing Game Module class] \RequirePackage{tabularx} % Redefine which environment will be displayed \includecomment{ifbasicstats} % Armour class macro % % The default for Basic stats is to use the same descending AC that is used in % the Basic rulebook. However, three alternative styles are defined: ascending % AC style, B1 style and Swords & Wizardry style. Thanks to Zenopus archives % for pointing out that the B1 style is an early form of ascending AC, see: % % http://zenopusarchives.blogspot.com/2014/02/ascending-ac-in-holmes-basic.html \newcounter{acasc@module} % Counter for AC calculations \newcommand\ac@module[1]{% \@ifundefined{acstyle@module}{\gdef\acstyle@module{desc}}{}% % Default AC style is descending \ifnum\pdfstrcmp{\acstyle@module}{desc}=\z@ % Display descending AC #1\ignorespaces \else\ifnum\pdfstrcmp{#1}{---}=\z@ #1\ignorespaces \else% \setcounter{acasc@module}{19}% \IfSubStr{#1}{\,}{\StrBefore{#1}{\,}[\tmpac]}{\def\tmpac{#1}}% \IfBeginWith{\tmpac}{\minus}{% \StrBehind{\tmpac}{\minus}[\tmpac]% \addtocounter{acasc@module}{\tmpac}% }{% \addtocounter{acasc@module}{-\tmpac}% }% \ifnum\pdfstrcmp{\acstyle@module}{asc}=\z@ % Display ascending AC \theacasc@module\ignorespaces \else\ifnum\pdfstrcmp{\acstyle@module}{b1}=\z@ % Display AC using B1 notation #1/\theacasc@module\ignorespaces \else\ifnum\pdfstrcmp{\acstyle@module}{sw}=\z@ % Display AC using Swords & Wizardry notation #1\,(\theacasc@module)\ignorespaces \else Unknown AC style \acstyle@module \fi\fi\fi \fi\fi } % Hit Dice : convert short form to long form for stat blocks \newcommand\hd@module[1]{\IfBeginWith{#1}{\half}{#1 (1-4 hp)}{#1}} % Saving throws : convert long form to short form for stat blocks \newcommand\sv@module[1]{\StrLeft{#1}{1}[\first]\StrBehind{#1}{ }[\second]\IfInteger{\second}{}{\StrLeft{\second}{1}[\second]}\first\second} % Morale macro : convert long form to short form for stat blocks \newcommand\ml@module[1]{\IfSubStr{#1}{ }{\StrBefore{#1}{ }}{#1}} % Alignment macro : convert long form to short form for stat blocks \newcommand\al@module[1]{% \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/AL}}{Any}=\z@ N % For monsters with variable alignment, assume Neutral unless we specify with changealignment \else \StrLeft{\pgfkeysvalueof{/#1/AL}}{1} \fi } % Change alignment macro, for monsters with Alignment ``Any'' or ``Variable'' \newcommand{\changealignment}[2]{\pgfkeys{/#1/AL = {#2}}} % Basic stat block key-value pairs \newcommand{\setstatbasic}[2]{% \ifcase\thecurrentstat \pgfkeys{/#1/Type/.initial = {#2}} \or \pgfkeys{/#1/SilverMagic/.initial = {#2}} \or \pgfkeys{/#1/AC/.initial = \ac@module{#2}} \or \pgfkeys{/#1/HD/.initial = {#2}} \pgfkeys{/#1/HDlong/.initial = \hd@module{#2}} \or \pgfkeys{/#1/MVturn/.initial = {#2}} \or \pgfkeys{/#1/MVrd/.initial = {#2}} \or \pgfkeys{/#1/MVspec/.initial = {#2}} \or \pgfkeys{/#1/MVspec_turn/.initial = {#2}} \or \pgfkeys{/#1/MVspec_rd/.initial = {#2}} \or \pgfkeys{/#1/Attshort/.initial = {#2}} \or \pgfkeys{/#1/Attlong/.initial = {#2}} \or \pgfkeys{/#1/Dmg/.initial = {#2}} \or \pgfkeys{/#1/Dmglong/.initial = {#2}} \or \pgfkeys{/#1/SV/.initial = \sv@module{#2}} \pgfkeys{/#1/SVlong/.initial = {#2}} \or \pgfkeys{/#1/ML/.initial = \ml@module{#2}} \pgfkeys{/#1/MLlong/.initial = {#2}} \or \pgfkeys{/#1/AL/.initial = {#2}} \or \pgfkeys{/#1/NoAppear/.initial = {#2}} \or \pgfkeys{/#1/NoInLair/.initial = {#2}} \or \pgfkeys{/#1/TT/.initial = {#2}} \or \pgfkeys{/#1/XP/.initial = {#2}} \fi } % Define monsters using \setstatbasic \def\scan@stats#1|{% \ifnum\pdfstrcmp{#1}{\relax}=\z@ \let\next\relax \else \setstatbasic{\currentmonster}{#1}\let\next\scan@stats \stepcounter{currentstat} \fi\next } % Display monster stats inline % % Usage: \stats[monster name]{monster key}{no. appearing}{hit points} \newcommand{\stats}[4][default]{% \ifnum\pdfstrcmp{#1}{default}=\z@ \ifnum\pdfstrcmp{#3}{1}=\z@ \pgfkeys{/#2/SingleName}: % Use the generic name defined for this monster (single instance) \else \pgfkeys{/#2/PluralName} (#3): % Use the generic name defined for this monster (plural + number appearing) \fi \else #1 % Override the generic name with the optional argument \fi AC~\pgfkeys{/#2/AC}, HD~\pgfkeys{/#2/HD}, hp~#4, MV~\pgfkeys{/#2/MVturn}\thinspacebrk(\pgfkeys{/#2/MVrd}), \ifnum\pdfstrcmp{\pgfkeysvalueof{/#2/MVspec}}{}=\z@\else \pgfkeys{/#2/MVspec}~\pgfkeys{/#2/MVspec_turn}\thinspacebrk(\pgfkeys{/#2/MVspec_rd}), \fi Att~\pgfkeys{/#2/Attlong}, D~\pgfkeys{/#2/Dmglong}, Save~\pgfkeys{/#2/SV}, ML~\pgfkeys{/#2/ML}, AL~\al@module{#2}, XP~\pgfkeys{/#2/XP}% } % Display monster stats in a stat block % % Usage: \statblock[monster name]{monster key}{no. appearing}{hit points} \newcommand{\statblock}[4][default]{% \begin{statblockfreestyle} \stats[#1]{#2}{#3}{#4} \end{statblockfreestyle} } % Table display format for wandering monsters \newcounter{wandering@module} \newenvironment{wanderingmonsters}[1][c]{\begin{center}\begin{tabular}{clccccccccc} \setcounter{wandering@module}{0}% \tableheader[#1]{Die Roll & Wandering Monster & No. & AC & HD & MV & Attacks & Damage & Save & ML & AL}} {\end{tabular}\end{center}} % Usage: \wanderitem[die roll]{monster key}{no. appearing} % % Leave no. appearing blank to use the value from the monster stats \newcommand{\wanderitem}[3][default]{% \ifnum\pdfstrcmp{#1}{default}=\z@ \stepcounter{wandering@module}\thewandering@module \else \IfInteger{#1}{\setcounter{wandering@module}{#1}\thewandering@module}{#1} \fi & \IfStrEq{#3}{}{% \if\pgfkeysvalueof{/#2/NoAppear}\string 1\pgfkeys{/#2/SingleName}\else\pgfkeys{/#2/PluralName}\fi & \pgfkeys{/#2/NoAppear} & }{% \ifnum\pdfstrcmp{#3}{1}=\z@\pgfkeys{/#2/SingleName}\else\pgfkeys{/#2/PluralName}\fi & #3 & } \pgfkeys{/#2/AC} & \pgfkeys{/#2/HD} & \pgfkeys{/#2/MVrd}% \ifnum\pdfstrcmp{\pgfkeysvalueof{/#2/MVspec}}{}=\z@\else /\pgfkeys{/#2/MVspec_rd}% \fi & \pgfkeys{/#2/Attshort} & \pgfkeys{/#2/Dmg} & \pgfkeys{/#2/SV} & \pgfkeys{/#2/ML} & \al@module{#2}\\ } % Table display format for monster roster \newenvironment{monsterroster}[1][c]{\begin{center}\begin{tabular}{clcccccccccc} \tableheader[#1]{Room & Monster & No. & AC & HD & hp & MV & Attacks & Damage & Save & ML & AL}} {\end{tabular}\end{center}} % Usage: \rosteritem{room key}{monster key}{no. appearing}{hit points} % % If the room key is provided as a reference, the class will create a hyperlink from the monster % roster to the room description \newcommand{\rosteritem}[4]{% #1 & \ifnum\pdfstrcmp{#3}{1}=\z@ \pgfkeys{/#2/SingleName} & \else \pgfkeys{/#2/PluralName} & \fi #3 & \pgfkeys{/#2/AC} & \pgfkeys{/#2/HD} & #4 & \pgfkeys{/#2/MVrd}% \ifnum\pdfstrcmp{\pgfkeysvalueof{/#2/MVspec}}{}=\z@\else /\pgfkeys{/#2/MVspec_rd}% \fi & \pgfkeys{/#2/Attshort} & \pgfkeys{/#2/Dmg} & \pgfkeys{/#2/SV} & \pgfkeys{/#2/ML} & \al@module{#2}\\ } % % New Monster environment % \newcommand{\newmonsterfont}{\Large\bfseries} \newcommand{\newmonsterbottomskip}{\topskip} \newcommand{\longname}[1]{% \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/Type}}{}=\z@ \pgfkeysvalueof{/#1/SingleName}% \else\ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/Type}}{\pgfkeysvalueof{/#1/SingleName}}=\z@ \pgfkeysvalueof{/#1/SingleName}% \else \pgfkeysvalueof{/#1/Type}, \texorpdfstring{\protect\StrDel{\pgfkeysvalueof{/#1/SingleName}}{ \pgfkeysvalueof{/#1/Type}}}{}% \fi\fi } \newenvironment{newmonster}[1]{% \@afterindentfalse\@afterheading\vspace{\topskip} \begin{tabularx}{\linewidth}{@{}l>{\raggedright\arraybackslash}Xl>{\raggedright\arraybackslash}X@{}} \multicolumn{4}{l}{\hspace{-\tabcolsep}\newmonsterfont\longname{#1}\pgfkeys{/#1/SilverMagic}% \phantomsection\addcontentsline{toc}{section}{\longname{#1}}}\\[\topskip] \ArmourClass: & \pgfkeys{/#1/AC} & No. Appearing: & \pgfkeys{/#1/NoAppear} (\pgfkeys{/#1/NoInLair})\\ Hit Dice: & \pgfkeys{/#1/HDlong} & Save As: & \pgfkeys{/#1/SVlong}\\ Move: & \pgfkeys{/#1/MVturn} (\pgfkeys{/#1/MVrd}) & Morale: & \pgfkeys{/#1/MLlong}\\ \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/MVspec}}{}=\z@\else \hspace{1em}\pgfkeys{/#1/MVspec}: & \pgfkeys{/#1/MVspec_turn} (\pgfkeys{/#1/MVspec_rd})\\ \fi Attacks: & \pgfkeys{/#1/Attlong} & Treasure Type: & \pgfkeys{/#1/TT}\\ Damage: & \pgfkeys{/#1/Dmglong} & Alignment: & \pgfkeys{/#1/AL}\\ \end{tabularx}\par\vspace{\newmonsterbottomskip}\noindent\ignorespaces}{\vspace{\newmonsterbottomskip}\par\aftergroup\@afterindentfalse\aftergroup\@afterheading} % Two-column version \newenvironment{newmonster2*}[4]{% \@afterindentfalse\@afterheading\vspace{\topskip} \begin{tabularx}{\linewidth}{@{}X>{\raggedright\arraybackslash}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}X@{}} \ifnum\pdfstrcmp{#3}{}=\z@\else \multicolumn{3}{l}{\hspace{-\tabcolsep}\newmonsterfont#3#4% \phantomsection\addcontentsline{toc}{section}{#3}}\\[\newmonsterbottomskip] \fi & \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/Type}}{\pgfkeysvalueof{/#1/SingleName}}=\z@ Normal \fi\pgfkeys{/#1/SingleName} & \pgfkeys{/#2/SingleName}\\ \cmidrule(l{\tabcolsep}r{\tabcolsep}){2-2} \cmidrule(l{\tabcolsep}r{\tabcolsep}){3-3} \ArmourClass: & \pgfkeys{/#1/AC} & \pgfkeys{/#2/AC}\\ Hit Dice: & \pgfkeys{/#1/HDlong} & \pgfkeys{/#2/HDlong}\\ Move: & \pgfkeys{/#1/MVturn} (\pgfkeys{/#1/MVrd}) & \pgfkeys{/#2/MVturn} (\pgfkeys{/#2/MVrd})\\ \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/MVspec}}{}=\z@\else \hspace{1em}\pgfkeys{/#1/MVspec}: & \pgfkeys{/#1/MVspec_turn} (\pgfkeys{/#1/MVspec_rd}) & \pgfkeys{/#2/MVspec_turn} (\pgfkeys{/#2/MVspec_rd})\\ \fi Attacks: & \pgfkeys{/#1/Attlong} & \pgfkeys{/#2/Attlong}\\ Damage: & \pgfkeys{/#1/Dmglong} & \pgfkeys{/#2/Dmglong}\\ No. Appearing: & \pgfkeys{/#1/NoAppear} (\pgfkeys{/#1/NoInLair}) & \pgfkeys{/#2/NoAppear} (\pgfkeys{/#2/NoInLair})\\ Save As: & \pgfkeys{/#1/SVlong} & \pgfkeys{/#2/SVlong}\\ Morale: & \pgfkeys{/#1/MLlong} & \pgfkeys{/#2/MLlong}\\ Treasure Type: & \pgfkeys{/#1/TT} & \pgfkeys{/#2/TT}\\ Alignment: & \pgfkeys{/#1/AL} & \pgfkeys{/#2/AL}\\ \end{tabularx}\par\vspace{\newmonsterbottomskip}\noindent\ignorespaces}{\vspace{\newmonsterbottomskip}\par\aftergroup\@afterindentfalse\aftergroup\@afterheading} \newenvironment{newmonster2}[2]{\begin{newmonster2*}{#1}{#2}{\pgfkeysvalueof{/#1/Type}}{\pgfkeysvalueof{/#1/SilverMagic}}}{\end{newmonster2*}} % Three-column version \newenvironment{newmonster3*}[5]{% \@afterindentfalse\@afterheading\vspace{\topskip} \begin{tabularx}{\linewidth}{@{}>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}X>{\raggedright\arraybackslash}X@{}} \ifnum\pdfstrcmp{#4}{}=\z@\else \multicolumn{4}{l}{\hspace{-\tabcolsep}\newmonsterfont#4#5% \phantomsection\addcontentsline{toc}{section}{#4}}\\[\newmonsterbottomskip] \fi & \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/Type}}{\pgfkeysvalueof{/#1/SingleName}}=\z@ Normal \fi\pgfkeys{/#1/SingleName} & \pgfkeys{/#2/SingleName} & \pgfkeys{/#3/SingleName}\\ \cmidrule(l{\tabcolsep}r{\tabcolsep}){2-2} \cmidrule(l{\tabcolsep}r{\tabcolsep}){3-3} \cmidrule(l{\tabcolsep}r{\tabcolsep}){4-4} \ArmourClass: & \pgfkeys{/#1/AC} & \pgfkeys{/#2/AC} & \pgfkeys{/#3/AC}\\ Hit Dice: & \pgfkeys{/#1/HDlong} & \pgfkeys{/#2/HDlong} & \pgfkeys{/#3/HDlong}\\ Move: & \pgfkeys{/#1/MVturn} (\pgfkeys{/#1/MVrd}) & \pgfkeys{/#2/MVturn} (\pgfkeys{/#2/MVrd}) & \pgfkeys{/#3/MVturn} (\pgfkeys{/#3/MVrd})\\ \ifnum\pdfstrcmp{\pgfkeysvalueof{/#1/MVspec}}{}=\z@\else \hspace{1em}\pgfkeys{/#1/MVspec}: & \pgfkeys{/#1/MVspec_turn} (\pgfkeys{/#1/MVspec_rd}) & \pgfkeys{/#2/MVspec_turn} (\pgfkeys{/#2/MVspec_rd}) & \pgfkeys{/#3/MVspec_turn} (\pgfkeys{/#3/MVspec_rd})\\ \fi Attacks: & \pgfkeys{/#1/Attlong} & \pgfkeys{/#2/Attlong} & \pgfkeys{/#3/Attlong}\\ Damage: & \pgfkeys{/#1/Dmglong} & \pgfkeys{/#2/Dmglong} & \pgfkeys{/#3/Dmglong}\\ No. Appearing: & \pgfkeys{/#1/NoAppear} (\pgfkeys{/#1/NoInLair}) & \pgfkeys{/#2/NoAppear} (\pgfkeys{/#2/NoInLair}) & \pgfkeys{/#3/NoAppear} (\pgfkeys{/#3/NoInLair})\\ Save As: & \pgfkeys{/#1/SVlong} & \pgfkeys{/#2/SVlong} & \pgfkeys{/#3/SVlong}\\ Morale: & \pgfkeys{/#1/MLlong} & \pgfkeys{/#2/MLlong} & \pgfkeys{/#3/MLlong}\\ Treasure Type: & \pgfkeys{/#1/TT} & \pgfkeys{/#2/TT} & \pgfkeys{/#3/TT}\\ Alignment: & \pgfkeys{/#1/AL} & \pgfkeys{/#2/AL} & \pgfkeys{/#3/AL}\\ \end{tabularx}\par\vspace{\newmonsterbottomskip}\noindent\ignorespaces}{\vspace{\newmonsterbottomskip}\par\aftergroup\@afterindentfalse\aftergroup\@afterheading} \newenvironment{newmonster3}[3]{\begin{newmonster3*}{#1}{#2}{#3}{\pgfkeysvalueof{/#1/Type}}{\pgfkeysvalueof{/#1/SilverMagic}}}{\end{newmonster3*}} % A few special macros for stat blocks \newcommand\x{$\times$} % Text-mode multiplication symbol \newcommand\minus{$-$} % Text-mode minus sign \newcommand{\?}{\discretionary{/}{}{/}} % Breaking slash \newcommand{\+}{\discretionary{\,+}{}{\,+\,}} % Breaking + with 1/6 em space either side % % Load pre-defined monster stats % \input{basic-stats.def}