Macros:Branching and Looping: Difference between revisions

From RPTools Wiki
Jump to navigation Jump to search
mNo edit summary
m (Replaced `¦¦` with `||`)
 
(15 intermediate revisions by 9 users not shown)
Line 1: Line 1:
This page details the branching and looping structures in MapTool. With the exception of the Block If statement, these are all [[Macros:Roll:types | roll options]] and should follow the general form for roll options:
{{Languages|Macros:Branching and Looping}}{{Intermediate}}
== Introduction ==
This page details the branching and looping structures in MapTool. With the exception of the block {{func|if}} statement, these are all [[Macros:Roll:types | roll options]] and should follow the general form for roll options:


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[option1[,option2]: body]
[option1[,option2]: body]
</source>
</syntaxhighlight>


These may be combined with other roll options (note that in some examples, they are combined with the [[Macros:Roll:types#.5B_.5D_Hidden_Rolls | Hidden Roll]] option (''h'') to hide the default output of the loop or branch).  
These may be combined with other roll options (note that in some examples, they are combined with the [[Macros:Roll:types#.5B_.5D_Hidden_Rolls | Hidden Roll]] option ({{code|h}}) to hide the default output of the loop or branch).  


'''If you wish to combine roll options in a single statement, separate the roll options with a comma, and place the colon at the end of the sequence of roll options.  Note that some combinations have unpredictable results, such as using IF() with MACRO().'''  
'''If you wish to combine roll options in a single statement, separate the roll options with a comma, and place the colon at the end of the sequence of roll options.  Note that some combinations have unpredictable results, such as using {{roll|if}} with {{roll|macro}}.'''  


For example, if you want to combine a Hidden Roll, TOKEN(), and FOREACH() option in a single statement, you would enter the line like so:
For example, if you want to combine a Hidden Roll, {{roll|token}}, and {{roll|foreach}} option in a single statement, you would enter the line like so:


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[H,TOKEN("JoeRandom"),FOREACH(item, TokensItemList): "This item's name is "+item+"!"]
[h,token("JoeRandom"),foreach(item, TokensItemList): "This item's name is "+item+"!"]
</source>
</syntaxhighlight>




Line 22: Line 24:
'''Introduced''': Version 1.3.b46
'''Introduced''': Version 1.3.b46


This IF is a roll option (as mentioned above), but operates similarly to the block-style if.
This {{roll|if}} is a roll option (as mentioned above), but operates similarly to the block-style {{func|if}}.


====Usage====
====Usage====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[IF(condition): true_body; false_body]
[if(condition): true_body; false_body]
</source>
</syntaxhighlight>


;or
;or
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[IF(condition): true_body]
[if(condition): true_body]
</source>
</syntaxhighlight>


Either the ''true_body'' or ''false_body'' will be used, depending on the value of ''condition''. If the ''false_body'' is not given but the ''condition'' is false, then there is no output.
Either the {{code|true_body}} or {{code|false_body}} will be used, depending on the value of {{code|condition}}. If the {{code|false_body}} is not given but the {{code|condition}} is {{false}}, then there is no output.


====Example====
====Example====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h:val=12]
[h:val=12]
[h,IF(val == 12): newVal=12*12]
[h,if(val == 12): newVal=12*12]
New Value = [r:newVal]
New Value = [r:newVal]
</source>
</syntaxhighlight>


Outputs ''New Value = 144''.
Outputs {{code|New Value {{=}} 144}}.


====Note====
====Note====
 
For an alternate method for evaluating "if" conditions, see the function {{func|if}}.  Note that the {{roll|if}} roll option cannot be (usefully) combined with the {{roll|macro}} roll option as the roll options are not guaranteed to be executed in any particular order.  This means that the {{func|if}} function is a better choice in those cases.
For an alternate method for evaluating "if" conditions, see the function [[Macros:Functions:if|if()]].  Note that the IF() roll option cannot be (usefully) combined with the MACRO() roll option as the roll options are not guaranteed to be executed in any particular order.  This means that the [[Macros:Functions:if|if()]] function is a better choice in those cases.


===SWITCH Option===
===SWITCH Option===
Line 53: Line 54:
'''Introduced''': Version 1.3.b46
'''Introduced''': Version 1.3.b46


SWITCH chooses among several options and executes code based on the switch expression.  
{{roll|switch}} chooses among several options and executes code based on the switch expression.  


Note that the ''expression'' is a regular expression, so metacharacters such as '''*''' and '''()''' will need to have backslashes in front of them if you want to match them literally.
*'''<span style="color: #FF0000;">Note</span>''' that the {{code|expression}} is a regular expression, so metacharacters such as {{code|*}} and {{code|()}} will need to have backslashes in front of them if you want to match them literally.


====Usage====
====Usage====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[SWITCH(expression):
[switch(expression):
case case1: body1;
case case1: body1;
case case2: body2;
case case2: body2;
default: default_body]
default: default_body]
</source>
</syntaxhighlight>
or with a code block:
<syntaxhighlight lang="mtmacro" line>
[switch(expression), code:
case case1: {body1};
case case2: {body2};
default: {default_body}]
</syntaxhighlight>


====Example====
====Example====
<syntaxhighlight lang="mtmacro" line>
[h:powerType="at-will"]
[switch(powerType):
  case "at-will": "You may use this power as much as you like";
  case "encounter": "You may only use this power once per encounter";
  case "daily": "You may only use this power once per day"
]
</syntaxhighlight>
Outputs {{code|You may use this power as much as you like}}


<source lang="mtmacro" line>
Using a code block:
<syntaxhighlight lang="mtmacro" line>
[h:powerType="at-will"]
[h:powerType="at-will"]
[SWITCH(powerType):
[switch(powerType), code:
case "at-will": "You may use this power as much as you like";
case "at-will": {
case "encounter": "You may only use this power once per encounter";
    [r:token.name]:<br>
case "daily": "You may only use this power once per day"]
    [r:"You may use this power as much as you like"]
</source>
  };
case "encounter": {
    [r:token.name]:<br>
    [r:"You may only use this power once per encounter"]
  };
case "daily": {
    [r:token.name]:<br>
    [r:"You may only use this power once per day"]
  };
]
</syntaxhighlight>


Outputs "You may use this power as much as you like"
Using regex:
 
<syntaxhighlight lang="mtmacro" line>
<source lang="mtmacro" line>
[h:powerType=".*sword.*"]
[h:powerType=".*sword.*"]
[SWITCH(powerType):
[switch(powerType):
case "flail": "one-handed weapon; two-handed does Str*2 damage";
  case "flail": "one-handed weapon; two-handed does Str*2 damage";
case "shortsword": "used for jabs, so is a puncturing weapon";
  case "shortsword": "used for jabs, so is a puncturing weapon";
case "longsword": "a slashing weapon"]
  case "longsword": "a slashing weapon"
</source>
]
 
</syntaxhighlight>
Outputs "used for jabs, so is a puncturing weapon".  Notice that the first matching clause was the one that the SWITCH() option found.
Outputs {{code|used for jabs, so is a puncturing weapon}}.  Notice that the first matching clause was the one that the {{roll|switch}} option found.


===MACRO Option===
===MACRO Option===
Line 92: Line 120:
'''Introduced''': Version 1.3.b46
'''Introduced''': Version 1.3.b46


MACRO runs the named macro, inserting its text into chat.
{{roll|macro}} runs the named macro, inserting its text into chat.


====Usage====
====Usage====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[MACRO("macro_name@location"): macro_arguments]
[macro("macro_name@location"): macro_arguments]
</source>
</syntaxhighlight>


The called macro sees a variable called [[Macros:Special_Variables:macro.args|''macro.args'']] which contains the value of ''macro_arguments''. The called macro can set a variable called [[Macros:Special_Variables:macro.return|''macro.return'']], which becomes available to the calling macro. Other than ''macro.return'', the called macro shares no variables with the calling macro.
The called macro sees a variable called [[Macros:Special_Variables:macro.args|{{code|macro.args}}]] which contains the value of {{code|macro_arguments}}. The called macro can set a variable called [[Macros:Special_Variables:macro.return|{{code|macro.return}}]], which becomes available to the calling macro. Other than {{code|macro.return}}, the called macro shares no variables with the calling macro.


====Examples====
====Examples====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[MACRO("getDamage@Lib:combat"): damageRoll]
[macro("getDamage@Lib:combat"): damageRoll]
</source>
</syntaxhighlight>


Calls the macro "getDamage" which resides on a [[Token:library_token|library token]] called "Lib:combat", and passes the variable ''damageRoll'' as an argument to the called macro.  
Calls the macro {{code|getDamage}} which resides on a [[Token:library_token|library token]] called {{code|Lib:combat}}, and passes the variable {{code|damageRoll}} as an argument to the called macro.  


====Location Requirements====
====Location Requirements====


The ''location'' can be one of the following:
The {{code|location}} can be one of the following:


# TOKEN - the currently impersonated token (use the word "TOKEN", not the token's name)
# {{code|TOKEN}} - the currently impersonated token (use the word {{code|TOKEN}}, not the token's name)
# Library Token - a [[Token:library_token|Library Token]] in the current campaign
# {{code|CAMPAIGN}} - macros from the Campaign panel (the panel does not need to be open or visible on the screen)
# ''this'' - if the macro is calling another macro in the same library, "this" may be used instead of retyping the full library token name
# {{code|Library Token}} - a [[Token:library_token|Library Token]] in the current campaign
# {{code|this}} - if the macro is calling another macro in the same library, {{code|this}} may be used instead of retyping the full library token name


====Notes====
====Notes====


When a token macro calls another macro, the macro instructions in the ''called'' macro are executed against the ''calling'' token (in other words, the macro uses properties available on the calling token and applies all results to that token), unless the focus is explicitly changed to another token via either a roll option, or the [[Macros:Functions:switchToken|switchToken()]] function, or the [[Macros:Functions:getLibProperty|getLibProperty()]] function.
When a token macro calls another macro, the macro instructions in the ''called'' macro are executed against the ''calling'' token (in other words, the macro uses properties available on the calling token and applies all results to that token), unless the focus is explicitly changed to another token via either a roll option, or the {{func|switchToken}} function, or the {{func|getLibProperty}} function.


Also- as of at least 1.3.b50- a variable must be given for ''macro_arguments'', or the  
Also, as of at least 1.3.b50, a variable must be given for {{code|macro_arguments}}, or the  
"Could not execute the command: Undefined function: MACRO"
''"Could not execute the command: Undefined function: MACRO"''
error will result.  However, the variable given as ''macro_arguments'' doesn't have to be used.
error will result.  However, the variable given as {{code|macro_arguments}} doesn't have to be used.


===TOKEN Option===
===TOKEN Option===
Line 130: Line 159:
'''Introduced''': Version 1.3.b48
'''Introduced''': Version 1.3.b48


TOKEN executes a series of instructions against a token specified in the argument to [TOKEN():], rather than against the token running the macro.  
{{roll|token}} executes a series of instructions against a token specified in the argument rather than against the token running the macro.  


This is a temporary change in the token that has the "focus" - only the instructions following the colon are applied to the designated token; following the end of that instruction block, operations resume being performed against the token running the macro.
This is a temporary change in the token that has the "focus" - only the instructions following the colon are applied to the designated token; following the end of that instruction block, operations resume being performed against the token running the macro.


To permanently switch (for the duration of the macro) the token against which macro commands are executed, see the [[Macros:Functions:switchToken|switchToken()]] function.
To permanently switch (for the duration of the macro) the token against which macro commands are executed, see the {{func|switchToken}} function.


====Usage====
====Usage====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[token(token_identifier): ]
[token(token_identifier): ]
</source>
</syntaxhighlight>


Executes the roll against token specified by ''token_identifier'', which can either be the token name or token id.
Executes the roll against the token specified by {{code|token_identifier}}, which can either be the token name or token id.


====Examples====
====Examples====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h:target="Orc 5"]
[h:target="Orc 5"]
[h,token(target): targetAC = getProperty("AC")]
[h,token(target): targetAC = getProperty("AC")]
</source>
</syntaxhighlight>


Uses the [[Macros:Functions:getProperty|getProperty()]] function to retrieve the property ''AC'' from the token named "Orc 5", and assigns that value to the variable ''targetAC''. ''targetAC'' can be used in future calculations, such as determining whether an attack hits. If the TOKEN option was not used, the macro would have looked for the property ''AC'' on the token currently ''running'' the macro. Note also that this function is considered [[Macros:TrustedMacros|trusted]].
Uses the {{func|getProperty}} function to retrieve the property {{code|AC}} from the token named {{code|"Orc 5"}}, and assigns that value to the variable {{code|targetAC}}. {{code|targetAC}} can be used in future calculations, such as determining whether an attack hits. If the {{roll|token}} option was not used, the macro would have looked for the property {{code|AC}} on the token currently ''running'' the macro. Note also that this function is considered [[Macros:TrustedMacros|trusted]].


==Looping==
==Looping==
Line 159: Line 188:
'''Introduced''': Version 1.3.b41
'''Introduced''': Version 1.3.b41


The COUNT option executes a statement for a specified number of times, storing the number of the current iteration in a variable called ''[[Macros:Special_Variables:roll.count|roll.count]]''.  
The {{roll|count}} option executes a statement for a specified number of times, storing the number of the current iteration in a variable called {{code|[[roll.count]]}}.  


====Usage====
====Usage====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[COUNT(num): body]
[count(num): body]
[COUNT(num, separator): body]
[count(num, separator): body]
</source>
</syntaxhighlight>


The ''[[roll.count]]'' variable will take on values from 0 to (number of loops - 1). The optional separator (default ", ") is printed between each iteration.
The {{code|[[roll.count]]}} variable will take on values from {{code|0}} to {{code|(number of loops - 1)}}. The optional separator (default {{code|","}}) is printed between each iteration.


====Example====
====Example====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h:numHits=3]
[h:numHits=3]
[COUNT(numHits): Damage = Damage + 1d12]
[count(numHits): Damage = Damage + 1d12]
</source>
</syntaxhighlight>


This will iterate the ''Damage = Damage + 1d12'' operation 3 times, separating the result of each iteration with the default separator (a comma). An optional second argument to COUNT() allows the setting of a different separator.
This will iterate the {{code|Damage {{=}} Damage + 1d12}} operation 3 times, separating the result of each iteration with the default separator (a comma). An optional second argument to {{roll|count}} allows the setting of a different separator.


===FOR Option===
===FOR Option===
Line 185: Line 214:
====Usage====
====Usage====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[FOR(var, start, end): body]
[for(var, start, end): body]
[FOR(var, start, end, stepsize): body]
[for(var, start, end, stepsize): body]
[FOR(var, start, end, stepsize, separator): body]
[for(var, start, end, stepsize, separator): body]
</source>
</syntaxhighlight>


The ''var'' variable counts from ''start'' to ''end'' during the loop. The optional ''stepsize'' (default +1) is added to var at each iteration. The loop does ''not'' evaluate when {{code|var}} reaches {{code|end}}.  
The {{code|var}} variable counts from {{code|start}} to {{code|1}} short of {{code|end}} during the loop (so the {{code|end}} number will not be part of the loop). The optional {{code|stepsize}} (default {{code|+1}}) is added to {{code|var}} at each iteration. The loop does ''not'' evaluate when {{code|var}} reaches {{code|end}}.


====Example====
====Example====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[FOR(i,10,0,-2): "i is now " + i]
[for(i,10,0,-2): "i is now " + i]
</source>
</syntaxhighlight>


Counts down even numbers from 10 to 0.
Counts down even numbers from 10 to 2.


===FOREACH Option===
===FOREACH Option===
Line 204: Line 233:
'''Introduced''': Version 1.3.b46
'''Introduced''': Version 1.3.b46


Iterates over the contents of a string list in the format "''item1, item2, item3''", the contents of a JSON Array, or the keys of a JSON Object.
Iterates over the contents of a string list in the format {{code|"item1, item2, item3"}}, the contents of a JSON Array, or the keys of a JSON Object.


====Usage====
====Usage====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[FOREACH(var, list): body]
[foreach(var, list): body]
[FOREACH(var, list, output_separator): body]
[foreach(var, list, output_separator): body]
[FOREACH(var, list, output_separator, list_separator): body]
[foreach(var, list, output_separator, list_separator): body]
[FOREACH(var, jsonarray): body]
[foreach(var, jsonarray): body]
[FOREACH(var, jsonarray, output_separator): body]
[foreach(var, jsonarray, output_separator): body]
[FOREACH(var, jsonobject): body]
[foreach(var, jsonobject): body]
[FOREACH(var, jsonobject, output_separator): body]
[foreach(var, jsonobject, output_separator): body]
</source>
</syntaxhighlight>


====Example Using a List ====
====Example Using a List ====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h: enemyList="Orcs, Goblins, Ogres, Trolls"]
[h: enemyList="Orcs, Goblins, Ogres, Trolls"]
[FOREACH(enemy, enemyList, "<br>"): "You really hate " + enemy]
[foreach(enemy, enemyList, "<br>"): "You really hate " + enemy]
</source>
</syntaxhighlight>


Outputs
Outputs:
  You really hate Orcs
  You really hate Orcs
  You really hate Goblins
  You really hate Goblins
Line 230: Line 259:


====Example Using a JSON Array ====
====Example Using a JSON Array ====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h: weapons = json.append("[]", "Longsword", "Dagger", "Bow")]
[h: weapons = json.append("[]", "Longsword", "Dagger", "Bow")]
[FOREACH(wpn, weapons): wpn]
[foreach(wpn, weapons): wpn]
</source>
</syntaxhighlight>


Outputs
Outputs:
  Longsword, Dagger, Bow
  Longsword, Dagger, Bow


====Example Using a JSON Object ====
====Example Using a JSON Object ====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h: weaponData = json.set("{}",
[h: weaponData = json.set("{}",
     "Name": "Longsword",
     "Name": "Longsword",
Line 246: Line 275:
     "Weight": 30,
     "Weight": 30,
)]
)]
[FOREACH(field, weaponData): field]
[foreach(field, weaponData): field]
</source>
</syntaxhighlight>


