Dynamically create defineFunction calls

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

Post Reply
koralas
Kobold
Posts: 24
Joined: Wed Jun 23, 2010 1:19 pm

Dynamically create defineFunction calls

Post by koralas »

Ok, so I am new... and have written some macros on multiple Lib: tokens, and am working on standardizing my code, at least to a degree... As such, I wanted to start making more use of Wiki: defineFunction(). But, where I didn't want to have to go in and write the Wiki: onCampaignLoad() code for each of the macros I want made into functions. Ok, so I may be a bit lazy at times too... ;) Anyhow, I came up with some code to do this dynamically for me. I searched the forums, but didn't find anything like this, at least with the searches I ran, so I thought I would post it in case anyone else will find it useful.

This code works by grabbing each macro from the token, and evaluating the GROUP property. Any group that ends with "functions" will have all macros in that group defined as functions for the campaign. Capitalization of the macro name, specifically the word "function" will not matter as I convert the name to lower case for the test as you will see in the code. It is important that the group name end in function, otherwise if you want it to start with that, you can change the code to Wiki: startsWith() instead of Wiki: endsWith(), and can change the trigger name if you so wish.

The name of the function is the same as the macro, or you can add a prefix (as I have done) or suffix to uniquely identify them as yours. I am adding a prefix of "saw.fw." to the functions as you will see in the code. For others that may be new, as I am, I have heavily commented the code (each line has a comment), much more commenting than I normally do, but it helped reinforce the steps to myself, it is basically a pseudocode.

EDIT - Also, you will throw errors if you try to define a function with the same name as another function on the same token. For example, if on token Lib:Race_ClassDefinitions in group RaceFunctions you have a macro called "getNames" and in ClassFunctions you copied that token (probably inadvertantly) you will get "getMacroProps(): first argument must be a number." You will have to find the offending macros on your own unfortunately. If, by chance, you have a macro named identically on two different tokens, such as Lib:RaceDef with a macro getNames, and Lib:ClassDef with a macro getNames, the last macro defined will win, and you will get some odd results later in your code. I would recommend to include a unique identifier for each token. I chose to use "saw.fw." to identify my functions, but also added additional information to the macro name (and thereby carried to the function name). So, keeping with the thought process of making the first word of the defined macro a verb, I have getRaceName and getClassName as macros which are then defined as functions saw.fw.getRaceNameand saw.fw.getClassName.
Spoiler

Code: Select all

<!-- Grab the list of macros on this token -->
[h: allMacros = getMacros("json")]

<!-- Start a blank list of macros to be declared as functions -->
[h: functionNames=""]

<!-- Loop through the list of macros -->
[foreach(object, allMacros, ""), code:
{
  <!-- Get the index of the current macro -->
  [h: macroIndex=getMacroIndexes(object)]
  
  <!-- Get the properties of the macro, stored in a JSON object -->
  [h: macroDetails=getMacroProps(macroIndex,"json")]
  
  <!-- Get the name of the macro -->
  [h: macroName=json.get(macroDetails, "label")]
  
  <!-- Get the name of the group and convert to lower case -->
  [h: groupName=lower(json.get(macroDetails, "group"))]
  
  <!-- Check to see if the group name ends in "functions" -->
  [if(endsWith(groupName,"functions")), code:
  {
    <!-- If it does, add it to the list of macros to be declared as functions -->
    [h: functionNames=json.append(functionNames,macroName)]
  };{}
  ]  
}
]

<!-- Loop through the list of macros to be declared as functions -->
[foreach(object,functionNames,""), code:
{
  <!-- Add a prefix "saw.fw." to the macro name to create the function name-->
  [h: defFunctionName="saw.fw."+object]
  
  <!-- Define the macro to be called by using the macro name followed by "@". -->
  <!-- Get the location (token name) of the token that contains the macro to -->
  <!-- complete the name of the macro to be called by the function -->
  [h: defMacroLocation=object+"@"+getMacroLocation()]
  
  <!-- Use the defineFunction command to create the function -->
  [h: defineFunction(defFunctionName,defMacroLocation)]
}
]

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

Re: Dynamically create defineFunction calls

Post by aliasmask »

Cool. I was thinking of doing something similar with MacroIO and have the option to build a function define list on another macro (autoUDF) which will be called from onCampaignLoad. That way there's no problem with overwriting other stuff in onCampaignLoad. I was going to make it a part of the import process, but it makes more sense to have it as a separate process. But rather than specifying a special group, I was going to scan the code for "[r:" and variations thereof. I'll come up with some regex pattern. Most of my code either starts with [h or [r and I put my html in a variable before outputting it.

I'll probably just use Wiki: matches() with the command of the macro to set the output. I'll also have a "clean" name for the macro. Some people put html tags and special characters which will not fly when calling a UDF. For example: "<html><b>Macro Name</b></html>" could be a valid name for a macro, but will not work for UDF call. I would clean it to be am.fw.macroName basically removing all tags first, then all other non-alphanumeric excluding [space,underscore,period]. If multiple words, lower case all, upper case first letter of the remaining words and remove spaces. I would probably leave the _ alone, but in most cases it wouldn't generally be there or treat it as a space.

koralas
Kobold
Posts: 24
Joined: Wed Jun 23, 2010 1:19 pm

Re: Dynamically create defineFunction calls

Post by koralas »

aliasmask wrote:Cool. I was thinking of doing something similar with MacroIO and have the option to build a function define list on another macro (autoUDF) which will be called from onCampaignLoad. That way there's no problem with overwriting other stuff in onCampaignLoad. I was going to make it a part of the import process, but it makes more sense to have it as a separate process. But rather than specifying a special group, I was going to scan the code for "[r:" and variations thereof. I'll come up with some regex pattern. Most of my code either starts with [h or [r and I put my html in a variable before outputting it.

I'll probably just use Wiki: matches() with the command of the macro to set the output. I'll also have a "clean" name for the macro. Some people put html tags and special characters which will not fly when calling a UDF. For example: "<html><b>Macro Name</b></html>" could be a valid name for a macro, but will not work for UDF call. I would clean it to be am.fw.macroName basically removing all tags first, then all other non-alphanumeric excluding [space,underscore,period]. If multiple words, lower case all, upper case first letter of the remaining words and remove spaces. I would probably leave the _ alone, but in most cases it wouldn't generally be there or treat it as a space.
Cool, I hadn't looked at Wiki: matches() maybe I'll make that change in my code. For the wildcard you can use ".*functions.*", so the keyword can be anywhere in the macro name. I also thought about having a trigger in the macro name, rather than group, i.e. funcGetRaceName and funcGetClassName, then have them defined by striping out the keyword "func" and name them as saw.fw.GetRaceName and saw.fw.GetClassName. I had ultimately made the choice to use group names, since it makes a lot of sense to use that to categorize the macros in your code, but that is just my preference, not to mention it was easy to do it this way. (And like I said, a bit lazy at times, so you can easily open one macro, edit the group name, then drag your other macros to it. Of course you have to remember to delete your macros from the original source then. Is there any way to drag/drop a macro name as a move rather than a copy/paste kind of function?) I suppose you could look for any macro containing [h: macro.return] in the code as well, or maybe a code block header comment like <!-- defineFunction -->, or perhaps a variable declaration [h: defineAsFunction=1]. Just shows there are many approaches that can work for this.

Post Reply

Return to “Macros”