Calling macros: Difference between revisions

From RPTools Wiki
Jump to navigation Jump to search
m (Taustin moved page calling macros to Calling macros over redirect)
 
(18 intermediate revisions by 5 users not shown)
Line 1: Line 1:
[[Category:Tutorial]]
{{Advanced}}
{{stub}}
= 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.


= Calling macros from a macro =
When calling a macro from another macro, you will often want to transfer data from one to the other
Sometimes you want to call a macro from a macro. When you are a coder you want this really often.
and vice versa. In addition, it is also important to know what happens with chat output.  
It enables you to split your code in small, simple chunks and reuses it over and over again.


This is a handy technique to keep stack size requirement low and to get rid of large code-level-nesting.
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.


If you want to use macros from another macro, you will want to transfer data from the one to the other and vice versa. Its also important to know what happens with chat output.


==4 Methods to call a macro==
==The Four Methods to Call a Macro==


===The macro roll option ===
This is the most straight forward way to do so. You use the roll option  {{roll|macro}} where you can specify the macro location and give a single argument. Note that this argument must always be specified, even when you dont need it. Common practice is to use an empty string "" in such a case.


This argument can be accessed inside the called macro via the special variable  [[macro.args]].
===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 "".


All output will then be inserted into the calling macro at the place where the macro roll option is.
This argument can be accessed inside the called macro via the special variable called [[macro.args]].
You can change that to a manually set value by setting [[macro.return]].


Usually the macro roll option output is inserted into the chat output but you can assign it to a variable or use it in a function call. The [[macro.return]] can be used in the calling macro after the macro was called and processed.
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====
====Example====
Lets say you have a attackRoll-macro that can roll any number of dice. In another macro you want it to roll 3 dice and use the result of the roll.
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:
Calling macro:


<source lang="mtmacro" line> [h, macro("attackRoll@Lib:Token"): 3]     
<syntaxhighlight lang="mtmacro" line> [h, macro("attackRoll@Lib:Token"): 3]     
 
Attack roll: [r: macro.return] </syntaxhighlight>
Attack roll: [r: macro.return] </source>


