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 |
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.
<operator> can have the following values:
Operator | Description |
+ | Addition a[*] + b |
- | Subtraction a - b |
MIN | Minimum (a, b), unsigned |
MAX | Maximum (a, b), unsigned |
SMIN | Minimum (a, b), signed |
SMAX | Maximum (a, b), signed |
/ | Division a / b, unsigned |
MOD | Modulo a % b, unsigned |
S/ | Division as above, signed |
SMOD | Modulo as above, signed |
* | Multiplication a * b, result will be truncated to Byte/Word/Dword |
AND | Bitwise a AND b |
OR | Bitwise a OR b |
XOR | Bitwise a XOR b |
RESTORE | store result and start fresh, included in store(), s.b. |
CMP | Compare; result is "0" if a < b, "1" if a == b and "2" if a > b, both values unsigned |
SCMP | Ditto, but both values signed |
ROR | Rotate 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
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.
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 |
vehicles | 60 | idcount() |
62 | veh_straight() | |
stations | 60 | cargowaiting() |
66 | anim_frame() | |
67 | tinfo_flatwater(), ~grfid(), ~height(), ~lclass(), ~slope(), ~terrain(), ~water(), ~waterclass() | |
68 | tinfo_statid() | |
69 | cargohist() | |
houses | 61 | typecount(), classcount() |
62 | houseinfo_height(), ~lclass(), ~slope(), ~terrain(), ~water(), ~waterclass() | |
63 | anim_frame() | |
64 | cargohist() | |
65 | findhouse() | |
66 | houseinfo_id(), houseinfo_class() | |
67 | houseinfo_grfid() | |
objects | 60 | objinfo_view(), ~type(), ~typeview() |
61 | objinfo_randombits() | |
62 | objinfo_height(), ~lclass(), ~slope(), ~terrain(), ~water(), ~waterclass() |
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().
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.
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 )
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 )