Problem with UDF

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

Problem with UDF

Post by koralas »

OK, so I have UDF that just doesn't want to work... not sure why, I am sure I'm missing something easy, but can't figure it out...

When trying to run the function [r: getabilitymodifier(18)] I get this-
java.lang.NullPointerException error executing expression getabilitymodifier(18).

Ok, so I thought, lets put quotes around the data being passed, can't hurt, right? I get this-
java.lang.NullPointerException error executing expression getabilitymodifier("18").

So next I want to make sure I am defined, and to my surprise I, when I run [r: isFunctionDefined("getAbilityModifier")] it returns- 2
Now, isn't that what is supposed to be returned if it is a built-in MapTool function? So I change the name of the function, and reload the campaign, get the same thing...

If I manually run the macro [macro("get_AbilityModifier@Lib:Charts"):"18"] [r: macro.return] I get what I expect to see, a result of 3.

I have a second function with the same issue, both of these are passing numeric arguments. A third that I have defined works fine, but it is passing a JSON string. What am I doing wrong here?

Here is the code of the macro:

Code: Select all

[h: abilityScore=macro.args]

[h: abilMod=json.get(getLibProperty("abilityModifiers","Lib:Charts"), abilityScore)]

[macro.return=abilMod]
Note with this I also have tried [h: abilityScore=number(macro.args)] to no avail.


An here is the onCampaignLoad where it is defined:

Code: Select all

[h: defineFunction("getAbilityModifier", "get_AbilityModifier@Lib:Charts")]

MikeKozar
Cave Troll
Posts: 73
Joined: Sat Sep 12, 2009 6:35 pm

Re: Problem with UDF

Post by MikeKozar »

I don't see anything wrong with your code at first glance, but wouldn't a table be an easier way to get the modifier you need? It should be as simple as

[bonus=table("Abilities",18)]

...with no need to define functions or try to figure out JSON objects.

Either way, best of luck!

Wiki: table()

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Problem with UDF

Post by biodude »

I believe functions are case-sensitive (unlike many other aspects of the Macro script language). Therefore

Code: Select all

[r: getabilitymodifier(18)]
will return an error if you defined the function as "getAbilityModifier". Granted, I would expect the error to be "function is not defined", but MapTool sometimes spits out weird error messages (as many such languages do).
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

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

Re: Problem with UDF

Post by koralas »

biodude wrote:I believe functions are case-sensitive (unlike many other aspects of the Macro script language). Therefore

Code: Select all

[r: getabilitymodifier(18)]
will return an error if you defined the function as "getAbilityModifier". Granted, I would expect the error to be "function is not defined", but MapTool sometimes spits out weird error messages (as many such languages do).
Thanks, this was it, well to a point. And I didn't catch it, something simple, you know. And...to...much...time...coding!!! I knew there was a reason I didn't become a programmer and went networking instead :)

So issue #2, and I found it myself. #pats self on back# I was having an issue where when using the function, it would return a null value, but calling the macro manually was fine. So I started plugging displays in the code and noted that the macro.args was showing as [18]... aha! An array, so, a little change to line one [h: abilScore=json.get(macro.args,0)] solved that problem. So, are all parms passed through a function converted to a JSON array? Explains my other issue, and why the JSON parms I was passing was not an issue.

BTW, I am using 1.3b70.

User avatar
plothos
Great Wyrm
Posts: 1890
Joined: Sat Jun 21, 2008 1:07 am

Re: Problem with UDF

Post by plothos »

Heh, yeah, that was going to be my suggestion after looking at your code, though I didn't think it'd produce an error message like you saw. :)

Note the arg() function is (I think) sorta the standard when dealing with UDFs.
Drop-In Utilities:
My Spell Manager for D&D3.5 and PFRPG
My Inventory Manager for D&D and PFRPG, but more generally useable than that.
My Message Manager -- My Top-Down D&D Token Images
and my Custom Initiative & Status/Spell-Effect Tracker (work in progress, but functional).

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

Re: Problem with UDF

Post by koralas »

MikeKozar wrote:I don't see anything wrong with your code at first glance, but wouldn't a table be an easier way to get the modifier you need? It should be as simple as

