Difference between revisions of "VariablesWML/How to use variables"

From The Battle for Wesnoth Wiki
(improperly enquoted attribute value)
m (Add one missing case of syntax hilitinh)
 
(12 intermediate revisions by 6 users not shown)
Line 21: Line 21:
 
: - variables holding a compound value, i.e. a pack of single values.
 
: - variables holding a compound value, i.e. a pack of single values.
 
They’re often called  containers in the documentation. Simple variables are created using the tag '''[set_variable]''':
 
They’re often called  containers in the documentation. Simple variables are created using the tag '''[set_variable]''':
[set_variable]
+
<syntaxhighlight lang="wml">
    name=simpleVariable
+
[set_variable]
    value=36
+
    name=simpleVariable
[/set_variable]
+
    value=36
 +
[/set_variable]
 +
</syntaxhighlight>
 
The tag defines the name and the value of the variable.
 
The tag defines the name and the value of the variable.
  
 
Next, we can access the variable value using the variable name prefixed with a dollar sign $.
 
Next, we can access the variable value using the variable name prefixed with a dollar sign $.
[modify_unit]
+
<syntaxhighlight lang="wml">
    [filter]
+
[modify_unit]
        id=$unit.id
+
    [filter]
    [/filter]
+
        id=$unit.id
    moves=$simpleVariable
+
    [/filter]
[/modify_unit]
+
    moves=$simpleVariable
 +
[/modify_unit]
 +
</syntaxhighlight>
 
This sets the moves of the unit to 36 since '''simpleVariable''' holds 36.
 
This sets the moves of the unit to 36 since '''simpleVariable''' holds 36.
  
 
When the line is  executed, the value 36 is substituted to '''$simpleVariable''', so it works as if we wrote:
 
When the line is  executed, the value 36 is substituted to '''$simpleVariable''', so it works as if we wrote:
[modify_unit]
+
<syntaxhighlight lang="wml">
    [filter]
+
[modify_unit]
        id=$unit.id
+
    [filter]
    [/filter]
+
        id=$unit.id
    moves=36
+
    [/filter]
[/modify_unit]
+
    moves=36
 +
[/modify_unit]
 +
</syntaxhighlight>
 
Using the same tag, we can change the value of '''simpleVariable''', or make some arithmetic  (see the tag documentation for the whole list).
 
Using the same tag, we can change the value of '''simpleVariable''', or make some arithmetic  (see the tag documentation for the whole list).
  
 
For example:
 
For example:
[set_variable]
+
<syntaxhighlight lang="wml">
    name=simpleVariable
+
[set_variable]
    sub=30
+
    name=simpleVariable
[/set_variable]
+
    sub=30
 +
[/set_variable]
 +
</syntaxhighlight>
 
will change the value to 6 of course. We can even set the variable to another value type:
 
will change the value to 6 of course. We can even set the variable to another value type:
[set_variable]
+
<syntaxhighlight lang="wml">
    name=simpleVariable
+
[set_variable]
    value="Delfador the Great"
+
    name=simpleVariable
[/set_variable]
+
    value="Delfador the Great"
 +
[/set_variable]
 +
</syntaxhighlight>
 
We shall not use  '''[set_variable]''' tag anymore. Instead, we shall use the '''VARIABLE''' shortcut:
 
We shall not use  '''[set_variable]''' tag anymore. Instead, we shall use the '''VARIABLE''' shortcut:
  
{VARIABLE simpleVariable "Delfador the Great"}
+
<syntaxhighlight lang="wml">
 +
{VARIABLE simpleVariable "Delfador the Great"}
 +
</syntaxhighlight>
 
stands for:
 
stands for:
[set_variable]
+
<syntaxhighlight lang="wml">
 +
[set_variable]
 +
    name=simpleVariable
 +
    value="Delfador the Great"
 +
[/set_variable]
 +
</syntaxhighlight>
 +
We shall not use the arithmetic variations of '''set_variable''' either. Instead we shall use the  [[Wesnoth_Formula_Language]] which is much more natural. Instead of:
 +
<syntaxhighlight lang="wml">
 +
[set_variable]
 
     name=simpleVariable
 
     name=simpleVariable
     value="Delfador the Great"
+
     value=35
[/set_variable]
+
[/set_variable]
We shall not use the arithmetic variations of '''set_variable''' either. Instead we shall use the  '''formulaAI''' syntax which is much more natural. Instead of:
+
[set_variable]
[set_variable]
 
 
       name=simpleVariable
 
       name=simpleVariable
       value=35
+
       add=$anotherVariable
[/set_variable]
+
[/set_variable]
[set_variable]
+
</syntaxhighlight>
      name=simpleVariable
 
      add=$anotherVariable
 
[/set_variable]
 
 
we shall write:
 
we shall write:
[set_variable]
+
<syntaxhighlight lang="wml">
    name=simpleVariable
+
[set_variable]
    value="$(35 + $anotherVariable)"
+
    name=simpleVariable
[/set_variable]
+
    value="$(35 + $anotherVariable)"
: # or
+
[/set_variable]
{VARIABLE simpleVariable "$(35 + $anotherVariable)"}
+
</syntaxhighlight>
The formulaAI syntax is easy to use, the important thing is to always put the formula in this  sequence: “'''$( …''' here comes the formula '''… )'''”
+
: or
 +
<syntaxhighlight lang="wml">
 +
{VARIABLE simpleVariable "$(35 + $anotherVariable)"}
 +
</syntaxhighlight>
 +
The [[Wesnoth_Formula_Language]] syntax is easy to use, the important thing is to always put the formula in this  sequence: “'''$( …''' here comes the formula '''… )'''”
 
In other words, '''$simpleVariable''' can be written everywhere you want  to use the value of  '''simpleVariable.'''
 
In other words, '''$simpleVariable''' can be written everywhere you want  to use the value of  '''simpleVariable.'''
 
Clearing variables can be done using the '''[clear_variable]''' tag:
 
Clearing variables can be done using the '''[clear_variable]''' tag:
[clear_variable]
+
<syntaxhighlight lang="wml">
    name=simpleVariable
+
[clear_variable]
[/clear_variable]
+
    name=simpleVariable
: # or using the following macro to delete more than one variable                                                                                     {CLEAR_VARIABLE simpleVariable,anotherOne,count} [[Please note the CLEAR_VARIABLE macro will not work if your variables names contain spaces or commas. It’s one good reason to avoid them.|<sup>2)</sup>]]
+
[/clear_variable]
 +
</syntaxhighlight>
 +
: or using the following macro to delete more than one variable: {CLEAR_VARIABLE simpleVariable,anotherOne,count} [[Please note the CLEAR_VARIABLE macro will not work if your variables names contain spaces or commas. It’s one good reason to avoid them.|<sup>2)</sup>]]
  
  
Line 92: Line 114:
 
It is a variable holding more than a simple value.
 
It is a variable holding more than a simple value.
 
A good example is the unit variable created automatically in '''moveto''' and '''attack''' events. It contains the full description of a unit, not only its name and its id. Containers are useful to store related values in a pack. All these different values are called “members” of the created container. Instead of writing this:
 
A good example is the unit variable created automatically in '''moveto''' and '''attack''' events. It contains the full description of a unit, not only its name and its id. Containers are useful to store related values in a pack. All these different values are called “members” of the created container. Instead of writing this:
{VARIABLE heroName "Delfador"}
+
<syntaxhighlight lang="wml">
{VARIABLE heroGold 250}
+
{VARIABLE heroName "Delfador"}
{VARIABLE heroFame 127}
+
{VARIABLE heroGold 250}
{VARIABLE heroFullName "Delfador the Great"}
+
{VARIABLE heroFame 127}
we can pack all this in a “hero” variable using '''[set_variables]''' (mark the ‘s’)
+
{VARIABLE heroFullName "Delfador the Great"}
[set_variables]
+
</syntaxhighlight>
    name=hero
+
we can pack all this in a “hero” variable using '''[set_variables]''' (notice the ‘s’)
    [value]
+
<syntaxhighlight lang="wml">
        name="Delfador"
+
[set_variables]
        gold=250
+
    name=hero
        fame=127
+
    [value]
        fullName="Delfador the Great"
+
        name="Delfador"
    [/value]
+
        gold=250
[/set_variables]
+
        fame=127
 +
        fullName="Delfador the Great"
 +
    [/value]
 +
[/set_variables]
 +
</syntaxhighlight>
 
Then, to get the values stored in the container, we shall use the $ sign as before, but appending the member name and a dot:[[That’s why dots are forbidden in variable names : they are used to specify members.|<sup>3)</sup>]]
 
Then, to get the values stored in the container, we shall use the $ sign as before, but appending the member name and a dot:[[That’s why dots are forbidden in variable names : they are used to specify members.|<sup>3)</sup>]]
$hero.name -> Delfador
+
 
$hero.gold -> 250
+
$hero.name -> Delfador
 +
 
 +
$hero.gold -> 250
 +
 
 
And if we want to change a value, the “gold” member for instance:
 
And if we want to change a value, the “gold” member for instance:
[set_variable]
+
<syntaxhighlight lang="wml">
    name=hero.gold
+
[set_variable]
    add=100
+
    name=hero.gold
[/set_variable]
+
    add=100
 +
[/set_variable]
 +
</syntaxhighlight>
 
It’s important to note that here, we changed the “gold” member as if it was a single variable whose name is “'''hero.gold'''”. We can also clear a member of the container in the same way:
 
It’s important to note that here, we changed the “gold” member as if it was a single variable whose name is “'''hero.gold'''”. We can also clear a member of the container in the same way:
{CLEAR_VARIABLE hero.fullName}
+
<syntaxhighlight lang="wml">
 +
{CLEAR_VARIABLE hero.fullName}
 +
</syntaxhighlight>
 
This will delete the '''fullName''' member permanently. Note it will not only clear the value, but  clear the member itself. Clearing the value would be:
 
This will delete the '''fullName''' member permanently. Note it will not only clear the value, but  clear the member itself. Clearing the value would be:
{VARIABLE hero.fullName ""}
+
<syntaxhighlight lang="wml">
 +
{VARIABLE hero.fullName ""}
 +
</syntaxhighlight>
 
Can we add later a member to an existing container ? Yes, it can be done:
 
Can we add later a member to an existing container ? Yes, it can be done:
{VARIABLE hero.hasStaff yes}
+
<syntaxhighlight lang="wml">
 +
{VARIABLE hero.hasStaff yes}
 +
</syntaxhighlight>
 
this creates the member hasStaff and set it to yes.
 
this creates the member hasStaff and set it to yes.
  
Line 126: Line 163:
  
 
All this will be clearer if we take a look at the way variables are stored. Opening a savegame with a text editor, we should find a part like this one:
 
All this will be clearer if we take a look at the way variables are stored. Opening a savegame with a text editor, we should find a part like this one:
[replay_start]   
+
<syntaxhighlight lang="wml">
    id=""
+
[replay_start]   
    [variables]   
+
    id=""
        damage_inflicted=18
+
    [variables]   
        heal_amount=10   
+
        damage_inflicted=18
        side_number=1   
+
        heal_amount=10   
        turn_number=26   
+
        side_number=1   
        x1=8   
+
        turn_number=26   
        x2=0   
+
        x1=8   
        y1=8   
+
        x2=0   
        y2=0   
+
        y1=8   
        simpleVariable=35   
+
        y2=0   
        [hero]   
+
        simpleVariable=35   
            name="Delfador"   
+
        [hero]   
            gold=250   
+
            name="Delfador"   
            fame=127   
+
            gold=250   
            fullName="Delfador the Great"   
+
            fame=127   
        [/hero]   
+
            fullName="Delfador the Great"   
        ...   
+
        [/hero]   
 +
        ...   
 +
</syntaxhighlight>
  
 
Here are our variables ! We can see simple ones are a pair name/value separated with an equal  
 
Here are our variables ! We can see simple ones are a pair name/value separated with an equal  
Line 156: Line 195:
 
Now, we can understand better what a container is. It’s a WML block, just as an ordinary tag,  
 
Now, we can understand better what a container is. It’s a WML block, just as an ordinary tag,  
 
and can hold <u>any valid WML content</u>. Look at this:  
 
and can hold <u>any valid WML content</u>. Look at this:  
[set_variables]   
+
<syntaxhighlight lang="wml">
    name=aVar   
+
[set_variables]   
    [value]   
+
    name=aVar   
        name=capture
+
    [value]   
        first_time_only=no   
+
        name=capture
        [filter]   
+
        first_time_only=no   
            side=3   
+
        [filter]   
            type=orc           
+
            side=3   
        [/filter]   
+
            type=orc           
        [set_variable]   
+
        [/filter]   
            name=tmp   
+
        [set_variable]   
            rand=1..2   
+
            name=tmp   
        [/set_variable]   
+
            rand=1..2   
    [/value]   
+
        [/set_variable]   
[/set_variables]                     
+
    [/value]   
 +
[/set_variables]                     
 +
</syntaxhighlight>
 
This is perfectly valid and creates this container variable:   
 
This is perfectly valid and creates this container variable:   
[aVar]   
+
<syntaxhighlight lang="wml">
    name=capture   
+
[aVar]   
    first_time_only=no   
+
    name=capture   
    [filter]   
+
    first_time_only=no   
        side=3   
+
    [filter]   
        type=orc   
+
        side=3   
    [/filter]   
+
        type=orc   
    [set_variable]   
+
    [/filter]   
        name=tmp   
+
    [set_variable]   
        rand=1..2   
+
        name=tmp   
    [/set_variable]   
+
        rand=1..2   
[/aVar]                                                     
+
    [/set_variable]   
 +
[/aVar]                                                     
 +
</syntaxhighlight>
 
We can modify members in the sub blocks too, using the full path to them, separated with  
 
We can modify members in the sub blocks too, using the full path to them, separated with  
 
dots. For instance:
 
dots. For instance:
{VARIABLE aVar.set_variable.rand “1..5”}   
+
<syntaxhighlight lang="wml">
 +
{VARIABLE aVar.set_variable.rand “1..5”}   
 +
</syntaxhighlight>
 
or   
 
