Interview with OnlineDM on Skyland Games

Here’s something fun: I was interviewed on another blog!

Thorynn over at Skyland Games reached out to me for an interview about online gaming. I thought he asked some great questions, and I enjoyed answering them.

 

Gee, I feel like such a celebrity now. <blushing>

-Michael the OnlineDM

Madness at Gardmore Abbey: Session Four

Past sessions: Session OneSession Two, Session Three

This is the recap of my fourth session running the Madness at Gardmore Abbey adventure via MapTool and Skype for my family group. As always, SPOILERS AHEAD.

Sora the dragonborn swordmage (played by my wife), Homer the elf hunter (played by my brother in law) and Stasi the half-elf warpriest of Pelor (played by my sister in law) found themselves in the Temple of Bahamut on Dragon’s Roost, having just finished an extended rest under the protection of Sir Oakley. Upon their waking, Sir Oakley offered the party a mysterious object he had found hidden in a niche on the altar to Bahamut: An ivory plate that the party immediately recognized as a second card from the Deck of Many Things, to go along with the Key card they had found earlier.

This card had an engraving of three women – one young, one middle-aged, and one old (with a pair of scissors). Stasi was able to figure out that this represented the Fates. (Note that I’m running the game online and therefore am not handing out the physical cards; I like that the players get to puzzle out what some of the cards represent based on a description that I provide rather than getting to read the names on the cards.) Stasi agreed to carry this card with the other for the time being.

Having escorted Sir Oakley to the Abbey and having helped him defeat the enemies in the Temple itself, the adventurers agreed to help him find the three missing relics that would be necessary for him to perform the needed cleansing rituals. He didn’t know where these relics were, but he knew that they must be somewhere within the grounds of Gardmore Abbey. Sir Oakley ultimately agreed to accompany the group on their search (with a three-PC party, it’s nice to have a companion character along to help with the scaling of battles).

The group decided to start by searching the catacombs. Stasi the warpriest was itching to blast the heck out of some undead creatures (which have been rare in the Essentials adventures to this point). Coming down the stairs, they heard prayers ahead. Homer the hunter stayed back on the stairs while Stasi and Sora accompanied Sir Oakley down to investigate. They found a bunch of humans in armor praying around an altar of Bahamut.

Thus began Encounter 23: Altar of Glory. I’ll say right here that I totally screwed this up, because this was supposed to be the first encounter where my party was to meet The Others – the rival adventuring party. Oops. I forgot all about that, and I hadn’t prepared The Others in MapTool yet anyway. Major oversight on my part, but I have an idea of how I’ll fix this.

My other oversight is that I once again forgot to have the cards from the Deck do anything in combat, but that’s in part because combat was a little weird in starting. This encounter began with a skill challenge for the party to figure out what was going on with these knights praying in the catacombs. Sir Oakley joined in the prayers at the urging of the PCs. A religion check from the warpriest showed that the lead knight was making up some of the prayers as he went along, and the other knights were following his lead. They also noticed that the knights had their scabbards loosened and kept their hands close to their weapons, as though they were expecting a fight. However, they failed to recall any history of the Abbey that might be helpful in understanding the situation, and they twice failed to notice that the knights weren’t casting shadows.

Thus, the skill challenge was failed, and the knights attacked with a surprise round. It soon became clear that these weren’t actual knights – they were pale reavers disguised in the forms they once held in life. I loved describing the first attack, as one of the minions disappeared into a wall, reappeared next to a PC, and then reached for his sword, which somehow transformed into a long mane of hair as the reaver’s true form was revealed.

The fight was challenging with the surprise round and the good initiative roll from the lead reaver, but our warpriest finally got to Smite Undead on the lead reaver, and the group kept him pinned in a corner for much of the fight while they beat up his friends and later focused on him. Some surges were spent, but none were actually drained by the reavers themselves.

Examining the room showed that the altar to Bahamut could infuse a weapon with the one-time ability to deal fire damage, which Homer the hunter was all over. Sir Oakley helped him with the prayers, and the dragon heads on the altar came to life and bathed Homer’s bow in flames, which then died down, leaving the bow warm to the touch. This came in handy in the next fight.

One sarcophagus in this room had been pried open, and the skeleton within was missing its skull. Corruption emanated from this coffin, and the party was able to figure out that the corruption could only be cleansed if the skull could be returned. No skull was to be found in this room, however.

Onward to the east, then! The stone doors opened smoothly enough, revealing a room with a badly damaged ceiling. Roots from above had grown through the ceiling, creating a tangle that extended most of the way to the floor, stopping six feet above the ground. Stasi’s Sun’s Glow showed a good portion of the room, and the party could hear some shuffling footsteps in a far corner and a very faint sound of movement coming from another corner of the room near the ceiling (up in the roots). After Stasi and Sora moved into the room, the light revealed a mummy coming toward them

Encounter 25: Memorial Chamber was under way. Homer won initiative but delayed, staying back in the Altar of Glory chamber. The mummy moved toward the doorway and cursed Stasi, so that she would take necrotic damage every time she tried to hurt the mummy (a brutal but cool ability). Sora figured out that she could yank on the roots in order to bring the fragile ceiling down on the mummy, which worked like a charm (I decided that DC16 Strength would be for a minor action check and DC12 would be for a standard action). That mummy struggled for the next three rounds to free its legs from the rubble (immobilized, save ends).

Knowing that they had heard other movement in the chamber, the party was cautious about moving farther in. Too bad for them, then, when a swarm of rot scarab beetles stealthily crawled through the roots on the ceiling without attracting attention and then rained down onto Sora’s head. This was a wonderfully disgusting moment, leaving Sora the swordmage inside the swarm. Homer eventually jumped into initiative at the end of the round, after Sir Oakley told him that the mummy would catch fire if hit with fire, using Bahamut’s blessing from the previous chamber to light that mummy up.

