summaryrefslogtreecommitdiff
path: root/macros/latex/contrib/gamebooklib/gamebooklib.dtx
blob: eed4fc64c6a7da0112cbff30b966b66eb71a63e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
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
% <package>\NeedsTeXFormat{LaTeX2e}[2005/12/01]
% <package>\ProvidesPackage{gamebooklib}
% <package>   [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}
%</driver>
% \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