From fc082f22f8569ded9b8f518fb13c32cbbbbf1e73 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 19 Aug 2021 20:21:03 +0000 Subject: gamebooklib (19aug21) git-svn-id: svn://tug.org/texlive/trunk@60276 c570f23f-e606-0410-a88d-b1316a301751 --- Master/texmf-dist/doc/latex/gamebooklib/README | 41 + .../doc/latex/gamebooklib/gamebooklib.pdf | Bin 0 -> 265430 bytes .../gamebooklib/gamebooklib_countpagesperseed.sh | 62 ++ .../doc/latex/gamebooklib/gamebooklib_test.pdf | Bin 0 -> 117057 bytes .../doc/latex/gamebooklib/gamebooklib_test.tex | 109 +++ .../texmf-dist/source/latex/gamebooklib/Makefile | 20 + .../source/latex/gamebooklib/gamebooklib.dtx | 998 +++++++++++++++++++++ .../source/latex/gamebooklib/gamebooklib.ins | 53 ++ .../tex/latex/gamebooklib/gamebooklib.sty | 266 ++++++ Master/tlpkg/bin/tlpkg-ctan-check | 3 +- Master/tlpkg/tlpsrc/collection-games.tlpsrc | 1 + Master/tlpkg/tlpsrc/gamebooklib.tlpsrc | 0 12 files changed, 1552 insertions(+), 1 deletion(-) create mode 100644 Master/texmf-dist/doc/latex/gamebooklib/README create mode 100644 Master/texmf-dist/doc/latex/gamebooklib/gamebooklib.pdf create mode 100755 Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_countpagesperseed.sh create mode 100644 Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.pdf create mode 100644 Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.tex create mode 100644 Master/texmf-dist/source/latex/gamebooklib/Makefile create mode 100644 Master/texmf-dist/source/latex/gamebooklib/gamebooklib.dtx create mode 100644 Master/texmf-dist/source/latex/gamebooklib/gamebooklib.ins create mode 100644 Master/texmf-dist/tex/latex/gamebooklib/gamebooklib.sty create mode 100644 Master/tlpkg/tlpsrc/gamebooklib.tlpsrc diff --git a/Master/texmf-dist/doc/latex/gamebooklib/README b/Master/texmf-dist/doc/latex/gamebooklib/README new file mode 100644 index 00000000000..01ac270ee26 --- /dev/null +++ b/Master/texmf-dist/doc/latex/gamebooklib/README @@ -0,0 +1,41 @@ +Copyright (C) 2021 by Robert J Lee + +This file 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 is in: + +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. + + +The gamebooklib package, provides a convenient set of macros to assist +the author choosing to develop a gamebook. In particular, it aims to +solve 2 non-trivial issues: + +1) Writing a set of entries in order, then shuffling them in the +output routine, preserving reference links, and + +2) Allowing LaTeX to output footnotes at the end of an entry, in the +case where another entry falls below the footnote mark on the page. + + +This package uses the standard TeX .dtx+.ins install mechanism, +although a UNIX Makefile is provided for convenience to generate all +files. + +The file "gamebooklib_test.tex" provides a simple demonstration of +this library. + +The file "gamebooklib_countpagesperseed.sh" is a utility script using +bash, perl and pdflatex that will report on the number of pages for +your document, when entries are shuffled with different pseudorandom +seeds. This is useful if you have large objects such as graphics and +want to minimise needless white space. Further instructions are +provided in the header of that file. + +It is suggested that the gamebooklib_countpagesperseed.sh utility +should be placed on the system path for ease of use, or into any +utility scripts directory. \ No newline at end of file diff --git a/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib.pdf b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib.pdf new file mode 100644 index 00000000000..991b237de3b Binary files /dev/null and b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib.pdf differ diff --git a/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_countpagesperseed.sh b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_countpagesperseed.sh new file mode 100755 index 00000000000..926e7d152eb --- /dev/null +++ b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_countpagesperseed.sh @@ -0,0 +1,62 @@ +#!/bin/bash +# Copyright (C) 2021 by Robert J Lee +# +# This file 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 is in: +# +# 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. +# +# A utility script to find the best seed number for a particular gamebook +# +# Prerequisites: bash, tee, tail, egrep, pdflatex, perl with Sort::Naturally +# +# To install Sort::Naturally, you can simply type "cpan install Sort::Naturally" +# assuming Perl is installed. +# +# First keep a backup of your .tex file, just in case. +# +# Next, ensure that you have a line containing "seed=," +# (including the comma) in your .tex file. +# +# The first occurence will be replaced with a sequence of numbers in turn, +# the PDF regenerated, and the number of output pages with each number reported +# on, first by seed number, then by reversed number of pages (smallest last). +# It's then up to you to choose the seed number appropriate for your work. +# +# Use this script with caution as it will edit your source file. +# +# The script may be placed on the system path or copied to the working directory. + +file=${1} + +if [[ -z $file ]]; then + echo "Usage: $0 .tex" + exit 1 +fi + +egrep -q "seed=[0-9]+," $file || ( + echo "$1 Does not contain \\usepackage[seed=123,quiet]{lcg}" + exit 2 +); + +pdf="pdflatex -interaction=nonstopmode $file" + +cp $file $file~ || exit 3 + +# FOR seed in (1..1000) +for seed in $(perl -e 'for (1..1000){print "$_\n"}'); do + # edit $file and replace any seed values + perl -i -pe "s/seed=[0-9]*,/seed=$seed,/" $file + # Run it through LaTeX, stripping out the line of output containing the number of pages, and then + # add the seed value + echo $($pdf 2>&1 | grep 'pages' | tail -n 1 | perl -pe "chomp") "; seed=$seed"; +done | \ + # report on the output as we get it, and report on the sorted pages at the end. + # (sort numerically by first numeral on lines with identical prefix) + perl -MSort::Naturally -pe \ + '{push @lines, $_}; END{print "SORTED:"; print reverse nsort(@lines)}' diff --git a/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.pdf b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.pdf new file mode 100644 index 00000000000..ceac9a9eb7f Binary files /dev/null and b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.pdf differ diff --git a/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.tex b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.tex new file mode 100644 index 00000000000..4d661dce633 --- /dev/null +++ b/Master/texmf-dist/doc/latex/gamebooklib/gamebooklib_test.tex @@ -0,0 +1,109 @@ +%% +%% This is file `gamebooklib_test.tex', +%% +%% Copyright (C) 2021 by Robert J Lee +%% +%% This file 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 is in: +%% +%% 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. +%% +\documentclass{report} +\usepackage[seed=123]{lcg} +\usepackage[verbose,footnote]{gamebooklib} +\usepackage{lipsum} +\begin{document} + +\newcommand{\turnto}[1]{\ref{gentry:#1}\par} + +\section*{Example of Use} + +This document was used for testing during development of gamebooklib. +Please see the \texttt{gamebooklib\_test.tex} file alongside the PDF +to see how the package behaves. + + +\begin{gentry}{codea} + This is the first gentry! + + It actually contains two gentryraphs. + +Turn to \turnto{codeb} +\end{gentry} + +\begin{gentry}{codea} + This one is a duplicate! This creates a `'multiple defined'' labels Warning! +\end{gentry} + +\begin{gentry}{codeb} + This is the second gentry. + + Turn to \turnto{codec} + +Turn to \turnto{coded} +\end{gentry} + +\begin{gentry}{codec}[You lose!] + This is the third gentry. + + You can't escape from here. +\end{gentry} + +\begin{gentry}[123]{coded}[You win!] + This is the second and final gentryraph. + + To play again, turn to \turnto{codea} + + (Note that this is the wrong number due to the duplicate label) +\end{gentry} + +\section*{Here is the result:} + +\thegentries +\eject + +For my next trick, here are some longer and multi-page examples. + +Note the location of the footnotes in these entries. + +\begin{gentry}{restart}\noentryfoot + This is the first gentryraph again. + \lipsum[1]\footnote{First footnote, forced to page end} +\end{gentry} + +\begin{gentry}{restart2} + \lipsum[2-7]\footnote{This is just Lorum Ipsum\footnote{Nonsense + pseudo-latin, with roughly English letter distribution, commonly + used to compare fonts\footnotemark.\footnotetext{Here, it's just nonsense.}}} +\end{gentry} + +\newcounter{foo} +\newcounter{bar} +\setcounter{foo}{3} +\setcounter{bar}{3} +\newcommand{\gentrytst}[2]{ + \begin{gentry}{restart#1} + \lipsum[#1]\footnote{This was gentry #1} + \lipsum[#1-#2]\footnote{This was also gentry #1} + \end{gentry} +} +\whiledo{\not{\value{foo}>10}}{ + \stepcounter{foo} + \stepcounter{bar} + \stepcounter{bar} + \edef\idx{\arabic{foo}} + \edef\idxx{\arabic{bar}} + \expandafter\gentrytst\expandafter{\idx}{\idxx} +} + +\section*{Here is the result:} + +\thegentries +\vfill + +\end{document} diff --git a/Master/texmf-dist/source/latex/gamebooklib/Makefile b/Master/texmf-dist/source/latex/gamebooklib/Makefile new file mode 100644 index 00000000000..eae89220722 --- /dev/null +++ b/Master/texmf-dist/source/latex/gamebooklib/Makefile @@ -0,0 +1,20 @@ +default: gamebooklib_test.pdf gamebooklib.pdf tidy + +gamebooklib_test.pdf : gamebooklib_test.tex gamebooklib.sty + pdflatex \\nonstopmode\\input gamebooklib_test.tex && \ + pdflatex \\nonstopmode\\input gamebooklib_test.tex + +gamebooklib.sty : gamebooklib.dtx gamebooklib.ins + rm -f gamebooklib.sty && latex gamebooklib.ins + +gamebooklib.pdf : gamebooklib.sty gamebooklib.dtx + pdflatex \\nonstopmode\\input gamebooklib.dtx && \ + makeindex -s gglo.ist -o gamebooklib.gls gamebooklib.glo && \ + makeindex -s gind.ist -o gamebooklib.ind gamebooklib.idx && \ + pdflatex \\nonstopmode\\input gamebooklib.dtx + +tidy: + rm -f *.log *.ind *.idx *.glo *.gls *.aux *.ilg + +clean: tidy + rm -f *~ *.sty diff --git a/Master/texmf-dist/source/latex/gamebooklib/gamebooklib.dtx b/Master/texmf-dist/source/latex/gamebooklib/gamebooklib.dtx new file mode 100644 index 00000000000..eed4fc64c6a --- /dev/null +++ b/Master/texmf-dist/source/latex/gamebooklib/gamebooklib.dtx @@ -0,0 +1,998 @@ +% \iffalse meta-comment +% +% Copyright (C) 2021 by Robert J Lee +% +% This file 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 is in: +% +% 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. +% +% \fi +% +% \iffalse +% \NeedsTeXFormat{LaTeX2e}[2005/12/01] +% \ProvidesPackage{gamebooklib} +% [2021/08/10 v1.0 gamebooklib typesetting] +% +%<*driver> +\documentclass{ltxdoc} +\usepackage{makeidx} +\usepackage[seed=1234]{lcg} +\usepackage{gamebooklib} +\usepackage{indentfirst} +\EnableCrossrefs +\CodelineIndex +\RecordChanges +\makeindex +\begin{document} + \DocInput{gamebooklib.dtx} +\end{document} +% +% \fi +% +% \CharacterTable +% {Upper-case \A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T\U\V\W\X\Y\Z +% Lower-case \a\b\c\d\e\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u\v\w\x\y\z +% Digits \0\1\2\3\4\5\6\7\8\9 +% Exclamation \! Double quote \" Hash (number) \# +% Dollar \$ Percent \% Ampersand \& +% Acute accent \' Left paren \( Right paren \) +% Asterisk \* Plus \+ Comma \, +% Minus \- Point \. Solidus \/ +% Colon \: Semicolon \; Less than \< +% Equals \= Greater than \> Question mark \? +% Commercial at \@ Left bracket \[ Backslash \\ +% Right bracket \] Circumflex \^ Underscore \_ +% Grave accent \` Left brace \{ Vertical bar \| +% Right brace \} Tilde \~} +% +% \CheckSum{507} +% +% \changes{v1.0}{2021/08/10}{Initial Release} +% +% \GetFileInfo{gamebooklib.sty} +% +% \DoNotIndex{\let, \edef, \xdef, \relax, \newcommand} +% \DoNotIndex{\csname, \endcsname, \@M, \@MM, \@gobble} +% \DoNotIndex{\@ifnextchar, \@tmp, \@undefined, \\} +% \DoNotIndex{\arabic, \begin, \begingroup, \botmark, \break} +% \DoNotIndex{\@tmp, \@tmp@, \mytmp@, \tmp@, \tmp@@} +% \DoNotIndex{\centerline, \cs, \def, \detokenize, \else} +% \DoNotIndex{\endgroup, \equal, \expandafter, \fi} +% \DoNotIndex{\gdef, \global, \head, \hspace, \Huge, \if} +% \DoNotIndex{\ifcsname, \ifhmode, \ifnum, \ifthenelse, \ifvmode} +% \DoNotIndex{\interlinepenalty, \interlinefootnotepenalty} +% \DoNotIndex{\large, \leftskip, \mark, \NeedsTeXFormat} +% \DoNotIndex{\newcounter, \newenvironment, \newtoks, \noexpand} +% \DoNotIndex{\noindexnt, \nopagebreak, \not, \PackageInfo, \pagebreak} +% \DoNotIndex{\par, \PassOptionsToClass, \penalty, \ProcessOptions} +% \DoNotIndex{\protected@xdef, \ProvidesPackage, \rand, \requirecommand} +% \DoNotIndex{\RequirePackage, \rightskip, \rule, \setbox} +% \DoNotIndex{\setcounter, \stepcounter, \textbf, \typeout, \value} +% \DoNotIndex{\vbox, \unvbox, \vfill, \vspace, \vsize} +% \DoNotIndex{\WarningFilter, \whiledo, \Collect@Body, \CurrentOption} +% \DoNotIndex{\DeclareOption, \g@addto@macro, \the, \noindent} +% \DoNotIndex{\interfootnotelinepenalty, \marginpar, \myidx} +% \DoNotIndex{\addtocounter, \refstepcounter, \label} +% \DoNotIndex{\iffalse, \iftrue, \outputpenalty, \renewcommand} +% \DoNotIndex{\noshuffle, \verbose, \endpage, \footnote} +% +% \title{The Gamebooklib Package\thanks{This document corresponds to \textsf{Gamebooklib}~\fileversion, dated \filedate.}} +% \author{Robert J Lee \\ latex@rjlee.homelinux.org} +% +% \maketitle +% +% \begin{abstract} +% +% This package provides macros and environments to allow the user to +% typeset a series of cross-referenced, numbered ``entries'', +% shuffled into random order +% +% \end{abstract} +% +% \section{Introduction} +% +% This package was written to allow the typesetting of gamebooks. +% +% A gamebook is a book divided into ``entries'' (which may be a single +% paragraph or longer), each with a sequentially numbered value. At +% the end of each entry, a link to one or more other numbered +% entries is given; the reader selects one and they follow through +% the story in this order. gamebooks traditionally start at the +% entry numbered~``1'' and some gamebooks have multiple endings, +% with the final section representing a success or victory for the +% reader. +% +% In particular, this package handles two technical challenges which +% are tricky to solve with \LaTeX\ directly: writing the ``entries'' +% in order but presenting them in a randomised order; and presenting +% footnotes at the end of each ``entry'', numbered in the order in +% which they appear, not the order in which they are written. +% +% Aside from shuffling the paragraphs and fixing the handling of +% footnotes, this package provides little help for the actual +% typesetting of gamebook entries. For this, please consider using +% Andr\'e Miede's |gamebook| package, available on +% CTAN\footnote{\texttt{https://www.ctan.org/pkg/gamebook}}. That +% package is orthogonal to this one; both may be used together. +% +% \section*{Terminology} +% +% For the purposes of this document, the term \textit{entry} means a +% numbered section of text. This term helps describe the +% common format of a gamebook, while avoiding confusion with the terms +% \textit{paragraph} and a \textit{section}, both of which +% already have a clear definition. +% +% The term \textit{gamebook} means a book consisting of numbered +% sections, typically presented in a non-linear order with a +% tree or graph of possible reading options. +% +% \section*{Usage} +% +% To load the class, use |\usepackage[|\textit{options}|]{gamebook}| +% +% The class options are described below; |[footnotes]| is normally a +% good choice, although there are some limitations. +% +% The user simply writes entries like this inside the document: +% \begin{verbatim} +% \begin{gentry}{codea} +% This is the first entry +% +% Turn to \turnto{codeb} +% \end{gentry} +% +% \begin{gentry}[123]{codeb}[title] +% This is the second and final entry.\par +% To play again, turn to \turnto{codea} +% \end{gentry} +% +% % Output: +% \thegentries +% \end{verbatim} +% +% Here, two entries are defined, and then output. +% +% The first entry is given a label of |gentry:codea|, and the +% \cs{turnto\{codea\}} in the second entry generates a link back to +% that label. +% +% As |entry| is a common term likely to conflict with other +% environments, the environment to declare an entry has been named +% |gentry| (gamebook entry) instead. +% +% Sometimes it is convenient, when writing an entry, to give it a fixed +% index in the text. The first optional argument in the second entry +% --- |[123]| --- fixes the entry at that position during +% shuffling. This is implemented by iterating over each entry and +% swapping it with that index. Duplicates are not currently checked +% for, but they would not be lost; if two entries are defined with the +% same fixed index, one will end up somewhere else in the text. +% +% The first and last entries are automatically fixed without +% needing to specify the optional number. +% +% Specifying the optional number as a value greater than or equal to +% the number of entries, or less than~2, is not recommended. (Yes, +% this example used |123|, which is beyond the number of entries. This +% was done to show the syntax). +% +% The second optional argument is a title to be displayed alongside +% the numerical value. +% +% Finally, the command \cs{thegentries} will output all entries defined so +% far, handling shuffling and footnotes. +% +% \DescribeMacro{\gentryheader}\marg{counterIdx}\marg{fixedIdx}\marg{code}\marg{title} +% +% Users may also want to redefine \cs{gentryheader} to change how the +% entries are displayed. The initial version is quite basic and +% intended for debugging; users should use \cs{renewcommand} to change +% it to something more pleasant. +% +% Here's one suggestion, using packages from CTAN. It works well if you have either a +% title, or at least 2 lines of text before the first paragraph break +% inside the |gentry|. +% +% +% It also requires near the start of the document: +% \begin{verbatim} +% \usepackage{lettrine} +% \usepackage{etoolbox} +% \usepackage{color} +% \end{verbatim} +% And this anywhere before \cs{thegentries}: +% \begin{verbatim} +% \renewcommand{\LettrineFontHook}{% +% \color[gray]{0.5}\fontfamily{ppl}\fontseries{bx}} +% \renewcommand{\gentryheader}[4]{ +% \lettrine[lines=3,lhang=0.2,loversize=0.2]{\raisebox{0.1em}{#1}}% +% {\textbf{\Large\textbf{\raisebox{0.3em}{~#4} +% }}}% +% \ifstrempty{#1}{}{\linebreak\mbox{}~}} +% \end{verbatim} +% On the last line, \cs{mbox} stops the \cs{parindent} space being +% ignored; the $\sim$ brings it back up to the expected spacing. +% +% \section*{Package options} +% +% These options can be specified as a comma-separated list to the +% \cs{usepackage} line. +% +% \begin{macro}{footnote} +% \changes{v1.0}{2021/08/10}{Footnote option} +% The |footnote| option causes \LaTeX\ to output footnotes at the end +% of each entry, or the end of each page, whichever comes first after +% the footnote mark. +% +% There are some limitations, described below. +% +% Support for other packages that affect footnotes is limited. If +% using |hyperref|, it is recommended to pass |[hyperfootnotes=false]| +% to avoid broken links. +% \end{macro} +% +% \begin{macro}{noshuffle} +% \changes{v1.0}{2021/08/10}{Noshuffle option} +% In general, it's easier to write gamebooks in a more linear fashion, +% in which related entries are kept together. But this is much less +% fun to play, as it's too easy for the reader to simply read the +% adjacent eentries to decide what to do. +% For this reason, this package shuffles the output entries by +% default. The first and last entries are never shuffled. +% +% Sometimes, perhaps for proofreading, you may not want the entries +% to be shuffled. In this case, you can use the |noshuffle| option. +% \end{macro} +% +% \begin{macro}{verbose} +% \changes{v1.0}{2021/08/10}{Verbose option} +% This macro causes the package to output information messages about +% what it's doing to the log file. This is not generally too useful, but it does +% include a mapping of the original paragraph indexes to their sorted +% positons, which may be useful to keep for handling proofreading +% corrections. +% \end{macro} +% +% \begin{macro}{endpage} +% \changes{v1.0}{2021/08/10}{Endpage option} +% Puts the last entry on a page on its own. This typically produces a +% better ``winning'' feel for reaching the last entry, but it can also +% produce documents with ugly spacing, so it's recommended to try it +% each way and see which works better for your text. +% \end{macro} +% +% +% \section*{Footnotes} +% +% When typesetting a gamebook with footnotes, it is confusing if they +% migrate to the bottom of each page, as the footnote becomes visually +% detached from the entry to which it relates. Some effort has been taken to +% ensure that footnotes can be typeset at the bottom of the page on which +% the mark appears, or the bottom of each entry, whichever comes +% first. +% +% However, this implementation has some limitations: +% +% \begin{itemize} +% \item Footnotes are not expanded until they are typeset. +% \item As a consequence, attaching one footnote to another with the +% use of \cs{footnotemark} within a \cs{footnote} argument will not +% advance the counter in time, so \cs{footnotetext} may not behave as +% you expect unless it is also set inside the first footnote's text. +% \item Footnotes at the end of the page will not be broken across +% pages. Putting sufficient text into footnotes will cause the page +% to overflow. +% \item Where the footnote \textbf{mark} appears at the very end of a page, +% the footnote \textbf{text} may be set at the top of the subsequent +% page. \TeX\ is asked not to break the page there, but this influence +% is limited. It may be possible for the author to avoid this, eg by +% adding a rubber length to the interline spacing (it may be easier to +% simply choose a different seed value for the |lcg| package to +% reshuffle). +% \item If you have a lot of rubber space in the text, or +% variably-sized items, \LaTeX\ may expand the footnote at the end of +% the entry before it works out where to put the page break. In this +% case, the footnote will appear on the wrong page. The macro +% \cs{noentryfoot} is provided to allow the author to fix this case. +% \item Support for other footnotes packages may be limited and the +% behaviour of packages like |footnote|, |endnotes|, |footmisx|, +% |fnote|, |dblfnote| \textit{etc} may be affected. +% \item If using the |hyperref| package, it is recommended to pass the +% option |hyperfootnotes=false| to that package, as the footnote links +% will be incorrect. +% \item The package uses \TeX\ \cs{mark}s to indicate which footnotes +% should appear on each page. Because \TeX\ supports only one set of +% marks, this would break any other package or usage of \cs{mark} +% while a gamebook was being output. +% \item Because \cs{mark} does not escape a floating environment, such +% as |minipage|, it is likely that this footnote implementation will +% not work as expected if the gamebook or \cs{footnote} is set in a |minipage| or +% similar. +% \item Each footnote is evaluated in a group. Just conceivably, this +% might affect the behaviour of some macros that affect the document +% beyond the footnote. +% \item We rely on some non-``public'' macros, such as \cs{@mpfn} and +% \cs{@footnotetext} defined by \LaTeX\ standard document +% classes. Other document classes may override these, which would +% override this package's footnotes too. +% \end{itemize} +% +% For this reason, the improved footnote handling is only enabled if +% you pass the |footnote| option, and only while the environment is +% active. +% +% \section*{Implementation} +%\StopEventually{\PrintChanges \pagebreak[4] \PrintIndex} +% +% \begin{macrocode} +\ProvidesPackage{gamebooklib}[2021/08/10 Gamebook by R Lee latex@rjlee.homelinux.org] +% \end{macrocode} +% We need \LaTeXe, for the extra token registers. +% \begin{macrocode} +\NeedsTeXFormat{LaTeX2e}[1994/06/01] +% \end{macrocode} +% \begin{macro}{verbose} +% The package option |verbose| enables detailed logging. +% Logging is via the macro \cs{gamebook@info}, which throws away detail messages +% unless the |verbose| option is given. +% \begin{macrocode} +\newcommand{\gamebook@info}[1]{}% +\DeclareOption{verbose}{% + \renewcommand{\gamebook@info}[1]{\PackageInfo{gamebooklib}{#1}}% + \gamebook@info{Gamebook Library package is logging}}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{endpage} +% The |endpage| option puts the last entry on its own page. This can +% work better when the last entryraph is about a page long, and also +% the final ``winning'' entry of the gamebook. +% \begin{macrocode} +\newcommand\gamebook@beforelast{} +\DeclareOption{endpage}{% + \renewcommand\gamebook@beforelast{\eject}% +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{footnote} +% The |footnote| option enables our footnote processing, to throw out +% footnotes at the end of each |gentry| so that they don't appear to be +% against subsequent entries for that page. +% +% This is genearlly recommended, unless you have a reason to turn it +% off (such as a conflicting package). It's disabled by default +% because it could cause unexpected errors. +% \begin{macrocode} +\def\if@gamebook@footnotes{\iffalse} +\DeclareOption{footnote}{% + \gdef\if@gamebook@footnotes{\iftrue}% + \gamebook@info{Gamebook Library footnotes per gamebook entry}% +}% +% \end{macrocode} +% \end{macro} +% +% \begin{macro}{noshuffle} +% \begin{macrocode} +\def\if@gamebook@shuffle{\iftrue} +\DeclareOption{noshuffle}{% + \gdef\if@gamebook@shuffle{\iffalse}% + \gamebook@info{Gamebook Library entries output in order}% +}% +% \end{macrocode} +% \end{macro} +% +% All unknown options are passed to |lcg|, as it's our only dependency with options. +% It's suggested that users specify a value for ``seed'' for stable +% builds, by adding the following before including this package: +% +% |\usepackage[seed=123]{lcg}| +% \begin{macrocode} +\DeclareOption*{% + \typeout{Current option: \CurrentOption}% + \PassOptionsToClass{\CurrentOption}{lcg}% +}% +\ProcessOptions\relax% +% \end{macrocode} +% We need to capture environment contents +% \begin{macrocode} +\RequirePackage{environ}% +% \end{macrocode} +% |Macroswap|: used to swap the commands that evaluate the macros +% \begin{macrocode} +\RequirePackage{macroswap}% +% \end{macrocode} +% |Ifthen| makes branching and loops a little easier +% \begin{macrocode} +\RequirePackage{ifthen} +% \end{macrocode} +% |LCG|: random numbers for the shuffle +% \begin{macrocode} +\RequirePackage{lcg}% +% \end{macrocode} +% |Silence| is used to suppress a warning from~|lcg| that gamebook is +% \textbf{not} wasting counter registers. |debrief| ensures that the +% user is at least told than warnings were suppressed. +% +% To see the warnings, put the following line before +% |\usepackage{gamebook}|: +% +% |\usepackage[debrief,showwarnings]{silence}|: +% \begin{macrocode} +\RequirePackage[debrief]{silence}% +% \end{macrocode} +% +% Let's count the entries we're reading in so we can build up token +% registers. +% \begin{macrocode} +\newcounter{gentryctr}% +\setcounter{gentryctr}{0}% +% \end{macrocode} +% +% \begin{environment}{gentry}\oarg{fixedIdx}\marg{code}\oarg{title} +% First, we need to parse the optional arguments. +% +% When we say \cs{begin}\{gentry\}, \LaTeX\ does some stuff ending in \cs{gentry} +% so we redefine \cs{gentry} to take an optional argument and delegate to \cs{@gentry} +% \begin{macrocode} +\newcommand{\gentry}[1][]{\@gentry{#1}}% +% \end{macrocode} +% \cs{gentry} just makes the first argument mandatory. +% +% \cs{@gentry} defines \cs{gentryidx} and \cs{gentrycode}, which can be used in +% the body of the \cs{gentry} environment to supply the unsorted index +% number and the current entry's unique code. +% +% NB: If you define two entries with the same code, \LaTeX\ will print +% out a "multiply defined" label warning. +% \begin{macrocode} +\newenvironment{@gentry}[2]{% + \def\gentryidx{#1}% + \def\gentrycode{#2}% + \@@gentry% +}{\ignorespacesandallpars% +}% +% \end{macrocode} +% \cs{@@gentry} then reads in the optional title argument, storing it in the \cs{gentrytitle} +% macro to supply the unsorted index number and the current entry's code respectively. +% \begin{macrocode} +\newcommand{\@@gentry}[1][]{% + \def\gentrytitle{#1}% + \stepcounter{gentryctr}% +% \end{macrocode} +% For fixed entries, define a macro to hold the requested index +% \begin{macrocode} + \ifthenelse{\equal{\gentryidx}{}}{}{% + \expandafter\xdef\csname fixedat\arabic{gentryctr}% + \endcsname{\gentryidx}% + }% + \expandafter\global\expandafter\newtoks\expandafter{% + \csname paratok\arabic{gentryctr}\endcsname}{ }% + \Collect@Body\gentry@store% +% \end{macrocode} +% This was supposed to discard any blank lines at the end of the +% |gentry| environment, but it still left odd spacing in the +% output somehow, and sometimes produced weird error messages about +% |\inaccessible| and |\head|. Removed for now. +% \begin{macrocode} +% \ignorespacesandallpars% +} +% \end{macrocode} +% Store the collected environment contents for \cs{thegentries} to output: +% This uses the token register \cs{gentry$N$}, where $N$ is the +% unsorted index of this entry. +% Later, we'll change the values of the $N$ bit of \cs{gentry$N$} +% macros when we shuffle (the underlying token registers stay the same). +% +% This relies on \cs{refstepcounter}\marg{gentryctr} before expanded +% before this macro. The label |gentry:\gentrycode| is thus set to the +% current index of the final output entry. +% \begin{macrocode} +\newcommand{\gentry@store}[1]{% + \edef\head{\noexpand\begingroup\noexpand\gentryheader% + {\arabic{gentryctr}}{\gentryidx}{\gentrycode}{\gentrytitle}% + \noexpand\label{gentry:\gentrycode}% + }% + \global\expandafter\csname paratok\arabic{gentryctr}\endcsname=% +% \end{macrocode} +% Output the header, the environment token list, flush any footnotes (if applicable) then the inter-gentry footer. +% \begin{macrocode} + \expandafter{\head #1% + \outputfootnotes@endgentry% + \gentryfooter\endgroup}% +}% +% \end{macrocode} +% \end{environment} +% +% \DescribeMacro{\gentry@footnotespergentry} +% This macro is executed inside the |gentry| group and sets up the +% commands to be run to allow footnotes. It is only expanded if +% \cs{if@gamebook@footnotes} is true, \textit{ie} the |footnotes| +% option is given. +% \begin{macrocode} +\newcommand{\gentry@footnotespergentry}{} +% \end{macrocode} +% +% \DescribeMacro{\thegentries} +% +% This macro shuffles the entries as required, then expands to them in +% the correct order. +% \begin{macrocode} +\newcommand{\thegentries}{% +% \end{macrocode} +% This command is in a group so that the output routine resets. +% +% The expansion of \cs{gentry@footnotespergentry} only happens if the +% |footnote| option was given; it sets up the output routine while the +% gamebook is running. +% \begin{macrocode} +\begingroup% + \if@gamebook@footnotes\gentry@footnotespergentry\fi% +% \end{macrocode} +% To begin, let's record the number of entries. This may come in +% useful. Note that you can use this macro in the |gentry| environment, +% because that's not been expanded yet. +% \begin{macrocode} + \xdef\gentrycount{\arabic{gentryctr}}% +% \end{macrocode} +% +% The next thing is to perform some surgery on LCG. +% This cuts out an \textbf{annoying} warning, hopefully more reliably +% than replacing the definition of \cs{p@stkeysr@nd}. +% The warning is simply that this package doesn't waste another +% counter every time it changes the random limits (which happens a lot +% during Knuth shuffle): +% \begin{macrocode} + \WarningFilter{lcg}{Using an already existing counter rand}% +% \end{macrocode} +% +% \subsection*{The output routine and end-of-page footnotes} +% The idea is to keep footnotes always on the same page as their mark where possible. +% +% \LaTeX\ does lots of fun things with the output routine, which we +% want to keep. So grab a copy of whatever the code is currently doing: +% +% Here I'm using the \cs{edef} trick to expand \cs{the}\cs{output} into a token register, because using a macro +% causes a weird error about an ``|{|'' after ``\cs{the}''. +% \begin{macrocode} +\newtoks\gentry@oldoutput{}% +\edef\mytmp@{\noexpand\gentry@oldoutput={\the\output}}\mytmp@% +% \end{macrocode} +% For the footnotes, if we reach the end of a page without outputting +% them, we need to flush them. +% +% \cs{output} is the output routine. It takes the page built up in +% \cs{box255}, annotates it with headers, footers etc, then ships it +% out. For our purposes, we only need to append the footnotes to the +% bottom of \cs{box255}. +% \begin{macrocode} +\if@gamebook@footnotes\output={% +% \end{macrocode} +% The \cs{outputpenalty} tells us why the output routine was called; generally, it's invoked whenever +% a new floatable environment is generated, or when a page is full. Anything less than -1000 means that +% the page was filled, so we should add any footnotes only in this case. +% \begin{macrocode} + \ifnum\outputpenalty<-\@M\else% + \edef\tmp@{footnotetoks\botmark} + \expandafter\ifcsname footnotetoks\botmark\endcsname% + \global\setbox255=\vbox to \vsize{% + \unvbox255\vfill\outputfootnotes@endpage}% + \fi% + \fi% + \the\gentry@oldoutput +}\fi% +% \end{macrocode} +% +% \subsection*{The shuffling algorithm} +% +% \TeX\ has well beyond 255 token registers these days, so don't +% bother to check that limit. +% +% The LCG package provides a suitable pseudo-random number generator. +% What we want is a repeatable series of numbers. +% +%\begin{enumerate} +% \item Work out how many entries there are~($N$). Provided \cs{thegentries} is +% called at the end, this is just the value of |gentryctr| +% \item Declare a set of token registers named \cs{paratoks}$n$, where $n$ is +% each integer 1\dots$N$ inclusive. These will hold the contents of the entry. +% \item Declare a set of macros named \cs{paraidx}$n$, where $n$ is +% each integer 1\dots$N$ inclusive, initialised to $n$. These will hold the number of the entry. +% \item Shuffle elements $\{2:N-1\}$, in that array. For $i=2$ through $N-2$ +% \begin{enumerate} +% \item Let $R$ be a random number between $i$ and $N-1$ inclusive +% \item If $R\ne i$ then swap macros \cs{gentrytoks}$R$ and \cs{gentrytoks}$i$ +% \item If $R\ne i$ then swap macros \cs{paraIdx}$R$ and \cs{gentryidx}$i$ +% \end{enumerate} +% \item For $i=1:n$, output token reg $i$ +%\end{enumerate} +% +% Define macros \cs{csname}~|paraIdx|$N'$\cs{endcsname} containing the arabic +% original page number to be put out on the $N'$th output ``page'' +% \begin{macrocode} + \xdef\gentrycount{\arabic{gentryctr}}% +% \end{macrocode} +% we can reuse |gentryctr|; we've finished this set of paras and +% kept the total count. +% \begin{macrocode} + \setcounter{gentryctr}{0}% +% \end{macrocode} +% If there are fewer than 3 entries, don't try and shuffle. +% \begin{macrocode} + \ifthenelse{\gentrycount<3}{}{%SHUFFLE START + \whiledo{\not{\value{gentryctr}>\gentrycount}}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\xdef\csname paraIdx\myidx\endcsname{\myidx}% + \stepcounter{gentryctr}% + }% +% \end{macrocode} +% Don't renumber para~1 or \cs{gentrycount}; Knuth-shuffle the rest +% NB: we stop at \cs{gentrycount}$-2$, because \cs{gentrycount}$-1$ would only shuffle +% with itself. +% \begin{macrocode} + \if@gamebook@shuffle + \setcounter{gentryctr}{2}% + \setcounter{rand}{\gentrycount}% + \addtocounter{rand}{-1}\edef% + \stoppoint{\arabic{rand}}% + \chgrand[last=\stoppoint]% + \whiledo{\value{gentryctr}<\stoppoint}{% +% \end{macrocode} +% If this is to be swapped with a fixed position, skip it +% \begin{macrocode} + \edef\myidx{\arabic{gentryctr}}% + \expandafter\ifcsname fixedat\myidx\endcsname% + \gamebook@info{Not shuffling \myidx; fixed pos}% + \stepcounter{gentryctr}% + \else% + \gamebook@info{Shuffling \myidx}% + \stepcounter{gentryctr}% + \edef\nextidx{\arabic{gentryctr}}% +% \end{macrocode} +% If we've hit an entry with fixed position, we may as well skip it +% \begin{macrocode} + \expandafter\ifcsname fixedto\arabic{rand}\endcsname\else% + \chgrand[first=\nextidx]% + \rand% + \gamebook@info{Shuffling \myidx to \arabic{rand}}% + \macroswap{paraIdx\myidx}{paraIdx\arabic{rand}}% + \fi% + \fi% + }% +% \end{macrocode} +% Now move fixed entries into their final place: +% \begin{macrocode} + \setcounter{gentryctr}{2}% + \whiledo{\value{gentryctr}<\stoppoint}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\ifcsname fixedat\myidx\endcsname% + \expandafter\edef\expandafter\mydest\expandafter% + {\expandafter\csname fixedat\myidx\endcsname}% + \gamebook@info{MOVING FIXED GAMEBOOK ENTRY INTO PLACE: \myidx -> \mydest}% + \macroswap{paraIdx\myidx}% + {paraIdx\expandafter\csname fixedat\myidx\endcsname}% + \fi% + \stepcounter{gentryctr}% + }}\fi%SHUFFLE END + \gamebook@info{Shuffled! Gentry order:}% + \setcounter{gentryctr}{1}% + \whiledo{\not{\value{gentryctr}>\gentrycount}}{% + \edef\myidx{\arabic{gentryctr}}% + \gamebook@info{\myidx -> \csname paraIdx\myidx\endcsname}% + \stepcounter{gentryctr}% + }% +% \end{macrocode} +% +% We can reuse the same counter again to output +% \begin{macrocode} + \setcounter{gentryctr}{0}% + \gamebook@info{Outputting \gentrycount\ gamebook entries}% + \whiledo{\value{gentryctr}<\gentrycount}{% +% \end{macrocode} +% Use refstepcounter in the loop to allow \cs{label} to work as expected. +% \begin{macrocode} + \refstepcounter{gentryctr}% +% \end{macrocode} +% Keep last entry on its own page +% \begin{macrocode} + \ifthenelse{\value{gentryctr}=\gentrycount}{% + \gamebook@beforelast% + }{}% + \edef\myidx{\arabic{gentryctr}}% + \xdef\youridx{\csname paraIdx\myidx\endcsname}% + \gamebook@info{Output gentry \myidx\ of \gentrycount,% + original idx \youridx}% +% \end{macrocode} +% Output the stored entry body, stripping any extraneous space: +% \begin{macrocode} + \the\csname paratok\youridx\endcsname% + }% +% \end{macrocode} +% Finally, we clear the registers and reset the counter in case we want to start again +% \begin{macrocode} + \gamebook@info{All gamebook entries added to main vertical list}% + \setcounter{gentryctr}{1}% + \whiledo{\not{\value{gentryctr}>\gentrycount}}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\global\expandafter\let% + \csname paratok\myidx\endcsname\@undefined% + \expandafter\ifcsname fixedat\myidx\endcsname% + \expandafter\global\expandafter\let% + \csname fixedat\myidx\endcsname\@undefined% + \fi% + \stepcounter{gentryctr}% + }% + \setcounter{gentryctr}{0}% + \eject + \endgroup% +}% +% \end{macrocode} +% The final \cs{eject} ensures that the output routine has flushed as +% many pages as it can, before the output routine is reset again. +% +% \DescribeMacro{\gentryheader}\marg{counterIdx}\marg{fixedIdx}\marg{code}\marg{title} +% +% This macro is called before outputting the header. Its +% job is to format whatever header information the user wants to see +% on each entry; generally, this will be the page number. +% +% It takes 4 parameters: +%\begin{enumerate} +%\item The \textit{unshuffled} index value (to help an author find an +% entry in the original text) +%\item The \textit{fixed} index value, if any; otherwise an empty argument +%\item The user-entered unique code for this entry +%\item The user-supplied title, if any; otherwise an empty argument +%\end{enumerate} +% +% The value of the output index can be obtained with |\arabic{gentryctr}| +% \begin{macrocode} +\newcommand{\gentryheader}[4]{% + \noindent\textbf{\Huge\arabic{gentryctr}\large\ #4}% + \nopagebreak% + \vspace{0.3em}% + \nopagebreak% + \par% + \marginpar{#3}% +}% +% \end{macrocode} +% +% \DescribeMacro{\gentryfooter} +% This takes no arguments and is simply expanded after the entry is typeset. The default adds some vertical +% space and a simple separator. +% +% \begin{macrocode} +\newcommand{\gentryfooter}{% + \par\vspace{2em}\centerline{---}\vspace{2em plus 1in}\par% +}% +% \end{macrocode} +% +% \DescribeMacro{\ignorespacesandallpars@} +% This is a technique described on StackExchange: +% +% {\small https://tex.stackexchange.com/questions/179016/\\ignore-spaces-and-pars-after-an-environment\#179034} +% +% This is used to ensure that extra space at the start and end of the |gentry| +% environment is ignored. +% +% \begin{macrocode} +\def\ignorespacesandallpars@{% + \@ifnextchar\par% + {\expandafter\ignorespacesandallpars\@gobble}% + {}% +}% +% \end{macrocode} +% +% \DescribeMacro{\@footnotetext}\marg{token register} +% Token registers to hold each footnote. +% +% Each footnote is held in its own token register, so that we can control +% which footnotes appear on each page. +% +% \LaTeX\ would normally use saveboxes to store footnotes, but I +% prefer to hold off on expanding them until we know which page +% they're to be expanded on, which saves some difficulties (and +% perhaps creates others). +% +% To start with, modify \LaTeX's \cs{@makefnmark} to output a mark for +% the footnote, holding its index. This tells the output routine which +% footnotes to include at the end of the page. The +% \cs{in@out} macro is only defined when outputting footnotes, so +% suppresses this mark when we don't need it. +% \begin{macrocode} +\g@addto@macro{\@makefnmark}{% + \ifcsname in@out\endcsname\else% + \mark{\arabic{\@mpfn}}% + \fi% +}% +% \end{macrocode} +% +% The \cs{footnotetext@save} is a convenience that takes the the +% footnote counter value in the first argument, the token register as +% a second argument, and the token list (footnote text) as the third. +% \begin{macrocode} +\g@addto@macro{\gentry@footnotespergentry}{% +\newcommand{\@footnotetext@save}[3]{% + \global\newtoks#2{}% + \global#2={\noindent\@footnotemark{}#3}% +}}% +% \end{macrocode} +% \cs{@footnotetext@save} unpacks the arguments for +% \cs{@footnotetext@save@}. This one uses the value of the \cs{@mpfn} +% counter to unpack the footnote counter, the token register (full csname) as +% the first argument, and the token list (footnote text) as the second. +% \begin{macrocode} +\g@addto@macro{\gentry@footnotespergentry}{% +\newcommand{\@footnotetext@save@}[2]{% + \edef\@tmp@{\expandafter\arabic{\@mpfn}}% + \expandafter\@footnotetext@save\expandafter{\@tmp@}{#1}{#2}% +}}% +% \end{macrocode} +% Footnotes are built in a group, so that \cs{in@out} can be defined +% locally to indicate that footnotes are being built (which stops +% spurious \cs{mark}s). +% \begin{macrocode} +\g@addto@macro{\gentry@footnotespergentry}{% +\renewcommand{\@footnotetext}[1]{% + \begingroup% + \def\in@out{}% flag that we're building footnotes + \edef\@tmp{\expandafter\csname footnotetoks\arabic{\@mpfn}\endcsname}% + \expandafter\@footnotetext@save@\expandafter{\@tmp}{% + % This fixes up the use of \cs{footnotemark} within footnotes: + #1}% + \endgroup% +}}% +% \end{macrocode} +% +% \DescribeMacro{\outputfootnotes}\marg{maxIdx} +% +% Command to output the footnotes, which are just in \cs{footnotetoks$N$} +% for now. +% +% An end user can call this at any point in the text to set out the footnotes. +% +% This macro takes one argument, being the maximum index of footnotes to output, inclusive. +% Footnotes after this index will be excluded. If not provided, the +% value provided by the counter \cs{@mfpn}, which in the default +% \LaTeX\ counter, will be used. +% \begin{macrocode} +\newcounter{fncounter}% +\newcommand{\outputfootnotes}[1]{% + \begingroup% + \def\in@out{}% flag that footnotes are outputting; suppresses marks + \setcounter{fncounter}{1} +% \end{macrocode} +% Called in vertical mode, and don't want to throw a page break. +% +% I'm not sure how \LaTeX\ renders the footnote rule exactly, so I'm just using my own. +% \begin{macrocode} + \def\footnote@rule{% +% \end{macrocode} +% The next line comess from the TUGboat suggestions, and protect against various user changes +% \begin{macrocode} + \leftskip=0pt\rightskip=0pt\interlinepenalty=1000% + \penalty-1000% + \vspace{1pt plus 2pt minus 0.5pt}% + \hspace{-0.5in}\rule{1.5in}{0.4pt}\\% + }% +% \end{macrocode} +% Invoke the output routine. This attempts to stop the output routine +% being invoked while we're adding the footnote rule, which could +% cause a blank footnote to appear. +% +% Instead, it means some footnotes could appear on the subsequent +% page. To guard against this, footnotes are set into a \cs{vbox} +% to prevent page breaking. +% +% The macro \cs{footnote@rule} is output before each footnote. The +% first time, it outputs a divider rule; subsequently, it just throws +% a new paragraph to keep footnotes on separate lines. +% +% The next line comess from the TUGboat suggestions, and protect against various user changes +% \begin{macrocode} + \outputpenalty=-\@MM\break% + \vbox{% + \whiledo{\not{\value{fncounter}>#1}}{% +% \end{macrocode} +% We are now looping over all possible entry numbers, in +% order. Some will already have been output, but we check them all +% anyway (it doesn't take much time). +% +% First, we check if the macro exists. If it does, it must contain the +% value of the token register. +% +% Next, we make sure that the token register has contents. +% +% \begin{macrocode} + \expandafter\ifcsname footnotetoks\arabic{fncounter}\endcsname% + \edef\tmp@@{\csname footnotetoks\arabic{fncounter}\endcsname}% +% \end{macrocode} +% This \cs{detokenize} black magic tests if a token reg is actually +% empty: +% +% https://tex.stackexchange.com/questions/263733/\\whats-the-best-practice-way-to-test-whether-parameter-is-empty +% \begin{macrocode} + \expandafter\if\expandafter\relax\expandafter% + \detokenize\expandafter{\the\tmp@@}\relax\else% + \footnote@rule\gdef\footnote@rule{\ifvmode\else\par\fi}% + \interlinepenalty\interfootnotelinepenalty% +% \end{macrocode} +% Actually output the footnotes. +% +% Having output the footnote, clear the token register (to save +% memory), then use \cs{let} to clear the definition of the +% macro. This ensures that we don't try to output the same footnote +% twice (at page end and entry end). +% \begin{macrocode} + \expandafter\the\tmp@@% + \global\expandafter\tmp@@={}% + \expandafter\let\tmp@@\@undefined% + \fi\fi% + \stepcounter{fncounter}% + }% + }% + \endgroup% +}% +% \end{macrocode} +% \LaTeX\ works by building up a little more than a page, then calling the output routine. +% The output routine then decides where to put the page end from the built-up material. +% If there is a footnote mark, it could come after the page end, so we can't rely on the +% fact that there's footnote register to determine if we should output footnotes or not. +% The edge case is: if the footnote mark is held back for the next page, the footnote text +% would appear on the footer of the page where being built when the mark was expanded, which +% is the page before the footnote. +% +% One fix for this is to have each footnote mark in the main body of +% the text output a \cs{mark} containing the footnote's counter value, +% and output the footnotes only to \cs{botmark}, which is the last +% \cs{mark} actually typeset on the page. +% +% (see https://www.tug.org/TUGboat/Articles/tb11-4/tb30salomon.pdf, +% page 598, to read around footnotes). +% +% \DescribeMacro{\outputfootnotes@endgentry} +% This macro is called at the end of each |gentry|. It switches into +% vertical mode (as ending |gentry| doesn't throw a \cs{break} or line +% end by itself), then we output all footnotes so far (by reading the +% footnote counter \cs{@mpfn}, as this is ``sequential''). +% \begin{macrocode} +\newcommand{\outputfootnotes@endgentry}{% + \if@gamebook@footnotes% + \nopagebreak\ifhmode\\\fi% get into vertical mode + \nopagebreak\outputfootnotes{\arabic{\@mpfn}}% + \fi% +}% +% \end{macrocode} +% \DescribeMacro{\noentryfoot} +% This method simply suppresses footnotes at the end of the entry +% within the current group. This forces footnotes within the current +% group to be printed at the bottom of the page. +% +% This is useful in the case where \LaTeX\ expands the footnote at the +% end of the entry, then decides to put the page split between the +% footnote mark and the text. +% \begin{macrocode} +\newcommand{\noentryfoot}{\def\outputfootnotes@endgentry{}} +% \end{macrocode} +% \DescribeMacro{\outputfootnotes@endpage} +% This is called by the output routine at the end of each page. If +% \cs{botmark} has a value, then we can output all footnotes up to +% that index. +% \begin{macrocode} +\g@addto@macro{\gentry@footnotespergentry}{ +\newcommand{\outputfootnotes@endpage}{% + \expandafter\if\expandafter\relax\expandafter% + \detokenize\expandafter{\botmark}\relax\else% + \outputfootnotes{\botmark}% + \fi% +}}% +% \end{macrocode} +% \Finale +\endinput diff --git a/Master/texmf-dist/source/latex/gamebooklib/gamebooklib.ins b/Master/texmf-dist/source/latex/gamebooklib/gamebooklib.ins new file mode 100644 index 00000000000..843a747bbdb --- /dev/null +++ b/Master/texmf-dist/source/latex/gamebooklib/gamebooklib.ins @@ -0,0 +1,53 @@ +%% +%% Copyright (C) 2021 by Robert J Lee +%% +%% This file 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 is in: +%% +%% 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. +% +\input docstrip.tex +\keepsilent +\usedir{tex/latex/gamebooklib} +\preamble + +This is a generated file. + +Copyright (C) 2021 by Robert J Lee + +This file 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 is in: + + 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. + +\endpreamble + +\generate{\file{gamebooklib.sty}{\from{gamebooklib.dtx}{package}}} + +\obeyspaces + +\Msg{****************************************************} +\Msg{* *} +\Msg{* To finish the installation you have to move the *} +\Msg{* following file into a directory searched by TeX: *} +\Msg{* *} +\Msg{* gamebooklib.sty *} +\Msg{* *} +\Msg{* To produce the documentation run the file *} +\Msg{* gamebooklib.dtx through LaTeX. *} +\Msg{* *} +\Msg{* Happy TeXing! *} +\Msg{* *} +\Msg{****************************************************} + +\endbatchfile diff --git a/Master/texmf-dist/tex/latex/gamebooklib/gamebooklib.sty b/Master/texmf-dist/tex/latex/gamebooklib/gamebooklib.sty new file mode 100644 index 00000000000..7822d932cdc --- /dev/null +++ b/Master/texmf-dist/tex/latex/gamebooklib/gamebooklib.sty @@ -0,0 +1,266 @@ +%% +%% This is file `gamebooklib.sty', +%% generated with the docstrip utility. +%% +%% The original source files were: +%% +%% gamebooklib.dtx (with options: `package') +%% +%% This is a generated file. +%% +%% Copyright (C) 2021 by Robert J Lee +%% +%% This file 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 is in: +%% +%% 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. +%% +\ProvidesPackage{gamebooklib}[2021/08/10 Gamebook by R Lee latex@rjlee.homelinux.org] +\NeedsTeXFormat{LaTeX2e}[1994/06/01] +\newcommand{\gamebook@info}[1]{}% +\DeclareOption{verbose}{% + \renewcommand{\gamebook@info}[1]{\PackageInfo{gamebooklib}{#1}}% + \gamebook@info{Gamebook Library package is logging}}% +\newcommand\gamebook@beforelast{} +\DeclareOption{endpage}{% + \renewcommand\gamebook@beforelast{\eject}% +}% +\def\if@gamebook@footnotes{\iffalse} +\DeclareOption{footnote}{% + \gdef\if@gamebook@footnotes{\iftrue}% + \gamebook@info{Gamebook Library footnotes per gamebook entry}% +}% +\def\if@gamebook@shuffle{\iftrue} +\DeclareOption{noshuffle}{% + \gdef\if@gamebook@shuffle{\iffalse}% + \gamebook@info{Gamebook Library entries output in order}% +}% +\DeclareOption*{% + \typeout{Current option: \CurrentOption}% + \PassOptionsToClass{\CurrentOption}{lcg}% +}% +\ProcessOptions\relax% +\RequirePackage{environ}% +\RequirePackage{macroswap}% +\RequirePackage{ifthen} +\RequirePackage{lcg}% +\RequirePackage[debrief]{silence}% +\newcounter{gentryctr}% +\setcounter{gentryctr}{0}% +\newcommand{\gentry}[1][]{\@gentry{#1}}% +\newenvironment{@gentry}[2]{% + \def\gentryidx{#1}% + \def\gentrycode{#2}% + \@@gentry% +}{\ignorespacesandallpars% +}% +\newcommand{\@@gentry}[1][]{% + \def\gentrytitle{#1}% + \stepcounter{gentryctr}% + \ifthenelse{\equal{\gentryidx}{}}{}{% + \expandafter\xdef\csname fixedat\arabic{gentryctr}% + \endcsname{\gentryidx}% + }% + \expandafter\global\expandafter\newtoks\expandafter{% + \csname paratok\arabic{gentryctr}\endcsname}{ }% + \Collect@Body\gentry@store% +} +\newcommand{\gentry@store}[1]{% + \edef\head{\noexpand\begingroup\noexpand\gentryheader% + {\arabic{gentryctr}}{\gentryidx}{\gentrycode}{\gentrytitle}% + \noexpand\label{gentry:\gentrycode}% + }% + \global\expandafter\csname paratok\arabic{gentryctr}\endcsname=% + \expandafter{\head #1% + \outputfootnotes@endgentry% + \gentryfooter\endgroup}% +}% +\newcommand{\gentry@footnotespergentry}{} +\newcommand{\thegentries}{% +\begingroup% + \if@gamebook@footnotes\gentry@footnotespergentry\fi% + \xdef\gentrycount{\arabic{gentryctr}}% + \WarningFilter{lcg}{Using an already existing counter rand}% +\newtoks\gentry@oldoutput{}% +\edef\mytmp@{\noexpand\gentry@oldoutput={\the\output}}\mytmp@% +\if@gamebook@footnotes\output={% + \ifnum\outputpenalty<-\@M\else% + \edef\tmp@{footnotetoks\botmark} + \expandafter\ifcsname footnotetoks\botmark\endcsname% + \global\setbox255=\vbox to \vsize{% + \unvbox255\vfill\outputfootnotes@endpage}% + \fi% + \fi% + \the\gentry@oldoutput +}\fi% + \xdef\gentrycount{\arabic{gentryctr}}% + \setcounter{gentryctr}{0}% + \ifthenelse{\gentrycount<3}{}{%SHUFFLE START + \whiledo{\not{\value{gentryctr}>\gentrycount}}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\xdef\csname paraIdx\myidx\endcsname{\myidx}% + \stepcounter{gentryctr}% + }% + \if@gamebook@shuffle + \setcounter{gentryctr}{2}% + \setcounter{rand}{\gentrycount}% + \addtocounter{rand}{-1}\edef% + \stoppoint{\arabic{rand}}% + \chgrand[last=\stoppoint]% + \whiledo{\value{gentryctr}<\stoppoint}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\ifcsname fixedat\myidx\endcsname% + \gamebook@info{Not shuffling \myidx; fixed pos}% + \stepcounter{gentryctr}% + \else% + \gamebook@info{Shuffling \myidx}% + \stepcounter{gentryctr}% + \edef\nextidx{\arabic{gentryctr}}% + \expandafter\ifcsname fixedto\arabic{rand}\endcsname\else% + \chgrand[first=\nextidx]% + \rand% + \gamebook@info{Shuffling \myidx to \arabic{rand}}% + \macroswap{paraIdx\myidx}{paraIdx\arabic{rand}}% + \fi% + \fi% + }% + \setcounter{gentryctr}{2}% + \whiledo{\value{gentryctr}<\stoppoint}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\ifcsname fixedat\myidx\endcsname% + \expandafter\edef\expandafter\mydest\expandafter% + {\expandafter\csname fixedat\myidx\endcsname}% + \gamebook@info{MOVING FIXED GAMEBOOK ENTRY INTO PLACE: \myidx -> \mydest}% + \macroswap{paraIdx\myidx}% + {paraIdx\expandafter\csname fixedat\myidx\endcsname}% + \fi% + \stepcounter{gentryctr}% + }}\fi%SHUFFLE END + \gamebook@info{Shuffled! Gentry order:}% + \setcounter{gentryctr}{1}% + \whiledo{\not{\value{gentryctr}>\gentrycount}}{% + \edef\myidx{\arabic{gentryctr}}% + \gamebook@info{\myidx -> \csname paraIdx\myidx\endcsname}% + \stepcounter{gentryctr}% + }% + \setcounter{gentryctr}{0}% + \gamebook@info{Outputting \gentrycount\ gamebook entries}% + \whiledo{\value{gentryctr}<\gentrycount}{% + \refstepcounter{gentryctr}% + \ifthenelse{\value{gentryctr}=\gentrycount}{% + \gamebook@beforelast% + }{}% + \edef\myidx{\arabic{gentryctr}}% + \xdef\youridx{\csname paraIdx\myidx\endcsname}% + \gamebook@info{Output gentry \myidx\ of \gentrycount,% + original idx \youridx}% + \the\csname paratok\youridx\endcsname% + }% + \gamebook@info{All gamebook entries added to main vertical list}% + \setcounter{gentryctr}{1}% + \whiledo{\not{\value{gentryctr}>\gentrycount}}{% + \edef\myidx{\arabic{gentryctr}}% + \expandafter\global\expandafter\let% + \csname paratok\myidx\endcsname\@undefined% + \expandafter\ifcsname fixedat\myidx\endcsname% + \expandafter\global\expandafter\let% + \csname fixedat\myidx\endcsname\@undefined% + \fi% + \stepcounter{gentryctr}% + }% + \setcounter{gentryctr}{0}% + \eject + \endgroup% +}% +\newcommand{\gentryheader}[4]{% + \noindent\textbf{\Huge\arabic{gentryctr}\large\ #4}% + \nopagebreak% + \vspace{0.3em}% + \nopagebreak% + \par% + \marginpar{#3}% +}% +\newcommand{\gentryfooter}{% + \par\vspace{2em}\centerline{---}\vspace{2em plus 1in}\par% +}% +\def\ignorespacesandallpars@{% + \@ifnextchar\par% + {\expandafter\ignorespacesandallpars\@gobble}% + {}% +}% +\g@addto@macro{\@makefnmark}{% + \ifcsname in@out\endcsname\else% + \mark{\arabic{\@mpfn}}% + \fi% +}% +\g@addto@macro{\gentry@footnotespergentry}{% +\newcommand{\@footnotetext@save}[3]{% + \global\newtoks#2{}% + \global#2={\noindent\@footnotemark{}#3}% +}}% +\g@addto@macro{\gentry@footnotespergentry}{% +\newcommand{\@footnotetext@save@}[2]{% + \edef\@tmp@{\expandafter\arabic{\@mpfn}}% + \expandafter\@footnotetext@save\expandafter{\@tmp@}{#1}{#2}% +}}% +\g@addto@macro{\gentry@footnotespergentry}{% +\renewcommand{\@footnotetext}[1]{% + \begingroup% + \def\in@out{}% flag that we're building footnotes + \edef\@tmp{\expandafter\csname footnotetoks\arabic{\@mpfn}\endcsname}% + \expandafter\@footnotetext@save@\expandafter{\@tmp}{% + % This fixes up the use of \cs{footnotemark} within footnotes: + #1}% + \endgroup% +}}% +\newcounter{fncounter}% +\newcommand{\outputfootnotes}[1]{% + \begingroup% + \def\in@out{}% flag that footnotes are outputting; suppresses marks + \setcounter{fncounter}{1} + \def\footnote@rule{% + \leftskip=0pt\rightskip=0pt\interlinepenalty=1000% + \penalty-1000% + \vspace{1pt plus 2pt minus 0.5pt}% + \hspace{-0.5in}\rule{1.5in}{0.4pt}\\% + }% + \outputpenalty=-\@MM\break% + \vbox{% + \whiledo{\not{\value{fncounter}>#1}}{% + \expandafter\ifcsname footnotetoks\arabic{fncounter}\endcsname% + \edef\tmp@@{\csname footnotetoks\arabic{fncounter}\endcsname}% + \expandafter\if\expandafter\relax\expandafter% + \detokenize\expandafter{\the\tmp@@}\relax\else% + \footnote@rule\gdef\footnote@rule{\ifvmode\else\par\fi}% + \interlinepenalty\interfootnotelinepenalty% + \expandafter\the\tmp@@% + \global\expandafter\tmp@@={}% + \expandafter\let\tmp@@\@undefined% + \fi\fi% + \stepcounter{fncounter}% + }% + }% + \endgroup% +}% +\newcommand{\outputfootnotes@endgentry}{% + \if@gamebook@footnotes% + \nopagebreak\ifhmode\\\fi% get into vertical mode + \nopagebreak\outputfootnotes{\arabic{\@mpfn}}% + \fi% +}% +\newcommand{\noentryfoot}{\def\outputfootnotes@endgentry{}} +\g@addto@macro{\gentry@footnotespergentry}{ +\newcommand{\outputfootnotes@endpage}{% + \expandafter\if\expandafter\relax\expandafter% + \detokenize\expandafter{\botmark}\relax\else% + \outputfootnotes{\botmark}% + \fi% +}}% +\endinput +%% +%% End of file `gamebooklib.sty'. diff --git a/Master/tlpkg/bin/tlpkg-ctan-check b/Master/tlpkg/bin/tlpkg-ctan-check index d1ae5dafe9b..9ae29efe24c 100755 --- a/Master/tlpkg/bin/tlpkg-ctan-check +++ b/Master/tlpkg/bin/tlpkg-ctan-check @@ -344,7 +344,8 @@ my @TLP_working = qw( ftc-notebook ftcap ftnxtra fullblck fullminipage fullwidth functan fundus-calligra fundus-cyr fundus-sueterlin fvextra fwlw - g-brief gaceta galois gamebook gammas garamond-libre garamond-math garuda-c90 + g-brief gaceta galois gamebook gamebooklib gammas + garamond-libre garamond-math garuda-c90 garrigues gastex gates gatherenum gauss gb4e gbt7714 gcard gchords gcite gckanbun gender geschichtsfrkl genealogy genealogytree gene-logic diff --git a/Master/tlpkg/tlpsrc/collection-games.tlpsrc b/Master/tlpkg/tlpsrc/collection-games.tlpsrc index 91c1aaa5d92..a25facc804f 100644 --- a/Master/tlpkg/tlpsrc/collection-games.tlpsrc +++ b/Master/tlpkg/tlpsrc/collection-games.tlpsrc @@ -13,6 +13,7 @@ depend crossword depend crosswrd depend egameps depend gamebook +depend gamebooklib depend go depend hanoi depend havannah diff --git a/Master/tlpkg/tlpsrc/gamebooklib.tlpsrc b/Master/tlpkg/tlpsrc/gamebooklib.tlpsrc new file mode 100644 index 00000000000..e69de29bb2d -- cgit v1.2.3