The m4nfo User manual and Technical Report

Global Functions

Using global functions

Introduction

Global functions in m4nfo may be used with all of TTD's features (vehicles, stations, houses, ...). They are used for querying actual game data inside a control chain:

Function Description
anim_counter([<shiftmask>,] <block>)General animation counter, incremented every game tick
calculate(<expression>, <block>)Evaluate expressions
cargotranslationtable(<List::Label>)Cargo translation table
getparameter(<parameter>, [<shiftmask>,] <block>)Read newGRF parameter
current_climate(<block>)Current climate (1 == TEMPERATE, 2 == ARCTIC, 4 == TROPIC, 8 == TOYLAND)
date(<block>)Current date (counted as days from 1920)
dayofmonth(<block>)Day of month (0 .. 30)
dayofyear(<block>)Day of year (0 .. 364, resp 0 .. 365)
game_level(<block>)Game difficulty level (0 == easy, 1 == medium, 2 == hard, 3 == custom)
game_mode(<block>)Game mode (0 == title screen, 1 == in game, 2 == in editor)
leapyear(<block>)Is leap year (1 == YES, 0 == NO)
month(<block>)Current month (jan == 1 .. dec == 12)
railtypetranslationtable(<List::Label>)Rail type translation table
snowline(<block>)Snow line height, 0xFF if snow isn't present at all
call(<reference>, <registers>, <block>) proc(<reference>, <head>, <block>) setregbase(<register>) subroutine(<reference>, <block>) Functions, procedures, subroutines
trafficside(<block>)Road traffic side (1 == right, 0 == left)
year(<block>)Current year, counted from 1920

Description

calculate(<expression>, <block>)

This function allows to evaluate expressions composed from performance functions, plain numbers and operators. It is used to combine several function results before making a decision.

Format

calculate(<expression>, <block>)
<expression> ::= "{" <term> <operator> <term> {<operator> <term>} "}"
<term> ::= <function> | <number>

Description

<operator> can have the following values:

Operator Description
+Addition a[*] + b
-Subtraction a - b
MINMinimum (a, b), unsigned
MAXMaximum (a, b), unsigned
SMINMinimum (a, b), signed
SMAXMaximum (a, b), signed
/Division a / b, unsigned
MODModulo a % b, unsigned
S/Division as above, signed
SMODModulo as above, signed
*Multiplication a * b, result will be truncated to Byte/Word/Dword
ANDBitwise a AND b
ORBitwise a OR b
XORBitwise a XOR b
RESTOREstore result and start fresh, included in store(), s.b.
CMPCompare; result is "0" if a < b, "1" if a == b and "2" if a > b, both values unsigned
SCMPDitto, but both values signed
RORRotate a by b positions to the right, always a 32-bit rotation
<<Shift a by b positions left, b should be in range 0 .. 31
>>Shift a by b positions right, unsigned; b should be in range 0 .. 31
S>>Shift a by b positions right, signed; b should be in the range 0 .. 31

[*] a is the value resulting from the current term and b is the value resulting from the following term.

<function> may be any non-custom[*] performance function, global functions subroutine() and getparameter(), or following special functions:

Function Description
store(<Byte>)store value in temporary register
register(<Byte>)access value from temporary register

There is no operator precedence, all terms in the expression are processed from left to right.

[*] E.g., see here for trains

Examples

Example 1 (vehicle aging):
def(0x20) calculate({date() - lastmaintenance() * 100 / servint()},
    engine(
	ref(16) if(0 .. 12)  // light: 00 .. 12%
	ref(17) if(13 .. 25) // normal: 13 .. 25%  
	ref(18) if(26 .. 50) // dark: 26 .. 50%
	ref(19) else	     // very dark: > 50%
    )
)
Example 2 (push/pull: checking foreign vehicles):
def(11) calculate({idcount(_A80D,) + idcount(_MR,) - veh_num()},
    engine(
	ref(8) if(1) // no foreign vehicles except trailer
	cbr(0) else  // foreign vehicles
    )
)
Example 3 (automatic change of light):
def(LIGHTS) calculate({veh_posabs(BACK,) SMIN 1 * 4 store(0)
  			veh_posabs(FRONT,) SMIN 1 * 2 OR register(0) store(0)
			veh_num() SMIN 1 OR register(0)},
    ref(5) if(0)       // single forward/backward	
    ref(6) if(5)       // head forward/backward
    ref(7) if(3)       // tail forward/backward
    ref(LI_OFF)	else   // no lights at all
)
Example 4 (setting callback result from newGRF parameter 1):
// LVST, BDMT, FICR := 150/300/450/600
def(0x74, __newcapacity) calculate({getparameter(1,) * 150},
	cbfail() else
)

...

// livestock, building materials, fibre crops
def(5) callback(
	ref(__newcapacity) if(CB_RCAP)
	cbr(15) if(CB_LOAD)
	ref(0) else
)

Note that <expression> has to be always quoted, and performance functions have to be given the correct number of parameters (excluding the <block> parameter), to avoid unneeded m4nfo error messages. See example 2.

getparameter(<parameter>, [<shiftmask>,] <block>)

This function reads a parameter of the current newGRF (if given). The result may be a Byte or Word value, and may be manipulated by auxiliary function shiftmask(). It is also available in the purchase list. See example 4 above.

