% game.cls --- base class for all Template documents % % Written by Jamie Morris starting spring 1998, incorporating % some earlier code of Jeremy Brown's. % The Czar should never need to touch this file; it contains the % hackery that makes other stuff work. If you want to establish a % macro for all your game documents, do it in the custom.sty file; % there's lots of stuff there for you to customize (*everything* there % is for you to customize). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% %% %% BEWARE OF CODE %% %% %% %% Here dwells the magic TeX stuff that makes %% %% everything work. It's very much black magic. %% %% If you play with it and aren't a TeX master, %% %% it will eat your soul and those of your team. %% %% %% %% You have been warned. %% %% %% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % Ask CVS to give us the date automatically (albeit in UTC, sigh); % make sure it parses with and without keywords/dollars (dev vs release) \begingroup \catcode`$=13 \def$#1 {} \catcode`:=13 \def:#1 {hrs } \xdef\cvs@date{2001/08/09 14:28:34 UTC} \xdef\cvs@name{v1-5d } \ifx\cvs@name\empty \gdef\cvs@name{dev} \fi \endgroup \ProvidesClass{game}[\cvs@date\space Assassins' Guild game template \cvs@name] \LoadClassWithOptions{article} \RequirePackage{newdef} % simulate latex.ltx's \g@addto@macro % \newcommand\tmp@str{} % check name not take \newtoks\tmp@str \newcommand\add@to@macro[2]{% \begingroup \tmp@str\expandafter{#1#2}% \xdef#1{\the\tmp@str}% \endgroup } \def\de@lim{\de@lim} \def\g@bble#1\g@bblethis{} % tossing stuff a group ahead \def\end@toss{\end@toss} \def\?{\futurelet\0\@toss} \def\@toss{% \ifx\0\end@toss\let\@NEXT\finish@toss% \else\ifx\0 \let\@NEXT\toss@space% \else\ifx\0\bgroup\let\@NEXT\toss@arg% \else\let\@NEXT\toss@usual% \fi\fi\fi% \@NEXT% } \def\finish@toss#1{} \def\toss@usual#1{\aftergroup#1\?} \let\space@tossed\space \def\toss@space#1 {\expandafter\aftergroup\space@tossed\?} \def\toss@arg#1{\aftergroup{\?#1\end@toss\aftergroup}\?} % \toss@without@braces should only be called locally! \def\toss@without@braces{\def\toss@arg##1{\?##1\end@toss\?}} % fake arrays \newcommand\set@array[3]{\expandafter\def\csname ARRAY#1-#2\endcsname{#3}} \newcommand\get@array[2]{\csname ARRAY#1-#2\endcsname} % Czar calls \usestats in custom.sty; % prepare for charsheet \saystats and for statcards \newcount\stat@count \def\stat@bad{} % we'll use this like a boolean and not waste an \if \def\usestats#1{\stat@count=0{}\do@stats#1\de@lim\de@lim\de@lim} \def\do@stats#1#2#3{% \def\@temp{#1}% \ifx\@temp\de@lim \edef\stat@total@count{\the\stat@count} \let\@NEXT\relax \else \set@array{stat-field}{\the\stat@count}{#1} \set@array{stat-short}{\the\stat@count}{#2} \set@array{stat-long}{\the\stat@count}{#3} \expandafter\ifx\csname BASE-#1\endcsname\relax \expandafter\ifx\csname COMP-#1\endcsname\relax \def\stat@bad{bad} \@warn{Stat #1 unknown; Czar should fix in custom.sty} \fi\fi \advance\stat@count by 1 \let\@NEXT\do@stats \fi \@NEXT } \def\get@stat{% \edef\cur@stat@field{\get@array{stat-field}{\the\stat@count}}% \begingroup \aftergroup\me \aftergroup{% \expandafter\?\cur@stat@field\end@toss \aftergroup}% \endgroup } % \Czar calls \usecharacterformat and \useplayerformat to % set the format of \newchar and \player respectively \def\usecharacterformat#1{\use@format\format@char{#1}} \def\useplayerformat#1{\use@format\format@player{#1}} \def\use@format#1#2{ \begingroup \aftergroup\gdef \aftergroup#1 \aftergroup{ \outer@format#2\de@lim \aftergroup} \endgroup } % outer loop of figuring out charinfo formats \def\outer@format#1{ \def\@temp{#1} \ifx\@temp\de@lim \let\@NEXT\relax % done with outer loop \else \inner@format#1; \de@lim; % inner fields delimited by "; " \aftergroup\an@outer \let\@NEXT\outer@format \fi \@NEXT } \def\split@semi#1;#2\end@split{#1; #2} % inner loop of figuring out charinfo formats \def\inner@format#1; { % inner fields delimited by "; " \def\@temp{#1} \ifx\@temp\de@lim \let\@NEXT\relax % done with inner loop \else \test@semicolon{#1} \if@semicolon % arrange for ";" wo space to also work \expandafter\inner@format\split@semi#1\end@split; \de@lim; % need "; " \else \set@format#1//\end@setformat \fi \let\@NEXT\inner@format \fi \@NEXT } % set that the field is a base field, and what its default value is \def\set@format#1/#2/#3\end@setformat{ \check@letters{#1} \def\@temp{#2} \expandafter\ifx\csname BASE-#1\endcsname\relax \ifx\@temp\empty\tmp@str={\ignorespaces}\else\tmp@str={#2}\fi \expandafter\xdef\csname BASE-#1\endcsname{\the\tmp@str} \?#1\end@toss\aftergroup\an@inner \else \@die{Char base info field ``#1'' illegally duplicated in custom.sty! Production Czar should fix this immediately.} \fi } \def\check@letters#1{ \begingroup \def\5{\futurelet\0\do@check} \def\do@check##1{ \ifx\0\de@lim \let\@NEXT\relax \else \ifcat\0A \else \@die{Char base/combo/pronoun info field ``#1'' contains non-letters in custom.sty! Production Czar should fix this immediately.} \fi \let\@NEXT\5 \fi \@NEXT } \5#1\de@lim \endgroup } \def\make@combo#1#2{ \check@letters{#1} \expandafter\ifx\csname COMP-#1\endcsname\relax \expandafter\ifx\csname BASE-#1\endcsname\relax \expandafter\def\csname COMP-#1\endcsname{#2} \else \@die{Char combo/pronoun ``#1'' illegally duplicates base info field in custom.sty! Production Czar should fix this immediately.} \fi \else \@die{Char combo/pronoun ``#1'' illegally duplicated in custom.sty! Production Czar should fix this immediately.} \fi } % Czar calls \usecombos in custom.sty; set the combo charinfo fields \def\usecombos#1{\do@combos#1\de@lim\de@lim} \def\do@combos#1#2{ \def\@temp{#1} \ifx\@temp\de@lim \let\@NEXT\relax \else \make@combo{#1}{#2\unskip} \let\@NEXT\do@combos \fi \@NEXT } % Czar calls \newpronoun in custom.sty, GMs can do it in sheets; % define the special pronoun combos \newdef\newpronoun[5]{% \make@pronoun{#1}{#2}{#3}{#4}% \make@pronoun{#2}{#1}{#3}{@#4}% \make@pronoun{\@uc#1}{\@uc#2}{\@uc#3}{#5}% \make@pronoun{\@uc#2}{\@uc#1}{\@uc#3}{@#5}% } \def\@uc#1{\uppercase{#1}} \def\make@pronoun#1#2#3#4{% \make@combo{#4}{\csname PRONOUN#4\endcsname{gender}}% \expandafter\def\csname PRONOUN#4\endcsname##1{% \ifx##1M#1\else\ifx##1F#2\else\ifx##1N#3\else \ifx##1m#1\else\ifx##1f#2\else\ifx##1n#3\else #1/#2\fi\fi\fi\fi\fi\fi }% } % \newchar\Ccontrol_sequence{data} \newcommand\eachchar{} \newcommand\eachcharaction{\@die{\string\eachcharaction not set}} \def\newchar#1#2{ \ifx#1\@this@macro@had@better@not@be@defined@\else \@die{\string#1 in Lists/characters.tex is redefined!} \fi \newdef#1[1]{\eval@char#1{##1}} \expandafter\outer@descend\format@char\de@lim#1\de@lim#2\de@lim\g@bblethis \add@to@macro\eachchar{\eachcharaction#1} \expandafter\def\csname CHARACTER-\string#1\endcsname{} } \def\player#1#2{ \expandafter\ifx\csname CHARACTER-\string#1\endcsname\relax \@die{\string#1: Can only give player info in Lists/players.tex for character macros (defined in Lists/characters.tex with \string\newchar)} \fi \expandafter\ifx\csname PLAYER-\string#1\endcsname\relax\else \@die{Can't give player info twice for \string#1 in Lists/players.tex} \fi \expandafter\outer@descend\format@player\de@lim#1\de@lim#2\de@lim\g@bblethis \expandafter\def\csname PLAYER-\string#1\endcsname{} } \newif\ifdelay@eval \delay@evalfalse \def\spaces@only{ } % any number of spaces/tabs are equivalent \def\eval@char#1#2{% char macro, requested info string \def\@temp{#2}% \ifx\@temp\empty \eval@char{#1}{default}% \else\ifx\@temp\spaces@only \eval@char{#1}{default}% \else \def\dump@datum##1{% \begingroup \aftergroup\expandafter \aftergroup\?% \expandafter\aftergroup\csname\string#1.##1\endcsname \aftergroup\end@toss \endgroup }% \begingroup % \< works like \? but since \< has actual use in normal tex % (in tabbing env) we redefine it only locally. \def\<{\futurelet\1\@scan}% \ifdelay@eval\aftergroup\?\fi \gdef\scan@str{}% \<#2\end@scan \ifdelay@eval\aftergroup\end@toss\fi \endgroup \ifdelay@eval\else\unskip\fi \fi\fi } % similar to \? stuff; \< is defined locally in \eval@char above \def\end@scan{\end@scan} \def\@scan{% \ifx\1\end@scan\let\@then\finish@scan% \else\ifx\1 \let\@then\scan@space% \else\ifx\1\bgroup\let\@then\scan@arg% \else\ifcat\noexpand\1A\let\@then\scan@letter% \else\if\noexpand\1@\let\@then\scan@letter% \else\let\@then\scan@other% \fi\fi\fi\fi\fi% \@then% } \newcommand\scan@str{} % check name not taken \def\do@dump{\expandafter\dump@scan\scan@str\@dumped} \def\finish@scan#1{\do@dump} \def\scan@space#1 {\do@dump\expandafter\aftergroup\space@tossed\<} \def\scan@arg#1{\do@dump\aftergroup{\<#1\end@scan\aftergroup}\<} \def\scan@letter#1{\add@to@macro\scan@str#1\<}% no do@dump! \def\scan@other#1{\do@dump\aftergroup#1\<} \def\dump@scan#1\@dumped{% \gdef\scan@str{}% \expandafter\ifx\csname BASE-#1\endcsname\relax \expandafter\ifx\csname COMP-#1\endcsname\relax \?#1\end@toss \else \begingroup \aftergroup\expandafter \aftergroup\<% \expandafter\aftergroup\csname COMP-#1\endcsname \aftergroup\end@scan \endgroup \fi \else \dump@datum{#1}% \fi } % eg \extractdef[\global]\mybadge\thechar{number desc} \def\extractdef{\futurelet\@tok\aux@extract} \def\aux@extract{% \ifx\@tok[% \let\@NEXT\do@extract \else \def\@NEXT{\do@extract[]}% \fi \@NEXT } \def\do@extract[#1]#2#3#4{% \begingroup \delay@evaltrue \?#1\def#2\end@toss \aftergroup{% #3{#4}% \aftergroup}% \endgroup } % outer loop of defining fields for specific char % gets next outer field; remaining outer fields; control seq; next char outer \def\outer@descend#1\an@outer#2\de@lim#3\de@lim#4{ \def\@temp{#4} \ifx\@temp\de@lim % that's all the data this char has \blank@outer#3#1\an@outer#2\de@lim\an@outer \let\@NEXT\g@bble \else \inner@descend#3#1\de@lim#4; \de@lim; \g@bblethis % use "; " \def\@temp{#2} \ifx\@temp\empty % that's all the outer fields there are \let\@NEXT\g@bble \else % keep going \def\@NEXT{\outer@descend#2\de@lim#3\de@lim} \fi \fi \@NEXT } \def\blank@outer#1#2\an@outer{ \def\@temp{#2} \ifx\@temp\de@lim % finished blanking stuff out \let\@NEXT\relax \else \blank@inner#1#2\de@lim\an@inner \def\@NEXT{\blank@outer#1} \fi \@NEXT } % inner loop of defining fields for specific char % gets control seq; next inner field; remaining inner fields; next char inner \def\inner@descend#1#2\an@inner#3\de@lim#4; { \def\@temp{#4} \ifx\@temp\de@lim % that's all the data this char has there \blank@inner#1#2\an@inner#3\de@lim\an@inner \let\@NEXT\g@bble \else \test@semicolon{#4} \if@semicolon \begingroup \?\def\@NEXT\end@toss \aftergroup{ \?\inner@descend#1#2\an@inner#3\de@lim\end@toss \expandafter\?\split@semi#4\end@split\end@toss \?; \end@toss \aftergroup} \endgroup \else \def\@temp{#4} \ifx\@temp\empty\tmp@str={\csname BASE-#2\endcsname}\else\tmp@str={#4}\fi \expandafter\edef\csname\string#1.#2\endcsname{\the\tmp@str} \def\@temp{#3} \ifx\@temp\empty % that's all the inner fields there are \let\@NEXT\g@bble \else % keep going \def\@NEXT{\inner@descend#1#3\de@lim} \fi \fi \fi \@NEXT } \def\blank@inner#1#2\an@inner{ \def\@temp{#2} \ifx\@temp\de@lim % finshed blanking stuff out \let\@NEXT\relax \else \expandafter\edef\csname\string#1.#2\endcsname{\csname BASE-#2\endcsname} \def\@NEXT{\blank@inner#1} \fi \@NEXT } \newdef\@warn[1]{% \immediate\write16{}% \immediate\write16{#1}% \immediate\write16{}% } \newdef\@die[1]{% \immediate\write16{}% \scrollmode \errmessage{#1}% \batchmode \@@end } % for literal (not field delimiter) semicolons in fields \newdef\semicolon[0]{;} % \newdef [0] --> no args but requires braces \newif\if@semicolon \def\LiteralSemicolon{;} \def\test@semicolon#1{\@semicolonfalse\dotest@semicolon#1\de@lim} \def\dotest@semicolon#1{ \def\@temp{#1} \ifx\@temp\de@lim \let\@NEXT\relax \else \ifx\@temp\LiteralSemicolon\@semicolontrue\fi \let\@NEXT\dotest@semicolon \fi \@NEXT } % Czar calls \usenumbers in custom.sty; use auto-generated numbers files \def\usenumbers#1{\do@numbers#1\de@lim} \def\do@numbers#1{ \def\@temp{#1} \ifx\@temp\de@lim \let\@NEXT\relax \else \expandafter\def\csname#1\endcsname##1##2{% \expandafter\ifx\csname NUMBER-#1-##1\endcsname\relax UNASSIGNED% \else \csname NUMBER-#1-##1\endcsname \fi } \def\assign@number##1##2{ \expandafter\def\csname NUMBER-#1-##1\endcsname{##2} } \InputIfFileExists{#1-assigned.tex}{}{} \let\@NEXT\do@numbers \fi \@NEXT } % \testnonzero sees if its argument, fully expanded and typeset, is % zero width, and sets \nonzerotrue or \nonzerofalse accordingly; % you use it by doing \testnonzero{blah blah blah} \ifnonzero... \else... \fi \newbox\test@box \newif\ifnonzero \def\testnonzero#1{% \setbox\test@box=\hbox{#1}% \ifdim\wd\test@box>0sp \nonzerotrue \else \nonzerofalse \fi } % \testinfo\Cchar{info} checks whether the char has a blank entry % for that info; if so, it sets \infofalse, otherwise \infotrue. % use with \testinfo\Cchar{info} \ifinfo... \else... \fi. % If you call with anything but a base info, it's always true, since % the char has *no* entry for anything else, therefore not a blank one! % Note that getting a non-blank default value counts as non-blank. \newif\ifinfo \def\Ignorespaces{\ignorespaces} \def\testinfo#1#2{% \extractdef\extract@temp#1{#2}% \ifx\extract@temp\Ignorespaces\infofalse\else\infotrue\fi } \def\name#1{ \gdef\me{#1}% \doname% } % defaults: \def\doname{} \def\me#1{Unowned} % a random useful macro for mechanics, \rot{n}{text} alphabet-rotates % 'text' by n; see macro glossary \newcount\rot@from \newcount\rot@to \newdef\rot[2]{% \begingroup \def\set@rot##1##2{% \rot@from=##1 \rot@to=#1 \ifnum#1>0 \advance\rot@to by ##1 \else \advance\rot@to by ##2 \advance\rot@to by 1 \fi \loop \ifnum\rot@to>##2 \rot@to=##1 \fi \lccode\rot@from=\rot@to \ifnum\rot@from<##2 \advance\rot@from by 1 \advance\rot@to by 1 \repeat }% \set@rot{`a}{`z}% \set@rot{`A}{`Z}% \lowercase{#2}% \endgroup } % now get the Czar's customizations, which (among other things) invoke % the macros that set up charinfo fields, auto-numbers, etc \RequirePackage{custom} % and now the actual charinfo \makeatother \input Lists/characters.tex \input Lists/players.tex \makeatletter % now we check for characters that don't have player info \let\missing@player\empty \def\miss@player#1{\gdef\missing@player{#1}} \renewcommand\eachcharaction[1]{ \expandafter\ifx csname PLAYER-\string#1\endcsname\relax \player#1{} % set info to blank so things work \miss@player#1 % prepare to warn about it below \fi } \eachchar % \ifx\missing@player\empty\else \@warn{Warning: \expandafter\string\missing@player, possibly others as well, is in Lists/characters.tex but not in Lists/players.tex.} \fi % now set up the usual meaning of \listsheet, in terms of which % list-type extractables should be defined; character extraction % magic will locally redefine it as appropriate %% \listsheet{filename}{code} \newdef\listsheet[2]{#2} \endinput