The m4nfo User manual and Technical Report

Functions for road vehicles

Using functions for road vehicles

Introduction

In m4nfo, road vehicles are using four types of functions:


Functions for sprite layout definition

Format

In m4nfo, all vehicles (trains, road vehicles, ships and planes) are using the same format for defining sprite layouts:

spriteset(move(<moving states>), load(<loading states>))

Where the move() and load() functions take spriteblock() references as their arguments.

Example 1 (bus):
spriteset(move(0),load(0))

This bus uses the same sprite (number 0, first sprite of spriteblock) both for its only moving state as for its only loading state.

Example 2 (truck):
spriteset(move(1,2,3,4),load(1,2,3,4))

This truck uses 4 different sprites for its moving states, and it uses the same sprites for its loading states.

Example 3 (truck):
spriteset(move(0,9,11),load(0,8,10))

This truck uses 3 different sprites for its moving states, and it uses different sprites for its loading states.

Functions for road vehicle performance

These functions are used to evaluate game-intrinsic variables, and make them accessible to the vehicle's activation function. A typical application would use a multitude of these functions linked together to form a "chain", connecting the graphics' sprites of a vehicle with its activation function. See here for an example.

Function Description
ageindays(<block>)Get age of vehicle counted in days
ageinyears(<block>)Get age of vehicle counted in years
animation(<block>)Access the animation counter
articulated(<block>)Get number of articulated vehicle part
autoreplace(<block>)Get iteration number from CB_ARVS
callback(<block>)Check incidence (and type) of callback
cargo(<block>)Check cargo type
cnst_cargoclasses(<block>)Get information about all cargo classes transported by the consist
cnst_cargo(<block>)Get most common cargo type in consist
cnst_refit(<block>)Get most common refit cycle of most common cargo type in consist
current_capacity(<block>)Get current capacity of vehicle
current_load(<block>)Get current load of vehicle
current_speed(<block>)Get current speed of vehicle
daycounter(<block>)Day counter, increased daily
daysintravel(<block>)Number of days travelling since last load/unload
dayspastservice(<block>)Elapsed time in days since the last service
direction(<block>)Get driving direction of vehicle
displaymode(<block>)Set display mode for vehicle sprite
elseThis is really a void statement
flipped(<block>)Check if road vehicle is flipped (forward/backward)
idcount(<Veh-ID>, <block>)Check number of vehicles with given ID
indepot(<block>)Check if vehicle is in depot
islast(<block>)Check if last vehicle in consist
lastmaintenance(<block>)Get time since last maintenance in days
lastservicedate(<block>)Date of last service in days since year 0
lastserviceyear(<block>)Date of last service in years since year 0
loadtime(<block>)Get time to load/unload remaining
maxspeed(<block>)Get maximum speed of vehicle
property(<block>)Get property number for CB_PROP
randombits([<shiftmask>,] <block>)Get random bits of vehicle
randomtriggers([<shiftmask>,] <block>)Get random triggers of vehicle
rcost_cargotype/rcost_cargosubtype(<block>) rcost_cargoclass([<shiftmask>,] <block>)Get target cargo type information from CB_RCOST
refitted(<block>)Check number of refits to same cargo type
reliabilitystate(<block>)Get current reliability state of vehicle (65535=100%)
servint(<block>)Get service interval of vehicle
soundevent(<block>)Check sound event
speedlimit(<block>)Current top speed of engine (w/o bridge, curve etc.), 0 if unlimited
veh_cargoclass(<block>)Get cargo class transported by the consist
veh_cargotype(<block>)Get cargo type transported (slot no in CTT!)
veh_cargoweight(<block>)Get cargo unit weight (in 1/16 tons)
veh_currentload(<block>)Get actual load of vehicle
veh_getinfo([-]<Byte>, <function>, [<shiftmask>,] <block>),
veh_getinfoabs(FRONT | BACK, <Byte>, <function>, [<shiftmask>,] <block>)
Accessing other vehicle's performance functions
veh_id(<Veh-ID>,<block>)Check vehicle ID for CB_ATAC
veh_marketinfo(<block>)Info about vehicle market availability
veh_num(<block>)Get total number of vehicles in consist
veh_posrel/veh_posabs(FRONT | BACK | MOD2 | MOD3 | MOD4 | MOD6,<block>)Get position of vehicle inside consist
veh_status(<block>)Get special status of vehicle
yearbuilt(<block>)Year built (counted from year 0)
randomrel/randomabs(<trigger>, <randombit> <list::reference>)Get random reference
randomcount(<trigger>, <randombit>, <type>, <count>, <List::ref()>)Get special random reference

