Writing macros, a series of lessons (4 of…)

Today, we’re going to run in a slightly different fashion. And, in fact, I’m going to tell you the big things we’re going to do. We’re going to learn the basics to figuring out why our macro isn’t working. We’re going to learn a bit about a couple of potential frustrations – spellsequence and target. And while we build a pretty good panic button we’ll pick up a few other odds and ends.

Back in the comments of lesson 2, Kestrel posted a nonfunctional macro asking for help. While I gave the basic solution, I asked for and received permission to humiliate him with his failure (grin). More seriously, I asked to use it as the basis of a lesson. What we’re going to do is start here with the non-functional macro, figure out where it’s broken, and then improve it a bit. To begin, then, we need the macro he posted.

And Kestrel’s complaint is that when he uses the macro, it always casts PW:S, never Fade. As it happens he has more problems but hasn’t yet discovered them. (In fairness, yes he has, but not at the time he posted this.)

The first thing in troubleshooting is to remember that – with the exception of the #show/#showtooltip lines – every line of a macro is independent. Which means every line AFTER the trouble point can be ignored. The second thing to remember is that since preceding lines can trigger events outside the macro which make it impossible to run other events (for example, trigger the GCD), we have to check every line BEFORE the problem line. So, let’s begin.

#showtooltip Fade.

Inherently there is nothing wrong with this – it’ll do what it’s supposed to do. Now thanks to the previous lesson we know that it will show fade regardless of whether our spellsequence is pointing to fade or PW:S, because the first parameter it finds to which an icon is keyed is an unconditional fade – the one in this line. Let’s go ahead and tweak that to improve by removing fade. This doesn’t solve our problem, but it’s an opportune improvement. So the next line is:

/stopcasting

A command with no parameters, spelled right. Yes, spelling matters to the computer. And while the line does have an impact on the UI, the impact will not deny what’s coming. In fact… as Kestrel’s comment says, it makes it possible. Let’s discuss this for a moment, ok?

Stopcasting means, “If I’m in the middle of casting a spell (channel or casting time), stop.” Up till 2.3 it was pretty much common in every spell macro used by “serious” casters as it made it possible to work around lag. 2.3 allegedly fixed it, and blizzard IMPLIED that continuing to use stopcasting in that fashion would actually make spellcasting LESS efficient. At this time the jury is still out on both those elements. Neither of those issues is applicable in this case, however, as this is EXACTLY the sort of thing for which the command exists. For the ‘speedcast’ people… we’ll have to wait and see. It’s a digression I’m not going to take here. (gasp)

It’s now obvious that our first intuition was right – the problem is in the castsequence(edited to correct) line. This is not surprising – stopcasting is not completely intuitive. First, of course, the line:

/castsequence reset=30 Fade, [target=player] Power Word: Shield

Now, castsequence needs explained before we go further. It is saying, “Cast the following list of spells, in order, on a loop.” OK, there are a pair of gotchas here, one of which is obvious, one of which is implied by what I just wrote.

First, there’s the loop. When your button-push reaches the last spell, then the next time you push the button you get the first spell in the list. It will not back up, it will not sit forever on the last spell, it loops.

Second… castsequence has a memory. It remembers the last spell it cast in this macro, whether that was 5 seconds ago or 5 DAYS ago. To make this little problem more obvious, let’s assume a typical shadowpriest sequence: Shadow word: pain, mindblast, mindblast, mindblast. You do your little battle and press the button six times – sw:p, mb, mb, mb, sw:p, mb. You run to the next battle and start pressing, and you get – mb, mb, sw:p… waitaminute – you need SW:P FIRST. Yep, but the sequence has the second MB up, so…

This is where the conditional of ‘reset’ comes into play. Now back when I was discussing conditionals, I said there was one exception to the rule that conditionals are in square brackets. You have just met it – no brackets. But having met it, let’s look at it, and at what it’s doing.

Reset is a conditional that tells castsequence, “If any of the following are true, start over from the beginning of the list.” Our shadowpriest is happy – provided he can get something that works. Fortunately for him, he can. We can choose any (or all, or some) of the following for resets: If we’re pressing Alt, Shift, or Ctrl keys; if we choose a new target; if we get into combat; and if a certain number of seconds has passed. Actually, while all the others are clear, that last is what causes most problems. See, the description is incomplete even though that’s what’s written and assumed. The full phrase needs to be: If a certain number of seconds has passed since the last time the macro was triggered.

What this means is that if Kestrel’s code was “working” he might have a rude surprise waiting. He casts fade, runs to tank, tank kills mob, battle gets finished. And as the new battle starts a mob targets him. Fade is up – has been for 10 seconds, he ‘panics’ – and poof, PW:S instead of fade. He’s only 25 seconds since the last use. We’ll be fixing – no, IMPROVING this in a bit. It’s not the problem, though.

