This is an in-depth description of station callbacks. 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., a station) previously defined. An example being the modification of the visual effect of a train vehicle, its speed, or its capacity.
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 station tile, according to the state of the corresponding animation.
Here's a list of all available callbacks for stations. Please note, that not all of them need to be explicitly activated in the callbacks() property function during the station definition stage:
Callback | Description |
CB_ACONTROL | Set frame the animation should jump to |
CB_AFRAME | Set next animation frame |
CB_AISELECT | AI construction/purchase selection |
CB_ASPEED | Set animation speed |
CB_AVAILABLE | Station availability in construction window |
CB_LAYOUT | Use callback to select sprite layout |
CB_SLOPE | Custom slope check |
CB_TILETYPE | Callback to select TTD tile type |
Please also note that CB_SLOPE has to be used in the MENU branch.
CB_ACONTROL - set frame the animation should jump to
This callback is called periodically or when an animation trigger happens. It returns the number of the frame the animation should jump to, or one of the following special values:
Value | Label | Meaning | 255 | A_STOP | stop animation in its current frame | 254 | A_START | start animation with its current frame | 253 | A_NOP | leave 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().
From TTDPatch 2.5 beta 2, you can ask for random bits by function randombits(). To enable this, set RANDOMBITS in the station property function flags().
This callback is always available, it doesn't need to be defined in the station's callbacks() property function.
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:
Value | Label | Meaning | 255 | A_STOP | stop animation. The current frame stays on screen until the animation is restarted. | 254 | A_NEXT | continue 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().
Since TTDPatch 2.5 beta 2, you can ask for random bits by function randombits(). To enable this, set RANDOMBITS in the station property function flags().
CB_AISELECT - AI construction/purchase selection
This is a so-called "generic feature" callback. It is called for various decisions when the AI is constructing a new route, and this callback can be used to make that selection depend on source and destination industries as well as service distance, among other things.
For stations, a number of special performance functions can be used:
Function | Meaning |
AI_destination() | destination industry type |
AI_distance() | distance between source and destination, in (dx+dy)/2 |
AI_event() | AI construction event |
AI_number() | construction number; for stations 0=source station, 1=destination station |
AI_source() | source industry type |
AI_stationlength() | station platform length |
AI_stationsize() | station size as nibbles <num-platforms>, <length> |
AI_stationwidth() | station's number of platforms |
For stations, the callback will be called only when building a station, and the value returned by function AI_event() will be the station-ID.
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 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 use only the properties and put multiple identical looking animation frames after each other.
CB_AVAILABLE - Station availability in construction window
This callback specifies whether a station can be constructed or not, i.e. whether it can be selected in the station construction window (e.g. depending on year). A non-zero callback return indicates the station can be built, and a return code of zero removes the station from the purchase selection.
CB_LAYOUT - Use callback to select sprite layout
This callback selects an entry from the station's sprite layout set by function tile(). If its return value is invalid then the sprite layout given from the default tile type will be used. This is the only way to have more than the default 4 TTD different sprite sets to choose from.
Bit 0 (station orientation) of the return value is ignored, and instead set to bit 0 of the actual tile, so that you do not have to check the orientation explicitly and return the two corresponding values, instead just organize the layouts such that even numbers correspond to X orientation (NE-SW) and odd numbers corresponding to Y orientation (NW-SE). See examples here.
The effect of this callback is like having additional tile types that are however not actually built, but only show different graphics depending on the callback.
Function cbset() can be used to select specific sprite layouts depending on their loading state.
layout(_PLAT, // empty tile(__plat, ground(1012) regular(0, xyz(0,0,0), dxdydz(16,5,3)) regular(2, xyz(0,11,0), dxdydz(16,5,3)) ) tile( ground(1011) regular(1, xyz(0,0,0), dxdydz(5,16,3)) regular(3, xyz(11,0,0), dxdydz(5,16,3)) ) // fences front tile( ground(1012) regular(0, xyz(0,0,0), dxdydz(16,5,3)) regular(2, xyz(0,11,0), dxdydz(16,5,3)) regular(20, xyz(0,16,3), dxdydz(16,0,3)) // fence ) tile( ground(1011) regular(1, xyz(0,0,0), dxdydz(5,16,3)) regular(3, xyz(11,0,0), dxdydz(5,16,3)) regular(21, xyz(16,0,3), dxdydz(0,16,3)) // fence ) // fences back ... ) ... def(1) plt_numrev( reftile(__plat+1) if(0) // fence front reftile(__plat) else // no fence ) def(2) plt_num( reftile(__plat+2) if(0) // fence back ref(1) else ) def(3) plt_total( reftile(__plat+3) if(1) // fence both ref(2) else ) def(10) callback( ref(3) if(CB_LAYOUT) ref(0) else )
This callback is called for each tile the new station part will be built. Return cbr(0) (or 400 in grf version 8) to accept the current tile or cbr(1) (or 401 in grf version 8) to make the station building fail with the "Land sloped in wrong direction" error message. Other return values are reserved for future use, don't use them for now.
Since the station isn't built yet, only some special station performance functions can be used: test_slope(), test_pltpos(), test_pltnum(), test_pltlength(), test_plttotal().
This callback is called only after the normal checks TTD does for slopes, so it's not possible to allow a slope that isn't allowed by default; you can only narrow the set of allowed slopes. In particular, it is not possible to built station segments with platforms on different levels. If the callback fails, the tile will be accepted.
Please note that the callback has to be used in the purchase menu. Use cargo type MENU for this.
// only allow slopes with N and E/W corners elevated (southern) def(12) test_slope( ALLOW if(NORTH+WEST) // allow ref(11) else // check for northern slope ... ) // menu def(13) callback( cbr(216) if(CB_LAYOUT) // icon ref(12) if(CB_SLOPE) ref(159) else // sprite block )
CB_TILETYPE - select TTD tile type
This callback may be used to further customize the station's tile layout. It is called once for every tile that is being built, and selects the TTD tile type for the tile to build, or to leave the tile as TTD's default if the callback fails. The only possible values for this callback are 0, 2, 4 and 6 (see tiletypelayout()).
This callback allows to set properties depending on the tile type like pylons, wires or non-track behaviour on a single-tile level, instead for the whole station.
Since the station hasn't been built yet, none of the plt_*() performance functions will be available, instead function test_position() can be used to request the position of the tile to be built inside the tile layout. For the same reason, it uses the same cargo type in function makestation() as the construction menu does (i.e., type MENU if defined or else the default).
This callback is always available, it doesn't need to be defined in the station's callbacks() property function. Please note that it has to be used in the purchase menu. Use cargo type MENU for this.