Outputs
Outputs:
  Name, Damage, Type, Weight
  Name, Damage, Type, Weight


If you really wanted to see the key ''and'' the data, try this:
If you really wanted to see the key ''and'' the data, try this:


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h: weaponData = json.set("{}",
[h: weaponData = json.set("{}",
     "Name": "Longsword",
     "Name": "Longsword",
Line 261: Line 290:
     "Weight": 30,
     "Weight": 30,
)]
)]
[FOREACH(field, weaponData):
[foreach(field, weaponData):
     field + ": " + json.get(weaponData, field)]
     field + ": " + json.get(weaponData, field)]
</source>
</syntaxhighlight>


Outputs
Outputs:
  Name: Longsword, Damage: 1d6, Type: Slashing, Weight: 30
  Name: Longsword, Damage: 1d6, Type: Slashing, Weight: 30


''P.S.:  Note the trailing comma after the Weight field in the json.set() function?  It's ignored.  But putting it in makes it easier to copy/paste new lines into the function...''
''P.S.:  Note the trailing comma after the Weight field in the {{func|json.set}} function?  It's ignored.  But putting it in makes it easier to copy/paste new lines into the function...''


===WHILE Option===
===WHILE Option===
Line 277: Line 306:


====Usage====
====Usage====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[WHILE(condition): body]
[while(condition): body]
[WHILE(condition, separator): body]
[while(condition, separator): body]
</source>
</syntaxhighlight>