At the beginning of round two, I remembered that I wanted to use the Deck of Many Things, and I decided later that I actually kind of prefer having the Deck manifest its power after the first round of battle. It feels artificial for the Deck to know exactly when combat is breaking out and to show up immediately; I like the idea that it responds to the stress of actual combat and then manifests.

In this case, the image of the Key appeared next to Stasi as a big glowing light. A minor action Arcana check revealed that someone standing in the Key square could use a move action to teleport 5 squares; pretty cool stuff!

Round two is also when the Flameskull revealed itself from behind a mosaic-covered wall on the far side of the room and dropped a fireball that enveloped three of the PCs plus the mummy and the scarab swarm. Uh oh! The new threat caused some major concern.

Eventually, Sir Oakley ended up charging into the chamber largely to get away from the swarm’s aura and to go after the Flameskull (and because I wanted to make the combat more dynamic than a chokepoint between two rooms). He was left to his own devices for a while as the PCs finished off the mummy and the swarm. Finally, the PCs came to help, rescuing Sir Oakley from unconsciousness and destroying the Flameskull.

When the Flameskull was defeated, the skull’s fires went out, leaving behind a normal skull. The PCs immediately thought – aha, perhaps this is the missing skull from the earlier sarcophagus. Indeed it was, and Stasi the warpriest returned it to its rightful place and used some healing magic to cleanse the corruption – in the process gaining Bahamut’s blessing and the one-time ability to breathe fire.

The Memorial Chamber was revealed to have a secret door to the north (the Perception check beat a 19, but not a 23), which led to a small room with three long-dead knights of Bahamut beneath a mural depicting the Platinum Dragon as a dracolich. Sir Oakley was able to explain that this was a private practice of some worshippers of Bahamut, and that it represented adherents steeling themselves to face death rather than worshipping undeath. Some searching of this secret chapel revealed two other doors leading to other chambers, three topazes that had been taken from the temple, and the fact that these knights evidently closed themselves in this room and starved to death rather than leaving. Interesting stuff. Having Oakley along at this point has been helpful.

From here, the party decided to go through the door on the west part of the north wall of the Memorial Chamber, which revealed a short hallway, beyond which was a room with a fountain – and a couple of skeletons.

Encounter 24: Font of Divine Health began with two skeletal tomb guardians arising and attacking. I once again had Sir Oakley get himself in the middle of things in order to create some movement. A blazing skeleton popped out from a niche to light Stasi on fire.

In round two, the Fates revealed themselves. The new card from the Deck manifested adjacent to Stasi, who boldly stepped into the light and understood that if she were hit by an attack while in the Fates’ square, she could force a re-roll of that attack with a -2 penalty. This power appealed greatly to Homer, the great chicken of the party, who camped in that square for several rounds.

Meanwhile, the tomb guardians were slicing and dicing all over the place, making effectively four attacks per round (a fun mechanic). Some skeletal minons revealed themselves, providing a flank for the guardians. All the while, the blazing skeleton kept burning things from a distance.

The fight ended with Stasi using a daily power, then finishing the final foe in a blaze of holy might. At this point, the mosaic of the head of Bahamut inlaid in the floor glowed brightly, and the whole party regained some free hit points. It was soon discovered that drinking from the fountain in this room would also regain some free hit points, plus grant some necrotic resistance. Good times; I love these alternate, short-term rewards.

Here we stopped for the night, with Homer and Stasi suggesting an extended rest in the secret chamber and Sir Oakley adamant that they must press on and find the holy relics. I hope they do press on; they’re not in severe shape just yet (Oakley is the lowest on surges by far). If they decide to rest in the secret chapel, so be it. It’s possible that their entrance has guaranteed that it will not remain secret indefinitely…

-Michael the OnlineDM

OnlineDM1 on Twitter

Next session: Session five

Opening for a gamer: Friday night MapTool game

Spread the word all: You could be the lucky person chosen to join an actual OnlineDM-run campaign in MapTool! Calooh! Callay!

All right, so maybe this isn’t the most exciting news ever, but I do have an opening in my long-running Friday night game. It’s a D&D Fourth Edition game in EN World’s War of the Burning Sky campaign saga. The characters are 17th-18th level, so we’re at upper paragon, in spitting distance of epic tier.

The game runs on Friday nights, starting at 6:00 PM US Mountain Time (8:00 PM Eastern, etc.). We usually game for about four hours, and the game runs most weeks (typically three weeks a month or so).

Obviously, if you’re very new to D&D 4th Edition, this probably isn’t a great fit since the party is at high level. But if you or someone you know is interested in joining the game, drop me a line at onlinedungeonmaster@gmail.com. Start the new year in a new campaign!

FYI, the party is pretty well-balanced, so almost any class of character would be welcome. We have a dwarf fighter, a tiefling warlock, a genasi wizard (damage focused), a pixie bard and a human hybrid wizard/swordmage (more wizardy with a control focus).

Death of a PC after a year and a half

I’m not a killer DM, but I’ve offed a few PCs in my time. Off the top of my head, I can remember the following deaths:

  • In the first Living Forgotten Realms game I ran, a low-level striker rushed into a room full of bad guys and got chomped on by two Guard Drakes, taking him below his negative bloodied value.
  • In the first game I ever ran for my wife’s brother and his wife, my brother-in-law’s character died in the first combat. He got better.
  • One party in an LFR game let the bulk of the party become separated from their healer, resulting in a dead seeker
  • I destroyed a PC in memorable fashion when the foolish thief rode a beholder into a river of lava after hanging on a little too long for the ride.
  • I’m pretty sure I killed off another PC played by the same player whose PC I killed off with the guard drakes in the first example, but I don’t remember when that was.

Now, in my longest-running campaign, my Friday night War of the Burning Sky campaign via MapTool and Skype, I had never killed a PC through 15 levels of play. They had some very close calls, but the numbers always seemed to come up in their favor when the chips were down. The characters had reached 15th level with no PC deaths.

