The m4nfo User manual and Technical Report

Templating

Defining templates for sprite graphics and m4nfo code

Content



Introduction

Templating in m4nfo is straightforward and needs no additional tools. Because m4nfo itself is implemented as a collection of M4 macros, it is both easy and efficient for the user to add his own macros, e.g. for templating purposes.

Using templates would be especially useful either for graphics sprite definition but also for substitution of (repetitive) code blocks. For the first task, m4nfo comes with a set of pre-implemented functions, the latter can be easily achieved by defining own macros.

Graphics templating

Graphics templating aims at simplifying repetitive real sprite (graphics) arrangements, usually inside the spriteblock() function.

Properties

Function Description
template(<template_name>, <file_name>, x(<list>), y(<word>)) defines graphics sprite block to be used
x(<list>) specifies x coordinates of sprites
y(<word>) specifies y coordinate of sprites

Description

template(<template_name>,<file_name>x(<list>),y(<word>))

This function creates a block of graphics sprites from the given file name and coordinates by using the specified template.

For vehicles, function x() either specifies the 4 or 8 x coordinates of the sprites, and function y() addresses the sprites' y coordinate.

Example (using sprite templates):
     template({TEMP4_normal},db#ayse.pcx,x(66,82,114,146),y(56)) 
expands into:
     sprite(db#ayse.pcx 66 56 01 23 8 -3 -14)
     sprite(db#ayse.pcx 82 56 09 16 21 -14 -8)
     sprite(db#ayse.pcx 114 56 01 12 32 -15 -8)
     sprite(db#ayse.pcx 146 56 09 16 21 -5 -8)

The templates themselves are simple M4 macros. The one used above would look like this and would be put into an extra include file, probably named "templates.m4" or similar:

Example (defining sprite templates):
define(TEMP4_normal,{
     sprite($1 $2 $6 01 23 8 -3 -14)
     sprite($1 $3 $6 09 16 21 -14 -8)
     sprite($1 $4 $6 01 12 32 -15 -8)
     sprite($1 $5 $6 09 16 21 -5 -8)}
)

Code templating

Introduction

Code templating in m4nfo is even more straightforward than graphics templating, because you don't even need to use any pre-defined functions.

Instead, code templating is easily achieved by defining own M4 macros.

Function templating

Consider the need to template the livery change of the German DB over the years. From the early 1950s to 1974 most of the electrics were painted green, from 1974 to 1986 they were painted in ocean-blue/cream, from 1986 to 1996 they were painted in "orient red" and from 1997 until today, they're in "traffic red".

So, instead of having to write something like the code below for almost all of the DB electrics in a set,

Example (defining sprite templates):
// engine 1
def(0) yearbuilt(
	ref(1) if(1950 .. 1974) // green
	ref(2) if(1975 .. 1986) // blue/cream
	ref(3) if(1987 .. 1996) // orient red
	ref(4) else		// traffic red
)

...

// engine 2
def(1) yearbuilt(
	ref(5) if(1950 .. 1974) // green
	ref(6) if(1975 .. 1986) // blue/cream
	ref(7) if(1987 .. 1996) // orient red
	ref(8) else		// traffic red
)

...

// engine 3
def(2) yearbuilt(
	ref(9) if(1950 .. 1974) // green
	ref(10) if(1975 .. 1986) // blue/cream
	ref(11) if(1987 .. 1996) // orient red
	ref(12) else		// traffic red
)

you could define a macro "DB_yearswitch()" which simplifies the source to something like this:

Example (defining private macro):
define(DB_yearswitch,{
	$1 if(1950 .. 1974)
	$2 if(1975 .. 1986)
	$3 if(1987 .. 1996)
	$4 else}
)
Example (usage of private macro):
// engine 1
def(0) yearbuilt(
	DB_yearswitch(ref(1), ref(2), ref(3), ref(4))
)

...

// engine 2
def(1) yearbuilt(
	DB_yearswitch(ref(5), ref(6), ref(7), ref(8))
)

...

// engine 3
def(2) yearbuilt(
	DB_yearswitch(ref(9), ref(10), ref(11), ref(12))
)

As mentioned above, these "private" macros would be put best into their own include file(s).

Especially when coding freight wagons, it would need lots of callbacks to set capacities, load amounts and/or cargo subtype texts for the different freight types. So, instead of having to write something like the code below

Example (exchanging frequent callbacks by macros):
// WDPR
def(8) property(
	cbr(5) if(loadamount())
	cbfail()
)

def(9) callback(
	cbr(15) if(CB_RCAP) // 15 t
	grftext(TSF_PLYW) if(CB_TSFX) 
	ref(8) if(CB_PROP) // 3 ticks
	ref(13) else // graphics
)

for every cargo, setting cargo capacity, load amount and cargo subtype text, for every freight car, this can be easily replaced by using a macro "setload()":

Example (exchanging frequent callbacks by macros):
// setload(<capacity>, <num-loadamount-ticks>, [<cargo-subtext>], <ref>)

def(9) setload(15 t, 3 ticks, TSF_PLYW, ref(13))

Here is the macro:

Example (exchanging frequent callbacks by macros):
define(__firstarg,{$1})
define(__number,{__firstarg(patsubst($1,{ +},{,}))})

define(setload,{dnl
property(
	cbr(__number($1) / __number($2)) if(loadamount())
	cbfail()
)
def(__lastdef) callback(
	cbr(__number($1)) if(CB_RCAP) // load
	ref(__lastdef) if(CB_PROP)  // ticks
ifelse($#,4,{grftext($3) if(CB_TSFX)
	$4},$3)
)})

The first two helper functions are used to extract the real numbers from the parameters to setload(). The setload() macro itself computes the load amount per given "tick", then writes a second def() with the needed callback handling. Note that it also checks for the cargo subtype callback, whether to be used or not.

String templating

It's also quite easy to replace text strings by macros and thus keep all strings of some distributed source files in a single "string file" (resp. a "language file").

It's even possible to replace not only single strings but complete parameter structures containing strings for most of m4nfo's functions.

So, instead of using function stationnames() explicitly in the source files:

Example (defining station names):
stationnames(FREIBURG,
	{US,"Freiburg (back view)", "Freiburg (front view)"},
	{D, UTF8 "Freiburg (Rückansicht)", "Freiburg (Vorderansicht)"},
	{F, UTF8 "Freiburg (vue de l'arrière)", "Freiburg (vue de face)"},
	{E, UTF8 "Freiburg (vista de atrás)", "Freiburg (vista frontal)"},
	{I, "Freiburg (vista di dietro)", "Freiburg(vista di fronte)"},
	{NL, "Freiburg (achterpaneel)", "Freiburg (vooranzicht)"}	
)

the text strings might be placed in some file "strings.nfx":

Example (defining text string macros):
define(STR_STAT_FREIBURG,{
	{US,"Freiburg (back view)", "Freiburg (front view)"},
	{D, UTF8 "Freiburg (Rückansicht)", "Freiburg (Vorderansicht)"},
	{F, UTF8 "Freiburg (vue de l'arrière)", "Freiburg (vue de face)"},
	{E, UTF8 "Freiburg (vista de atrás)", "Freiburg (vista frontal)"},
	{I, "Freiburg (posteriore)", "Freiburg(vista di fronte)"},
	{NL, "Freiburg (achterpaneel)", "Freiburg (vooranzicht)"}}	
)

(please note the extra quoting!), and thus be used (probably together with other strings) in any ~.nfx file like this:

Example (using external text strings):
include(strings.nfx)

...

stationnames(FREIBURG, STR_STAT_FREIBURG)

Iterative templating

Using M4 macro wizardry, even more flexible and more powerful templating can be achieved. Consider iterative function

forloop(<variable>, <m4nfo-expression>, <arguments>)

which is part of m4nfo (i.e. you don't need to define it yourself). This function writes out <m4nfo-expression> for each of its arguments, given either as a range (1 .. 3), or a list (1,2,3). <variable> specifies the symbol used in <m4nfo-expression> to be replaced by the current arguments, with <variable> itself might be used inside an algebraic expression.

Then, the following code snippet:

Example (m4nfo source to be reduced by private macro):
// connection with mole, front
def(40) spriteset(normal(WATER), normal(14), xy(0,0), dxdydz(16,16,6))
def(42) spriteset(normal(WATER), normal(16), xy(0,0), dxdydz(16,16,6))
def(44) spriteset(normal(WATER), normal(18), xy(0,0), dxdydz(16,16,6))

could be easily reduced by introducing above function:

Example (usage of private macro):
forloop(X, {def(40+X) spriteset(normal(WATER), normal(14+X), xy(0,0), dxdydz(16,16,6))
}, 0,2,4)

Another example shows how to reduce lengthy definitions of spritesets for rail cars:

Example:
def(0) spriteset(move(0),load(1,2,3))
def(1) spriteset(move(4),load(5,6,7))
def(2) spriteset(move(8),load(9,10,11))
def(3) spriteset(move(12),load(13,14,15))
def(4) spriteset(move(16),load(17,18,19))
def(5) spriteset(move(20),load(21,22,23))
def(6) spriteset(move(24),load(25,26,27))
def(7) spriteset(move(28),load(29,30,31))

With

Example:
forloop(X, {def(X) spriteset(move(4*X),load(4*X+1,4*X+2,4*X+3))
}, 0 .. 7)

Doing exactly the same.

Of course, it is also possible to use forloop() on named spritesets:

Example:
forloop(X,{def(0+X) spriteset(move(__oldred+X),load(__oldred+X))
}, 0 .. 3)

Yet another example how to reduce lengthy animation sequences:

Example: station tile animation
forloop(X,{
    tile(
      ground(GRASS)
      normal(69, xyz(1,1,0),dxdydz(8,16,40)) // building
      normal(X, xyz(0,14,40),dxdydz(16,15,16), TTD) // smoke
     )
    tile(
      ground(GRASS)
      normal(68, xyz(1,1,0),dxdydz(16,8,40)) // building
      normal(X, xyz(14,0,40),dxdydz(16,15,16), TTD) // smoke
     )
},0xE75 .. 0xE7C)

This forloop produces code for a station depot boiler house, using the original TTD 'smoke' sprites (0xE75 .. 0xE7C) for each pair of x/y tiles.

Template templating

It is also possible to "template" templates (s.a.), i.e. using macros on macros, e.g.

Example:
forloop(X, {set(template({NG_A80D},remorque.pcx,x(LAYOUT_STANDARD),y(X*SPACING_LOCOS+10)))
}, 0 .. 16)

with LAYOUT_STANDARD = {10,26,58,100,128,144,176,218} and SPACING_LOCOS = 30, these macros will generate a huge block of real sprites:

Example:
    sprites/remorque.pcx 10 10 01 15 6 -2 -11 
    sprites/remorque.pcx 26 10 09 12 15 -9 -6 
    sprites/remorque.pcx 58 10 01 11 21 -3 -7 
    sprites/remorque.pcx 100 10 09 12 15 -2 -5 
    sprites/remorque.pcx 128 10 01 15 6 -2 -5 
    sprites/remorque.pcx 144 10 09 12 15 -11 -5 
    sprites/remorque.pcx 176 10 01 11 21 -16 -7 
    sprites/remorque.pcx 218 10 09 12 15 -4 -6 
 
    sprites/remorque.pcx 10 40 01 15 6 -2 -11 
    sprites/remorque.pcx 26 40 09 12 15 -9 -6 
    sprites/remorque.pcx 58 40 01 11 21 -3 -7 
    sprites/remorque.pcx 100 40 09 12 15 -2 -5 
    sprites/remorque.pcx 128 40 01 15 6 -2 -5 
    sprites/remorque.pcx 144 40 09 12 15 -11 -5 
    sprites/remorque.pcx 176 40 01 11 21 -16 -7 
    sprites/remorque.pcx 218 40 09 12 15 -4 -6

   ...

    sprites/remorque.pcx 10 490 01 15 6 -2 -11 
    sprites/remorque.pcx 26 490 09 12 15 -9 -6 
    sprites/remorque.pcx 58 490 01 11 21 -3 -7 
    sprites/remorque.pcx 100 490 09 12 15 -2 -5 
    sprites/remorque.pcx 128 490 01 15 6 -2 -5 
    sprites/remorque.pcx 144 490 09 12 15 -11 -5 
    sprites/remorque.pcx 176 490 01 11 21 -16 -7 
    sprites/remorque.pcx 218 490 09 12 15 -4 -6