The m4nfo User manual and Technical Report

Functions for ships

Using functions for ships

Introduction

In m4nfo, ships are using four types of functions:

Functions for ship performance

These functions are used to evaluate game-intrinsic variables, and make them accessible to the ship'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 ship with its activation function. See here for an example.

Function Description
ageindays(<block>)Get age of ship counted in days
ageinyears(<block>)Get age of ship counted in years
animation(<block>)Access the animation counter
autoreplace(<block>)Get iteration number from CB_ARVS
callback(<block>)Check incidence (and type) of callback
cargo(<block>)Check cargo type
direction(<block>)Get driving direction of ship
displaymode(<block>)Set display mode for ship sprite
elseThis is really a void statement
indepot(<block>)Check if ship is in depot
lastmaintenance(<block>)Get time since last maintenance in days
property(<block>)Get property number for CB_PROP
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
soundevent(<block>)Check sound event
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 ship
veh_id(<Veh-ID>,<block>)Check ship ID
veh_status(<block>)Get special status of ship
yearbuilt(<block>)Year built (counted from year 0)
randomrel/randomabs(<trigger>, <randombit> <list::reference>)Get random reference

Description

ageindays(<block>)

This function returns the age of a ship 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 vessel):
def(1) 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 ship in years. As above, its result will be returned as a WORD.

animation(<block>)

This function returns the amount of motion that a ship has done. Its value is in units of 1/4096 of a tile. A ship 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 ship motion and 4 frames in total for motion across an entire tile. This can be overriden by applying two parameters, one for number of frames per ship motion, and the other for number of frames in total for motion across an entire tile. Note however, that graphics are only updated every time the ship actually moves.

Example:
def(4) animation(
         ref(0) if(3) // use frame3 graphics
         ref(1) if(2) // use frame2 graphics
         ref(2) if(1) // use frame1 graphics
         ref(3) else  // use frame0 graphics
)

callback(<block>)

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

direction(<block>)

This function returns the actual driving direction of the ship, 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 ship sprite. In this way, it is possible to use different sprites for the ship in question, depending on the display mode (on map, in depot, in purchase window, ...).

ValueMeaning
VIEWPORTShip is drawn in a viewport, i.e. on the map
DEPOTGUIShip is drawn in the depot GUI
VEHICLEGUIShip is drawn in the vehicle details GUI (This includes the refit GUI)
VEHICLELISTShip is drawn in the vehicle list
PURCHASELISTShip is drawn in the purchase list (This includes the autoreplace GUI)
PREVIEWGUIShip 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 graphic's 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.

indepot(<block>)

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

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

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 ship's properties by use of the function cbr().

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

PropertyMeaning
capacity()capacity
cargoaging()cargo aging (OTTD only)
price()price
runningcost()running cost
speed()speed

Instead of giving the property function with an empty parameter list you might also drop the parentheses at all.

Example (increase speed to 30 km/h after 1955):
def(5) yearbuilt(
         cbfail() if(<< 1955) // built before 1955, callback fail
         cbr(30) else         // built after 1955, set 30 km/h
) 

def(6) property(
         ref(5) if(speed()) // go test if built after 1955
         cbfail() else      // callback fail  
)

def(7) callback(
         ref(6) if(CB_PROP) // change speed
         ref(4) else        // graphics
)

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 ship.

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

def(4) 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 ships:

EventMeaning
SND_STARTStart sound, called when leaving a station
SND_TUNNELTunnel sound, called when ship enters tunnel
SND_BRKDOWNBreakdown sound, called when ship breaks down
SND_RUNNINGRunning sound, called once per engine tick, but no more than once per ship motion
SND_EFFECTTrain 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

veh_cargoclass(<block>)

This function checks the cargo class value of the cargo transported by the ship.

Example (increase speed if refrigerated cargo):
def(9) veh_cargoclass(
         cbr(40) if(REEF) // refrigerated cargo, set 40 km/h
         cbfail() else    // else keep 25 km/h
)

veh_cargotype(<block>)

This function returns the cargo type transported by the ship. The cargo type is translated if a cargo translation table has been installed.

Please note that if the newGRF 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(40) if(FISH) // refrigerated cargo (fish), set 40 km/h
         cbfail() else    // else keep 25 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 ship capacity if cargo unit weight > 1 tonne ):
def(9) veh_cargoweight(
         cbr(200) if(> 16) // reduce capacity if cargo unit weight > 1 tonne
         cbfail() else     // else keep standard capacity
)

veh_currentload(<block>)

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

Example (reduce wagon capacity if cargo unit weight > 1 tonne ):
def(9) veh_currentload(
         cbr(20) if(> 100) // reduce speed if actual load > 100 tonnes
         cbfail() else     // else keep standard speed
)

veh_status(<block>)

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

StatusMeaning
INVISIBLEShip is invisible, e.g. in a tunnel
STOPPEDShip is stopped
CRASHEDShip 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 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.

Ships have 8 random bits, and the following triggers:

ValueTrigger
CONSTRUCTShip is built/bought
LOADShip gets new load of cargo (only after it was empty)
SERVICEShip 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 versions before 2.0.1 alpha 30, leave this at "0".

Since 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 makeship() 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"}
)

...

// Ship that has special graphics for livestock, wool, fish, food and milk
// and generic graphics for all other cargoes
makeship(_DEMOL,  
	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))
)