That all ended with our most recent game.

SPOILERS AHEAD FOR ADVENTURE SIX OF WAR OF THE BURNING SKY

After the session last week with the Storm Titan and friends, the party was ready to burst into a mysterious laboratory. The lone healer in the party (a pacifist cleric optimized for massive healing) wasn’t around for the night’s game, so we pressed on with a party of four PCs.

The first fight was against a flying minotaur and some freaky creatures from vats of goo, and the party was definitely up to the challenge. They blew through the bad guy without much trouble.

The second fight of the night (which was the fourth fight of the adventuring day) started when the PCs opened the doors to a fancy two-story library/office room with a glass-domed ceiling and an opulent rug inside the door. Thorfin the fighter marched into the room and just barely jumped out of the way in time as the rug itself tried to reach up and grab him. With that attack having missed, an invisible flying monk tossed him across the room and into a wall, beginning the combat.

The party was at level 15, and the monk was a level 19 solo. I updated her stats to be more in line with modern solos, but since there were only four PCs instead of five, I lowered her hit points from 718 to 450.

This was one bad-ass monk, and the party had a hard time with her. They tried throwing out various controlling effects, but she had the ability to shake off a condition once per round, which made a big difference. It didn’t help that the adventurers’ dice turned ice cold on them for long stretches. If it weren’t for the fact that Hammer Rhythm let the fighter deal 5 damage even on a miss, things would have been far worse.

Vena, our elf seeker, found herself knocked unconscious by the monk’s lightning hands. Faebs, the human wizard/swordmage hybrid, managed to deactivate the man-eating rug and knock Vena off the suspended sculpture where she had fallen unconscious so she could let Vena spend her second wind. Shortly after getting back on her feet, Vena was knocked down once more. She failed a couple of death saving throws and then rolled a 19 – which, using a bonus point, turned into a 20 and let her spend a healing surge! Boy, were they missing their cleric.

When the monk darted out of the room, the party decided to close the doors of the library and barricade themselves inside. They created a hole in the glass-dome ceiling so they could climb out. The monk huffed and puffed and blew a hole in the door, by which time two of the PCs were on the roof with the other two on the rope on their way up.

The monk’s teleport power failed to recharge, so she couldn’t pop into the room that way.

The monk’s power to summon a magical fist inside the room to attack the climbing PCs failed to recharge.

But the monk’s “turn into lightning and zap a bunch of PCs” power DID recharge. Up the rope she went, zapping the climbers and ending on the roof.

Vena the seeker tried to take care of the frightening monk, but her dice betrayed her once more. She found herself stuck next to the monk when the monk’s bonus turn to make a free basic attack came up – and the lightning hand dropped Vena to the ground.

Whereupon Vena promptly failed her third death saving throw, in round 14 of the fight.

Things were looking grim for the party, when all of a sudden the player of our pacifist cleric showed up! His character was on the opposite side of the battlefield and spent the first round and a half rushing over to the fray – in time to resurrect Thorfin, who had fallen unconscious, but too late for Vena.

With the battle teetering on the brink, the cleric made the monk vulnerable to damage, and the wizard finished the monk off with a super-powered magic missile. The party got away from the lab (with the body of their fallen comrade) just in time to watch a magical storm destroy the building.

Thus ends the tale of fair Vena the elf seeker. Her character’s paragon path was Twilight Guardian, which to her meant that she respected the natural cycle of life, and therefore would not want to be resurrected (despite the cleric’s attempts to make it happen). We’ll work on a new character for Vena’s player; we may very well end up with the first Pixie PC I’ve seen in action!

RIP Vena.

MapTool states: Tons of useful D&D 4e conditions

In polishing my campaign framework for D&D 4th Edition in MapTool (see my earlier post about tracking conditions if you missed it), I ended up creating a lot of icons to represent various states in MapTool. I love the states that came with the torrent of images over at rptutorials.net, but I needed to create some of my own, too.

   

Most of the icons I created are for numeric states. For instance, I already had -10 through +10 in both red and blue squares to represent attack penalties/bonuses and defense penalties/bonuses. I realized at some point that I really ought to have separate states for damage penalties/bonuses, so I created orange squares with those numbers (once again -10 to +10).

      

I also thought it would be nice to have a visual way to represent vulnerability and resistance. Now, I didn’t take it to the level of vulnerability/resistance to various TYPES of damage, but it’s rare to have to worry too much about that in practice. I decided that a black square with a white number in it would represent vulnerability while a white square with a black number in it would represent resistance.

  

Finally, I’ve long had a state for ongoing damage, but I thought it would also be useful to have a NUMBER associated with it (ongoing 5, ongoing 10, etc.). So, I created some green triangles with black numbers to represent ongoing damage.

      

Given all of these new states, I had to create macros to handle them; not too hard since I could base them on work I’d already done for the attack and defense modifier macros. I also thought it would be a nice touch to have a reminder pop up in the “Take damage” macro if a character had resistance or vulnerability:

[h: VulnerableReminder=if(VulnerableState==0, "", add(" (note Vulnerable ", VulnerableState,")"))]
[h: ResistReminder=if(ResistState==0, "", add(" (note Resist ", ResistState, ")"))]
[h: DamageString=add("Amount of damage taken", VulnerableReminder, ResistReminder)]
[h: x=input("Dmg|0|"+DamageString)]
[h: abort(x)]

If the character is both Vulnerable 5 and Resist 5, the message will look like this:

Anyway, all of these states are included in my campaign framework. If you’d like a ZIP file with the images for all of the states that I use, plus some additional states that come with the big torrent, you can download that ZIP file here.

What’s missing? What additional states should I include that aren’t already here?

-Michael the OnlineDM (OnlineDM1 on Twitter)

Advanced MapTool macros part 2: Intro to JSON arrays