What all this discussion should have done, though, is cued you to the problem. Remember the macro sequence: command, all conditionals, all parameters. Look at this line, and what do you see? command, conditional, parameter, conditional, parameter. The macro interpreter assumes that everything between known conditionals are just miswritten conditionals, which are inherently false for tests. In this case, there’s no effect other than “fade” doesn’t happen.

So obviously, fade needs moved. Not obviously, there’s a further bit of syntax issue. Specifically, for castsequence the reset is always the last conditional. Putting other conditionals after may give you problems. With this knowledge, we can fix the line. And with our other changes, we now have:

That’s lots better – at first. Unfortunately, as I said before, there’s another problem. Targetlasttarget won’t do what Kestrel wants it to do. Instead, here’ s what’s going to happen at some point.

Kestrel has been healing the tank, and once in a while healing the rogue in the party. He’s got the tank targeted when suddenly he has to hit the panic button. He gets out of trouble with the fade, starts a greater heal as the tank is hurting, and the rogue gets a massive overheal.

The rogue? wtf?

Remember, except where the line changes the situation outside the macro, each line is independent. That bit about target=player in the castsequence ONLY APPLIES to the castsequence – it doesn’t change the “real” target. Targetlasttarget, however, did. And the last target prior to the current tank was… the rogue. Which means the fix to THIS particular problem is to delete the line. And we have a working panic button:

Now, I’m going to do some more improvements, but first I need to back up and digress. I’ve casually written that word “target” and not said a word about the fact it’s popping up in a couple of places that aren’t the same. Didn’t I say that for the computer a word means what it means and nothing else? Well, meet a place where we taught our computer “context”.

We can put “target” in command, conditional, and parameter. It’s a peculiar thing in conditional – in its own way as bad as reset, but worse because it can show up in so many other places. Let’s get the command and parameter parts over first.

Target (or targetfriend or targetlasttarget or…) means, “Make {option} my new target.” If I put something in the parameter and it doesn’t match what’s in range, the macro interpreter will make a ‘best guess’ using the first letters as match.”

Target in parameters is always just a text string. It does NOT mean “my current target.” If I use, for example:

/target target

and there is a mob in range named Tarheel… my macro is going to try and act against Tarheel instead of my current target. Which, if I’m using an attack and tarheel is the pet of my group’s hunter, is going to be just a little bit embarrassing.

Instead, we’re back to the parameter. And here’s where target gets annoying. It resembles marklar, only with a German twist. Let’s dig a bit.

When target is in a conditional, it is always followed by an equals sign (=). It is NOT a true-false test, which would technically mean it’s not a conditional, but since it’s within brackets and it’s got to sit in with the rest of the conditionals, that’s what I’m calling it. Seriously, however, it’s a limiter. It is saying, “the command will be executed against THIS target.” And THIS target is what is on the right side of the equal sign (=) . It can be a specifically named target (though this might cause a couple of problems) or it can be player – meaning The player, YOU – or a few other things. One of those things can be target, or (here comes that German twist) targettargettargettarget… Yes, you can go on and on, and now I need to explain what I mean by ‘German twist’. German is one of the languages where words exist that are basically a bunch of smaller words strung together – and the final word can be huuuuuge. There are restrictions and rules, of course, in both German and in this case. I’m not teaching German, however. So… Every “target” to the right of the equals sign is actually “Target of the preceding <noun>” If it’s the first noun, the preceding (and assumed) noun is the player. So what I’ve said in that string is, “my target’s target’s target’s target’s…” It is very rare you’re going out that far, but you can. There is a limit, of course. If not on the number of “targets” (I honestly do not know), there’s ALWAYS the 255 character limit to macros.

One last thing about “target” for now (yes, there’s more, but not now). There is a shorthand that can be used in some circumstances that always means ‘my target’ – or rather, when the line is executed it will be replaced with the name of your current target. If your use can’t use the name of the current target, this won’t work. Typically you find it in parameters of things, or where the only option for the command is <target>. The shorthand is %t.

And with that, we’re off to improving the existing macro – with a couple of additional lessons along the way. The first thing is… this is a panic button. I have one more thing I personally like if I’m panicing – my last-ditch “Oh, pretty please, go ANYWHERE else” attempt – Psychic Scream. I’m going to add it to the end of the castsequence chain.

But I’ve also mentioned I don’t like the renew timer. And with psychic scream it’s potentially even worse. To be specific consider if I toss my fade, 25 seconds later I toss the shield as already mentioned. And 25 seconds after that, or 50 seconds after I faded, I cause the mob that’s attacking me – and up to four other mobs we’re in the midst of fighting – to go running around picking up MORE adds. Ummm…. oops?

Picking the right renew time is something of an art, especially when there are several items in the chain. In this particular situation, though, I’m going to use the cooldown of the first spell in the chain. Sure, it could still get me into trouble. On the other hand, between the faster cooldown and the fact my icon shows what spell’s ready, I should be a little better off. So my first change makes the macro:

But wait, there’s more. See, as Kestrel noted in a later comment, this is only good in groups. What if we’re solo? We’ve just wasted mana and cooldown – in a panic situation no less – on fade. Well, we can do two fixes.

First, we can fix this so it’s ONLY happening when we’re in groups. Which means we’re adding the conditional [group] to the line. Except not quite like that. See, if we put it in its own brackets, that gets an independent test. Castsequence is weird, and target is weird, so it may behave exactly as we intend. But to be certain, we’re going to put it in the same bracket set as the target statement, and separate them with a comma. So we’re going to have [group,target=player] in there.

By the way, group can be a further conditional – we can say “only when party” and “only when raid” by following group with a conditional AND either party or raid. Anyway…

That makes our panic button work do fade only when we’re in a group. But when we’re solo the ONLY thing that happens is our party (what party?) gets the “help me” message. Let’s make this panic button useful for solo, too, shall we?

In solo, I would normally cast shield, then renew, and then psychic scream. That’s just me, adjust to your flavor. The point is I have a solo version of my castsequence, and the line goes:

And I’m going to stick that right after the other castsequence. But… I’m beginning to get worried about the total length of 255 characters. (Actually I’m not, but I’ve got another lesson coming.) Remember way back in an earlier lesson, I told you that the semicolon (;) is a shorthand? Technically it’s a boolean ‘OR’ for the command. In practical terms it is a replacement for “{newline}same command”. In other words, exactly our situation. Our new line is (this is all ONE LINE, reader):

But there’s that bit at the end – the bit that tells the party (that’s what /p does, sends the rest of the line into party chat) that you’re in trouble. If you’re in a raid this is still sorta ok – after all, you’ll still be part of a party. Just hope it’s not all the healers grouped together… ummmm.

We have a bunch of options. Now before I start listing them, I want to raise a human condition. Remember first that this message is going out every time you tap the button. So if you panic three times (that is, fade/shield/scream), the message goes out three time — and in a short handful of seconds. “So?” you ask? OK, put the shoe on the other foot.

What’s your feeling about the “Heal Me” macros other players use?

Yeah, thought so. This is possibly a little different – possibly – but let’s be aware of the issue, ok? Assuming these are OK, let’s look at covering all the bases.

Now the first thing we could do is repeat the same message, but repeat it for /s (or say) and /ra (or raid channel). Three lines total. hmmm.

OR… we can use /rw. This slaps our “help me” message right up in front of group, WITH a sound, regardless of whether we’re in party or raid. remember the human problem, of course. And that still leaves us with the need to “say” when we’re not in party. Which leads me to my preferred solution.

/v help

Use this in replacement of the existing /p message. Yes, you aren’t able to modify it so it says who needs help and why, but it DOES do two things. First, it “says” in chat – which even your party and raid sees. Second, there’s a corresponding sound – pretty plain, at that. “Help Me.”

Like this:

Related

9 Responses to “Writing macros, a series of lessons (4 of…)”

Hi Kirk, this is my first post here. I love the blog, and these macro posts are really solid. Two things:

First, you wrote:

“It’s now obvious that our first intuition was right – the problem is in the stopcasting line. This is not surprising – stopcasting is >not completely intuitive. First, of course, the line:”

I’m confused – did you mean to refer the castsequence line? I’m exhausted after a long work week, so please forgive me if I’m just not reading this correctly.

Secondly, is it possible to attach different /s or /p text to different conditions? For example, take the Fade/Res macro you put together in part 3. Could we work the macro so that is attached to the [nocombat] Resurrection and is attached to the [combat] Fade? Or does the ‘no conditionals for insecure commands’ rule make this impossible?

1 – yes, castsequence line. I’ll get an edit on that to fix it, thanks.

2 – no, you cannot put conditionals on chats. In macros. Now when we get to scripting — and that’s going to be a bit — there are some “you can’t” things that suddenly become, “well, if you’re willing to do THIS…”

Great treatment here, Kirk…I definitely learned some things! And now I have a very nice “Your healer is on fire; if you don’t wanna wipe, douse me!” macro. But I think I’ll leave out the Psychic Scream (for now).

Once the series is complete (and maybe before) I’ll definitely hype it on the Aerie.

Kirk, great job. Your post explained more about macro syntax to me than 3 billion forum posts. I have to admit though, at one point while reading, I fully expected some Perl or C code to come into play! Would have been interesting..
Keep up the good work

thx a laot for doing this fine piece of work (and publishing).
i think, Your approach is really fine, to explain some basics and then take an almost^^ working little piece of code and do some improvements and bug fixing along with decent explanations on WHYs and WHY_NOTs.

About…

I've experience with many priests of many races and specs on several servers. The more I play, the more I realize how much I do not yet know. This is a share of some of what I have learned.
My main these days is Zingiber on the Undermine server.