Description

ageindays(<block>)

This function returns the age of a vehicle in days since its purchase date. Please note that the result will be returned as a WORD, and requesting the result by function if() has to ensure this.

Example (age of coaches):
def(RF_SILVER) ageindays(
	ref(0x14) if(0 .. 1825) // new: < 5 years
	ref(0x15) if(1826 .. 3650)    // medium: < 10 years
	ref(0x16) else		      // old
)

ageinyears(<block>)

This function returns the age of the vehicle in years. As above, its result will be returned as a WORD.

animation(<block>)

This function returns the amount of motion that a vehicle has done. It is only valid for the first vehicle in a consist. Its value is in units of 1/4096 of a tile. A vehicle actually moves visibly every time the motion counter increases by 256, and since a tile consists of 16 such subunits, 16*256=4096 motion units mean motion across one tile.

This function is predefined to achieve an animation with one frame per vehicle motion and 4 frames in total for motion across an entire tile. This can be overriden by applying two extra parameters, one for number of frames per vehicle motion, and the other for number of frames in total for motion across an entire tile. Note however, that vehicle graphics are only updated every time the vehicle actually moves.

For an example see the train section.

articulated(<block>)

When building articulated vehicles by use of CB_ARTI, this function will return the number of the articulated part, i.e. "1" for the first articulated part, "2" for the second and so on.

Example (articulated city bus):
def(4) articulated(
	addveh(_BUSSING_KASSBOHRER) if(1)
	ENDLIST else
)

def(5) callback(
	ref(4) if(CB_ARTI)
	ref(2) else
)

callback(<block>)

This function checks for a callback incidence, and if so, returns the type of the incurred callback. See example above.

cnst_cargoclasses(<block>)

Following three functions work on whole consists.

This first function returns a bit mask of all cargo classes transported by the consist. Information returned is only for cargo classes from this vehicle on. To get information about the whole consist, you might 'bracket' the associated evaluation block for this function, using the function engine(). This also holds for the next two functions, cnst_cargo() and cnst_refit().

Example (graphics change by refit only when "most common cargoclass" == REEF):
def(14) cnst_cargoclasses(
	ref(13) if(REEF) // refrigerated cargo
	ref(11) else
)

cnst_cargo(<block>)

This function returns the most common cargo type transported by the consist. Note that even if the grf file has installed a cargo translation table, the returned value is the actual cargo. The reason for this is that it cannot be translated in general, because different vehicles can be from different grf files and have different translation tables. Use veh_cargotype() if you want the translated cargo type.

Example (graphics change by refit only when "most common cargo" == PASS):
def(14) cnst_cargo(
	ref(13) if(ttdcargo(PASS)) // use original PASS here (untranslated!)
	ref(11) elsek
)

cnst_refit(<block>)

This function returns the most common refit cycle of the cargo type returned by cnst_cargo().

Example (check common refit cycle in consist):
def(13) cnst_refit(
        ref(10) if(2) // green
	ref(13) if(1) // dark green 
	ref(11) else    
)

direction(<block>)

This function returns the actual driving direction of the vehicle, i.e. 0 = up, 1 = up/right, 2 = right, 3 = right/down, 4 = down, and so on, in a clockwise manner.

