The m4nfo User manual and Technical Report

General Functions

Using general functions

Introduction

General functions are feature-independent functions, e.g. for parameter and error handling, branching inside the newGRF, definition of text strings, etc. Unlike global functions, they are never used inside a control chain, but either stand-alone or as parameters to other functions:

Function Description
InitialisationInitialisation functions
Flow of controlFlow of control functions and parameter handling
Error handlingFunctions for error handling and newGRF de-activation
Auxiliary functionsAuxiliary functions
Callback helper functionsCallback helper functions
Sound handlingSound handling functions
RecolouringFunctions for recolouring purposes
Price and cost handlingFunctions for including 'real' prices and costs
basecost(<List::(<Label>,<Byte>)>)Setting base costs
grf_override(<block>)Define newGRF overrides
insertcomment(<String>)Insert a permanent comment into the generated nfo code
random_[im]pair(<range>)get even/odd random number from range
readregister(<register>, <reference>)read register and chain
setproperties(<ID> | <Range::ID>)Apply single property function to feature ID(s)
setrefreshbox(<block>)setrefreshbox
setregisters(<register>, <List::value>, <reference>)set register(s) and chain
snowlinetable(<Byte*12*32>)Set snow line height data

Description

basecost(<List::(<Label>,<Byte>)>)

This function allows to set base costs, which define how much everything in game costs. Each cost is calculated from a (fixed) factor times the base cost, which is adjusted by inflation every month.

The function takes as its parameter a list of pairs of cost entries and factors. The default factor of a base cost entry is '1', which leaves the base cost unchanged. Doubling the factor doubles the base cost, negative factors will halve it (i.e., -64,-32,-16,-8,-4,-2,1,2,4,8,16,32,64,128).

Available base cost entries are:

ID Label Default Description
0x01BUILD_TRACK100build track tile
0x02BUILD_ROAD95build road tile
0x03BUILD_SIGNAL65place signal
0x04BUILD_BRIDGE275build bridge tile
0x05BUILD_DEPOT600build rail depot
0x06BUILD_RVDEPOT500build road vehicle depot
0x07BUILD_SHIPDEPOT700build ship depot
0x08BUILD_TUNNEL450 build tunnel unit
0x09BUILD_PLAT200build station platform tile
0x0BBUILD_AIRPORT600build airport tile
0x0CBUILD_BUS200build bus station
0x0DBUILD_LORRY200build lorry loading area
0x0EBUILD_DOCK350build dock
0x0FBUILD_ENGINE400,000locomotive purchase
0x10BUILD_WAGON2000wagon purchase
0x11BUILD_PLANE700,000build aircraft
0x12BUILD_RV14,000road vehicle purchase
0x13BUILD_SHIP65,000ship purchase
0x14BUILD_TREE20plant tree
0x15BUILD_LAND250raise/lower land
0x16CLEAR_GRASS20clear grass tile
0x17CLEAR_ROUGH40clear rough land tile
0x18CLEAR_ROCK200clear rocky tile
0x19CLEAR_FIELD500clear field tile
0x1ADEL_TREE20remove tree
0x1BDEL_TRACK-70remove track tile
0x1CDEL_SIGNAL10remove signal
0x1DDEL_BRIDGE50remove bridge tile
0x1EDEL_DEPOT80remove rail depot
0x1FDEL_RVDEPOT80remove road vehicle depot
0x20DEL_SHIPDEPOT90remove ship depot
0x21DEL_TUNNEL30remove tunnel tile
0x22CLEAR_WATER10,000clear water tile
0x23DEL_PLAT50remove station platform tile
0x24DEL_AIRPORT30remove airport tile
0x25DEL_BUS50remove bus station
0x26DEL_LORRY50remove lorry loading area
0x27DEL_DOCK55remove dock
0x28DEL_HOUSE1600remove house
0x29DEL_ROAD40remove road tile
0x2ARUN_STEAM5600steam engine running cost
0x2BRUN_DIESEL5200diesel engine running cost
0x2CRUN_ELECTRIC4800electric engine running cost
0x2CRUN_ENGINE4800locomotive running cost
0x2DRUN_PLANE9600aircraft running cost
0x2ERUN_RV1600road vehicle running cost
0x2ERUN_WAGON1600wagon/coach running cost
0x2FRUN_SHIP5600ship running cost
0x30BUILD_INDUSTRY1,000,000fund industry
0x31DEL_INDUSTRY1600remove industry
0x32BUILD_OBJECT40build object
0x33DEL_OBJECT40remove object
0x34BUILD_WAYP600build waypoint
0x35DEL_WAYP80remove waypoint
0x36BUILD_BUOY350build buoy
0x37DEL_BUOY50remove buoy
0x3CBUILD_CANAL5000build canal
0x3DDEL_CANAL5000remove canal
0x3EBUILD_ACQUAE10,000build aquaeduct
0x3FDEL_ACQUAE2000remove aquaeduct
0x40BUILD_LOCK7500build lock
0x41DEL_LOCK2000remove lock

