The m4nfo User manual and Technical Report

Industry tile callbacks

Using callbacks for industry tiles

Introduction

This is an in-depth description of callbacks for industry tiles. More basic information about callbacks can be found here.

A 'callback' is a special action that TTDPatch 'calls' in order to modify various attributes of a feature (e.g., an industry tile) previously defined. An example being the control of animation frames or the manipulation of accepted cargoes.

This is done by identifying the incidence and the possible type of a callback in the callback() function. Depending on the type of the callback, control is handed over eventually to a function which handles the callback, or lets it fail. In the latter case, the value previously set by the appropriate property function will be kept using.

In addition, all animation callbacks also allow to trigger sound effects by returning a sound-ID value in the high byte of the callback result. This could be achieved in m4nfo by shifting the sound-ID value 8 bits to the left and OR-ing it with the appropriate animation state value, like this: cbr(eval((<sound_ID> <<8) | <animstate>)).

Instead, one of m4nfo's helper functions could be used, supplying a second parameter (in addition to the callback result) which will be interpreted as a sound effect ID, like this: animcontrol(0, SND_HORN) if(CB_ACONTROL). The corresponding sound effect will then be played on the appropriate house tile, according to the state of the corresponding animation.

Here's is a list of all available callbacks for ndustry tiles:

Callback Description
CB_ACONTROLPeriodically start/stop the animation
CB_AFRAMEDecide next animation frame
CB_ASPEEDDecide animation speed
CB_AUTOSLOPEAllow/disallow autosloping
CB_BUILDCheck if tile can be built
CB_CARGOAMOUNTSDecide the cargo amounts accepted
CB_CARGOTYPESDecide the cargo types accepted
CB_FOUNDATIONDecide if default foundations need to be drawn

Description

CB_ACONTROL - periodically start/stop the animation

This callback is always used when defined, it needs no activation in property function callbacks(). It is called periodically, when an animation trigger happens, and returns the number of the frame the animation should jump to, or one of the following special values:

ValueLabelMeaning
255A_STOPstop animation in its current frame
254A_STARTstart animation with its current frame
253A_NOPleave the animation in its current state (do nothing)

Callback values have to be specified as usual by cbr(), or by using the helper function animcontrol().

Function xxx (the low byte of variable 18) contains the reason (animation trigger) for the call. The callback gets random bits retruned by function randombits() to allow randomizing changes.

Randomness depends on the type of the event. For triggers that happen for the whole industry ( SYNCHRON, CARGO_IN and CARGO_OUT), the high 16 bits will be the same for all tiles, while the low 16 bits will be different. For other triggers, all 32 bits are independent.

CB_AFRAME - set next animation frame

Called in every animation frame, this callback returns the number of the next frame to display. Additionally, it can return these special values:

ValueLabelMeaning
255A_STOPstop animation. The current frame stays on screen until the animation is restarted.
254A_NOPcontinue with next frame as usual. You can return this for stages where you don't want to do anything special.

Callback values have to be specified as usual by cbr(), or by using the helper function animframe().

From TTDPatch 2.5 beta 2, you can ask for random bits. To enable this, set RANDOMBITS in property function flags().

If any of the above animation callbacks return a nonzero value in the high byte, it will be interpreted as a sound effect number, and the corresponding sound effect will be played on the house tile.

CB_ASPEED - set animation speed

Called to decide how long the current animation frame should last. The value of the delay should be given in the same way as for property function anim_speed(). Decreasing the return value speeds the animation up instantly. Increasing, on the other hand, doesn't slow it down instantly: the actual duration of the current frame will be somewhere between the old and the new delays. The new delay is applied correctly for later frames.

Note: This is one of the most time consuming callbacks as it is called for every animated tile every ~30 milliseconds. For better performance try to avoid using it where reasonable, e.g. try to set animation speed only by its property and put multiple identical looking animation frames after each other.

CB_AUTOSLOPE - allow/disallow autosloping

