MapTool Macros – Multi-Target Attacks

Edit 7/19/2010: Since I first wrote this post, I have learned more about MapTool macros, and you can find the improved information at this post.  However, I’ve left the original post below for posterity – and as an example of how to learn how to improve your macro writing, rather than just giving the finished product.

Building on my recent post in which I described how to create a rather complicated macro to accurately run a basic attack, it’s time to talk about a somewhat more complicated attack – one that targets multiple enemies.  Some characters in D&D Fourth Edition might only rarely have one of these attacks, but a controller like a Wizard or Druid or even a character like Barbara’s Swordmage with her close burst 1 attacks will run into these a lot.

If you have an attack that targets every enemy within a given burst, you could program the attack a few different ways.  The simple approach would be to click the button once per enemy, and only use the damage roll from either the first or the last click (since in D&D 4e multi-target attacks generally only have a single damage roll).  Alternatively, you could create separate macro buttons for the attack (which you would click once per enemy) and the damage roll (which you would only click one time at the end).  You might prefer this latter approach to begin with – separate attack rolls and damage rolls – even for single-target attacks, since lots of players do it that way in real life (only rolling damage if they hit).

My preferred approach is a little more elegant, I think.  It’s a macro that you click one time for the multi-target attack, which then asks you how many targets you’re attacking.  It then gives you an attack roll for each target and a single damage roll.  It also handles telling you the the max damage amount for any attack rolls that are critical hits.  The macro essentially starts with the basic attack macro from my earlier post and builds from there.

First, a recap of the basic attack macro:

[h: SkillMod=FLOOR((getProperty("Strength")-10)/2)]

[h: DamageDie=getProperty("MeleeDamageDie")]
[h: HalfLevel=FLOOR(getProperty("Level")/2)]
[h: Prof=getProperty("MeleeProficiency")]
[h: Enh=getProperty("MeleeEnhancement")]

[h: AttackBonus=SkillMod+HalfLevel+Prof+Enh]
[h: DamageBonus=SkillMod+Enh]

[h: d20roll=d20]
[h: DamageRoll=roll(1,DamageDie)]

[h: AttackRoll=d20roll+AttackBonus]
[h: MaxDamage=DamageDie+DamageBonus]
[h: RegularDamage=DamageRoll+DamageBonus]

<b>Melee Basic Attack</b><br>
Attack: [d20roll] + [AttackBonus] = <b>[AttackRoll]</b> versus AC<br>
[if(d20roll==20), CODE:
{<font color=Red>--CRITICAL HIT--</font><br>
Damage: [DamageDie] + [DamageBonus] = <b>[MaxDamage]</b> damage
};
{Damage: [DamageRoll] + [DamageBonus] = <b>[RegularDamage]</b> damage}
]

This macro figures out the attack bonus based on the appropriate skill modifier, weapon proficiency bonus, weapon enhancement and character level.  It gets the weapon damage die, and it calculates the damage bonus based on the skill modifier, weapon enhancement and character level.  It rolls the attack and the normal damage as well as calculating the max damage for a crit.  It tells you what the result of the attack roll was.  If it was a crit, it tells you so and gives the max damage amount.  If it wasn’t a crit, it just tells you the rolled damage amount.

The guts of the multi-attack macro will be the same.  It gets the appropriate skill modifier (which may be something other than Strength, of course), proficiency, enhancement, level and damage die.  It calculates regular and max damage in just the same way.  The difference only comes on the attack roll – it has to generate one attack roll per target.

To do this, we define a variable with no value – and MapTool will then ask the user to input a value.  Here’s what this part of the macro looks like (for the Sword Burst attack from Barbara’s Swordmage named Surina):

<b>Sword Burst</b><br>
[h: x=NumberOfTargets]
Attacking [x] adjacent
[if(x==1,"enemy.","enemies.")]
<br>

When MapTool executes the line that reads [h: x=NumberOfTargets], it will pop up a box that looks like this:

Input BoxThe user then enters a number.  If you enter something else (like a word), you’ll get an error message.  Assuming you do enter a valid number – let’s say 4 – MapTool will store this as the value of x. It then confirms for the user the number that was entered for x and has a little extra piece of code to say that you’re attacking “5 enemies” but “1 enemy” (the first version I wrote could have you attacking “1 enemies,” which annoyed me).

It then moves on to the following bit of code:

[h: i=1]
[WHILE(i <= x, "<br>"), CODE:
{
...MORE CODE WILL GO HERE IN A MINUTE...
}
]