or   
{VARIABLE aVar.filter.side 2}.   
+
<syntaxhighlight lang="wml">
 +
{VARIABLE aVar.filter.side 2}.   
 +
</syntaxhighlight>
 
Capito ?   
 
Capito ?   
  
 
We can delete members, values or even whole blocks in the same way:   
 
We can delete members, values or even whole blocks in the same way:   
{CLEAR_VARIABLE aVar.filter.type}
+
<syntaxhighlight lang="wml">
 +
{CLEAR_VARIABLE aVar.filter.type}
 +
</syntaxhighlight>
 
will remove the key ‘type’ in the filter block:
 
will remove the key ‘type’ in the filter block:
[aVar]   
+
<syntaxhighlight lang="wml">
    name=capture   
+
[aVar]   
    first_time_only=no   
+
    name=capture   
    [filter]   
+
    first_time_only=no   
        side=3   
+
    [filter]   
    [/filter]   
+
        side=3   
    [set_variable]   
+
    [/filter]   
        name=tmp   
+
    [set_variable]   
        rand=1..2   
+
        name=tmp   
    [/set_variable]   
+
        rand=1..2   
[/aVar]   
+
    [/set_variable]   
 
+
[/aVar]   
{CLEAR_VARIABLE aVar.filter } will remove the whole filter block:   
+
 
+
{CLEAR_VARIABLE aVar.filter } will remove the whole filter block:   
[aVar]   
+
    name=capture   
+
[aVar]   
    first_time_only=no   
+
    name=capture   
    [set_variable]   
+
    first_time_only=no   
        name=tmp   
+
    [set_variable]   
        rand=1..2   
+
        name=tmp   
    [/set_variable]   
+
        rand=1..2   
[/aVar]  
+
    [/set_variable]   
 +
[/aVar]  
 +
</syntaxhighlight>
 
This example is rather confusing because this variable looks much more like a piece of code   
 
This example is rather confusing because this variable looks much more like a piece of code   
 
than data (and it’s part of the content of an event, of course). But, if you want to follow us to   
 
than data (and it’s part of the content of an event, of course). But, if you want to follow us to   
Line 229: Line 280:
 
fetched in a savegame or with ''':inspect''' in debug mode. It’s rather complex. Here is an   
 
fetched in a savegame or with ''':inspect''' in debug mode. It’s rather complex. Here is an   
 
example (many lines have been deleted, particularly animations):
 
example (many lines have been deleted, particularly animations):
[unit]   
+
<syntaxhighlight lang="wml">
    flying=yes   
+
[unit]   
    gender="female"   
+
    flying=yes   
    hitpoints=26   
+
    gender="female"   
    id="Lestiviel"   
+
    hitpoints=26   
    image="units/elves-wood/shaman.png"   
+
    id="Lestiviel"   
    max_experience=26   
+
    image="units/elves-wood/shaman.png"   
    max_hitpoints=26   
+
    max_experience=26   
    max_moves=5   
+
    max_hitpoints=26   
    moves=5   
+
    max_moves=5   
    name=_"Lestiviel"   
+
    moves=5   
    overlays="misc/hero-icon.png"   
+
    name=_"Lestiviel"   
    profile="portraits/Lestiviel-y.png"   
+
    overlays="misc/hero-icon.png"   
    race="elf"   
+
    profile="portraits/Lestiviel-y.png"   
    [attack]   
+
    race="elf"   
        damage=3   
+
    [attack]   
        description=_"staff"   
+
        damage=3   
        icon="attacks/druidstaff.png"   
+
        description=_"staff"   
        name="staff"   
+
        icon="attacks/druidstaff.png"   
        number=2   
+
        name="staff"   
        range="melee"   
+
        number=2   
        type="impact"   
+
        range="melee"   
    [/attack]   
+
        type="impact"   
    [attack]   
+
    [/attack]   
        damage=3   
+
    [attack]   
        description=_"entangle"   
+
        damage=3   
        name="entangle"   
+
        description=_"entangle"   
        number=2   
+
        name="entangle"   
        range="ranged"   
+
        number=2   
        type="impact"   
+
        range="ranged"   
        [specials]   
+
        type="impact"   
            [slow]   
+
        [specials]   
                description=_"Slow:"   
+
            [slow]   
                id="slow"   
+
                description=_"Slow:"   
                name=_"slows"   
+
                id="slow"   
            [/slow]   
+
                name=_"slows"   
        [/specials]   
+
            [/slow]   
    [/attack]   
+
        [/specials]   
    [modifications]   
+
    [/attack]   
        [trait]   
+
    [modifications]   
            description=_"Zero upkeep"   
+
        [trait]   
            female_name=_"female^loyal"   
+
            description=_"Zero upkeep"   
            id="loyal"   
+
            female_name=_"female^loyal"   
            male_name=_"loyal"   
+
            id="loyal"   
            [effect]   
+
            male_name=_"loyal"   
                apply_to="loyal"   
+
            [effect]   
            [/effect]   
+
                apply_to="loyal"   
        [/trait]   
+
            [/effect]   
        [trait]   
+
        [/trait]   
            female_name=_"female^intelligent"   
+
        [trait]   
            id="intelligent"   
+
            female_name=_"female^intelligent"   
            male_name=_"intelligent"   
+
            id="intelligent"   
            [effect]   
+
            male_name=_"intelligent"   
                apply_to="max_experience"   
+
            [effect]   
                increase="-20%"   
+
                apply_to="max_experience"   
            [/effect]   
+
                increase="-20%"   
        [/trait]   
+
            [/effect]   
    [/modifications]   
+
        [/trait]   
[/unit]
+
    [/modifications]   
 +
[/unit]
 +
</syntaxhighlight>
 
Here we have a problem: this unit has two attack blocks and two traits blocks. How can we   
 
Here we have a problem: this unit has two attack blocks and two traits blocks. How can we   
 
access them ? Writing only '''$unit.attack.name''' can’t be correct since we have two blocks. We   
 
access them ? Writing only '''$unit.attack.name''' can’t be correct since we have two blocks. We   
Line 293: Line 346:
 
order they are written, and we can use this index to state which one we want:
 
order they are written, and we can use this index to state which one we want:
  
$unit.attack[0].name -> "staff"   
+
$unit.attack[0].name -> "staff"   
$unit.attack[1].name -> "entangle" 
 
       
 
$unit.modifications.trait[0].id -> "loyal" 
 
$unit.modifications.trait[1].id -> "intelligent"
 
  
Mark indexes begins with 0. So, can we modify the value of the intelligent trait using   
+
$unit.attack[1].name -> "entangle" 
 +
     
 +
$unit.modifications.trait[0].id -> "loyal" 
 +
     
 +
$unit.modifications.trait[1].id -> "intelligent"
 +
 
 +
Note: Indexes begins with 0. So, can we modify the value of the intelligent trait using   
 
VARIABLE ? Yes, just do it:   
 
VARIABLE ? Yes, just do it:   
 
    
 
    
{VARIABLE $unit.modifications.trait[1].increase "-50%"}
+
<syntaxhighlight lang="wml">
 +
{VARIABLE $unit.modifications.trait[1].increase "-50%"}
 +
</syntaxhighlight>
 
   
 
   
 
Does this modification apply to the unit ? Not immediately. You should use '''[unstore_unit]'''   
 
