Calling macros: Difference between revisions
No edit summary |
m (Taustin moved page calling macros to Calling macros over redirect) |
||
(31 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
{{Advanced}} | |||
{{ | = Calling Macros From another Macro = | ||
Sometimes you want to call a macro from another macro. As a coder, you want this often, because it | |||
enables you to split your code in small, simple chunks that can be reused over and over again. This | |||
handy technique also helps keep the stack size requirement low and mitigates the need for large | |||
code-level-nesting. | |||
When calling a macro from another macro, you will often want to transfer data from one to the other | |||
and vice versa. In addition, it is also important to know what happens with chat output. | |||
There are four different ways to call a macro - and they all behave a little bit differently. While | |||
reading this tutorial, please be sure to follow the wiki-links provided for any items you are not | |||
familiar with, as they will not be covered. | |||
==The Four Methods to Call a Macro== | |||
===The Macro Roll Option === | |||
This is the most straight forward way to call a macro. When using the roll option {{roll|macro}}, | |||
you must specify the location of the macro being called and give a single argument. Note that this | |||
argument must always be specified, even if you do not need it. In such cases, it is common practice | |||
is to use an empty string "". | |||
This argument can be accessed inside the called macro via the special variable called [[macro.args]]. | |||
All of the called macro's output will be inserted into the calling macro at the point in the code | |||
where the {{roll|macro}} roll option is placed. This output usually goes into the chat output. | |||
However, you can choose to instead assign the returned value to the variable [[macro.return]]. | |||
Once the called macro is processed, the [[macro.return]] variable can be used in the calling macro in a function call. | |||
====Example==== | |||
Lets say you have a macro called {{code|attackRoll}} that can roll any number of dice. In another macro, you want to call {{code|attackRoll}}, have it roll three dice and then use the result of that roll. | |||
Calling macro: | |||
<syntaxhighlight lang="mtmacro" line> [h, macro("attackRoll@Lib:Token"): 3] | |||
Attack roll: [r: macro.return] </syntaxhighlight> | |||
Called macro (named {{code|attackRoll}} and placed on token {{code|Lib:Token}}): | |||
== | <syntaxhighlight lang="mtmacro" line> [h: diceNr = macro.args] | ||
[r: roll(diceNr, 6)] </syntaxhighlight> | |||
Note that you could write the called macro this way, too: | |||
<syntaxhighlight lang="mtmacro" line> [h: diceNr = macro.args] | |||
[h: macro.return = roll(diceNr, 6)] </syntaxhighlight> | |||
For this simple example, it really makes no difference. | |||
===User Defined Function=== | |||
Often referred to as UDF in the forum, User Defined Functions are probably the most convenient way to do complex coding. A User Defined Function can be used just like a regular function. It can have arguments and it will be replaced by its resulting value when MapTool parses the calling macro. ''User defined'' means that you can specify the macro to be called when you create the function. | |||
Arguments are assigned to the UDF by writing them inside the parentheses (), separated by comas. The macro's complete chat output will be used as the resulting value and replace the function call, so you can easily assign it to a variableor use it as an argument for another function. It is important to note that HTML comments will be included in this output as well, even though they will not appear in the chat window. Also note that only the first macro in an execution chain will dump its output to chat. | |||
In the called macro, the arguments are inside the [[macro.args]] variable, formatted as a JSON array. | |||
You can use {{func|argCount}} and {{func|arg}} for easy access. | |||
To set up a UDF you have to call {{func|defineFunction}} on every client where the function will be used. | |||
This can be done automatically by placing all the {{func|defineFunction}} calls inside the special macro [[onCampaignLoad]] on a library token. This is the standard practice. It is executed whenever the campaign file is loaded by MT (whether from a server or from file). | |||
====Example==== | |||
Lets rewrite the above example using a UDF. | |||
{{code|onCampaignLoad}} on a token {{code|Lib:Token}}: | |||
<syntaxhighlight lang="mtmacro" line> | |||
[defineFunction("attackRoll", "attackRoll@Lib:Token")] | |||
</syntaxhighlight> | |||
Calling macro: | |||
<syntaxhighlight lang="mtmacro" line> | |||
Attack roll: [r: attackRoll(3)] | |||
</syntaxhighlight> | |||
Called macro (named {{code|attackRoll}} and placed on token {{code|Lib:Token}}): | |||
<syntaxhighlight lang="mtmacro" line> | |||
[h: assert(argCount()>0, "attackRoll() expects one argument.")] | |||
[h: diceNr = arg(0)] | |||
[r: roll(diceNr, 6)] | |||
</syntaxhighlight> | |||
====Create UDFs Automatically==== | |||
You can write a macro that scans your [[Library_Token|Lib:token]] macros and converts them all into user defined functions. This is a nice, convenient little trick that's done here ([http://forums.rptools.net/viewtopic.php?f=20&t=19856#p209019m see forum post]), in really elaborated way, by aliasmask. | |||
===Macro Links=== | |||
When you want to call macros on user reaction, you can send out clickable links to chat or place them into frames. Also use them if you want to work with HTML forms or the fancier form-based events. | |||
Since the macro is not executed immediately, there is a way to use the macro's result in the calling macro. Arguments, the token in context and where the output should be sent can all be specified precisely when you create the macro link. | |||
See also {{func|macroLink}}, {{func|macroLinkText}}. | See also {{func|macroLink}}, {{func|macroLinkText}}. | ||
====Example==== | |||
This time, let us assume we want to send an attack roll to chat and then ask for a defense roll. We also want to send the Macro Link to everybody connected (because that is much easier) and don't care about the current token in context. | |||
Calling macro: | |||
<syntaxhighlight lang="mtmacro" line> | |||
[h: atk = roll(3,6)] | |||
Attack roll: [r: atk]<br> | |||
[r: macroLink("Do you want to defend?", "defenceRoll@Lib:Token", "all", atk)] | |||
</syntaxhighlight> | |||
Called macro (named {{code|defenceRoll}} and placed on token {{code|Lib:Token}}): | |||
''Note that this macro will be executed whenever the link is clicked.'' | |||
<syntaxhighlight lang="mtmacro" line> | |||
[h: atk = macro.args] | |||
[h: def = roll(3,6)] | |||
Defence roll: [r: def] [r, if(atk<def): "You defended successfully!"; "You are hit."] | |||
</syntaxhighlight> | |||
===Evaluate a Macro=== | |||
This does not directly call a macro stored somewhere, but rather evaluates some string you feed into the function as if it were macro code. This happens in place. It is not easy to retrieve the macro code from a stored macro, thus this is not a good way to call a macro stored in the usual way. This is most often used for small code snippets created dynamically or stored on token properties. | |||
See also {{func|evalMacro}}, {{func|execMacro}}, {{func|json.evaluate}}, {{func|eval}}. Here, {{code|evalMacro}} and {{code|execMacro}} do exactly the same thing; they are just two different names with the same functionality. | |||
====Example==== | |||
Lets say a RPG has a complex weapon damage system with formulas that follow no rule. The formula for the active would be stored in a token property called {{code|damageFormula}}. | |||
<syntaxhighlight lang="mtmacro" line> | |||
[h: myFormula = getProperty("damageFormula")] | |||
You made [r: evalMacro(myFormula)] damage. | |||
</syntaxhighlight> | |||
Content of the {{code|damageFormula}} property: | |||
<syntaxhighlight lang="mtmacro" line> | |||
[h: "<!-- roll 1d3, weapon makes 3d6 dmg on 1 or 2 , 2d10 on a 3 -->"] | |||
[h: firstRoll = 1d3] | |||
[r, if(firstRoll==3): 2d10; 3d6] | |||
</syntaxhighlight> | |||
''Yeah, such a damage system would be horrible.'' | |||
One notable difference between {{code|eval}} and {{code|evalMacro}} is how you pass a parameter:<br> | |||
For example -- the call ... | |||
<syntaxhighlight lang="mtmacro" line> | |||
[r:myFunction()] | |||
</syntaxhighlight> | |||
... can also be called as follows with the two methods: | |||
<syntaxhighlight lang="mtmacro" line> | |||
[r:evalMacro("[r:myFunction()]"] | |||
</syntaxhighlight> | |||
Which has the same result as: | |||
<syntaxhighlight lang="mtmacro" line> | |||
[r:eval("myFunction()"] | |||
</syntaxhighlight> | |||
Do you see how {{code|evalMacro()}} works with square brackets (roll options) and {{code|eval()}} just the function text? | |||
== | ==Variable Context (Scope)== | ||
Usually, a new macro creates a new context (scope) for variables, thus locally defined variables in one macro are not defined in another. | |||
By using {{func|defineFunction}}, you can call macros that operate in the same ''variable context (scope)'' as the calling macro -- when you want so. | |||
The token context is usually transported along with the macro call. With macro links, you can specify the token context. | |||
[[Category:Tutorial]]] |
Latest revision as of 23:59, 15 March 2023
THIS IS AN ADVANCED ARTICLE
Calling Macros From another Macro
Sometimes you want to call a macro from another macro. As a coder, you want this often, because it enables you to split your code in small, simple chunks that can be reused over and over again. This handy technique also helps keep the stack size requirement low and mitigates the need for large code-level-nesting.
When calling a macro from another macro, you will often want to transfer data from one to the other and vice versa. In addition, it is also important to know what happens with chat output.
There are four different ways to call a macro - and they all behave a little bit differently. While reading this tutorial, please be sure to follow the wiki-links provided for any items you are not familiar with, as they will not be covered.
The Four Methods to Call a Macro
The Macro Roll Option
This is the most straight forward way to call a macro. When using the roll option [macro():], you must specify the location of the macro being called and give a single argument. Note that this argument must always be specified, even if you do not need it. In such cases, it is common practice is to use an empty string "".
This argument can be accessed inside the called macro via the special variable called macro.args.
All of the called macro's output will be inserted into the calling macro at the point in the code where the [macro():] roll option is placed. This output usually goes into the chat output. However, you can choose to instead assign the returned value to the variable macro.return.
Once the called macro is processed, the macro.return variable can be used in the calling macro in a function call.
Example
Lets say you have a macro called attackRoll
that can roll any number of dice. In another macro, you want to call attackRoll
, have it roll three dice and then use the result of that roll.
Calling macro:
[h, macro("attackRoll@Lib:Token"): 3]
Attack roll: [r: macro.return]
Called macro (named attackRoll
and placed on token Lib:Token
):
[h: diceNr = macro.args]
[r: roll(diceNr, 6)]
Note that you could write the called macro this way, too:
[h: diceNr = macro.args]
[h: macro.return = roll(diceNr, 6)]
For this simple example, it really makes no difference.
User Defined Function
Often referred to as UDF in the forum, User Defined Functions are probably the most convenient way to do complex coding. A User Defined Function can be used just like a regular function. It can have arguments and it will be replaced by its resulting value when MapTool parses the calling macro. User defined means that you can specify the macro to be called when you create the function.
Arguments are assigned to the UDF by writing them inside the parentheses (), separated by comas. The macro's complete chat output will be used as the resulting value and replace the function call, so you can easily assign it to a variableor use it as an argument for another function. It is important to note that HTML comments will be included in this output as well, even though they will not appear in the chat window. Also note that only the first macro in an execution chain will dump its output to chat.
In the called macro, the arguments are inside the macro.args variable, formatted as a JSON array. You can use argCount() and arg() for easy access.
To set up a UDF you have to call defineFunction() on every client where the function will be used. This can be done automatically by placing all the defineFunction() calls inside the special macro onCampaignLoad on a library token. This is the standard practice. It is executed whenever the campaign file is loaded by MT (whether from a server or from file).
Example
Lets rewrite the above example using a UDF.
onCampaignLoad
on a token Lib:Token
:
[defineFunction("attackRoll", "attackRoll@Lib:Token")]
Calling macro:
Attack roll: [r: attackRoll(3)]
Called macro (named attackRoll
and placed on token Lib:Token
):
[h: assert(argCount()>0, "attackRoll() expects one argument.")]
[h: diceNr = arg(0)]
[r: roll(diceNr, 6)]
Create UDFs Automatically
You can write a macro that scans your Lib:token macros and converts them all into user defined functions. This is a nice, convenient little trick that's done here (see forum post), in really elaborated way, by aliasmask.
Macro Links
When you want to call macros on user reaction, you can send out clickable links to chat or place them into frames. Also use them if you want to work with HTML forms or the fancier form-based events.
Since the macro is not executed immediately, there is a way to use the macro's result in the calling macro. Arguments, the token in context and where the output should be sent can all be specified precisely when you create the macro link.
See also macroLink(), macroLinkText().
Example
This time, let us assume we want to send an attack roll to chat and then ask for a defense roll. We also want to send the Macro Link to everybody connected (because that is much easier) and don't care about the current token in context.
Calling macro:
[h: atk = roll(3,6)]
Attack roll: [r: atk]<br>
[r: macroLink("Do you want to defend?", "defenceRoll@Lib:Token", "all", atk)]
Called macro (named defenceRoll
and placed on token Lib:Token
):
Note that this macro will be executed whenever the link is clicked.
[h: atk = macro.args]
[h: def = roll(3,6)]
Defence roll: [r: def] [r, if(atk<def): "You defended successfully!"; "You are hit."]
Evaluate a Macro
This does not directly call a macro stored somewhere, but rather evaluates some string you feed into the function as if it were macro code. This happens in place. It is not easy to retrieve the macro code from a stored macro, thus this is not a good way to call a macro stored in the usual way. This is most often used for small code snippets created dynamically or stored on token properties.
See also evalMacro(), execMacro(), json.evaluate(), eval(). Here, evalMacro
and execMacro
do exactly the same thing; they are just two different names with the same functionality.
Example
Lets say a RPG has a complex weapon damage system with formulas that follow no rule. The formula for the active would be stored in a token property called damageFormula
.
[h: myFormula = getProperty("damageFormula")]
You made [r: evalMacro(myFormula)] damage.
Content of the damageFormula
property:
[h: "<!-- roll 1d3, weapon makes 3d6 dmg on 1 or 2 , 2d10 on a 3 -->"]
[h: firstRoll = 1d3]
[r, if(firstRoll==3): 2d10; 3d6]
Yeah, such a damage system would be horrible.
One notable difference between eval
and evalMacro
is how you pass a parameter:
For example -- the call ...
[r:myFunction()]
... can also be called as follows with the two methods:
[r:evalMacro("[r:myFunction()]"]
Which has the same result as:
[r:eval("myFunction()"]
Do you see how evalMacro()
works with square brackets (roll options) and eval()
just the function text?
Variable Context (Scope)
Usually, a new macro creates a new context (scope) for variables, thus locally defined variables in one macro are not defined in another.
By using defineFunction(), you can call macros that operate in the same variable context (scope) as the calling macro -- when you want so.
The token context is usually transported along with the macro call. With macro links, you can specify the token context.]