Called macro (named "attackRoll" and placed on token "Lib:Token):
Called macro (named {{code|attackRoll}} and placed on token {{code|Lib:Token}}):


<source lang="mtmacro" line> [h: diceNr = macro.args]
<syntaxhighlight lang="mtmacro" line> [h: diceNr = macro.args]
[r: roll(diceNr, 6)]    </syntaxhighlight>


[r: roll(diceNr, 6)]    </source>
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===
===User Defined Function===
In the forums often encountered as UDF. This is probably the most convenient way when you do complex coding.
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.  
 
A user defined function can be used just like a regular function. It can have arguments and it will be replaced by its result value when MapTool parses the calling macro. User defined means that you can specify what macro should be called when you write the given function name.


Arguments are given to the UDF by writing them comma separated in the parentheses(). The complete macro chat output will be used as result value (this is important. HTML comments for example will be in this output too, although they cant be seen in the chat window) and replace the function call, so you can easily assign it to a variable or use it as argument for another function. Note that only the first macro in an execution chain will dump its output to the chat.
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 JSON array. You can use {{func|argCount}} and {{func|arg}} for easier 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 if you place all defineFunction()-calls on a library token inside the special macro [[onCampaignLoad]]. This is the standard way to go. It is executed whenever the campaign file is loaded by MT (whether from a server or from file).
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.


A good read is aliasmask's version of automated UDF-creation: [http://forums.rptools.net/viewtopic.php?f=20&t=19856#p209019m see post]
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====
====Example====
Lets rewrite the example above using a UDF.
Lets rewrite the above example using a UDF.


onCampaignLoad on a token "Lib:Token"
{{code|onCampaignLoad}} on a token {{code|Lib:Token}}:
<syntaxhighlight lang="mtmacro" line>
[defineFunction("attackRoll", "attackRoll@Lib:Token")]
</syntaxhighlight>


<source lang="mtmacro" line>
[defineFunction("attackRoll", "attackRoll@Lib:Token")]
</source>


Calling macro:
Calling macro:
 
<syntaxhighlight lang="mtmacro" line>
<source lang="mtmacro" line>
Attack roll: [r: attackRoll(3)]
Attack roll: [r: attackRoll(3)]
</source>
</syntaxhighlight>
 
Called macro (named "attackRoll" and placed on token "Lib:Token):


<source lang="mtmacro" line>  
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: assert(argCount()>0, "attackRoll() expects one argument.")]
[h: diceNr = arg(0)]
[h: diceNr = arg(0)]
[r: roll(diceNr, 6)]
[r: roll(diceNr, 6)]
</source>
</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===
===Macro Links===
When you want to call macros on user reaction you can sent out clickable links to the chat or into frames. This way is also to be used if you want to work with html forms or the fancier form-based events.
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 way to use the result of the macro in the calling macro.  
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.
Arguments, the token in context and where the output should be sent to can specified precisely when you create the macro link.


See also {{func|macroLink}}, {{func|macroLinkText}}.
See also {{func|macroLink}}, {{func|macroLinkText}}.


====Example====
====Example====
This time let us assume we want to send an attack roll to chat and ask for a defense roll. We want to send the macro link to everybody connected (because that is much easier) and don't care about the current token in context.
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:
Calling macro:
 
<syntaxhighlight lang="mtmacro" line>
<source lang="mtmacro" line>
[h: atk = roll(3,6)]
[h: atk = roll(3,6)]
Attack roll: [r: atk]<br>
Attack roll: [r: atk]<br>
[r: macroLink("Do you want to defend?", "defenceRoll@Lib:Token", "all", atk)]
[r: macroLink("Do you want to defend?", "defenceRoll@Lib:Token", "all", atk)]
</source>
</syntaxhighlight>
 


Called macro (named "defenceRoll" and placed on token "Lib:Token):
Called macro (named {{code|defenceRoll}} and placed on token {{code|Lib:Token}}):
''Note that this macro will be executed whenever the link is clicked.''
''Note that this macro will be executed whenever the link is clicked.''
<source lang="mtmacro" line>  
<syntaxhighlight lang="mtmacro" line>
[h: atk = macro.args]
[h: atk = macro.args]
[h: def = roll(3,6)]
[h: def = roll(3,6)]


Defence roll: [r: def] [r, if(atk<def): "You defended successfully!"; "You are hit."]
Defence roll: [r: def] [r, if(atk<def): "You defended successfully!"; "You are hit."]
</source>
</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.
 
===Evaluate A Macro===
This does not directly call a macro stored somewhere but evaluates some string you feed into the function as if it would be macro code. This happens in place. Its not easy to retrieve the macro code from a stored macro so this is not a good way to call 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}}


====Example====
====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".
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>
<source lang="mtmacro" line>  
[h: myFormula = getProperty("damageFormula")]
[h: myFormula = getProperty("damageFormula")]
You made [r: evalMacro(myFormula)] damage.
You made [r: evalMacro(myFormula)] damage.
</source>
</syntaxhighlight>
 
Content of the "damageFormula" property


<source lang="mtmacro" line>  
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: "<!-- roll 1d3, weapon makes 3d6 dmg on 1 or 2 , 2d10 on a 3 -->"]
[h: firstRoll = 1d3]
[h: firstRoll = 1d3]
[r, if(firstRoll==3): 2d10; 3d6]
[r, if(firstRoll==3): 2d10; 3d6]
</source>
</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>


''Yeah, such a damage system would be horrible.''
... 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==
==Variable Context (Scope)==
Usually a new macro creates a new context for variables, so locally defined variables in the one macro are not defined in the other.
Usually, a new macro creates a new context (scope) for variables, thus locally defined variables in one macro are not defined in another.


Using defineFunction() you can call macros that operate in the same variable context then the caller macro -- when you want so.
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 with the macro call. With macro links you can specify the token context.
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

ADVANCED
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.]