As I mentioned in the first post in this series, I’ve been working on a set of macros to help me better track conditions on characters for D&D 4th Edition in MapTool. Building those macros has led me deeper into some of the capabilities of the MapTool macro language that I had previously avoided. Part 1 of the series focused on JSON objects; today we talk about JSON arrays.

What is a JSON array?

In any context, an array is basically a list. “1, 2, 3” is an array. A shopping list is an array.

A JSON array is a list enclosed in brackets and separated by commas for use in MapTool. As for what it’s a list OF, well, JSON arrays are flexible on that point.

You can have a JSON array that’s a list of numbers:

[r: MyJSONArray='[12, 3, -44]' ]

You can have a JSON array that’s a list of words:

[r: MyJSONArray='["Red", "Purple", "Yellow"]' ]

You can have a JSON array that’s a mixture of numbers and words:

[r: MyJSONArray='["Red", 3, -44]' ]

More interestingly, you can have a JSON array that’s a list of JSON objects – or even a list of other JSON arrays!

JSONObject1=[r: JsonObject1= '{"Color":"Red", "Number":12}' ]<br>
JSONObject2=[r: JsonObject2= '{"Color":"Purple", "Number":3}' ]<br>
JSONObject3=[r: JsonObject3= '{"Color":"Yellow", "Number":-44}' ]<br>
[h: MyJSONArray=' [ ]' ]
Array step 1=[r: MyJSONArray=json.append(MyJSONArray, JSONObject1) ]<br>
Array step 2=[r: MyJSONArray=json.append(MyJSONArray, JSONObject2) ]<br>
Array complete=[r: MyJSONArray=json.append(MyJSONArray, JSONObject3) ]<br>
<br>Second item = [r: SecondItem=json.get(MyJSONArray, 1)]
<br>Color of second item = [r: SecondItemColor=json.get(SecondItem, "Color")]

How do you build a JSON array?

As you can see from the examples above, you can build a simple JSON array in a manner very similar to building a JSON object. The contents of the array are separated by commas and enclosed in square brackets [ ], and the whole thing is enclosed in single quotes so that MapTool will allow you to use double quotes inside the array (so that it can contain words).

Another option I showed was to use the json.append function in MapTool. You need to start with an existing or blank JSON array (hence the MyJSONArray = ‘[ ]’ bit, creating a blank JSON array). You then append the new item to the end of the array.

What do you do with a JSON array?

As with a JSON object, a JSON array is useful if you store it as a property on a library token and then have it available for other macros to use later. It’s also useful because it’s an array – a list of things. With a list, you can do things like loop through each thing on the list using a FOREACH loop and do something to each thing on the list.

If you want to get some information out of an array, the simplest way to do it is to use the json.get command along with the appropriate index in the array. Each thing in the array has an index number, but you’ll want to keep in mind that the indexes start from zero.

Let me repeat that: The first item in an array is item 0, followed by item 1, and so on.

So, going back to my second array example, if I want to know what the middle of the three elements is, I’d do the following:

[r: MyJSONArray='["Red", "Purple", "Yellow"]' ]<br>
The middle color is [r: json.get(MyJSONArray, 1)]

Which returns:

[“Red”, “Purple”, “Yellow”]
The middle color is Purple

You’ll note that picked index 1 in my json.get statement. If I’d wanted it to say “Red” (the first element of the list), I would have asked for json.get(MyJSONArray, 0). Very important to remember.

If I want to know how many items are in the JSON array, I can use json.length. Keep in mind that my three-item array with have json.length=3, but the highest index number will be 2 (because it starts counting from zero). Confusing, yes.

How do I change what’s in a JSON array?

Technically speaking, you can’t edit a JSON array. However, you can re-save a new array with the same name that’s based off an existing array.

If you have a particular element of the array that you want to set to a new value, you can use the json.set command.

Original array: [r: MyJSONArray='["Red", "Purple", "Yellow"]' ]<br>
I'm changing the array now... [h: MyJSONArray=json.set(MyJSONArray, 1, "Green")]<br>
New array: [r: MyJSONArray]

This returns:

Original array: [“Red”, “Purple”, “Yellow”]
I’m changing the array now…
New array: [“Red”,”Green”,”Yellow”]

What I’ve done is created a new array that is just like the original array except that I’ve set the second element (index 1 is the second element, remember) to the value “Green” instead of whatever it was before. I’ve saved this new array with the SAME NAME as the original array. So, I’ve effectively changed the original array, but I’ve technically created a new array with the same name (which accomplishes the same thing).

Another useful command is json.remove, which gives you the array minus whatever you’ve removed.

Original array: [r: MyJSONArray='["Red", "Purple", "Yellow"]' ]<br>
If I remove the middle element, I get this new array: [r: NewJSONArray=json.remove(MyJSONArray, 1)]

This returns:

Original array: [“Red”, “Purple”, “Yellow”]
If I remove the middle element, I get this new array: [“Red”,”Yellow”]

If I’d used MyJSONArray= instead of NewJSONArray= in the second line, I would have effectively edited the array to remove the second element (index 1).

JSON objects within JSON arrays

Now, keep in mind that you can get fancy with JSON arrays. The things in the array can themselves be JSON objects or, messier still, JSON arrays. My last example in the opening section shows you one of these. It can get a little confusing depending on how deep you want to go, but there are definitely times when it’s useful to have lists of pairs of things (like colors with numbers, or token names with conditions).

Comparing JSON objects and JSON arrays

Bringing it all together, what are these things all about?

JSON objects are enclosed in curly braces { }. JSON arrays are enclosed in square brackets [ ].

JSON objects consist of labels and values (Name: Bob. Age: 23). JSON arrays are just lists of things (Bob, 23). However, you can put objects within arrays. You can also put objects within objects or arrays within objects or arrays within arrays… it can get messy!

To get a value out of an object, you can use json.get and then specify which key you want (PersonName=json.get(MyJSONObject, “Name”)). To get a value out of an array, you can use json.get and then specify the index of the item in the array that you want, with the first item having an index of 0 (PersonName=json.get(MyJSONArray, 0)).

