\ProvidesPackage{charextracts} % This package provides the magic parts of extraction from character sheets. % It should only be used by character.sty, and it's highly unlikely that % the Czar wants to fool around with it. % No .tex file should ever define \WriteGameExtractsAlongPath. % It is set on the command line when extraction is supposed to happen, % e.g. from perl in Charsheets/DVI: %% latex "\def\WriteGameExtractsAlongPath{../Extracts} \input ../foobar" % Whether it is defined sets whether to do extraction: % \newif\if@extracting \expandafter\ifx\csname WriteGameExtractsAlongPath\endcsname\relax \@extractingfalse \else \@extractingtrue \newcommand\extract@prefix{\WriteGameExtractsAlongPath/\jobname^} \fi % generic counter to use here \newcommand\e@n{} % check name not taken \newcount\e@n % specific counter for multiplicities \newcommand\en@multiplicity{} % check name not taken \newcount\en@multiplicity % \if's always want to be defined so rest of this will *parse* \newif\if@tex@extract@ % tex-type as opposed to list-type % % Now we'll have the other things that need to always be defined, % and then the things that are only needed \if@extracting. % % Czar defines new tex-type extractable field in character.sty w \tex@extract % \newcommand\tex@extract[2]{ % do necessary initialization for this extractable \expandafter\newcommand\csname extractable-#1\endcsname{% \if@extracting \@tex@extract@true \extract@open{#1}{#2}% \fi } \expandafter\let\csname suite-#1\endcsname\empty } % % Czar defines new list-type extractable field in character.sty w \list@extract % \newcommand\list@extract[1]{ % do necessary initialization for this extractable \expandafter\newcommand\csname extractable-#1\endcsname{% \if@extracting \@tex@extract@false \immediate\write\extract@list{EXTRACTABLE #1}% \fi } \expandafter\let\csname suite-#1\endcsname\empty } % When GMs do things in a charsheet that need to be passed along % to extractables---e.g. use \newpronoun or \newcommand or something % to create a \foobar that they stick in a memory packet, item, \name, % whatever---they're supposed to do it inside one or more \frobbies at the % top of the charsheet, so that it can get passed along to the extractables % so that the extracted .tex for the mempackets etc doesn't break on it. % Of course, they don't get passed to list-types, so GMs shouldn't % be *too* enthusiastic about simultaneously giving list-type macros args % and doing clever new-macro things in them. % \newcommand\frobbies@set{} \newcommand\frobbies[1]{\add@to@macro\frobbies@set{#1^^J}#1} \@onlypreamble\frobbies % Obviously \extract, \secret, \mention always want to be defined. % If \@extractingfalse they won't actually do all that much, but % they're a bit convoluted anyway from the combination of having % an optional argument (multiplicity) and newdef-wise enforcing braces % around the mandatory argument (to avoid foot-shooting). % % % Simplest first: \mention doesn't do *anything* interesting % beyond handle optional multiplicity and newdef brace behavior. % To get the error messages to always be in terms of \mention, % we define it back and forth. % This first defn is the inner one and won't actually be around much. \newdef\mention[1]{% \ifnum\en@multiplicity=1\relax \let\tell@multiplicity\empty \else \def\tell@multiplicity{(x\the\en@multiplicity)}% \fi \ifnum\en@multiplicity>0\relax % put in braces for grouping, to restrict \em-like changes \item \tell@multiplicity {#1}% \fi \let\mention\outer@mention } % Now we stash that away so that we can invoke it when desired. \let\inner@mention\mention % And now the value that \mention will usually have, which invokes the inner. \newcommand\outer@mention[1][1]{% \en@multiplicity=#1\relax \let\mention\inner@mention \mention } % Now set \mention to its usual value. \let\mention\outer@mention % % % Follow that? \extract is much the same (it even uses \inner@mention % to do its \item typesetting (harmlessly resetting \mention to its outer), % but it also actually Does Stuff. In particular, it extracts things % (multiplicity) times. (\cleverlistsheet does so for list-types.) % Oh --- and \extract[-N] becomes \secret[+N] and vice versa. % Again, this first is just the inner defn. \newdef\extract[1]{% \if@extracting \if@tex@extract@ \e@n=0\relax \loop \ifnum\e@n<\en@multiplicity\relax \to@extract{#1}% \advance\e@n by 1\relax \repeat \else \let\listsheet\cleverlistsheet \def\listextracttype{NORMAL}% \fi \fi \inner@mention{#1}% % why write the same ``list it'' code again? \let\extract\outer@extract \let\secret\outer@secret % in case called as negative secret } % Stash that away for invocation by \outer@extract. \let\inner@extract\extract % The outer meaning that \extract will usually have. \newcommand\outer@extract[1][1]{% \ifnum#1<0\relax \en@multiplicity=-#1\relax \let\extract\inner@secret \else \en@multiplicity=#1\relax \let\extract\inner@extract \fi \extract } % And set to the usual. \let\extract\outer@extract % % % Same song, next verse. Negative \secret is positive \extract. \newbox\secretbox \newdef\secret[1]{% \if@extracting \if@tex@extract@ \e@n=0\relax \loop \ifnum\e@n<\en@multiplicity\relax \add@to@macro\extract@secret{#1^^J}% \advance\e@n by 1\relax \repeat \else \let\listsheet\cleverlistsheet \def\listextracttype{SECRET}% \fi \fi \ifnum\en@multiplicity>0\relax \setbox\secretbox\hbox{#1}% % evaluate code for global side effects (once) \fi \let\secret\outer@secret \let\extract\outer@extract % in case called as negative extract } \let\inner@secret\secret \newcommand\outer@secret[1][1]{% \ifnum#1<0\relax \en@multiplicity=-#1\relax \let\secret\inner@extract \else \en@multiplicity=#1\relax \let\secret\inner@secret \fi \secret } \let\secret\outer@secret % In extractable ``suites,'' sets of extractables (often of different fields) % common to multiple characters, we want to say ``when you get to the % Abilities field, give the \Afoo and \Abar; when you get to Items, \Ibaz.'' % \augment{field name}{material} does the storage part, into % \csname suite-(field name)\endcsname (initialized at field defn time), % and \suitestuff in an extractable list pulls out all augments, then % erases them to be idempotent. \suitestuff is automatically called % when the extractable env ends, so augments appear there by default. % It is an error to never list an \augment'd field. % \newdef\augment[2]{% \expandafter\ifx\csname suite-#1\endcsname\relax \@die{can't \string\augment\space undefined field ``#1''}% \fi \expandafter\ifx\csname EXTRACTED-#1\endcsname\relax\else \@die{can't \string\augment\space already-listed field ``#1''}% \fi \expandafter\ifx\csname suite-#1\endcsname\empty \AtEndDocument{% \expandafter\ifx\csname suite-#1\endcsname\empty \else \@die{#1 extractable field was augmented but never listed}% \fi }% \fi \expandafter\add@to@macro\csname suite-#1\endcsname{#2}% } \newdef\suitestuff{% \ifx\cur@extract\ThisMacroIsNotDefined \@die{can't call \string\suitestuff\space outside extractable field}% \fi \csname suite-\cur@extract\endcsname % do all augments for this field \expandafter\gdef\csname suite-\cur@extract\endcsname{} % be idempotent } % The extractable environment itself, i.e. what happens when GMs % do \begin{extractable}{Foo Bar} ... \end{extractable}. % Does some magic itself, mostly safety checks, and invokes other magic. % \newcommand\NoBracesAllowedInExtractableName[1]{% \def\AnyBraceTest#1FakeName##1{FakeDef}} \newenvironment{extractable}[1] {%% do at \begin: % first check against nested extractable environments \ifx\cur@extract\ThisMacroIsNotDefined \def\cur@extract{#1} % automatically local to environment \else \@die{cannot nest extractables (``#1'' is in ``\cur@extract'')} \fi % now check that this extractable hasn't been done already \expandafter\ifx\csname EXTRACTED-#1\endcsname\relax \expandafter\gdef\csname EXTRACTED-#1\endcsname{}% \else \@die{cannot give extractable #1 twice} \fi % check the extractable name has no braces in it \NoBracesAllowedInExtractableName{#1}% % check that it's a defined extractable, if so do its initialization \expandafter\ifx\csname extractable-#1\endcsname\relax \@die{no such extractable ``#1'' in character.sty} \else \csname extractable-#1\endcsname % has been set up to do initialization \fi % ok, now do the list \filbreak % try to keep list header + list items all together \noindent \textbf{#1}% \begin{tightlist}% }% {%% do at \end \suitestuff % dump suite augments if they haven't been dumped already \end{tightlist}% \if@extracting\if@tex@extract@\extract@close\fi\fi } % % The rest of this we only want when doing magic extraction, so % if we're not, stop now. % \if@extracting \else \endinput \fi % % list-type information all goes to one place, the .Lists file, for perl % \newwrite\extract@list \immediate\openout\extract@list=\extract@prefix.Lists % \extract and \secret will do local redefines of \listsheet so that it DTRT, % including not doing extraneous extractions from nesting % \newcommand\listextracttype{} % will be set to NORMAL or SECRET \let\normallistsheet\listsheet \newdef\cleverlistsheet[2]{% \global\let\listsheet\normallistsheet % We extract these (multiplicity) times 'cause hey, might as well; % the Perl may or may not treat lists as just ``you're there or not.'' \e@n=0\relax \loop \ifnum\e@n<\en@multiplicity % set up by \extract or \secret \immediate\write\extract@list{\cur@extract: \listextracttype\space#1}% \advance\e@n by 1\relax \repeat #2% } % tex-type information, plus the \name tex information, goes various places % we usually send things there literally with \to@extract % \newwrite\extract@tex \newcommand\to@extract[1]{% \begingroup \tmp@str{#1}% \immediate\write\extract@tex{\the\tmp@str}% \endgroup } % want to turn names of extractable fields like ``Foo Bar Baz'' into FooBarBaz % call ``\build@file@name Foo Bar Baz @ '' to build up \extract@file that way % \def\at@sign{@}% \def\build@file@name#1 {% \def\@tmp{#1}% \ifx\@tmp\at@sign \else \add@to@macro\extract@file{#1}% \expandafter\build@file@name \fi } % called when tex-type extractable starts, in its \csname'd initialization % open file for tex-type (or name) information % gets called with args {extractable field}{package-use code} % start the file with appropriate headers % \newcommand\extract@open[2]{% \def\extract@file{\extract@prefix}% \build@file@name#1 @ % trailing space needful \immediate\openout\extract@tex=\extract@file.tex \def\extract@secret{^^J\dosecretextracts^^J^^J}% initialize, will add to it \to@extract{\documentclass{game}}% \to@extract{#2^^J}% % Repeat the charsheet's frobbies. Writing them out will double any #s, % but we first write them out *inside* \newcommand\frobbies{...}, which % neatly undoubles them, and then execute \frobbies to get them. \to@extract{\newcommand\frobbies}% \begingroup \tmp@str\expandafter{\frobbies@set}% \immediate\write\extract@tex{{^^J\the\tmp@str}}% \endgroup \to@extract{\frobbies}% \to@extract{^^J\begin{document}^^J}% \begingroup \tmp@str\expandafter{\me}% \immediate\write\extract@tex{\string\name{\the\tmp@str}^^J}% \endgroup } % gets called when tex-type extractable ends (if @extracting) % spit out the \secret stuff and end the file % \def\extract@close{% \begingroup \tmp@str\expandafter{\extract@secret}% % has been built up with secrets \immediate\write\extract@tex{\the\tmp@str}% \endgroup \to@extract{\end{document}}% \immediate\closeout\extract@tex } % Finally, if we're extracting (remember, if we're not we never got down % to this code at all!) we want to make the name-only file that'll get % turned into EPS for the char's other sheets to blip in; we treat % that like a tex-type extractable, field ``.Name'' (dot prevents conflict), % using the charname.sty package, which sets up for \name to DTRT. % We also want to be polite and close the .Lists file. % \AtEndDocument{% \immediate\closeout\extract@list \extract@open{.Name}{\usepackage{charname}}% \extract@close } % We now return you to your regularly scheduled character.sty file.