Difference between revisions of "VarAction2Advanced"

From GRFSpecs
Jump to navigationJump to search
m (Fix table format)
(ottd always behaved like that; though only now it is official)
 
(14 intermediate revisions by 5 users not shown)
Line 3: Line 3:
 
A regular action 2 can access one variable and perform limited modifications on it (shift, and, add, divide/modulo) instead of a simple variable access. Using an advanced action 2, it is possible to do a nearly unlimited number of many different operations on several variables.
 
A regular action 2 can access one variable and perform limited modifications on it (shift, and, add, divide/modulo) instead of a simple variable access. Using an advanced action 2, it is possible to do a nearly unlimited number of many different operations on several variables.
   
In addition, using procedure calls (with variable 7E), it is possible to reuse variational action 2 blocks in several places on the NFO code.
+
In addition, using procedure calls (with variable 7E), it is possible to reuse VarAction2 blocks in several places on the NFO code.
   
==Format==
+
== Format ==
   
An advanced variational action 2 looks as follows:
+
An advanced VarAction2 looks as follows:
   
 
<Sprite-number> * <Length> 02 <feature> <set-id> <type> <variable> <varadjust> '''[<operator> <variable> <varadjust>]'''... <nvar> (<set-id> <low-range> <high-range>){n} <default>
 
<Sprite-number> * <Length> 02 <feature> <set-id> <type> <variable> <varadjust> '''[<operator> <variable> <varadjust>]'''... <nvar> (<set-id> <low-range> <high-range>){n} <default>
Line 13: Line 13:
 
The new elements are the <operator> (a byte), followed by another <variable> and <varadjust> pair. &nbsp;This sequence may be repeated as often as necessary, by setting the appropriate bit in the previous <varadjust> (see below).
 
The new elements are the <operator> (a byte), followed by another <variable> and <varadjust> pair. &nbsp;This sequence may be repeated as often as necessary, by setting the appropriate bit in the previous <varadjust> (see below).
   
===varadjust===
+
=== varadjust ===
   
 
<varadjust> itself has the same format as a regular var. action 2, however to perform a calculation, bit 5 in <shift-num> has to be set:
 
<varadjust> itself has the same format as a regular var. action 2, however to perform a calculation, bit 5 in <shift-num> has to be set:
Line 31: Line 31:
 
Bit 5 needs to be set for each <shift-num>, except the last one that isn't going to be followed by another calculation (i.e., <operator> <variable> <varadjust> set). Bit 5 clear terminates the calculation and uses the resulting value for checking the ranges determining the set-id to use (or, if nvar=0, as a callback result).
 
Bit 5 needs to be set for each <shift-num>, except the last one that isn't going to be followed by another calculation (i.e., <operator> <variable> <varadjust> set). Bit 5 clear terminates the calculation and uses the resulting value for checking the ranges determining the set-id to use (or, if nvar=0, as a callback result).
   
===operator===
+
=== operator ===
   