[bonus=table("Abilities",18)]

...with no need to define functions or try to figure out JSON objects.

Either way, best of luck!

Wiki: table()
Yeah, easier to a point, but I am looking at portability, and ease of upgrade in the future. Now if there was a way to modify tables from a macro, I'd be all over it. I know you can export the tables, unzip them, and edit them outside of the tool, but it is a pain from what I have seen so far of it. Unfortunately, looking at some, like Plothos spell manager, row 0 is to large to edit with XMLPad, and to cumbersome to edit with OpenOffice.Org Calc.

I have been using OOO to make template that I can fill out and then generate JSON data sets from. And I suppose I could then parse that and put it in the same format as the tables, but that is to much like work, when I can do this, and create functions to grab that data. Though, if tables could be modified from a macro, that would be another story.

For example, my class definitions are broken down to two different sets of data. One is the Class Ability Def's, the other is the Class Def. The Class Def has a header with information such as Class Name, Prime Attribute, HD type, etc. and some arrays for things like an XP chart, permitted weapons, permitted armor, and the list of class abilities. Note this last actually work like a relational db, in that the list is just the key field to pull information from the Class Abilities Def. There are cases where abilities overlap between classes, so they only need be entered once. Though for the C&C framework I am working on, I have duplicated them between the classes, and only use the references when setting up the multi-class options.

Now, each of the classes (or multi-classes in the way C&C handles them) have their own ClassDef macro. For example, the fighter class has a macro called "fighterClassDef", and the rogue, "rogueClassDef". This contains all the pertinent information to define the class. There is also another macro called "fighterClassAbilitiesDef", and one called "rogueClassAbilitiesDef". I then created two macro's "addClasses" and "addClassAbilities", these macros basically search for all macro's ending in either "ClassDef" or "ClassAbilitiesDef", and creates one JSON data set for each, and stores that in a property on the token. This way, if you want to add another class, you simply need to add the appropriate ClassDef and ClassAbilitiesDef macro's, then run the addClasses and addClassAbilities to recreate those JSON data sets. It also allows you (in the C&C method) to add a new "multi-class", by simply creating the ClassDef macro, and in the abilities list of that data object, referencing the abilities from the parent classes. You then only need to run the addClasses macro to have it be available as a new class. For example, the Thug, a Fighter/Rogue multi-class (using the C&C Class and a Half rules) would only need a new thugClassDef macro, since all of it's class abilities can be referenced from the existing abilities, like "fighterCombatDominance" or "rogueMoveSilently". You could also add a new class like "Sailor" which may have some of it's own special abilities like "Navigate", but also an ability like "Climb", well you would need to create a new class abilities def macro "sailorClassAbilities" and add "sailorNavigate" to it, but in the "sailorClassDef" macro, you can, in the class abilities list, reference both "sailorNavigate" and "rangerClimb". Why rangerClimb and not rogueClimb, well because rogueClimb includes scaling sheer surfaces like walls, where rangerClimb is not that, but ropes, trees, etc. You could, of course, make a new sailorClimb ability and define it more strictly to ropes and poles if you so desired.

My thought process for the way I am designing the framework is to have a large number of Library Tokens, with each having a smaller number of macros. Basically creating a Library Token for each different type of function, though some will be named similarly and really work as a group, so you can replace the tokens for bug fixes, or added functionality, and impact the smallest common component.

For example, my next generation character sheet will actually be it's own token (well it is now), but some of the pages of the sheet will be written to from other tokens. That is, I will have a Lib:Inventory token, and it will be responsible for managing the equipment a character possesses, and also filling in the inventory section of the character sheet. A separate but related token would be Lib:Encumbrance, which is obviously related to the former library, but has it's own set of functions, pulling data from the former to make those calculations. That way, if we change the encumbrance rules, only that token need be updated, and all of the components will be easy to find.

