Oblivion XML Reference/Operators
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.
- 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.
Copy operator
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.