The m4nfo User manual and Technical Report

Handling callbacks

Basic informimgation about callbacks

Introduction

This is an introduction into the concept of callbacks and its general use. For applying callbacks in a certain feature-specific context (vehicles, stations, houses, industries, ...) see the appropriate pages (trains, ships, objects,...) for that very feature.

In addition to setting static feature properties by property functions in the various define*() functions, TTDPatch implements a second approach allowing to change feature properties dynamically in-game, depending on game-intrinsic functions or player interactions.

This is achieved by 'callbacks', special actions that TTDPatch 'calls' in order to modify various attributes of a feature (e.g., a train vehicle) previously defined.

This is done by identifying the incidence and the possible type of a callback in the diverse callback() functions. E.g., the callback() function for train vehicles allows changing of vehicle properties like length, capacity, load amount, power, speed, colour, sound, additional texts, etc.

In any way, depending on the type of the callback (CB_PROP in the illustration on the left), the chain of control is traversed backwards until a function which handles the callback by returning a valid 'callback result', most frequently by use of the cbr() function, or lets it fail. In the latter case, the value previously set by the appropriate property function will be kept using.

Be aware that a 'callback result' is different from a 'reference', the latter being used most of the time in grf-coding. In general, a callback result is either a value, replacing the default value from the appropriate property function (e.g., CB_RCAP changing the capacity of a vehicle), or it's sort of a BOOLEAN, allowing or disallowing something associated with that particular callback (e.g., CB_SLOPE checking if a certain type of slope would be fitting for placing a building or an industry).

In principal, a callback() function might handle every callback type separately. The 'else' branch in the callback() function's block taking every other result from the callback() function, be it a different callback type (and ztgus allow 'chaining' of callbacks) or a direct or indirect reference to a graphics sprite block.

So, if you need to handle more than one callback type, you need to handle every type separately by including appropriate references to their callback handling chains inside the callback() function's block. See example below.

Example (handling many callback types):
def(10) callback(
	cbr(5) if(CB_LOAD)	   // set loading amount = 5
	grftext(TSX_SGLM) if(CB_TSFX) // set text suffix
	ref(5) if(CB_PROP)	   // change property (capacity)
	ref(4) else		   // graphics  
)

If a callback goes unnoticed, by use of the 'else' statement, it will be passed to the graphics chain and automatically 'fail' there. E.g., if there was another callback type CB_WLEN in the illustration on the left, it would fail because it's not handled properly ending in a valid callback result.

In case a newGRF does not contain any graphics sprites which can be used as invalid callback results, function cbfail() can be used.

Description

Please note that in m4nfo, besides the usual cbr() function, there are more possibilities to return callback results. As can be seen in the example above, the grftext() function also returns a callback result, although this is a bit hidden to the user.

Other built-in functions in m4nfo returning valid callback results are:

Callback helper function Callback Description
cbr(<Word>)allgeneric callback result
cbfail()return invalid callback result
addveh(<Veh-ID>)CB_ARTIadd part vehicle to articulated vehicle
addvehrev(<Veh-ID>)add reversed part vehicle to articulated vehicle
attach(<error>)CB_ATACflag error message when attaching vehicle to consist
animcontrol(<Byte> | A_STOP | A_START | A_NOP [,<Sound-ID>])CB_ACONTROLstart/stop animation at certain frame [and play sound]
animframe(<Byte> | A_STOP | A_NOP [,<Sound-ID>])CB_AFRAMEset frame or stop animation [and play sound]
autorefit(<Word> [,%])CB_RCOSTflag autorefitting and set refit cost
customcargo(<CargoType>*3)CB_CARGOTYPESreturn 3 custom cargo
effect(<EffectType>)CB_POWRset effect type and position for vehicle
grftext(<Text-ID>), reftxtcb(<Label>)CB_TEXTreturn text-ID of previously defined text string
CB_TSFX
refsnd(<Sound-ID>)CB_SOUNDreturn sound-ID of previously defined sound
reftile(<Label>[+<Word>])CB_LAYOUTreturn tile number as callback result
seteffort(<Unit-term:tractive-effort>,<unit_term:weight>)CB_PROPreturn tractive effort value as callback result
setpower(<Unit-term:power>)return power value as callback result
setspeed(<Unit-term:speed>)return speed value as callback result
setengineprice(<Dword>)return engine price as callback result
setwagonprice(<Dword>)return coach / wagon price as callback result
setenginecost(<Dword>)return engine running cost as callback result
setwagoncost(<Dword>)return coach / wagon running cost as callback result
setlen(1/8 .. 8/8)CB_WLENreturn vehicle length as callback result

Please note that because m4nfo is based on a macro processor (M4), it's easy to define locally available constants, e.g. ALLOW or DISALLOW, which would boil down to a simple cbr(0) resp cbr(1), to be used for some special callback types, e.g. CB_SLOPE, when allowing or disallowing to place buildings on certain slope types. Or something else.