In m4nfo, rail types are using only two types of functions:
Functions for sprite layout definition
The sprite layout for rail types is very simple:
def(6) spriteset(0) // level crossing open def(7) spriteset(1) // level crossing closed
Functions for rail type performance
These functions are used to evaluate game-intrinsic variables, and make them accessible to the rail type's activation function.
| Function | Description |
| depotage(<block>) | Depot age in years (since m4nfo_railtypes r13) |
| depotdate(<block>) | Depot construction date (since m4nfo_railtypes r13) |
| displaymode(<block>) | Set display mode for custom signal sprites |
| gatesclosed(<block>) | Level crossing status: "0" = open (or not a crossing), "1" = closed. |
| terrain(<block>) | Terrain type: NORMAL (0), DESERT (1), RAINFOREST (2), SNOW (4, on or above snowline). |
| townzone(<block>) | Town zone of level crossing or depot (since OTTD r23866) |
| tunneltype(<block>) | Type of tunnel (0 = plain portal) (since OTTD r23952) |
| signal_type(<block>), signal_variant(<block>), signal_state(<block>), signal_restricted(<block>), signal(<block>) | Custom signal sprites information (since OTTD r24367) |
| randomrel(0, <randombit> <List::ref()>) | Get random reference |
depotage(<block>) / depotdate(<block>)
These two functions are used to check a depot's age in years and/or a depot's building date:
def(1) depotage( ref(7) if(>20) // older than 20 years ref(6) else )Example 2 (check a depot's building date):
def(2) depotdate( ref(7) if(<date(1-1-1970)) // built before 1970 ref(6) else )
This function returns the actual display mode associated with custom signal sprites. In this way, it is possible to use different sprites for the signal in question, depending on the display mode (on map, or in the GUI window).
| Value | Meaning |
| DP_VIEWPORT | Signal is drawn in a viewport, i.e. on the map |
| DP_SIGNALGUI | Signal is drawn in the signal GUI. The returned sprite set must still have 8 sprites, but OpenTTD will only use the 7th sprite, so all other sprites can be empty. |
All other values are reserved and must not be used.
This feature is only available in OTTD since r24367.
This function returns the town zone, from centre to outmost, where the current level crossing or depot is situated. Returned values are as follows:
| Value | Label | Meaning |
| 4 | TZ_CENTRE | Innermost zone, street lights |
| 3 | TZ_COMMERCIAL | Trees |
| 2 | TZ_RESIDENTIAL | Paved roads |
| 1 | TZ_PERIPHERY | Outer zones, plain roads |
| 0 | TZ_OUTSKIRTS |
Please note that smaller towns might have fewer zones.
These functions return extra information about custom signal sprites.
| Function | |
| Value | Description |
| signal_type(<block>) | |
| 0 | Normal block signal |
| 1 | Entry pre-signal |
| 2 | Exit pre-signal |
| 3 | Combo pre-signal |
| 4 | Two-way path signal |
| 5 | One-way path signal |
| signal_variant(<block>) | |
| 0 | Light signal |
| 1 | Semaphore signal |
| signal_state(<block>) | |
| 0 | Red signal |
| 1 | Green signal |
| signal_restricted(<block>) | |
| 0 | no routing restrictions |
| 1 | routing restrictions attached |
All other values are reserved and must not be used in "trunk" OpenTTD. The JGR PatchPack includes custom features to support both programmable (signal type 6) and restricted signals with custom graphics.
Function signal() returns a combined value for a signal's variant, type and state:
| Light signals | Semaphore signals | ||||
| description | label | value | label | value | |
| block | BLK_LIT_RED | 0 | BLK_SEM_RED | 64 | |
| BLK_LIT_GRN | 128 | BLK_SEM_GRN | 192 | ||
| entry | ENT_LIT_RED | 1 | ENT_SEM_RED | 65 | |
| ENT_LIT_GRN | 129 | ENT_SEM_GRN | 193 | ||
| exit | EXT_LIT_RED | 2 | EXT_SEM_RED | 66 | |
| EXT_LIT_GRN | 130 | EXT_SEM_GRN | 194 | ||
| combo | CMB_LIT_RED | 3 | CMB_SEM_RED | 67 | |
| CMB_LIT_GRN | 131 | CMB_SEM_GRN | 195 | ||
| PBS 2-way | PBS2_LIT_RED | 4 | PBS2_SEM_RED | 68 | |
| PBS2_LIT_GRN | 132 | PBS2_SEM_GRN | 196 | ||
| PBS 1-way | PBS1_LIT_RED | 5 | PBS1_SEM_RED | 69 | |
| PBS1_LIT_GRN | 133 | PBS1_SEM_GRN | 197 | ||
def(1) gatesclosed( ref(7) if(1) // closed ref(6) else // open )Example 2 (town zones):
def(2) townzone( ref(4) if(TZ_PERIPHERY) ref(5) if(TZ_RESIDENTIAL) ref(6) if(TZ_COMMERCIAL) ref(7) if(TZ_CENTRE) ref(8) else // TZ_OUTSKIRTS )Example 3 (custom signals):
def(3) signalvariant( ref(5) if(1) // semaphores ref(6) else // light signals )Example 4 (custom signals):
def(4) signal( ref(0) if(PBS2_SEM_RED) // PBS german semaphores 2-way RED ref(2) if(PBS1_SEM_RED) // PBS german semaphores 1-way RED ref(1) if(PBS2_SEM_GRN) // PBS german semaphores 2-way GREEN ref(3) if(PBS2_SEM_GRN) // PBS german semaphores 1-way GREEN ref(0) else )
randomrel(<trigger>, <randombit> <List::ref()>)
Unlike the performance functions above, whose results are always determined by a predictable decision, one can also use random functions to pick one of several graphics sets or callback results.
There are no triggers for railtypes, hence you must always define 0 (or CONSTRUCT).
Setting randombit determines the first bit to be re-randomized, as well as basing the random graphics on. The total number of bits used is the 2-logarithm of the number of references used, e.g., for 16 references, 4 bits are used.
Rail tiles have 2 pseudo random bits, based on tile location.
The number of referenced sets to choose from must be a power of 2, i.e. 2, 4, 8, 16 etc.
define(_YEAR,1959) // transition year def(12) randomrel(0,1,ref(10),ref(10),ref(10),ref(11)) // 25% def(13) randomrel(0,1,ref(10),ref(10),ref(11),ref(11)) // 50% def(14) randomrel(0,1,ref(10),ref(11),ref(11),ref(11)) // 75% def(5) year( ref(10) if(<_YEAR) // all old ref(12) if(_YEAR .. _YEAR+4) // 25% modern ref(13) if(_YEAR+5 .. _YEAR+9) // 50% modern ref(14) if(_YEAR+10 .. _YEAR+14) // 75% modern ref(11) else // all modern )