XML question

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
xavram
Dragon
Posts: 891
Joined: Tue Apr 20, 2010 8:22 pm

XML question

Post by xavram »

My re-do of the character sheets that my players use is almost completed. A quick shout out to the various folks who have helped me with some of the issues I've had, from caching XML to dynamic variables...really helped!

I have the sheet set up so that when you click on a token, bang, all the info loads. What I'm hoping to add is if you click OFF a token, then the sheet clears itself. I have all the player controls for attacks, saves, skills, spells, etc, etc, as buttons on the form but they won't work if you click on a button while your token isn't selected....and I know my players, I'm going to get a lot of...

"Why isn't the button working, I'm clicking on it?"
"Do you have your token selected?"
"Yes...er, not sure...oh wait, I'm on the distance tool, sorry!"

So to avoid that, I wanted to have the XML form clear (or maybe just have a "No token selected" line at the top) when a player clicks off the token. Is there any way to achieve this? Here's my code currently for displaying the character sheet, using the onSelectionChanged (really wish that would only fire once, any way to prevent that?)

Right now, I have the code aborting if you don't select anything.

Code: Select all

[h, if(getSelected() == "") : abort(0)]
[h : allSelected = getSelected()]
[h, if(listCount(allSelected) > 1) : abort(0)]
[h : switchToken(getSelected())]
[h, if(getPropertyType() != "Pathfinder Player") : abort(0)]
[h: link = macroLinkText("NewStatBlock@Lib:PC_CombatMacros", "none", "", getSelected())]
[frame("New Stat Block"):
{
<html>
<head>
<link rel="onChangeSelection" type="macro" href="[r:link]">
<link rel='stylesheet' type='text/css' href='StyleSheet@Lib:PC_CombatMacros'></link>
<title>New Stat Block</title>
</head>
<body>
[h: link = macroLinkText("Actions@Lib:PC_CombatMacros",  "all", "", "selected")]
<form action="[r:link]" method="json">
<div class="statBlock">
[h : usedTopXml = TopXml]
[h : usedTopXml = replace(usedTopXml, "MELEEREPLACE", MeleeXml)]
[h : usedTopXml = replace(usedTopXml, "RANGEDREPLACE", RangedXml)]
[r : usedTopXml]

[r, SWITCH(BottomXmlId):
case "1": FSXml;
case "2": SpecialXml;
case "3": OtherXml;
case "4": DailiesXml;
case "5": InventoryXml]

</form>
</div>
</body>
</html>}
]

User avatar
aliasmask
RPTools Team
Posts: 9031
Joined: Tue Nov 10, 2009 6:11 pm
Location: California

Re: XML question

Post by aliasmask »

Here is some code I use to demonstrate selecting and unselecting tokens. It does require each player to have a lib token named after them "lib:player_"+lower(getPlayerName()).

This code gets rid of the multiple select events and only runs once for each select. It also identifies when a selected token is selected again and displays as a "double click" for additional functionality, but that's only when there are no other selection changes. For example, if you have 3 tokens selected and then just click one of the selected tokens deselecting the other two. That won't show up as a double click. Click the single token again and it does.

To keep it from running the select event multiple times each time the frame is redrawn it creates an event id and saves it to the player lib token. The onSelectToken macro checks for that id and when duplicated ignores the event. It's because I include the event id in the onChangeSelection call when building the frame.

Code: Select all

@@ @onCampaignLoad
<!-- All functions will be given a UDF name excluding tags and special characters

It is recommended to change the prefix to avoid conflict with other lib tokens. 
   Format is your initials (xx) and a short code (lib) to represent the token.
   Functions can then be called by this format: xx.lib.functionName() 
-->

[H: prefix = "am.xx."]
[H: this = getMacroLocation()]

<!-- Define functions here with options other than ignoreOutput = 1 and NewScope = 1 -->
<!-- Example. Feel free to delete -->
[R: defineFunction(prefix+"reload","onCampaignLoad@"+this,0,1)]
[R: defineFunction(prefix+"lifelink.process","lifelink.process@"+this,1,0)]
[R: defineFunction(prefix+"saveSettings","saveSettings@"+this,1,0)]

<!-- default settings for functions. No output and parent variables not accessed without passing -->
[H: allMacros = getMacros("json")]
[H: ignoreOutput = 1]
[H: newScope = 1]

