The m4nfo Tutorial

Parameter usage


Introduction

TTDPatch allows for 127 parameters (of 4 bytes) per newGRF. These parameters can be set by the user, and/or they can be used for math calculations, internal to the newGRF in question.

Hence, m4nfo provides a number of functions and macros to read, write and do math calculations on parameters:

Be aware that following examples don't result into 100% complete newGRFs, but instead concentrate on the most important code features.

How to define parameters

Parameters in TTDPatch are defined in file newgrf(w).cfg, by adding the parameters as numbers after the filename, separated by spaces:

newgrf/trainset.grf 15 3

This sets the first parameter to "15" and the second parameter to "3". What the parameters do depends on the newGRF set, so be sure to read the documentation that it provides.

In OpenTTD, parameters can be set by the user in-game, and have to be defined inside m4nfos's grfinit() function:

Example (newGRF parameter definition):
	...
	grfsetting(
		settype(ENUM,1,0,8)
		setlimits(1,4)
		setdefault(1)
		setname(
			ALL, "Capacity factor",
			D, UTF8 "Kapazitätsfaktor",
			F, UTF8 "Facteur de capacité",
			E, "Factor de capacidad",
			I, UTF8 "Fattore di capacità",
			NL, "Capaciteitsfactor",
			RUS, UTF8 "Коэффициент вместимости"
		)
	...

Here, 8 bits of parameter 1 are defined to be used as a capacity factor, with a range of 1 .. 4, the default value being set to "1".

Setting properties depending on parameters

This example shows how to set ship properties capacity and load amount depending on the value of said parameter 1. This is done by overwriting those two properties after the ships's definition with its default values.

Example (ship property definitions):
defineship(_KVAERNER,{"'Kværner' Bulk freighter"},
	intro(1961)
	climate(TEMPERATE,  ARCTIC, TROPIC)
	newgraphics()
	refittable(YES)
	price(160)
	speed(37 km/h)                
	cargotype(MENU)
	capacity(420) // tons
	loadamount(70)
	runningcost(180)
	soundeffect(4)
	callbacks(CB_RCOL, CB_RCAP, CB_LOAD, CB_TSFX)
	refitcost(3 %)
	canalspeed(40 %)
	cargoclasses(+BULK, +COVERED, -LIQUID, -PGOODS, -REEF, -HAZARD)
)

Now, to overwrite these default values with the new values for capacity and load amount, based on the capacity factor (supplied by parameter 1), we have to use m4nfo's functions skipif() and skip() to keep control of property assignment:

Example (overwriting ship properties):
// Test parameter 1 being [1 .. 4]
skipif(2,getowngrfparameter(1),!=,2)
defineship(_KVAERNER,{""},
	capacity(840)
	loadamount(140)
)
skip(5)

skipif(2,getowngrfparameter(1),!=,3)
defineship(_KVAERNER,{""},
	capacity(1260)
	loadamount(210)
)
skip(2)

skipif(1,getowngrfparameter(1),!=,4)
defineship(_KVAERNER,{""},
	capacity(1680)
	loadamount(250)
)

Hence, capacity would be changed to 1680 if parameter 1 is set to "4", to 1260 for a value of "3", to 840 for a parameter value of "2".

Setting properties dynamically

It is of course possible to use values given by parameter to change properties dynamically by use of callbacks. In the example below, we do this by use of CB_RCAP (refitted capacity callback), and by calculating the refit capacity from the value of parameter 1. This is done with help from function calculate(), and will result into different loading weights, depending on stowage factors for the cargo type in question:

Example (setting callback return values):
// all heavy cargo:
// IORE, AORE, SAND, GRVL, CLAY, LIME, BDMT (cement), SCMT => 420 tons
def(0x60, __W420) calculate({getparameter(1,) * 420},
	cbfail() else
)

// COAL, GRAI => 402 tons
def(0x61, __W402) calculate({getparameter(1,) * 402},
	cbfail() else
)

// OLSD => 384 tons
def(0x62, __W384) calculate({getparameter(1,) * 384},
	cbfail() else
)

// FERT => 350 tons
def(0x63, __W350) calculate({getparameter(1,) * 350},
	cbfail() else
)

// WDPR, SGBT => 280 tons
def(0x64, __W280) calculate({getparameter(1,) * 280},
	cbfail() else
)

What we do here, is to set up a number of labeled def()s, each returning the result of a different capacity value multiplicated by the capacity factor set via parameter 1.

Note that results are returned as callback results, since the function blocks do not return any ordinary result, except the ubiquitous default value (that cbfail() in the "else" branch) which will never be taken, only if the callback would fail at all. This is a special behaviour of the underlying nfo layer.

Now, only part missing, would be to link the cargo types to be refitted to those capacity results by use of CB_RCAP. In the same go, we're addressing the required colour of the bulk cargoes as well, by use of CB_RCOL:

Example (cargo weight handling):
...
def(0) spriteset(move(0,1),load(2,3,4,5)) // ship graphics

// IORE, SCMT
def(1) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__IORE) if(CB_RCOL) // recolour: iron ore
	ref(0) else
)

