The m4nfo User manual and Technical Report

Properties for train vehicles

Defining propriuceperties for train vehicles


Properties are defined inside the definevehicle() function block. Depending on the type of vehicle (locomotive type or wagon type) and its specification needs, different properties have to be set. See the diverse examples. All train properties are covered by the property functions below.


Property function Description
airdrag(<Byte>)Airdrag for this vehicle
aiusage(<Byte>[, PASS])How the AI may use this train vehicle for
callbacks(<List::Callback>)Set callbacks for this vehicle
capacity(<Byte>)Cargo capacity of train vehicle
cargoaging(<Word>)Set cargo aging factor
cargoclasses(<List::(+|-)CargoClass>)Set cargo classes for this vehicle
cargolist(<List::(+|-)CargoType>)Set cargo list for this vehicle
cargomask(<List::CargoType>)Set cargo mask for this vehicle
cargotype(<CargoType>)Type of cargo this train vehicle might carry
climate(<List::Climate>)Climate(s) available for using train vehicle
dualhead(<Boolean>)Train vehicle is a dual-headed vehicle
effort(<Unit-term>)Tractive effort for this vehicle
enginetype(<EngineType>)Engine type, sets traction type and running cost base
flags(<List>)Set special flags for this vehicle
intro(<Date>)Introduction date of train vehicle
lifecycle(<Date> <Word> <Word>)Introduction and withdrawal year of train vehicle
loadamount(<Byte>)Load amount per tick of train vehicle
modlife(<Byte>)Model life of train vehicle
newgraphics([<Veh-ID>])Train vehicle introduces new graphics (or uses graphics of given vehicle)
power(<Unit-term>)Max power of train vehicle
price(<Byte> | <Dword>)Price for vehicle
railtype(<Byte>)Rail type for train vehicle
refitcost(<Byte> | <Unit-term>)Cost to refit this vehicle
reliability(<Byte>)Reliability decay speed of vehicle
retire(<Byte>)Set early retire date for this vehicle
runningcost(<Byte> | <Dword>)Running cost of vehicle
seteffect(<Effect>)Set effect for this vehicle
shortening(0 .. 7)Shortening factor for this vehicle
sortbefore(<Byte>)How to sort this vehicle into the purchase list
speed(<Unit-term>)Max speed of train vehicle
railtype(<Byte>)Rail type to use with this train vehicle
userdata(<Byte>)Set user-defined data for this vehicle
vehlife(<Byte>)Vehicle life of train vehicle
vehlen(1/8 .. 8/8)Length of vehicle
wagonpower(<Unit-term> [,<Unit-term>])Powered wagons, add extra power and weight
weight(<Byte>)Weight of train vehicle


airdrag(<Byte>[, PASS])

This property sets the air drag coefficient c2 used for the realistic acceleration model, from 1 (no airdrag) to 255 (most air drag) in arbitrary units. 0 means to use the default value that depends on the top speed (to simulate the fact that high-speed engines are generally more streamlined).

The default values are the following:

Top speed (mph/1.6)<16162432486496128192256...

For higher speeds, the series is continued in the same manner.

Air drag in Newtons will then be c2 * v2 with v in m/s, although it is probably futile to attempt to make c2 a realistic number due to the lack of TTD's consistent scaling. If a train doesn't reach its historical top speed, one might try setting the airdrag value one or two steps lower than the default above, otherwise it's probably a good idea to leave it at the default.


This property sets the engine rank for the AI. The AI always selects the highest-rank engine of those it can buy. An optional second parameter may be set to "PASS" if this engine is 'optimized' for passenger service, i.e., in this case the AI won't use it for other cargo.


This property takes as its parameter a list of callbacks to use with this engine. The following callbacks may be enabled by this function:

Callback Description
CB_ARTI Build articulated engines
CB_LOAD Load amount (not for GRF version 8)
CB_POWR Powered wagons and visual effect
CB_RCAP Set refitted capacity
CB_RCOL Select colour mapping for vehicle
CB_SOUND Set sound effect
CB_TSFX Show a suffix after the cargo type name
CB_WLEN Wagon length (not for GRF version 8)

