Updating Macro Buttons Using a Macro (Group Method)
Updating color of our macro buttons using DnD4e as an example
For an example I will use DnD4e powers; when a power is used it will then update the button to show this. Although this example is for DnD4e the technique is applicable for many other systems.
Lets assume that you have macro groups named "Daily Powers" and "Encounter Powers" where we place all of our powers, and we want to set the color of the macro button to blue if the power has been used. If you want another way to do this without using different groups see Tracking Used DnD 4e Powers (Macro Prefix Method)
Version update: This tutorial was written before 1.3b50. As of 1.3b50 you can use getMacroButtonIndex to determine the index of the button that was pressed. Instead of setting/getting the properties of the button by name you can use the index of the button in place of the name, this way you will not run the risk of updating buttons with the same name.
Short Rest, resetting color of buttons in the "Encounter Powers" group
So first we will create a macro button called "Short Rest", for this tutorial I will assume you just create it as a campaign macro --just remember to check the Apply to Selected Tokens check mark-- you can just as easily create these as macros in library token macros and call them from from macro buttons on your tokens.
So create the "Short Rest" macro button and copy the following code into it
[abort(hasImpersonated())] <!-- Abort macro if no token is impersonated -->
[h,foreach(macro, getMacros()), code: {
<!--
== each label can appear more than once (i.e. more than one
== button with same label, so we need to get all the button
== indexes for a label
-->
[h,foreach(index, getMacroIndexes(macro)), code: {
[props = getMacroProps(index)]
[isBlue = if(getStrProp(props, "color") == "blue" &&
getStrProp(props, "group") == "Encounter Powers", 1, 0)]
[h,if(isBlue): setMacroProps(index, "color=default")]
}]
}]
[abort(0)] <!-- Suppress output text -->
The way the above macro works is by getting a list of the macros getMacros() which will return all the labels of the macro buttons on the Current Token. Since a token can contain multiple macro buttons with the same label the function getMacroIndexes() is used to return the unique index of each macro button for each of the labels. Then we use getMacroProps() to get the properties of the macro button in a string property list. The color of the macro button is extracted from this using getStrProp() and we check to see if it is "blue", if it is we also check to see if it is in the "Encounter Powers" group, and set the value of isBlue based on this. Then if isBlue is true (non zero) we use setMacroProps() to change the color back to the default.
You can test this macro by dragging a token onto the map and adding a macro button to it called "Something or other" in the "Encounter Powers" group and set it to blue in the creation dialog.
Extended Rest, resetting color of buttons starting with "Encounter Powers" or "Daily Powers"
For an extended rest we want to reset the color of any macro buttons that start with either "Encounter Powers" or "Daily Powers". So create a campaign macro called "Extended Rest" (don't forget to check the Apply to Selected Tokens check box) and copy the following code into it
[abort(hasImpersonated())] <!-- Abort macro if no token is impersonated -->
[h,foreach(macro, getMacros()), code: {
<!--
== each label can appear more than once (i.e. more than one
== button with same label, so we need to get all the button
== indexes for a label
-->
[h,foreach(index, getMacroIndexes(macro)), code: {
[props = getMacroProps(index)]
[isBlue = if(getStrProp(props, "color") == "blue" &&
matches(getStrProp(props, "group"),
"(Daily|Encounter) Powers"), 1, 0)]
[h,if(isBlue): setMacroProps(index, "color=default")]
}]
}]
[abort(0)] <!-- Suppress output text -->
The only difference between this macro and the previous one is where it checks the group of the macro button. In the "Short Rest" macro button we had
[isBlue = if(getStrProp(props, "color") == "blue" &&
getStrProp(props, "group") == "Encounter Powers", 1, 0)]
Where in the "Extended Rest" macro button it is
[isBlue = if(getStrProp(props, "color") == "blue" &&
matches(getStrProp(props, "group"),
"(Daily|Encounter) Powers"), 1, 0)]
The pattern (Daily|Encounter) Powers matches both the "Daily Powers" and "Encounter Powers" strings. Hopefully from this you can see how to add powers with different durations, say you wanted to add powers that could be used once per round and place them in the "Round Group", for your "New Round" macro which resets the color you would change the lines to
[isBlue = if(getStrProp(props, "color") == "blue" &&
getStrProp(props, "group") == "Round Powers", 1, 0)]
And for your "Short Rest" you would change it to refresh encounter and round powers
[isBlue = if(getStrProp(props, "color") == "blue" &&
matches(getStrProp(props, "group"),
"(Round|Encounter) Powers"), 1, 0)]
And for your "Extended Rest" you would change it to refresh daily, encounter and round powers
[isBlue = if(getStrProp(props, "color") == "blue" &&
matches(getStrProp(props, "group"),
"(Round|Encounter|Daily) Powers"), 1, 0)]
Using Powers, or getting the blues...
So now all that is left is to set the color of the buttons when they are used. As of 1.3b48 there is no way to determine which button has been pressed from a macro, but what you can do is to add code like the following to your power macros
[h: setMacroProps("Burning Hands", "color=blue")]
Replacing the "Burning Hands" with the label of your macro button. So lets try it, on your token create a macro button called "Sleep" in the group called "Daily Powers" and in the button place the following code
Watch, the watch, you are getting sleepy, your eyelids are getting heavy.... [h: setMacroProps("Sleep", "color=blue")]
Click on the button and hopefully you should see it change to blue.
Multiple Power Buttons with the same name
A word of warning though: the above method will change the color of all buttons with that label so if you have duplicates and only want to set one (you may want to implement multi use per day powers as multiple buttons for example).
Drag a new token onto the map and change its name to Lib:DnD4ePowers, and create a macro button called "UseDailyPower", then copy in the following code
[h: found = 0]
[h: indexes = getMacroIndexes(macro.args)]
[h, foreach(button, indexes), code: {
[if(found==0), code: {
[props = getMacroProps(button)]
[group = getStrProp(props, "group")]
[color = getStrProp(props, "color")]
[if(color=="default" && group == "Daily Powers"):
setMacroProps(button, "color=blue")
]
[if(color=="default" && group == "Daily Powers"): found=1]
}]
}]
This will loop through all of the indexes for the macro buttons with the specified name searching for one that is the default color and a daily power, once it finds one it sets its color to blue and sets found=1 so no other buttons are changed (as of 1.3b48 there is no way to break out of a loop).
Now create a macro button called "Lay On Hands" and copy the following in
Oooh tingly!
[h,macro("UseDailyPower@Lib:DnD4ePowers"): "Lay On Hands"]
Duplicate that a few times and then when you click on on of the buttons then one of the "Lay On Hands" buttons will turn blue.
Fine you say but I would like to stop players using powers that are blue (or in the case of multi use powers where there are no non blue ones remaining).
We can do that by changing the "UseDailyPower" macro we created above on the Lib:DnD4ePowers library token. Change it to the following
Sorry Sir/Madam, you have already used that!
[h: found = 0]
[h: indexes = getMacroIndexes(macro.args)]
[h, foreach(button, indexes), code: {
[if(found==0), code: {
[props = getMacroProps(button)]
[group = getStrProp(props, "group")]
[color = getStrProp(props, "color")]
[if(color=="default" && group == "Daily Powers"):
setMacroProps(button, "color=blue")
]
[if(color=="default" && group == "Daily Powers"): found=1]
}]
}]
<!-- if "free" one is not found then inform user they can't do it -->
[if(found==0), code: {
[dialog("PowerUsed"): {
<title>Can Not Use Power</title>
<meta name="temporary" content="true">
You have already used [r: macro.args]
}]
}]
[abort(found)] <!-- Abort the macro if an unused power was not found -->
And change the token's "Lay On Hands" macro code (don't forget to change all the duplicates too)
[h,macro("UsePower@Lib:DnD4ePowers"): "Daily:Lay On Hands"]
Oooh tingly!
Then clickity, clickity, click on the "Daily:Lay On Hands" buttons and when you have none left you should get the following dialog
It ain't pretty but the concept is there and you can easily expand on it to pretty it up.
While we are at it we should add a "UseEncounterPower" macro to Lib:DnD4ePowers
[h: found = 0]
[h: indexes = getMacroIndexes(macro.args)]
[h, foreach(button, indexes), code: {
[if(found==0), code: {
[props = getMacroProps(button)]
[group = getStrProp(props, "group")]
[color = getStrProp(props, "color")]
[if(color=="default" && group == "Encounter Powers"):
setMacroProps(button, "color=blue")
]
[if(color=="default" && group == "Encounter Powers"): found=1]
}]
}]
<!-- if "free" one is not found then inform user they can't do it -->
[if(found==0), code: {
[dialog("PowerUsed"): {
<title>Can Not Use Power</title>
<meta name="temporary" content="true">
You have already used [r: macro.args]
}]
}]
[abort(found)] <!-- Abort the macro if an unused power was not found -->
You can also use this for cases where there is only a single button for a power.
You can download this part of the tutorial in in a campaign file which was made using MapTool 1.3b48.