====Example====
====Example====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h:num=10]
[h:num=10]
[WHILE(num>=0): num = num-1]
[while(num>=0): num = num-1]
</source>
</syntaxhighlight>


Outputs ''9,8,7,6,5,4,3,2,1''
Outputs {{code|9,8,7,6,5,4,3,2,1}}


==Code Execution==
==Code Execution==
Line 296: Line 325:
'''Introduced''': Version 1.3.b46
'''Introduced''': Version 1.3.b46


The CODE option is used in conjunction with looping / branching options to execute multiple statements within a single "block" of a loop or branch, allowing the creation of more complex loops and branches.  
The {{roll|code}} option is used in conjunction with looping / branching options to execute multiple statements within a single "block" of a loop or branch, allowing the creation of more complex loops and branches.  


====Usage====
====Usage====
<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[CODE: { code_block }]
[code: { code_block }]
</source>
</syntaxhighlight>


The ''code_block'' is a collection of text and macro code, enclosed in a single {} pair. Everything within the {} is treated as a single block for the purposes of any looping or branching options.
The {{code|code_block}} is a collection of text and macro code, enclosed in a single {{code|{<nowiki>}</nowiki>}} pair. Everything within the {{code|{<nowiki>}</nowiki>}} is treated as a single block for the purposes of any looping or branching options.