Example (does front car travel downwards?):
def(3) direction(
         ref(1) if(3) // \
         ref(1) if(4) // |
         ref(1) if(5) // /
         ref(0) else  // no
)

displaymode(<block>)

This function returns the actual display mode associated with the current vehicle sprite. In this way, it is possible to use different sprites for the vehicle in question, depending on the display mode (on map, in depot, in purchase window, ...).

ValueMeaning
VIEWPORTVehicle is drawn in a viewport, i.e. on the map
DEPOTGUIVehicle is drawn in the depot GUI
VEHICLEGUIVehicle is drawn in the vehicle details GUI (This includes the refit GUI)
VEHICLELISTVehicle is drawn in the vehicle list
PURCHASELISTVehicle is drawn in the purchase list (This includes the autoreplace GUI)
PREVIEWGUIVehicle is drawn in the exclusive preview GUI or in the advertisement news

PURCHASELIST and PREVIEWGUI are to be used in the purchase list chain (MENU), the others are to be used in the actual vehicle chain.

This feature is only available in OTTD since r23080.

else

This is a void statement. It's just there to enhance readability. See example above.

flipped(<block>)

This function checks if a vehicle is 'flipped', i.e. in a 2-part Multiple Unit (MU), the front car is usually travelling in forward direction, and the back one is travelling backwards.

Example (2-part tram):
def(2) flipped( 
	ref(0) if(FORWARD) 
	ref(1) else
)

idcount(<Veh-ID>,<block>)

This function checks the number of vehicles with a given ID in a consist. The additional parameter is the veh-ID to check for.

For an example see the train section.

indepot(<block>)

This function checks if the vehicle is currently in a depot.

Example:
def(4) indepot(
	ref(35) if(INDEPOT)
	ref(33) else
)

islast(<block>)

This function checks if the current vehicle is the last one in the consist.

Example (backlights on trailer):
def(8) islast( 
	ref(0) if(NOTLAST) // normal
	ref(4) else	   // use backlights
)

property(<block>)

This function returns the property type needed for applying the 'property callback' CB_PROP. By using this callback, it is possible to change most of the vehicle's properties by use of the function cbr() (CallBack Result).

The following table lists the properties which may be modified for road vehicles:

PropertyMeaning
capacity()capacity
cargoaging()custom cargo ageing (OTTD only)
effort()tractive effort
power()power
price()price
runningcost()running cost
speed()speed
userdata()user data
weight()weight

Instead of giving the property function with an empty parameter list you might also drop the parentheses at all. Please note that property callbacks for road vehicles are only available for OpenTTD.

randombits/randomtriggers([<shiftmask>,] <block>)

These functions return random bits and random triggers of the current vehicle or the consist's engine. See here for road vehicle triggers.

You may also make use of auxiliary function shiftmask() to adjust the result to your needs. This must be done according to the number of random sets and the (starting) random bit being used in randomrel()/randomabs(). I.e., you should shift right by the number of the random bits used, and mask with the number of random sets - 1. E.g., when there are two random sets, using random bit 0, you should set shiftmask(0,1), in case there are four random sets and the random bit had been set to 2, you should use shiftmask(2,3), to only acces the valid random bits.

rcost_cargotype/rcost_cargosubtype(<block>) rcost_cargoclass([<shiftmask>,] <block>)

These three functions provide information about the target cargo type from callback CB_RCOST. These are only available since OpenTTD r23086.

FunctionValue
rcost_cargotypeThe cargo type being refitted to; translated if a translation table has been installed
rcost_cargosubtypeThe cargo subtype being refitted to
rcost_cargoclassThe cargo class value of the cargo being refitted to

Example (autorefittng for bulk cargo):
// refit from bulk
def(1) rcost_cargoclass(shiftmask(0,BULK),
	autorefit(0) if(BULK) // free refit[*]
	cbfail() else // other classes need depot visit
)