This field, and the following variable/varadjust pair, exist only if the previous shift-num had bit 5 set. This field has escape sequences for each of its valid values, as shown in the table below. See [[GRFActionsDetailed#Byte_order|the discussion of escape sequences]] for further information on escape sequences in general. Can have the following values:
+
This field, and the following variable/varadjust pair, exist only if the previous shift-num had bit 5 set. This field has escape sequences for each of its valid values, as shown in the table below. See [[GRFActionsDetailed#Byte order|the discussion of escape sequences]] for further information on escape sequences in general. Can have the following values:
   
 
{|
 
{|
!Value!!Escape!!Effect!!Notes
+
!Value!!Escape!!Effect!!Version!!Notes
 
|-
 
|-
|00||\2+||result = val1 + val2||
+
|00||\2+||result = val1 + val2||{{ottdp|0.6|2.5}}||
 
|-
 
|-
|01||\2-||result = val1 - val2||
+
|01||\2-||result = val1 - val2||{{ottdp|0.6|2.5}}||
 
|-
 
|-
|02||\2<||result = min(val1, val2)||val1 and val2 are both considered signed
+
|02||\2<||result = min(val1, val2)||{{ottdp|0.6|2.5}}
  +
|rowspan=2|val1 and val2 are both considered signed
 
|-
 
|-
|03||\2>||result = max(val1, val2)||val1 and val2 are both considered signed
+
|03||\2>||result = max(val1, val2)||{{ottdp|0.6|2.5}}
 
|-
 
|-
|04,05||\2u<, \2u>||result = max(val1, val2)||Same as 02/03, but operands are considered unsigned
+
|04||\2u<||result = min(val1, val2)||{{ottdp|0.6|2.5}}
  +
|rowspan=2|val1 and val2 are both considered unsigned
 
|-
 
|-
|06||\2/||result = val1 / val2||val1 and val2 are both considered signed
+
|05||\2u>||result = max(val1, val2)||{{ottdp|0.6|2.5}}
 
|-
 
|-
|07||\2%||result = val1 mod val2||val1 and val2 are both considered signed
+
|06||\2/||result = val1 / val2||{{ottdp|0.6|2.5}}
  +
|rowspan=2|val1 and val2 are both considered signed
 
|-
 
|-
|08,09||\2u/, \2u%||result = val1 mod val2||Same as 06/07, but operands are considered unsigned
+
|07||\2%||result = val1 mod val2||{{ottdp|0.6|2.5}}
 
|-
 
|-
|0A||\2*||result = val1 * val2||result will be truncated to B/W/D (that makes it the same for signed/unsigned operands)
+
|08||\2u/||result = val1 / val2||{{ottdp|0.6|2.5}}
  +
|rowspan=2|val1 and val2 are both considered unsigned
 
|-
 
|-
|0B||\2&||result = val1 & val2||bitwise AND
+
|09||\2u%||result = val1 mod val2||{{ottdp|0.6|2.5}}
 
|-
 
|-
  +
|0A||\2*||result = val1 * val2||{{ottdp|0.6|2.5}}||result will be truncated to B/W/D (that makes it the same for signed/unsigned operands)
|0C||\2<nowiki>|</nowiki>||result = val1 <nowiki>|</nowiki> val2||bitwise OR
 
 
|-
 
|-
|0D||\2<nowiki>^</nowiki>||result = val1 <nowiki>^</nowiki> val2||bitwise XOR
+
|0B||\2&||result = val1 & val2||{{ottdp|0.6|2.5}}||bitwise AND
 
|-
 
|-
 
|0C||\2<nowiki>|</nowiki>||result = val1 <nowiki>|</nowiki> val2||{{ottdp|0.6|2.5}}||bitwise OR
|0E||\2s or \2sto (a)||var7D[val2] = val1, result = val1||Store result, available since 2.6 r1246. val2 must not exceed FF (before r1301) or 10F (starting from r1301) (*)
 
 
|-
 
|-
|0F||\2r or \2rst (a)||result = val2||Available since 2.6 r1246
+
|0D||\2<nowiki>^</nowiki>||result = val1 <nowiki>^</nowiki> val2||{{ottdp|0.6|2.5}}||bitwise XOR
 
|-
 
|-
|10||\2psto (c)||var7C[val2] = val1, result = val1||Store result into persistent storage, available since 2.6 r1315 (**)
+
|0E||\2s or \2sto <ref name="renum1265">Supported since grfcodec and nforenum r1265 (1.0.0 and 3.4.4, respectively.)</ref>||var7D[val2] = val1, result = val1||{{ottdp|0.6|2.6|ttdprev=r1246}}||Store result. See [[Storages#Temporary storage|Temporary storage]].
 
|-
 
|-
 
|0F||\2r or \2rst <ref name="renum1265"/>||result = val2 <ref>Operator 0F is only useful when using variable 7B, or immediately after operators 0E or 10, to store the result of a calculation, and then discard that result and start fresh.</ref>||{{ottdp|0.6|2.6|ttdprev=r1246}}||
|11||\2ror or \2rot (b)||result = val1 rotate right val2||Always a 32-bit rotation. Available since 2.6 r1651
 
 
|-
 
|-
  +
|10||\2psto <ref name="grfcodec100">Supported since grfcodec 1.0.0 and nforenum 4.0.0.</ref>||var7C[val2] = val1, result = val1||{{ottdp|0.6|2.6|ttdprev=r1315}}||Store result into persistent storage. See [[Storages#Persistent storage|Persistent storage]].
|12||\2cmp (c)||see notes||Result is 0 if val1<val2, 1 if val1=val2 and 2 if val1>val2. Both values are considered signed. Available since 2.6 r1698 (***)
 
 
|-
 
|-
  +
|11||\2ror or \2rot <ref>Supported since grfcodec and nforenum r1655 (1.0.0 and 3.4.6, respectively.)</ref>||result = val1 rotate right val2||{{ottdp|0.6|2.6|ttdprev=r1651}}||Always a 32-bit rotation.
|13||\2ucmp (c)||see notes||The same as 12, but operands are considered unsigned. Available since 2.6 r1698 (***)
 
 
|-
 
|-
 
|12||\2cmp <ref name="grfcodec100"/>||see notes||{{ottdp|0.6|2.6|ttdprev=r1698}}||Result is 0 if val1<val2, 1 if val1=val2 and 2 if val1>val2. Both values are considered signed. <ref name="compare">Operations 12 and 13 should be preferred over operation 01 (subtraction) when only the relation of the two values is needed and the difference itself is irrelevant. This is because operation 01 can overflow and give the wrong sign if the difference is too big, but comparisons work correctly for all values.</ref>
|14||\2<< (c)||result = val1 << val2||shift left; val2 should be in the range 0 to 31. Available since OpenTTD r20332 and TTDPatch r2335.
 
 
|-
 
|-
 
|13||\2ucmp <ref name="grfcodec100"/>||see notes||{{ottdp|0.6|2.6|ttdprev=r1698}}||The same as 12, but operands are considered unsigned. <ref name="compare"/>
|15||\2u>> (c)||result = val1 >> val2||shift right (unsigned); val2 should be in the range 0 to 31. Available since OpenTTD r20332 and TTDPatch r2335.
 
 
|-
 
|-
|16||\2>> (c)||result = val1 >> val2||shift right (signed); val2 should be in the range 0 to 31. Available since OpenTTD r20332 and TTDPatch r2335.
+
|14||\2<< <ref name="grfcodec100"/>||result = val1 << val2||{{ottdp|1.1|2.6|ottdrev=r20332|ttdprev=r2335}}||shift left; val2 should be in the range 0 to 31.
  +
|-
 
|15||\2u>> <ref name="grfcodec100"/>||result = val1 >> val2||{{ottdp|1.1|2.6|ottdrev=r20332|ttdprev=r2335}}||shift right (unsigned); val2 should be in the range 0 to 31.
  +
|-
  +
|16||\2>> <ref name="grfcodec100"/>||result = val1 >> val2||{{ottdp|1.1|2.6|ottdrev=r20332|ttdprev=r2335}}||shift right (signed); val2 should be in the range 0 to 31.
 
|}
 
|}
   
 
where val1 is the value resulting from the current variable/varadjust pair (or the result of the previous calculations if this isn't the first pair) and val2 is the value resulting from the following variable/varadjust pair. By setting bit 5 of shift-num repeatedly, you can combine several variables together before making your decision.
 
where val1 is the value resulting from the current variable/varadjust pair (or the result of the previous calculations if this isn't the first pair) and val2 is the value resulting from the following variable/varadjust pair. By setting bit 5 of shift-num repeatedly, you can combine several variables together before making your decision.
   
  +
<references/>
(a) Supported since grfcodec and nforenum r1265 (1.0.0 and 3.4.4, respectively.)
 
 
(b) Supported since grfcodec and nforenum r1655 (1.0.0 and 3.4.6, respectively.)
 
 
(c) Supported since grfcodec 1.0.0 and nforenum 4.0.0.
 
 
<nowiki>*</nowiki> Registers above FFh are write-only because you can't give a value bigger than FFh to var 7D. These registers are special; they allow passing extra data to some 4x and 6x variables, as well as returning extra data from callbacks.
 
 
<nowiki>**</nowiki> Currently only feature A (industries) is supported, you have 16 4-byte slots per industry. This storage can be reached from industry tiles as well by using var. action 2 type 82/86/8A. This operation shouldn't be used when the industry structure isn't available.
 
 
<nowiki>***</nowiki> Operations 12 and 13 should be preferred over operation 01 (subtraction) when only the relation of the two values is needed and the difference itself is irrelevant. This is because operation 01 can overflow and give the wrong sign if the difference is too big, but comparisons work correctly for all values.
 
 
Registers (temporary and persistent alike) always have the size of 4 bytes. If you're writing them using smaller sizes (anything but type 89/8A), the given value will be sign-extended to 4 bytes. Therefore, be careful when you read a register using a bigger size than it was written with. This also applies to registers read by TTDPatch; if not indicated specifically, TTDPatch reads all 4 bytes of the register.
 
 
Note that a bitwise NOT can be done by XORing with variable 1A. Similarly, you can specify literal values (i.e. plain numbers instead of variables), by using variable 1A and an and-mask of the value you want. For example, to specify a literal 26, use variable=1A, shift-num=00 (or the higher bits set if you need further calculations), and and-mask=26. This works with B, W or D sized literals if you use the right and-mask size for the given type of action 2. The appropriate \b, \w, or \d escape sequence can be useful for specifying literals. See [[GRFActionsDetailed#Byte_order|the discussion of escape sequences]] for further information.
 
   
 
Note that a bitwise NOT can be done by XORing with variable 1A. Similarly, you can specify literal values (i.e. plain numbers instead of variables), by using variable 1A and an and-mask of the value you want. For example, to specify a literal 26, use variable=1A, shift-num=00 (or the higher bits set if you need further calculations), and and-mask=26. This works with B, W or D sized literals if you use the right and-mask size for the given type of action 2. The appropriate \b, \w, or \d escape sequence can be useful for specifying literals. See [[GRFActionsDetailed#Byte order|the discussion of escape sequences]] for further information.
Operator 0F is only useful when using variable 7B, or immediately after operators 0E or 10, to store the result of a calculation, and then discard that result and start fresh.
 
   
===Using types 81/82 (etc) simultaneously===
+
=== Using types 81/82 (etc) simultaneously ===
  +
{{ottdp|0.6|2.5|ttdprev=2.0.1 alpha 59}}
 
Since TTDPatch 2.0.1 alpha 59, it is possible to access variables using both VarAction2 type 81 and 82 (and their W/D cousins) indirectly through the new variable 1C.
   
 
This variable stores the result of the current VarAction2 and makes it available to the next VarAction2 in the chain. &nbsp;Therefore, to access, for instance when drawing a house, both house variables (type 81) and town variables (type 82), you would read the house variables in one var. action 2, type 81, and then chain to the next VarAction2, type 82. &nbsp;There, you now have access to the house variable value stored in variable 1C as well as the town variables in the regular variables.
Since 2.0.1 alpha 59, it is possible to access variables using both var.action 2 type 81 and 82 (and their W/D cousins) indirectly through the new variable 1C.
 
   
 
Note that to chain to the next VarAction2, you ''must not'' use nvar=0, because that returns the result value as a callback result. &nbsp;Instead, you need to use nvar=1, and specify the chained VarAction2 in both the <set-id> and <default> positions.
This variable stores the result of the current var.action 2 and makes it available to the next var.action 2 in the chain. &nbsp;Therefore, to access, for instance when drawing a house, both house variables (type 81) and town variables (type 82), you would read the house variables in one var. action 2, type 81, and then chain to the next var.action 2, type 82. &nbsp;There, you now have access to the house variable value stored in variable 1C as well as the town variables in the regular variables. Since 2.6 r1246, you may also store values in the 7D array, where they will persist for the life of the action 2 chain, unless overwritten.
 
   
  +
{{ottdp|0.6|2.6|ttdprev=r1246}}
Note that to chain to the next var.action 2, you ''must not'' use nvar=0, because that returns the result value as a callback result. &nbsp;Instead, you need to use nvar=1, and specify the chained var.action 2 in both the <set-id> and <default> positions.
 
  +
Since TTDPatch 2.6 r1246, you may also store values in the 7D array, where they will persist for the life of the action 2 chain, unless overwritten.
   
===Using procedures===
+
=== Using procedures ===
  +
{{ottdp|0.6|2.5|ttdprev=2.5 beta 7}}
 
When the variable in a VarAction2 is 7E, the procedure given by the 60+x parameter is invoked. This means that the byte following the variable number (7E) specifies a variational or random action 2 ID to call, similarly to how a regular VarAction2 branches to other action 2 entries. However, instead of branching, it is a subroutine call, with the value calculated by the called entry being used as variable value.
   
  +
The called action 2 should return a callback result. {{ottdp|0.6|2.6|ttdprev=r2367}} If the chain ends in a regular action2, the returned result is 0xFFFF. For earlier versions of TTDPatch the variable 7E value was undefined in this case.
When the variable in a var.action 2 is 7E, the procedure given by the 60+x parameter is invoked. This means that the byte following the variable number (7E) specifies a variational or random action 2 ID to call, similarly to how a regular var.action 2 branches to other action 2 entries. However, instead of branching, it is a subroutine call, with the value calculated by the called entry being used as variable value.
 
   
The called action 2 must return a callback result. If the chain ends in a regular action 2 instead of returning a callback result, the variable 7E value is undefined. &nbsp;Because callback results are limited to 15 bits, to access the full 32 bit result you can read variable 1C instead (e.g. by anding the 7E result with 0 and then adding var. 1C).
+
Because callback results are limited to 15 bits, to access the full 32 bit result you can read variable 1C instead (e.g. by and-ing the 7E result with 0 and then adding var. 1C).
   
 
The feature of this called action 2 is ignored, and all variables accessed use the same feature as the calling VarAction2. It is however valid to use any type of VarAction2, e.g. types 81 and 82 and the various byte/word/dword sizes may be mixed. It is also valid to use nvar=00 to return the computed value as callback result, instead of specifying ranges, although this way the return value is still limited to 15 bits. When using a random action 2 in the called chain, random triggers are processed in the same way as in "normal" chains.
In TTDPatch 2.5 beta 9 and earlier and in TTDPatch 2.6 prior to r846, var.action 2s that attempt to perform multiple sequential (as opposed to nested) procedure calls will have undefined results.
 
   
  +
{{ottdp|0.6|2.5/2.6|ttdprev=r846, will be in yet unreleased 2.5 beta 10}}
The feature of this called action 2 is ignored, and all variables accessed use the same feature as the calling var.action 2. It is however valid to use any type of var.action 2, e.g. types 81 and 82 and the various byte/word/dword sizes may be mixed. It is also valid to use nvar=00 to return the computed value as callback result, instead of specifying ranges, although this way the return value is still limited to 15 bits. When using a random action 2 in the called chain, random triggers are processed in the same way as in &quot;normal&quot; chains.
 
 
In TTDPatch 2.5 beta 9 and earlier and in TTDPatch 2.6 prior to r846, VarAction2s that attempt to perform multiple sequential (as opposed to nested) procedure calls will have undefined results.

Latest revision as of 19:06, 22 August 2011

Introduction

A regular action 2 can access one variable and perform limited modifications on it (shift, and, add, divide/modulo) instead of a simple variable access. Using an advanced action 2, it is possible to do a nearly unlimited number of many different operations on several variables.

In addition, using procedure calls (with variable 7E), it is possible to reuse VarAction2 blocks in several places on the NFO code.

Format

An advanced VarAction2 looks as follows:

<Sprite-number> * <Length> 02 <feature> <set-id> <type> <variable> <varadjust> [<operator> <variable> <varadjust>]... <nvar> (<set-id> <low-range> <high-range>){n} <default>

The new elements are the <operator> (a byte), followed by another <variable> and <varadjust> pair.  This sequence may be repeated as often as necessary, by setting the appropriate bit in the previous <varadjust> (see below).

varadjust

<varadjust> itself has the same format as a regular var. action 2, however to perform a calculation, bit 5 in <shift-num> has to be set:

Bit(s) Value Meaning
0..4 0..1F number of bits to right shift <variable>
5 20 an <operator> <variable> <varadjust> triple follows this <varadjust>
6 40 This is a shift-and-add-divide adjustment.
7 80 This is a shift-and-add-modulo adjustment.

Bit 5 needs to be set for each <shift-num>, except the last one that isn't going to be followed by another calculation (i.e., <operator> <variable> <varadjust> set). Bit 5 clear terminates the calculation and uses the resulting value for checking the ranges determining the set-id to use (or, if nvar=0, as a callback result).

operator

This field, and the following variable/varadjust pair, exist only if the previous shift-num had bit 5 set. This field has escape sequences for each of its valid values, as shown in the table below. See the discussion of escape sequences for further information on escape sequences in general. Can have the following values:

Value Escape Effect Version Notes
00 \2+ result = val1 + val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5
01 \2- result = val1 - val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5
02 \2< result = min(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered signed
03 \2> result = max(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5
04 \2u< result = min(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered unsigned
05 \2u> result = max(val1, val2) Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5
06 \2/ result = val1 / val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered signed
07 \2% result = val1 mod val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5
08 \2u/ result = val1 / val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 val1 and val2 are both considered unsigned
09 \2u% result = val1 mod val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5
0A \2* result = val1 * val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 result will be truncated to B/W/D (that makes it the same for signed/unsigned operands)
0B \2& result = val1 & val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 bitwise AND
0C \2| result = val1 | val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 bitwise OR
0D \2^ result = val1 ^ val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.52.5 bitwise XOR
0E \2s or \2sto [1] var7D[val2] = val1, result = val1 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1246)2.6 Store result. See Temporary storage.
0F \2r or \2rst [1] result = val2 [2] Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1246)2.6
10 \2psto [3] var7C[val2] = val1, result = val1 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1315)2.6 Store result into persistent storage. See Persistent storage.
11 \2ror or \2rot [4] result = val1 rotate right val2 Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1651)2.6 Always a 32-bit rotation.
12 \2cmp [3] see notes Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1698)2.6 Result is 0 if val1<val2, 1 if val1=val2 and 2 if val1>val2. Both values are considered signed. [5]
13 \2ucmp [3] see notes Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1698)2.6 The same as 12, but operands are considered unsigned. [5]
14 \2<< [3] result = val1 << val2 Supported by OpenTTD 1.1 (r20332)1.1 Supported by TTDPatch 2.6 (r2335)2.6 shift left; val2 should be in the range 0 to 31.
15 \2u>> [3] result = val1 >> val2 Supported by OpenTTD 1.1 (r20332)1.1 Supported by TTDPatch 2.6 (r2335)2.6 shift right (unsigned); val2 should be in the range 0 to 31.
16 \2>> [3] result = val1 >> val2 Supported by OpenTTD 1.1 (r20332)1.1 Supported by TTDPatch 2.6 (r2335)2.6 shift right (signed); val2 should be in the range 0 to 31.

where val1 is the value resulting from the current variable/varadjust pair (or the result of the previous calculations if this isn't the first pair) and val2 is the value resulting from the following variable/varadjust pair. By setting bit 5 of shift-num repeatedly, you can combine several variables together before making your decision.

  1. 1.0 1.1 Supported since grfcodec and nforenum r1265 (1.0.0 and 3.4.4, respectively.)
  2. Operator 0F is only useful when using variable 7B, or immediately after operators 0E or 10, to store the result of a calculation, and then discard that result and start fresh.
  3. 3.0 3.1 3.2 3.3 3.4 3.5 Supported since grfcodec 1.0.0 and nforenum 4.0.0.
  4. Supported since grfcodec and nforenum r1655 (1.0.0 and 3.4.6, respectively.)
  5. 5.0 5.1 Operations 12 and 13 should be preferred over operation 01 (subtraction) when only the relation of the two values is needed and the difference itself is irrelevant. This is because operation 01 can overflow and give the wrong sign if the difference is too big, but comparisons work correctly for all values.

Note that a bitwise NOT can be done by XORing with variable 1A. Similarly, you can specify literal values (i.e. plain numbers instead of variables), by using variable 1A and an and-mask of the value you want. For example, to specify a literal 26, use variable=1A, shift-num=00 (or the higher bits set if you need further calculations), and and-mask=26. This works with B, W or D sized literals if you use the right and-mask size for the given type of action 2. The appropriate \b, \w, or \d escape sequence can be useful for specifying literals. See the discussion of escape sequences for further information.

Using types 81/82 (etc) simultaneously

Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.5 (2.0.1 alpha 59)2.5 Since TTDPatch 2.0.1 alpha 59, it is possible to access variables using both VarAction2 type 81 and 82 (and their W/D cousins) indirectly through the new variable 1C.

This variable stores the result of the current VarAction2 and makes it available to the next VarAction2 in the chain.  Therefore, to access, for instance when drawing a house, both house variables (type 81) and town variables (type 82), you would read the house variables in one var. action 2, type 81, and then chain to the next VarAction2, type 82.  There, you now have access to the house variable value stored in variable 1C as well as the town variables in the regular variables.

Note that to chain to the next VarAction2, you must not use nvar=0, because that returns the result value as a callback result.  Instead, you need to use nvar=1, and specify the chained VarAction2 in both the <set-id> and <default> positions.

Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r1246)2.6 Since TTDPatch 2.6 r1246, you may also store values in the 7D array, where they will persist for the life of the action 2 chain, unless overwritten.

Using procedures

Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.5 (2.5 beta 7)2.5 When the variable in a VarAction2 is 7E, the procedure given by the 60+x parameter is invoked. This means that the byte following the variable number (7E) specifies a variational or random action 2 ID to call, similarly to how a regular VarAction2 branches to other action 2 entries. However, instead of branching, it is a subroutine call, with the value calculated by the called entry being used as variable value.

The called action 2 should return a callback result. Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.6 (r2367)2.6 If the chain ends in a regular action2, the returned result is 0xFFFF. For earlier versions of TTDPatch the variable 7E value was undefined in this case.

Because callback results are limited to 15 bits, to access the full 32 bit result you can read variable 1C instead (e.g. by and-ing the 7E result with 0 and then adding var. 1C).

The feature of this called action 2 is ignored, and all variables accessed use the same feature as the calling VarAction2. It is however valid to use any type of VarAction2, e.g. types 81 and 82 and the various byte/word/dword sizes may be mixed. It is also valid to use nvar=00 to return the computed value as callback result, instead of specifying ranges, although this way the return value is still limited to 15 bits. When using a random action 2 in the called chain, random triggers are processed in the same way as in "normal" chains.

Supported by OpenTTD 0.60.6 Supported by TTDPatch 2.5/2.6 (r846, will be in yet unreleased 2.5 beta 10)2.5/2.6 In TTDPatch 2.5 beta 9 and earlier and in TTDPatch 2.6 prior to r846, VarAction2s that attempt to perform multiple sequential (as opposed to nested) procedure calls will have undefined results.