Help with NPE Error (fixed)

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
Esheon
Giant
Posts: 115
Joined: Mon Dec 26, 2011 12:53 am

Help with NPE Error (fixed)

Post by Esheon »

Once again, I'm in over my head trying to adapt code to use in my own simple framework. In this case, I'm ripping apart Rumble Slim 2.0's PowerMaker/MacroBuilder macros and rearranging it to fit my needs. The code itself is essentially Rumble's code with sections pulled out and/or rearranged, so those familiar with the code will notice a lot of, um... plagiarism, to be completely honest. Many thanks to Rumble for giving me a starting point and some good ideas. :)

What I'm trying to end up with is a macro button on the token that just contains the variables needed to define the power. When clicked, the macro passes those variables to an attack macro on the Lib token, which opens a dialog window for number of targets, conditional bonuses, etc. I'm trying to do this without using Lib properties to store the power variables, so one could take that token and drop it in another campaign without having to worry about calling on non-existent props. The only Lib property in use is the base "blank power" template that is initially called in the input macro.
Empty Power Template

Code: Select all

{"pname":"Power Name", "pflavor":"--none--","usage":"at-will","recharge":"","action":"standard","rangetype":"melee","isAOE":0,"targType":"single creature","rangeText":"melee weapon","attBonus":"StrBonus","defense":"AC","woi":0,"slot":"main","dmgDice":"1MHW+Str","critDice":0,"dmgType":"untyped","trigger":"--none--","hit":"--none--","miss":"--none--","effect":"--none--","sustain":"--none--","special":"--none--", "mto":0, "ppCost":0}
I don't intend to detect hits/misses, apply damage, set states, or indeed do any automation except rolling dice for attacks and damage, including modifiers to be set permanently in power creation or temporarily in the attack dialog. In fact, those functions are mostly what I've removed from Rumble's code. (I may end up including target selection in the attack dialog, I haven't decided yet.)

The macro sequence runs as follows:
1.) Player clicks New Attack Power on the token.
2.) "New Attack Power calls the UDF esh.inputAttackPower, which opens an input dialog to specify the power.
3.) Player fills out the dialog and clicks OK
4.) esh.inputAttackPower creates a JSON, which it passes to the UDF esh.buildAttackMacro
esh.inputAttackPower output

Code: Select all

[h:macroBuilderInfo = json.set(template, "pname", pname, "pflavor", flavor, "usage", usage, "recharge", recharge, "action", action, "rangetype", rangetype, "isAOE", numtargets, "targType", targType, "rangeText", rangeText, "attBonus", attBonus, "defense", defense, "woi", woi, "slot", slot, "dmgDice", dmgDice, "critDice", critDice, "dmgType", dmgType, "trigger", trigger, "hit", hit, "miss", miss, "effect", effect, "sustain", sustain, "special", special, "mto", mto, "ppCost", ppCost)]

[h:esh.buildAttackMacro(macroBuilderInfo)]
5.) esh.buildAttackMacro pulls that data out and organizes it, creating the button specifics through esh.getLabel (just rfw.getLabel with a few bits removed). The data is put into a series of strings, which are then combined into one final string that will serve as the argument for the createMacro command. Unfortunately, there is where I hit a snag. esh.buildAttackMacro makes it all the way down to that "combine everything" step, and something goes wrong.
Error Message

Code: Select all

