NML:Random switch

From GRFSpecs
Revision as of 18:42, 7 July 2021 by Andythenorth (talk | contribs) (There is an absence of consistency between 'rail types' and 'railtypes' in our docs (also applies to roadtypes and tramtypes); make this page consistent with https://newgrf-specs.tt-wiki.net/wiki/NML:Properties_and_variables_and_callbacks)
Jump to navigationJump to search
Block Syntax

While a switch-block allows making a predictable decision, a random_switch allows a randomised choice between several options.

random_switch (<feature>, <type>, <ID>[, <triggers>]) {
	(independent: <other_random>;)*
	(dependent: <other_random>;)*
	(<probability>: <return_value>;)+
}

The first parameter <feature> specifies the feature to use. <type>, the second parameter, is used to define what random data is used. The supported combinations are listed in the following table. The amount of random data is given in the third column. The maximum number of possible combinations is equal to 2^x, with x being the number of random bits. Not all combinations support re-randomizing via triggers, this is indicated in the fourth column. The last column specifies the object of which the random data is used.

Feature Type # of random bits Triggers possible? Random data used
Vehicles SELF 8 Yes Vehicle itself
Vehicles PARENT 8 No Leading engine
Vehicles BACKWARD_SELF(x) 8 No Count x vehicles backward (away from the engine), starting at the vehicle itself [1]
Vehicles FORWARD_SELF(x) 8 No Count x vehicles forward (towards the engine), starting at the vehicle itself [1]
Vehicles BACKWARD_ENGINE(x) 8 No Count x vehicles backward, starting at the leading engine [1]
Vehicles BACKWARD_SAMEID(x) 8 No Count x vehicles backward, starting at the first vehicle in the chain with the same ID [1]
Stations / airport tiles SELF 16 Stations only Station
Stations / airport tiles TILE 4 Stations only Specific station tile
Canals SELF 8 No Canal tile
Houses SELF 8 Yes House tile [2]
Industry tiles SELF 8 Yes Industry tile
Industry tiles PARENT 16 Yes Industry as a whole
Industries SELF 16 No Industry (same as above)
Objects SELF 8 No Object tile (different per tile)
Railtypes SELF 2 No Rail tile (pseudo-random, based on location)
Roadtypes SELF 2 No Road tile (pseudo-random, based on location)
Tramtypes SELF 2 No Road tile (pseudo-random, based on location)
  1. 1.0 1.1 1.2 1.3 x can be any expression. It may access parameters, registers, or variables of the vehicle itself. Uses register 0x100 if x is not a constant expression between 1 and 15
  2. The random bits are initially the same for all tiles of a multi-tile house.

The third parameter <ID> can be used to refer to this block from other (random-)switch-blocks. The last parameter <triggers> is optional and allows re-randomizing when certain conditions occur. The events and accompanying trigger names are given in the following tables. To re-randomize graphics when any of a set of events occurs, use bitmask(TRIGGER_A, TRIGGER_B, .. , TRIGGER_Z) To re-randomize only when all of the events have occured, add TRIGGER_ALL_NEEDED to the list of used triggers, e.g. use bitmask(TRIGGER_ALL_NEEDED, TRIGGER_A, ...)

Vehicle triggers
Trigger Event
TRIGGER_VEHICLE_NEW_LOAD Vehicle gets new load of cargo (only after it was empty)
TRIGGER_VEHICLE_SERVICE Vehicle enters a depot and is serviced
TRIGGER_VEHICLE_UNLOAD_ALL The consist has unloaded all cargo (use with type 'SELF')
TRIGGER_VEHICLE_ANY_LOAD Any vehicle of the consist receives cargo (use with type 'SELF')
TRIGGER_VEHICLE_32_CALLBACK 32-day callback returned 1

To re-randomize when the consist has been emptied and then receives new cargo, use bitmask(TRIGGER_VEHICLE_UNLOAD_ALL, TRIGGER_VEHICLE_ANY_LOAD, TRIGGER_ALL_NEEDED)

Station triggers
Trigger Event Triggered on
TRIGGER_STATION_NEW_CARGO New cargo waiting Entire station
TRIGGER_STATION_NO_MORE_CARGO No more cargo waiting Entire station
TRIGGER_STATION_TRAIN_ARRIVES Train arrives (starts loading/unloading) Platform the train is on
TRIGGER_STATION_TRAIN_LEAVES Train leaves (done loading/unloading) Platform the train is on
TRIGGER_STATION_TRAIN_LOADS_UNLOADS Train loads/unloads cargo Platform the train is on
TRIGGER_STATION_TRAIN_RESERVES Train reserves platform using PBS Platform the train is on

The last four triggers in the table only trigger on the platform on which they occur. Note that triggers are only saved per-station, not per-tile, so using TRIGGER_ALL_NEEDED doesn't make a lot of sense for per-platform triggers

House triggers
Trigger Event
TRIGGER_HOUSE_TILELOOP The house tile is processed in the periodic tile-processing loop
TRIGGER_HOUSE_TOP_TILELOOP The top tile of the house is processed in the periodic tile-processing loop

Using TRIGGER_HOUSE_TOP_TILELOOP, it is possible to re-randomize the whole building as one unit. All tiles of a multi-tile house will get the same new random data.

Industry tile triggers
Trigger Event
TRIGGER_INDUSTRYTILE_TILELOOP The industry tile is processed in the periodic tile-processing loop
TRIGGER_INDUSTRYTILE_256_TICKS Triggers simultaneously for all tiles of the industry every 256 ticks. If the industry is a primary one, output cargo is generated at the same time.
TRIGGER_INDUSTRYTILE_CARGO_DELIVERY Cargo is delivered to the industry. If the industry is a processing one, output cargo is generated at the same time.

Industry tiles can rerandomise both their own random bits as well as the random bits of the industry (via type PARENT). The triggers for rerandomisation are in both cases shared per tile (when using TRIGGER_ALL_NEEDED), i.e. the triggers are in all cases independent for different tiles. Industries have random bits. However, they can only be rerandomised by their tiles.

<independent>: <other_random>; makes sure that this random_switch block and <other_random> use different (non-intersecting) random data. This allows making multiple randomized decisions that are independent of each other.

<dependent>: <other_random>;, on the other hand, lets this random_switch block and <other_random> use the same random data. To make this possible, it should not require more random data (i.e. have a higher sum of probabilities) than the block it is dependent on.

Both of these options require enough random data to be available. Also, the combination must make sense. For example, if A is dependent on B and B is dependent on C, then A cannot be independent of C.

Following this, there is a list of options, one of which will be chosen randomly. The syntax is <probability>: <return_value>;. The relative probability that this option will be chosen is given by <probability>. The chance is equal to the probability divided by the sum of all probabilities. If this sum is not equal to a power of two (2, 4, 8, 16, ...), then rounding is done and the probabilities may be inexact. For information about <return_value>, see the switch-block. Note that accessed variables in the return value are always in the scope of the item itself (i.e. SELF)

Note that re-randomizing is done only during a special callback, random_trigger. This callback is called whenever a trigger event occurs. As a consequence, it's absolutely necessary for the random_switch block to be executed during this callback, or trigger-based re-randomizing won't take place. Also, the callback may not fail (end with CB_FAILED), so return an arbitrary value or even a sprite set instead. This return value is not used anywhere.

Let's conclude with an example:

 random_switch (FEAT_TRAINS, SELF, random_42, bitmask(TRIGGER_VEHICLE_UNLOAD_ALL, TRIGGER_VEHICLE_ANY_LOAD, TRIGGER_ALL_NEEDED)) {
 	/* re-randomize when vehicle has been emptied and then received new cargo */
 	independent: random_43; // decision should be independent from random_43, which is defined elsewhere
 	6: group_open;          // 6/11 chance to display open wagon
 	3: group_closed;        // 3/11 chance to display closed wagon (with cover)
 	2: group_halfopen;      // 2/11 chance to display a half-covered wagon
 }