As you may have guessed, where you see “…MORE CODE WILL GO HERE IN A MINUTE…” we will soon be putting more code.  First, though, I wanted to show you the structure of this loop.  I begin by introducing a new variable, i (for “iteration”) and set its value to 1.  The first open bracket before the word “WHILE” is paired with the closed bracket at the very end of the code above.  The open curly bracket on the line below the WHILE statement is paired with the closed curly bracket two lines below it.  The expression [WHILE(i <= x, "<br>", CODE: {...}] means:

  • Check to see if i is less than or equal to x
  • If i is indeed less than or equal to x, then do whatever is in the {…} section (note that you had better have something in the {…} section that increases the value of i!)
  • Put in a line break (that’s the “<br>”)
  • Come back to check and see if i is still less than or equal to x
  • If so, repeat what’s in the {…} section, then insert another line break
  • Do this process up until i is greater than x, then move on past the closed square bracket

This is what’s often known in other programming languages as a DO loop.  MapTool also has a FOR…NEXT loop function as well as a COUNT function that can also handle looping through lines multiple times, but this WHILE loop is the easiest to explain (the numbering on the other versions gets a little weird).

Now that we’ve set up the structure of the loop and we know it will keep repeating until we don’t want it to repeat any more, we need to put the guts of the attack roll and its result in between those curly brackets.  That means we need to roll an attack and print out the result of that attack roll first:

[h: d20roll=d20]
[h: AttackRoll=d20roll+AttackBonus]
Target [i]:   [d20roll] + [AttackBonus] = <b>[AttackRoll]</b> versus Reflex

Simple enough.  The only thing different here from our basic attack macro, aside from the fact that this particular power goes after Reflex instead of AC, is that we have it print “Target [i]” instead of just “Attack” before the attack roll.  The [i] in brackets will show whatever the value of i currently is – starting at 1, and going up from there (Target 1, then Target 2, and so on).

Next, we put in the same type of code that we saw in the basic attack to check for a critical hit, only this time we’ll put a line with the max damage right next to the attack result (no line break) since the crit will only apply to that particular attack roll:

[if(d20roll==20), CODE:
{<font color=Red>  --CRITICAL HIT--</font> [DamageDie] + [DamageBonus] = <b>[MaxDamage]</b> force damage};{}
]

Finally, we need to increase the value of i by 1 so that the loop will eventually end (otherwise it would repeat infinitely with i=1):

[h: i=i+1]

At this point, we’ve reached the end of the looping code, so we have the closed curly bracket and the closed square bracket to end the loop.  We then move on to printing out the regular damage roll, with a couple of line breaks before it:

<br><br>
Damage: [DamageRoll] +[DamageBonus] = <b>[RegularDamage]</b> force damage

Putting it all together, the macro looks like this:

[h: SkillMod=FLOOR((getProperty("Intelligence")-10)/2)]

[h: DamageDie=6]
[h: HalfLevel=FLOOR(getProperty("Level")/2)]
[h:Prof=getProperty("MeleeProficiency")]
[h: Enh=getProperty("MeleeEnhancement")]

[h: AttackBonus=SkillMod+HalfLevel+Enh]
[h: DamageBonus=SkillMod+Enh]

[h: d20roll=d20]
[h: DamageRoll=roll(1,DamageDie)]

[h: MaxDamage=DamageDie+DamageBonus]
[h: RegularDamage=DamageRoll+DamageBonus]

<b>Sword Burst</b><br>
[h: x=NumberOfTargets]
Attacking [x] adjacent
[if(x==1,"enemy.","enemies.")]
<br>
[h: i=1]
[WHILE(i <= x, "<br>"), CODE:
{
[h: d20roll=d20]
[h: AttackRoll=d20roll+AttackBonus]
Target [i]:   [d20roll] + [AttackBonus] = <b>[AttackRoll]</b> versus Reflex
[if(d20roll==20), CODE:
{<font color=Red>  --CRITICAL HIT--</font> [DamageDie] + [DamageBonus] = <b>[MaxDamage]</b> force damage};{}
]
[h: i=i+1]
}
]
<br><br>
Damage: [DamageRoll] +[DamageBonus] = <b>[RegularDamage]</b> force damage

You’ll note that it’s a little different from the basic attack macro in that it doesn’t check the character sheet to get the size of the weapon damage die, since the damage die is defined in the power itself.  That’s typical for a lot of multi-target attacks in D&D 4e.  This particular macro can have pretty much any value you want for NumberOfTargets (it’s fun to run with 50 attacks, for instance), but for a number such as 4, the output will look something like this:

Sword Burst
Attacking 4 adjacent enemies.
Target 1: 20 + 4 = 24 versus Reflex –CRITICAL HIT– 6 + 3 = 9 force damage
Target 2: 3 + 4 = 7 versus Reflex
Target 3: 17 + 4 = 21 versus Reflex
Target 4: 1 + 4 = 5 versus Reflex

Damage: 5 +3 = 8 force damage

I hope this macro is helpful for you. You can use the same structure for monster multi-attacks, of course, though I wouldn’t bother with looking up attack and damage bonuses from the monster’s character sheet since there’s not much point in HAVING a monster character sheet!  Just hard-code those values in.

I’m always interested to hear about other people’s macros, and I’m also interesting in helping to program other MapTool macros that I haven’t thought of yet.  If you have macros of your own to share or would like some help in writing new macros, please leave a comment below or send me a message via email.

2 thoughts on “MapTool Macros – Multi-Target Attacks

    • @K – Thanks for the comment! I do indeed know about the MapTool wiki – it’s my own main reference for learning about the macro language. I put all of the content here on my blog in order to help someone who’s brand-new to macro writing learn the process – you can see my thinking, my learnings, the “why” behind what I do. But you’re right that a lot of it duplicates the kind of thing that’s already on the wiki.

      I never considered actually putting any of my own macros up there, mainly because I know that lots of people are far more experienced than I am at MapTool macro writing, and I figured those are the people who should be posting to the wiki. But maybe you’re right – maybe it WOULd make sense for me to include my macros in the cookbook section of the wiki or something like that.

      Thanks for the suggestion!

Leave a Reply