====Example====
====Example====


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h:num=5]
[h:num=5]
[WHILE(num > 0), CODE:
[while(num > 0), code:
{
{
   This is iteration [r:num] <br>
   This is iteration [r:num] <br>
Line 315: Line 344:
   [num=num-1]
   [num=num-1]
}]
}]
</source>
</syntaxhighlight>


Outputs
Outputs:


  This is iteration 5 There are 4 iterations left
  This is iteration 5 There are 4 iterations left
Line 326: Line 355:
  0
  0


'''NOTE''': the digit output at the beginning of each line is an artifact of the WHILE loop's evaluation of ''num'' - since this roll does not have the ''h'' option active, the result of that evaluation is displayed.
'''NOTE''': the digit output at the beginning of each line is an artifact of the {{roll|while}} loop's evaluation of {{code|num}} - since this roll does not have the {{roll|h}} option active, the result of that evaluation is displayed.


====Nested CODE Blocks====
====Nested CODE Blocks====


To nest CODE:{} blocks, use a second CODE:{ } option, like so:
To nest {{code|code:{<nowiki>}</nowiki>}} blocks, use a second {{roll|code}} option, like so:


<source lang="mtmacro" line>
<syntaxhighlight lang="mtmacro" line>
[h:d20roll=1d20]
[h:d20roll=1d20]
[h:attackRoll=d20roll+AttackBonus]
[h:attackRoll=d20roll+AttackBonus]
[h,IF(attackRoll >= 16),CODE:
[h,if(attackRoll >= 16),code:
{
{
   [IF(d20roll == 20),CODE:
   [if(d20roll == 20),code:
   {
   {
     The attack is a critical hit!
     The attack is a critical hit!
Line 350: Line 379:
   The attack misses!
   The attack misses!
}]
}]
</source>
</syntaxhighlight>


MapTool can only handle two levels of nested code.
MapTool can only handle two levels of nested code.
Line 362: Line 391:
* {{code|<{{=}}}} - Less than or equal to
* {{code|<{{=}}}} - Less than or equal to
* {{code|{{=}}{{=}}}} - Equal to
* {{code|{{=}}{{=}}}} - Equal to
* {{code|!{{=}}}} or {{code|ne}} - Not equal
* {{code|!{{=}}}} - Not equal


Logical Operators:
Logical Operators:
Line 368: Line 397:
* {{code|{{!}}{{!}}}} - Or
* {{code|{{!}}{{!}}}} - Or


Boolean Operators:
It is important to note that the ''Equal'' condition operator must be two equal signs. If you are checking for a text string, place quotes around the text.
* {{code|true}}
* {{code|false}}


It is important to note that the ''Equal'' condition operator must be two equal signs. If you are checking for a text string, place quotes around the text.
Operator Precedence:
* {{code|( )}} - Parentheses are always done first; they can be nested
* {{code|!}} - Logical NOT
* {{code|&&}} - Logical AND
* {{code|{{!}}{{!}}}} - Logical OR
 
[[Category:Tutorial]]
{{Languages|Macros:Branching and Looping}}

Latest revision as of 23:59, 12 March 2024

Languages:  English  • 日本語

INTERMEDIATE
THIS IS AN INTERMEDIATE ARTICLE

Introduction

This page details the branching and looping structures in MapTool. With the exception of the block if() statement, these are all roll options and should follow the general form for roll options:

[option1[,option2]: body]

These may be combined with other roll options (note that in some examples, they are combined with the Hidden Roll option (h) to hide the default output of the loop or branch).

If you wish to combine roll options in a single statement, separate the roll options with a comma, and place the colon at the end of the sequence of roll options. Note that some combinations have unpredictable results, such as using [if():] with [macro():].

For example, if you want to combine a Hidden Roll, [token():], and [foreach():] option in a single statement, you would enter the line like so:

[h,token("JoeRandom"),foreach(item, TokensItemList): "This item's name is "+item+"!"]


Branching

IF Option

Introduced: Version 1.3.b46

This [if():] is a roll option (as mentioned above), but operates similarly to the block-style if().

Usage

[if(condition): true_body; false_body]
or
[if(condition): true_body]

Either the true_body or false_body will be used, depending on the value of condition. If the false_body is not given but the condition is false(0), then there is no output.

Example

[h:val=12]
[h,if(val == 12): newVal=12*12]
New Value = [r:newVal]

Outputs New Value = 144.

Note

For an alternate method for evaluating "if" conditions, see the function if(). Note that the [if():] roll option cannot be (usefully) combined with the [macro():] roll option as the roll options are not guaranteed to be executed in any particular order. This means that the if() function is a better choice in those cases.

SWITCH Option

Introduced: Version 1.3.b46

[switch():] chooses among several options and executes code based on the switch expression.

  • Note that the expression is a regular expression, so metacharacters such as * and () will need to have backslashes in front of them if you want to match them literally.

Usage

[switch(expression):
case case1: body1;
case case2: body2;
default: default_body]

or with a code block:

[switch(expression), code:
case case1: {body1};
case case2: {body2};
default: {default_body}]

Example

[h:powerType="at-will"]
[switch(powerType):
  case "at-will": "You may use this power as much as you like";
  case "encounter": "You may only use this power once per encounter";
  case "daily": "You may only use this power once per day"
]

Outputs You may use this power as much as you like

Using a code block:

[h:powerType="at-will"]
[switch(powerType), code:
case "at-will": {
    [r:token.name]:<br>
    [r:"You may use this power as much as you like"]
  };
case "encounter": {
    [r:token.name]:<br>
    [r:"You may only use this power once per encounter"]
  };
case "daily": {
    [r:token.name]:<br>
    [r:"You may only use this power once per day"]
  };
]

Using regex:

[h:powerType=".*sword.*"]
[switch(powerType):
  case "flail": "one-handed weapon; two-handed does Str*2 damage";
  case "shortsword": "used for jabs, so is a puncturing weapon";
  case "longsword": "a slashing weapon"
]

Outputs used for jabs, so is a puncturing weapon. Notice that the first matching clause was the one that the [switch():] option found.

MACRO Option

Introduced: Version 1.3.b46

[macro():] runs the named macro, inserting its text into chat.

Usage

[macro("macro_name@location"): macro_arguments]

The called macro sees a variable called macro.args which contains the value of macro_arguments. The called macro can set a variable called macro.return, which becomes available to the calling macro. Other than macro.return, the called macro shares no variables with the calling macro.

Examples

[macro("getDamage@Lib:combat"): damageRoll]

Calls the macro getDamage which resides on a library token called Lib:combat, and passes the variable damageRoll as an argument to the called macro.

Location Requirements

The location can be one of the following:

  1. TOKEN - the currently impersonated token (use the word TOKEN, not the token's name)
  2. CAMPAIGN - macros from the Campaign panel (the panel does not need to be open or visible on the screen)
  3. Library Token - a Library Token in the current campaign
  4. this - if the macro is calling another macro in the same library, this may be used instead of retyping the full library token name

Notes

When a token macro calls another macro, the macro instructions in the called macro are executed against the calling token (in other words, the macro uses properties available on the calling token and applies all results to that token), unless the focus is explicitly changed to another token via either a roll option, or the switchToken() function, or the getLibProperty() function.

Also, as of at least 1.3.b50, a variable must be given for macro_arguments, or the "Could not execute the command: Undefined function: MACRO" error will result. However, the variable given as macro_arguments doesn't have to be used.

TOKEN Option

Introduced: Version 1.3.b48

[token():] executes a series of instructions against a token specified in the argument rather than against the token running the macro.

This is a temporary change in the token that has the "focus" - only the instructions following the colon are applied to the designated token; following the end of that instruction block, operations resume being performed against the token running the macro.

To permanently switch (for the duration of the macro) the token against which macro commands are executed, see the switchToken() function.

Usage

[token(token_identifier): ]

Executes the roll against the token specified by token_identifier, which can either be the token name or token id.

Examples

[h:target="Orc 5"]
[h,token(target): targetAC = getProperty("AC")]

Uses the getProperty() function to retrieve the property AC from the token named "Orc 5", and assigns that value to the variable targetAC. targetAC can be used in future calculations, such as determining whether an attack hits. If the [token():] option was not used, the macro would have looked for the property AC on the token currently running the macro. Note also that this function is considered trusted.

Looping

COUNT Option

Introduced: Version 1.3.b41

The [count():] option executes a statement for a specified number of times, storing the number of the current iteration in a variable called roll.count.

Usage

[count(num): body]
[count(num, separator): body]

The roll.count variable will take on values from 0 to (number of loops - 1). The optional separator (default ",") is printed between each iteration.

Example

[h:numHits=3]
[count(numHits): Damage = Damage + 1d12]

This will iterate the Damage = Damage + 1d12 operation 3 times, separating the result of each iteration with the default separator (a comma). An optional second argument to [count():] allows the setting of a different separator.

FOR Option

Introduced: Version 1.3.b46

Executes a statement for a number of iterations based on a start and end value.

Usage

[for(var, start, end): body]
[for(var, start, end, stepsize): body]
[for(var, start, end, stepsize, separator): body]

The var variable counts from start to 1 short of end during the loop (so the end number will not be part of the loop). The optional stepsize (default +1) is added to var at each iteration. The loop does not evaluate when var reaches end.

Example

[for(i,10,0,-2): "i is now " + i]

Counts down even numbers from 10 to 2.

FOREACH Option

Introduced: Version 1.3.b46

Iterates over the contents of a string list in the format "item1, item2, item3", the contents of a JSON Array, or the keys of a JSON Object.

Usage

[foreach(var, list): body]
[foreach(var, list, output_separator): body]
[foreach(var, list, output_separator, list_separator): body]
[foreach(var, jsonarray): body]
[foreach(var, jsonarray, output_separator): body]
[foreach(var, jsonobject): body]
[foreach(var, jsonobject, output_separator): body]

Example Using a List

[h: enemyList="Orcs, Goblins, Ogres, Trolls"]
[foreach(enemy, enemyList, "<br>"): "You really hate " + enemy]

Outputs:

You really hate Orcs
You really hate Goblins
You really hate Ogres
You really hate Trolls

Example Using a JSON Array

[h: weapons = json.append("[]", "Longsword", "Dagger", "Bow")]
[foreach(wpn, weapons): wpn]

Outputs:

Longsword, Dagger, Bow

Example Using a JSON Object

[h: weaponData = json.set("{}",
    "Name": "Longsword",
    "Damage": "1d6",
    "Type": "Slashing",
    "Weight": 30,
)]
[foreach(field, weaponData): field]

Outputs:

Name, Damage, Type, Weight

If you really wanted to see the key and the data, try this:

[h: weaponData = json.set("{}",
    "Name": "Longsword",
    "Damage": "1d6",
    "Type": "Slashing",
    "Weight": 30,
)]
[foreach(field, weaponData):
    field + ": " + json.get(weaponData, field)]

Outputs:

Name: Longsword, Damage: 1d6, Type: Slashing, Weight: 30

P.S.: Note the trailing comma after the Weight field in the json.set() function? It's ignored. But putting it in makes it easier to copy/paste new lines into the function...

WHILE Option

Introduced: Version 1.3.b46

Repeatedly executes a statement until a condition becomes false.

Usage

[while(condition): body]
[while(condition, separator): body]

Example

[h:num=10]
[while(num>=0): num = num-1]

Outputs 9,8,7,6,5,4,3,2,1

Code Execution

CODE

Introduced: Version 1.3.b46

The [code():] option is used in conjunction with looping / branching options to execute multiple statements within a single "block" of a loop or branch, allowing the creation of more complex loops and branches.

Usage

[code: { code_block }]

The code_block is a collection of text and macro code, enclosed in a single {} pair. Everything within the {} is treated as a single block for the purposes of any looping or branching options.

Example

[h:num=5]
[while(num > 0), code:
{
  This is iteration [r:num] <br>
  There are [r:num-1] iterations left<br>
  [num=num-1]
}]

Outputs:

This is iteration 5 There are 4 iterations left
4, This is iteration 4 There are 3 iterations left
3, This is iteration 3 There are 2 iterations left
2, This is iteration 2 There are 1 iterations left
1, This is iteration 1 There are 0 iterations left
0

NOTE: the digit output at the beginning of each line is an artifact of the [while():] loop's evaluation of num - since this roll does not have the [h:] option active, the result of that evaluation is displayed.

Nested CODE Blocks

To nest code:{} blocks, use a second [code():] option, like so:

[h:d20roll=1d20]
[h:attackRoll=d20roll+AttackBonus]
[h,if(attackRoll >= 16),code:
{
  [if(d20roll == 20),code:
  {
    The attack is a critical hit!
    [h:damage=critDamage]
  };
  {
    The attack is a hit!
    [h:damage=regDamage]
  }]
};
{
  The attack misses!
}]

MapTool can only handle two levels of nested code.

Additions

Conditional Operators:

  • > - Greater than
  • < - Less than
  • >= - Greater than or equal to
  • <= - Less than or equal to
  • == - Equal to
  • != - Not equal

Logical Operators:

  • && - And
  • || - Or

It is important to note that the Equal condition operator must be two equal signs. If you are checking for a text string, place quotes around the text.

Operator Precedence:

  • ( ) - Parentheses are always done first; they can be nested
  • ! - Logical NOT
  • && - Logical AND
  • || - Logical OR

Languages:  English  • 日本語