4

I'm making an rpg system. I want to separate the contents of the spell from the formatting of the spell to, for example, be able to format the spells differently for character sheets vs a dedicated spell document, or whatever.

I'm trying to use a separate file that has arguments for a formatting macro (\spell) arranged in a predefined fashion that then would get inputed into the document.

This is what I tried.

\documentclass{article}

\newcommand{\spell}[4]{
\noindent   \textbf{#1}
\\  \underline{#2}
\\  \textit{#3}
\\  #4
}

\newcommand{\info}{
{SPELL NAME}
{MANA COST}
{CASTING TIME}
{DESCRIPTION}
}

\begin{document}
    \input{spells/spell template.tex}

    \spell{spell name}{mana cost}{casting time}{description}

    \expandafter\spell\info

%   \expandafter\spell\input{spells/spell template.tex}
\end{document}

spells/spell template.tex contains

{Spell Name}
{Mana Cost}
{Casting Time}
{Description}

The first three tests I have in this file work perfectly fine but when I try to uncomment the fourth one latex crashes. I have read that \input isn't expandable but I haven't seen any solutions that would solve this problem. This post looked promising but seems to be outdated (https://tex.stackexchange.com/a/282602/301785). Any help would be appreciated thanks.

5
  • A simple workaround would be, to write the command \spell directly into the file. Maybe you have later another version of that command, and then it would also be useful to differentiate from within the single files.
    – dexteritas
    Oct 14, 2023 at 19:34
  • @dexteritas if \spell is only a single example for a formatting macro and there are others, this does make sense to decouple.
    – Skillmon
    Oct 14, 2023 at 19:40
  • @Skillmon Ah, I see. Then you don't need to redefine the command.
    – dexteritas
    Oct 14, 2023 at 19:43
  • Yeah \spell is only one example.
    – Amit
    Oct 14, 2023 at 21:49
  • Just a random note: If you intend to use files programmatically you should avoid spaces in file names. Those tend to result in interesting behaviour of ad-hoc scripts and the like.
    – Skillmon
    Oct 15, 2023 at 15:03

4 Answers 4

2

Assuming you need the behaviour to pick arguments from a file for more than just \spell, the following defines a helper macro \FromFile that takes as first argument the code after which the file contents should be inserted, and as second argument the file path.

\documentclass{article}

\begin{filecontents}{spell_template.tex}
{SPELL NAME}
{MANA COST}
{CASTING TIME}
{DESCRIPTION}
\end{filecontents}

\newcommand{\spell}[4]{%
  \noindent   \textbf{#1}
  \\  \underline{#2}
  \\  \textit{#3}
  \\  #4%
}

\ExplSyntaxOn
\msg_new:nnn { amit } { file-not-found }
  { File~ #1~ not~ found.~ RPG-element~ not~ defined! }
\NewDocumentCommand \FromFile { m m }
  {
    \file_get:nnNTF {#2} {} \l_tmpa_tl
      { \exp_last_unbraced:Nno \use:n {#1} \l_tmpa_tl }
      { \msg_error:nnn { amit } { file-not-found } {#2} }
  }
\ExplSyntaxOff

\newcommand\info
  {%
    {SPELL NAME}%
    {MANA COST}%
    {CASTING TIME}%
    {DESCRIPTION}%
  }

\begin{document}
    \input{spell_template.tex}

    \spell{spell name}{mana cost}{casting time}{description}

    \expandafter\spell\info

   \FromFile\spell{spell_template.tex}
\end{document}

If treating leading and trailing spaces is important, one could use the following variant:

\documentclass{article}

\begin{filecontents}{spell_template.tex}
{SPELL NAME}
{MANA COST}
{CASTING TIME}
{DESCRIPTION}
\end{filecontents}

\newcommand{\spell}[4]{%
  \noindent   \textbf{#1}
  \\  \underline{#2}
  \\  \textit{#3}
  \\  #4%
}

\ExplSyntaxOn
\msg_new:nnn { amit } { file-not-found }
  { File~ #1~ not~ found.~ RPG-element~ not~ defined! }
\NewDocumentCommand \FromFile { m m }
  {
    \file_get:nnNTF {#2} {} \l_tmpa_tl
      { \use:e { \exp_not:n {#1} \tl_trim_spaces:o \l_tmpa_tl } }
      { \msg_error:nnn { amit } { file-not-found } {#2} }
  }
\ExplSyntaxOff

\newcommand\info
  {%
    {SPELL NAME}%
    {MANA COST}%
    {CASTING TIME}%
    {DESCRIPTION}%
  }

\begin{document}
    \input{spell_template.tex}

    \spell{spell name}{mana cost}{casting time}{description}

    \expandafter\spell\info

   \FromFile\spell{spell_template.tex}
\end{document}
1
  • Thank you this was perfect!
    – Amit
    Oct 14, 2023 at 22:02
5

LaTeX hides TeX primitive \input into the control sequence \@@input. You can set the equivalent command \primitiveinput by:

{\catcode`\@=11 \global\let\primitiveinput=\@@input}

and then you can say

\expandafter\spell\primitiveinput{spells/spell-template.tex}

Edit: the accepted answer implements a \FromFile macro which tests in the given file exists. I can show such a implementation using OpTeX just for comparison

\fontfam[lm]

\def\spell#1#2#3#4{1:#1, 2:#2, 3:#3, 4:#4}

\def\FromFile#1#2{%
    \isfile{#2}\iftrue \ea#1\input{#2}%
    \else \opwarning{File "#2" not found, \string#1 not executed}\fi
}

\FromFile\spell{template.tex}

\bye
1

This answer is similar to the one wipet gave. The idea is to use the primitive meaning of \input. With XeTeX or LuaTeX you can use the \primitive primitive for that (in pdfTeX it is called \pdfprimitive).

\documentclass{article}

\begin{filecontents}{spell_template.tex}
    {SPELL NAME}
    {MANA COST}
    {CASTING TIME}
    {DESCRIPTION}
\end{filecontents}

\newcommand{\spell}[4]{
    \noindent   \textbf{#1}
    \\  \underline{#2}
    \\  \textit{#3}
    \\  #4
}

\newcommand{\info}{
    {SPELL NAME}
    {MANA COST}
    {CASTING TIME}
    {DESCRIPTION}
}

\begin{document}
    \input{spell_template.tex}
    
    \spell{spell name}{mana cost}{casting time}{description}
    
    \expandafter\spell\info
    
    \expandafter\spell\primitive\input spell_template.tex
\end{document}
0

This is a variant which attempts to take care of spaces and other things in the file trailing the four arguments.

\documentclass{article}

\begin{filecontents*}{template.tex}
{SPELL NAME}
{MANA COST}
{CASTING TIME}
{DESCRIPTION} Something that gets ignored.
Something that gets ignored, too.
\end{filecontents*}

\newcommand{\spell}[4]{%
  \par
  \noindent\textbf{#1}\\%
  \underline{#2}\\%
  \textit{#3}\\%
  #4%
}%

\newcommand\spellGetArguments[4]{\spellRemoveToEndOfLine{\spell{#1}{#2}{#3}{#4}}}%
\newcommand\SpellSwitchEndlinecharcategory{\catcode\endlinechar=12 }%
\makeatletter
\begingroup
\catcode`\^^M=12 %
\@firstofone{%
  \endgroup%
  \@ifdefinable\spellRemoveToEndOfLine{%
    \long\def\spellRemoveToEndOfLine#1#2^^M{\endgroup#1}%
  }%
}%
\newcommand\spellinput[1]{%
  \begingroup
  \expandafter\expandafter\expandafter\SpellSwitchEndlinecharcategory
  \expandafter\expandafter\expandafter\endinput
  \expandafter\spellGetArguments\@@input"#1" %
}%
\makeatother

\begin{document}

\spellinput{template.tex} Space.

\bigskip

\spellinput{template.tex}NoSpace.

\end{document}

enter image description here

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .