% \iffalse meta-comment %<*internal> \iffalse % %<*readme> cardgame -- Typeset custom playing cards for printing =================================================== The `cardgame` package allows you to define `cards`, which have various attributes like image, text, and a bunch of layout features. You can then include those cards as images or, more importantly, generate a document containing all of the cards you defined laid out optimally for printing and cutting. You can optionally create card lists for your game's manual, and you can create multiple copies of cards and automatically print the correct number of back sides. Installation ------------ The package is supplied in `.dtx` format and as a pre-extracted `.zip` file, `cardgame.zip`. The latter is most convenient for most users: simply unzip this in your local `texmf` directory. If you want to unpack the `.dtx` yourself, running `tex cardgame.dtx` will extract the package whereas `latex cardgame.dtx` or `pdflatex cardgame.dtx` will extract it and also typeset the documentation. The package requires LaTeX3 support as provided in the `l3kernel` and `l3packages` bundles. Both of these are available on [CTAN](http://www.ctan.org/) as ready-to-install `.zip` files. Suitable versions are available in MiKTeX 2.9 and TeX Live 2012 (updating the relevant packages online may be necessary). LaTeX3, and so `cardgame`, requires the e-TeX extensions: these are available on all modern TeX systems. Typesetting the documentation requires a number of packages in addition to those needed to use the package. To compile the documentation without error, you will need the following packages: - `listings` - `float` % %<*internal> \fi \def\nameofplainTeX{plain} \ifx\fmtname\nameofplainTeX\else \expandafter\begingroup \fi % %<*install> \input l3docstrip.tex \keepsilent \askforoverwritefalse \preamble --------------------------------------------------------------- cardgame --- Typeset custom board game cards Maintained by Adrian Rettich E-mail: latex@homeworld.space Released under the LaTeX Project Public License v1.3c or later See http://www.latex-project.org/lppl.txt --------------------------------------------------------------- \endpreamble \postamble Copyright (C) 2026 by Adrian Rettich It may be distributed and/or modified under the conditions of the LaTeX Project Public License (LPPL), either version 1.3c of this license or (at your option) any later version. The latest version of this license is in the file: http://www.latex-project.org/lppl.txt This work is "maintained" (as per LPPL maintenance status) by Adrian Rettich. This work consists of the file cardgame.dtx and the derived files cardgame.ins, cardgame.pdf and cardgame.sty. \endpostamble \usedir{tex/latex/cardgame} \generate{ \file{\jobname.sty}{\from{\jobname.dtx}{package}} } % % \endbatchfile %<*internal> \usedir{source/latex/cardgame} \generate{ \file{\jobname.ins}{\from{\jobname.dtx}{install}} } \nopreamble\nopostamble \usedir{doc/latex/cardgame} \generate{ \file{README.txt}{\from{\jobname.dtx}{readme}} } \ifx\fmtname\nameofplainTeX \expandafter\endbatchfile \else \expandafter\endgroup \fi % %<*driver|package> \RequirePackage{xparse} % %<*driver> \documentclass[full]{l3doc} \usepackage[playtest]{cardgame} \usepackage[final]{listings} \usepackage{float} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \makeatletter % % ^^A For creating examples with nice highlighting of code, and so % ^^A on; based on the system used in the listings source (lstsample). % \lst@RequireAspects{writefile} % \newsavebox{\LaTeXdemo@box} % \lstnewenvironment{LaTeXdemo}[1][code and example]{^^A % \global\let\lst@intname\@empty % \expandafter\let\expandafter\LaTeXdemo@end % \csname LaTeXdemo@#1@end\endcsname % \@nameuse{LaTeXdemo@#1}^^A % }{^^A % \LaTeXdemo@end % } % \newcommand*\LaTeXdemo@new[3]{^^A % \expandafter\newcommand\expandafter*\expandafter % {\csname LaTeXdemo@#1\endcsname}{#2}^^A % \expandafter\newcommand\expandafter*\expandafter % {\csname LaTeXdemo@#1@end\endcsname}{#3}^^A % } % \newcommand*\LaTeXdemo@common{^^A % \setkeys{lst}{ % basicstyle = \small\ttfamily, % basewidth = 0.51em, % gobble = 4, % keywordstyle = \color{blue}, % language = [LaTeX]{TeX}, % moretexcs = { % bibnote , % bibnotemark , % bibnotesetup , % bibnotetext , % printbibnotes % } % }^^A % } % \newcommand*\LaTeXdemo@input{^^A % \MakePercentComment % \catcode`\^^M=10\relax % \small % \begingroup % \setkeys{lst}{ % SelectCharTable=\lst@ReplaceInput{\^\^I}{\lst@ProcessTabulator} % }^^A % \leavevmode % \input{\jobname.tmp}^^A % \endgroup % \MakePercentIgnore % } % \LaTeXdemo@new{code and example}{^^A % \setbox\LaTeXdemo@box=\hbox\bgroup % \lst@BeginAlsoWriteFile{\jobname.tmp}^^A % \LaTeXdemo@common % }{^^A % \lst@EndWriteFile % \egroup % \begin{center} % \ifdim\wd\LaTeXdemo@box>0.48\linewidth\relax % \hbox to\linewidth{\box\LaTeXdemo@box\hss}^^A % \begin{minipage}{\linewidth} % \LaTeXdemo@input % \end{minipage} % \else % \begin{minipage}{0.48\linewidth} % \LaTeXdemo@input % \end{minipage} % \hfill % \begin{minipage}{0.48\linewidth} % \hbox to\linewidth{\box\LaTeXdemo@box\hss}^^A % \end{minipage} % \fi % \end{center} % } % \LaTeXdemo@new{code only}{^^A % \LaTeXdemo@common % }{^^A % } % % \providecommand*\opt[1]{\texttt{#1}} % % \makeatother % \GetFileInfo{\jobname.sty} % % \changes{v1.0}{2026/04/19}{Initial release} % \title{^^A % \pkg{cardgame} --- Typeset custom game cards^^A % \thanks{^^A % This file describes version \fileversion, last revised % \filedate.^^A % }^^A % } % \author{^^A % Adrian Rettich\thanks{E-mail: latex@homeworld.space}^^A % } % \date{Released \filedate} % % \maketitle % % \begin{abstract} % The \pkg{cardgame} package allows you to define `cards', which % have various attributes like image, text, and a bunch of % layout features. You can then include those cards as % images or, more importantly, generate a document containing % all of the cards you defined laid out optimally for printing % and cutting. You can optionally create card lists for % your game's manual, and you can create multiple copies % of cards and automatically print the correct number % of back sides. % \end{abstract} % % \tableofcontents % % \begin{documentation} % % \section{Introduction} % % The main use of this package is as follows: % You have created a board or card game. You want to play it. % You do not want to use handwritten cards anymore. % Using the \pkg{cardgame} package, you can input your cards % in a reasonably intuitive way and let \LaTeX{} handle the % layout for you. The package produces a PDF file that contains % all of your cards, ready to be cut out. The size is such that % the cards fit into standard gaming sleeves; the easiest way to % use them without printing on expensive card stock is to % put cheap Magic: the Gathering cards or any other pre-existing % cards into sleeves, then slide your own cards on top of them. % You may use sleeves with transparent back in order to have your % own card back design; in this case the generated PDF file % contains the correct amount of card backs for you to cut out. % \section{Installation} % % The package is supplied in \file{dtx} format and as a pre-extracted % zip file, \file{\jobname.zip}. The latter is most convenient for % most users: simply unzip this in your local texmf directory and % run \texttt{texhash} to update the database of file locations. If % you want to unpack the \file{dtx} yourself, running % \texttt{tex \jobname.dtx} will extract the package whereas % \texttt{latex \jobname.dtx} or \texttt{pdflatex \jobname.dtx} % will extract it and also typeset the documentation. % % The package requires \LaTeX3 support as provided in the % \pkg{l3kernel} and \pkg{l3packages} bundles. Both of these are available % on \href{http://www.ctan.org}{\textsc{ctan}} as ready-to-install % zip files. Suitable versions are available in MiKTeX 2.9 and % TeX Live 2011 (updating the relevant packages online may be % necessary). \LaTeX3, and so \pkg{cardgame}, requires the e-\TeX{} % extensions: these are available on all modern \TeX{} systems. % % Typesetting the documentation requires a number of packages in % addition to those needed to use the package. To % compile the documentation without error, you will need the following packages: % \begin{itemize} % \item \pkg{listings} % \item \pkg{float} % \end{itemize} % % \section{Quick Start} % % If you just want to get to making basic cards and do not need % any advanced features, this section provides basic code you % can copy and paste into your own document. % \begin{LaTeXdemo}[code only] % \usepackage[]{cardgame} % \end{LaTeXdemo} % You can generate a simple playing card as follows. % For the artwork, a PNG with transparency is recommended, but you can % of course use any image file. % Our examples use the excellent CTAN lion drawn by Duane Bibby. % A card has two mandatory arguments: a name and a text. % All other options are specified as key-value pairs in the optional argument. % \begin{LaTeXdemo} % \Card[ % art=lion.png, % south east content=5, % tags=animal % ] {Lion} { % You get a billion points. The card costs 5 of some game resource % and is of type `animal'.\\ % Line breaks are supported, but a blank line such as this\\\ \\ % must contain an explicit space. % } % \begin{figure}[H] % \ShowCard % \end{figure} % \end{LaTeXdemo} % Line breaks are supported via explicit backslashes. Do not attempt to have a blank line inside your text argument; it will break because everything on a card is happening inside a TikZ picture. Always use the backslashes. % % You can customise many of the features of a card; they are documented in the later sections of this manual. % Here is another card showcasing some of these features. % Note how you can pass name and text as options if you leave the mandatory arguments empty. % \begin{LaTeXdemo} % \Card[ % name=Landscape Lion, % art=lion.png, % text={ % This card is in landscape mode.\\ % Note how the tags are sorted automatically to have % the same order on all of your cards.}, % orientation=landscape, % north east content=5, % north east pre=cost:, % tags={predator,animal}, % background colour=Lavender!20, % title colour=BlueViolet!50, % title gradient=BrickRed, % tags colour=BlueViolet!50, % tags gradient=DarkOrchid!50, % frame colour=Blue, % border colour=darkgray % ]{}{} % \begin{figure}[H] % \rotatebox{270}{ % \ShowCard % } % \end{figure} % \end{LaTeXdemo} % The keys \opt{background color} and so forth are provided as an alternative to the default spelling. % % Note how both cards are \emph{defined} before they are ever put inside a figure; the command \cs{ShowCard} is a convenience function that immediately typesets the most recently defined card. % Mostly, if you are designing a game, you just want to print all the cards on as few sheets of cardstock as possible. % To this end, you do not use the \cs{ShowCard} macro. % Rather, first define all of your cards. % If you want to have more than one copy of a card, simply include \opt{copies=N} for the desired number \opt{N} in the \cs{Card} call. % % Once you have defined all of your cards, call \cs{PrintCards} to generate a list of \emph{all} cards you have created. % Note that you must compile your document twice for this to work. % % You can easily keep your cards sorted in different files by simply having files filled with \cs{Card} commands and using \cs{input} to pull them into one PDF file. % % By default, cards are sorted by type, subtype, and then title, and one back side is created for each card. % You can suppress sorting by passing \opt{sort=false} to \cs{Cardlist} as an optional argument, and you can suppress creation of back sides by passing \opt{backs=false}. % % You can avoid copying options that should be the same for each card by using the \cs{DeclareCardType} macro. % \begin{LaTeXdemo}[code only] % \DeclareCardType{GreenCard}{ % title colour=green % } % \end{LaTeXdemo} % This provides you with a new command \cs{GreenCard}, which acts exactly like \cs{Card} except that it has a green title box by default. % If \cs{DeclareCardType} receives the name of an existing card type as an optional argument, it inherits all options from that card type. % Each option can still be overwritten in individual calls. % % If you want to include some of your cards in your game manual, give them a \opt{uid}. % \begin{LaTeXdemo} % \Card[ % art=lion.png, % south east content=5, % tags=animal, % uid=truelion % ]{Lion}{ % You get a billion points. % } % \Card[ % art=lion.png, % ]{Tiger}{ % You lose all your points. % } % \end{LaTeXdemo} % Now, even though the last card we defined is ``tiger'', we can get our lion back by passing its \opt{uid} to the \cs{ShowCard} command. % \begin{LaTeXdemo} % \begin{figure}[H] % \ShowCard[truelion] % \end{figure} % \end{LaTeXdemo} % \opt{uid} stands for ``unique identifier'', and as such, the package will throw an error if you try to create a second card with the same \opt{uid}, whereas multiple cards with the same \opt{name} are allowed. % % This concludes the introductory part of this documentation. % Read on if you want to know about the full set of features. % % \section{The Parts of a Card} % % In order to communicate information about typesetting of game cards, it is helpful to establish some shared terminology. % % All cards generated by \pkg{cardgame} have the following layout. % \Card[ % north west content=1, % north east content=2, % south west content=3, % south east content=4, % art=lion.png, % separator=true, % tags={tag list}, % version=1.0, % type=type, % subtype=subtype, % more text=More text % ]{Name}{This is the main text.} % \begin{figure}[H] % \ShowCard % \end{figure} % The parts of this card are as follows. % \begin{description} % \item[Border] The black outline of the card, showing you where to cut. It should probably not carry information. % \item[Title Box] The coloured box at the top, containing the numbers 1 and 2 and the word ``name''. % \item[Tag Box] The bottom box, containing the numbers 3 and 4 and the words ``tag list''. % \item[Frame] The vertical orange bars on the left and right. Do not confuse the frame with the border; the frame's colour usually changes with the card colour, while the border is always black. % \item[Main Area] The central area containing the artwork and main text. In this card, it is split in half by a horizontal divider, called the card's separator. In landscape mode, the split is vertical instead. % \item[Artwork] The area above the divider line, usually occupied by a picture. % \item[Text] The area below the divider line. % \item[Name] The card's name is displayed in the centre of the title box. % \item[Tags] A card can have any number of ``tags'' passed as a comma-separated list. How you use them is up to you; they could for example be subtypes. The list is sorted alphabetically and by default is displayed in the tag box, though you can change this. % \item[Corners] The card has four corners. Each of the corners can display content. They are called north west (1), north east (2), south west (3), and south east (4). % \item[Playtesting Information] If you look carefully, you can see the string ``type -- subtype -- v1.0'' displayed in the eastern frame. This optional information tells you the type (and subtype) of card and its newest revision, useful when you are playtesting a game and updating cards as you go. The version information lets you check quickly whether the printed version is currently up to date. % This text does not appear by default; set the \opt{playtest} package option to enable it. % \end{description} % Apart from those visible elements, each card has additional attributes that may or may not be visible. % They are explained in detail later, but the most important are the following ones. % \begin{description} % \item[Type] Each card has a ``type''. This could affect which of various piles in a game the card is a part of. It is not usually displayed on the card itself since it is easier to distinguish cards by colours or symbols. When generating a list of cards, they are sorted by type first. % \item[Subtype] Cards can also have a subtype. If you want cards with multiple subtypes, then use the tag list instead. An option lets you control whether to display the subtype in the tag box. % \item[Cost] Cards can carry costs. These can, but need not, be numbers, and they can be displayed in one or more corners of the card. % \item[Level] Cards can have a level. This is a non-negative integer and can be displayed in one or more corners. % \item[Mark] This can contain any text you like. It can be displayed in one or more corners. % \item[Secondary Mark] Same principle as mark. % \end{description} % Everything we just talked about makes up the \emph{front side} or \emph{face} of the card. A card also has a \emph{back}, which has the same parts, except that there is no main text and the artwork is centred on the card. % \section{Defining Cards} % \subsection{Options} % All card-making macros accept all options listed in this section. % Options are given as a comma-separated list of key-value pairs in the form \opt{=} or \opt{=\{\}}. % Enclosing the value in curly braces is necessary if it contains a comma or an equals sign. % % For options that define boolean values (those that take either \opt{true} or \opt{false} as their value), the equals sign and value can be omitted. Passing \opt{} is the same as passing \opt{=true}. % \subsubsection{Options Defining Card Text} % \DescribeOption{name} % The title of the card. If this is too long, it may be hard to fit into the title box. See \opt{modify name} and \opt{title box content} for when that happens. % The name can also be passed as the first mandatory argument of the \cs{Card} macro. % % \DescribeOption{type} % The ``type'' of card. In reality, this can be any text. It is the primary key for sorting cards. You might want to display this on the back of the card. % % \DescribeOption{subtype} % The secondary key when sorting. Again, this can be any text. If your game is very complex, you might still want this on the back of the card in order to sort your various draw piles. % % \DescribeOption{text} % The main text of the card. Can also be passed as the second mandatory argument of the \cs{Card} macro. % Text is wrapped automatically. If you want to force a line break, use \texttt{\textbackslash\textbackslash}. Blank lines in the input are not allowed. If you want to force a blank line in your text, either use the \opt{more text} option below or put a single explicit space on the blank line with a construction like \mbox{\texttt{\textbackslash\textbackslash\textbackslash\ \textbackslash\textbackslash}}. % % \DescribeOption{more text} % Text passed to this option is typeset in the main text area, but at the bottom. The effect is as if you had a \cs{vfill} between \opt{text} and \opt{more text}. % Of course, it is your responsibility to ensure that both texts fit on the card; if they are too long, \pkg{cardgame} will print them overlapping. % % \DescribeOption{art} % This option takes the name of a file that \LaTeX{} can import as a graphic. % A PNG file with transparency works well if you want to still see the background colour of the card. % Otherwise, your image should bring its own frame \emph{or} you will have to make sure that it has exactly the right dimensions in order to not look strange. % % In portrait format, images are scaled (keeping the aspect ratio) to a maximum size of 56mm times 31mm. % In landscape format, the maximum size is 40mm times 38mm. % % \DescribeOption{back art} % This option takes the name of a file that \LaTeX{} can import as a graphic. % It is set centred on the back of the card. % It is scaled just like the face art. % % \DescribeOption{level} % This option takes a non-negative integer, which can be displayed in various places. % If it exists, then it becomes the secondary key in sorting cards and the \opt{subtype} is demoted to tertiary key. % % \DescribeOption{cost} % This option is meant to define what game resources a card costs. It can be an integer, but it can also be arbitrary text. It can be displayed in various places. % % \DescribeOption{mark} % This option takes arbitrary text and sets the \opt{mark} attribute of the card. It has no inherent meaning, but it can be displayed in various places. % % \DescribeOption{secondary mark} % This option takes arbitrary text and sets the \opt{secondary mark} attribute of the card. It has no inherent meaning, but it can be displayed in various places. % % \DescribeOption{tags} % This option takes a comma-separated list of tags, all of which are added to the card. By default, tags are sorted alphabetically and displayed in the tag box. % % When specifying more than one tag, remember to enclose your list in curly braces. % If you really want to, you can specify tags that include commas by nesting braces; for example, \opt{tags=\{c,\{b,a\}\}} would be sorted as \texttt{b,a,c} because \texttt{b,a} is a single tag. % \subsubsection{Options Modifying the Display of Text} % \DescribeOption{modify name} % Content of this field is executed right before the name of the card is typeset on the card. This allows you to, for example, change the font size for very long names without modifying the name itself, so it is still read correctly by macros like \cs{ListCards}. % By default, the card name uses \cs{Large}\cs{bf}. Use \opt{modify name=} (an empty value) to produce a non-bold normalsize card title. % % \emph{Nota bene}, because this argument is passed to \texttt{TiKZ}, you must use the deprecated series of commands \cs{bf}, \cs{it}, and so forth to change the font face. The modern macros \cs{textbf}, \cs{textit}, and so forth will lead to errors.\footnote{If someone knows how to do this better, feel free to submit a pull request or just send your solution to \texttt{latex@homeworld.space}. Maybe using \texttt{TikZ}'s \opt{font} feature is not the correct approach in the first place?} % % \DescribeOption{modify text} % The same as \opt{modify name}, except it modifies the main text of the card and is empty by default. % % \DescribeOption{modify more text} % The same as \opt{modify text}, but for the \opt{more text} field at the bottom of the text box. % % \DescribeOption{modify } % \DescribeOption{modify pre} % \DescribeOption{modify post} % In each of these options, \opt{} is a corner specification as described in section \ref{sec:corners}. % The options modify the main text respectively the \opt{pre} or \opt{post} text of that corner just as \opt{modify text} modifies the main text. % The default value of \opt{modify } is empty, while the other two use \cs{tiny} by default. % \subsubsection{Options Controlling the Placement of Text}\label{sec:corners} % The options in this section might be difficult to understand without pictures, so there is an example using most of them at the end of the section. % % Apart from the main parts of the card (name, text, and more text) there are six places available for displaying information. % These are the centre of the title box, the centre of the tag box, and the four corners. Corners are specified by compass directions: the four corners are \opt{north west}, \opt{north east}, \opt{south west}, and \opt{south east}. Every occurrence of the string \opt{} in this section can be replaced by any of those four specifications. % In landscape format, the cardinal directions are oriented such that the card name is north, not such that the top of the page is north. % % In each of these places, you can of course put some text manually. However, you also have the option to link certain information about the card to them, as follows. % % \DescribeOption{title box}\DescribeOption{tag box}\DescribeOption{} % Set these options to link card attributes to the specified location. Possible values are \opt{name}, \opt{type}, \opt{subtype}, \opt{tags}, \opt{level}, \opt{cost}, \opt{mark}, and \opt{secondmark}. For example, the option \opt{north west=mark} means that the value of the \opt{mark} option is set in the upper right corner. This is useful when you define your own card types (see section \ref{sec:cardtypes}). You can set any of these options to be empty in order to not display anything in that position. % % The default value of \opt{title box} is \opt{name}. % The default value of \opt{tag box} is \opt{tags}. % The corners are empty by default. % % Even if a link is specified, you can override it on a case by case basis by setting the appropriate \opt{content} option, described next. % % \DescribeOption{back title box}\DescribeOption{back tag box}\DescribeOption{back } % These options work just like those just described, but place text on the back of the card. % By default, the \opt{back title box} contains the \opt{type} of the card, while the others are empty. % % \DescribeOption{title box content} % \DescribeOption{tag box content} % \DescribeOption{ content} % These options override the automatic content of the specified location. If you want to put something in the corner of just one card, it is easier to specify this. % % You can also use this to shorten the display name of a card while retaining its full name for card lists. % % \DescribeOption{ pre} % The value of this option is set before the content of the corner, on a separate line. It has a smaller font size by default, but this can be overridden via the \opt{modify pre} option. % % \DescribeOption{ post} % Same as the previous option, but set after the corner content. % % Here is an example using the options just described. % Note how the \opt{mark} is used to place the same content in multiple places without duplicating it in the code. % \begin{LaTeXdemo} % \Card[ % type=animal, % mark=M, % tag box=type, % north west=mark, % north east=mark, % modify north east=\Huge, % north west pre=mark:, % south west content=SW, % south west post=post, % south east=mark, % modify south west post=\it\scriptsize, % ] {Text Placement} {} % \begin{figure}[H] % \ShowCard % \end{figure} % \end{LaTeXdemo} % \subsubsection{Colours} % \pkg{cardgame} depends on the \pkg{xcolor} package and automatically loads it with the \opt{dvipsnames} option. % % For every option containing the word \opt{colour}, a US English equivalent (\opt{color}) is available. % % \DescribeOption{link colours} % Set this Boolean option to \opt{true} in order to link the colours on the back of the card to the colours on the face. By default, they are controlled separately. % If this is \opt{true}, then setting any of the subsequent options from this section for the face of the card automatically sets the same option for the back. However, each option can still be overridden by explicitly setting it for the back side. % % All other options described in this section take as their value a colour name. % This name must be fit to go into a \cs{colorlet} macro (see the \pkg{xcolor} documentation for details). As such, \opt{black}, \opt{ForestGreen}, and \opt{Dandelion!50} are valid, but if you want to define your own colour by RGB values, you must use \pkg{xcolor}'s \cs{definecolor} first. % % Some parts of the card allow gradients. If you specify both a colour and a gradient for a part of the card, then that part will be coloured with a gradient such that the \opt{colour} option is further inside and the \opt{gradient} option is closer to the border of the card. There is an example card at the end of this section. % % \DescribeOption{background colour} % \DescribeOption{back background colour} % This is the background of the main area of the card. Choose a light colour to ensure your text is legible. The default for this is \opt{Dandelion!20}. % % \DescribeOption{title colour}\DescribeOption{title gradient} % \DescribeOption{back title colour}\DescribeOption{back title gradient} % Colour for the title box. The default for both of these is \opt{Dandelion!60}. % % \vspace{3\baselineskip} % \DescribeOption{tags colour}\DescribeOption{tags gradient} % \DescribeOption{back tags colour}\DescribeOption{back tags gradient} % Colour for the tag box. If empty, these default to the corresponding \opt{title} value. % % \vspace{3\baselineskip} % \DescribeOption{frame colour} % \DescribeOption{back frame colour} % The frame's colour. The default value for this is \opt{Bittersweet!60}. % % \vspace{\baselineskip} % \DescribeOption{separator colour} % \DescribeOption{back separator colour} % If you set the \opt{separator} option, this controls the colour of the separator bar. If empty, it defaults to the value of \opt{frame colour}. % % \DescribeOption{border colour} % \DescribeOption{back border colour} % The colour for the outer border of the card. The default is \opt{black}. % % \vspace{\baselineskip} % Here is a card using some of the options described above. % \begin{LaTeXdemo} % \Card[ % background colour=ForestGreen!10, % frame colour=PineGreen, % title colour=ForestGreen, % title gradient=Goldenrod, % tags colour=ForestGreen, % tags gradient=MidnightBlue, % border colour=Gray!30, % separator % ] {Colours} {} % \begin{figure}[H] % \ShowCard % \end{figure} % \end{LaTeXdemo} % \subsubsection{Options Controlling the Overall Layout} % \DescribeOption{orientation} % This must be either \opt{portrait} or \opt{landscape}. The default is \opt{portrait}. % % \DescribeOption{separator} % \DescribeOption{back separator} % These are boolean options. They make the separator in the middle of the main area appear on the face respectively the back of the card. % % \DescribeOption{rounded corners} % This is a boolean option that is \opt{true} by default. Setting \opt{rounded corners=false} disables the rounded corners on the outside of the card border. It does \emph{not} affect any of the corners of boxes on the card, only the part where you cut it. This can prevent ugly white corners around the printed cards if you want to cut your cards with a machine but do not have a corner rounder available. % % \DescribeOption{back} % This is a boolean option and is \opt{false} by default. If set, \emph{only} the back of the card is generated. It is unlikely that you will ever want to set this option; the \cs{PrintCards} macro can automatically generate the back sides of all cards. % \subsubsection{Invisible Attributes} % The options described in this section set various attributes of the card which do not usually appear on the card itself, but can be used for control flow, debugging, and by the \cs{ListCards} macro. % % \DescribeOption{uid} % Set this option to a unique string to later be able to fetch this specific card from your card list. For example, you may have a document containing dozens or hundreds of cards, but in the manual for your game you want to show just one of them as an example. % Instead of recreating the card for the manual, give it a uid and \cs{input} your card list, then typeset only that card by passing its uid to the \cs{ShowCard} macro. % % IDs must be unique. If you specify the same \opt{uid} for more than one card, compilation will abort and tell you to fix it. % % Do not use integers as your uids, because \pkg{cardgame} uses them internally. It might work, but it might also not. % % \DescribeOption{version} % You can assign a version number to each card (or, of course, use a card type definition to set all your cards to the same version) in order to track changes while playtesting. % If the \opt{playtest} package option is set, then each card's version number is displayed in its frame. % % \DescribeOption{rulings} % Set this option to describe any additional rules, specific interactions with other cards, or FAQs that are too long to appear on the card itself. You can use the \cs{ListCards} macro to display these rulings in your game's manual. % % \DescribeOption{copies} % Set this to a positive integer to have the \cs{PrintCards} macro generate that many copies of the card (and that many copies of the back as well). % \subsection{Card-Defining Macros} % The main command to make a card is the \cs{Card} macro. % \DescribeMacro {\Card} % \begin{syntax} % \cs{Card} \oarg{options} \marg{name} \marg{text} % \end{syntax} % A card only has two mandatory parts: a name (usually displayed in the title box) and a text. % However, either or both of those mandatory arguments can be empty. % If any of them are empty, then the card instead uses its \opt{name} respectively \opt{text} options, if given. % If those are empty as well, then the card is generated without a name respectively text, which is perfectly legal. % % \opt{options} is a comma-separated list of key-value pairs, as described in the previous sections. % \subsection{Defining New Card Types}\label{sec:cardtypes} % In order to avoid duplicating information in your document, you can define new ``card types'' that share any number of default options. % This has nothing to do with the \opt{type} attribute of a card, though it would be natural for a card type to have a shared \opt{type} as well. % \DescribeMacro {\DeclareCardType} % \begin{syntax} % \cs{DeclareCardType} \oarg{type} \marg{name} \marg{defaults} % \end{syntax} % This command defines a new macro \cs{} (or raises an error if that command already exists). The new macro behaves exactly like the \cs{Card} macro, except that any key-value pairs you pass to the final argument of \cs{DeclareCardType} become the default values for cards created by the new macro. % You can still overwrite them on a case by case basis. % % Card types can inherit from each other: if you pass the name of an existing card type (created by an earlier \cs{DeclareCardType}) as the optional argument to \cs{DeclareCardType}, then all the options of that existing type are imported into the new type. You can override any number of them by passing new values in the \opt{} argument. % % For both the \opt{} and the \opt{} option, do not include the backslash. That is, use \cs{DeclareCardType[type]\{name\}}, not \cs{DeclareCardType[\textbackslash type]\{\textbackslash name\}}. % \section{Outputting Things} % The commands in this section are the only ones that actually typeset something. % In order to output a card, you must first have defined it as described in the previous section. % % \DescribeMacro{\ShowCard} % \begin{syntax} % \cs{ShowCard} \oarg{uid} % \end{syntax} % This macro typesets a single card. It is equivalent to a call to \cs{tikz} and as such you probably want to wrap it in a \texttt{figure} environment. % % If no argument is given, then the most recently defined card is shown. % Otherwise, the argument should be the \opt{uid} of an existing card; that card is shown. % % \DescribeMacro{\PrintCards} % \begin{syntax} % \cs{PrintCards} \oarg{options} % \end{syntax} % This macro typesets \emph{all} the cards you have defined in this document. % % Cards are printed flush with each other, nine to a page (the maximum possible for a home printer). % % If you call this in a document that also contains other things, you probably want to call \cs{newpage} first. % % \opt{} is a list of key-value pairs. The following options are recognised. % % \DescribeOption{sort} % This is a boolean. The default is \opt{true} and sorts the cards by type, then level, then subtype, then name. All faces are printed first, then all back sides (if enabled). % If \opt{false}, then cards appear in the order in which they are defined in the document, though back sides still appear last. % % \DescribeOption{backs} % This is a boolean. The default is \opt{true}. % If \opt{false}, only faces are generated, no back sides. % % \cs{PrintCards} requires two compilations to work properly. % % \DescribeMacro{\ListCards} % \begin{syntax} % \cs{ListCards}\oarg{options} % \end{syntax} % This macro generates a table with information about \emph{all} cards you have defined. You can include it in your game manual as a reference. % It takes one optional key-value argument, \opt{sort}, which has the same effect as for the \cs{PrintCards} command. % \begin{LaTeXdemo}[code only] % \ListCards[sort=false] % \end{LaTeXdemo} % \ListCards[sort=false] % \section{Package Options} % The \pkg{cardgame} package accepts the following options. % % \DescribeOption{sort} % This sets the default value of the \opt{sort} option for the \cs{PrintCards} and \cs{ListCards} commands. % % \DescribeOption{backs} % This sets the default value of the \opt{backs} option for the \cs{PrintCards} command. % % \DescribeOption{playtest} % A boolean that defaults to \opt{false}. % If \opt{true}, include versioning information in the frame of the card. % This documentation has been compiled with \opt{playtest=true}. % \section{Issues} % If you find a bug or have a request, please contact me at \texttt{latex@homeworld.space} or open an issue at \href{https://codeberg.org/BettaGeorge/cardgame}{https://codeberg.org/BettaGeorge/cardgame}. % % The implementation of \cs{ListCards} is rudimentary and to be improved in future versions of this package. % \end{documentation} % % \begin{implementation} % % \section{Implementation} % % \begin{macrocode} %<*package> % \end{macrocode} % % \begin{macrocode} %<@@=cardgame> % \end{macrocode} % % \begin{macrocode} \ProvidesExplPackage {cardgame} {2026-04-19} {1.0} {Typesetting custom game cards} \RequirePackage{l3keys2e} \RequirePackage[dvipsnames]{xcolor} \RequirePackage{tabularray} % \end{macrocode} % TikZ libraries cannot be loaded while explicit syntax is on, even though they can be \emph{used} without problem. % \begin{macrocode} \ExplSyntaxOff \usepackage{tikz} \usetikzlibrary{calc} \ExplSyntaxOn % \end{macrocode} % \subsection{Variables and constants} % % \begin{macro}{\c_@@_file_message_tl} % A short piece of text that is added to the top of an auto-generated % file. Setting this up as a constant means that it can be changed % (for example for translation) if necessary. % \begin{macrocode} \tl_const:Nn \c_@@_file_message_tl { \iow_char:N \% ~ This~is~an~auxiliary~file~used~by~the~'cardgame'~package. \iow_newline: \iow_char:N \% ~ This~file~may~safely~be~deleted. \iow_newline: \iow_char:N \% ~ It~will~be~recreated~as~required. \iow_newline: } % \end{macrocode} % \end{macro} % \begin{macro}{defaultbgcolour,defaulttitlecolour,defaultframecolour,defaultbordercolour} % The following are the default colours for things, just because we need to do \emph{something} if the user sets no options. I like yellow. % \begin{macrocode} \colorlet{defaultbgcolour}{Dandelion!20} \colorlet{defaulttitlecolour}{Dandelion!60} \colorlet{defaultframecolour}{Bittersweet!60} \colorlet{defaultbordercolour}{black} % \end{macrocode} % \end{macro} % These token lists contain what certain things should be called. % \begin{macrocode} \tl_const:Nn \const_portrait_tl {portrait} \tl_const:Nn \const_cost_tl {cost} \tl_const:Nn \const_mark_tl {mark} \tl_const:Nn \const_secondarymark_tl {secondary~mark} \tl_const:Nn \const_level_tl {level} \tl_const:Nn \const_type_tl {type} \tl_const:Nn \const_subtype_tl {subtype} \tl_const:Nn \const_tags_tl {tags} \tl_const:Nn \const_name_tl {name} % \end{macrocode} % These two comma-separated lists save all existing cards. % We are going to write precompiled key lists into them, then later call \cs{tl_use} on each element to recover the values. % \begin{macrocode} \clist_new:N \g_cards_clist \clist_new:N \g_cardbacks_clist % \end{macrocode} % This list saves which uids are currently being used, so we can check whether a newly requested uid is in fact unique. % \begin{macrocode} \clist_new:N \g_uid_clist % \end{macrocode} % The following increments by one each time a card is created and assigns it a unique identifier, if the user has not provided one. % \begin{macrocode} \int_new:N\g_uid_int % \end{macrocode} % This list tracks which tags \emph{exist} at all. This is not currently useful, but we might want to generate lists of tags or something like that later. % \begin{macrocode} \clist_new:N \g_existingtags_clist % \end{macrocode} % debugging messages % \begin{macrocode} \msg_new:nnn { cardgame } { tagcount } { #1:~#2 } \msg_new:nnn { cardgame } { cornercontent }{ Unknown~identifier~'#1';~ typesetting~nothing~and~continuing. } \msg_new:nnn { cardgame } { uid }{ UID~'#1'~is~already~taken;~assigning~ a~random~one. } % \end{macrocode} % \subsection{Key-Value Options} % We now set up the various key-value options. All of these are documented in the user part of this manual, so we just list them here without further comment. % % First, the options that can be passed to the package itself. % \begin{macrocode} \keys_define:nn { cardgame } { sort .bool_set:N = \l_sort_bool, sort .initial:n = { true }, sort .default:n = { true }, backs .bool_set:N = \l_generatecardbacks_bool, backs .initial:n = { true }, backs .default:n = { true }, playtest .bool_set:N = \l_playtestinfo_bool, playtest .initial:n = { false }, playtest .default:n = { true }, } \ProcessKeysOptions{cardgame} % \end{macrocode} % % The options that can be passed to list-producing macros (\cs{PrintCards} and \cs{ListCards}) overlap with the package options, but we may want to add more in the future. % \begin{macrocode} \keys_define:nn { cardlist } { sort .bool_set:N = \l_sort_bool, sort .initial:n = { true }, sort .default:n = { true }, backs .bool_set:N = \l_generatecardbacks_bool, backs .initial:n = { true }, backs .default:n = { true }, } % \end{macrocode} % By far the most expansive list is the one for the \cs{Card} command. % The meta keys are just a trick to make \opt{color} set \opt{colour}. % \begin{macrocode} \keys_define:nn { card } { % OPTIONS DEFINING CARD TEXT name .tl_set:N = \l_name_tl, name .initial:n = {}, name .default:n = {}, type .tl_set:N = \l_type_tl, type .initial:n = {}, type .default:n = {}, subtype .tl_set:N = \l_subtype_tl, subtype .initial:n = {}, subtype .default:n = {}, text .tl_set:N = \l_text_tl, text .initial:n = {}, % text .default:n = {}, more~text .tl_set:N = \l_moretext_tl, more~text .initial:n = {}, % more~text .default:n = {}, art .tl_set:N = \l_art_tl, art .initial:n = {}, art .default:n = {}, back~art .tl_set:N = \l_backart_tl, back~art .initial:n = {}, back~art .default:n = {}, cost .tl_set:N = \l_cost_tl, cost .initial:n = { 0 }, cost .default:n = 0, mark .tl_set:N = \l_mark_tl, mark .initial:n = {}, mark .default:n = {}, secondary~mark .tl_set:N = \l_mark_tl, secondary~mark .initial:n = {}, secondary~mark .default:n = {}, level .int_set:N = \l_level_int, level .initial:n = { 0 }, level .default:n = 0, tags .clist_set:N = \l_tags_clist, tags .initial:n = {}, tags .default:n = {}, % OPTIONS MODIFYING THE DISPLAY OF TEXT modify~name .tl_set:N = \l_modifyname_tl, modify~name .initial:n = { \Large\bf }, modify~name .default:n = {}, modify~text .tl_set:N = \l_textsize_tl, modify~text .initial:n = {\normalsize}, modify~text .default:n = {\normalsize}, modify~more~text .tl_set:N = \l_moretextsize_tl, modify~more~text .initial:n = {\normalsize}, modify~more~text .default:n = {\normalsize}, modify~north~west .tl_set:N = \l_nwtextsize_tl, modify~north~west .initial:n = {\normalsize}, modify~north~west .default:n = {\normalsize}, modify~north~west~pre .tl_set:N = \l_nwpretextsize_tl, modify~north~west~pre .initial:n = {\tiny}, modify~north~west~pre .default:n = {\tiny}, modify~north~west~post .tl_set:N = \l_nwposttextsize_tl, modify~north~west~post .initial:n = {\tiny}, modify~north~west~post .default:n = {\tiny}, modify~north~east .tl_set:N = \l_netextsize_tl, modify~north~east .initial:n = {\normalsize}, modify~north~east .default:n = {\normalsize}, modify~north~east~pre .tl_set:N = \l_nepretextsize_tl, modify~north~east~pre .initial:n = {\tiny}, modify~north~east~pre .default:n = {\tiny}, modify~north~east~post .tl_set:N = \l_neposttextsize_tl, modify~north~east~post .initial:n = {\tiny}, modify~north~east~post .default:n = {\tiny}, modify~south~east .tl_set:N = \l_setextsize_tl, modify~south~east .initial:n = {\normalsize}, modify~south~east .default:n = {\normalsize}, modify~south~east~pre .tl_set:N = \l_sepretextsize_tl, modify~south~east~pre .initial:n = {\tiny}, modify~south~east~pre .default:n = {\tiny}, modify~south~east~post .tl_set:N = \l_seposttextsize_tl, modify~south~east~post .initial:n = {\tiny}, modify~south~east~post .default:n = {\tiny}, modify~south~west .tl_set:N = \l_swtextsize_tl, modify~south~west .initial:n = {\normalsize}, modify~south~west .default:n = {\normalsize}, modify~south~west~pre .tl_set:N = \l_swpretextsize_tl, modify~south~west~pre .initial:n = {\tiny}, modify~south~west~pre .default:n = {\tiny}, modify~south~west~post .tl_set:N = \l_swposttextsize_tl, modify~south~west~post .initial:n = {\tiny}, modify~south~west~post .default:n = {\tiny}, % OPTIONS CONTROLLING THE PLACEMENT OF TEXT tag~box .tl_set:N = \l_tagbox_tl, tag~box .initial:n = { tags }, tag~box .default:n = {}, tag~box~content .tl_set:N = \l_tagboxcontent_tl, tag~box~content .initial:n = {}, tag~box~content .default:n = {}, back~tag~box .tl_set:N = \l_btagbox_tl, back~tag~box .initial:n = {}, back~tag~box .default:n = {}, title~box .tl_set:N = \l_titlebox_tl, title~box .initial:n = { name }, title~box .default:n = {}, title~box~content .tl_set:N = \l_titleboxcontent_tl, title~box~content .initial:n = {}, title~box~content .default:n = {}, back~title~box .tl_set:N = \l_btitlebox_tl, back~title~box .initial:n = { type }, back~title~box .default:n = {}, south~west~content .tl_set:N = \l_swcontent_tl, south~west~content .initial:n = {}, south~west~content .default:n = {}, south~west~pre .tl_set:N = \l_swpre_tl, south~west~pre .initial:n = {}, south~west~pre .default:n = {}, south~west~post .tl_set:N = \l_swpost_tl, south~west~post .initial:n = {}, south~west~post .default:n = {}, south~west .tl_set:N = \l_sw_tl, south~west .initial:n = {}, south~west .default:n = {}, back~south~west .tl_set:N = \l_bsw_tl, back~south~west .initial:n = {}, back~south~west .default:n = {}, south~east~content .tl_set:N = \l_secontent_tl, south~east~content .initial:n = {}, south~east~content .default:n = {}, south~east~pre .tl_set:N = \l_sepre_tl, south~east~pre .initial:n = {}, south~east~pre .default:n = {}, south~east~post .tl_set:N = \l_sepost_tl, south~east~post .initial:n = {}, south~east~post .default:n = {}, south~east .tl_set:N = \l_se_tl, south~east .initial:n = {}, south~east .default:n = {}, back~south~east .tl_set:N = \l_bse_tl, back~south~east .initial:n = {}, back~south~east .default:n = {}, north~west~content .tl_set:N = \l_nwcontent_tl, north~west~content .initial:n = {}, north~west~content .default:n = {}, north~west~pre .tl_set:N = \l_nwpre_tl, north~west~pre .initial:n = {}, north~west~pre .default:n = {}, north~west~post .tl_set:N = \l_nwpost_tl, north~west~post .initial:n = {}, north~west~post .default:n = {}, north~west .tl_set:N = \l_nw_tl, north~west .initial:n = {}, north~west .default:n = {}, back~north~west .tl_set:N = \l_bnw_tl, back~north~west .initial:n = {}, back~north~west .default:n = {}, north~east~content .tl_set:N = \l_necontent_tl, north~east~content .initial:n = {}, north~east~content .default:n = {}, north~east~pre .tl_set:N = \l_nepre_tl, north~east~pre .initial:n = {}, north~east~pre .default:n = {}, north~east~post .tl_set:N = \l_nepost_tl, north~east~post .initial:n = {}, north~east~post .default:n = {}, north~east .tl_set:N = \l_ne_tl, north~east .initial:n = {}, north~east .default:n = {}, back~north~east .tl_set:N = \l_bne_tl, back~north~east .initial:n = {}, back~north~east .default:n = {}, % COLOUR OPTIONS link~colours .bool_set:N = \l_linkcolours_bool, link~colours .initial:n = { false }, link~colours .default:n = { true }, link~colors .meta:n = { link~colours={#1} }, background~colour .tl_set:N = \l_col_bg_tl, background~colour .initial:n = {}, background~colour .default:n = {}, background~color .meta:n = { background~colour={#1} }, title~colour .tl_set:N = \l_col_title_tl, title~colour .initial:n = {}, title~colour .default:n = {}, title~color .meta:n = { title~colour={#1} }, title~gradient .tl_set:N = \l_grad_title_tl, title~gradient .initial:n = {}, title~gradient .default:n = {}, tags~colour .tl_set:N = \l_col_tags_tl, tags~colour .initial:n = {}, tags~colour .default:n = {}, tags~color .meta:n = { tags~colour={#1} }, tags~gradient .tl_set:N = \l_grad_tags_tl, tags~gradient .initial:n = {}, tags~gradient .default:n = {}, frame~colour .tl_set:N = \l_col_frame_tl, frame~colour .initial:n = {}, frame~colour .default:n = {}, frame~color .meta:n = { frame~colour={#1} }, separator~colour .tl_set:N = \l_col_separator_tl, separator~colour .initial:n = {}, separator~colour .default:n = {}, separator~color .meta:n = { frame~colour={#1} }, border~colour .tl_set:N = \l_col_border_tl, border~colour .initial:n = {}, border~colour .default:n = {}, border~color .meta:n = { border~colour={#1} }, back~background~colour .tl_set:N = \l_bcol_bg_tl, back~background~colour .initial:n = {}, back~background~colour .default:n = {}, back~background~color .meta:n = { back~background~colour={#1} }, back~title~colour .tl_set:N = \l_bcol_title_tl, back~title~colour .initial:n = {}, back~title~colour .default:n = {}, back~title~color .meta:n = { back~title~colour={#1} }, back~title~gradient .tl_set:N = \l_bgrad_title_tl, back~title~gradient .initial:n = {}, back~title~gradient .default:n = {}, back~tags~colour .tl_set:N = \l_bcol_tags_tl, back~tags~colour .initial:n = {}, back~tags~colour .default:n = {}, back~tags~color .meta:n = { back~tags~colour={#1} }, back~tags~gradient .tl_set:N = \l_bgrad_tags_tl, back~tags~gradient .initial:n = {}, back~tags~gradient .default:n = {}, back~frame~colour .tl_set:N = \l_bcol_frame_tl, back~frame~colour .initial:n = {}, back~frame~colour .default:n = {}, back~frame~color .meta:n = { back~frame~colour={#1} }, back~separator~colour .tl_set:N = \l_bcol_separator_tl, back~separator~colour .initial:n = {}, back~separator~colour .default:n = {}, back~separator~color .meta:n = { frame~colour={#1} }, back~border~colour .tl_set:N = \l_bcol_border_tl, back~border~colour .initial:n = {}, back~border~colour .default:n = {}, back~border~color .meta:n = { back~border~colour={#1} }, % OPTIONS CONTROLLING THE OVERALL LAYOUT orientation .tl_set:N = \l_orientation_tl, orientation .initial:n = {portrait}, orientation .default:n = {portrait}, separator .bool_set:N = \l_separator_bool, separator .initial:n = { false }, separator .default:n = { true }, back~separator .bool_set:N = \l_bseparator_bool, back~separator .initial:n = { false }, back~separator .default:n = { true }, rounded~corners .bool_set:N = \l_rounded_corners_bool, rounded~corners .initial:n = { true }, rounded~corners .default:n = { true }, back .bool_set:N = \l_back_bool, back .initial:n = { false }, back .default:n = { true }, % OPTIONS FOR INVISIBLE ATTRIBUTES version .tl_set:N = \l_version_tl, version .initial:n = {}, version .default:n = {}, rulings .tl_set:N = \l_rulings_tl, rulings .initial:n = {}, rulings .default:n = {}, uid .tl_set:N = \l_uid_tl, uid .initial:n = {}, uid .default:n = {}, copies .int_set:N = \l_copies_int, copies .initial:n = { 1 }, copies .default:n = 1, } % \end{macrocode} % \subsection{Helper Functions} % \begin{macro}{\insert_attribute:n} % Use this in places where we want to typeset one of the % various attributes of a card based on the user's preference. % The sole argument is the string the user passed. % For example, if \opt{south west=mark} is set by the user, then % while typesetting the south west corner, we call % \cs{insert_attribute:n\{mark\}}. % \begin{macrocode} \cs_new:Nn \insert_attribute:n { \tl_case:NnF{#1}{ \const_tags_tl { \clist_use:Nn \l_tags_clist {,~} } \const_cost_tl { \tl_use:N\l_cost_tl } \const_level_tl { \int_use:N\l_level_int } \const_mark_tl { \tl_use:N\l_mark_tl } \const_secondarymark_tl { \tl_use:N\l_secondarymark_tl } \const_type_tl { \tl_use:N\l_type_tl } \const_subtype_tl { \tl_use:N\l_subtype_tl } \const_name_tl { \tl_use:N\l_name_tl } }{ \msg_error:nnV { cardgame }{ cornercontent }{ #1 } } } % \end{macrocode} % \end{macro} % % \subsection{Defining Cards} % \begin{macro}{\card:n} % The main command to make a card. % It takes all of its arguments via the key-value interface. % It stores the card indexed by its type, subtype, and name, such that we can later % sort them alphabetically. % It also stores a copy of the card indexed by its uid, because I could not figure % out how to use references. % We do not typeset the cards yet; this is handled by separate commands. % If automatic card back generation is on, the command calls itself % once to generate the back side. % \begin{macrocode} \cs_generate_variant:Nn \keys_precompile:nnN { nVN } \cs_new:Nn \card:n { \group_begin: \keys_set:nn { card } { #1 } % first, if a uid was provided, check whether it is valid. % if not, assign one. % Do not do any of this for backsides though. \bool_if:NF \l_back_bool { \tl_if_empty:NTF\l_uid_tl { \tl_set:NV\l_uid_tl\g_uid_int \int_gincr:N\g_uid_int }{ \clist_if_in:NVT\g_uid_clist\l_uid_tl { \msg_error:nnV {cardgame} {uid} {\l_uid_tl} \tl_set:Ne\l_uid_tl{\int_use:N\g_uid_int} \int_incr:N\g_uid_int } } \clist_gput_right:NV \g_uid_clist \l_uid_tl } % we simply set a new token list to contain all the keys of the card. % since we want to process back sides separately, they go in a special % back side list. \bool_if:NTF \l_back_bool { \tl_new:c { cardback @ \l_type_tl @ \int_use:N\l_level_int @ \l_subtype_tl @ \l_name_tl @ \l_uid_tl } \keys_precompile:nnN { card } { #1 } \l_tmpa_tl % precompile is a local assignment, and there is no global variant. \tl_gset_eq:cN { cardback @ \l_type_tl @ \int_use:N\l_level_int @ \l_subtype_tl @ \l_name_tl @ \l_uid_tl } \l_tmpa_tl % also, we somehow need to remember which cards exist. % Since it is hard to expand multiple copies during the typesetting loop later, % we simply copy the card now. \int_set:Nn \l_tmpa_int { 0 } \int_while_do:nNnn { \l_tmpa_int } < { \l_copies_int } { \clist_gput_right:Nx \g_cardbacks_clist { cardback @ \l_type_tl @ \int_use:N\l_level_int @ \l_subtype_tl @ \l_name_tl @ \l_uid_tl } \int_incr:N \l_tmpa_int } }{ \tl_new:c { \l_type_tl @ \int_use:N\l_level_int @ \l_subtype_tl @ \l_name_tl @ \l_uid_tl } \tl_set:Nn\l_tmpb_tl { #1 } \tl_put_right:Nx\l_tmpb_tl {, uid=\tl_use:N\l_uid_tl } \keys_precompile:nVN { card } \l_tmpb_tl \l_tmpa_tl % precompile is a local assignment, and there is no global variant. \tl_gset_eq:cN { \l_type_tl @ \int_use:N\l_level_int @ \l_subtype_tl @ \l_name_tl @ \l_uid_tl } \l_tmpa_tl % We are currently doing a front side, so we also save the card in the uid list. \tl_gset_eq:cN { card_by_uid @ \l_uid_tl } \l_tmpa_tl % also, we somehow need to remember which cards exist. % Since it is hard to expand multiple copies during the typesetting loop later, % we simply copy the card now. % Yes, this is unnecessary memory overhead, but when will you % ever have so many cards that it matters in practice? \int_set:Nn \l_tmpa_int { 0 } \int_while_do:nNnn { \l_tmpa_int } < { \l_copies_int } { \clist_gput_right:Nx \g_cards_clist { \l_type_tl @ \int_use:N\l_level_int @ \l_subtype_tl @ \l_name_tl @ \l_uid_tl } \int_incr:N \l_tmpa_int } \bool_if:NT \l_generatecardbacks_bool { \card:n { #1, back } } } \group_end: } % \end{macrocode} % \end{macro} % \subsection{Generating Output} % \begin{macro}{\typeset_card:nnn} % \cs{typeset_card} is what actually TikZes cards. % You should not have to call it directly, but in case you do, here is what it does. % The first argument is either empty or a card UID as generated by \cs{card}, that is, % the string \texttt{card_by_uid@}. % The second argument is a list of key-value pairs. % The third argument is either empty or an integer between 0 and 8. % The card to be typeset gets all the characteristics of the card specified % in the first argument. Then all the keys specified in the second argument % override those. If the first argument is empty, then naturally you must % specify all characteristics as key-value pairs in the second argument. % If the third argument is empty, a TikZ picture is then produced with no % special placement. Wrap it in a figure environment or whatever else you % want to do with the picture. % If the third argument is an integer, the card is placed at that index plus % one on the current page using the overlay mechanism. This is used by % the \cs{print_cards} macro. % % An M:tG card is 63x88 mm big. We use 64x89 mm to leave some room for error while cutting while still fitting inside a standard sleeve. % An A4 page is 210x297 mm. % In particular, a 3x3 portrait layout is optimal, since a landscape layout % only admits 2x4 cards. % \begin{macrocode} \cs_new:Nn \typeset_card:nnn { \tl_if_blank:nF {#3} { % We calculate the offset of the lower left corner of the card % (henceforth referred to as the "base"). \int_zero_new:N \l_xoffset_int \int_zero_new:N \l_yoffset_int \int_set:Nn \l_xoffset_int { % x offset in mm ( \int_mod:nn { #3 - 1 } { 3 } ) * 66 } \int_set:Nn \l_yoffset_int { % y offset in mm ( 2 - \int_div_truncate:nn { #3 - 1 } { 3 } ) * 91 } } \group_begin: \tl_if_blank:nF{#1}{ \tl_use:c { #1 } } \tl_if_blank:nF{#2}{ \keys_set:nx { card } { #2 } } % The tags should always be sorted the same. \clist_sort:Nn \l_tags_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } % And let's keep a handy counter of the tags used. \bool_if:NF \l_back_bool { \clist_map_inline:Nn \l_tags_clist { \int_if_exist:cF { tagcounter @ ##1 }{ \int_new:c { tagcounter @ ##1 } \clist_gput_right:Nn \g_existingtags_clist { ##1 } } \int_gincr:c { tagcounter @ ##1 } } } % First, we figure out all the colours to use on the face of the card. \tl_if_empty:NTF\l_col_bg_tl{ \colorlet{bg}{defaultbgcolour} }{ \colorlet{bg}{\tl_use:N\l_col_bg_tl} } \tl_if_empty:NTF\l_col_frame_tl{ \colorlet{marg}{defaultframecolour} }{ \colorlet{marg}{\tl_use:N\l_col_frame_tl} } \tl_if_empty:NTF\l_col_separator_tl{ \colorlet{separator}{marg} }{ \colorlet{separator}{\tl_use:N\l_col_separator_tl} } \tl_if_empty:NTF\l_col_title_tl{ \colorlet{box}{defaulttitlecolour} }{ \colorlet{box}{\tl_use:N\l_col_title_tl} } \tl_if_empty:NTF\l_grad_title_tl{ \colorlet{boxgradient}{box} }{ \colorlet{boxgradient}{\tl_use:N\l_grad_title_tl} } \tl_if_empty:NTF\l_col_tags_tl{ \colorlet{tagscolour}{box} }{ \colorlet{tagscolour}{\tl_use:N\l_col_tags_tl} } \tl_if_empty:NTF\l_grad_tags_tl{ \colorlet{tagsgradient}{boxgradient} }{ \colorlet{tagsgradient}{\tl_use:N\l_grad_tags_tl} } \tl_if_empty:NTF\l_col_border_tl{ \colorlet{border}{defaultbordercolour} }{ \colorlet{border}{\tl_use:N\l_col_border_tl} } % Now, if the back colours are linked, copy everything. % If not, initialise them to the default colours. They might get overridden % in the next step, stay tuned. \bool_if:NTF\l_linkcolours_bool { \colorlet{bbg}{bg} \colorlet{bmarg}{marg} \colorlet{bseparator}{separator} \colorlet{bbox}{box} \colorlet{bboxgradient}{boxgradient} \colorlet{btagscolour}{tagscolour} \colorlet{btagsgradient}{tagsgradient} \colorlet{bborder}{border} }{ \colorlet{bbg}{defaultbgcolour} \colorlet{bmarg}{defaultframecolour} \colorlet{bseparator}{bmarg} \colorlet{bbox}{defaulttitlecolour} \colorlet{bboxgradient}{bbox} \colorlet{btagscolour}{bbox} \colorlet{btagsgradient}{bboxgradient} \colorlet{bborder}{defaultbordercolour} } % All back colours that were explicitly specified override the above. \tl_if_empty:NF\l_bcol_bg_tl{ \colorlet{bbg}{\tl_use:N\l_bcol_bg_tl} } \tl_if_empty:NF\l_bcol_frame_tl{ \colorlet{bmarg}{\tl_use:N\l_bcol_frame_tl} } \tl_if_empty:NF\l_bcol_separator_tl{ \colorlet{bseparator}{\tl_use:N\l_bcol_separator_tl} } \tl_if_empty:NF\l_bcol_title_tl{ \colorlet{bbox}{\tl_use:N\l_bcol_title_tl} } \tl_if_empty:NF\l_bgrad_title_tl{ \colorlet{bboxgradient}{\tl_use:N\l_bgrad_title_tl} } \tl_if_empty:NF\l_bcol_tags_tl{ \colorlet{btagscolour}{\tl_use:N\l_bcol_tags_tl} } \tl_if_empty:NF\l_bgrad_tags_tl{ \colorlet{btagsgradient}{\tl_use:N\l_bgrad_tags_tl} } \tl_if_empty:NF\l_bcol_border_tl{ \colorlet{bborder}{\tl_use:N\l_bcol_border_tl} } % Let the drawing begin. \tikz[ remember~picture=\tl_if_blank:nTF{#3}{false}{true}, overlay=\tl_if_blank:nTF{#3}{false}{true}, minimum~size=0pt, outer~sep=0pt, inner~sep=0pt, ]{ % BASE NODE \tl_if_blank:nTF{#3}{ \node[] (base) at (0,0) {}; }{ \node[] (base) at ( $ (current~page.south~west) + (0.8cm,1.5cm) + (\int_use:N \l_xoffset_int mm,\int_use:N \l_yoffset_int mm) $ ) {}; } % card background \draw[ fill=\bool_if:NTF\l_back_bool{bbg}{bg} ] (base) rectangle ($(base) + (64mm,89mm)$); % colour frame \draw[ line~width=2mm, color=\bool_if:NTF\l_back_bool{bmarg}{marg}, ] \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base) + (2mm,89mm) $) -- ($(base) + (2mm,0mm) $) ; } { ($(base) + (0mm,2mm) $) -- ($(base) + (64mm,2mm) $) ; } \draw[ line~width=2mm, color=\bool_if:NTF\l_back_bool{bmarg}{marg}, ] \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base) + (62mm,89mm) $) -- ($(base) + (62mm,0mm) $) ; } { ($(base) + (0mm,87mm) $) -- ($(base) + (64mm,87mm) $) ; } % SEPARATOR \bool_if:NTF \l_back_bool { \bool_if:NT \l_bseparator_bool { \path[ draw=bseparator, line~width=1mm, ] \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base) + (3mm,40mm)$) -- +(58mm,0mm); }{ ($(base) + (3mm,44.5mm)$) -- +(58mm,0mm); } } }{ \bool_if:NT \l_separator_bool { \path[ draw=separator, line~width=1mm, ] \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base) + (3mm,40mm)$) -- +(58mm,0mm); }{ ($(base) + (3mm,44.5mm)$) -- +(58mm,0mm); } } } % TITLE BOX \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { \shade[ bottom~color=\bool_if:NTF\l_back_bool{bbox}{box}, top~color=\bool_if:NTF\l_back_bool{bboxgradient}{boxgradient}, draw=none, rounded~corners=4mm, ] ($(base) + (0mm,89mm) $) -- ++(0mm,-15mm) --++(64mm,0mm) -- ++(0mm,15mm) --cycle; }{ \shade[ right~color=\bool_if:NTF\l_back_bool{bbox}{box}, left~color=\bool_if:NTF\l_back_bool{bboxgradient}{boxgradient}, draw=none, rounded~corners=4mm, ] ($(base) + (0mm,0mm) $) -- ++(0mm,89mm) --++(15mm,0mm) -- ++(0mm,-89mm) --cycle; } % TAG BOX \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { \shade[ top~color=\bool_if:NTF\l_back_bool{btagscolour}{tagscolour}, bottom~color=\bool_if:NTF\l_back_bool{btagsgradient}{tagsgradient}, draw=none, rounded~corners=4mm, ] (base) -- ++(0mm,9mm) -- ++(64mm,0mm) -- ++(0mm,-9mm) --cycle; }{ \shade[ left~color=\bool_if:NTF\l_back_bool{btagscolour}{tagscolour}, right~color=\bool_if:NTF\l_back_bool{btagsgradient}{tagsgradient}, draw=none, rounded~corners=4mm, ] ($(base) + (64mm,0mm) $) -- ++(-9mm,0mm) -- ++(0mm,89mm) -- ++(9mm,0mm) --cycle; } % Tags \node[ anchor=center, align=center, font=\normalsize, text~width=50mm, rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , ] (tags) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($ (base) + (32mm,6mm) + (0mm,-1.5mm) $) } { ($ (base) + (58mm,44.5mm) + (1.5mm,0mm) $) } { \bool_if:NTF \l_back_bool{ \tl_if_empty:NF \l_btagbox_tl { \insert_attribute:n{\l_btagbox_tl} } } { \tl_if_empty:NTF \l_tagboxcontent_tl { \tl_if_empty:NF \l_tagbox_tl { \insert_attribute:n{\l_tagbox_tl} } }{ \tl_use:N\l_tagboxcontent_tl } } }; % CARD BORDER \draw[ color=\bool_if:NTF\l_back_bool{bborder}{border}, line~width=2mm, rounded~corners=4mm, fill=none, ] (base) -- ++(0mm,89mm) -- ++(64mm,0mm) -- ++(0mm,-89mm) --cycle; % I could not find a way to make a TikZ path have rounded corners % only on one side, so instead we simply draw over it if the outer % corners are desired to be unrounded. \bool_if:NF\l_rounded_corners_bool{ \draw[ color=\bool_if:NTF\l_back_bool{bborder}{border}, line~width=2mm, fill=none, ] (base) -- ++(0mm,89mm) -- ++(64mm,0mm) -- ++(0mm,-89mm) --cycle; } % CARD NAME \node[ anchor=center, align=center, font=\tl_use:N\l_modifyname_tl, text~width=\tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {54mm} {70mm} , rotate=\tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , ] (title) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($ (base) + (32mm,81mm) $) } { ($ (base) + (8mm,44.5mm) $) } { \bool_if:NTF \l_back_bool { \tl_if_empty:NF \l_btitlebox_tl { \insert_attribute:n \l_btitlebox_tl } } { \tl_if_empty:NTF \l_titleboxcontent_tl { \tl_if_empty:NF \l_titlebox_tl { \insert_attribute:n \l_titlebox_tl } } { \l_titleboxcontent_tl } } }; % CORNER CONTENT % NORTH WEST \node[ anchor=west , align=left, font=\tl_use:N\l_nwtextsize_tl, rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , minimum~size=5mm, ] (nwcontent) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base)+(5mm,81.5mm)$) }{ ($(base)+(7.5mm,5mm)$) } { \bool_if:NTF\l_back_bool { \tl_if_empty:NF\l_bnw_tl{ \tl_use:N\l_bnw_tl } }{ \tl_if_empty:NF\l_nwpre_tl{ \tl_use:N\l_nwpretextsize_tl \tl_use:N\l_nwpre_tl\\[-1mm] } \tl_if_empty:NTF\l_nw_tl{ \tl_use:N\l_nwcontent_tl }{ \insert_attribute:n \l_nw_tl } \tl_if_empty:NF\l_nwpost_tl{ \\[-2mm] \tl_use:N\l_nwposttextsize_tl \tl_use:N\l_nwpost_tl } } }; % SOUTH WEST \node[ anchor=west , align=left, font=\tl_use:N\l_swtextsize_tl, rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , minimum~size=5mm, ] (swcontent) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base)+(5mm,5mm)$) }{ ($(base)+(59mm,5mm)$) } { \bool_if:NTF\l_back_bool { \tl_if_empty:NF\l_bsw_tl{ \tl_use:N\l_bsw_tl } }{ \tl_if_empty:NF\l_swpre_tl{ \tl_use:N\l_swpretextsize_tl \tl_use:N\l_swpre_tl\\[-1mm] } \tl_if_empty:NTF\l_sw_tl{ \tl_use:N\l_swcontent_tl }{ \insert_attribute:n \l_sw_tl } \tl_if_empty:NF\l_swpost_tl{ \\[-2mm] \tl_use:N\l_swposttextsize_tl \tl_use:N\l_swpost_tl } } }; % NORTH EAST \node[ anchor=east, align=right, font=\tl_use:N\l_netextsize_tl, rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , minimum~size=5mm, ] (necontent) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base)+(59mm,81.5mm)$) }{ ($(base)+(7.5mm,84mm)$) } { \bool_if:NTF\l_back_bool { \tl_if_empty:NF\l_bne_tl{ \tl_use:N\l_bne_tl } } { \tl_if_empty:NF\l_nepre_tl{ \tl_use:N\l_nepretextsize_tl \tl_use:N\l_nepre_tl\\[-1mm] } \tl_if_empty:NTF\l_ne_tl{ \tl_use:N\l_necontent_tl }{ \insert_attribute:n \l_ne_tl } \tl_if_empty:NF\l_nepost_tl{ \\[-2mm] \tl_use:N\l_neposttextsize_tl \tl_use:N\l_nepost_tl } } }; % SOUTH EAST \node[ anchor=east, align=right, font=\tl_use:N\l_setextsize_tl, rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , minimum~size=5mm, ] (secontent) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base)+(59mm,5mm)$) }{ ($(base)+(59mm,84mm)$) } { \bool_if:NTF\l_back_bool { \tl_if_empty:NF\l_bse_tl{ \tl_use:N\l_bse_tl } } { \tl_if_empty:NF\l_sepre_tl{ \tl_use:N\l_sepretextsize_tl \tl_use:N\l_sepre_tl\\[-1mm] } \tl_if_empty:NTF\l_se_tl{ \tl_use:N\l_secontent_tl }{ \insert_attribute:n \l_se_tl } \tl_if_empty:NF\l_sepost_tl{ \\[-2mm] \tl_use:N\l_seposttextsize_tl \tl_use:N\l_sepost_tl } } }; % PLAYTESTING INFORMATION \bool_if:NF \l_back_bool { \bool_if:NT \l_playtestinfo_bool { \node[ anchor=south~west, rotate=\tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {90}{180}, ] (type) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($(base)+(62.7mm,10mm)$) }{ ($(base)+(54mm,87.7mm)$) }{ \tiny \textcolor{black!70}{ \l_type_tl{} ~ -- ~ \l_subtype_tl{} ~ -- ~ v\l_version_tl{} } }; } } % CARD TEXT \bool_if:NF \l_back_bool { \tl_if_eq:NNF \l_type_tl \const_monsterstring_tl { \node[ anchor=north~west, font=\tl_use:N\l_textsize_tl, text~width=\tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {50mm}{40mm}, rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0}{90}, minimum~height=30mm, ] (text) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($ (base) + (5mm,40mm) $) }{ ($ (base) + (20mm,5mm) $) } { \tl_use:N \l_text_tl }; % MORE TEXT \tl_if_blank:VF \l_moretext_tl { \tl_trim_spaces:N \l_moretext_tl \node[ rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , anchor=south~west, align=left, font=\l_moretextsize_tl, text~width=50mm, ] (goalline) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($ (base) + (5mm,10mm) $) }{ ($ (base) + (54mm,5mm) $) } { \l_moretext_tl }; } } } % ART \bool_if:NF \l_back_bool { \tl_if_empty:NF\l_art_tl{ \node[ rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , anchor=center, ] (backgraphics) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($ (base) + (32mm,57mm) $) }{ ($ (base) + (35mm,65.5mm) $) }{ \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { \includegraphics[ height=31mm, width=56mm, keepaspectratio ]{\tl_use:N\l_art_tl} }{ \includegraphics[ width=40mm, height=38mm, keepaspectratio ]{\tl_use:N\l_art_tl} } }; } } % BACK ART \bool_if:NT \l_back_bool { \tl_if_empty:NF\l_backart_tl{ \node[ rotate= \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl {0} {90} , anchor=center, ] (backgraphics) at \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { ($ (base) + (30mm,42.5mm) $) }{ ($ (base) + (35mm,42.5mm) $) }{ \tl_if_eq:NNTF \l_orientation_tl \const_portrait_tl { \includegraphics[ width=56mm, height=31mm, keepaspectratio ]{\tl_use:N\l_backart_tl} }{ \includegraphics[ height=38mm, width=40mm, keepaspectratio ]{\tl_use:N\l_backart_tl} } }; } } } \group_end: } % \end{macrocode} % \end{macro} % \begin{macro}{\print_cards:n} % This command takes a list of key-value pairs as specificed % in the \texttt{cardlist} definition. % It iterates over all existing cards and calls \cs{typeset_card:nnn} repeatedly, % inserting page breaks along the way. % \begin{macrocode} \cs_new:Nn \print_cards:n { \keys_set:nn { cardlist } { #1 } % If desired, sort all cards alphabetically by type, subtype, and card name. \bool_if:NT \l_sort_bool { \clist_sort:Nn \g_cards_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } \clist_sort:Nn \g_cardbacks_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } } % Loop over all cards, figuring out where to place them. \int_zero_new:N \l_i_int \int_set:Nn \l_i_int { 9 } % this is just a better zero \clist_map_inline:Nn \g_cards_clist { \int_incr:N \l_i_int \int_compare:nNnT { \l_i_int } > { 9 } { \newpage \pagenumbering{gobble} \thispagestyle{empty} \int_set:Nn \l_i_int { 1 } } \typeset_card:nnn { ##1 } {} { \int_use:N \l_i_int } } % All faces are generated. If back sides are desired, we do those now. \bool_if:NT\l_generatecardbacks_bool { \clist_map_inline:Nn \g_cardbacks_clist { \int_incr:N \l_i_int \int_compare:nNnT { \l_i_int } > { 9 } { \newpage \pagenumbering{gobble} \thispagestyle{empty} \int_set:Nn \l_i_int { 1 } } \typeset_card:nnn { ##1 } {} { \int_use:N \l_i_int } } } % Debugging message: show the tags. \clist_sort:Nn \g_existingtags_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } \clist_map_inline:Nn \g_existingtags_clist { \msg_term:nnxx { cardgame } { tagcount } { ##1 } { \int_use:c { tagcounter @ ##1 } } } } % \end{macrocode} % \end{macro} % \begin{macro}{\list_card:n} % This command produces a single row suitable for including in a tabularray. % The only way to do this seems to be to build a token list for the entire table % body and then expand it all at once. See: % https://tex.stackexchange.com/questions/736567/making-forloop-works-with-tabularray-it-works-with-tabularx % % The macro takes as its sole argument an entry from \cs{g_cards_clist}, which is % of course a token list of precompiled key-value assignments. % % We use a global token list variable which is cleared with % \cs{tl_gclear} inside \cs{list_cards} (the function after % this one). The reason for this is that we have to wrap the % code of \cs{list_card} inside a group to prevent card % attributes from spilling. % \begin{macrocode} \tl_new:N\g_cardtblr_tl \cs_new:Nn \list_card:n { \group_begin: \tl_use:c { #1 } % The tags should always be sorted the same. \clist_sort:Nn \l_tags_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } % note: because texts can contain newlines, the x expansion % is necessary here because we need to surround the cell content % with additional braces. \tl_gput_right:Nx\g_cardtblr_tl{{\tl_use:N\l_name_tl}} \tl_gput_right:Nn\g_cardtblr_tl{&} \tl_gput_right:Nx\g_cardtblr_tl{{\tl_use:N\l_text_tl}} \tl_gput_right:Nn\g_cardtblr_tl{&} \tl_gput_right:Nx\g_cardtblr_tl{{\tl_use:N\l_rulings_tl}} \tl_gput_right:Nn\g_cardtblr_tl{\\} \group_end: } % \end{macrocode} % \end{macro} % \begin{macro}{\list_cards:n} % The internal command to make the table of cards. % It takes as its argument key-value pairs as defined by \texttt{cardlist}. % \begin{macrocode} \cs_new:Nn \list_cards:n { \keys_set:nn { cardlist } { #1 } % sort all cards alphabetically by type, subtype, and card name. \bool_if:NT \l_sort_bool { \clist_sort:Nn \g_cards_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } \clist_sort:Nn \g_cardbacks_clist { \str_compare:nNnTF { ##1 } < { ##2 } { \sort_return_same: } { \sort_return_swapped: } } } \tl_gclear:N\g_cardtblr_tl \clist_map_inline:Nn \g_cards_clist { \list_card:n { ##1 } } \begin{tblr}[expand=\g_cardtblr_tl]{ colspec = {XXX}, colsep = 8mm, hlines = {2pt, white}, row{odd} = {azure8}, row{even} = {gray8}, row{1} = {2em,azure2,fg=white,font=\bfseries\sffamily}, row{2-Z} = {1em} } Name & Text & Rulings\\ \g_cardtblr_tl \end{tblr} } % \end{macrocode} % \end{macro} % \subsection{User-Exposed Functions} % For usage notes, see the user manual. % \begin{macro}{\Card} % There is probably a better way to do this than these nested conditionals, but my knowledge of how \LaTeX3 handles arguments and key-value pairs is not deep enough. % \begin{macrocode} \NewDocumentCommand\Card{ O{} m m }{ \IfBlankTF{#2}{ \IfBlankTF{#3}{ \card:n{ #1, } }{ \card:n{ #1, text={#3}, } } }{ \IfBlankTF{#3}{ \card:n{ #1, name={#2} } }{ \card:n{ #1, text={#3}, name={#2} } } } } % \end{macrocode} % \end{macro} % \begin{macro}{\DeclareCardType} % If no parent type is given, this just creates a wrapper for \cs{Card}. % To inherit from a parent, we just wrap said parent. % \begin{macrocode} \NewDocumentCommand\DeclareCardType{O{} m m}{ \IfBlankTF{#1}{ \expandafter\NewDocumentCommand\csname#2\endcsname{O{} m m}{ \Card[#3,##1]{##2}{##3} } }{ \expandafter\NewDocumentCommand\csname#2\endcsname{O{} m m}{ \csname#1\endcsname[#3,##1]{##2}{##3} } } } % \end{macrocode} % \end{macro} % \begin{macro}{\ShowCard} % If no argument is given, we access the last element of the global card list. % Otherwise, we reconstruct the name under which the card was saved by uid. % \begin{macrocode} \cs_generate_variant:Nn\typeset_card:nnn{onn} \NewDocumentCommand\ShowCard{ o }{ \IfNoValueTF{#1}{ \typeset_card:nnn{\clist_item:Nn\g_cards_clist{-1}}{}{} }{ \typeset_card:onn{card_by_uid @ #1}{}{} } } % \end{macrocode} % \end{macro} % \begin{macro}{\PrintCards} % \begin{macrocode} \NewDocumentCommand\PrintCards{ O{} }{ \print_cards:n{#1} } % \end{macrocode} % \end{macro} % \begin{macro}{\ListCards} % \begin{macrocode} \NewDocumentCommand\ListCards{ O{} }{ \list_cards:n{#1} } % \end{macrocode} % \end{macro} % \begin{macrocode} % % \end{macrocode} % % \end{implementation} % % \PrintChanges % \PrintIndex