Next step

So what’s next? Well, I keep talking about storing these objects and arrays on library tokens, so I think it’s time I talked about what a library token is.

Advanced MapTool macros part 1: Intro to JSON objects

Over the past few weeks, I’ve been working on what I expected would be a not-too-hard MapTool macro project. I wanted to create a macro (or set of macros, as it turned out) for putting conditions on tokens that would also keep track of when those conditions needed to end. Seemed simple enough; after all, I already had nice little macros that would toggle a condition on or off of a selected token or group of tokens. Those looked like this:

[h: SelectedTokens=getSelected()]
[FOREACH(TokenID, SelectedTokens, " "), CODE:
 {[h: NewState=if(getState("Dazed",TokenID)==1,0,1)]
 [h: setState("Dazed",NewState,TokenID)]
 }
]

However, in figuring out how to handle the automatic tracking of the condition end time, I had to delve deeper, trying to avoid Balrogs as I went.

Yes, dear readers: I had to learn about JSON! (Cue horrified screaming)

JSON stands for JavaScript Object Notation, and it’s something I had seen used by the REAL heavy lifters among MapTool macro writers. They had JSONs all over the place in their campaigns, and I tried my best to avoid them. They looked… intimidating.

I’ll warn you all right now, if you’re just looking for casual stuff about MapTool, skip this series of posts. This is heavy-duty macro programming compared to what I’ve written before. It still pales in comparison to the stuff written by the folks who create full-on MapTool frameworks, but it’s more than I’ve done in the past. However, if you need to learn about using JSONs, I hope to present it in an easy-to-follow manner.

What is a JSON object?

There are types of JSON in MapTool: JSON objects and JSON arrays. I’m going to talk about JSON objects today.

A JSON object is a single variable or property enclosed in braces {} that can hold several pieces of information.

For instance, here is a JSON object I ended up creating for my state-tracking macros:

{"TokenName":"Brute 1","State":"Cursed","EndCount":2,"EndRound":1,"AutoEnd":"1"}

This single object contains 5 pieces of information: The TokenName (Brute 1), the State on that token (Cursed), the count in the initiative order when that state will end (2), the round in which it will end (2) and a variable that indicates whether I want the condition to simply end automatically (1 means “yes” in this case) or not.

How do you build a JSON object?

If I wanted to build the object above, I could do it by assigning as a regular variable in MapTool, enclosing the whole thing in single quotes:

[h: MyJSONObject='{"TokenName":"Brute 1","State":"Cursed","EndCount":2,"EndRound":1,"AutoEnd":"1"}']

I could also use the ADD function if I had already created separate variables for, say, TokenName and State:

