Lets leave the character sheet at this for the moment and move on to a new
example.
Edit->Campaign Properties, Token Properties Tab, Basic Token type,
add the following properties
We are going to store our weapons in a string property list with the folowing
keys.
- NumWeapons - The number of weapons in our property list.
- UsingWeapon - The weapon we are currently using.
- WeaponXName - The name of weapon number X
- WeaponXDamage - The damage of weapon number X
- WeaponXBonus - The bonus of weapon number X
We could add a lot more, but lest keep it semi simple for this post.
The first thing we need is a way to enter weapons, we could use the input()
function but since this is a tutorial on frames and dialogs, I should probably
show you how to do it in a dialog.
But first we need to do some set up, when the player creates a new weapon we
will need to get NumWeapons add 1 to it, save it back to the property and use
that number (lets not worry about what happens if a player cancels the entry
of the weapon as we are not really that worried if we have gaps in our
numbering scheame). One problem is though what do we do first time around
snce the property list would be empty so trying to use the token property
Weapons in strProp*() functions would result in the user being prompted for
a value. We could add a default value in the campaing for the token, but
there are also other methods.
One thing we can do is use the isPropertyEmpty() function to check if the
property is empty and if so use a initial value for it, or the
getProperty() function that will just return an empty string ("") not
prompt if there is no property.
So lets create a macro that returns the number of a new weapon. Create
a macro called NextWeaponNumber and then paste the following code into it.
Code:
<!--
Returns the number for the next weapon as well as updating the
the counter.
-->
<!-- If Weapons token property is empty set it to a default value -->
[h,if(isPropertyEmpty("Weapons")): Weapons = "NumWeapons=0;"]
[h: numWeapons = getStrProp(Weapons, "NumWeapons") + 1]
<!-- Now update our property -->
[h: Weapons = setStrProp(Weapons, "NumWeapons", numWeapons)]
<!-- Finally set out return value -->
[h: macro.return = numWeapons]
You can test it by running running the following code from chat a few times
Code:
[macro("NextWeaponNumber@Lib:Test"): ""] [macro.return]
When you are done you can reset the weapon count simply by editing the token
properties and clearing out the text for weapons.
Lets also make a macro called AddWeapon which takes a property list with the
following keys
And adds or updates the weapon in the string property list.
Code:
<!--
Adds a weapon to the Weapons property list
Parameters (in a string property list)
Name = Name of Weapon
Damage = Damage Weapon does
Bonus = Bonus of Weapon
Number = The index number of the Weapon
-->
[h: num = getStrProp(macro.args, "Number")]
[h: damage = getStrProp(macro.args, "Damage")]
[h: name = getStrProp(macro.args, "Name")]
[h: bonus = getStrProp(macro.args, "Bonus")]
[h: Weapons = setStrProp(Weapons, strformat("Weapon%{num}Name"), name)]
[h: Weapons = setStrProp(Weapons, strformat("Weapon%{num}Damage"), damage)]
[h: Weapons = setStrProp(Weapons, strformat("Weapon%{num}Bonus"), bonus)]
You can test this macro too by a little typing at the command line.
Code:
[macro("AddWeapon@Lib:Test"): "Number=1; Damage=1d8; Name=LongSword; Bonus=0"]
Look at the Weapons property and see how its built up our string propety list
for us. It wont have modified NumWeapons but that is ok we are going to assume
that NextWeaponNumber is always used before adding a new weapon. Before clearing
out the Weapons property to reset it lets write a function to retrieve a
weapon.
Create a macro called GetWeapon on your Lib:Test token and paste the following
into it.
Code:
<!--
Retrieves a weapon from the Weapons Property list.
Parameters
Weapon Number
Returns
A string property list with following keys
Name = Name of Weapon
Damage = Damage Weapon does
Bonus = Bonus of Weapon
Number = The index number of the Weapon
If the weapon is not found then an empty string ("") is returned.
-->
[h: num = macro.args]
[h: damage = getStrProp(Weapons, strformat("Weapon%{num}Damage"))]
[h: name = getStrProp(Weapons, strformat("Weapon%{num}Name"))]
[h: bonus = getStrProp(Weapons, strformat("Weapon%{num}Bonus"))]
[h, if(name == ""):
macro.return = ""
;
macro.return = strformat("Number=%{num}; Damage=%{damage}; Bonus=%{bonus}; Name=%{name}")
]
Test it with
Code:
[h, macro("GetWeapon@Lib:Test"): 1] [macro.return]
Lets add a way to delete items. Create a macro called DeleteWeapon and paste
the following code.
Code:
<!-- ============================================================ -->
<!-- ============================================================ -->
<!-- ============================================================ -->
<!--
Deletes a weapon from the Weapons property List.
Parameters
The weapon number
-->
[h: num = macro.args]
[h: Weapons = deleteStrProp(Weapons, strformat("Weapon%{num}Damage"))]
[h: Weapons = deleteStrProp(Weapons, strformat("Weapon%{num}Name"))]
[h: Weapons = deleteStrProp(Weapons, strformat("Weapon%{num}Bonus"))]
One more "setup" function then we should be good to go.
Lets create a function that returns a string list of all the item numbers
(remember we can have gaps because a user could cancel the addition of the
item after calling NextWeaponNumber or they could delete a weapon).
Create a macro on Lib:Test called GetWeaponNumbers
Code:
<!--
Gets a string list of the valid weapon numbers
-->
<!-- If Weapons token property is empty set it to a default value -->
[h,if(isPropertyEmpty("Weapons")): Weapons = "NumWeapons=0;"]
[h: maxNum = getStrProp(Weapons, "NumWeapons")]
[h: wnumList=""]
[h,c(maxNum), code: {
[h: wnum = roll.count+1]
[h: name = getStrProp(Weapons, strformat("Weapon%{wnum}Name"))]
[if(name != ""):
wnumList = listAppend(string(wnumList), string(wnum))
]
}]
[h: macro.return = wnumList]
The string() around the arguments in listAppend() is to convert the
arguments to strings, as of b48 listAppend() seems to have problems
with arguments that could be interpreted as numbers.
So now we can get back to the dialogs. Lets create a dialog to edit weapons.
Create a macro on your Lib:Test called EditWeaponDialog and paste the following
into it.
Code:
[dialog("weaponInput", "undecorated=1"): {
[h: weaponNum = getStrProp(macro.args, "Number")]
[h: name = getStrProp(macro.args, "Name")]
[h: bonus = getStrProp(macro.args, "Bonus")]
[h: damage = getStrProp(macro.args, "Damage")]
<!-- If we do not have a weapon number grab the next one -->
[h, if(weaponNum == ""), code: {
[h,macro("NextWeaponNumber@this"): ""]
[h: weaponNum = macro.return]
}]
<html>
<head>
<title>Edit Weapon Dialog</title>
<meta name="input" content="true">
</head>
<body>
<form name="weaponInput" action="[r:macroLinkText('AddWeapon@Lib:Test')]">
<table>
<tr>
<th>
<label for="Name">Weapon Name</label>
</th>
<td>
<input type="text" name="Name" value="[r: name]"></input> <br>
</td>
</tr>
<tr>
<th>
<label for="Damage">Weapon Damage</label>
</th>
<td>
<input type="text" name="Damage" value="[r: damage]"></input> <br>
</td>
</tr>
<tr>
<th>
<label for="Bonus">Weapon Bonus</label>
</th>
<td>
<input type="text" name="Bonus" value="[r: bonus]"></input>
</td>
</tr>
<tr>
<!-- hidden input with the weapon number -->
<input type="hidden" name="Number" value="[r: weaponNum]"></input>
<input type="submit" name="Save" value="Save"> </input>
</form>
</body>
</html>
}]
[img]http://lmwcs.com/maptool/images/DialogTut/EditWeaponDialog1.png[/img]
A Couple of things to notice, for the second argument to dialog() I have
undecorated=1. This is the same as using <meta name="input" content="1">.
Unfortionately the meta tag option has a bug at the moment so for now
you should use "undecorated=1", when I submit the patch to fix the
meta tag either option will work. The second thing to note is @this will
not work in a macro link, so I build the @ portion of the macro to
call when the form is submitted.
The action=... portion of the form tag specifies which macro to call when
any submit button is pushed for the form. If the dialog is specified as
a input dialog, the close button down the bottom is not displayed and when
any form on the dialog is submitted it is closed.
The arguments to the macro that is called when the form is submitted is
a string property list with the names of the input fields as the keys and
the entered value as the values. Since I named all my inputs the same
as the keys in the parameter for the AddWeaponMacro I can call that
straight from the submit action on the form (some times is seems like
I almost know what I am doing).
The only problem is our edit weapon is kinda plain compared to our character
sheet so time to add a little bling.
Change your EditWeaponDialog button on Lib:Test to
Code:
[dialog("weaponInput", "undecorated=1"): {
[h: weaponNum = getStrProp(macro.args, "Number")]
[h: name = getStrProp(macro.args, "Name")]
[h: bonus = getStrProp(macro.args, "Bonus")]
[h: damage = getStrProp(macro.args, "Damage")]
<!-- If we do not have a weapon number grab the next one -->
[h, if(weaponNum == ""), code: {
[h,macro("NextWeaponNumber@this"): ""]
[h: weaponNum = macro.return]
}]
<html>
<head>
<title>Edit Weapon Dialog</title>
<meta name="input" content="true">
<link rel="stylesheet" type="text/css" href="EditWeapon_css@[r: getMacroLocation()]">
</head>
<body>
<form name="weaponInput" action="[r:macroLinkText('AddWeapon@Lib:Test')]">
<table>
<tr>
<td>
<table>
<tr>
<th>
<label for="Name">Weapon Name</label>
</th>
<td>
<input type="text" name="Name" value="[r: name]">
</input> <br>
</td>
</tr>
<tr>
<th>
<label for="Damage">Weapon Damage</label>
</th>
<td>
<input type="text" name="Damage" value="[r: damage]">
</input> <br>
</td>
</tr>
<tr>
<th>
<label for="Bonus">Weapon Bonus</label>
</th>
<td>
<input type="text" name="Bonus" value="[r: bonus]">
</input>
</td>
</tr>
</table>
</td>
<td>
<img src='[r: getTokenImage(100)]'></img>
</td>
</tr>
</table>
<!-- hidden input with the weapon number -->
<input type="hidden" name="Number" value="[r: weaponNum]">
</input>
<input id="saveButton" type="submit" name="Save" value="Save">
</input>
</form>
</body>
</html>
}]
And add EditWeapon_css to Lib:Test that contains
Code:
body {
background-color: #CCBBBB
}
And you might as well add a AddWeapon button to your Token that contains
Code:
[macro("EditWeaponDialog@Lib:Test"): "" ]
[abort(0)]
Now our dialog looks like

