The m4nfo User manual and Technical Report

Auxiliary Functions

Using auxiliary functions

label

Introduction

In m4nfo, 'auxiliary' functions are used inside the context of performance functions, most probably inside their parameter lists, to adjust their results or arrange for special parameters.

Scope Function Description
Blockengine/industry/townDenoting hierarchies in performance function blocks
Parameter list labelturn 4 char <Label> into a DWORD
little/lotsStation spriteset evaluation
load/moveVehicle spriteset evaluation
nibble/posPacking and unpacking coordinates
signedentering negative numbers using two's complement
subentering subroutine call
shiftmask/shiftmask_add_div/shiftmask_add_modadjusting performance function results

Description

engine/industry/town(<block>)

These functions define a hierarchy level. Most of TTD's features have a related or a "parent" object:

FeatureRelated objectFunction
_TRAIN/_ROADVEHICLE/_SHIP/_AIRCRAFTFirst vehicle of consist (if any)engine()
_STATIONTown to which this station belongstown()
_BRIDGETown of bridge
_HOUSETown of building
_INDUSTRYTILEIndustry containing this tileindustry()
_INDUSTRYTown to which this industry belongstown()
_OBJECTTown of object

I.e., "bracketing" a block in m4nfo by any of these functions means to address the features' related object instead of the current one. I.e., in case of feature _TRAIN, the use of engine() would query data not from the current vehicle inside the train's consist, but from the locomotive, and in case of a house, use of town() would query the town the house belongs to.

Example (check age of consist's engine):
def(6) yearbuilt(
	engine(
		ref(4) if(< 1922)
		ref(5) else
	)
)

signed(- <Byte>)

This function converts a negative number into its two's complement. To be used in function if(), in case the block function returns a negative result, e.g. station performance function plt_midpos():

Example (check age of consist's engine):
def(9) plt_midpos(
	self(
		ref(6) if(signed(-2))
		ref(7) if(signed(-1))
		ref(8) if(0)
		ref(9) if(1)
		ref(0) else
	)
)

label(<Label>)

This function is used to turn a 4 char label, e.g. a grf ID, into a Dword:

Example (check grf ID for adjacent station tile):
def(11) tinfo_grfid(pos(1,0),
	ref(1) if(label(6D 62 06 01)) // or 6D620601 or GRF_NEWSTATIONS
	ref(0) else
)

Chars of the label have to be given as ascii codes, not as real chars.

nibble(<Coordinate>)

This function is used to check a returned byte-packed coordinate value in the usual form (<x>, <y>). Parameter range is [-8 ... +7], both for x- and y-offsets. See example:

Example (check position of tile in object):
def(51) position(
 	ref(47) if(nibble(0,0)) // upper part
	ref(46) else		// lower part
)

pos(<Coordinate>)

This function supplies the given coordinates in a byte-packed form to another function. Parameter range is [-8 ... +7], both for x- and y-coordinates. Please note that the coordinate (0,0) refers to the current tile itself. See example:

Example (check type of neighbour (x = 0, y = -1) object tile):
def(1) objinfo_type(pos(0,-1),
	cbr(1) if(_MOLE,_QUAY,_QUAYEXT1 .. _QUAYEXT4)
	cbr(0) else
)

load(<List>)

This function is used to define a set of loading states for the vehicle. The numbers in the given list must reference sprite set numbers from a previous spriteblock() function. The numbers representing the graphics the vehicle should show while it's loading or unloading. For example, if it has two states, full or empty, this would need two parameters. If it has three states, full, half full or empty, this would need three.

move(<List>)

This function is used to define a set of moving states for the vehicle. The numbers in the given list must reference sprite set numbers from a prior spriteblock() function. The meaning is the same as above.

Example 1 (coach):
spriteset(move(0),load(0))

Example 2 (freight wagon):
spriteset(move(0,1,2,3),load(0,1,2,3))

Example 3 (freight wagon):
spriteset(move(0,9,11),load(0,8,10))

In example 1, the coach uses the same sprite (number 0, first sprite of spriteblock) both for its only moving state as for its only loading state. In example 2, the freight wagon uses 4 different sprites for its moving states, and it uses the same sprites for its loading states. In example 3, the freight wagon uses 3 different sprites for its moving states, and it uses 3 different sprites for its loading states.

little(<List>), lots(<List>)

These two auxiliary functions are used to define sets of loading states for station tiles. The numbers in the given lists must reference sprite set numbers from a previous spriteblock() function.

Likewise for vehicles, this decides the graphics set to use depending on the amount of cargo waiting. There are two caveats, though. Firstly, a station can have 12 different cargoes waiting, not just a single one. Secondly, the maximum amount is 4095 for all stations and all cargo types.

Regarding the first issue, the amount of cargo this refers to depends on the makestation() function that triggered this spriteset():

Regarding the second issue, TTDPatch introduces the distinction between "little" and "lots of" cargo. The amount where this occurs is set by property function threshold(), which is zero by default. But if set to a non-zero value, for example 200, then the range 0 to 199 is divided by <numlittlesets>, and the remaining range 200 to 4095 is divided by <numlotssets>. If threshold() is set to zero, the "little" sets are never used, and so <numlittlesets> may be zero. <numlotssets> must never be zero, however.

Example 1 (use graphics sprite set):
def(ALL_NORMAL) spriteset(little(0),lots(0))

Example 2 (pick callback results representing random sprites):
def(1) cbset(little(0,0,8,16,24,32,40,48),lots(56))
def(2) cbset(little(0,8,16,24,32,40,48,56),lots(64))
def(3) cbset(little(0,16,24,32,40,48,56,64),lots(64))
def(4) randomcb(__roofs, ARRIVE,16,ref(1),ref(2),ref(2),ref(3))

shiftmask(<shift>,<mask>)

This function adjusts a performance function result to a more useful range. Its first parameter defines the value to right-shift the result of the function, and the second parameter means the value with which to AND the result after shifting.

Please note that parameter <mask> might be of WORD-size in a given context. See example:

Example 1 (vehicle user bits):
// check if only bit 0 set (long-distance only)
def(6) getubits(shiftmask(0,UB_ALL),
	attach(ATT_OK) if(UB_F) // Fernverkehr
	ref(5) else
)

Example 2 (check tile's own slope):
def(8) tinfo_slope(pos(0,0),shiftmask(0,NORTH+SOUTH),
	ref(5) if(NORTH)
	ref(6) if(SOUTH)
	ref(7) else
)   

Example 3 (adjust animation counter result to randomly pause animation):
def(22) anim_counter(shiftmask(0,0x1FF),
	animframe(A_NEXT) if(0) // proceed
	animframe(1) else // stay in pause
)

shiftmask_add_div(<shift>,<mask>,<add>,<div>)

As above, this function adjusts a performance function result to a more useful range. Its first parameter defines the value to right-shift the result of the function, and the second parameter means the value with which to AND the result after shifting.

In addition, the third and fourth parameters define values to adjust the former result even further, by adding to and/or dividing it.

shiftmask_add_mod(<shift>,<mask>,<add>,<mod>)

Likewise, this function works as above, only by applying the modulo function instead division.