Does this modification apply to the unit ? Not immediately. You should use '''[unstore_unit]'''   
Line 311: Line 368:
  
  
==== <u>More with arrays</u> ====
+
==== <u>Using arrays</u> ====
Arrays can be created using the '''[set_variables]''' tag. In it, we can repeat the '''[value][/value]'''
+
Now that we understand containers (read the previous section if you skipped that), it's time to move on to arrays. Arrays can be created using the '''[set_variables]''' tag. In it, we can repeat the '''[value][/value]''' block at will, and this will create a container array:  
block at will, and this will create a container array:  
+
<syntaxhighlight lang="wml">
[set_variables]   
+
[set_variables]   
    name=heroes   
+
    name=heroes   
    [value]   
+
    [value]   
        name="Delfador"   
+
        name="Delfador"   
        gold=250   
+
        gold=250   
        fame=127   
+
        fame=127   
        fullName="Delfador the Great"   
+
        fullName="Delfador the Great"   
    [/value]   
+
    [/value]   
    [value]   
+
    [value]   
        name="Konrad"   
+
        name="Konrad"   
        gold=125   
+
        gold=125   
        fame=10   
+
        fame=10   
        fullName="Konrad the Heir"   
+
        fullName="Konrad the Heir"   
    [/value]   
+
    [/value]   
    [value]   
+
    [value]   
        name="Lisar"   
+
        name="Lisar"   
        gold=1258   
+
        gold=1258   
        fame=250   
+
        fame=250   
        fullName="Princess Lisar"   
+
        fullName="Princess Lisar"   
    [/value]   
+
    [/value]   
[/set_variables]   
+
[/set_variables]   
 +
</syntaxhighlight>
 
This will create three [heroes] blocks numbered from 0 to 2.   
 
This will create three [heroes] blocks numbered from 0 to 2.   
[heroes]   
+
<syntaxhighlight lang="wml">
    name="Delfador"   
+
[heroes]   
    gold=250   
+
    name="Delfador"   
    fame=127   
+
    gold=250   
    fullName="Delfador the Great"   
+
    fame=127   
[/heroes]   
+
    fullName="Delfador the Great"   
[heroes]   
+
[/heroes]   
    name="Konrad"   
+
[heroes]   
    gold=125   
+
    name="Konrad"   
    fame=10   
+
    gold=125   
    fullName="Konrad the Heir"   
+
    fame=10   
[/heroes]   
+
    fullName="Konrad the Heir"   
[heroes]   
+
[/heroes]   
    name="Lisar"   
+
[heroes]   
    gold=1258   
+
    name="Lisar"   
    fame=250   
+
    gold=1258   
    fullName="Princess Lisar"   
+
    fame=250   
[/heroes]
+
    fullName="Princess Lisar"   
 +
[/heroes]
 +
</syntaxhighlight>
 
Arrays all have a special property named '''length'''. It holds the number of blocks in the array.   
 
Arrays all have a special property named '''length'''. It holds the number of blocks in the array.   
 
So here:   
 
So here:   
$heroes.length -> 3 
 
  
$unit.modifications.trait.length -> 2
+
$heroes.length -> 3 
 +
 
 +
$unit.modifications.trait.length -> 2
 
(from the previous ‘unit’ example)   
 
(from the previous ‘unit’ example)   
 
    
 
    
Line 366: Line 427:
 
All these arrays can be modified: we can add later a block or delete it. Deletion is done with   
 
All these arrays can be modified: we can add later a block or delete it. Deletion is done with   
 
the '''[clear_variable]''' tag:
 
the '''[clear_variable]''' tag:
{CLEAR_VARIABLE heroes[1]}   
+
<syntaxhighlight lang="wml">
 +
{CLEAR_VARIABLE heroes[1]}   
 +
</syntaxhighlight>
 
This will delete the Konrad record. Please note that the <u>array will be renumbered</u>: so the Lisar record will now have the index 1.
 
This will delete the Konrad record. Please note that the <u>array will be renumbered</u>: so the Lisar record will now have the index 1.
  
Line 384: Line 447:
 
different kind of records (for instance units and locations). The reason is most often, arrays   
 
different kind of records (for instance units and locations). The reason is most often, arrays   
 
are fetched and manipulated in loops. Loops are much more easy to program when all records   
 
are fetched and manipulated in loops. Loops are much more easy to program when all records   
have the same structure.
+
have the same structure.
 +
 
 +
{{DevFeature1.13|2}} This explanation of loops is still relevant, but it's now recommended to
 +
use the '''[for]''' or '''[foreach]''' tags instead of the '''FOREACH''' macro.
 +
 
 
The '''FOREACH''' macro is most often used for this purpose. It takes an array name and a   
 
The '''FOREACH''' macro is most often used for this purpose. It takes an array name and a   
 
variable name as arguments. The variable will contain an index incremented by one on each   
 
variable name as arguments. The variable will contain an index incremented by one on each   
 
step of the loop by the ending macro '''NEXT'''. For instance, we can summarize the wealth of   
 
step of the loop by the ending macro '''NEXT'''. For instance, we can summarize the wealth of   
 
our heroes with this code:   
 
our heroes with this code:   
{FOREACH heroes index}   
+
<syntaxhighlight lang="wml">
    [set_variable]   
+
{FOREACH heroes index}   
        name=tmp   
+
    [set_variable]   
        add=$heroes[$index].gold   
+
        name=tmp   
    [/set_variable]   
+
        add=$heroes[$index].gold   
{NEXT index}   
+
    [/set_variable]   
+
{NEXT index}   
[message]   
+
 
    speaker=narrator   
+
[message]   
    message="Team has $tmp gold."   
+
    speaker=narrator   
[/message]   
+
    message="Team has $tmp gold."   
 +
[/message]   
 +
</syntaxhighlight>
 
Here, we accumulate the gold amount of our heroes in the variable '''tmp'''. The loop will begin   
 
Here, we accumulate the gold amount of our heroes in the variable '''tmp'''. The loop will begin   
 
with '''index'''=0 and repeat the code inserted between '''FOREACH''' and '''NEXT''', incrementing the value of index by one and will stop when '''index=heroes.length'''.   
 
with '''index'''=0 and repeat the code inserted between '''FOREACH''' and '''NEXT''', incrementing the value of index by one and will stop when '''index=heroes.length'''.   
Line 405: Line 474:
 
:- make sure you don’t use the index variable elsewhere: it shall be cleared at the end.   
 
:- make sure you don’t use the index variable elsewhere: it shall be cleared at the end.   
 
:- when using such a loop to delete some records. For instance this:   
 
:- when using such a loop to delete some records. For instance this:   
{FOREACH heroes index}   
+
<syntaxhighlight lang="wml">
    [if]   
+
{FOREACH heroes index}   
        [variable]   
+
    [if]   
            name=heroes[$index].name   
+
        [variable]   
            equals="Konrad"   
+
            name=heroes[$index].name   
        [/variable]   
+
            equals="Konrad"   
        [then]   
+
        [/variable]   
            {CLEAR_VARIABLE heroes[$index]}   
+
        [then]   
        [/then]   
+
            {CLEAR_VARIABLE heroes[$index]}   
    [/if]   
+
        [/then]   
{NEXT index}   
+
    [/if]   
 +
{NEXT index}   
 +
</syntaxhighlight>
 
will not work exactly as expected. As we saw earlier, the array will be renumbered when the   
 
will not work exactly as expected. As we saw earlier, the array will be renumbered when the   
 
“Konrad” record is deleted. So the next record will take the “Konrad” index, and, since index   
 
“Konrad” record is deleted. So the next record will take the “Konrad” index, and, since index   
 
is incremented at the end of the step, <u>the record following “Konrad” will be skipped</u>. The   
 
is incremented at the end of the step, <u>the record following “Konrad” will be skipped</u>. The   
 
correct way to do this is fetching the array in reverse order[[Less easy because there is no macro for that.|<sup>5)</sup>]] or decrementing the index after the deletion:   
 
correct way to do this is fetching the array in reverse order[[Less easy because there is no macro for that.|<sup>5)</sup>]] or decrementing the index after the deletion:   
{FOREACH heroes index}   
+
<syntaxhighlight lang="wml">
    [if]   
+
{FOREACH heroes index}   
        [variable]   
+
    [if]   
            name=heroes[$index].name   
+
        [variable]   
            equals="Konrad"   
+
            name=heroes[$index].name   
        [/variable]   
+
            equals="Konrad"   
        [then]   
+
        [/variable]   
            {CLEAR_VARIABLE heroes[$index]}   
+
        [then]   
            [set_variable]   
+
            {CLEAR_VARIABLE heroes[$index]}   
                name=index   
+
            [set_variable]   
                sub=1   
+
                name=index   
            [set_variable]   
+
                sub=1   
        [/then]   
+
            [set_variable]   
    [/if]   
+
        [/then]   
{NEXT index}
+
    [/if]   
 +
{NEXT index}
 +
</syntaxhighlight>
 +
 
 +
==== <u>More with arrays</u> ==== 
 +
 
 +
Let's say we want our Trapper unit to be able to put traps all around the map. Each trap position is saved as a location, a container with '''x''' and '''y''' values, stored using '''[store_locations]'''. We have stored all our trap positions in this variable "trap_pos", but now we want to add another location where the Trapper is currently standing ($x1, $y1). How would we do that? Here is one simple way, using '''find_in''':
 +
 
 +
<syntaxhighlight lang="wml">
 +
[store_locations]
 +
    x=$x1
 +
    y=$y1
 +
    [or]
 +
        find_in=trap_pos
 +
    [/or]
 +
    variable=trap_pos
 +
[/store_locations]
 +
</syntaxhighlight>
 +
 
 +
Continuing the example above, what if our Trapper had a change of heart, and now he wants to remove the trap where he is standing? He can easily remove that position from the "trap_pos" array, again by using '''find_in''':
 +
 
 +
<syntaxhighlight lang="wml">
 +
[store_locations]
 +
    find_in=trap_pos
 +
    [not]
 +
        x=$x1
 +
        y=$y1
 +
    [/not]
 +
    variable=trap_pos
 +
[/store_locations]
 +
</syntaxhighlight>
 +
 
 +
Now the final piece is an event that will catch any unsuspecting unit who dares to step upon our well-placed traps:
 +
<syntaxhighlight lang="wml">
 +
[event]
 +
    name=moveto
 +
    first_time_only=no
 +
    [filter]
 +
        [filter_location]
 +
            find_in=trap_pos
 +
        [/filter_location]
 +
    [/filter]
 +
    # Boom! Gotcha
 +
[/event]
 +
</syntaxhighlight>
 +
 
 +
The same '''find_in''' trick for growing and shrinking arrays of locations can also be used with arrays of units, using the '''find_in''' key of '''[store_unit]'''.
 +
 
 
=== <u>First steps into darkness</u> ===   
 
=== <u>First steps into darkness</u> ===   
  
Line 445: Line 563:
  
 
Look at these macros:  
 
Look at these macros:  
#define LSB_STOREPERSO ID KILL   
+
<syntaxhighlight lang="wml">
    [store_unit]   
+
#define LSB_STOREPERSO ID KILL   
        [filter]   
+
    [store_unit]   
            id=${ID}   
+
        [filter]   
        [/filter]   
+
            id=${ID}   
        variable=${ID}_back   
+
        [/filter]   
        kill={KILL}   
+
        variable=${ID}_back   
    [/store_unit]   
+
        kill={KILL}   
#enddef
+
    [/store_unit]   
 +
#enddef
  
#define LSB_RECALLPERSO ID XY   
+
#define LSB_RECALLPERSO ID XY   
    [unstore_unit]   
+
    [unstore_unit]   
        variable=${ID}_back   
+
        variable=${ID}_back   
        find_vacant=yes   
+
        find_vacant=yes   
        {XY}   
+
        {XY}   
    [/unstore_unit]   
+
    [/unstore_unit]   
#enddef
+
#enddef
 +
</syntaxhighlight>
 
They store and retrieve an unit using it’s ID to create the name of the variable. The unit ID is   
 
They store and retrieve an unit using it’s ID to create the name of the variable. The unit ID is   
 
found in the variable whose name is given in the parameter ID. For instance, it can be '''unit.id''' in a moveto event:   
 
found in the variable whose name is given in the parameter ID. For instance, it can be '''unit.id''' in a moveto event:   
[event]   
+
<syntaxhighlight lang="wml">
    name=moveto   
+
[event]   
    # ... the filter will come here   
+
    name=moveto   
    {LSB_STOREPERSO unit.id yes} # the unit disappear   
+
    # ... the filter will come here   
    # but is stored in a variable named after its id,   
+
    {LSB_STOREPERSO unit.id yes} # the unit disappear   
    # for instance "Konrad_back"   
+
    # but is stored in a variable named after its id,   
[/event]   
+
    # for instance "Konrad_back"   
 +
[/event]   
 +
</syntaxhighlight>
 
The variables created using this code shouldn’t be confused with an array. They’re individual   
 
The variables created using this code shouldn’t be confused with an array. They’re individual   
 
variables, even if they look more or less the same in the ''':inspect''' display. Particularly, they   
 
variables, even if they look more or less the same in the ''':inspect''' display. Particularly, they   
Line 481: Line 603:
 
So, in a '''moveto''' event of one of these boats, '''RF_$unit.id''' is the name of the array   
 
So, in a '''moveto''' event of one of these boats, '''RF_$unit.id''' is the name of the array   
 
containing the crew. This code make them pop out.   
 
containing the crew. This code make them pop out.   
{FOREACH RF_$unit.id| n}   
+
<syntaxhighlight lang="wml">
    [unstore_unit]   
+
{FOREACH RF_$unit.id| n}   
        variable=RF_$unit.id|[$n]   
+
    [unstore_unit]   
        x,y=$rft[0].x,$rft[0].y   
+
        variable=RF_$unit.id|[$n]   
        find_vacant=yes   
+
        x,y=$rft[0].x,$rft[0].y   
    [/unstore_unit]                     
+
        find_vacant=yes   
{NEXT n}
+
    [/unstore_unit]                     
 +
{NEXT n}
 +
</syntaxhighlight>
 
Fine, but here, the engine could have a problem: what is exactly RF_$unit.id[$n] ? It can   
 
Fine, but here, the engine could have a problem: what is exactly RF_$unit.id[$n] ? It can   
 
be :   
 
be :   
Line 500: Line 624:
 
Here is the way to do this. We shall use here our “heroes” array, and store it twice into a new   
 
Here is the way to do this. We shall use here our “heroes” array, and store it twice into a new   
 
created array:   
 
created array:   
[set_variables]   
+
<syntaxhighlight lang="wml">
    name=biDim   
+
[set_variables]   
    [insert_tag]   
+
    name=biDim   
        name=value   
+
    [insert_tag]   
        variable=heroes   
+
        name=value   
    [/insert_tag]   
+
        variable=heroes   
    [insert_tag]   
+
    [/insert_tag]   
        name=value   
+
    [insert_tag]   
        variable=heroes # of course it could be something different   
+
        name=value   
    [/insert_tag]   
+
        variable=heroes # of course it could be something different   
[/set_variables]   
+
    [/insert_tag]   
 +
[/set_variables]   
 +
</syntaxhighlight>
 
'''Insert_tag''' creates a new block whose tag is equal to its '''name''' key, so each '''insert_tag''' will create a block:  
 
'''Insert_tag''' creates a new block whose tag is equal to its '''name''' key, so each '''insert_tag''' will create a block:  
[value]   
+
<syntaxhighlight lang="wml">
    [heroes]   
+
[value]   
        name="Delfador"   
+
    [heroes]   
        gold=250   
+
        name="Delfador"   
        fame=127   
+
        gold=250   
        fullName="Delfador the Great"   
+
        fame=127   
    [/heroes]   
+
        fullName="Delfador the Great"   
    [heroes]   
+
    [/heroes]   
        name="Konrad"   
+
    [heroes]   
        gold=125   
+
        name="Konrad"   
        fame=10   
+
        gold=125   
        fullName="Konrad the Heir"   
+
        fame=10   
    [/heroes]   
+
        fullName="Konrad the Heir"   
    [heroes]   
+
    [/heroes]   
        name="Lisar"   
+
    [heroes]   
        gold=1258   
+
        name="Lisar"   
        fame=250   
+
        gold=1258   
        fullName="Princess Lisar"   
+
        fame=250   
    [/heroes]   
+
        fullName="Princess Lisar"   
[/value]   
+
    [/heroes]   
 +
[/value]   
 +
</syntaxhighlight>
 
Still with us? OK, now to access our heroes we shall use the ordinary syntax. For instance:
 
Still with us? OK, now to access our heroes we shall use the ordinary syntax. For instance:
$biDim[0].heroes[1].name -> Konrad   
+
<syntaxhighlight lang="wml">
 +
$biDim[0].heroes[1].name -> Konrad   
 +
</syntaxhighlight>
 
    
 
    
 
And of course, one can walk all the array using a nested FOREACH loop.   
 
And of course, one can walk all the array using a nested FOREACH loop.   
{FOREACH biDim i}   
+
<syntaxhighlight lang="wml">
    {FOREACH biDim[$i].heroes index}   
+
{FOREACH biDim i}   
        [if]   
+
    {FOREACH biDim[$i].heroes index}   
            [variable]   
+
        [if]   
                name=biDim[$i].heroes[$index].gold   
+
            [variable]   
                less_than=100   
+
                name=biDim[$i].heroes[$index].gold   
            [/variable]   
+
                less_than=100   
            [then]   
+
            [/variable]   
                [set_variable]   
+
            [then]   
                    name=biDim[$i].heroes[$index].gold   
+
                [set_variable]   
                    add=100   
+
                    name=biDim[$i].heroes[$index].gold   
                [/set_variable]   
+
                    add=100   
            [/then]   
+
                [/set_variable]   
        [/if]   
+
            [/then]   
    {NEXT index}   
+
        [/if]   
{NEXT i}   
+
    {NEXT index}   
 +
{NEXT i}   
 +
</syntaxhighlight>
 
This adds some gold to the purse of the poorest heroes of biDim array.
 
This adds some gold to the purse of the poorest heroes of biDim array.
  
 
==== <u>Using variables strings in events names</u> ====   
 
==== <u>Using variables strings in events names</u> ====   
 
This syntax:   
 
This syntax:   
[event]   
+
<syntaxhighlight lang="wml">
    name=$myEvent   
+
[event]   
    ...   
+
    name=$myEvent   
[/message]   
+
    ...   
 +
[/message]   
 +
</syntaxhighlight>
 
Is perfectly valid. Of course, '''myEvent''' should contain some valid event name, consistent with   
 
Is perfectly valid. Of course, '''myEvent''' should contain some valid event name, consistent with   
 
the event body. One use of this is to specify turn numbers. For instance:   
 
the event body. One use of this is to specify turn numbers. For instance:   
[event]   
+
<syntaxhighlight lang="wml">
    name=turn $afterDark   
+
[event]   
    ...   
+
    name=turn $afterDark   
[/event]
+
    ...   
 +
[/event]
 +
</syntaxhighlight>
 
With this you can set '''afterDark''' in order to state when the event should fire (it must then   
 
With this you can set '''afterDark''' in order to state when the event should fire (it must then   
 
contain a number or a string of the form '''side number'''). Even more useful is the way to fire an   
 
contain a number or a string of the form '''side number'''). Even more useful is the way to fire an   
 
event some turn after another occurred. Suppose we want to raise a storm two turns after some   
 
event some turn after another occurred. Suppose we want to raise a storm two turns after some   
 
hero visited a particular location. Then we shall write:   
 
hero visited a particular location. Then we shall write:   
[event]   
+
<syntaxhighlight lang="wml">
    name=moveto   
+
[event]   
    # ... the moveto filter will come here   
+
    name=moveto   
+
    # ... the moveto filter will come here   
    [event]   
+
 
        name="turn $($turn_number + 2)"   
+
    [event]   
+
        name="turn $($turn_number + 2)"   
        # start the storm   
+
 
    [/event]   
+
        # start the storm   
[/event]   
+
    [/event]   
 +
[/event]   
 +
</syntaxhighlight>
 
This will create the nested event and make it fire 2 turns later.   
 
This will create the nested event and make it fire 2 turns later.   
 
It can be used with '''fire_event''' too. This code sets the variable '''myEvent''' according to the   
 
It can be used with '''fire_event''' too. This code sets the variable '''myEvent''' according to the   
 
incomer type, then fires the corresponding event.   
 
incomer type, then fires the corresponding event.   
[switch]   
+
<syntaxhighlight lang="wml">
    name=unit.type   
+
[switch]   
    [case]   
+
    name=unit.type   
        value=Elvish Sorceress   
+
    [case]   
        {VARIABLE myEvent storm}   
+
        value=Elvish Sorceress   
    [/case]   
+
        {VARIABLE myEvent storm}   
    [case]   
+
    [/case]   
        value=Troll Warrior   
+
    [case]   
        {VARIABLE myEvent monster}   
+
        value=Troll Warrior   
    [/case]   
+
        {VARIABLE myEvent monster}   
    [case]   
+
    [/case]   
        value=Mermaid Initiate   
+
    [case]   
        {VARIABLE myEvent flood}   
+
        value=Mermaid Initiate   
    [/case]   
+
        {VARIABLE myEvent flood}   
[/switch]   
+
    [/case]   
+
[/switch]   
# --- somewhat later…   
+
 
[fire_event]   
+
# --- somewhat later…   
    name=$myEvent   
+
[fire_event]   
[/fire_event]   
+
    name=$myEvent   
 +
[/fire_event]   
  
# ... of course, these events should be defined elsewhere   
+
# ... of course, these events should be defined elsewhere   
[event]   
+
[event]   
    name=storm   
+
    name=storm   
    ...   
+
    ...   
[/event]
+
[/event]
  
[event]   
+
[event]   
    name=monster   
+
    name=monster   
    ...   
+
    ...   
[/event]
+
[/event]
  
[event]   
+
[event]   
    name=flood   
+
    name=flood   
    ...   
+
    ...   
[/event]
+
[/event]
 +
</syntaxhighlight>
  
 
=== <u>Voodoo and black magics</u> ===   
 
=== <u>Voodoo and black magics</u> ===   
Line 624: Line 764:
  
 
At this point, maybe you begin to suspect many things can be replaced with variables,   
 
At this point, maybe you begin to suspect many things can be replaced with variables,   
including parts of the code itself. That’s true and interesting in some cases, but it should be
+
including parts of the code itself. That’s true and interesting in some cases, but it should be  
 
 
 
clear that using the features explained later creates code much more difficult to understand   
 
clear that using the features explained later creates code much more difficult to understand   
 
and to debug. You certainly shall discover that much more can be done than what we   
 
and to debug. You certainly shall discover that much more can be done than what we   
Line 641: Line 780:
 
creates a new flag named '''isHero''' in unit status. Of course, the game engine will not display   
 
creates a new flag named '''isHero''' in unit status. Of course, the game engine will not display   
 
anything as it does with '''slow''' and '''poison''' status key, but you can use it in filters:   
 
anything as it does with '''slow''' and '''poison''' status key, but you can use it in filters:   
[event]   
+
<syntaxhighlight lang="wml">
    name=die   
+
[event]   
    first_time_only=no   
+
    name=die   
    [filter_condition]   
+
    first_time_only=no   
        [variable]   
+
    [filter_condition]   
            name=unit.status.isHero   
+
        [variable]   
            boolean_equals=yes   
+
            name=unit.status.isHero   
        [/variable]   
+
            boolean_equals=yes   
    [/filter_condition]   
+
        [/variable]   
      …   
+
    [/filter_condition]   
 
+
    …   
 +
</syntaxhighlight>
 +
 
 
The same can be done in objects. In this object block, the programmer added two custom   
 
The same can be done in objects. In this object block, the programmer added two custom   
 
keys, category and price.   
 
keys, category and price.   
[object]   
+
<syntaxhighlight lang="wml">
    name= _ "Poisonous Bow"   
+
[object]   
    image=items/bow.png   
+
    name= _ "Poisonous Bow"   
    description= _ "This bow deals 20% more damage than other bows, it shots poisonned arrows to enemies and is also quicker."   
+
    image=items/bow.png   
    category=bows   
+
    description= _ "This bow deals 20% more damage than other bows, it shots poisonned arrows to enemies and is also quicker."   
    price=150   
+
    category=bows   
    [effect]   
+
    price=150   
        apply_to=new_attack   
+
    [effect]   
        name=longbow   
+
        apply_to=new_attack   
        type=pierce   
+
        name=longbow   
        range=ranged   
+
        type=pierce   
        damage=12   
+
        range=ranged   
        number=4
+
        damage=12   
        movement_used=0   
+
        number=4
        icon=attacks/bow-elven-magic.png   
+
        movement_used=0   
        [specials]   
+
        icon=attacks/bow-elven-magic.png   
            {WEAPON_SPECIAL_POISON}   
+
        [specials]   
        [/specials]   
+
            {WEAPON_SPECIAL_POISON}   
    [/effect]   
+
        [/specials]   
[/object]  
+
    [/effect]   
 +
[/object]  
 +
</syntaxhighlight>
 
And when this object is applied to an unit, the extra keys are not deleted or modified in any   
 
And when this object is applied to an unit, the extra keys are not deleted or modified in any   
 
way.
 
way.
Line 687: Line 830:
 
Suppose we want to display a nice message which can be spoken by various units. Of course,   
 
Suppose we want to display a nice message which can be spoken by various units. Of course,   
 
we can use role or a global variable '''whoSpeaks''' to specify who is speaking. For instance:   
 
we can use role or a global variable '''whoSpeaks''' to specify who is speaking. For instance:   
  [event]   
+
<syntaxhighlight lang="wml">
 +
[event] 
 +
    name=nice_speech  
 +
    [message] 
 +
        speaker=$whoSpeaks 
 +
        message=_"Vanish, foul messengers of Sauron…" # and so on… 
 +
    [/message] 
 +
[/event] 
 +
</syntaxhighlight>
 +
We can fire this event with:       
 +
<syntaxhighlight lang="wml">
 +
{VARIABLE whoSpeaks Gandalf} # or any other character 
 +
[fire_event]   
 
     name=nice_speech   
 
     name=nice_speech   
    [message] 
+
[/fire_event]
        speaker=$whoSpeaks 
+
</syntaxhighlight>
        message=_"Vanish, foul messengers of Sauron…" # and so on… 
 
    [/message] 
 
[/event] 
 
We can fire this event with:       
 
{VARIABLE whoSpeaks Gandalf} # or any other character 
 
[fire_event]
 
      name=nice_speech 
 
[/fire_event]
 
 
But we would have to clear the whoSpeaks variable later. There is another way. In the   
 
But we would have to clear the whoSpeaks variable later. There is another way. In the   
 
fire_event tag documentation, one can find:   
 
fire_event tag documentation, one can find:   
 
'''[primary_attack]''': Information passed to the primary attack filter and $weapon variable on   
 
'''[primary_attack]''': Information passed to the primary attack filter and $weapon variable on   
the new event.
+
the new event. Of course, we have no attacker and no attack here, but what happens if we set something here   
Of course, we have no attacker and no attack here, but what happens if we set something here   
 
 
like :  
 
like :  
[fire_event]   
+
<syntaxhighlight lang="wml">
    name=nice_speech   
+
[fire_event]   
    [primary_attack]   
+
    name=nice_speech   
        whoSpeaks=Gandalf   
+
    [primary_attack]   
    [/primary_attack]   
+
        whoSpeaks=Gandalf   
[/fire_event]   
+
    [/primary_attack]   
+
[/fire_event]   
[event]   
+
 
    name=nice_speech   
+
[event]   
    [message]   
+
    name=nice_speech   
        speaker=$weapon.whoSpeaks   
+
    [message]   
        message=_"Vanish, foul messengers of Sauron…" # and so on…   
+
        speaker=$weapon.whoSpeaks   
    [/message]   
+
        message=_"Vanish, foul messengers of Sauron…" # and so on…   
[/event]
+
    [/message]   
 +
[/event]
 +
</syntaxhighlight>
 
Well, it pure voodoo but it works. Of course, one can pass anything in the '''primary_attack'''   
 
Well, it pure voodoo but it works. Of course, one can pass anything in the '''primary_attack'''   
 
block, not only a single value. The block itself will be discarded when he event is finished,   
 
block, not only a single value. The block itself will be discarded when he event is finished,   
just like call parameters in subroutines.
+
just like call parameters in subroutines.
 
 
  
 
==== <u>Dynamic code with insert_tag</u> ====
 
==== <u>Dynamic code with insert_tag</u> ====
Line 731: Line 878:
 
First step is creating a container holding the WML block you want to execute. For instance,   
 
First step is creating a container holding the WML block you want to execute. For instance,   
 
this action:   
 
this action:   
[harm_unit]   
+
<syntaxhighlight lang="wml">
    [filter]   
+
[harm_unit]   
        x,y=$x1,$y1   
+
    [filter]   
    [/filter]   
+
        x,y=$x1,$y1   
    amount=10   
+
    [/filter]   
    animate=yes   
+
    amount=10   
    kill=no   
+
    animate=yes   
[/harm_unit]   
+
    kill=no   
 +
[/harm_unit]   
 +
</syntaxhighlight>
 
The easiest way is to use a macro to define the block content:   
 
The easiest way is to use a macro to define the block content:   
 
    
 
    
#define BLOCK_CONTENT   
+
<syntaxhighlight lang="wml">
    [filter]   
+
#define BLOCK_CONTENT   
        x,y=$x1,$y1   
+
    [filter]   
    [/filter]   
+
        x,y=$x1,$y1   
    amount=10   
+
    [/filter]   
    animate=yes   
+
    amount=10   
    kill=no   
+
    animate=yes   
#enddef   
+
    kill=no   
      
+
#enddef   
    [set_variables]   
+
 
        name=harmingCode   
+
     [set_variables]   
        [value]   
+
        name=harmingCode   
            {BLOCK_CONTENT}   
+
        [value]   
        [/value]   
+
            {BLOCK_CONTENT}   
    [/set_variables]   
+
        [/value]   
 +
    [/set_variables]   
 +
</syntaxhighlight>
 
Else, we should have to create the variable first, and then add the subtag in this way:   
 
Else, we should have to create the variable first, and then add the subtag in this way:   
[set_variables]   
+
<syntaxhighlight lang="wml">
    name=harmingCode   
+
[set_variables]   
    [value]   
+
    name=harmingCode   
        amount=10   
+
    [value]   
        animate=yes   
+
        amount=10   
        kill=no   
+
        animate=yes   
    [/value]   
+
        kill=no   
[/set_variables]
+
    [/value]   
+
[/set_variables]
[set_variables]   
+
 
    name=harmingCode.filter   
+
[set_variables]   
    [value]   
+
    name=harmingCode.filter   
        x,y=$x1,$y1   
+
    [value]   
    [/value]   
+
        x,y=$x1,$y1   
[/set_variables]   
+
    [/value]   
Mark we didn’t include the '''[harm_unit]''' tag. Then we can use '''insert_tag''' in this way:   
+
[/set_variables]   
[insert_tag]   
+
</syntaxhighlight>
    name=harm_unit   
+
Notice, we didn’t include the '''[harm_unit]''' tag. Then we can use '''insert_tag''' in this way:   
    value=$harmingCode   
+
<syntaxhighlight lang="wml">
[/insert_tag]   
+
[insert_tag]   
 +
    name=harm_unit   
 +
    variable=harmingCode   
 +
[/insert_tag]   
 +
</syntaxhighlight>
 
This will produce exactly the original block above. Sometimes, it’s not very practical to   
 
This will produce exactly the original block above. Sometimes, it’s not very practical to   
 
define only the block content in the macro (maybe you would like to use it elsewhere, as an   
 
define only the block content in the macro (maybe you would like to use it elsewhere, as an   
 
ordinary macro). Then you can specify the '''command''' tag in the '''insert_tag'''. This tag does   
 
ordinary macro). Then you can specify the '''command''' tag in the '''insert_tag'''. This tag does   
 
nothing except creating blocks. Then it would be:
 
nothing except creating blocks. Then it would be:
  #define HARM_UNIT   
+
<syntaxhighlight lang="wml">
 +
#define HARM_UNIT  
 +
  [harm_unit] 
 +
        [filter] 
 +
            x,y=$x1,$y1 
 +
        [/filter] 
 +
        amount=10 
 +
        animate=yes 
 +
        kill=no 
 +
    [/harm_unit] 
 +
#enddef 
 +
 
 +
    [set_variables] 
 +
        name=harmingCode 
 +
        [value] 
 +
            {HARM_UNIT} 
 +
        [/value] 
 +
    [/set_variables] 
 +
 
 +
    # --- somewhere else… 
 +
 
 +
    [insert_tag] 
 +
        name=command 
 +
        variable=harmingCode 
 +
    [/insert_tag]
 +
</syntaxhighlight>
 +
But… this code works not always. The reason is the $x1, $y1. They are replaced with their values when we create the '''harmingCode''' variable, which is not what we generally want. We want to use the values they hold when the '''insert_tag''' is executed (most often, it’s later), in other words, we want to delay their substitution. To mark these variables to be substituted later , we shall add a pipe character just after the dollar sign: 
 +
<syntaxhighlight lang="wml">
 +
#define HARM_UNIT   
 
     [harm_unit]   
 
     [harm_unit]   
        [filter]   
+
        [filter]   
            x,y=$x1,$y1   
+
            x,y=$|x1,$|y1   
        [/filter]   
+
        [/filter]   
        amount=10   
+
        amount=10   
        animate=yes   
+
        animate=yes   
        kill=no   
+
        kill=no   
    [/harm_unit]   
+
    [/harm_unit]   
#enddef   
+
#enddef   
+
</syntaxhighlight>
    [set_variables] 
+
 
          name=harmingCode 
+
Then the code works. And the macro can be used in a normal way too. Another way to avoid the early variable substitution is using the tag '''literal''' instead of '''value''' when creating the '''harmingCode''' variable:
          [value] 
+
 
              {HARM_UNIT} 
+
<syntaxhighlight lang="wml">
          [/value] 
+
    [set_variables]
    [/set_variables] 
+
        name=harmingCode  
+
         [literal]   
    # --- somewhere else… 
+
             {HARM_UNIT}  
+
         [/literal]   
    [insert_tag] 
+
    [/set_variables]   
        name=command 
+
</syntaxhighlight>
        value=$harmingCode 
+
 
    [/insert_tag]
+
Pay heed, '''insert_tag''' must be included in some action (event and so on). It works not at the scenario level for instance.   
But… this code works not always. The reason is the $x1, $y1. They are replaced with their values when we create the '''harmingCode''' variable, which is not what we generally want. We want to use the values they hold when the '''insert_tag''' is executed (most often, it’s later), in other words, we want to delay their substitution. To mark these variables to be substituted later , we shall add a pipe character just after the dollar sign:
 
#define HARM_UNIT 
 
    [harm_unit]   
 
         [filter]   
 
             x,y=$|x1,$|y1  
 
         [/filter]   
 
        amount=10 
 
        animate=yes 
 
        kill=no 
 
    [/harm_unit]   
 
#enddef 
 
Then the code works. And the macro can be used in a normal way too. 
 
Mark '''insert_tag''' must be included in some action (event and so on). It works not at the scenario level for instance.   
 
 
As you can see, the '''insert_tag''' allows to do many things, but in our opinion, should be used scarcely. Here we shall show how it can be used to solve a common problem. Suppose we want to list the objects of a unit in a option message, let the user choose an object and remove it from the unit. This can be done with this kind of code:
 
As you can see, the '''insert_tag''' allows to do many things, but in our opinion, should be used scarcely. Here we shall show how it can be used to solve a common problem. Suppose we want to list the objects of a unit in a option message, let the user choose an object and remove it from the unit. This can be done with this kind of code:
[message]   
+
<syntaxhighlight lang="wml">
    message="Choose an item to drop"   
+
[message]   
    speaker=narrator   
+
    message="Choose an item to drop"   
    [option]   
+
    speaker=narrator   
        message="Fabulous speed potion"   
+
    [option]   
        [show_if]   
+
        message="Fabulous speed potion"   
            # here a conditional expression stating if the unit has   
+
        [show_if]   
            # the fabulous item.   
+
            # here a conditional expression stating if the unit has   
        [/show_if]   
+
            # the fabulous item.   
        [command]   
+
        [/show_if]   
            # ... drop the item   
+
        [command]   
        [/command]   
+
            # ... drop the item   
    [/option]   
+
        [/command]   
        # more options blocks...                             
+
    [/option]   
[/message]   
+
        # more options blocks...                             
 +
[/message]   
 +
</syntaxhighlight>
 
The '''show_if''' tag prevents the line to show if the unit has not the object. There are two problems with this code. First, the condition is not easy to write: the object list of the unit must be searched for that particular object. Next, the message block must have an option for each possible item. No problem if you have only a few, but when, like in some add-ons we shall name not, you have near one hundred…   
 
The '''show_if''' tag prevents the line to show if the unit has not the object. There are two problems with this code. First, the condition is not easy to write: the object list of the unit must be searched for that particular object. Next, the message block must have an option for each possible item. No problem if you have only a few, but when, like in some add-ons we shall name not, you have near one hundred…   
 
Here, we shall create dynamically a message block listing all the objects in the modification block of the unit. Thus, we don’t need the '''show_if''' tag et we want something like that:
 
Here, we shall create dynamically a message block listing all the objects in the modification block of the unit. Thus, we don’t need the '''show_if''' tag et we want something like that:
[message]   
+
<syntaxhighlight lang="wml">
    message="Choose an item to drop"   
+
[message]   
    speaker=narrator   
+
    message="Choose an item to drop"   
    [option]   
+
    speaker=narrator   
        message="Fabulous speed potion"   
+
    [option]   
        [command]   
+
        message="Fabulous speed potion"   
            # ... drop the item   
+
        [command]   
        [/command]   
+
            # ... drop the item   
    [/option]   
+
        [/command]   
    [option]   
+
    [/option]   
        message="Amazing flashing bow"   
+
    [option]   
        [command]   
+
        message="Amazing flashing bow"   
            # ... drop the item   
+
        [command]   
        [/command]   
+
            # ... drop the item   
        [/option]   
+
        [/command]   
            # ... more options if more objects   
+
        [/option]   
        [option]   
+
            # ... more options if more objects   
            message="Exit"   
+
        [option]   
        [command]   
+
            message="Exit"   
            # ... exit the loop   
+
        [command]   
        [/command]   
+
            # ... exit the loop   
    [/option]   
+
        [/command]   
[/message]   
+
    [/option]   
 +
[/message]   
 +
</syntaxhighlight>
 
So we shall create dynamically this block in a container variable and next use '''insert_tag''' to   
 
So we shall create dynamically this block in a container variable and next use '''insert_tag''' to   
 
execute it. The block itself is embedded in a loop which executes until the exit option is   
 
execute it. The block itself is embedded in a loop which executes until the exit option is   
 
chosen (this sets the flag '''t_done'''):
 
chosen (this sets the flag '''t_done'''):
# list unit objects   
+
<syntaxhighlight lang="wml">
#define LSB_LIST_UNIT_THINGS   
+
# list unit objects   
    {VARIABLE t_done no}   
+
#define LSB_LIST_UNIT_THINGS   
 +
  {VARIABLE t_done no
 +
 
 +
    [while] 
 +
        [variable] 
 +
            name=t_done 
 +
            equals=no 
 +
        [/variable] 
 +
        [do] 
 +
            {CLEAR_VARIABLE t_menu} 
 +
            {VARIABLE t_menu.message " Choose an item to drop:"} 
 +
            {VARIABLE t_menu.speaker narrator}   
 
   
 
   
    [while] 
+
# here begins the objects listing. They are in the modifications block of the unit   
        [variable] 
+
            {FOREACH unit.modifications.object nc}   
            name=t_done 
+
                # creates the option block with the name of the object   
            equals=no 
+
                [set_variables]   
        [/variable] 
+
                    name=t_menu.option   
        [do] 
+
                    mode=append   
            {CLEAR_VARIABLE t_menu} 
+
                      [value]   
            {VARIABLE t_menu.message " Choose an item to drop:"} 
+
                          message=$unit.modifications.object[$nc].name   
            {VARIABLE t_menu.speaker narrator} 
+
                          [command]   
 
+
                              # remove the object, or anything else   
# here begins the objects listing. They are in the modifications block of the unit   
+
                              {LSB_CLEAR_UNITOBJECT}   
            {FOREACH unit.modifications.object nc}   
+
                          [/command]   
                # creates the option block with the name of the object   
+
                        [/value]               
                [set_variables]   
+
                [/set_variables]   
                    name=t_menu.option   
+
            {NEXT nc}   
                    mode=append   
+
 
                        [value]   
+
# this adds the “exit” option to the end of the list   
                            message=$unit.modifications.object[$nc].name   
+
            {VARIABLE nc $t_menu.option.length}   
                            [command]   
+
                [set_variables]   
                                # remove the object, or anything else   
+
                    name=t_menu.option   
                                {LSB_CLEAR_UNITOBJECT}   
+
                    mode=append   
                            [/command]   
+
                    [value]   
                        [/value]               
+
                        message="Exit."   
                [/set_variables]   
+
                        [command]   
            {NEXT nc}   
+
                            {VARIABLE t_done yes}   
+
                        [/command]   
# this adds the “exit” option to the end of the list   
+
                    [/value]               
            {VARIABLE nc $t_menu.option.length}   
+
                [/set_variables]   
                [set_variables]   
+
 
                    name=t_menu.option   
+
# this finally displays the list on screen and let the user choose   
                    mode=append   
+
                [insert_tag]   
                    [value]   
+
                    name=message   
                        message="Exit."   
+
                    variable=t_menu   
                        [command]   
+
                [/insert_tag]   
                            {VARIABLE t_done yes}   
+
            [/do]   
                        [/command]   
+
        [/while]   
                    [/value]               
+
    {CLEAR_VARIABLE t_menu,nc}   
                [/set_variables]   
+
#enddef   
+
 
# this finally displays the list on screen and let the user choose   
+
# this is an example of use: in a right-click menu item.   
                [insert_tag]   
+
    [set_menu_item]   
                    name=message   
+
        id=LSB_drop   
                    variable=t_menu   
+
        description="Drop items."   
                [/insert_tag]   
+
        [show_if]   
            [/do]   
+
            [variable]   
        [/while]   
+
                name=unit.side   
    {CLEAR_VARIABLE t_menu,nc}   
+
                equals=1   
#enddef   
+
            [/variable]   
+
        [/show_if]   
# this is an example of use: in a right-click menu item.   
+
        [command]   
    [set_menu_item]   
+
            {LSB_LIST_UNIT_THINGS}   
        id=LSB_drop   
+
        [/command]   
        description="Drop items."   
+
    [/set_menu_item]
        [show_if]   
+
</syntaxhighlight>
            [variable]   
 
                name=unit.side   
 
                equals=1   
 
            [/variable]   
 
        [/show_if]   
 
        [command]   
 
            {LSB_LIST_UNIT_THINGS}   
 
        [/command]   
 
    [/set_menu_item]
 
 
Of course, you may want to list not all objects applied to a unit. It’s easy to do that adding a   
 
Of course, you may want to list not all objects applied to a unit. It’s easy to do that adding a   
 
custom key to the objects you want to list, for instance '''droppable=yes''', and testing if it is   
 
custom key to the objects you want to list, for instance '''droppable=yes''', and testing if it is   
Line 934: Line 1,110:
 
    
 
    
 
Another example of code managing pickuppable items on the map. We first define those objects using macros. For instance, this one is the core Storm Trident with some additional keys. The '''[object]''' tag is missing in order to use this macro more easily in an '''[insert_tag]'''.   
 
Another example of code managing pickuppable items on the map. We first define those objects using macros. For instance, this one is the core Storm Trident with some additional keys. The '''[object]''' tag is missing in order to use this macro more easily in an '''[insert_tag]'''.   
# --- Object definition example, don’t include the [object] tag   
+
<syntaxhighlight lang="wml">
#define LSB_STORM_TRIDENT   
+
# --- Object definition example, don’t include the [object] tag   
    name="storm trident"   
+
#define LSB_STORM_TRIDENT   
    image=items/storm-trident.png   
+
    name="storm trident"   
    duration=forever   
+
    image=items/storm-trident.png   
    description={RTN_USTR-6}   
+
    duration=forever   
    category=spears   
+
    description={RTN_USTR-6}   
    level=2   
+
    category=spears   
    [effect]   
+
    level=2   
        apply_to=new_attack   
+
    [effect]   
        name="storm trident"   
+
        apply_to=new_attack   
        description="storm trident"   
+
        name="storm trident"   
        icon=attacks/lightning.png   
+
        description="storm trident"   
        type=fire   
+
        icon=attacks/lightning.png   
        range=ranged   
+
        type=fire   
        [specials]   
+
        range=ranged   
            {WEAPON_SPECIAL_MAGICAL}   
+
        [specials]   
        [/specials]   
+
            {WEAPON_SPECIAL_MAGICAL}   
        damage=15   
+
        [/specials]   
        number=2   
+
        damage=15   
    [/effect]   
+
        number=2   
 
+
    [/effect]   
    {LIGHTNING_ANIMATION "storm trident" 1}   
+
    {LIGHTNING_ANIMATION "storm trident" 2}   
+
    {LIGHTNING_ANIMATION "storm trident" 1}   
    {LIGHTNING_ANIMATION "storm trident" 3}   
+
    {LIGHTNING_ANIMATION "storm trident" 2}   
#enddef   
+
    {LIGHTNING_ANIMATION "storm trident" 3}   
 +
#enddef   
 +
</syntaxhighlight>
 
At the beginning of the scenario or even the campaign, we define an object list containing all pickuppable items. Please note it’s the only place we need to modify when creating or deleting an object in our campaign. All the code needed to manage them is generic.   
 
At the beginning of the scenario or even the campaign, we define an object list containing all pickuppable items. Please note it’s the only place we need to modify when creating or deleting an object in our campaign. All the code needed to manage them is generic.   
# --- shortcut to store an object into an array   
+
<syntaxhighlight lang="wml">
#define LSB_OBJINFO OBJ   
+
# --- shortcut to store an object into an array   
    [value]   
+
#define LSB_OBJINFO OBJ   
        {OBJ}   
+
    [value]   
    [/value]   
+
        {OBJ}   
#enddef   
+
    [/value]   
 
+
#enddef   
# This is the main objects list which must be created at first start: it creates an array with all pickuppable items and give them an uid. 
 
#define LSB_CREATEOBJECTS_LIST 
 
    [set_variables] 
 
        name=Objets 
 
        mode=replace 
 
        {LSB_OBJINFO {RTN_OBJ_TELNECKLACE} } 
 
        {LSB_OBJINFO {RTN_OBJ_AELTHRANK} } 
 
        {LSB_OBJINFO {LSB_GOLD} } 
 
        {LSB_OBJINFO {LSB_STORM_TRIDENT} } 
 
        # add more here at will 
 
    [/set_variables]
 
 
    
 
    
    {FOREACH Objets i} # this adds an id to objects   
+
# This is the main objects list which must be created at first start: it creates an array with all pickuppable items and give them an uid. 
        [set_variable]   
+
#define LSB_CREATEOBJECTS_LIST 
            name=Objets[$i].uid   
+
    [set_variables] 
            value=$i   
+
        name=Objets 
        [/set_variable]   
+
        mode=replace 
    {NEXT i}   
+
        {LSB_OBJINFO {RTN_OBJ_TELNECKLACE} } 
#enddef   
+
        {LSB_OBJINFO {RTN_OBJ_AELTHRANK} } 
 +
        {LSB_OBJINFO {LSB_GOLD} } 
 +
        {LSB_OBJINFO {LSB_STORM_TRIDENT} } 
 +
        # add more here at will 
 +
    [/set_variables]
 +
 +
    {FOREACH Objets i} # this adds an id to objects   
 +
        [set_variable]   
 +
            name=Objets[$i].uid   
 +
            value=$i   
 +
        [/set_variable]   
 +
    {NEXT i}   
 +
#enddef   
 +
</syntaxhighlight>
 
Here a macro to drop objects on the map. Note the NUM parameter is the uid created before, not the full object itself. Of course, it can be a variable holding an uid.   
 
Here a macro to drop objects on the map. Note the NUM parameter is the uid created before, not the full object itself. Of course, it can be a variable holding an uid.   
#define LSB_DROP_OBJECT NUM X Y   
+
<syntaxhighlight lang="wml">
    [item] # place the item on the map   
+
#define LSB_DROP_OBJECT NUM X Y   
          image=$Objets[{NUM}].image   
+
    [item] # place the item on the map   
          x,y={X},{Y}   
+
        image=$Objets[{NUM}].image   
    [/item]   
+
        x,y={X},{Y}   
    [set_variables] # add it to the dropped objects list   
+
    [/item]   
        name=D_Objets   
+
    [set_variables] # add it to the dropped objects list   
        mode=append   
+
        name=D_Objets   
        [value]   
+
        mode=append   
              x={X}   
+
        [value]   
              y={Y}   
+
            x={X}   
              code={NUM}   
+
            y={Y}   
        [/value]               
+
            code={NUM}   
    [/set_variables]   
+
        [/value]               
#enddef   
+
    [/set_variables]   
 +
#enddef   
 +
</syntaxhighlight>
 
At last, we can set up a moveto event to trigger the pick up dialog   
 
At last, we can set up a moveto event to trigger the pick up dialog   
#define LSB_GETOBJECT FILTER ID   
+
<syntaxhighlight lang="wml">
    [event]   
+
#define LSB_GETOBJECT FILTER ID   
        name=moveto   
+
    [event]   
        id=GETOBJECT_{ID}   
+
        name=moveto   
        first_time_only=no   
+
        id=GETOBJECT_{ID}   
        [filter]   
+
        first_time_only=no   
            [filter_location] # fires only if there is something on the map   
+
        [filter]   
                find_in=D_Objets   
+
            [filter_location] # fires only if there is something on the map   
            [/filter_location]   
+
                find_in=D_Objets   
            {FILTER} # and for some units   
+
            [/filter_location]   
        [/filter]   
+
            {FILTER} # and for some units   
+
        [/filter]   
        {VARIABLE i $D_Objets.length}   
+
 
        [while] # maybe we have more than one object here   
+
        {VARIABLE i $D_Objets.length}   
            [variable]   
+
        [while] # maybe we have more than one object here   
                name=i   
+
            [variable]   
                greater_than=0   
+
                name=i   
            [/variable]   
+
                greater_than=0   
            [do]   
+
            [/variable]   
                [set_variable]   
+
            [do]   
                    name=i   
+
                [set_variable]   
                    sub=1   
+
                    name=i   
                [/set_variable]   
+
                    sub=1   
               
+
                [/set_variable]   
# message   
+
               
                [if]   
+
# message   
                    [variable]   
+
                [if]   
                        name=D_Objets[$i].x   
+
                    [variable]   
                        equals=$x1   
+
                        name=D_Objets[$i].x   
                    [/variable]   
+
                        equals=$x1   
                    [variable]   
+
                    [/variable]   
                        name=D_Objets[$i].y   
+
                    [variable]   
                        equals=$y1   
+
                        name=D_Objets[$i].y   
                    [/variable]   
+
                        equals=$y1   
                    [then]                         
+
                    [/variable]   
                        [message]   
+
                    [then]                         
                            speaker=narrator   
+
                        [message]   
                            message=_ "There is a $Objets[$D_Objets[$i].code].name on the ground. Should $unit.name take it ?"   
+
                            speaker=narrator   
                            [option]   
+
                            message=_ "There is a $Objets[$D_Objets[$i].code].name on the ground. Should $unit.name take it ?"   
                                message=_ "Take it"                                         
+
                            [option]   
                                [command]                                     
+
                                message=_ "Take it"                                         
                                    # --- give object to unit   
+
                                [command]                                     
                                    [insert_tag]   
+
                                    # --- give object to unit   
                                        name=object   
+
                                    [insert_tag]   
                                        variable=Objets[$D_Objets[$i].code]   
+
                                        name=object   
                                    [/insert_tag]   
+
                                        variable=Objets[$D_Objets[$i].code]   
                                             
+
                                    [/insert_tag]   
                                    # --- clear the map   
+
                                           
                                    [remove_item]                                         
+
                                    # --- clear the map   
                                        image=$Objets[$D_Objets[$i].code].image   
+
                                    [remove_item]                                         
                                        x,y=$unit.x,$unit.y                 
+
                                        image=$Objets[$D_Objets[$i].code].image   
                                    [/remove_item]   
+
                                        x,y=$unit.x,$unit.y                 
                                    {CLEAR_VARIABLE D_Objets[$i]}   
+
                                    [/remove_item]   
                                [/command]   
+
                                    {CLEAR_VARIABLE D_Objets[$i]}   
                            [/option]   
+
                                [/command]   
                            [option]   
+
                            [/option]   
                                message= _ "Leave it"   
+
                            [option]   
                            [/option]   
+
                                message= _ "Leave it"   
                        [/message]   
+
                            [/option]   
                    [/then]
+
                        [/message]   
                [/if]   
+
                    [/then]
            [/do]
+
                [/if]   
        [/while]   
+
            [/do]
    [/event]   
+
        [/while]   
#enddef
+
    [/event]   
 +
#enddef
 +
</syntaxhighlight>
 +
 
 +
[[Category:WML_Reference]]
 +
[[Category:WML_Tutorials]]

Latest revision as of 12:57, 17 July 2019

WML Variables HowTo (or Descent into Darkness)

In this document, we shall try to explain WML variables and their use with some details. We’ll start under the burning sun of lawful ordinary use, but, step by step, we shall go deeper in the shadows of necromancy, exploring undocumented features and hidden pits as we may. The first part should be understandable by any beginner, but the last one most probably requires a good WML understanding.

Under the burning sun

Variable definitions

This section and the next one can be skipped if you already know what variables are and how to use them. Variables are some kind of container a programmer can use to store pieces of information (s)he needs to manipulate : numbers, names, sentences, and anything else. In most programming languages, variables must be declared before they can be used. Declaration is an instruction giving a name (used later to refer to the variable) and a type, which defines the kind of content of the variable (number, characters strings, and so on). Later in the program, instructions can be used to store and retrieve the content of the container, which is most often called the value of the variable. In WML, variables need no declaration and their value have no precise type1). This means a new variable will be created at the first time the programmer stores something in it. And that (s)he can store anything in it. Variables use memory, so it’s good practice to clear them when they’re not needed anymore. Variables names are freely chosen by the programmer with some restrictions. They should not be the name of a language instruction (keyword) or operator. In WML, you can use quite any name or sentence to name a variable, but you shouldn’t if you want to shun subtle problems. A classical rule is :

- variable name should begin with a letter
- variable names should only contain letters (not accented) and numbers and the underscore _ character.

No spaces, no accented letters, no special characters, no minus and plus signs, etc… Those names are always safe and correctly interpreted by the engine as variables names. Note that some special characters are forbidden in variable names: $ , . | {} [] = because they have a special meaning we shall see later. I would strongly suggest to avoid common tags names like “event” “side” and too long names like:

“name_of_the_guy_who_killed_the_orc_on_last_turn” which is not the same as:
“name_of_the_gyu_who_killed_the_orc_on_last_turn”, but it’s not really obvious at first glance.

It’s a common error to type wrongly a variable name: in WML this don’t rise any error message, but the variable will have no value, giving most probably what you don’t expect. Last but not least, variables names are case sensitive: in other words, ‘aVar’ is not the same as ‘avar’.


Variables creation and manipulation

Even if WML variables contents have no precise types.1), we shall discuss two kinds:

- variables holding a single value, like a number, a character string
- variables holding a compound value, i.e. a pack of single values.

They’re often called containers in the documentation. Simple variables are created using the tag [set_variable]:

[set_variable]
    name=simpleVariable
    value=36
[/set_variable]

The tag defines the name and the value of the variable.

Next, we can access the variable value using the variable name prefixed with a dollar sign $.

[modify_unit]
    [filter]
        id=$unit.id
    [/filter]
    moves=$simpleVariable
[/modify_unit]

This sets the moves of the unit to 36 since simpleVariable holds 36.

When the line is executed, the value 36 is substituted to $simpleVariable, so it works as if we wrote:

[modify_unit]
    [filter]
        id=$unit.id
    [/filter]
    moves=36
[/modify_unit]

Using the same tag, we can change the value of simpleVariable, or make some arithmetic (see the tag documentation for the whole list).

For example:

[set_variable]
    name=simpleVariable
    sub=30
[/set_variable]

will change the value to 6 of course. We can even set the variable to another value type:

[set_variable]
    name=simpleVariable
    value="Delfador the Great"
[/set_variable]

We shall not use [set_variable] tag anymore. Instead, we shall use the VARIABLE shortcut:

{VARIABLE simpleVariable "Delfador the Great"}

stands for:

[set_variable]
    name=simpleVariable
    value="Delfador the Great"
[/set_variable]

We shall not use the arithmetic variations of set_variable either. Instead we shall use the Wesnoth_Formula_Language which is much more natural. Instead of:

[set_variable]
     name=simpleVariable
     value=35
[/set_variable]
[set_variable]
      name=simpleVariable
      add=$anotherVariable
[/set_variable]

we shall write:

[set_variable]
    name=simpleVariable
    value="$(35 + $anotherVariable)"
[/set_variable]
or
{VARIABLE simpleVariable "$(35 + $anotherVariable)"}

The Wesnoth_Formula_Language syntax is easy to use, the important thing is to always put the formula in this sequence: “$( … here comes the formula … )” In other words, $simpleVariable can be written everywhere you want to use the value of simpleVariable. Clearing variables can be done using the [clear_variable] tag:

[clear_variable]
    name=simpleVariable
[/clear_variable]
or using the following macro to delete more than one variable: {CLEAR_VARIABLE simpleVariable,anotherOne,count} 2)


Containers

What is a container? It is a variable holding more than a simple value. A good example is the unit variable created automatically in moveto and attack events. It contains the full description of a unit, not only its name and its id. Containers are useful to store related values in a pack. All these different values are called “members” of the created container. Instead of writing this:

{VARIABLE heroName "Delfador"}
{VARIABLE heroGold 250}
{VARIABLE heroFame 127}
{VARIABLE heroFullName "Delfador the Great"}

we can pack all this in a “hero” variable using [set_variables] (notice the ‘s’)

[set_variables]
    name=hero
    [value]
        name="Delfador"
        gold=250
        fame=127
        fullName="Delfador the Great"
    [/value]
[/set_variables]

Then, to get the values stored in the container, we shall use the $ sign as before, but appending the member name and a dot:3)

$hero.name -> Delfador

$hero.gold -> 250

And if we want to change a value, the “gold” member for instance:

[set_variable]
    name=hero.gold
    add=100
[/set_variable]

It’s important to note that here, we changed the “gold” member as if it was a single variable whose name is “hero.gold”. We can also clear a member of the container in the same way:

{CLEAR_VARIABLE hero.fullName}

This will delete the fullName member permanently. Note it will not only clear the value, but clear the member itself. Clearing the value would be:

{VARIABLE hero.fullName ""}

Can we add later a member to an existing container ? Yes, it can be done:

{VARIABLE hero.hasStaff yes}

this creates the member hasStaff and set it to yes.


A glance into the pit

All this will be clearer if we take a look at the way variables are stored. Opening a savegame with a text editor, we should find a part like this one:

[replay_start]  
    id=""
    [variables]  
        damage_inflicted=18
        heal_amount=10  
        side_number=1  
        turn_number=26  
        x1=8  
        x2=0  
        y1=8  
        y2=0  
        simpleVariable=35  
        [hero]  
            name="Delfador"  
            gold=250  
            fame=127  
            fullName="Delfador the Great"  
        [/hero]  
        ...

Here are our variables ! We can see simple ones are a pair name/value separated with an equal sign.4)

Container are stored in a WML block whose name is the variable name. Actually, it’s just like folders and files on your hard disk. Each name/value pair is like a file, and other tags like folders. This explains the syntax used: set_variable and clear_variable operate on name/value pairs, and you use the dot to specify the path to the line you want to create or modify, for example hero.name. Using set_variable creates a line if it exists not. So we can use it to add lines to the hero container using hero.something name, just as we can add a new line at the first level. Now, we can understand better what a container is. It’s a WML block, just as an ordinary tag, and can hold any valid WML content. Look at this:

[set_variables]  
    name=aVar  
    [value]  
        name=capture
        first_time_only=no  
        [filter]  
            side=3  
            type=orc           
        [/filter]  
        [set_variable]  
            name=tmp  
            rand=1..2  
        [/set_variable]  
    [/value]  
[/set_variables]

This is perfectly valid and creates this container variable:

[aVar]  
    name=capture  
    first_time_only=no  
    [filter]  
        side=3  
        type=orc  
    [/filter]  
    [set_variable]  
        name=tmp  
        rand=1..2  
    [/set_variable]  
[/aVar]

We can modify members in the sub blocks too, using the full path to them, separated with dots. For instance:

{VARIABLE aVar.set_variable.rand “1..5”}

or

{VARIABLE aVar.filter.side 2}.

Capito ?

We can delete members, values or even whole blocks in the same way:

{CLEAR_VARIABLE aVar.filter.type}

will remove the key ‘type’ in the filter block:

[aVar]  
    name=capture  
    first_time_only=no  
    [filter]  
        side=3  
    [/filter]  
    [set_variable]  
        name=tmp  
        rand=1..2  
    [/set_variable]  
[/aVar]  
 
{CLEAR_VARIABLE aVar.filter } will remove the whole filter block:  
 
[aVar]  
    name=capture  
    first_time_only=no  
    [set_variable]  
        name=tmp  
        rand=1..2  
    [/set_variable]  
[/aVar]

This example is rather confusing because this variable looks much more like a piece of code than data (and it’s part of the content of an event, of course). But, if you want to follow us to the deeper of darkness, you should already face this ominous truth: data and code are not separated in WML, and it’s possible to modify the code with data manipulation instructions. Fortunately with some limits. Actually, you can only modify from WML what can be put into a variable: units, locations, and some code blocks, but you can’t directly access to scenario level.

A more usual example is the unit container. Moveto events create a unit variable holding the full description of the moving unit. When pushed in your torture room (the ‘unit’ variable) you’re allowed to access any field of the unit. Exact composition of a unit block can be fetched in a savegame or with :inspect in debug mode. It’s rather complex. Here is an example (many lines have been deleted, particularly animations):

[unit]  
    flying=yes  
    gender="female"  
    hitpoints=26  
    id="Lestiviel"  
    image="units/elves-wood/shaman.png"  
    max_experience=26  
    max_hitpoints=26  
    max_moves=5  
    moves=5  
    name=_"Lestiviel"  
    overlays="misc/hero-icon.png"  
    profile="portraits/Lestiviel-y.png"  
    race="elf"  
    [attack]  
        damage=3  
        description=_"staff"  
        icon="attacks/druidstaff.png"  
        name="staff"  
        number=2  
        range="melee"  
        type="impact"  
    [/attack]  
    [attack]  
        damage=3  
        description=_"entangle"  
        name="entangle"  
        number=2  
        range="ranged"  
        type="impact"  
        [specials]  
            [slow]  
                description=_"Slow:"  
                id="slow"  
                name=_"slows"  
            [/slow]  
        [/specials]  
    [/attack]  
    [modifications]  
        [trait]  
            description=_"Zero upkeep"  
            female_name=_"female^loyal"  
            id="loyal"  
            male_name=_"loyal"  
            [effect]  
                apply_to="loyal"  
            [/effect]  
        [/trait]  
        [trait]  
            female_name=_"female^intelligent"  
            id="intelligent"  
            male_name=_"intelligent"  
            [effect]  
                apply_to="max_experience"  
                increase="-20%"  
            [/effect]  
        [/trait]  
    [/modifications]  
[/unit]

Here we have a problem: this unit has two attack blocks and two traits blocks. How can we access them ? Writing only $unit.attack.name can’t be correct since we have two blocks. We have here our first example of arrays. Arrays are lists of WML blocks sharing the same name (here attack or modifications.trait). The blocks in arrays are implicitly numbered in the order they are written, and we can use this index to state which one we want:

$unit.attack[0].name -> "staff"

$unit.attack[1].name -> "entangle"

$unit.modifications.trait[0].id -> "loyal"

$unit.modifications.trait[1].id -> "intelligent"

Note: Indexes begins with 0. So, can we modify the value of the intelligent trait using VARIABLE ? Yes, just do it:

{VARIABLE $unit.modifications.trait[1].increase "-50%"}

Does this modification apply to the unit ? Not immediately. You should use [unstore_unit] first to pull them out of your torture room, and then… well, it works for some values, but not all of them. There is an automatic healing process at work and some unit properties are overwritten when [unstore_unit] happens, but the variable itself is really changed. You can verify this using :inspect.


Using arrays

Now that we understand containers (read the previous section if you skipped that), it's time to move on to arrays. Arrays can be created using the [set_variables] tag. In it, we can repeat the [value][/value] block at will, and this will create a container array:

[set_variables]  
    name=heroes  
    [value]  
        name="Delfador"  
        gold=250  
        fame=127  
        fullName="Delfador the Great"  
    [/value]  
    [value]  
        name="Konrad"  
        gold=125  
        fame=10  
        fullName="Konrad the Heir"  
    [/value]  
    [value]  
        name="Lisar"  
        gold=1258  
        fame=250  
        fullName="Princess Lisar"  
    [/value]  
[/set_variables]

This will create three [heroes] blocks numbered from 0 to 2.

[heroes]  
    name="Delfador"  
    gold=250  
    fame=127  
    fullName="Delfador the Great"  
[/heroes]  
[heroes]  
    name="Konrad"  
    gold=125  
    fame=10  
    fullName="Konrad the Heir"  
[/heroes]  
[heroes]  
    name="Lisar"  
    gold=1258  
    fame=250  
    fullName="Princess Lisar"  
[/heroes]

Arrays all have a special property named length. It holds the number of blocks in the array. So here:

$heroes.length -> 3

$unit.modifications.trait.length -> 2 (from the previous ‘unit’ example)

Another way to create arrays is using [store_unit] and [store_locations] tags, or [set_variables] when using the split key to split a string.

All these arrays can be modified: we can add later a block or delete it. Deletion is done with the [clear_variable] tag:

{CLEAR_VARIABLE heroes[1]}

This will delete the Konrad record. Please note that the array will be renumbered: so the Lisar record will now have the index 1.

Adding blocks can be done with [set_variables] using the additional key mode. By default, [set_variables] creates a new array (or container), erasing any previous variable with the same name. But, using one of the different modes allows to add new blocks in various places:

  • replace: will clean the array name and replace it with given data, it’s the default.
  • append: will append given data to the current array
  • merge: will merge in the given data into name
  • insert: will insert the given data at the index specified in the name attribute, such as

name=my_array[1].

Note that [store_unit] has also a mode key allowing to append more units blocks to an array.

You can place any kind of block in an array, but usually, it’s good practice to avoid meddling different kind of records (for instance units and locations). The reason is most often, arrays are fetched and manipulated in loops. Loops are much more easy to program when all records have the same structure.

(Version 1.13.2 and later only) This explanation of loops is still relevant, but it's now recommended to use the [for] or [foreach] tags instead of the FOREACH macro.

The FOREACH macro is most often used for this purpose. It takes an array name and a variable name as arguments. The variable will contain an index incremented by one on each step of the loop by the ending macro NEXT. For instance, we can summarize the wealth of our heroes with this code:

{FOREACH heroes index}  
    [set_variable]  
        name=tmp  
        add=$heroes[$index].gold  
    [/set_variable]  
{NEXT index}  

[message]  
    speaker=narrator  
    message="Team has $tmp gold."  
[/message]

Here, we accumulate the gold amount of our heroes in the variable tmp. The loop will begin with index=0 and repeat the code inserted between FOREACH and NEXT, incrementing the value of index by one and will stop when index=heroes.length. FOREACH is easy to use, but one should be aware of two things:

- make sure you don’t use the index variable elsewhere: it shall be cleared at the end.
- when using such a loop to delete some records. For instance this:
{FOREACH heroes index}  
    [if]  
        [variable]  
            name=heroes[$index].name  
            equals="Konrad"  
        [/variable]  
        [then]  
            {CLEAR_VARIABLE heroes[$index]}  
        [/then]  
    [/if]  
{NEXT index}

will not work exactly as expected. As we saw earlier, the array will be renumbered when the “Konrad” record is deleted. So the next record will take the “Konrad” index, and, since index is incremented at the end of the step, the record following “Konrad” will be skipped. The correct way to do this is fetching the array in reverse order5) or decrementing the index after the deletion:

{FOREACH heroes index}  
    [if]  
        [variable]  
            name=heroes[$index].name  
            equals="Konrad"  
        [/variable]  
        [then]  
            {CLEAR_VARIABLE heroes[$index]}  
            [set_variable]  
                name=index  
                sub=1  
            [set_variable]  
        [/then]  
    [/if]  
{NEXT index}

More with arrays

Let's say we want our Trapper unit to be able to put traps all around the map. Each trap position is saved as a location, a container with x and y values, stored using [store_locations]. We have stored all our trap positions in this variable "trap_pos", but now we want to add another location where the Trapper is currently standing ($x1, $y1). How would we do that? Here is one simple way, using find_in:

[store_locations]
    x=$x1
    y=$y1
    [or]
        find_in=trap_pos
    [/or]
    variable=trap_pos
[/store_locations]

Continuing the example above, what if our Trapper had a change of heart, and now he wants to remove the trap where he is standing? He can easily remove that position from the "trap_pos" array, again by using find_in:

[store_locations]
    find_in=trap_pos
    [not]
        x=$x1
        y=$y1
    [/not]
    variable=trap_pos
[/store_locations]

Now the final piece is an event that will catch any unsuspecting unit who dares to step upon our well-placed traps:

[event]
    name=moveto
    first_time_only=no
    [filter]
        [filter_location]
            find_in=trap_pos
        [/filter_location]
    [/filter]
    # Boom! Gotcha
[/event]

The same find_in trick for growing and shrinking arrays of locations can also be used with arrays of units, using the find_in key of [store_unit].

First steps into darkness

Using simple strings in names

Consider this variable name:

heroes_$index  

How to understand this? At execution time, $index will be replaced with the value of the variable index6). Suppose it contains 2, the variable used will then be heroes_2. Of course, it can be anything else, “Konrad” for instance. Then the variable will be heroes_Konrad.

Look at these macros:

#define LSB_STOREPERSO ID KILL  
    [store_unit]  
        [filter]  
            id=${ID}  
        [/filter]  
        variable=${ID}_back  
        kill={KILL}  
    [/store_unit]  
#enddef

#define LSB_RECALLPERSO ID XY  
    [unstore_unit]  
        variable=${ID}_back  
        find_vacant=yes  
        {XY}  
    [/unstore_unit]  
#enddef

They store and retrieve an unit using it’s ID to create the name of the variable. The unit ID is found in the variable whose name is given in the parameter ID. For instance, it can be unit.id in a moveto event:

[event]  
    name=moveto  
    # ... the filter will come here  
    {LSB_STOREPERSO unit.id yes} # the unit disappear  
    # but is stored in a variable named after its id,  
    # for instance "Konrad_back"  
[/event]

The variables created using this code shouldn’t be confused with an array. They’re individual variables, even if they look more or less the same in the :inspect display. Particularly, they can’t be fetched using a FOREACH loop. But if this is not needed, one can find this better to create bi-dimensional arrays, particularly because distinct records are easier to identify in the :inspect display. In this code, we use quite the same system as above to create a bi-dimensional array to store boats and units on board. The unit ID (of the boat) is used to create the name of an array storing the units it contains, whose name is RF_$ID, for example RF_B1, RF_B2 and so on. So, in a moveto event of one of these boats, RF_$unit.id is the name of the array containing the crew. This code make them pop out.

{FOREACH RF_$unit.id| n}  
    [unstore_unit]  
        variable=RF_$unit.id|[$n]  
        x,y=$rft[0].x,$rft[0].y  
        find_vacant=yes  
    [/unstore_unit]                     
{NEXT n}

Fine, but here, the engine could have a problem: what is exactly RF_$unit.id[$n] ? It can be :

- the nth record of the array RF_$unit.id (if unit.id = B1, it would be RF_B1[$n])
- the simple variable named RF_$unit.id[$n], where the suffix is taken from $unit.id array.

That’s why the pipe character | is appended to the array name. It states the first case is the good one, or in other words, the array name is delimited between the $ sign and the pipe.

This is one way to create and use bi-dimensional arrays. But it’s possible to create real bi- dimensional array: they are arrays containing arrays (which could contain arrays as well, and so on… but will you really need that ?) Here is the way to do this. We shall use here our “heroes” array, and store it twice into a new created array:

[set_variables]  
    name=biDim  
    [insert_tag]  
        name=value  
        variable=heroes  
    [/insert_tag]  
    [insert_tag]  
        name=value  
        variable=heroes # of course it could be something different  
    [/insert_tag]  
[/set_variables]

Insert_tag creates a new block whose tag is equal to its name key, so each insert_tag will create a block:

[value]  
    [heroes]  
        name="Delfador"  
        gold=250  
        fame=127  
        fullName="Delfador the Great"  
    [/heroes]  
    [heroes]  
        name="Konrad"  
        gold=125  
        fame=10  
        fullName="Konrad the Heir"  
    [/heroes]  
    [heroes]  
        name="Lisar"  
        gold=1258  
        fame=250  
        fullName="Princess Lisar"  
    [/heroes]  
[/value]

Still with us? OK, now to access our heroes we shall use the ordinary syntax. For instance:

$biDim[0].heroes[1].name -> Konrad

And of course, one can walk all the array using a nested FOREACH loop.

{FOREACH biDim i}  
    {FOREACH biDim[$i].heroes index}  
        [if]  
            [variable]  
                name=biDim[$i].heroes[$index].gold  
                less_than=100  
            [/variable]  
            [then]  
                [set_variable]  
                    name=biDim[$i].heroes[$index].gold  
                    add=100  
                [/set_variable]  
            [/then]  
        [/if]  
    {NEXT index}  
{NEXT i}

This adds some gold to the purse of the poorest heroes of biDim array.

Using variables strings in events names

This syntax:

[event]  
    name=$myEvent  
    ...  
[/message]

Is perfectly valid. Of course, myEvent should contain some valid event name, consistent with the event body. One use of this is to specify turn numbers. For instance:

[event]  
    name=turn $afterDark  
    ...  
[/event]

With this you can set afterDark in order to state when the event should fire (it must then contain a number or a string of the form side number). Even more useful is the way to fire an event some turn after another occurred. Suppose we want to raise a storm two turns after some hero visited a particular location. Then we shall write:

[event]  
    name=moveto  
    # ... the moveto filter will come here  

    [event]  
        name="turn $($turn_number + 2)"  

        # start the storm  
    [/event]  
[/event]

This will create the nested event and make it fire 2 turns later. It can be used with fire_event too. This code sets the variable myEvent according to the incomer type, then fires the corresponding event.

[switch]  
    name=unit.type  
    [case]  
        value=Elvish Sorceress  
        {VARIABLE myEvent storm}  
    [/case]  
    [case]  
        value=Troll Warrior  
        {VARIABLE myEvent monster}  
    [/case]  
    [case]  
        value=Mermaid Initiate  
        {VARIABLE myEvent flood}  
    [/case]  
[/switch]  

# --- somewhat later…  
[fire_event]  
    name=$myEvent  
[/fire_event]  

# ... of course, these events should be defined elsewhere  
[event]  
    name=storm  
    ...  
[/event]

[event]  
    name=monster  
    ...  
[/event]

[event]  
    name=flood  
    ...  
[/event]

Voodoo and black magics

« He who enters this place must quit all hopes… »

At this point, maybe you begin to suspect many things can be replaced with variables, including parts of the code itself. That’s true and interesting in some cases, but it should be clear that using the features explained later creates code much more difficult to understand and to debug. You certainly shall discover that much more can be done than what we describe, but here, we shall restrain ourselves to some limits. Trespassing them is most often getting caught in mysterious traps, and most often, absolutely useless. In other words, you’re at risk to fall into deep darkness under heavy bugs attack. So take care…

Existing blocks customization

Since we can add members to existing containers, why not add some to documented containers like units or objects ? It’s perfectly possible, and finally harmless (You’re only are at risk your code become broken if the key name is used in further versions of Wesnoth) 7) . WML just ignores what it knows nothing of. In units blocks, you have a special block named variables where you can add safely all what you want, but it’s common to find in campaigns additions to the status block. For instance:

{VARIABLE unit.status.isHero yes}

creates a new flag named isHero in unit status. Of course, the game engine will not display anything as it does with slow and poison status key, but you can use it in filters:

[event]  
    name=die  
    first_time_only=no  
    [filter_condition]  
        [variable]  
            name=unit.status.isHero  
            boolean_equals=yes  
        [/variable]  
    [/filter_condition]  
     

The same can be done in objects. In this object block, the programmer added two custom keys, category and price.

[object]  
    name= _ "Poisonous Bow"  
    image=items/bow.png  
    description= _ "This bow deals 20% more damage than other bows, it shots poisonned arrows to enemies and is also quicker."  
    category=bows  
    price=150  
    [effect]  
        apply_to=new_attack  
        name=longbow  
        type=pierce  
        range=ranged  
        damage=12  
        number=4
        movement_used=0  
        icon=attacks/bow-elven-magic.png  
        [specials]  
            {WEAPON_SPECIAL_POISON}  
        [/specials]  
    [/effect]  
[/object]

And when this object is applied to an unit, the extra keys are not deleted or modified in any way.


Passing “parameters” to an event

The trick explained here is for use with the fire-event tag. As you know, this tag allows to trigger an event (custom or not) from another piece of code. Really useful for instance, when you don’t want to repeat the same code in many places. As the reference manual says, it can be used as some sort of subroutine call. Fine, but in programming languages, subroutines calls accept parameters for use of the subroutine, and fire_event don’t. Suppose we want to display a nice message which can be spoken by various units. Of course, we can use role or a global variable whoSpeaks to specify who is speaking. For instance:

[event]  
    name=nice_speech  
    [message]  
        speaker=$whoSpeaks  
        message=_"Vanish, foul messengers of Sauron…" # and so on…  
    [/message]  
[/event]

We can fire this event with:

{VARIABLE whoSpeaks Gandalf} # or any other character  
[fire_event]  
     name=nice_speech  
[/fire_event]

But we would have to clear the whoSpeaks variable later. There is another way. In the fire_event tag documentation, one can find: [primary_attack]: Information passed to the primary attack filter and $weapon variable on the new event. Of course, we have no attacker and no attack here, but what happens if we set something here like :

[fire_event]  
    name=nice_speech  
    [primary_attack]  
        whoSpeaks=Gandalf  
    [/primary_attack]  
[/fire_event]  

[event]  
    name=nice_speech  
    [message]  
        speaker=$weapon.whoSpeaks  
        message=_"Vanish, foul messengers of Sauron…" # and so on…  
    [/message]  
[/event]

Well, it pure voodoo but it works. Of course, one can pass anything in the primary_attack block, not only a single value. The block itself will be discarded when he event is finished, just like call parameters in subroutines.

Dynamic code with insert_tag

We have already seen a use of this tag above. Its effect is to insert at execution time a WML block contained in a variable. It is like a macro, but macro substitution occurs once when loading the code which makes a huge difference. With insert_tag, the variable used (i.e. the code executed) can change during the scenario. First step is creating a container holding the WML block you want to execute. For instance, this action:

[harm_unit]  
    [filter]  
        x,y=$x1,$y1  
    [/filter]  
    amount=10  
    animate=yes  
    kill=no  
[/harm_unit]

The easiest way is to use a macro to define the block content:

#define BLOCK_CONTENT  
    [filter]  
        x,y=$x1,$y1  
    [/filter]  
    amount=10  
    animate=yes  
    kill=no  
#enddef  
   
    [set_variables]  
        name=harmingCode  
        [value]  
            {BLOCK_CONTENT}  
        [/value]  
    [/set_variables]

Else, we should have to create the variable first, and then add the subtag in this way:

[set_variables]  
    name=harmingCode  
    [value]  
        amount=10  
        animate=yes  
        kill=no  
    [/value]  
[/set_variables]

[set_variables]  
    name=harmingCode.filter  
    [value]  
        x,y=$x1,$y1  
    [/value]  
[/set_variables]

Notice, we didn’t include the [harm_unit] tag. Then we can use insert_tag in this way:

[insert_tag]  
    name=harm_unit  
    variable=harmingCode  
[/insert_tag]

This will produce exactly the original block above. Sometimes, it’s not very practical to define only the block content in the macro (maybe you would like to use it elsewhere, as an ordinary macro). Then you can specify the command tag in the insert_tag. This tag does nothing except creating blocks. Then it would be:

#define HARM_UNIT  
   [harm_unit]  
        [filter]  
            x,y=$x1,$y1  
        [/filter]  
        amount=10  
        animate=yes  
        kill=no  
    [/harm_unit]  
#enddef  

    [set_variables]  
         name=harmingCode  
         [value]  
             {HARM_UNIT}  
         [/value]  
    [/set_variables]  

    # --- somewhere else…  

    [insert_tag]  
        name=command  
        variable=harmingCode  
    [/insert_tag]

But… this code works not always. The reason is the $x1, $y1. They are replaced with their values when we create the harmingCode variable, which is not what we generally want. We want to use the values they hold when the insert_tag is executed (most often, it’s later), in other words, we want to delay their substitution. To mark these variables to be substituted later , we shall add a pipe character just after the dollar sign:

#define HARM_UNIT  
    [harm_unit]  
        [filter]  
            x,y=$|x1,$|y1  
        [/filter]  
        amount=10  
        animate=yes  
        kill=no  
    [/harm_unit]  
#enddef

Then the code works. And the macro can be used in a normal way too. Another way to avoid the early variable substitution is using the tag literal instead of value when creating the harmingCode variable:

    [set_variables]  
         name=harmingCode  
         [literal]  
             {HARM_UNIT}  
         [/literal]  
    [/set_variables]

Pay heed, insert_tag must be included in some action (event and so on). It works not at the scenario level for instance. As you can see, the insert_tag allows to do many things, but in our opinion, should be used scarcely. Here we shall show how it can be used to solve a common problem. Suppose we want to list the objects of a unit in a option message, let the user choose an object and remove it from the unit. This can be done with this kind of code:

[message]  
    message="Choose an item to drop"  
    speaker=narrator  
    [option]  
        message="Fabulous speed potion"  
        [show_if]  
            # here a conditional expression stating if the unit has  
            # the fabulous item.  
        [/show_if]  
        [command]  
            # ... drop the item  
        [/command]  
    [/option]  
        # more options blocks...                            
[/message]

The show_if tag prevents the line to show if the unit has not the object. There are two problems with this code. First, the condition is not easy to write: the object list of the unit must be searched for that particular object. Next, the message block must have an option for each possible item. No problem if you have only a few, but when, like in some add-ons we shall name not, you have near one hundred… Here, we shall create dynamically a message block listing all the objects in the modification block of the unit. Thus, we don’t need the show_if tag et we want something like that:

[message]  
    message="Choose an item to drop"  
    speaker=narrator  
    [option]  
        message="Fabulous speed potion"  
        [command]  
            # ... drop the item  
        [/command]  
    [/option]  
    [option]  
        message="Amazing flashing bow"  
        [command]  
            # ... drop the item  
        [/command]  
        [/option]  
            # ... more options if more objects  
        [option]  
            message="Exit"  
        [command]  
            # ... exit the loop  
        [/command]  
    [/option]  
[/message]

So we shall create dynamically this block in a container variable and next use insert_tag to execute it. The block itself is embedded in a loop which executes until the exit option is chosen (this sets the flag t_done):

# list unit objects  
#define LSB_LIST_UNIT_THINGS  
   {VARIABLE t_done no}  

    [while]  
        [variable]  
            name=t_done  
            equals=no  
        [/variable]  
        [do]  
            {CLEAR_VARIABLE t_menu}  
            {VARIABLE t_menu.message " Choose an item to drop:"}  
            {VARIABLE t_menu.speaker narrator}  
 
# here begins the objects listing. They are in the modifications block of the unit  
            {FOREACH unit.modifications.object nc}  
                # creates the option block with the name of the object  
                [set_variables]  
                    name=t_menu.option  
                    mode=append  
                       [value]  
                           message=$unit.modifications.object[$nc].name  
                           [command]  
                               # remove the object, or anything else  
                               {LSB_CLEAR_UNITOBJECT}  
                           [/command]  
                        [/value]              
                [/set_variables]  
            {NEXT nc}  

# this adds the “exit” option to the end of the list  
            {VARIABLE nc $t_menu.option.length}  
                [set_variables]  
                    name=t_menu.option  
                    mode=append  
                    [value]  
                        message="Exit."  
                        [command]  
                            {VARIABLE t_done yes}  
                        [/command]  
                    [/value]              
                [/set_variables]  

# this finally displays the list on screen and let the user choose  
                [insert_tag]  
                    name=message  
                    variable=t_menu  
                [/insert_tag]  
            [/do]  
        [/while]  
    {CLEAR_VARIABLE t_menu,nc}  
#enddef  

# this is an example of use: in a right-click menu item.  
    [set_menu_item]  
        id=LSB_drop  
        description="Drop items."  
        [show_if]  
            [variable]  
                name=unit.side  
                equals=1  
            [/variable]  
        [/show_if]  
        [command]  
            {LSB_LIST_UNIT_THINGS}  
        [/command]  
    [/set_menu_item]

Of course, you may want to list not all objects applied to a unit. It’s easy to do that adding a custom key to the objects you want to list, for instance droppable=yes, and testing if it is present before creating the option block.

Another example of code managing pickuppable items on the map. We first define those objects using macros. For instance, this one is the core Storm Trident with some additional keys. The [object] tag is missing in order to use this macro more easily in an [insert_tag].

# --- Object definition example, don’t include the [object] tag  
#define LSB_STORM_TRIDENT  
    name="storm trident"  
    image=items/storm-trident.png  
    duration=forever  
    description={RTN_USTR-6}  
    category=spears  
    level=2  
    [effect]  
        apply_to=new_attack  
        name="storm trident"  
        description="storm trident"  
        icon=attacks/lightning.png  
        type=fire  
        range=ranged  
        [specials]  
            {WEAPON_SPECIAL_MAGICAL}  
        [/specials]  
        damage=15  
        number=2  
    [/effect]  
 
    {LIGHTNING_ANIMATION "storm trident" 1}  
    {LIGHTNING_ANIMATION "storm trident" 2}  
    {LIGHTNING_ANIMATION "storm trident" 3}  
#enddef

At the beginning of the scenario or even the campaign, we define an object list containing all pickuppable items. Please note it’s the only place we need to modify when creating or deleting an object in our campaign. All the code needed to manage them is generic.

# --- shortcut to store an object into an array  
#define LSB_OBJINFO OBJ  
    [value]  
        {OBJ}  
    [/value]  
#enddef  
  
# This is the main objects list which must be created at first start: it creates an array with all pickuppable items and give them an uid.  
#define LSB_CREATEOBJECTS_LIST  
    [set_variables]  
        name=Objets  
        mode=replace  
        {LSB_OBJINFO {RTN_OBJ_TELNECKLACE} }  
        {LSB_OBJINFO {RTN_OBJ_AELTHRANK} }  
        {LSB_OBJINFO {LSB_GOLD} }  
        {LSB_OBJINFO {LSB_STORM_TRIDENT} }  
        # add more here at will  
    [/set_variables] 
 
    {FOREACH Objets i} # this adds an id to objects  
        [set_variable]  
            name=Objets[$i].uid  
            value=$i  
        [/set_variable]  
    {NEXT i}  
#enddef

Here a macro to drop objects on the map. Note the NUM parameter is the uid created before, not the full object itself. Of course, it can be a variable holding an uid.

#define LSB_DROP_OBJECT NUM X Y  
    [item] # place the item on the map  
         image=$Objets[{NUM}].image  
         x,y={X},{Y}  
    [/item]  
    [set_variables] # add it to the dropped objects list  
        name=D_Objets  
        mode=append  
        [value]  
             x={X}  
             y={Y}  
             code={NUM}  
        [/value]              
    [/set_variables]  
#enddef

At last, we can set up a moveto event to trigger the pick up dialog

#define LSB_GETOBJECT FILTER ID  
    [event]  
        name=moveto  
        id=GETOBJECT_{ID}  
        first_time_only=no  
        [filter]  
            [filter_location] # fires only if there is something on the map  
                find_in=D_Objets  
            [/filter_location]  
            {FILTER} # and for some units  
        [/filter]  

        {VARIABLE i $D_Objets.length}  
        [while] # maybe we have more than one object here  
            [variable]  
                name=i  
                greater_than=0  
            [/variable]  
            [do]  
                [set_variable]  
                    name=i  
                    sub=1  
                [/set_variable]  
                
# message  
                [if]  
                    [variable]  
                        name=D_Objets[$i].x  
                        equals=$x1  
                    [/variable]  
                    [variable]  
                        name=D_Objets[$i].y  
                        equals=$y1  
                    [/variable]  
                    [then]                         
                        [message]  
                            speaker=narrator  
                            message=_ "There is a $Objets[$D_Objets[$i].code].name on the ground. Should $unit.name take it ?"  
                            [option]  
                                message=_ "Take it"                                         
                                [command]                                     
                                    # --- give object to unit  
                                    [insert_tag]  
                                        name=object  
                                        variable=Objets[$D_Objets[$i].code]  
                                    [/insert_tag]  
                                             
                                    # --- clear the map  
                                    [remove_item]                                         
                                        image=$Objets[$D_Objets[$i].code].image  
                                        x,y=$unit.x,$unit.y                 
                                    [/remove_item]  
                                    {CLEAR_VARIABLE D_Objets[$i]}  
                                [/command]  
                            [/option]  
                            [option]  
                                message= _ "Leave it"  
                            [/option]  
                        [/message]  
                    [/then]
                [/if]  
            [/do]
        [/while]  
    [/event]  
#enddef
This page was last edited on 17 July 2019, at 12:57.