Example (set train base costs):
basecost(
	{BUILD_SIGNAL,4},
	{BUILD_BRIDGE,2},
	{BUILD_DEPOT,4},
	{BUILD_TUNNEL,8},
	{BUILD_ENGINES,2},
	{BUILD_WAGGONS,4},
	{DEL_SIGNAL,2},
	{DEL_BRIDGE,4},
	{DEL_DEPOT,2},
	{DEL_TUNNEL,8},
)

Be aware that using basecost() with multiple parameters will produce the same number of nfo code 'sprites'. Therefore, if a basecost() function needs to be skipped, the same number has to be used in a skipif() function, not just "1". For an example, see here.

grf_override(<block>)

This function needs to be used to enable newGRFs to modify data of another newGRF in OpenTTD. It provides a list of 'source' and 'target' newGRF IDs to let engines in the source newGRF "override" those in the target newGRF, but only when OpenTTD's "dynamic engines" feature is enabled.

The parameter block is set up by one or more entries of function maymodify().

Multiple entries may be used, and different newGRFs can be set to override the same 'target' newGRF, but only the last instance of a 'source' newGRF is active. newGRF IDs that are not present will have no effect.

The scope of this feature is quite limited and it should be used only for sets that modify data of another set, for example the DBSetXL ECS addon for DBSetXL, or the censored version of LV4.

maymodify(<Label>, <Label>)

This function takes a pair of newGRF IDs (1. source-ID, 2. dest-ID), and has to be used in the context of grf_override(), see above.

Example (DBXL_ECS may override DBXL):
// "enginepool" only available in OTTD
skipif(1, PLATFORM, ==, PATCH)

// if OTTD: DBXL_ECS may override DBXL
grf_override(
	maymodify(GRF_DBXLECS, GRF_DBXL)
)

random_[im]pair(<range>)

These are two functions which return even or odd random numbers for a given range. The return value is a callback, to be used as input to other functions.

E.g., possible results from

random_pair(4 .. 11) might be 4, 6, 8, or 10 and
random_impair(4 .. 11) could be 5, 7, 9, or 11.

A nice use case would be to include randomisation into the setregisters() function when working with ASL:

def(_PAIR) random_pair(4 .. 35) // return even random numbers

def(10) setregisters(5, sub(_PAIR), ref(RESOLVE))

which would replace this explicit code:

Example:
def(1) setregisters(5,4, ref(RESOLVE))
def(2) setregisters(5,6, ref(RESOLVE))
def(3) setregisters(5,8, ref(RESOLVE))
def(4) setregisters(5,10, ref(RESOLVE))
def(5) setregisters(5,12, ref(RESOLVE))
def(6) setregisters(5,14, ref(RESOLVE))
def(7) setregisters(5,16, ref(RESOLVE))
def(8) setregisters(5,18, ref(RESOLVE))
def(9) setregisters(5,20, ref(RESOLVE))
def(10) setregisters(5,22, ref(RESOLVE))
def(11) setregisters(5,24, ref(RESOLVE))
def(12) setregisters(5,26, ref(RESOLVE))
def(13) setregisters(5,28, ref(RESOLVE))
def(14) setregisters(5,30, ref(RESOLVE))
def(15) setregisters(5,32, ref(RESOLVE))
def(16) setregisters(5,34, ref(RESOLVE))

def(10) random(BUILT,16, ref(1),ref(2),ref(3),ref(4),ref(5),ref(6),ref(7),ref(8),
	ref(9),ref(10),ref(11),ref(12),ref(13),ref(14),ref(15),ref(16))

Or, better yet, setting registers 5 (for x-) and register 6 (for y-direction) in one go:

def(_PAIR) random_pair(4 .. 35) // return even random numbers
def(_IMPAIR) random_impair(4 .. 35) // return odd random numbers

