From GRFSpecs
Revision as of 21:31, 30 August 2022 by Heresy (talk | contribs) (Changed from table to sortable.)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search
Block Syntax
switch (<feature>, (SELF|PARENT), <ID>, <expression>) {
	(<range>: <return_value>;)*

The four parameters have the following meaning:

  • <feature>: The feature for which this switch is used (see Features).
  • (SELF|PARENT): Which variables to use. SELF uses the variables of the item itself, while PARENT uses the variables of a related object. This related object differs per feature, refer to the table below.
  • <ID>: Name of this switch block. This (unique) name can be used to refer to the block from other switch- or graphics-blocks.
  • <expression>: The expression that will be evaluated to make a decision. This expression may contain variables. Instead of a single expression this may also be an array of expressions. In that case all of the array elements are evaluated in order and the last one is used to make a decision.
Feature SELF PARENT permanent
FEAT_TRAINS Engine / wagon / articulated part itself Front engine no
FEAT_ROADVEHS Road vehicle part First road vehicle part no
FEAT_SHIPS Ship Ship no
FEAT_AIRCRAFT Aircraft (a) Aircraft no
FEAT_STATIONS Station tile Town no
FEAT_CANALS Canal tile N/A no
FEAT_BRIDGES Bridge Town no
FEAT_HOUSES House tile Town no
FEAT_INDUSTRYTILES Industry tile Industry to which the tile belongs no
FEAT_INDUSTRIES Industry Town yes
FEAT_CARGOS Cargo type N/A no
FEAT_OBJECTS Object tile Town no
FEAT_ROADTYPES Roadtype on tile N/A no
FEAT_TRAMTYPES Tramtype on tile N/A no

(a) Except for mail capacity callbacks, where it refers to the mail compartment. To refer to the aircraft itself, use PARENT scope there.

<range> can either be a single number or two numbers separated by two dots. In the first case, the range matches if the result of the computation is equal to the supplied value. If a range (a .. b) is supplied, it matches if the computed result is within that range. If several lines of the switch block match the expression being tested the first applicable result will be returned.

Possible values for <return_value> are the following:

  • <ID>; chains to the (random-)switch or spritegroup with the given ID.
  • return (<expression>|<string>); returns the given expression or string as a result of the callback. A returned expression may contain variables (with the same scope as the switch block itself), parameter accesses and everything else that a normal switch-expression may contain.
  • return; returns the computed value directly as a callback result. Note that the maximum value for callback results is 32511 (0x7EFF).
  • CB_FAILED; stops processing and returns an explicit failure code.

Some example code is given below. Note that the code does not have an actual meaning, it is merely meant to demonstrate all the various options available.

 switch (FEAT_TRAINS, PARENT, some_vehicle_switch, (position_in_consist + param[1]) % 4) {
 	0..2: return string(STRING_FOO_BAR); //return a text message
 	3: return; //return the computed value
 	5..300: return 42; //42 is always a good answer
 	400: other_switch; //chain to some other switch block
 	401: return num_vehs_in_consist + 1; //return a value with a variable access
 	CB_FAILED; //return a failure result as default

You can also use temporary variables and with some features also pemanent variables to store and retrieve some values by means of STORE_TEMP(value, register), LOAD_TEMP(register) and STORE_PERM(value, register) and LOAD_PERM(register[, grfid]) respectively. Using temporary storage is useful when you want to use the result of an expensive calculation in various decisions (switches) within the chain (like for example the number of a certain wagon type within a train). Permanent storage might be useful to control the long-term behaviour of an object. Note that it is only available for industries, airports and towns. For towns, you can optionally specify a grfid to load registers belonging to a different grf. Note that accessing (both reading and writing) permanent town registers thrashes the contents of temporary register 0x100.

NML 0.3 As of NML 0.3 it is possible to specify the default value using default: <value>; as one of the 'ranges'. Specifying multiple default values (either this way or by the classical method) is of course not possible, but omitting a default value is allowed. In that case, if none of the ranges matches, the callback fails.