<!-- Define UDFs.  Already defined UDFs will not be redefined. -->
[R, foreach(macroName,allMacros), code: {
   <!-- get rid of any html in macro name -->
   [H: functionName = replace(macroName,"<[^>]*?>","")]
   <!-- remove special characters from macro name. Only numbers, letters, underscore and period allowed -->
   [H: functionName = replace(functionName,"[^a-zA-Z0-9_.]","")]
   <!-- add the prefix for the final function name -->
   [H: functionName = strformat("%{prefix}%{functionName}")]
   [R, if(! isFunctionDefined(functionName)): defineFunction(functionName,strformat("%{macroName}@"+this),ignoreOutput,newScope)]
}]

!!
@@ @openSelectFrame
[H: eventId = d100000000]
[H: frameName = "Player Selection: "+getPlayerName()]
[H, if(! isFrameVisible(frameName)), code: {
   [H: deselectTokens(getSelected())]
   [H: am.xx.setPlayerProperty("selectedTokenList","[]")]
};{}]
[frame(frameName): {
<html>
<head>
   <link rel="onChangeSelection" type="macro" href="[r: macroLinkText('onSelectToken@'+getMacroLocation(), 'none',eventId)]">
</head>
<body>
Please leave this frame open,
the on-Map-buttons wont work
otherwise!
</body>
</html>
}]

!!
@@ @onSelectToken
[H: eventId = macro.args]
[H: lastEventId = am.xx.getPlayerProperty("selectEventId")]
[H: matchingEvent = if(eventId == lastEventId,1,0)]
[H, if(matchingEvent): abort(0)]
[H: selectedTokens = getSelected("json")]
[H, if(json.isEmpty(selectedTokens)): selectedTokens = "[]"]
[H, if(json.type(selectedTokens) == "UNKNOWN"): selectedTokens = json.fromList(selectedTokens)]
[H: selectedList = am.xx.getPlayerProperty("selectedTokenList")]
[H, if(json.isEmpty(selectedList)): selectedList = "[]"]
[H: selectChange = json.difference(selectedTokens,selectedList)]
[H: deselectChange = json.difference(selectedList,selectedTokens)]
[H: eventProcessed = json.equals(selectedTokens,selectedList)]
[H: singleSelect = if(json.length(selectedTokens) == 1,1,0)]
[H, if(! eventProcessed), code: {
   [H: am.xx.setPlayerProperty("selectedTokenList",selectedTokens)]
   [H, if(! json.isEmpty(deselectChange)), code: {
      [H: link = macroLinkText("multiDeselectChange@"+getMacroLocation(),"none",deselectChange)]
      [H: execLink(link,1)]
   };{}]
   [H, if(! json.isEmpty(selectChange)), code: {
      [H: link = macroLinkText("multiSelectChange@"+getMacroLocation(),"none",selectChange)]
      [H: execLink(link,1)]
   };{}]
   [H: am.xx.deferFunction("am.xx.openSelectFrame")]
   [H: am.xx.setPlayerProperty("selectEventId",eventId)]
};{
   [H, if(singleSelect), code: {
      [H: broadcast(strformat("<b>Double Click: </b>%s",getName(json.get(selectedTokens,0))))]
      [H: am.xx.deferFunction("am.xx.openSelectFrame")]
      [H: am.xx.setPlayerProperty("selectEventId",eventId)]
   };{}]
}]

!!
@@ @setPlayerProperty
[H: currentPlayer = lower(getPlayerName())]
[H, if(argCount() >= 3): playerName = lower(arg(2));playerName = currentPlayer]
[H, if(playerName != currentPlayer): am.xx.autoExecute(macroLinkText("setPlayerProperty@"+getMacroLocation(),"none",macro.args),playerName);
   setLibProperty(arg(0),arg(1),"lib:player_"+playerName)]

!!
@@ @getPlayerProperty
[H, if(argCount() >= 2): playerLibName = "lib:player_"+lower(arg(1));playerLibName = "lib:player_"+lower(getPlayerName())]
[H: macro.return = getLibProperty(arg(0),playerLibName)]

!!
@@ @autoExecute
[H: link = arg(0)]
[H: runAsPlayer = arg(1)]
[H: player = getPlayerName()]
[H, if(runAsPlayer == player): execLink(link);broadcast(strformat('<i>"%{player}" sent data to "%{runAsPlayer}."</i><a href="%{link}"></a>'),json.append("",runAsPlayer,player),"json")]