Ok now lets make a quick dialog to display our weapons. Create a new
macro on your Lib:Test called ViewWeapons and paste in the following
Code:
[dialog("Weapons"): {
<html>
<head>
<title>Weapons</title>
<link rel="stylesheet" type="text/css" href="ViewWeapon_css@[r: getMacroLocation()]">
</head>
<body>
[h,macro("GetWeaponNumbers@this"): ""]
[h: wpList = macro.return]
<table>
[foreach(weapon, wpList, ""), code: {
[h,macro("GetWeapon@this"): weapon]
[h: wProp = macro.return]
<tr class="WeaponName">
<th>
[r: getStrProp(wProp, "Name")]
</th>
</tr>
<tr>
<th>Damage</th>
<td>[r: getStrProp(wProp, "Damage")]</td>
<th>Bonus</th>
<td>[r: getStrProp(wProp, "Bonus")]</td>
</tr>
}]
</table>
</body>
</html>
}]
For good measure create a macro button called ViewWeapon_css on Lib:Test
paste in the following.
Code:
.WeaponName {
background-color: #55AA55;
color: white;
text-align: center;
}
Add a button to your token called ViewWeapons which contains
Code:
[macro("ViewWeapons@Lib:Test"): ""]
[abort(0)]
And this is what it looks like

* Edited to correct syntax errors