Difference between revisions of "VariablesWML"
(→The [set_variable] tag) |
(→The [set_variables] tag) |
||
Line 114: | Line 114: | ||
=== The [set_variables] tag === | === The [set_variables] tag === | ||
− | As of 1.18.0, the '''[set_variables]''' tag can only be used as ActionWML. Support for placing it in '''[modify_unit]''' and '''[modify_side]''' is planned. See the documentation of these tags for details. | + | As of 1.18.0, the '''[[InternalActionsWML#.5Bset_variables.5D|[set_variables]]]''' tag can only be used as ActionWML. Support for placing it in '''[modify_unit]''' and '''[modify_side]''' is planned. See the documentation of these tags for details. |
=== The [variables] tag === | === The [variables] tag === |
Revision as of 15:42, 17 October 2024
Variables in WML are used to store data for later retrieval. Each variable is identified by its name. Once created, a variable persists until the end of a campaign or scenario unless explicitly cleared. Variable names can contain only alphanumerics and underscores and are case-sensitive. Though technically permitted, it is recommended not to use an underscore as the first character of a variable name.
The three basic manipulations of WML variables are assigning a value, querying the value, and clearing the variable.
- Assigning to a variable: stores a value in the variable, either creating a new variable or modifying a variable that already exists.
- Querying a variable: retrieves the last value stored in the variable (usually returning an empty string if no value was stored).
- Clearing a variable: makes the WML engine forget about that variable. This is useful for reducing overhead, since all used variables are stored in saved games.
Contents
Kinds of Variables
Scalar
A scalar variable can store a single string, number, or boolean value.
[set_variable]
name=my_variable
value="sample value"
boolean_value=yes
[/set_variable]
The full name of a scalar variable is its given name, in this case my_variable. Note that the value of the variable can be translatable or even a formula expression (Special Attribute Values).
Container
A container variable can store any number of scalar variables, as well as nested containers. There are tags to assign specific information, for instance [store_side]. To refer to a variable bar
stored in a container foo
you would write foo.bar
. This works even if one of the variable names is entirely numeric.
Array
An array variable is a numbered sequence of container variables. There are some specific tags that assign array information, for example [store_unit] and [store_locations]. One could create an array using [set_variable] like this:
[set_variable]
name=my_awesome_array[0].x
value=10
[/set_variable]
[set_variable]
name=my_awesome_array[1].x
value=12
[/set_variable]
[set_variable]
name=my_awesome_array[2].x
value=14
[/set_variable]
However, when working with arrays, it is usually easier to make use of [set_variables]. This would be written as follows:
[set_variables]
name=my_awesome_array
[value]
x=10
[/value]
[value]
x=12
[/value]
[value]
x=14
[/value]
[/set_variables]
Arrays are indexed from 0, which means that if foo
is the name of an array, foo[0]
is the full name of its first container variable, foo[1]
the full name of its second, and so on.
For an array variable, foo.length
is the special variable that always stores the number of containers in the array foo
. Hence, if the value stored in foo.length
is 18, the last container in the array would be foo[17]
.
If you try to query an array as if it were a container, then it will simply use the first index. Thus $foo.bar
would be the same as $foo[0].bar
. An explicit index inside an array is also considered a container.
Note: Do not attempt to store a scalar value to the explicit index of an array, which is a container of scalar variables. Hence referring to a variable named foo[3]
as if it were a scalar one is illegal; instead, you would use foo[3].value
to store a scalar value. (While it may appear to work to an extent if you ignore this rule, it may also cause undefined behavior. For example, loading a save of a game that contains such variables will fail with a WML error.)
Global
Most variables are global variables – they exist in the global scope. Variables created with [set_variable], [set_variables], or any of the [store_something] styled WML tags are all global variables, and variable substitution and the [variable] conditional tag only read global variables.
Unit
Unit variables are stored on a specific unit. That means that, if desired, every unit could have a variable with the same name, and it doesn't conflict with a global variable of the same name. Note that they are not global only in the sense that they are "namespaced" and not in the sense that they cannot be accessed globally.
Unit variables can be created with [modify_unit], but there is currently no way to read them directly in WML. You can read them from formulas (eg in StandardUnitFilter or AbilitiesWML) and the [filter_wml][variables] tag in StandardUnitFilter, or you can use [store_unit] to copy the unit along with all its variables into a global variable which can then be accessed normally – if you call the variable my_unit
, then you could access the scalar unit variable stamina
as $my_unit.variables.stamina
.
If modifying unit variables after storing the unit, remember to [unstore_unit] for the changes to be kept.
An example of how you can filter on a unit variable in StandardUnitFilter:
[filter]
[filter_wml]
[variables]
my_variable="test"
[/variables]
[/filter_wml]
[/filter]
Side
Side variables are stored on a specific side. That means that, if desired, every side could have a variable with the same name, and it doesn't conflict with a global variable of the same name. Note that they are not global only in the sense that they are "namespaced" and not in the sense that they cannot be accessed globally.
Side variables can be created with [modify_side], but there is currently no way to read them directly in WML. You can read them from formulas (eg in StandardSideFilter), or you can use [store_side] to copy the side along with all its variables into a global variable which can then be accessed normally – if you call the variable my_side
, then you could access the scalar side variable wood_amount
as $my_side.variables.wood_amount
.
Since there is no [unstore_side] tag, don't modify the variables in a stored side. Instead, use [modify_side] for the changes to be kept.
Assigning Variables
Variables can be created and modified using ActionWML or Lua. There are several ways to do this:
- The [set_variable] tag can assign or modify a scalar variable using a wide variety of operations.
- The [set_variables] tag can assign or modify a container or array variable using a wide variety of operations.
- The [variables] tag can be used to set up a large number of variables all at once.
- The {VARIABLE} macro can assign a new scalar variable with a given value.
- The {VARIABLE_OP} macro can modify an existing scalar variable using a wide variety of operations.
- There are several ways to assign variables from Lua. They won't be covered here, however.
The [set_variable] tag
The [set_variable] tag can be directly used as ActionWML to work with global variables, or it can be placed in the [modify_unit] or [modify_side] ActionWML tags to work with unit or side variables, respectively. See the documentation of these tags for details.
The [set_variables] tag
As of 1.18.0, the [set_variables] tag can only be used as ActionWML. Support for placing it in [modify_unit] and [modify_side] is planned. See the documentation of these tags for details.
The [variables] tag
Unlike the others, the [variables] tag cannot be directly used as ActionWML. It can be used in a number of other places, however:
- Placed in a scenario tag to assign initial global variables at scenario start.
- Placed in a [side] tag to assign initial side variables at scenario start.
- Placed in a [unit] tag to assign initial unit variables when the unit is spawned.
- Placed in [leader] with the same meaning as in [unit].
- Placed in [modify_unit] and [modify_side] ActionWML to adjust the values of several variables at once using "merge mode" (see [set_variables] documentation for the explanation of how this works).
- Seen in saved games to describe the current value of each variable when the game was saved.
When using the [variables] tag, a scalar variable is assigned using an attribute, where the attribute's key is the variable's given name, and the attribute's value is the value to be stored in the variable.
A container variable with given name foo is assigned using a [foo] tag that contains the definitions for the contained variables.
An array variable with given name foo is assigned using several [foo] tags, where the first tag describes foo[0], the second foo[1], ...
Reading Variables
Variables can be queried in a variety of ways, including substitutions, ConditionalWML, Wesnoth Formula Language, and Lua. The Lua methods won't be covered here, however.
Conditionals
Variables may be compared by using [variable] within an [if] or [while] tag. The {VARIABLE_CONDITIONAL} macro can also be used for this purpose. For more information, please refer to ConditionalActionsWML.
Filters
In unit filters, the [variables] tag can be used in [filter_wml] to check the value of unit variables, respectively.
Both unit filters and side filters also support querying variables via WFL. The details of how this works is not covered here, however.
Variable Substitution
When writing scenario events (EventWML) and filters, a scalar variable can generally be substituted in the right-hand side of any key=value assignment. To do this, enclose the full variable name to be queried between a dollar sign ($
) and a pipe (|
). The variable name should be the entire dot-separated path, where each component is an identifier (consisting of English letters, Arabic digits, and underscores) optionally followed by a pair of square brackets containing either a number or another substitution. The number represents the array index. When such a substitution appears in the text, the content which has previously been put into this variable name is used instead of the name of the variable.
In certain situations, the |
that marks the end of the variable name to be queried can be omitted. The exact rule is: if there is no |
, variable names span identifier characters (as defined in the preceding paragraph), balanced square brackets and some periods. Doubled periods, final periods (followed by a non-identifier character), and some periods that would result in an illegal variable name will not be included. If the variable name ends up being empty (e.g. when using $|
), then it will be replaced by just $
, giving you an easy way to include a dollar sign in an interpolated string.
(Version 1.13.2 and later only) If you want to substitute a default value when the variable is uninitialized or empty, add a question mark (?
) followed by the default value after the variable name, eg $varname?default text|
. In this case, the pipe (|
) is required. The default text can be anything at all, as long as it doesn't contain a pipe. (There exists no mechanism to escape a pipe so it can be included in the default text.)
A dollar sign at the end of a string or followed by a character that's not an identifier will be left alone. If it's followed by a pipe, the pipe will be removed.
Variable substitutions (and also formula substitutions, see here for more details) in a string are processed one at a time from right to left. That is, the engine finds the last dollar sign, substitutes that variable in, and then moves on to the next one in the resulting string. This means that the result of one variable substitution can affect the next one.
For example, consider the substitution $house_$colour|.title
. First the "colour" variable is substituted in. If it contains "blue", the result is $house_blue.title
, so the game will then look for a container variable called "house_blue" and substitute in its "title" key. On the other hand, if the "colour" variable contains "red", the result of the first substitution is $house_red.title
, so the engine will instead look for a container variable called "house_red" and substitute in its "title" key.
The most common use-case for this is using a scalar variable to index an array variable, for example $houses[$n].title
. In cases where substitution will occur more than once, such as a nested event, it can also be used to delay substitution to the second phase by simply adding a pipe directly after the dollar sign. The first round of substitution will remove the pipe and stop there (moving on to the next substitution left of it, if any), and then the second round of substitution will detect the variable and replace it.
Here's a more complete example:
[event]
name=turn 1
[set_variable]
name=my_variable
value= _ "Konrad"
[/set_variable]
[message]
speaker=Delfador
message= _ "Hello, $my_variable|... How are you?"
[/message]
[/event]
The WML code above will cause Delfador to say "Hello, Konrad... How are you?" on turn 1.
Literal Mode
There are a few places where the substitution mode is literal. In these places, attribute values are used exactly as provided, nothing is substituted, and the $
will not have special significance. The following places use the literal mode:
- The value of literal= inside [set_variable]
- The contents of [literal] inside [set_variables]
- The special [variables] tag, when used to give initial values to many variables upon scenario start
- In general, anything that's not nested inside [event], [command], [tunnel], [story], or a filter. For example, the [unit_type] tag cannot use variable substitution (except in filters).
[insert_tag]
The [insert_tag] tag inserts a variable as WML. In other words, the value of the passed container variable will be injected into the game as if they had been written out in WML form.
This works in any tag that permits sub-tags and supports variable substitution. That means that, like variable substitution, it will not work in places that use literal mode.
- name: The ["name"] to be given to the tag. This must be a tag which would be valid at the place where [insert_tag] is used, for anything to happen. (For example, if used as ActionWML, it should be a ActionWML tag name, and it may be a recognized subtag such as "option" when used within a [message]).
- variable: The name of the container variable which will have its value inserted into the tag. If the container variable is a WML array, [insert_tag] will insert a different tag for each of its elements.
Here's an example of its use:
[event]
name=moveto
[set_variable]
name=temp.speaker
value=Konrad
[/set_variable]
[set_variable]
name=temp.message
value= _ "Yo Kalenz!"
[/set_variable]
[insert_tag]
name=message
variable=temp
[/insert_tag]
[/event]
This is effectively identical to:
[event]
name=moveto
[message]
speaker=Konrad
message= _ "Yo Kalenz!"
[/message]
[/event]
Clearing Variables
This is done with [clear_variable] or the {CLEAR_VARIABLE} macro. It can also be done from Lua.
Like [set_variable], the [clear_variable] tag can either be used as ActionWML or placed inside [modify_unit] or [modify_side].
Automatically Stored Variables
- side_number: the number of the current player's side (may be empty during start or prestart events)
- turn_number: the number of the current turn (may be empty during start or prestart events)
- x1: this is the x-coordinate of the location where the most recent event was triggered
- y1: this is the y-coordinate of the location where the most recent event was triggered
- x2: this is the x-coordinate of the location that assisted in triggering the most recent event
- y2: this is the y-coordinate of the location that assisted in triggering the most recent event
- unit: inside an event, this is the unit at $x1,$y1
- second_unit: inside an event, this is the unit at $x2,$y2
- this_unit: inside a standard unit filter, this is the unit currently being considered for a possible match
- other_unit: inside some standard unit filters, this is an adjacent unit relevant to the match
- damage_inflicted: inside attacker_hits and defender_hits events, this is the amount of damage that was inflicted
- weapon: inside attack, attack_end, attacker_hits, attacker_misses, defender_hits, defender_misses, die and last_breath events, this is some information about the weapon that is/was being used by the unit at $x1,$y1. It contains the attributes from [attack], see UnitTypeWML.
- second_weapon: inside attack, attack_end, attacker_hits, attacker_misses, defender_hits, defender_misses, die and last_breath events, this is some information about the weapon that is/was being used by the unit at $x2,$y2. It contains the attributes from [attack], see UnitTypeWML.
- owner_side: inside a capture event, this contains the number of the previous owner (0 if previously unowned)
- teleport_unit: inside the [tunnel] tag used by a teleport ability, this is the unit with that ability
Note: Automatically stored container and array variables are only stored once one of their attributes is accessed for the first time. This means that one can sometimes get incorrect results, for instance by killing the unit at $x1,$y1 as first action in a moveto event and then accessing $unit.something. This can be worked around by previously making a dummy access, such as adding 0 to hitpoints.
Variable Usage Examples
Consider a saved game with the following [variables] tag (or a freshly started scenario with that tag)
[variables]
attitude_of_elves=hate
attitude_of_dwarves=love
attitude_of_humans=like
current_opponent=elves
[/variables]
Then,
[message]
message="Oh, I see $current_opponent|! They surely $attitude_of_$current_opponent|| us!"
[/message]
displays the message
Oh, I see elves! They surely hate us!
Consider another game with variables
[variables]
our_side=1
their_side=2
[/variables]
where side 1 has 75 gold, and side 2 50 gold. Then,
[store_side]
side=$our_side
variable=we
[/store_side]
[store_side]
side=$their_side
variable=they
[/store_side]
[message]
message=We have $we.gold gold, they have $they.gold gold.
[/message]
[if]
[variable]
name=we.gold
greater_than=$they.gold
[/variable]
[then]
[message]
message=This should be easy!
[/message]
[/then]
[else]
[message]
message=This will not be easy!
[/message]
[/else]
[/if]
[clear_variable]
name=we
[/clear_variable]
[clear_variable]
name=they
[/clear_variable]
displays the messages
We have 75 gold, they have 50 gold. This should be easy!
If side 2 had 100 gold instead, the same code would display the messages
We have 75 gold, they have 100 gold. This will not be easy!
The code
[store_unit]
[filter]
canrecruit=yes
side=1
[/filter]
variable=leader
[/store_unit]
[message]
message=Our leader's first attack does $leader[0].attack[0].damage damage per hit.
[/message]
[clear_variable]
name=leader
[/clear_variable]
always displays a true sentence.
You may find more complicated examples of variable use in the UsefulWMLFragments section.