Now, since I am still learning this macro language, it's extents and limitations, my code isn't necessarily very pretty, nor at this point very large. Maybe a couple thousand lines, not including the data sets. Once I have something workable to start a campaign with, I will spend time rewriting to standardize my code, and to make sure the modular approach fits well. I know I will be freely plagiarizing from other peoples code, especially at the beginning, heck in many ways that is how I learn best! Now this won't be whole sale stealing of code, but most likely snippets, and lots of learning from reading it. And this forum has been invaluable so far...

Wow did this get long, must have needed a break... time to get back to coding!

User avatar
biodude
Dragon
Posts: 444
Joined: Sun Jun 15, 2008 2:40 pm
Location: Montréal, QC

Re: Problem with UDF

Post by biodude »

plothos wrote:Heh, yeah, that was going to be my suggestion after looking at your code, though I didn't think it'd produce an error message like you saw. :)

Note the arg() function is (I think) sorta the standard when dealing with UDFs.
Good eye - I missed that one. Yes, All arguments passed to a function are automatically wrapped into a json array (even if it's only 1 argument).
Wiki: arg() is a convenient way of fetching a particular argument passed in to a macro that was called as a UDF, although as far as I know, there is no discernible difference between calling a macro as a UDF, or passing all arguments wrapped up into a json array within a macro() call. Thus, the following statements can be treated identically within the called macro:

Code: Select all

[H: myFunction( args ) ]
[H, MACRO( "myFunctionMacro@this" ): json.append( "" , args )]
Similarly, in the called function, the following two statements are also identical (IIRC):

Code: Select all

[H: arg0 = arg(0) ]
[H: arg0 = json.get( macro.args, 0 )]
I actually rarely use Wiki: arg() anymore, as I automatically pass macro.args to a local variable before processing it, and just extract all the arguments using Wiki: json.get() (after checking that the arguments are, in fact, a json array, and converting them if not). For me, it's standard practice, no matter how many arguments the function is expecting: it's just easier and works in any situation. I use UDFs a lot, but I try not to rely on them being called as such when processing arguments.
"The trouble with communicating is believing you have achieved it"
[ d20 StatBlock Importer ] [ Batch Edit Macros ] [ Canned Speech UI ] [ Lib: Math ]

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

Re: Problem with UDF

Post by aliasmask »

biodude wrote:
plothos wrote:Heh, yeah, that was going to be my suggestion after looking at your code, though I didn't think it'd produce an error message like you saw. :)

Note the arg() function is (I think) sorta the standard when dealing with UDFs.
Good eye - I missed that one. Yes, All arguments passed to a function are automatically wrapped into a json array (even if it's only 1 argument).
Wiki: arg() is a convenient way of fetching a particular argument passed in to a macro that was called as a UDF, although as far as I know, there is no discernible difference between calling a macro as a UDF, or passing all arguments wrapped up into a json array within a macro() call. Thus, the following statements can be treated identically within the called macro:

Code: Select all

[H: myFunction( args ) ]
[H, MACRO( "myFunctionMacro@this" ): json.append( "" , args )]
Similarly, in the called function, the following two statements are also identical (IIRC):

Code: Select all

[H: arg0 = arg(0) ]
[H: arg0 = json.get( macro.args, 0 )]
I actually rarely use Wiki: arg() anymore, as I automatically pass macro.args to a local variable before processing it, and just extract all the arguments using Wiki: json.get() (after checking that the arguments are, in fact, a json array, and converting them if not). For me, it's standard practice, no matter how many arguments the function is expecting: it's just easier and works in any situation. I use UDFs a lot, but I try not to rely on them being called as such when processing arguments.
What he said. I was going to point out the macro calling thing as well. But I think arg is only created for UDFs. I remember trying to use them with a "macro()" call but I got errors. But I was knew to the macroscript back then and I don't think I tried passing a json array.

If I ever need to pass more than 3 arguments, I usually just pass an object... usually. I also use UDF fairly exclusively and I follow the format I've seen CoveredInFishes does with putting prefixes on the function names. (he uses cif.xx.function() where cif is his initials, xx are the lib tokens initials). I plan to update MacroIO to find all the defined functions and do all the prefixing automatically when that option is used as well as create the onCampaignLoad (sort of) to define all the functions. I posted the details in another thread under MacroIO thread.

Post Reply

Return to “Macros”