Macro Best Practices

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

User avatar
Bone White
Great Wyrm
Posts: 1124
Joined: Tue Aug 23, 2011 11:41 am
Location: Cornwall, UK

Re: Macro Best Practices

Post by Bone White »

That's what I thought at first Rumble, which is why I edited my post. Unfortunately that edit came between your replies it appears. I'll run some tests:

macro"UDF"

Code: Select all

[h: varX = "UDF"]
macro"Macro Clicked"

Code: Select all

[h: varX = "default"]
[h: runUDF()]
[varX]
Remember, defineFunction is in the format defineFunction(function, macro, ignoreOutput, newScope).

when [h: defineFunction("runUDF","UDF@this",0,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",0,1)] ; varX = "default"
when [h: defineFunction("runUDF","UDF@this",1,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",1,1)] ; varX = "default"

So the controlling variable in the question is indeed newScope. I'm going to edit my original reply back to be correct.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Macro Best Practices

Post by aliasmask »

davout wrote:
Barrodin wrote:I chose to do the whole eval and assign deal because I specifically didn't want it to require me to remember the name of variables -inside- the function when I was writing code that called the function. By passing a variable name and evaluating it inside the function (and vice versa on the return), I can avoid either side having to have "magic names" for the input and output variable.
:shock: Oh my that is major mind shift from other languages.

Thanks.
Yeah, I find it best to avoid "magic names" too. What I also do with macros that share a scope with the parent is "scope" the variable names by putting local.varName or something similar so I know this variable is from there and stays there. Other magic variable that may appear would stand out. Also, even if I can use variables directly from the parent, I usually pass it. But in regards to this thread, we're talking about when it's more economical to reference them indirectly by name.

Code: Select all

...
[H: bigVarObject = {...}]
[H: bigVarName = "bigVarObject"]
[H: am.xx.processBigVarUDF(bigVarName)]
<!-- bigVarObject would be updated by this line --> 

Code: Select all

[H: local.varName = arg(0)]
[H: set("local.varValue",eval(local.varName))]
<!-- do stuff with local.varValue -->
...
<!-- now update original object -->
[H: set("local.varName",local.varValue)] 

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Macro Best Practices

Post by aliasmask »

Bone White wrote:That's what I thought at first Rumble, which is why I edited my post. Unfortunately that edit came between your replies it appears. I'll run some tests:

macro"UDF"

Code: Select all

[h: varX = "UDF"]
macro"Macro Clicked"

Code: Select all

[h: varX = "default"]
[h: runUDF()]
[varX]
Remember, defineFunction is in the format defineFunction(function, macro, ignoreOutput, newScope).

when [h: defineFunction("runUDF","UDF@this",0,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",0,1)] ; varX = "default"
when [h: defineFunction("runUDF","UDF@this",1,0)] ; varX = "UDF"
when [h: defineFunction("runUDF","UDF@this",1,1)] ; varX = "default"

So the controlling variable in the question is indeed newScope. I'm going to edit my original reply back to be correct.
Here's a more complicated version of my onCampaignLoad:

Code: Select all

@@ @onCampaignLoad
[H: '<!-- All functions will be given a UDF name excluding tags and special characters -->']
[H: prefix = "am.play."]
[H: this = getMacroLocation()]

[H: '<!-- Macro names ending with .html will allow output. "local" and "sub" also sets newScope to 0. -->']
[H: outputExtensions = json.append("","html","local","out","output","sub","css")]

[H: '<!-- Define functions here with options other than ignoreOutput = 1 and NewScope = 1 -->']
[H: defineFunction(prefix+"jsonToVars","jsonToVars@"+this,1,0)] 
[H: defineFunction(prefix+"varsToJson","varsToJson@"+this,1,0)] 
[H: defineFunction("math.mod","mod@"+this,1)] 
[H: defineFunction("math.div","div@"+this,1)] 
[H: defineFunction("math.isOdd","isOdd@"+this,1)] 

[H: '<!-- List functions names here to exclude from UDF definition -->']
[H: excludeList = json.append("","(new)","mod","div","isOdd")]
[H: tableFunctions = json.difference(getMacros("json"),excludeList)]

[H: '<!-- Define UDFs.  Already defined UDFs will not be redefined. -->']
[H, foreach(fn,tableFunctions), code: {
   [H: fName = replace(fn,"<[^>]*?>","")]
   [H: fName = replace(fName,"[^a-zA-Z0-9_.]","")]
   [H: cleanFN = strformat("%{prefix}%{fName}")]
   [H: extension = listGet(cleanFN,listCount(cleanFN,".")-1,".")]
   [H: newScope = 1 - listContains("local,sub",extension)]
   [H: ignoreOutput = 1 - json.contains(outputExtensions,extension)]
   [H: exclude = listContains("x",extension)]
   [H, if(! isFunctionDefined(cleanFN) && ! exclude): defineFunction(cleanFN,strformat("%{fn}@"+this),ignoreOutput,newScope)]
}]

[H: am.play.setupServer()]

!! 
This is really convenient when I want to set the type of UDF options just by naming the macro a certain way.

User avatar
davout
Cave Troll
Posts: 78
Joined: Wed Oct 12, 2011 2:28 am

Re: Macro Best Practices

Post by davout »

aliasmask wrote:What I also do with macros that share a scope with the parent is "scope" the variable names by putting local.varName or something similar so I know this variable is from there and stays there.
How far deep does this "no new scope" go? If you had UDF A call UDF B which calls UDF C, all defined with not having new scope. Could UDF C modify variables in UDF A? If yes, you may have to go beyond local.varName since it's possible other UDFs could have that. I guess you might have to use functionName.varName

For me, coming from Object-Oriented Programming where data encapsulation is held as sacred, having methods have access to variables of the caller warps my fragile little mind and gives me the heebee jeebees :)

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Macro Best Practices

Post by aliasmask »

Yes, if you're doing more than one level you'll have to consider your scope variables. I never found the need to do that though.

User avatar
davout
Cave Troll
Posts: 78
Joined: Wed Oct 12, 2011 2:28 am

Re: Macro Best Practices

Post by davout »

Aliasmask,

On your onCampaignLoad how does outputExtensions work?

I created two macros both with the following line of code:

Code: Select all

[R: macro.return = 1d20]
One is named: TestAutoDef and the other is TestAutoDef.output

When I call:

Code: Select all

[H: ld_FC_TestAutoDef()]
The above does not produce any output as expected.

When I call:

Code: Select all

[H: ld_FC_TestAutoDef.output()]
The above also does not produce any output, not as expected.

Am I missing something?

Thanks.

BTW ld_FC_ is my prefix.

User avatar
aliasmask
RPTools Team
Posts: 9023
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Macro Best Practices

Post by aliasmask »

The extensions I use are for Wiki: defineFunction() purposes. If you call TestAutoDef.output, that is the macro that should have the output.

So, given what you have, change it in the following way.

Code: Select all

[H: macro.return = 1d20]

Code: Select all

[R: ld_FC_TestAutoDef()]

User avatar
davout
Cave Troll
Posts: 78
Joined: Wed Oct 12, 2011 2:28 am

Re: Macro Best Practices

Post by davout »

OK, my example was a poor example. But your comment did help me come up with what I was expecting.

Code: Select all

@@ @TestAutoDef
@PROPS@ fontColor=black;autoExecute=true;fontSize=1.00em;sortBy=;color=default;playerEditable=false;applyToSelected=false;group=Init;tooltip=;minWidth=
<html><table><tr><td>[R: 1d20]</td></tr></table></html>

!!
@@ @TestAutoDef.output
@PROPS@ fontColor=black;autoExecute=true;fontSize=1.00em;sortBy=;color=default;playerEditable=false;applyToSelected=false;group=Init;tooltip=;minWidth=
<html><table><tr><td>[R: 1d20]</td></tr></table></html>

!!

Code: Select all

<!-- This produces NO output -->
[R:ld_FC_TestAutoDef()]

<!-- This produces output -->
[R: ld_FC_TestAutoDef.output()]
Thanks

JimBlue
Kobold
Posts: 11
Joined: Tue Apr 07, 2015 6:40 pm
Contact:

Re: Macro Best Practices

Post by JimBlue »

Veggiesama wrote:And... USE HTML COMMENTS on anything even remotely strange, and don't slack on the description. You'll thank yourself later when you put down the project for a month and try to jump back into it.
While I have yet to write anything for rptools, comments are always a good idea. Never wait too long, you will forget. I guarantee it. If you don't like comments for yourself, other people will want to read them if you share your framework.
--
Jim
AD&D first edition site and maps http://crestofastar.drivein-jim.net/ villages, cities, nations. No adverts either site.
My Traveller site, some deck plans http://travellergame.drivein-jim.net/ 1096 planet maps

Post Reply

Return to “Macros”