Unlike vehicles, stations have no pre-defined IDs in TTD. Therefore, IDs are free to be chosen, and will in fact be allocated automatically by TTDPatch. In the station's definition, you only specify IDs relative to the set, i.e. the ID of the first station type is 0, the second station type is 1 and so on. In total, each game can only have 255 station IDs for all active newGRF files.
The property you must set for each station ID is the class label, anything else can be left at the default. It must be the first property you set for each station ID, because the station ID is actually undefined until it has been assigned a class. Also, all station IDs must get their classes in the right order, starting from ID 0 onwards.
Properties are defined inside the definestation() function block. All station properties are covered by the property functions below.
To simplify definition of a series of stations with consecutive station-IDs, the property functions inside the definestation() function may be supplied property data for multiple stations at once. This is simply done by separating the properties by commas (","), except in cases where the parameter of a property function for a single station would be a list, i.e. already including commas. These must always be "quoted", even when defining only a single station-ID:
definestation(STAIRS,"Stairs", class(PLATFORMS) callbacks({CB_LAYOUT, CB_SLOPE}) // <-- this is a list, quote! include_widths(1) include_lengths(1) threshold(36) nopylons(0) nowires(0) flags({GROUNDSPRITES, DIVAMOUNT, FOUNDATIONS}) // <-- a list, quote! )
definestation(0x36,{"3rd track","3rd track"}, class(THIRD_TRACK, THIRD_TRACK) callbacks(CB_LAYOUT, CB_LAYOUT) // <-- this is not a list, no quoting include_widths(2, 2) include_lengths(1, 1) threshold(128, 128) nopylons(0, 0) nowires(0, 0) nontrack({0,1}, {0,1}) // <-- these are two lists, quote! flags(GROUNDSPRITES, GROUNDSPRITES) )
Property function | Description |
anim_info(<Byte>, LOOP | NOLOOP) | Animation information |
anim_speed(<Byte>) | Animation speed |
anim_triggers(<List>) | Animation triggers |
callbacks(<List::Callback>) | Station callbacks |
class(<Label>) | Station class |
flags(<List>) | General Flags |
include_lengths/exclude_lengths(<List::Byte>) | Selection of lengths of platforms |
include_widths/exclude_widths(<List::Byte>) | Selection of numbers of platforms |
nontrack(<Label>) | Set non-track tiles |
pylons/nopylons(<Label>) | Pylon placement |
setcargotriggers(<List::CargoType>) | Cargo types for random triggers |
tiletypelayout({<tiletypes>,}) | Define custom tile type layout |
threshold(<Word>) | Little/lots threshold |
wires/nowires(<Label>) | Wire placement |
anim_info(<Byte>, LOOP | NOLOOP)
This function defines the way how animation for station tiles should work. Its parameter specifies the number of animation frames to use. Maximum number of frames is 255, although you might have some problems if your animation exceeds 253 frames. The second parameter sets the type of animation, either looping or non-looping.
Since you cannot have properties for individual station tiles, this property applies for every tile of the station. If you don't want to animate some tiles, you should check the current position of the tile during callback CB_ACONTROL and return a value of "A_NOP" (253) if the current tile doesn't need to be animated. If you also need animations of different length per tile, you'll have to use callback CB_AFRAME for that.
The meaning is the same as for house animation speed, but the lower limit is 0 instead of 2, so the fastest possible animation changes frames every game tick (27 ms). The default value is 0.
Parameter is a list of events that should trigger callback CB_ACONTROL, allowing to change the animation state:
Trigger | Meaning | Happens on |
BUILT | Station part is built | the newly built tiles |
NEWCARGO | New cargo arrives to station | whole station |
NOCARGO | A cargo type gets completely removed from station | |
PERIODIC | Every 250 ticks | |
ARRIVE | Train enters station (starts loading/unloading) | platform of this train |
LEAVE | Train leaves station (done loading/unloading) | |
LOADING | Train loads/unloads cargo |
The "happens on" column tells which tiles CB_ACONTROL will be called on.
For triggers NEWCARGO and NOCARGO, function cargo_trigger() returns the cargo type in question.
This property takes as its parameter a list of callbacks to use with this station. The following callbacks may be enabled by this function:
Callback | Description |
CB_AFRAME | Decide next animation frame |
CB_ASPEED | Decide animation speed |
CB_AVAILABLE | Whether to make station available in construction window (non-zero callback return) or not (callback returns zero) |
CB_LAYOUT | Use callback to select sprite layout |
CB_SLOPE | Custom slope check |
callbacks(CB_LAYOUT, CB_SLOPE)
TTDPatch/OpenTTD groups sets of new station graphics into various classes. The classes can be selected by the top dropdown list in the construction window, and the individual stations within the class from the bottom dropdown list. In addition, each station can alter its appearance using performance and/or random functions.
Class labels are unique identifiers composed of four Bytes (or characters). Only two class labels are pre-defined:
Class label | Representation | Intended use for station |
DFLT | 44 46 4c 54 | Default, no special station type |
WAYP | 57 41 59 50 | Non-cargo stations, waypoints, signal boxes etc. |
You may simply use other classes than the above, as long as no more than (at the moment) 16 classes are used among all active newGRF files at any time.
When a WAYP station is built, it will not accept cargo nor will any cargo appear from nearby industries or towns. Trains will not stop at WAYP stations, regardless of the non-stop order and/or the nonstop switch.
The parameter of this function is a list containing one or more of the following flags:
Flag | Meaning |
DIVAMOUNT | when calculating the cargo amount to display, divide the amount by the station size (to simulate cargo distributed over the area of the station) |
EXTENDED | When FOUNDATIONS is set, use extended foundation block instead of the simple one |
FOUNDATIONS | Use custom foundations on sloped tiles (function tiletype() returns "2" for foundation sprites) |
GROUNDSPRITES | use different sprite set for ground sprites (function tiletype() returns "1" for ground sprites) |
RANDOMBITS | accessability of random bits by function randombits() |
FOUNDATIONS works somewhat similarly to GROUNDSPRITES: your sprite selection will be called again, with tiletype() returning "2". If the chain ends on a callback result, TTDPatch will assume the foundation selection has failed and will use the default foundaton sprites. Function tiletype()will return the tile type of the current tile; if you have callback CB_LAYOUT enabled, this will be that callbacks return value - otherwise, the default TTD types (0/1 - plain platform, 2/3 - platform with building, 4/5 - platform with roof left side, 6/7 - platform with roof right side) are used. In either case, '1' is added for the NW-SE orientation, in case your station needs different foundations depending on its orientation. In the result value, bits 16 and 17 are set if the NW and NE foundations are to be merged with the corresponding neighbour tile, so you shouldn't draw the corresponding edge in the foundation sprite.
Your sprite selection code should select a foundation sprite block. The contents of this block depends on whether EXTENDED is set or not.
TTDPatch will combine the needed foundation from these 8 sprites depending on the current slope. You don't need to care about the merge data in bits 16..17, TTDPatch will take care of that automatically by adding the 7th and 8th sprite only when necessary.
You need to have one sprite for every slope that's possible below a rail station. TTDPatch will select the correct one depending on the current slope. It can't handle the merging itself, however, so you need four foundation blocks: one with no edges removed, one with the NW edge removed, one with the NE edge removed and one with both north edges removed. Your sprite selection code is responsible for selecting the correct of those blocks according to the merge info returned by function tiletype().
In both cases, you can put an additional value into register 0x100, which will serve as an offset into the selected block. If you don't modify register 0x100 during the chain, the offset will default to 0.
It is important that the dimensions of the sprites remain the same - i.e., you must use bit 6 in the real sprites to prevent GRFCodec from cropping the 'empty blue' areas. The offset of these foundation sprites must be -31 in X and -9 in Y direction.
include_lengths/exclude_lengths(<List::Byte>), include_widths/exclude_widths(<List::Byte>)
By default, all platform lengths and any number of platforms is available for custom stations. Using these property functions, you might choose which platforms or lengths should be unavailable by supplying the corresponding numbers as parameters. Macros NO_WIDTHS / NO_LENGTHS and ALL_WIDTHS / ALL_LENGTHS can be used as well.
exclude_widths(8) exclude_lengths(8)// glass roofs for "double" platforms and lengths < 8
exclude_widths({1,8}) exclude_lengths(8)// "waypoints" w max 4 "platforms" and length == 1
include_widths({1,2,3,4}) include_lengths(1)// a small station hall w max 4 platforms and lengths < 8
include_widths({1,2,3,4}) exclude_lengths(8)// a large station hall w 2 or 4 platforms and lengths <8
include_widths({2,4}) exclude_lengths(8)
This function sets the tile types on which trains are prevented from routing through or entering any tile of this type.
TTD supports 4 station "tile types" (each in x- and y-direction), which will be placed depending on the station's size, e.g. tile types 2/3 (TTD_BUILDING) for a single tile station (1*1), or 4/5 (TTD_ROOFLEFT) for the left two tiles of a 2*2 station, etc.
Please note that TTDPatch/OpenTTD only modify station tiles graphics-wise, but do not change the underlying TTD tile type. I.e., for placing pylons and wires or prevent tiles from being entered by vehicles, you'll need to set the TTD tile types in functions pylons()/nopylons(), wires()/nowires() and nontrack() as shown below:
Tile type | Label | Description | Default(s) |
n/a | TTD_NONE | all tile types | default | 0/1 | TTD_PLATFORM | plain platform | no pylons, wires | 2/3 | TTD_BUILDING | platform with building | 0/1, 2/3 | TTD_PLATFORMBUILDING | either plain platform or platform with building | 4/5 | TTD_ROOFLEFT | platform with roof left side | pylons, wires | 6/7 | TTD_ROOFRIGHT | platform with roof right side | 4/5, 6/7 | TTD_ROOFBOTH | platform with roof either left or right | TTD_ALLTILES | all of the above | likewise |
Functions nontrack(), pylons(), nopylons(), wires() and nowires() must set one of these labels, either the default values are being used. In addition, you might set every tile type combination explicitly, using quotes. I.e., nontrack({0,1,2,3,4,5,6,7}) is the same as nontrack(TTD_ALLTILES), etc.
When specifying more than one station-ID in a definestation() function, it might be needed to explicitly specify a default value for either pylons, wires or non-track for a subset of these station-IDs. This can be done by using TTD_NONE (i.e., no pylons, no wires, or no non-track tiles for functions pylons(), wires() or nontrack(); respectively all pylons and all wires for functions nopylons() or nowires()).
Similar to function nontrack(), these functions set the tile types on which there should be pylons resp no pylons displayed.
This property functions should only be used when the pylons cause problems with the sprite sorter, because even when the pylons are obscured by a station hall or similar, they should still show up in transparent mode so that each tile can easily be verified as being electrified.
setcargotriggers(<List::CargoType>)
This function defines which cargo types should trigger re-randomizing. The cargo types are given as a list. If nothing is set (the default), no random triggers will happen, to conserve CPU time.
tiletypelayout({<tiletypes>,})
This property function allows to choose which TTD tile type
will be built at which tile of a newly built station. The TTD tile type has nothing to do with the graphics shown for a certain station tile, but has to be taken into account when drawing pylons, wires or defining non-track tiles.
There are four different tile types, which TTD normally defines as
Tile type | Appearance |
0/1 | plain platform |
2/3 | platform with building |
4/5 | platform with roof, left side |
6/7 | platform with roof, right side |
Even numbers are used for stations in NE-SW direction (x), odd numbers are used for stations in the NW-SE direction (y).
To define a custom layout, the tile types for each platform, starting at 0, have to be listed, one platform after another, starting at platform number 0. Only values allowed are 0, 2, 4 or 6.
This property is only useful for defining tile types for multi-tile stations with only one or very few layouts (i.e., different numbers and lengths of platforms), because it is necessary to define the layouts for all supported combinations of lengths and numbers. Any combinations that are not defined will be built using TTD's default layout.
Note that it's usually way easier to draw different sprite sets using plt_*() functions, rather than redefining the tile type layout. In addition, callback CB_TILETYPE will be used to further customize the layout as defined by this property function. This may also be easier than defining a layout for every combination of length and number of platforms in the first place.
The tile type layout may be copied for use with other station-IDs by function copytilelayout().
The example below defines tile type layouts for two station-IDs. For the first ID, two layouts are defined: the first one for a 3*3 station, setting the first platform to tile type 0, the second platform to type 2, and the third platform to 4; and the second layout for a 3*5 station. For the second station-ID, a single 4*3 layout is defined with all tiles set to type 0.
tiletypelayout( tiletypes({{0,0,0},{2,2,2},{4,4,4}}, {{0,2,4},{0,2,4},{0,0,0},{0,2,4},{0,2,4}}), tiletypes({{0,0,0,0},{0,0,0,0},{0,0,0,0}}) )
Use cases for setting tile type layouts might be rare. The only one I have ever seen is the roundhouses in NewStations.
TTDPatch separates the full range of cargo amounts (0 .. 4095) into two separate subranges, "little" and "lots" of cargo. This function defines the amount of cargo for switching from "little" to "lots" of cargo, allowing better control of cargo amount based graphics (if needed).
If, for example, you set threshold to "200", then the range 0 .. 199 is divided by <num_littlesets>, and the remaining range 200 .. 4095 is divided by <num_lotssets>. Note that when setting threshold to zero, the "little" sets are never used, and so <num_littlesets> may be zero. However, <num_lotssets> must never be zero.
These functions set the tile types on which there should be wires resp no wires displayed. By default, all tile types have wires.
Like pylons/nopylons() above, these tile types do not consider callback CB_LAYOUT, but rather the default type as it was built. And again, these property functions should only be used when the wires cause problems with the sprite sorter.