[Tex/LaTex] Making cards for Magic: The Gathering and similar card games

funtikz-pgf

I'm creating cards for a card game. They will be similar to cards in Magic: The Gathering (left-hand image below). What I have is in the right-hand image below.

fireball
fury

The problem is that the code doesn't generate the image in a modifiable/maintainable way, in the following respect. The image essentially consists of four juxtaposed blocks of varying shapes ('Fury', the white square, 'Persistent' and the final text square). There is then a red border (as it happens, with a drop shadow) drawn around these blocks and along the boundaries between them. At the moment I am manually specifying the path of the border.

Now, I want to be able to change the number and position of the various blocks (for different types of cards) and have the border automatically adapt. It seems that it might be possible to do this by taking the union of the blocks, and 'expanding' it a little to form a red object which sits behind the blocks. So something like a drop shadow operation, but more complex. (Then that red object would itself need a drop shadow.)

Is this feasible?

NB. Any other visual improvements would be gratefully received!

Edit: I've realised that drawing the 4 boxes with thick red lines at the borders might have the desired effect. Or more exactly, I need the following in order:

  1. the drop shadows (thickened lines, black, translated) and (thickened lines, white, translated)
  2. the border (thickened lines, red)
  3. the original material.

A key problem is that applying 1,2,3 for the first block, then the second, etc. will fail — all step 1s need to happen before all step 2s, and so on.

Is it possible to make a style that does this?

Code:

\documentclass[a4paper,11pt]{article}
\usepackage[T1]{fontenc}
\usepackage[margin=2mm]{geometry}
\usepackage{tikz,nopageno}
\usepackage{wasysym}
\usepackage{expl3}
\usepackage{ifthen}
\usepackage{etoolbox}
\usepackage{color}
\usepackage{blindtext}
%\usepackage{contour}

\usepackage{libertine}
\usepackage{multicol}


\ExplSyntaxOn
\cs_new_eq:NN \Repeat \prg_replicate:nn
\ExplSyntaxOff