Callbacks CB_ARVS (Autoreplace vehicle selection), CB_ATAC (can wagon be attached?), CB_PROP (change vehicle properties), CB_RCOST (change refitting cost), CB_STOP (start/stop check), CB_TEXT (additional text in purchase screen) and CB_32DAY (32-day callback), do not need to be activated in this property function, because they are always active and will be used automatically if needed.

Note that CB_LOAD and CB_WLEN have been removed in GRF version 8. Their functionality being substituted by CB_PROP and usage of property functions loadamount() and vehlen().

Example (set callbacks for length, power, recolouring and text suffixes):


This function specifies the period (in game ticks) after which cargo carried by the vehicle is 'aged'. Setting the parameter to "0" disables any cargo aging, and by not setting this property, cargo is aged every 185 ticks by default. This property can be modified via callback CB_PROP (using property _CARGOAGING).

This property is only available in OpenTTD since r22713.


To make vehicle sets more compatible with future new cargo definitions, this property allows vehicles to define what kinds of cargo they should be refittable to and what not. A vehicle will be refittable to all cargo classes that match the included ones (+), except for the ones that are excluded (-). In addition, afterwards those cargo types listed in property function cargomask() will be toggled. In terms of logic, it is:

Refit list = (<List::+CargoClass> AND NOT <List::-CargoClass>) [XOR cargomask()]

This means, if a cargo type is in the cargoclass list, setting it in cargomask()will disable it again. Conversely, if the cargo type is not in the cargoclass list, setting it in cargomask() will add it. This way, if cargoclasses are unset, cargomask() will retain its original meaning, but it is still able to selectively add or remove certain cargo types even if cargoclasses are used, see example below.

As a consequence, there is full control over known cargo types (using cargomask()), and yet unknown types (using cargoclasses). Leave the cargos that you don't know of unset in cargomask(), and set/unset the entries for known cargos to add/remove them from cargomask() as appropriate.

Note that cargo types may belong to several classes. This is the reason for making two properties, an additive and a subtractive one, because this way a vehicle can be specified to be refittable to, for example, all express cargo that does not require refrigeration.

For the cargo types added in this way, the vehicle will probably have no specific graphics, linked with in makevehicle(), to show the proper cargo load. In this case, the default will be used, which should therefore be sufficiently generic-looking if possible. However, using the function veh_cargoclass() you can at least select the graphics most appropriate for the cargo type's class.

Example (set cargoclasses for bulk, sheltered and express cargo; don't accept passengers and liquid cargo; and don't accept fish, although it belongs to EXPRESS):


This function allows to unconditionally include (+) and/or exclude (-) cargo types for refittability, independent of any of the other refit properties or the cargo classes specified. Listed cargo types should be available from the cargo translation table. Non-available cargo types listed will be silently ignored.

This property is only available in OpenTTD since r23291.

Example (setting a cargo list):
cargolist(+GOOD, +WOOL, +WDPR, -LVST, -FERT, -COAL)


This function sets the type of a train's engine, i.e. whether it is powered by steam, diesel, electric, monorail or maglev technology, and its running cost base. It also sets the corresponding sound effect of the engine, and if seteffect() is not used, the visual effect as well.

Valid parameters are STEAM, DIESEL, ELECTRIC, MONORAIL, MAGLEV and WAGON. Default value is STEAM. Please be aware that setting the parameter to WAGON means that this vehicle will have no power. For setting the appropriate rail type, use railtype().

In OpenTTD, if a rail type translation table is loaded, setting this property does NOT alter the track type.


The parameter of this function is a list containing one or more of the following flags:

TILTINGVehicle tilts in curves, and thus gets a speed bonus
2CCVehicle uses two company colours
DEMUVehicle is a multiple unit (DMU/EMU), for colour selection
FLIPPINGVehicle can be flipped around in the depot (OpenTTD >r21966)
AUTOREFITTINGAuto-refitting is enabled for refits where CB_RCOST allows it or property function refitcost() specifies zero cost (OpenTTD >r23087)
CARGOMULTIPLIERChange the way cargo capacities are handled during a refit (OpenTTD >r23861)
NOBRKEFFECTDisable breakdown smoke effect. (OpenTTD >r24124)
STACKEDSPRITESCompose vehicle from multiple sprites (OpenTTD >r27668)