Functions, procedures, subroutines

In m4nfo, two types of subroutine calls are supported: function subroutine() provides functions without parameters, and function call() provides parameterized functions. The reason for having two different types is efficiency, since supporting parameters is more expensive in terms of its underlying implementation, and most of the time, parameters are not needed at all for subroutines.

subroutine(<reference>, <block>)

This function constitutes a subroutine call, with the first parameter given being used as the subroutine's reference.

The subroutine itself (which might consist of a normal chain, or a special function) must deliver its result(s) as callback result(s) which may be used as normal values from the function block calling the subroutine, see examples.

Using subroutines is most useful inside a calculate() function, but it can be used as well in a profitable way from any point inside the chain of control.

Example (using subroutines):
define(SLOPE_WATER,200) // using a very high c-ID

// return values:
// 0 - flat, no slope 
// 1 - slope NW, no water in front
// 2 - slope SE, no water in front
// 3 - slope NW, water in front
// 4 - slope SE, water in front

def(10) tinfo_waterclass(position(0,1),
	cbr(1) if(WC_LAND) // no water in front
	cbr(3) else	   // water
)

def(11) tinfo_waterclass(position(0,-1),
	cbr(2) if(WC_LAND) // no water in back
	cbr(4) else	   // water
)

def(SLOPE_WATER) tinfo_slope(position(0,0),shiftmask(0,NORTH+WEST+SOUTH+EAST),
	ref(10) if(NORTH+WEST) // slope, check water tile front
	ref(11) if(SOUTH+EAST) // slope, check water tile back
	cbr(0) else	       // flat
)

...

def(1) subroutine(SLOPE_WATER,
	cbr(0) if(4) // access to water
	cbr(58) else // no water access, fence
)

call(<reference>, <registers>, <block>)

In m4nfo, parameterized functions/procedures are built on m4nfo's function calculate() and the use of registers. In fact, all to be used parameters are mapped to registers, which have to be specified beforehand by use of function setregbase(), defining the first register of the to be used range of registers.

Only performance functions with "real" parameters (with REGISTER as its alternative parameter; based on nfo 60+x vars) can be used like this in a reasonable way, see table below. Using performance functions without parameters, function proc() will fall back to its underlying functionality of calculate().

TTD Feature nfo var Functions
vehicles60idcount()
62veh_straight()
stations60cargowaiting()
66anim_frame()
67tinfo_flatwater(), ~grfid(), ~height(), ~lclass(), ~slope(), ~terrain(), ~water(), ~waterclass()
68tinfo_statid()
69cargohist()
houses61typecount(), classcount()
62houseinfo_height(), ~lclass(), ~slope(), ~terrain(), ~water(), ~waterclass()
63anim_frame()
64cargohist()
65findhouse()
66houseinfo_id(), houseinfo_class()
67houseinfo_grfid()
objects60objinfo_view(), ~type(), ~typeview()
61objinfo_randombits()
62objinfo_height(), ~lclass(), ~slope(), ~terrain(), ~water(), ~waterclass()

proc(<register>, <head>, <block>)

Any parameterized function/procedure is defined by function proc(), which is just a special wrapper for calculate(), and takes as its first parameter the register to be used for the function/procedure's parameter. The second parameter to proc() implements the function/procedure, i.e. the quoted set of instructions to execute, in the same way as for function calculate(). See examples below. The third parameter to proc() is the usual functional block.

Now, the parameterized functions/procedures can be called at a later time by function call(), its first parameter being the reference/name of the to be called functions/procedures, which might be chained in the usual way. The second parameter of call() is a list of parameter values to be mapped on the parameter(s) in question in a sequential way, see examples. Third parameter to call() is the usual functional block.

Likewise using subroutines, parameterized function/procedures must return its final results as callbacks, using function cbr().

setregbase(<register>)

This function defines the first one of registers to be used in a combination of proc()/call() functions. If more than one parameter is to be used (defined by function call()), registers will be defined as a consecutive range, starting with this very parameter.

Example (using 1 parameter):
setregbase(7) // registers to use: 7

def(1) proc(7, {tinfo_statid(REGISTER,) AND 0x800},
	cbr(2) if(0x800)
	cbr(1) else
)

def(2) proc(7, {tinfo_statid(REGISTER,) AND 0xFF},
	ref(1) if(MODULAR_LOW .. MODULAR_HIGH)
	cbr(0) else
)

def(3, building_nearby) proc(7, {tinfo_grfid(REGISTER,) AND 0xFF},
	ref(2) if(label(GRFID_PART1))
	cbr(0) else
)

def(4) call(building_nearby, 0xFF,
	cbr(7) if(1, 2)
	cbr(8) else
)

Example (using 2 parameters):
setregbase(7) // registers to use: 7, 8

def(8) proc(7, {anim_frame(REGISTER,)},
	cbr(0) if(0)
	cbr(1) else
)

def(9, FRAME) proc(8, {tinfo_tracktype() AND tinfo_terrain(REGISTER,)},
	ref(8) if0)
	cbr(1) else
)

def(10) call(FRAME, {pos(-1,1), SNOW},
	ref(1) if(0)
	ref(2) if(1)
	ref(3) else
)