def(10) setregisters(5,{sub(_PAIR), sub(_IMPAIR)}, ref(RESOLVE))

Note that for stations, there are only 4 random bits available per tile. So, to get tile-based randomness, don't use ranges larger than 16.

setproperties(<ID> | <Range::ID>)

This function is used to set (or overwrite) a single property function, either for a single ID or a continuous range of IDs. It is useful in cases where one single property has to be modified due to external reasons, for example based on the value of a parameter.

IDs may be for any of TTD's features (vehicles, stations, houses, ...).

Example 1 (set railtype property due to parameter value):
// main line electrified
setproperties(_BR111,
	railtype(SACE)
)

// branch line
setproperties(_BR92 .. _BR38,
	railtype(SABN)
)

Example 2 (set bridge clearance for stations to 3 levels in case of NMF):
skipif(1, PLATFORM, !=, NMF)
setproperties(ANDRAE .. BUIR,
	bridge_height({3,3,3,3})
)

setregisters(<register>, <List::value>, <reference>)

This function sets registers, starting with <register>, to values given in the list, and chains to <reference>. By supplying '-' as a value, setting of associated registers are suspended, which might be useful in some applications. Instead of a value, a subroutine call might be included as well, by using helper function sub(<reference>). As usual, a list consisting of more than one value has to be quoted.

// set register "4" to value "1" and chain to ref(0x14)
def(6) setregisters(4, 1, ref(0x14))

// set registers "4 .. 6" to values "1,2,3" and chain
def(6) setregisters(4, {1,2,3}, ref(0x14))

// set registers "4 .. 6" to values "1,2,3" and register "8" to "4",
// leave register "7" untouched
def(6) setregisters(4, {1,2,3,-,4}, ref(0x14))

// set register "4" to result of subroutine "5"
def(6) setregisters(4, sub(5), ref(0x14))

// set registers "4 .. 6" to values "1,2,3" and register "8" to
// result of subroutine "__randomsprite", leave register "7" untouched
def(6) setregisters(4, {1,2,3,-,sub(__randomsprite)}, ref(0x14))

This function is likely to be used in connection with advanced sprite layouts (ASL). See example, or tutorials.

snowlinetable(<Byte*12*32>)

This function installs a so-called "snow line height table", which allows to specify the snow line height for every day of the year. The table must contain 12*32 = 384 bytes, values will be read as hexadecimal numbers.

To simplify things for TTDPatch, every month has 32 entries, and the impossible combinations (like 32th January or 31th April) will never be read. The entries should be a multiply of 8 or else some visual glitches may appear. Values below "10" (hex!) and above "EF" may result in overflow and should not be used. Since the highest possible land is 0x78 high, giving "88" or above will effectively disable the snow line.

If the temperate snow line is enabled, this table applies on temperate as well.

Warning:

Some code in TTD assumes that the snow line will remain constant: Some industries are built only above/below snowline and assume that the snow line won't move when they're already built. Similarly, snowy houses on arctic will still appear snowy even when the snow disappears around them. If you want to use this feature, make sure to counter these effects by overriding arctic houses and industries with snow-aware versions.

Example (variable snow line):
snowlinetable(
	20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
	20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 // jan
	20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20
	20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 // feb
	20 20 20 20 20 20 20 20 20 20 20 28 28 28 28 28
	28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28 // mar
	28 28 30 30 30 30 30 30 30 30 30 30 30 30 30 30
	30 30 38 38 38 38 38 38 38 38 38 38 38 38 38 38 // apr
	38 38 40 40 40 40 40 40 40 40 40 40 40 40 40 40
	48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48 // may
	48 48 50 50 50 50 50 50 50 50 50 50 50 50 50 50
	50 50 50 50 50 58 58 58 58 58 58 58 58 58 58 58 // jun
	58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58
	58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 // jul
	58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58
	58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 58 // aug
	58 58 58 58 58 58 58 58 58 58 58 58 58 50 50 50
	50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 50 // sep
	50 48 48 48 48 48 48 48 48 48 48 48 48 48 48 48
	48 40 40 40 40 40 40 40 40 40 40 40 40 40 40 40 // oct
	40 38 38 38 38 38 38 38 38 38 38 38 38 38 38 38
	30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 30 // nov
	30 28 28 28 28 28 28 28 28 28 28 28 28 28 28 28
	28 28 28 28 20 20 20 20 20 20 20 20 20 20 20 20 // dec
)