!!
@@ @deferFunction
[H: function = arg(0)]
[H, if(argCount() >= 2), code: {
   [H: args = arg(1)]
   [H, if(json.type(args) != "ARRAY"): args = json.append("",args)]
};{
   [H: args = ""]
}]
[H, if(currentToken() == ""): target = getMacroLocation();target = currentToken()]
[H: deferArgs = json.append("",function,args)]
[H: link = macroLinkText("execFunction@"+getMacroLocation(),"none",deferArgs,target)]
[H: execLink(link,1)]

!!
@@ @execFunction
[H: function = arg(0)]
[H: args = arg(1)]
[H: argList = ""]
[H, foreach(arg,args), code: {
   [H: argList = listAppend(argList,"arg"+roll.count)]
   [H: set("arg"+roll.count,arg)]
}]
[H: fnStr = strformat("%{function}(%{argList})")]
[H,C(500):"Delay one half second."]
[H: eval(fnStr)]

!!
@@ @multiDeselectChange
[H: deselectedTokens = macro.args]
[H: broadcast(strformat("<b>Deselect Group:</b><div style=margin-left:5px>%s</div>",json.toList(deselectedTokens,"<br>")))]
[H, foreach(deselectedToken,deselectedTokens), code: {
   [H: broadcast(strformat("<b>Token Deselected: </b>%s",getName(deselectedToken)))]
}]

!!
@@ @multiSelectChange
[H: selectedTokens = macro.args]
[H: broadcast(strformat("<b>Select Group Selected:</b><div style=margin-left:5px>%s</div>",json.toList(selectedTokens,"<br>")))]
[H, foreach(selectedToken,selectedTokens), code: {
   [H: broadcast(strformat("<b>Token Selected: </b>%s",getName(selectedToken)))]
}]

!!
keyword: amsave onSelectToken
Attachments
onSelectToken DEMO 1-24-15.cmpgn
(66.76 KiB) Downloaded 25 times

xavram
Dragon
Posts: 891
Joined: Tue Apr 20, 2010 8:22 pm

Re: XML question

Post by xavram »

Skinned the cat a little differently, seems to work...

At the end of the "display character sheet" macro, I call...

Code: Select all

[h: setLibProperty("lastTokenClicked", json.set(getLibProperty("lastTokenClicked"), getPlayerName(), getSelected()))]
And then at the very beginning of the "display character sheet", I added...

Code: Select all

[h : lastSelection = json.get(getLibProperty("lastTokenClicked"), getPlayerName())]
[h, if(lastSelection == getSelected()) : abort(0)]
So while the macro does get called twice on a token switch, it aborts before all the heavy lifting if the selected token doesn't actually switch.

User avatar
aliasmask
RPTools Team
Posts: 9031
Joined: Tue Nov 10, 2009 6:11 pm
Location: California

Re: XML question

Post by aliasmask »

That will definitely work fine for a single user but for multiple users I can foresee a problem. What if another user clicks a token and then you click the same token. You won't get the display because it'll think it will already have been selected. Using your method you could have each player have their own unique variable to write to the lib token, perhaps based on the user name but you would have to filter the name to make it a valid property name. Then the only problem you'll run in to is an occasional double selection because when one person writes to the lib token at the same time, one persons data is ignored and the other person will have the duplication. But that would likely be a very insignificant and livable error in this case since running it twice doesn't really affect performance.

Oh, I do see another issue. The token can't be refreshed in the frame until you select a different token and then go back to token you want to see.

xavram
Dragon
Posts: 891
Joined: Tue Apr 20, 2010 8:22 pm

Re: XML question

Post by xavram »

at least in my tests, should work fine.

Because when one player click a token and a different player clicks the same token (assuming of course that they both have ownership), the property stores each player's selection individually. If its NOT different than the previous selection, the form doesn't need to refresh anyway (the sheets don't Auto Refresh on just a selection, you actually have to DO something to make it refresh).

And yes, if the double selection causes one person to NOT write to the library property, no biggie, you get the "double selection".

Anyway, it seems to work the way I'm expecting when I run a server as a DM and then connect two other players.

Heh, we'll see, when it actually gets used...:)

Post Reply

Return to “Macros”