%\renewcommand\Repeat[2]{#2}

\setlength\parskip{0mm}
\setlength\parindent{0mm}

\usetikzlibrary{shadows}

\newcommand{\rounding}{3mm}
\newcommand{\cardheight}{88mm}
\newcommand{\cardwidth}{63mm}
\newcommand{\backgroundthickness}{5mm}
\newcommand{\framethickness}{0.6mm}
\newcommand{\attbartop}{42mm}
\newcommand{\attbarbottom}{35mm}
\newcommand{\topbarbottom}{76mm}

\begin{document}

\newcommand{\highlight}[2]{%
\begin{tikzpicture}[baseline = (text.base)]
  \node[inner sep=0pt] (text) {#2};
  \begin{pgfinterruptboundingbox}
    \begin{pgfonlayer}{background}
    \node[fit=(text), rounded corners, fill=#1, draw=none] {};
    \end{pgfonlayer}
  \end{pgfinterruptboundingbox}
\end{tikzpicture}%
}

\tikzset{path image/.style={
path picture={
\node at (path picture bounding box.center) {
\includegraphics[height=90mm]{#1}
};}}}

\tikzset{double shadow/.style={
drop shadow={shadow xshift=-0.5mm,shadow yshift=-0.5mm,black},
preaction={drop shadow={shadow xshift=0.3mm,shadow yshift=0.3mm,white!70!black}}
}}

\newcommand{\card}[7]{%
\begin{tikzpicture}
    \fill[black,rounded corners=\rounding] (0,0) rectangle (\cardwidth,\cardheight); 

    %\draw[path image=texture_#1.png,rounded corners=1mm] (\rounding,\rounding) rectangle (\cardwidth-\rounding,\cardheight-\rounding);

    \fill[#1!75!black, %Draw red frame
          double shadow]
       (\backgroundthickness,\backgroundthickness) 
         -- (\backgroundthickness,\attbarbottom) 
         to[bend left] (\backgroundthickness,\attbartop) 
         -- (\backgroundthickness,\topbarbottom) 
         to[bend left] (\backgroundthickness,\cardheight-\backgroundthickness) 
         -- (\cardwidth-\backgroundthickness,\cardheight-\backgroundthickness) 
         to[bend left] (\cardwidth-\backgroundthickness,\topbarbottom) 
         -- (\cardwidth-\backgroundthickness,\attbartop) 
         to[bend left] (\cardwidth-\backgroundthickness,\attbarbottom) 
         -- (\cardwidth-\backgroundthickness,\backgroundthickness) 
         -- cycle;

    \fill[#1!10,drop shadow={shadow xshift=-0.2mm,shadow yshift=0.3mm,black}] %Draw text box
        (\backgroundthickness+\framethickness,\backgroundthickness+\framethickness) rectangle 
        (\cardwidth-\backgroundthickness-\framethickness,\attbarbottom);

    \draw[black,thick,fill=#1!40] %Draw center bar
         (\backgroundthickness+\framethickness,\attbarbottom+\framethickness) 
         to[bend left] (\backgroundthickness+\framethickness,\attbartop-\framethickness) 
         -- (\cardwidth-\backgroundthickness-\framethickness,\attbartop-\framethickness) 
         to[bend left] (\cardwidth-\backgroundthickness-\framethickness,\attbarbottom+\framethickness) 
         -- cycle;

    \draw[black,thick,fill=white] %Draw Image placeholder
        (\backgroundthickness+\framethickness,\attbartop) rectangle 
        (\cardwidth-\backgroundthickness-\framethickness,\topbarbottom);

    \draw[black,thick,fill=#1!40] %Draw top bar
         (\backgroundthickness+\framethickness,\topbarbottom+\framethickness) 
         to[bend left] (\backgroundthickness+\framethickness,\cardheight-\backgroundthickness-\framethickness) 
         -- (\cardwidth-\backgroundthickness-\framethickness,\cardheight-\backgroundthickness-\framethickness) 
         to[bend left] (\cardwidth-\backgroundthickness-\framethickness,\topbarbottom+\framethickness) 
         -- cycle;

    \node[anchor=south west] at (\backgroundthickness,\topbarbottom+1mm) {\raisebox{0pt}[\height][0pt]{\bf #2}};
    \node[anchor=east] at (\cardwidth-\backgroundthickness-\framethickness+0.5mm,\topbarbottom+3.5mm) {\raisebox{0pt}[\height][0pt]{\bf #3}};
    \node[anchor=south west] at (\backgroundthickness,\attbarbottom+1mm) {\raisebox{0pt}[\height][0pt]{#5}};    

    \node[anchor=north west,text width=\cardwidth-2*\backgroundthickness-2*\framethickness-2mm,font=\footnotesize] at (\backgroundthickness+\framethickness,\attbarbottom-0.5mm) {#6};

    \node[anchor=south east,font=\tiny] at (\cardwidth-\backgroundthickness-\framethickness,\backgroundthickness+\framethickness) {\raisebox{0pt}[\height][0pt]{\bf Tier #7}};

\end{tikzpicture}}

\newcommand{\cost}[2]{%
\begin{tikzpicture}
    \node[fill=#1!75!black,shape=circle,inner sep=0.3mm,draw=black,text=white,text width={}] (TEXT) {\raisebox{0pt}[\height][0pt]{\bf #2}};
\end{tikzpicture}}

\newcommand{\inlinecost}[2]{%
\begin{tikzpicture}[baseline={(TEXT.base)}]
    \node[fill=#1!75!black,shape=circle,inner sep=0.3mm,draw=black,text=white,text width={}] (TEXT) {\raisebox{0pt}[\height][0pt]{\bf #2}};
\end{tikzpicture}}

\card{red}{Fury}{\cost{red}{1}}{}{Persistent}{You gain a \textbf{Bonus} of 1 to all melee attack rolls.\\\  \\Effect ends when a double is rolled on a melee attack.\\\  \\\textit{Bonuses do not stack.}}{1}

\end{document}

(I've commented out a line that uses a red texture, but just in case: it's here and must be renamed to texture_red.png.)

Best Answer

Since the question is "Is this feasible?" not "How do I do this thing specifically?", my answer relates to the effort/quality/time tradeoffs your situation presents.

If there are going to be a limited number of cases (i.e., only four total variations of this format), you may find better-looking results by hardcoding each of the cases and defining each of them as a particular new environment that accepts your title, cost, type, and text as inputs. Hardcoding on a short list of cases has the added benefit of saving you time if the solution to this automatic-adjustment case is as truculent as it is likely to be.

If however there are going to be a very large number of cases (e.g., fitting the size of the text block to the text dynamically for every individual card or resizing the image block based on the size or aspect ratio of the art), then you're on the right track, I suppose, and you may wish to consider coding each segment of the outer boundary as its own piece with length parameters set at generation (or even potentially generating the image segments in a programming language better suited to parametric image generation).