The tilting speed bonus only applies if all vehicles in the train have this flag set. In TTDPatch it requires the curves switch to be on. For realistic curves, it gives the train two free curving pieces with no speed decrease, and for other settings, it increases the curves setting by one (0->1, 1->2, 2->2). In OpenTTD, the curve speed limit increases by 20% if 'realistic' acceleration is enabled, else there is no effect.

When you enable flipping of a vehicle in depots, you must make sure that the graphics will be correctly aligned when the vehicle is flipped. This is especially important in the case of shortened vehicles. For dualheaded or articulated vehicles this flag has no effect because these vehicles are never to be flipped around in the depot.

In versions of OpenTTD before r21966 (~1.1.0-beta5) all non-dualheaded/non-articulated vehicles would be able to flip around in the depot. As such this disables a "feature" in OpenTTD, but often it makes no sense to flip vehicles around (symmetric vehicles) or the graphics are wrong (most shortened vehicles), and thus the bit should only be set where it makes sense.

By autorefitting, a vehicle may be refitted "on-the-fly", i.e. during a station visit, before loading. A vehicle with autorefittng enabled does not need a depot visit to get refitted to a different cargo type.

If cargomultiplier is set, the default cargo type does no longer affect the capacity of other refits. The capacity property and CB_PROP return the capacity in a generic unit ("tons of coal"), independent of the actual transported cargo type. CB_RCAP is called always, i.e. also for the default cargo type and in the purchase list.

These changes allow vehicles to set specific capacities for cargo types they know (via CB_RCAP), and set a generic capacity (in "tons of coal") for other cargo types. The capacity of unknown cargo types is then controlled by the cargo definition, which also controls the refittability via cargo classes.

With stackedsprites set, vehicle sprites may be composed from multiple single sprites. Currently this is limited to a number of 4 layers per articulated part of a vehicle.

lifecycle(<Date>, <Word>, <Word>)

This function offers an alternative way to define the lifecycle of a vehicle. Usually, the definition of a vehicle's lifecycle needs specification of introduction date, vehicle life span, model life span, and early retirement date. All of these dates have to be carefully chosen to get the desired behaviour of the vehicle over time. Use of this function offers an alternative way.

The function needs three points in time as its parameters:

lifecycle(<introduction-date>, <last-building-year>, <withdrawal-year>)

E.g., lifecycle(1910/3/1, 1930, 1945) would be identical to:


There is a second form for this function using only 2 arguments, for cases of an unlimited vehicle model life:

lifecycle(<introduction-date>, <vehicle-life>)

Please note that neither functions intro(), modlife(), vehlife(), nor retire() should be used together with lifecycle().


This function sets the vehicle's power which may be given in different units: hp (imperial horse power), PS (metric horse power), or kW (kilowatt).

price(<Byte> | <Dword>)

This function sets the vehicle's purchase price. The given parameter is either taken as a factor to be multiplied by an internal (TTD) base cost price (£ 400,000), or to be taken as the 'real' price. There is no distinction between steam, diesel or electric engines, they all use the same base cost price.

Please note that prices are dependent on the game's difficulty setting as well as the base cost being set. The table below should give some ideas for finding the right price for engines:

Engines (Prices in £)
Factor*1 *2 *4 *8 *16 *32 *64 *128
11'562 3'125 6'250 12'500 25'000 50'000 100'000 200'000
23'125 6'250 12'500 25'000 50'000 100'000 200'000 400'000
46'250 12'500 25'000 50'000 100'000 200'000 400'000 800'000
812'500 25'000 50'000 100'000 200'000 400'000 800'000 1'600'000
1625'000 50'000 100'000 200'000 400'000 800'000 1'600'000 3'200'000
3250'000 100'000 200'000 400'000 800'000 1'600'000 3'200'000 6'400'000
64100'000 200'000 400'000 800'000 1'600'000 3'200'000 6'400'000 12'800'000
128200'000 400'000 800'000 1'600'000 3'200'000 6'400'000 12'800'000 25'600'000
255398'437 796'875 1'593'750 3'187'500 6'375'000 12'750'000 25'500'000 51'000'000

