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)))]
}]
!!