// check cargo class
def(2) veh_cargoclass(
	ref(1) if(BULK) // refit from bulk
	cbfail() else   // refit from non-bulk, needs depot visit
)

def(3) callback(
	ref(2) if (CB_RCOST) // refit cost/autorefitting
	... else
)

The example uses callback helper function autorefit()

[*] free refit only for class "BULK", but not for other classes like "BULK, COVERED", "BULK, REEF", "BULK,PGOODS", etc.

refitted(<block>)

This function checks the number of refits to the same cargo type of the vehicle.

Example (refitted capacity):
def(3) refitted(
         cbr(48) if(0) // 48 pass
         cbr(32) else  // 32 pass  
)

def(F_OLD) callback(
         ref(3) if(CB_RCAP) // refit capacity
         ref(2) else	    // graphics
)

soundevent(<block>)

This function checks the sound event. Following events are handled for road vehicles:

EventMeaning
SND_STARTStart sound, called when leaving a station
SND_TUNNELTunnel sound, called when vehicle enters tunnel
SND_BRKDOWNBreakdown sound, called when vehicle breaks down
SND_RUNNINGRunning sound, called once per engine tick, but no more than once per vehicle motion
SND_EFFECTRoad vehicle visual effect generation (steam plume, diesel smoke, electric spark)
SND_RUN16Running sound, called every 16 engine ticks if in motion
SND_STOP16Stopped sound, called every 16 engine ticks if stopped (e.g. at signal/in station)
SND_LOADLoad/unload sound, called when consist loads or unloads cargo

Example (different sounds depending on sound event):
def(ALLSOUNDS) soundevent(
	refsnd(SND_BELL1) if(SND_START)
	cbr(0x0E) if(SND_BRKDOWN) // TTD sound
	cbfail() else
)

veh_cargoclass(<block>)

This function checks the cargo class value of the cargo transported by this vehicle. Unlike the cnst_*() functions, this and the next 2 functions return the info of the current vehicle only, not of the consist.

Example (increase speed if refrigerated cargo):
def(9) veh_cargoclass(
         cbr(120) if(REEF) // refrigerated cargo, set 120 km/h
         ref(0) else       // else keep 80 km/h
)

veh_cargotype(<block>)

This function returns the cargo type transported by the vehicle. The cargo type is translated if a cargo translation table has been installed. Please note that if the grf has installed a cargo translation table, the return value is the slot number in that table, irrespective of which actual slot the cargo is using in the game. If a table has been installed, but the current cargo is not listed there, the returned value will be set to 255 (hex FF).

Example (increase speed if cargo is 'fish'):
def(9) veh_cargotype(
         cbr(120) if(FISH) // refrigerated cargo (fish), set 120 km/h
         ref(0) else       // else keep 80 km/h
)

veh_cargoweight(<block>)

This function returns the cargo unit weight (in 1/16 tons),i.e., the value defined in cargo property function weight().

Example (reduce truck capacity if cargo unit weight > 1 tonne):
def(9) veh_cargoweight(
         cbr(20) if(> 16) // reduce capacity if cargo unit weight > 1 tonne
         ref(0) else      // else keep standard capacity
)

veh_currentload(<block>)

This function returns the actual load (in tons, pieces, bags, ..) of the vehicle.

Example (reduce speed if cargo load > 10 tons):
def(9) veh_currentload(
         cbr(80) if(> 10) // reduce speed if actual load > 10 tonnes
         ref(0) else      // else keep standard speed
)

veh_getinfo([-]<Byte>, <function>, [<shiftmask>,] <block>), veh_getinfoabs(FRONT | BACK, <Byte>, <function>, [<shiftmask>,] <block>)