[h: CurrentTokenName="Brute 1"]
[h: CurrentState="Cursed"]
[h: MyJSONObject=add('{"TokenName":"', Brute 1, '","State":"', Cursed, '","EndCount":2,"EndRound":1,"AutoEnd":"1"}']

There’s also the option of using some of MapTool’s JSON functions, such as json.set:

[h: MyJSONObject=json.set("{ }", "TokenName", CurrentTokenName, "State", CurrentState, "EndCount", 2, "EndRound", 1, "AutoEnd", 1)]

Note that there are no single quotes needed with json.set. I start with the empty braces in quotes to let MapTool know that this will be a JSON object rather than a JSON array. I then list the key/value pairs, separated by commas.

Getting information from JSON objects

Once I’ve created this object, what the heck do I do with it? Well, assuming I save it as the value of a property of some token, I can have other macros refer to this property later to get information out of it. I do this with MapTool’s json.get function.

First, let’s say I have a library token called lib:LibToken1. Great name, right? And let’s say it has a property called StateObject, which starts off being set to 0. Now let’s assume that I assign my new JSON object to this property as follows:

[h: setProperty("StateObject", MyJSONObject, "lib:LibToken1")]

So, I set the StateObject property of the token called lib:LibToken1 equal to MyJSONObject.

In a later macro I can retrieve this property from the token and extract the information:

[h: CurrentStateObject=getProperty("StateObject","lib:LibToken1")]
[h: CurrentTokenName=json.get(CurrentStateObject, "TokenName")]
[h: CurrentState=json.get(CurrentStateObject, "State")]

The first line gets the StateObject property from the lib:LibToken1 token (which, remember, is equal to whatever I put in MyJSONObject) and stores it as CurrentStateObject (so CurrentStateObject=MyJSONObject). Then I can extract the TokenName value from that object and store it as CurrentTokenName using json.get. I do the same for the State value. I could do the same for the EndCount, EndRound and AutoEnd values. And then I can do whatever I like with these values inside my macro.

Why would you use a JSON object?

This is the question that kept me away from learning about JSON stuff for so long. Why do I even need it?

In lots of cases, you don’t actually NEED to use JSON, but it can make life easier. Where I decided I finally needed to use it was when I wanted to have a single property on a token that could hold lots of different stuff (in this case, information about states on tokens and when they end). The JSON object above is an example (actually as a piece of a JSON array) that appears in the StatesArray property of a token I created specifically to hold this kind of thing. Without using JSON, I would need to create a whole bunch of different properties on the token – one each for TokenName, State, EndCount, EndRound and AutoEnd. I could do that, but it would be messier.

And if I wanted to have a varying number of these objects – one for each token and state in the game – I’d need to create a huge multitude of properties, and I’d have to set up enough of them in advance so that I’d never run out. If I have 20 minions each getting slowed by a controller’s blast, well, I’d better have 100 properties set up in advance. If they’re being slowed and blinded, now I need 200 properties.

There’s got to be an easier way, and there is: JSON arrays. That’s the next post!

MapTool macro: Attack and defense modifier states

More MapTool macro fun! Yes, I’m a nerd and proud of it.

In a game like Dungeons and Dragons 4th Edition, it’s pretty common to get temporary bonuses to attack rolls or defenses. When you’re playing with pen and paper, you have to keep track of these yourself. When you’re using MapTool, the program can help.

Now, I’m sure that some of the snazzier MapTool frameworks out there will handle temporary modifiers in super-fancy ways that actually change the calculation of attacks based on these modifiers and so on. I’m not looking for any of that. I’m simply looking for a visible reminder on a token that it has a temporary bonus or penalty.

I started creating +2 attack and-5 attack and +2 defense states in a piecemeal manner months ago. As PCs and monsters in my games started getting powers that could apply these bonuses or penalties, I created states that would remind me of them. I would then turn these states on and off very manually – right click on the token, find the right state, click on it. Repeat to turn it off.

Well, I’m sick of that, so I decided to automate the process.

First, I created a set of 20 token states: +1 to +5 attack, -1 to -5 attack, +1 to +5 defense and -1 to -5 defense. They look something like this:

      

Depending on whether I’m using MapTool online or with my projector setup, I either display these states as 3×3 grid images (online) or 2×2 grid images (projector – the larger images show up better). The red boxes are for attack (or damage) and the blue boxes are for defenses. I use 80% opacity, so you can still make out a little of the token art behind the states.

A monster with +5 to attack, one with -3 to defense, and one with +2 to attack and defense

For a while, I was creating individual toggle buttons for my states. Click a token, then click the “+2 Attack” button, and the +2 Attack state will toggle on or off. Now that I have 20 of these states, I decided to streamline. Rather than 20 buttons, I have two: One to set attack modifiers and one to set defense modifiers.

Attack bonus macro (download the macro)

[h: x=input("NewMod|0|What is the token's new attack modifier?")]
[h: abort(x)]

I start by asking for the new attack modifier in a pop-up; if the user clicks the Cancel button, the macro ends. Note that the user can specify a +2 bonus as either “2” or “+2”; the macro works either way.

[h: SelectedTokens=getSelected()]
[FOREACH(TokenID, SelectedTokens, " "), CODE:

I create a list of token IDs called SelectedTokens for however many tokens are currently selected, and then I loop through each of these tokens to perform the same code on them. This lets me apply (or remove) a bonus or penalty to a bunch of tokens at once.

 {[if(NewMod>5 || NewMod<-5), CODE: {[assert(1==0,add("<b>Error</b>: Attack modifier must be between -5 and +5"),0)]}; {}]

I throw off an error message if the user tries to enter a number over 5 or under -5, since that’s the range of states I’ve created.

  [h: OldMod=getProperty("AttackState",TokenID)]
  [h: OldState=if(OldMod>=0, add("+", OldMod, " Attack"), add(OldMod, " Attack"))]
  [h: NewState=if(NewMod>=0, add("+", NewMod, " Attack"), add(NewMod, " Attack"))]

I figure out what the current value of the token’s AttackState property is (a number from -5 to 5), and then convert this to a string like “+2 Attack” or “-3 Attack”. This corresponds to the names of the states in my campaign file. I do the same for the new attack modifier, getting the appropriate state name.

  [h, if(OldMod==0), CODE:{}; {[h: setState(OldState,0,TokenID)]}  ]
  [h, if(NewMod==0), CODE:{}; {[h: setState(NewState,1,TokenID)]}  ]

If the new or old modifier is zero, I don’t try to change it (since no state is displayed on the token for a zero bonus). Otherwise, I turn off the old state (setting it to false, or 0) and turn on the new state (setting it to true, or 1).

  [h: setProperty("AttackState", NewMod, TokenID)]
    }
   ]

I set the value of the AttackState property to whatever the user entered, so that the macro can reference that property the next time it’s run. I then close the CODE block and the FOREACH loop that I started at the top. Voila!

The whole macro is as follows:

[h: x=input("NewMod|0|What is the token's new attack modifier?")]
[h: abort(x)]

[h: SelectedTokens=getSelected()]
[FOREACH(TokenID, SelectedTokens, " "), CODE:
 {[if(NewMod>5 || NewMod<-5), CODE: {[assert(1==0,add("<b>Error</b>: Attack modifier must be between -5 and +5"),0)]}; {}]
  [h: OldMod=getProperty("AttackState",TokenID)]
  [h: OldState=if(OldMod>=0, add("+", OldMod, " Attack"), add(OldMod, " Attack"))]
  [h: NewState=if(NewMod>=0, add("+", NewMod, " Attack"), add(NewMod, " Attack"))]
  [h, if(OldMod==0), CODE:{}; {[h: setState(OldState,0,TokenID)]}  ]
  [h, if(NewMod==0), CODE:{}; {[h: setState(NewState,1,TokenID)]}  ]
  [h: setProperty("AttackState", NewMod, TokenID)]
    }
   ]

Defense bonus macro (download the macro)

The defense macro is exactly the same as the attack macro – just replace “Attack” with “Defense”.

[h: x=input("NewMod|0|What is the token's new defense modifier?")]
[h: abort(x)]

[h: SelectedTokens=getSelected()]
[FOREACH(TokenID, SelectedTokens, " "), CODE:
 {[if(NewMod>5 || NewMod<-5), CODE: {[assert(1==0,add("<b>Error</b>: Defense modifier must be between -5 and +5"),0)]}; {}]
  [h: OldMod=getProperty("DefenseState",TokenID)]
  [h: OldState=if(OldMod>=0, add("+", OldMod, " Defense"), add(OldMod, " Defense"))]
  [h: NewState=if(NewMod>=0, add("+", NewMod, " Defense"), add(NewMod, " Defense"))]
  [h, if(OldMod==0), CODE:{}; {[h: setState(OldState,0,TokenID)]}  ]
  [h, if(NewMod==0), CODE:{}; {[h: setState(NewState,1,TokenID)]}  ]
  [h: setProperty("DefenseState", NewMod, TokenID)]
    }
   ]

I hope this type of macro is useful for folks out there. As always, let me know if you have questions about my macros or requests for new macros. I love this kind of thing, as you can no doubt tell!

Note: All macros were generated with version 1.3.b66 of MapTool, but they work with 1.2.b86 as well.

Download the properties for online games with these states

Download the properties for projector games with these states

Download my complete campaign template for online games

Download my complete campaign template for projector games

MapTool macros: Numbers on tokens – elevation, etc.

Ah, how I love to geek out with new MapTool macros!

Some months ago, I was going to be running a Living Forgotten Realms adventure that involved underwater combat, which meant that elevation could come into play. I wanted to have a visible way of displaying a token’s elevation right on the token itself, so I settled on using some token states that would display a number from 1 to 9 on the upper right corner of the token, representing its elevation.

However, I later realized that having a generic number to stick on a token is a useful thing. For instance, a later battle with a hydra showed me that it would be nice to have an indicator for the number of heads. Furthermore, I’d like to be able to display higher numbers as well, just in case.

Three monsters - one with no elevation, one with elevation 29 and one with elevation 4

Enter the new Elevation macro. First, I should explain that this macro requires you to have a certain set of token states (downloadable as a full set of properties and states here for online use or here for projector use) that include “_0” through “_9” for the ones digit and “0_” through “9_” for the tens digit. You’ll need an Elevation property on your token. And then you’ll need a macro like this one, which you can keep on either the Campaign or Global window.

[h: x=input("NewElevation|0|What is the token's new elevation?")]
[h: abort(x)]

The code above brings up an input window that asks for the new elevation and ends the macro if the user chooses the Cancel option.

[h: SelectedTokens=getSelected()]
[FOREACH(TokenID, SelectedTokens, " "), CODE:

This creates a list of token IDs called SelectedTokens, then starts a loop through each of the tokens, performing the following code on them. Thus, you could set the elevation for a group of three monsters all to the same level at once if you wished.

 {[h: OldElevation=getProperty("Elevation",TokenID)]

Figure out the token’s current Elevation property; assign it to the OldElevation variable.

   [if(NewElevation>99), CODE: {[assert(1==0,add("<b>Error</b>: Elevation cannot be higher than 99"),0)]}; {}]
   [if(NewElevation<0), CODE: {[assert(1==0,add("<b>Error</b>: Elevation cannot be lower than 0"),0)]}; {}]

Give an error message if the user tries to enter a new elevation that’s over 99 or less than zero.

   [h: OldTensDigit=FLOOR(OldElevation/10)]
   [h: OldOnesDigit=OldElevation-10*OldTensDigit]

   [h: NewTensDigit=FLOOR(NewElevation/10)]
   [h: NewOnesDigit=NewElevation-10*NewTensDigit]

Figure out the tens digit and ones digit of both the old and new elevations. I don’t know if MapTool has a modulus function, so I went with an alternative way of getting the ones digit (pretty elegant in the end, I think).

   [h: OldOnesState=add("_", OldOnesDigit)]
   [h: OldTensState=add(OldTensDigit, "_")]

   [h: NewOnesState=add("_", NewOnesDigit)]
   [h: NewTensState=add(NewTensDigit, "_")]

Since the states are called “_1” and “_2” and so on for the ones, and “1_” and “2_” and so on for the tens, I need to concatenate some strings to set up the names of the old and new states (in preparation for removing the old states and adding the new).

   [h: setState(OldOnesState,0,TokenID)]
   [h: setState(OldTensState,0,TokenID)]

   [h: setState(NewOnesState,1,TokenID)]
   [h: setState(NewTensState,1,TokenID)]

And here I actually set those states. The old states get set to zero (turned off) while the new states get set to one (turned on). Thus, if the token is changing from an elevation of 13 to an elevation of 20, I’m turning off “1_” and “_3” and turning on “2_” and “_0”.

   [h: setProperty("Elevation", NewElevation, TokenID)]

Since I’m changing the elevation states, I need to make sure I also change the elevation value on the token itself so that the macro will work again the next time I run it (the new elevation after this go-through will become the old elevation for the next time I run the macro).

   [if(NewElevation<10),CODE:{[h: setState(NewTensState,0,TokenID)]};{}]

This is just for prettiness’ sake; if the new elevation is between 1 and 9, I want to drop the zero from the tens place so the elevation will show up as “7” instead of “07” for instance.

   [if(NewElevation==0),CODE:{
     [h: setState(NewOnesState,0,TokenID)]
     [h: setState(NewTensState,0,TokenID)]};{}]

Further prettiness; If the new elevation is zero, I want no elevation states to be displayed at all, so I now remove the new states that I just added.

    }
   ]

And here I close the FOREACH loop and the Code brackets that I started way up at the top.

This macro is useful any time you want to stick a visible number on a token for whatever reason. Now that I’ve got it working, I’ll be interested to see how often it will come up. My suspicion is that I’ll end up using it a lot.

As always, please let me know what macro questions you have or any requests for new macros. I love this stuff!

Note: All macros were generated with version 1.3.b66 of MapTool.

Campaign template for online use, including this and all of my other macros

Campaign template, formatted for projector use

Campaign properties for online use

Campaign properties for projector use

Download the elevation macro alone

The full elevation macro code:

[h: x=input("NewElevation|0|What is the token's new elevation?")]
[h: abort(x)]

[h: SelectedTokens=getSelected()]
[FOREACH(TokenID, SelectedTokens, " "), CODE:
 {[h: OldElevation=getProperty("Elevation",TokenID)]
   [if(NewElevation>99), CODE: {[assert(1==0,add("<b>Error</b>: Elevation cannot be higher than 99"),0)]}; {}]
   [if(NewElevation<0), CODE: {[assert(1==0,add("<b>Error</b>: Elevation cannot be lower than 0"),0)]}; {}]

   [h: OldTensDigit=FLOOR(OldElevation/10)]
   [h: OldOnesDigit=OldElevation-10*OldTensDigit]

   [h: NewTensDigit=FLOOR(NewElevation/10)]
   [h: NewOnesDigit=NewElevation-10*NewTensDigit]

   [h: OldOnesState=add("_", OldOnesDigit)]
   [h: OldTensState=add(OldTensDigit, "_")]

   [h: NewOnesState=add("_", NewOnesDigit)]
   [h: NewTensState=add(NewTensDigit, "_")]

   [h: setState(OldOnesState,0,TokenID)]
   [h: setState(OldTensState,0,TokenID)]

   [h: setState(NewOnesState,1,TokenID)]
   [h: setState(NewTensState,1,TokenID)]

   [h: setProperty("Elevation", NewElevation, TokenID)]

   [if(NewElevation<10),CODE:{[h: setState(NewTensState,0,TokenID)]};{}]

   [if(NewElevation==0),CODE:{
     [h: setState(NewOnesState,0,TokenID)]
     [h: setState(NewTensState,0,TokenID)]};{}]
    }
   ]

Running online D&D: Weekly prep

I’ve talked a fair amount on my blog about macros that I’ve programmed in MapTool for my online games and recaps of adventures that I’ve run, but I realized that I haven’t spent any time talking about the prep process. Preparing to run a game online has a lot in common with an in-person game with vinyl mats and minis, but it definitely has its differences.

1 week before the game

I figure out what I’m going to be running. For my longest-running online game, this is easy; we’re running EN World’s War of the Burning Sky campaign and have been for over a year. I do still need to make sure I’ve read far enough ahead in the campaign to know what’s coming in the broad sense, but I try to be good about staying ahead of things there.

For my other online games, this might mean picking out a one-shot game (like a Living Forgotten Realms adventure) or actually writing my own adventures (a topic for another blog post).

This is also the time that I reach out to the online players about any changes they need to make to their characters. For instance, if they’ve leveled up after the last session, I remind them to tell me what choices they’re making for their characters. This is a difference between an online game and an in-person game; online, I have to maintain the tokens for the PCs and add new powers, adjust stats, etc. I also talk about magic items that the party acquired in the previous session and see which PC is going to be using them (so I can update their tokens).

3 days before the game

I send out an email to the group, announcing that there will be a session at our usual time (6:00 PM Mountain Time on Friday night for me and the person in South Dakota; 5:00 PM for the person in California; 7:00 PM for the people in Indiana and Texas, 8:00 PM for the people in New Jersey and Florida, and 9:00 AM Saturday for the person in Japan), asking who will be able to attend. I usually get a couple of responses right away and the rest trickle in over the next couple of days. Sometimes I’ll get a “maybe” (there’s a possible schedule conflict, but they might be able to come – this is usually a “no” in the end). I have a total of seven players, and we usually have 4-5 show up each week. One of the seven is almost never there, and four are almost always there; the other two are there most of the time, but not all of the time. This works for us, though I know some DMs don’t like it if players are absent irregularly. This is why I have seven players! We can still game even if three people are unavailable.

1-2 days before the game

I do my actual prep work (sometimes I get this done earlier, of course). This involves a few things:

  • Updating PC stats if the players have sent them to me after a level-up
  • Updating PC treasure if the players have decided on what they’re using
  • Setting up the maps for the next few encounters (easy for War of the Burning Sky, since decent JPG versions of the maps are available)
  • Building monsters for the next few encounters using my handy-dandy monster construction macro

For some reason, I used to procrastinate more about the PC stuff than the map and monster stuff. When the PCs hit paragon tier, I actually canceled a session so I could use that four-hour time slot to work on their PC tokens. It’s gotten better since I’ve changed my PC properties to be easier to level up (defenses now scale automatically with level, for instance), but there was a time when I almost wanted to stop running online games just because of the extra layer of work on the DM to update PC tokens. It’s better now, though.

The new monster macro has made building the monsters way easier and actually more fun. I also enjoy the process of using TokenTool to create cool-looking monster tokens from images I find online. I’m sure lots of these images are copyrighted and such, but I’m only using them in my own game (this is part of the reason I don’t distribute lots of monster tokens on my blog – well, that and laziness).

The day of the game

Since our game starts at 6:00 PM my time, I go to work early so I can leave at 4:00 PM. It only takes me 15 minutes to get home, at which point I’ll chat with my wife briefly and help take care of household tasks (feed the cats, figure out dinner). This usually leaves me at the computer by around 5:00, giving me time for last-minute prep. If there are any monsters I haven’t done yet, I’ll try to put those together quickly. If I already have a good image for the monster, it tends to take about 5 minutes per monster type to assemble.

If all goes well, I like to spend the time from 5:30 onward re-reading the material I’ll be running that evening. War of the Burning Sky is a very story-heavy adventure, and I want to make sure I understand the various NPCs and the branching points of the tale so that it all makes sense during the game.

At 5:45, I start up the MapTool server so that my players can connect. It’s not at all unusual for one or two people to be ready to go right at that time, and we might chat a bit in the text window of MapTool until start time, or they might say, “Hi, I’m here, but I’m going to be busy with something else for the next few minutes.” I also might say, “Okay, the server is up, but I’m still preppping! Please talk amongst yourselves. I’ll give you a topic: A half-elf is neither a halfling nor an elf. Discuss.”

At 6:00, assuming we have at least 3 players on MapTool, I’ll start the Skype audio call so we can talk to each other… and away we go!

Game on!

After that, we play D&D for four hours. Honestly, the online experience is darn near as good as the in-person experience for me. We still get to know each other out-of-game and chat as friends. Role-playing still happens. Combat is still exciting – and pretty quick, too, thanks to the software handling a lot of the math. It’s a ton of fun, and while I also enjoy in-person games, my online game is my longest-running campaign by far.

I’ll talk in later posts about the process of running a session, but I hope this window into the online game prep process helps to show you what it’s like. Give online D&D a try sometime – it’s a ton of fun!