// CORE
def(2) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__CORE) if(CB_RCOL) // recolour: copper ore
	ref(0) else
)

// LIME, BDMT, CLAY, GRVL
def(3) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__LIME) if(CB_RCOL) // recolour: lime stone
	ref(0) else
)

// SAND
def(4) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__SAND) if(CB_RCOL) // recolour sand
	ref(0) else
)

// AORE
def(5) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__AORE) if(CB_RCOL) // recolour: bauxite
	ref(0) else
)

// POTA
def(6) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__POTA) if(CB_RCOL) // recolour: potash
	ref(0) else
)

// SULP
def(7) callback(
	ref(__W420) if(CB_RCAP) // 420t
	ref(__SULP) if(CB_RCOL) // recolour: sulphur
	ref(0) else
)



// GRAI
def(8) callback(
	ref(__W402) if(CB_RCAP) // 402t
	ref(__GRAI) if(CB_RCOL) // recolour: grain, cereals, wheat, maize 
	ref(0) else
)

// OLSD
def(9) callback(
	ref(__W384) if(CB_RCAP) // 384t
	ref(__OLSD) if(CB_RCOL) // recolour: oil seeds
	ref(0) else
)

// FERT
def(10) callback(
	ref(__W350) if(CB_RCAP) // 350t
	ref(__SAND) if(CB_RCOL) // recolour: sand
	ref(0) else
)

// SGBT
def(11) callback(
	ref(__W280) if(CB_RCAP) // 280t
	ref(__CORE) if(CB_RCOL) // recolour: copper ore
	ref(0) else
)


// 402t (menu)
def(12) property(
      ref(__W402) if(capacity()) 
      cbfail() else         
)

// default & menu
def(13) callback(
	ref(__W402) if(CB_RCAP) // 402t
	ref(12) if(CB_PROP)
	ref(__COAL) if(CB_RCOL) // recolour: coal
	ref(0) else
)

makeship(_KVAERNER,
	link(ref(13), MENU) // default cargo is COAL, show 402t as well, use CB_PROP!
	link(ref(13), COAL) // 402t, needs CB_PROP!
	link(ref(1),IORE, SCMT) // FIRS
	link(ref(2),CORE)
	link(ref(3),LIME, BDMT, CLAY, GRVL)
	link(ref(4),SAND)
	link(ref(5),AORE)
	link(ref(6),POTA)
	link(ref(7),SULP)
	link(ref(8),CERE, GRAI, WHEA, MAIZ)
	link(ref(9),OLSD) 
	link(ref(10),FERT)
	link(ref(11),SGBT)
	default(ref(11)) 
)

Please note def(13), which is somewhat special. Point is that COAL should be the default cargo for that ship, but the appropriate capacity (402 tons) could only be set by CB_RCAP through a manual refit operation, so it must be set by CB_PROP to be shown properly in the purchase menu and as the default.