java.lang.NullPointerException error executing expression h,finalCmd = strformat('%{cmdvars}%{n}%{n}%{cmdJSONbuilder}%{n}%{n}%{cmdpdata}%{n}%{n}%{cmdargbuilder}%{n}%{cmdAttackDialog}').
Now, from what I understand (which isn't much), a Null Pointer Exception means that I set a variable but didn't link it to a proper value. The code itself looks ok, but I'm obviously missing something.
esh.buildAttackMacro (full code)

Code: Select all

[h:info = arg(0)]

<!-- check that the incoming data is just one object -->
[h,if(argCount() != 1): assert(0, "MacroBuilder requires exactly one argument", 0); jsonObject = arg(0)]

<!-- Convert the JSON object to a series of variables & assign values to those variables -->
[h:varList=json.fields(jsonObject)]

[h,foreach(var,varList),CODE:
{
     [value = json.get(jsonObject,var)]
     [set(var,value)]
}]

<!-- Create a JSON object to define macro button label/group/sort -->
[h:label = esh.getLabel(usage, rangetype, pname, action, recharge, trigger)]
[h:props = macro.return]
[h,switch(lower(usage)):
case "at-will": pcolor = "green";
case "encounter": pcolor="red";
case "daily": pcolor="black";
case "recharge": pcolor = "blue"]

[h:fcolor="white"]
[h:n = decode("%0A")]

<!-- Create a string to define the macro button tooltip -->
[h:"Generating tooltip/card"]
[h,if(trigger == "--none--"): trigPart = ""; trigPart = strformat("<b>Trigger</b>: %{trigger}<br>")]
[h,if(hit == "--none--"): hitPart = strformat("<b>Hit</b>: %{dmgDice} %{dmgType} damage<br>"); hitPart = strformat("<b>Hit</b>: %{dmgDice} %{dmgType} damage; %{hit}<br>")]
[h,if(miss == "--none--"): missPart = ""; missPart = strformat("<b>Miss</b>: %{miss}<br>")]
[h,if(effect == "--none--"): effectPart = ""; effectPart = strformat("<b>Effect</b>: %{effect}<br>")]
[attPart = strformat("<b>Attack</b>: %{attBonus} vs. %{defense} (%{rangeText})<br>")]
[targPart = strformat("<b>Target</b>: %{targType}<br>")]

[h,if(sustain == "--none--"): sustPart = ""; sustPart = strformat("<b>Sustain</b>: %{sustain}<br>")]
[h,if(special == "--none--"): specPart = ""; specPart = strformat("<b>Special</b>: %{special}")]

[h:pct2 = strformat("%{trigPart}%{attPart}%{targPart}%{hitPart}%{missPart}%{effectPart}%{sustPart}%{specPart}")]

<!-- Create a string to define the power variables -->
[h:cmdvars = strformat('[h:pname = "%{pname}"]%{n}[h:usage="%{usage}"]%{n}[h:recharge="%{recharge}"]%{n}[h:action="%{action}"]%{n}[h:rangetype="%{rangetype}"]%{n}[h:isAOE=%{isAOE}]%{n}[h:targType="%{targType}"]%{n}[h:rangeText="%{rangeText}"]%{n}[h:attBonus="%{attBonus}"]%{n}[h:defense="%{defense}"]%{n}[h:woi=%{woi}]%{n}[h:slot="%{slot}"]%{n}[h:dmgDice="%{dmgDice}"]%{n}[h:critDice="%{critDice}"]%{n}[h:dmgType="%{dmgType}"]%{n}[h:trigger="%{trigger}"]%{n}[h:hit="%{hit}"]%{n}[h:miss="%{miss}"]%{n}[h:effect="%{effect}"]%{n}[h:sustain="%{sustain}"]%{n}[h:special="%{special}"]%{n}[h:mto=%{mto}]%{n}[h:ppCost=%{ppCost}]%{n}%{n}[h:pcolor="%{pcolor}"]%{n}[h:fcolor="%{fcolor}"]%{n}%{n}[h,if(dmgType != "untyped"):dtype = dmgType; dtype = ""]<!--Display cleanup so it does not say "damage damage"-->%{n}%{n}[h:pflavor="%{pflavor}"]%{n}[h:pct2="%{pct2}"]%{n}')]

<!-- Stick the power variables in a JSON -->
[h:cmdJSONbuilder = strformat('[h:powerData = json.set("{}", "pname", pname, "pflavor", pflavor, "usage", usage, "recharge", recharge, "action", action, "rangetype", rangetype, "isAOE", isAOE, "targType", targType, "rangeText", rangeText, "attBonus", attBonus, "defense", defense, "woi", woi, "slot", slot, "dmgDice", dmgDice, "critDice", critDice, "dmgType", dmgType, "trigger", trigger, "hit", hit, "miss", miss, "effect", effect, "sustain", sustain, "special", special, "mto", mto, "ppCost", ppCost)]')]

<!-- Define more tooltip data -->
[h:pct = strformat("<table width=300 bgcolor=white><tr bgcolor=%{pcolor} color=%{fcolor}><td>%{pname} (%{action})</td></tr><tr><td><b>Trigger</b>: %{trigger}<br><b>Attack</b>: %{attBonus} vs. %{defense} (%{rangeText})<br><b>Hit</b>: %{dmgDice} %{dmgType} damage; %{hit}<br><b>Miss</b>: %{miss}<br><b>Effect</b>: %{effect}<br><b>Special</b>: %{special}</td></tr></table>")]

<!-- define a Power Card (to be linked from the chat display... theoretically) -->
[h:cmdpdata = '[h:pCardTable= strformat("<table width=400 bgcolor=white><tr bgcolor=%{pcolor} color=%{fcolor}><td><h4>%{pname} (%{action})</h4></td></tr><tr><td>%{pct2}</td></tr></table>")]']

<!-- define macro args for opening attack dialog -->
[h:cmdargbuilder = strformat('[h:token=getName()]%{n}[h:dialogArgs = json.append("[]", token, pcardtable, getMacroName(), powerData)]')]

<!-- define which macro to call when power is used -->
[h:cmdAttackDialog=strformat('%{n}{[macro("esh.showAttackDialog@Lib:MacroControl"):dialogArgs]%{n}}]')]

<!-- combine all of the above into the command parameter for createMacro ERROR HERE? -->
[h,finalCmd = strformat('%{cmdvars}%{n}%{n}%{cmdJSONbuilder}%{n}%{n}%{cmdpdata}%{n}%{n}%{cmdargbuilder}%{n}%{cmdAttackDialog}')]

<!-- check for existing macro with this label & delete if present -->
[h,if(hasMacro(label)),CODE:
{
    [h:indexes = getMacroIndexes(label)]
    [foreach(index, indexes): removeMacro(index)]
}]

<!-- set tooltip for createMacro -->
[h:tooltip = strformat("<html>%{pct}</html>")]
[h:tooltip2 = strformat("<html><p width=300>%{pct2}</p></html>")]]
[h:props = json.set(props, "tooltip", tooltip2)]