These are special functions to access the return value of another function addressing a different vehicle in the consist, i.e. only available for articulated road vehicles. Both functions are not supported during a callback that is used to modify vehicle properties, to avoid circular dependencies. This currently limits these functions to callbacks CB_ATAC, CB_RCOL, CB_STOP and CB_32DAY when inside a callback scope.

Function veh_getinfo() uses a relative offset to address a certain vehicle in the consist, whereas function veh_getinfoabs() addresses other vehicles by using absolute offsets, either counting from the front of the consist, or from the back.

For veh_getinfo(), the first parameter specifies the offset in the chain from the current vehicle. Positive values are interpreted as towards the end, negative values as towards the front. If the offset points outside the consist, the result value will be 0. Special parameter LAST will access the last vehicle of the consist.

For veh_getinfoabs(), the first parameter specifies the direction of counting. FRONT means to start counting from the front of the consist (i.e., the first vehicle has index 0), and BACK means to count backwards from the end of the consist (i.e., the last vehicle has index 0). The second parameter gives the absolute offset, depending on the direction of counting, which in both cases must be a positive value.

Following parameter for both functions specifies the m4nfo function to be called for the other vehicle. If that function needs a parameter it has to be supplied as well to avoid error message ERR_NUMPAR. You may also make use of auxiliary function shiftmask() to adjust the result to your needs.

Due to implementation restrictions, only a limited number of functions are allowed as a parameter for veh_getinfo():

ageindays(), cnst_cargo(), cnst_cargoclasses(), cnst_refit(), curvature_front(), curvature_back(), curvature_triplet(), flipped(), getubits(), getubyte(), idcount(<Veh-ID>), refitted(), reversed(), veh_cargoclass(), veh_cargotype(), veh_cargoweight(), veh_id(), veh_num(), veh_posabs(<direction>), veh_posrel(<direction>), veh_ispowered(), veh_maypowered(), veh_railtype(), yearbuilt().

This function is available since OpenTTD r22997.

Examples:
def(1) veh_getinfo(4,getubits(),
         ref(2) if(0)
         ref(3) else
)

def(1) veh_getinfo(-1,idcount(13,),
         ref(3) if(0) // veh has ID = 0
         ref(4) else
)

def(1) veh_getinfoabs(BACK,0,getubyte(),
	 ref(2) if(1) // livery of last vehicle is "1"
	 ref(3) if(2)	    // livery of last vehicle is "2"
	 ref(4) else	    // must be livery "4"
)

def(1) veh_getinfoabs(FRONT,0,randombits(),shiftmask(0,0x01),
	 ref(2) if(0) // get random livery of front vehicle
	 ref(3) else
)

veh_id(<Veh-ID>,<block>)

This function checks the vehicle ID of the current vehicle. This might be useful in deciding whether a certain vehicle might be attached (see callback CB_ATAC)

Example (check if a certain vehicle may be attached to a road vehicle consist):
def(4) veh_id(
         ref(3) if(_TRAILER)	     // OK
         attach(ATT_CARBADTYPE) else // "wrong type of trailer"
)

def(5) callback(
         ref(4) if(CB_ATAC) // check if trailer might get attached
         ref(9) else	    // graphics
)

veh_marketinfo(<block>)

This function returns information about market availability of the current vehicle or the consist's engine type. Values returned are:

StatusMeaning
ONMARKETVehicle type is available on the market
TESTINGPHASEVehicle type is in the testing phase
TESTINGOFFERExclusive testing offer for a human player active