For coaches/wagons, TTD/TTDPatch/OpenTTD use substantial smaller base costs:

Coaches/wagons (Prices in £)
Factor*1 *2 *4 *8 *16 *32 *64 *128
17 15 31 62 125 250 500 1'000
215 31 62 125 250 500 1'000 2'000
431 62 125 250 500 1'000 2'000 4'000
862 125 250 500 1'000 2'000 4'000 8'000
16125 250 500 1'000 2'000 4'000 8'000 16'000
32250 500 1'000 2'000 4'000 8'000 16'000 32'000
64500 1'000 2'000 4'000 8'000 16'000 32'000 64'000
1281'000 2'000 4'000 8'000 16'000 32'000 64'000 128'000
2551'992 3'984 7'968 15'937 31'875 63'750 127'500 254'003

Alternatively, m4nfo supports the use of 'real' prices in a number of currencies. Please note that it makes no sense to mix both ways to define prices or costs!


This property function sets the rail type of a train vehicle. Defaults are: 0 = rail, 1 = monorail, 2 = maglev.

In OpenTTD, if a rail type translation table is loaded, the function's parameter is an index into the table instead.

refitcost(<Byte> | <Unit-term>

The refit cost can be given either as a byte number, its maximum value "255" correlating to 50% of the vehicle's purchase price cost base, or directly as a percent value (1 % .. 100 %). The given/resulting value will be clamped for both alternatives.

The given cost value can be changed dynamically by using CB_RCOST.


The reliability decay speed is set when a new vehicle is bought, and specifies how quickly the reliability decays after servicing. The initial TTD default for all vehicles is "20". If a vehicle goes without servicing for a long time, or if it gets very, very old, this number increases, meaning faster decay and more breakdowns. Larger numbers mean faster decay, smaller number slower decay. If set to "0", reliability never decreases in normal operation.

runningcost(<Byte> | <Dword>)

As for prices, running cost for vehicles is calculated by multiplying base costs with a given factor. Alternatively, running costs can be given as 'real' cost in m4nfo in the same way it can be done for prices.

In m4nfo, all train engines (steam, diesel, electric) are using the same TTD base costs. Coaches and freight wagons are using TTD base costs for road vehicles. See below.

Engines (Cost in £)
Factor*1 *2 *4 *8 *16 *32 *64 *128
118 37 75 150 300 600 1'200 2'400
237 75 150 300 600 1'200 2'400 4'800
475 150 300 600 1'200 2'400 4'800 9'600
8150 300 600 1'200 2'400 4'800 9'600 19'200
16300 600 1'200 2'400 4'800 9'600 19'200 38'400
32600 1'200 2'400 4'800 9'600 19'200 38'400 76'800
641'200 2'400 4'800 9'600 19'200 38'400 76'800 153'600
1282'400 4'800 9'600 19'200 38'400 76'800 153'600 307'200
2554'781 9'562 19'125 38'250 76'500 153'000 306'000 612'000

Coaches / freight wagons (Cost in £)
Factor*1 *2 *4 *8 *16 *32 *64 *128
16 12 25 50 100 200 400 800
212 25 50 100 200 400 800 1'600
425 50 100 200 400 800 1'600 3'200
850 100 200 400 800 1'600 3'200 6'400
16100 200 400 800 1'600 3'200 6'400 12'800
32200 400 800 1'600 3'200 6'400 12'800 25'600
64400 800 1'600 3'200 6'400 12'800 25'600 51'200
128800 1'600 3'200 6'400 12'800 25'600 51'200 102'400
2551'593 3'187 6'375 12'750 25'500 51'000 102'000 204'000

In m4nfo, it is also possible to set 'real' costs dynamically by CB_PROP and helper functions setenginecost() and setwagoncost(). For more info, see here.

Please note that running costs are also dependent on the game's difficulty setting as well as the base cost set.


This function sets the visual effect of a vehicle and repositions it. By default, effect of train engines is determined by the traction type as given in enginetype(). This function enables to change the type of effect as well as its position relative to the vehicle. The type of effect may be controlled by following values:

ValueEffect type
STEAMSteam puffs
DIESELDiesel fumes
ELECTRICElectric sparks
NONEDisable visual effect
NOPOWERDisable wagon power
steam()Repositioned steam puffs
diesel()Repositioned diesel fumes
electric()Repositioned electric sparks

The position parameter of the effect in the auxiliary functions provides a range from "0" to "15" that is added to the type of the effect. Position "0" corresponds to a point half a vehicle length ahead of the vehicle, "4" is the front of the vehicle, "8" the middle, "12" the end and "15" is a half length behind the vehicle. Intermediate values are in-between.

Note that for TTDPatch and OpenTTD up to r21229 only the positioning of steam actually works. For OpenTTD r21230 and later positioning diesel smoke and electric sparks works as well.

shortening(0 .. 7) / vehlen(1/8 .. 8/8)

This property reduces the length of train vehicles, in units of 1/8th (12.5%). The value "0" means the vehicle has the full length, "1" means shorter by 1/8th (12.5%), up to "5" == shorter by 5/8ths (62.5%). Larger numbers will not work properly*, except at the end of the train**. The vehicle length is set whenever it leaves a depot.

* This restriction has been removed in OpenTTD r15793. Every vehicle can have a length between 1/8 and 8/8 independent of its position in the chain.

** This means that it is only safe to use values larger than 5 in callback CB_WLEN, making sure that they are only returned for the very last vehicle in the train. Otherwise the train will occasionally fall apart, with the wagons after the shortest one stopping to move.

Alternatively, you might use property function vehlen(1/8 .. 8/8) which takes as parameter the vehicle length in units of 1/8, where the full length vehicle is 8/8.

Callback CB_WLEN may be used with either a shortening level value by function cbr(), or with a unit length value given by helper function setlen().


This is not a property as such, but an action. It forces TTDPatch to shuffle the vehicle this "property" is being set for in front of the vehicle with the given vehicle ID. The order of this list is only used in the train purchase window.

For example, setting this property for vehicle 9 to a value of 7 would lead to the following internal list of vehicle IDs: ... 5 6 9 7 8 10 11 ...

This property can not simply be overwritten, because the list is already shuffled when trying to do so. It is however possible to reset the list to its original order with a special function resetorder(). Resetting the list should however only be done by sets that contain replacements for all train vehicles.


This property sets the vehicle's speed, using different units: km/h (kilometers per hour) or mph (imperial miles per hour).

For wagons, this value is only used if the wagonspeedlimit switch is set, and it limits the speed of the train to that of the lowest wagon speed. This limit is ignored for wagons with a livery override for the current train, so that train sets always get their max speed from the engine's max speed.

For wagons, a value of 0 means "default" (which depends on cargo type and date of introduction), and 65535 means no limit.


This function sets the rail type of the vehicle: 0 = rail, 1 = monorail, 2 = maglev. For setting the appropriate traction type, see enginetype().

In OpenTTD, if a rail type translation table is loaded, this property is an index into the table instead.


This function sets a user-defined bit mask in a static way. Its values may be interpreted either as bits or numerical values, depending on the function used to access the user data: function getubits() returns the user data as a bit pattern, but function getubyte() returns the data as a numerical value. The user data area may be written dynamically by using functions setubit()/clrubit() or setubyte() (even concurrently) while being in a callback CB_PROP chain, see there.

Example (defining static user bits):
define(UB_F,0x01)   // express coach
define(UB_N,0x02)   // local coach
define(UB_STW,0x04) // active driving cab
define(UB_FPP,0x08) // express coach for push-pull service
define(UB_NPP,0x10) // local coach for push-pull service
define(UB_MU,0x20)  // multiple unit



wagonpower(<Unit-term> [,<Unit-term>])

This function alllows to model 'powered wagons', which are most often found in modern real-life trains.

The function's first parameter determines how much power each wagon adds to the train when attached to the train's engine. In addition, the second parameter (if set) determines how much weight should be added by making wagons powered. Note that in TTD, these values are set for the train's engine instead for each powered wagon.

Also, both extra power and extra weight only apply to wagons with an override for this engine as well. This means that, for example, passenger wagons with an override will add power (and weight), but freight cars with no override will not.