<!-- create macro using all of the above -->
[h:createMacro(label, finalCmd, props)]
Last edited by Esheon on Sun Feb 26, 2012 1:28 am, edited 1 time in total.

User avatar
jfrazierjr
Deity
Posts: 5176
Joined: Tue Sep 11, 2007 7:31 pm

Re: Help with NullPointerException Error in Power Creation M

Post by jfrazierjr »

Just like any other problem with multi steps, break this down into chunks. Localize it until you get down to the smallest part that will fail.

Also, if you are getting a null pointer exception, there will be ALOT more to the stack trace, so we need to see that information also. This almost always means we failed to account for some scenario in the code for something that might go wrong(ie, not your fault, but your exact series of steps revealed something that was already a potential bug)
I save all my Campaign Files to DropBox. Not only can I access a campaign file from pretty much any OS that will run Maptool(Win,OSX, linux), but each file is versioned, so if something goes crazy wild, I can always roll back to a previous version of the same file.

Get your Dropbox 2GB via my referral link, and as a bonus, I get an extra 250 MB of space. Even if you don't don't use my link, I still enthusiastically recommend Dropbox..

User avatar
Rumble
Deity
Posts: 6235
Joined: Tue Jul 01, 2008 7:48 pm

Re: Help with NullPointerException Error in Power Creation M

Post by Rumble »

As you say, this is a macro NPE somewhere, which probably is stemming from an uninitialized variable OR from a misplaced comma/parenthesis/other problem. Like jfrazierjr says, you need to isolate it. I would liberally salt debug pauses into the macro builder - before each major step - and see which one fails. Then you can backtrack whether it's a variable or a comma error or whatever.

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: Help with NullPointerException Error in Power Creation M

Post by Azhrei »

I've committed a patch to SVN (revision 5849) which leaves the original %{ } format intact when an NPE occurs. In the future, an incorrect variable name will remain as a format in the output string which should make it much easier to detect. ;)

Esheon
Giant
Posts: 115
Joined: Mon Dec 26, 2011 12:53 am

Re: Help with NullPointerException Error in Power Creation M

Post by Esheon »

I are dumb.

h:finalCmd instead of h,finalCmd got me past that error... Now I'm getting another error, but at least I'm stuck on something else :P


EDIT: It took some more editing, but now it's creating the macro with exactly the info I want in it. Time to move on to the attack dialog :)

Thanks for the help... A combination of Rumble's debugging trick, aliasmask's Notepad++ plugin, and this thread's tips on what to look for got me looking in the right places.

User avatar
Azhrei
Site Admin
Posts: 12086
Joined: Mon Jun 12, 2006 1:20 pm
Location: Tampa, FL

Re: Help with NPE Error (fixed)

Post by Azhrei »

And now we just need some way to preserve that newfound knowledge!

Maybe a section on the wiki that shows not just how to correctly write macros, but also introduces errors and shows how to fix them!

In my training classes I generally find that people learn more from mistakes than from something that works. So my hands-on exercises typically have the students try things that I know won't work, but I want them to see the error message(s) so that when they see them again back at their desk they aren't completely flabbergasted!

Post Reply

Return to “Macros”