Calling macros: Difference between revisions

From RPTools Wiki
Jump to navigation Jump to search
Line 37: Line 37:


[r: roll(diceNr, 6)]    </source>
[r: roll(diceNr, 6)]    </source>
Note that you could do this like this too:
<source lang="mtmacro" line> [h: diceNr = macro.args]
[h: macro.return = roll(diceNr, 6)]    </source>
It really makes no difference in this simple example.


===User Defined Function===
===User Defined Function===

Revision as of 20:49, 23 June 2012


 This article is a stub, you can help the RPTools Wiki project by contributing content to expand this article.

Calling macros from a macro

Sometimes you want to call a macro from a macro. When you are a coder you want this really often. It enables you to split your code in small, simple chunks and reuses it over and over again.

Also this is a handy technique to keep stack size requirement low and to get rid of large code-level-nesting.

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. There are four different ways to do so - and they all behave a bit differently. Let me introduce them shortly. Ensure that you follow the links I give you for I do not repeat all information that you can find there.

4 Methods to call a macro

The macro roll option

This is the most straight forward way to do so. You use the roll option [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.

All output will then be inserted into the calling macro at the place where the macro roll option is. 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.

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.

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 do this like this too:

 [h: diceNr = macro.args]

[h: macro.return = roll(diceNr, 6)]

It really makes no difference in this simple example.

User Defined Function

In the forums often encountered as UDF. This is probably the most convenient way when you 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 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. In the called macro the arguments are inside the macro.args variable formatted as JSON array. You can use argCount() and arg() for easier 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 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).

A good read is aliasmask's version of automated UDF-creation: see post

Example

Lets rewrite the example above 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)]



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.

Since the macro is not executed immediately there is way to use the result of the macro in the calling macro. Arguments, the token in context and where the output should be sent to can 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 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.


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 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 evalMacro(), execMacro(), json.evaluate(), eval() Here evalMacro and execMacro do exactly the same thing, they're 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]

One notable difference between eval and evalMacro is how you pass a parameter: Eg the 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()"]

Yeah, such a damage system would be horrible.

Variable context (scope)

Usually a new macro creates a new context (scope) for variables, so locally defined variables in the one macro are not defined in the other.

Using defineFunction() you can call macros that operate in the same variable context (scope) as the caller macro -- when you want so.

The token context is usually transported with the macro call. With macro links you can specify the token context.