With this callback, you can prevent the autoslope feature from altering the ground below an industr tile. To allow altering the ground, return cbr(0) (or ALLOW). Returning anything other than zero will disallow land modifications.

Example (Don't allow autosloping):
def(50) callback(
	DISALLOW if(CB_AUTOSLOPE) // do not allow change of slope
	ref(49) else // graphics
)

CB_BUILD - decide whether industry tile can be built on a given tile

This callback is called when TTD checks if a given tile is suitable for the industry tile. Since the tile isn't built yet, you can use variables 41 (terrain), 42 (townzone), 43 (position), and 60 (slope, lclass, height, water, waterclass, member) only, and you can't access any industry variables, either. Function slope() being most useful here, since you can check the slope of the tile to be built and/or the slope of nearby tiles.

This callback is the only way to allow your tile to be built on steep slopes. The callback should return zero if the tile isn't suitable, or any other value if it is suitable.

Notes: Since TTDPatch 2.6 r1728, the lowest byte of variable 18 contains the layout number selected for the industry. The other bits of variable 18 remain undefined for now. Since OpenTTD r19744 you can access the random bits of the industry (it would get after construction) via industry variable 5F resp. RandomAction2 type 83. Since OpenTTD r20942, the second lowest byte of variable 18 contains the trigger event of the industry construction. The values are the same as the lowest byte of variable 18 of callback 28.

Important: The meaning of the returned value will change in GRF version 7; it will work the same way as callback 28. Also, since TTDPatch r1755, you can use the text reference stack for your error messages, similarly to callback 3A. The only difference is that only 4 registers are copied instead of 6; see callback 28 for details. When this callback is called, TTD is just checking, and hasn't placed any industry tiles yet. Therefore, bit 8 (tile belongs to the same industry as current) of variable 60 will always be clear, even for the current tile.

CB_CARGOAMOUNTS - decide the cargo amounts accepted

Called to get the amount of acceptance for the industry tile, in 1/8 units. The cargo types are defined by property function cargoamounts(), or callback CB_CARGOTYPES if enabled. The callback must return a 15-bit value in the following format:

Bit rangeMeaning
0 .. 3Acceptance for the first cargo (0 .. 8)
4 .. 7Acceptance for the second cargo (0 .. 8)
8 .. 11Acceptance for the third cargo (0 .. 8)

If the callback fails, the values set by cargoamounts() are used instead.

So, for example, returning cbr(0x426) means "4/8 first cargo type, 2/8 second cargo type, 6/8 third cargo type", while returning cbr(0x205) means "2/8 first cargo type and 5/8 third cargo type".

CB_CARGOTYPES - decide the cargo types accepted

Called when TTD needs to know what cargoes an industry tile can accept. The bits of the result must be filled in the following way:

Bit rangeMeaning
0 .. 4First cargo type
5 .. 9Second cargo type
10 .. 14Third cargo type

You may want to use helper function customcargo() to assist in filling in the bits above.

If the callback fails, the according values from property function cargotypes() are used instead. This callback is most useful in combination with CB_CARGOAMOUNTS, so you can fully control the acceptance of your industry tile, even allowing dynamic changes.

From GRF version 7 and above, the interpretation of the returned value changes: instead of climate-dependent cargo slot numbers, you have to return climate-independent cargo IDs. If your GRF has a cargo translation table, then this ID is the index in that table; otherwise, it's the cargo bit. Acceptance of cargoes not currently present will automatically be disabled.

CB_FOUNDATION - decide if default foundations need to be drawn

This callback is called when TTD starts drawing the industry tile on sloped land. It should return zero to disable the default slope graphics and any other value to enable them.

If you chose to disable the foundation, you can draw your own by specifying


Example 5 (including custom foundations):
def(0) spriteset(
	set(NOSPRITE, notransparency(2), xyz(0,0,0), dxdydz(16,16,7)) // foundation
	set(notransparency(1), xyoff(0,1)) // real groundsprite
	set(normal(0), xyz(0,0,0), dxdydz(16,16,16)) // building
)

Please note that TTD doesn't have suitable foundation graphics for steep slopes.