Example (check if exclusive testing offer for consist's engine is active):
def(8) veh_marketinfo(
	engine(	     
         	ref(1) if(TESTINGOFFER) // show special livery
         	ref(2) else             // normal
	)
)

veh_num(<Veh-ID>,<block>)

This function returns the total number of vehicles in the consist.

Example (only allow for 2 cars, not more):
def(8) veh_num(
	engine(	     
         	attach(ATT_OK) if(< 2)        // max +2 Wagen
         	attach(ATT_MOTCARBADNUM) else // "wrong number of motor cars"
	)
)

def(9) veh_id(
         ref(8) if(__DUEWAG_GT6)     // OK
         attach(ATT_CARBADTYPE) else // "wrong car type"
)

def(10) callback(
         ref(9) if(CB_ATAC) // may car be attached?
         ref(6) else        // graphics
)

Note that we used a 'bracket' in def(8). The engine() function keeps care of a specialty when using callback CB_ATAC. Because this callback is called for the first road vehicle in a consist, the usual vars refer to the trailer that is to be attached, not to the road vehicle it is being attached to. That's why one has to 'force' the function to refer to the road vehicle the trailer is being attached to, if one wants to find out the length of the consist it is being attached to.

veh_posrel/veh_posabs(FRONT | BACK | MOD2 | MOD3 | MOD4 | MOD6, <block>)

These two functions check the position of the current vehicle within the consist.

The first parameter decides on the direction of counting, it can either be set to FRONT or BACK, depending on the direction of counting. In addition, the same parameter might get set to either MOD2, MOD3, MOD4 or MOD6 too, to carry out the appropriate 'modulo' operations.

For veh_posabs(), these numbers refer to the whole consist, but for veh_posrel(), they only refer to the chain of consecutive vehicles with the same ID as the current vehicle (including itself, but possibly excluding the engine).

Note the use of bracketing in the illustration, where 'self' means no bracketing at all, and 'engine' means to use the function engine() for bracketing.

Example 1 (show cars for articulated tram):
def(3) veh_posrel(MOD2, 
         ref(4) if(0)      // first
         ref(5) else
)
Example 2 (set different lengths for the parts of an articulated car):
def(3) veh_posrel(FRONT,
         cbr(3) if(1) // set length to 37,5%
         cbr(5) else  // set length to 62,5%
)

def(5) callback(
         ref(3) if(CB_WLEN) // wagon length
         ref(2) if(CB_ARTI) // articulated
         ref(1) else        // graphics
)
Example 3 (handle all cars of a 4-part tram):
def(4) veh_posabs(FRONT, 
         ref(1) if(3) // back
         ref(2) if(2) // 3rd car
         ref(3) if(1) // 2nd car
         ref(0) else  // front
)

veh_status(<block>)

This function returns the 'special' status of a vehicle. Values returned are:

StatusMeaning
INVISIBLEVehicle is invisible, e.g. in a tunnel
STOPPEDVehicle is stopped
CRASHEDVehicle is crashed

Note that the result is a bitmask, i.e. you might check for more than one bit set by simply adding above values in the if() function.

randomrel/randomabs(<triggers>, <randombit>, <list::reference>)

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.

These two functions base their randomisation on different prerequisites: randomrel() randomises the feature (vehicle, station, building, industry, object) based on its own triggers and random bits, and randomabs() randomises the feature based on its "related" object:

FeatureRelated object
VehicleFirst vehicle of consist (engine)
Stationn/a
Buildingn/a
Industry tileIndustry containing this tile
Industryn/a
Objectn/a

Description

<triggers>

This is a bit mask of triggers which cause re-randomizing. Normally, any matching trigger causes the graphics to be randomized again, but if you add ALLTRIGGERS to the bitmask, the re-randomizing only happens if all triggers have occurred.

Vehicles have 8 random bits, and the following triggers:

ValueTrigger
CONSTRUCTVehicle is built/bought
LOADVehicle gets new load of cargo (only after it was empty)
SERVICEVehicle enters a depot and is serviced
UNLOADThe consist has unloaded all cargo
ANYLOADAny vehicle of the consist receives cargo
CB32CB_32DAY returned 1
ALLTRIGGERSRe-randomizing only if all triggers have occurred

The consist triggers UNLOAD and ANYLOAD allow re-randomizing whenever the consist receives cargo after fully unloading. They should be used with randomrel(), not randomabs(), and the random-triggers should have ALLTRIGGERS added to it, to make the re-randomizing happen only if the consist was empty and then received new cargo.

<randombit>

For TTDPatch versions before 2.0.1 alpha 30, leave this at "0".

Since TTDPatch 2.0.1 alpha 30, only those bits that actually get triggered will be re-randomized. Prior versions always re-randomized all bits. This will make it possible to have independent sets of bits for independent triggers (or untriggered bits, set only at the time of purchase). 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.

<list::reference>

The number of referenced sets to choose from must be a power of 2, i.e. 2, 4, 8, 16 etc.

Example (4 different liveries by random when being bought):
def(16) randomrel(CONSTRUCT,1,ref(12),ref(13),ref(14),ref(15))

Example (load either "pinot gris", "merlot" or "chardonnay"):
def(19) randomabs(UNLOAD,0,ref(16),ref(16),ref(16),ref(17),
                  ref(17),ref(18),ref(18),ref(18))

Auxiliary functions

These functions are used to evaluate special parameters for some of the functions above.

shiftmask(<shift>,<mask>)

This function adjusts a performance function result to a more useful range. The first parameter defines the value to right-shift the result, and the second parameter gives the value with which to AND the result after shifting. Please note that parameter <mask> might be of WORD-size in a given context. See example.

ttdcargo(<cargo-ID>)

This function returns the untranslated original cargo-ID from TTD.

Global functions

cargotranslationtable(<list::label>)

This function installs a so-called "cargo translation table" (CTT) to aid with coding vehicle newGRFs that wish to support more than the standard cargo types. This table is a list of cargo labels, each put in double quotes and "quoted", like this: {"LVST"}.

Each entry represents a corresponding cargo which is meant when using its ID in a makevehicle() function, or when being listed in the vehicle's cargomask() function. See example. In this way, the vehicle newGRF doesn't need to know or care which cargo slot and cargo bit a certain cargo type uses, it can define its own ID for each cargo that it wishes to support, and thus be independent of both what cargo types are really available in the game and what slots/bits they use.

Because the vehicle's cargomask() function only allows for 32 cargo types, only the first 32 entries in the cargo translation table can make use of this cargo mask. Other cargo types have to be added via the cargo classes, so put the cargos that need exceptions to the cargo class based refitting first, so that they can go in the cargo mask.

Note that a cargo translation table cannot be set incrementally, you must set all cargo types to be used in a single cargo translation table.

Example (set Cargo Translation Table):
cargotranslationtable( 
	{"LVST"}, {"GOOD"}, {"FERT"}, {"WOOL"}, // refit start
	{"WDPR"}, {"GLAS"}, {"DYES"}, {"FOOD"}, 
	{"FISH"}, {"WATR"}, {"RUBR"}, {"FICR"}, 
	{"CMNT"}, {"VEHI"}, {"COAL"}, {"OIL_"},
	{"WOOD"}, {"IORE"}, {"STEL"}, {"PAPR"},
	{"FRUT"}, {"CORE"}, {"POTA"}, {"SAND"}, 
	{"OLSD"}, {"RFPR"}, {"PETR"}, {"BRCK"},
	{"SULP"}, {"LIME"}, {"MILK"}, {"RSGR"}, // refit end
	{"FRVG"}, {"SCRP"}, {"GRVL"}, {"AORE"},
	{"BDMT"}, {"CLAY"}, {"SALT"}, {"ENSP"},
	{"MNSP"}, {"FMSP"}, {"GRAI"}
)

...

// Truck that has special graphics for livestock, wool, fish, food and milk
// and generic graphics for all other cargoes
makevehicle(_MB124,  
	link(ref(14),MENU)
	link(ref(2),LVST)
	link(ref(7),WOOL)
	link(ref(8),FISH)
	link(ref(11),FOOD)
	link(ref(12),MILK)
	default(ref(5))
)