Operators act as a stack of sorts. Each operator element has its own value (either as the contents of its XML tag, or pulled from another element via the src and trait attributes) and it returns a value, operating with awareness of its previous sibling's returned value.
Trait elements can only contain raw number values, raw string values, or operator child elements. Operator elements can generally only contain raw number values or operator child elements. Besides copy, there are no string operators. Here are some examples of operators:
<copy>3</copy> <copy src="other_tile" trait="other_trait" /> <add>3</add> <add src="other_tile" trait="other_trait" /> <copy>&true;</copy> <!-- "&true;" equals "2" under the hood -->
Operators can also be nested up to any depth:
<copy>3</copy> <mult> <copy>1</copy> <add>2</add> </mult> <!-- This code computes: 3 * (1 + 2) -->
The following terms are used when explaining operators:
- argument
- The value inside of an operator's tags.
- current working value
- The value that has been computed right up to an operator being processed.
- to select
- To pull a value from somewhere else, using the src and trait attributes. The src attribute can be the name of any tile in the menu, or a special code called a "selector."
- to return
- The act of providing a result. As an example, the mult operator multiplies the argument by the current working value, and "returns" the result. The returned value then becomes the next operator's current working value.
In the above code sample, the add operator has a current working value of 1, and an argument of 2.
List of operatorsEdit
Copy operatorEdit
The copy operator is used to overwrite the current working value:
<copy>3</copy> <add>2</add> <!-- Current working value: 3 + 2 --> <copy>1</copy> <!-- Current working value: 1 -->
However, the copy operator can also be used to implement XML switch-cases. If the current working value is a number, and the copy operator selects a trait whose name starts and ends with an underscore, then that trait is treated as a list of choices suffixed with numbers:
<_traitName_1> First Value </_traitName_1> <_traitName_2> Second Value </_traitName_2> <_traitName_3> Third Value </_traitName_3> <string> <copy>2</copy> <add>1</add> <copy src="me()" trait="_traitName_" /> </string>
In that code example, the current working value is (2 + 1), or 3. The last copy operator selects a trait bordered by underscores (_traitName_
), so the current working value is tacked onto that trait name (_traitName_3
), and the resulting trait's value is returned. The underscore-bordered traits are referred to as cases.
The utility of this is that you can switch-case on any value. For example, the RepairMenu
is actually used for selecting an item to repair, selecting an ingredient to add to a potion, or selecting a soul gem to use for an enchantment. The user0 trait on the menu identifies what it's being used for. That means you can do something like this:
<text name="menu_title"> <_title_1> Repair your gear </_title_1> <_title_2> Pay a merchant to repair gear </_title_2> <_title_3> Select an alchemy ingredient </_title_3> <_title_4> Select an item to enchant </_title_4> <_title_5> Select a soul gem to enchant with </_title_5> <_title_6> Select a sigil stone </_title_6> <string> <copy src="RepairMenu" trait="user0" /> <copy src="me()" trait="_title_" /> </string> </text>
There are a few caveats to keep in mind regarding XML switch-cases:
- The zeroth (0) case will never be used.
- If the copy operator ends up looking for a missing trait, then the number zero will be returned — not an empty string.
- The switch-case feature doesn't handle trait names properly if they contain an underscore in the middle, e.g.
_trait_name_3
. The XML properly extracts the3
, but it will then look for_trait_3
. - According to Bethesda's code comments (in the stats menu), switch-cased strings only work on the first frame if the cases are defined before the copy operator is used.
Other operatorsEdit
- abs
- If the current working value is less than zero, then the operator's argument is added to it, and the operator returns the absolute value of the result. Otherwise, the current working value is returned verbatim.
- add
- Adds its value to the current working value, and returns the result.
- and
- Returns
&true;
if both the current working value and the operator's argument evaluate to&true;
, or&false;
- ceil
- Returns the current working value rounded up to the nearest integer. Disassembly of the executable suggests that the operator's argument is added to the current working value before the rounding is performed.
- div
- Divides the current working value by the argument, and returns the result.
- eq
- Returns
&true;
if the current working value is equal to the argument, or&false;
otherwise. Note that this operator only works on floating-point values; it always returns false for strings. - floor
- trunc
- Returns the current working value rounded down to the nearest integer. Disassembly of the executable suggests that the operator's argument is added to the current working value before the rounding is performed.
- The trunc tag isn't used in any vanilla menus, but its meaning was verified through examining the executable.
- gt
- Returns
&true;
if the current working value is greater than its value, or&false;
otherwise. - gte
- Returns
&true;
if the current working value is greater than or equal to its value, or&false;
otherwise. - log
- Computes and returns the base-10 logarithm of the current working value. Disassembly suggests that the operator's argument is not used.
- ln
- Computes and returns the natural logarithm of the current working value. Disassembly suggests that the operator's argument is not used.
- lt
- Returns
&true;
if the current working value is less than the argument, or&false;
otherwise. - lte
- Returns
&true;
if the current working value is less than or equal to its value, or&false;
otherwise. - max
- Compares the argument to the current working value, and returns whichever value is larger.
- min
- Compares the argument to the current working value, and returns whichever value is smaller.
- mod
- Returns the current working value modulo the operator's argument.
- mul
- mult
- Multiplies the current working value by the argument, and returns the result.
- neq
- Returns
&true;
if the current working value is not equal to the argument, or&false;
otherwise. - not
- Casts the argument to a Boolean, inverts it, and returns the result.
- onlyif
- Returns the current working value if the operator's argument evaluates to
&true;
, or zero otherwise. - onlyifnot
- Returns the current working value if the operator's argument evaluates to
&false;
, or zero otherwise. - or
- Returns
&true;
if one or both of the current working value and the operator's argument evaluate to&true;
. - rand
- Sets the current working value to a random integer between 1 and the operator's argument, inclusive.
- ref
- This operator generally does nothing, and is completely ignored. It is only handled by keyboard navigation.
- sub
- Subtracts the argument from the current working value, and returns the result.
SelectorsEdit
When specifying operators within a trait's value, you can specify that the operator should pull its own value from a different trait defined on the same tile, or any trait defined on a different tile, by using the src attribute. The value of the src attribute can be the name of any tile in the same menu, or a special code called a selector. The selector searches for a tile according to some logic, and then "returns" that tile for use.
Note that either way, src attribute values longer than 255 characters will be truncated.
Selectors are special strings that can retrieve tiles based on their position relative to the current tile – that is, the tile specifying a trait that is using the selector. Every selector takes the format of the selector name, followed by a pair of parentheses; the parentheses may also contain an "argument," though not all selectors use these. The syntax looks like this:
<copy src="selectorName(selectorArgument)" trait="desiredTrait" />
Internally, the selector returns (a pointer to) the tile, or it returns nothing (nullptr
). If it returns a tile, the game will pull the specified trait from that tile; if the tile or the trait do not exist, the float 0.0 is used.
- child
- If an argument is specified, then this selector's searches the current tile's child and descendant tiles for one with the same name as the passed-in argument, in backwards-sibling order; returns the found tile or nullptr. Otherwise, the selector returns the current tile's last child or nullptr.
- "Backwards-sibling order" is a clumsy improvised term to describe searching downward through the tile hierarchy, but iterating over child tiles in reverse order, such that a tile's last child is checked before its first child. This reflects the order in which tiles are stored internally.
- last
- Code analysis indicates that this retrieves an unknown tile belonging to the current tile's containing menu. It may return the last tile generated from a template. A specified argument will be ignored.
- me
- Retrieves the current tile. Use this to allow one trait on a tile to use the value of another trait on the same tile. A specified argument will be ignored.
- parent
- Returns the current tile's parent tile. A specified argument will be ignored.
- screen
- A special-case tile maintained by the game engine; it exists outside of all menus. Its width, height, cropX, and cropY traits are set to the (normalized) screen dimensions and to the safe zone bounds. Internally, this tile serves as the "menu root" (and is identified as such by OBSE): it is the parent tile of all menus' root tiles.
- sibling
- If an argument is specified, searches the current tile's siblings for one with the same name as the passed-in argument; returns the found tile or nullptr. Otherwise, returns the current tile's previous sibling or nullptr.
- strings
- A special-case tile maintained by the game engine; it exists outside of all menus. Its contents are defined by the strings.xml file; each string is an underscore-prefixed trait. Bethesda used this to make it easy to localize Oblivion for different languages.