Healing Rotations(APLs)

Healing simulations on AMR are being upgraded today! This is a short tutorial on modifying healing rotations.

Healing rotations are more complicated than DPS rotations. This is because, unlike DPS, healers do not (and cannot) act at all times. Mana limits your actions. Healers will, in general, react to the state of their allies to decide what action to take. To achieve this in the simulator, we’ve added many new tools and functions at your disposal.

Healing Phases

The scripts used for healing simulation can now be organized into “Healing Phases” that help signal the type of raid damage that is predominant at the current point in the simulation. The phase types are AoE, Normal, Tank, and Light. There are two functions available in rotations that will be very useful:

This returns how long until the next phase of the specified type will occur. It returns 0 if you are currently in that healing phase. This allows you to do things like, start pre-HoT’ing targets X seconds before an AoE heal phase. Or, create a sub-rotation that only runs if you are in the AoE heal phase.

This tells you how long the next phase of the specified duration will last. As an example for how this is useful: imagine you are a Holy Paladin and there is a 10 second AoE phase coming in 10 seconds. You might want to use Avenging Wrath (a 20 second buff) early to top people up before the AoE occurs, but still have time left on the buff for after the damage hits. You can create a condition like:
Avenging Wrath if SecondsUntilHealPhase(Aoe) + NextHealPhaseDurationSec(Aoe) <= BuffDurationSec(AvengingWrath)

This condition will pass and execute Avenging Wrath once SecondsUntilHealPhase(Aoe) is <= 10 seconds. You will want to be wary of some nuances with conditions like this. What if Avenging Wrath is on cooldown until 5 seconds into the AoE healing phase? This condition will pass and use Avenging Wrath with only 5 seconds left in the AoE phase, at which point ally health is already getting topped up, so it would be a waste. You would want to add some protection for that into the condition:
Avenging Wrath if SecondsUntilHealPhase(Aoe) + NextHealPhaseDurationSec(Aoe) <= BuffDurationSec(AvengingWrath) and SecondsUntilHealPhase(Aoe) > 0

The second condition ensures that we don’t use Avenging Wrath if the healing phase already started.

Ally Health

In game, we most often decide who to heal based on how much health our allies have. By default, actions targeted at allies will only be cast on injured targets, but we often only want to cast spells on targets that are below a certain threshold of health. As an example, you aren’t going to cast Holy Word: Serenity on a target that is at 95% health. The following functions are available for use in rotations:

This returns the percent health of the current ally target. For example, to only execute an action on allies with less than 50% health, you would do:
AllyHealthPercent < 0.5

This finds the ally with the lowest health and returns their health as a percent.

InjuredAlliesInRadius(Spell Effect, Health Threshold)
Returns the number of injured allies in the radius of the specified spell who have health below the specified threshold. As an example, if you only want to cast Prayer of Healing if there are at least 5 targets below 75% health in the radius of the spell, you would do:
Prayer of Healing if InjuredAlliesInRadius(PrayerOfHealing, 0.75) >= 5

Some nuances to this: Some spells have to be implemented such that the AoE component of the spell has a different spell effect name than the spell itself. For example, Vivify is targeted at a specific ally and then heals 2 more targets around that target. So, “Vivify” is the heal on the target you choose and “VivifyAoE” is the heal on the 2 other targets. To use this function, you would use it like:
InjuredAlliesInRadius(VivifyAoE, 0.75) >= 3

Check the wiki page for AoE spells if you are unsure which spell effect to use. Also, you can look at our starter or default rotations for examples on how to use various spells.

Another nuances is that the radius of the spell is sometimes relative to the current ally target, or sometimes relative to the player. This is defined in the spell itself. Vivify heals in a radius around the target. Halo heals in a radius around the player. Keep this in mind when using this function to check allies.

InjuredAlliesInExplicitRadius(Radius, Center on Self, Health Threshold)
This is just like InjuredAlliesInRadius, except that it does not depend on the radius of a particular spell effect. You can instead specify a radius in yards to check. The second parameter specifies if you should check that radius around the player or current ally target. True is player, False is current ally target. In general, you don’t need to use this in rotations - its main purpose is for use with the AI healers in the boss scripts, since you wouldn’t want to reference any particular spell effect. If you are simulating a Mistweaver and the AI healer is checking the radius of, say, Prayer of Healing - that would cause an error since Prayer of Healing wouldn’t have been loaded into the current simulation.

Ally Target Selection

Once you get into healing simulation and making rotations… you will start to realize that there is another piece required to make these rotations function. I can make a condition like the following:

Vivify if InjuredAlliesInRadius(VivifyAoE,0.75) >= 3

As a player, I am going to pick one of the lowest health targets to cast this on. So, we need a way to make the simulator check targets in a “smart” order, like a player would.

By default, the “Current Ally Target” is always the player, or, Self. If you do nothing other than enter the above condition on the spell action, it will only check that condition on the player, and only cast the spell on the player. To enable the ally target selection options, you need to check the “Cast on Allies” checkbox under the action:

This brings up the ally targeting options:

Ally Targets
This is a list of the ally target types you want to check for casting this spell. Leaving it blank will cause the condition to be checked against all ally targets. The types are: Tank, OffTank, Melee, Ranged, Self, and None. Allies can only have one type. So, even if you are a Holy Priest standing at range, you are only “Self” and will not be included in the “Ranged” target type.

Injured Filter
By default, only injured allies are checked. You could change this to check All allies or you could also set it to check only uninjured allies.

Search Order
This is the bread and butter of the healing simulator. By default, the simulator will look for the most injured ally and try the condition on that ally first. In general, this is how we play. It is also absolutely essential to avoid running out of mana very early on in fights. The options are:

Most Injured
Find the most injured ally and check them first. A note on this: Human players will not be able to perfectly check/pick the lowest health ally every time. Raid frames aren’t going to allow you to perfectly discern if a player at 60% health is lower than a player at 61% health (or even 65% health), as an example. To avoid the simulator from being too perfect and running into strange results, we did the following: Start with the most injured ally, and walk in order to the least injured. We gather up all allies within 10% HP of the most injured ally and return those in random order, then repeat. This is to help simulate the “heat of the moment” where you just have to glance at your raid frames and pick someone low. Our testing shows that this built-in fuzz doesn’t have a negative impact on the healing, and yields our desired result of avoiding odd behavior due to inhumanly perfect target selection.

Check allies based on proximity. Allies at the same distance are randomized.

Check allies in random order.

Type then [Most Injured][Closest][Random]
If you specify ally types to check in the Ally Targets field, you can prioritize targets by type first, then by any of the three search methods. As an example, you could set the Ally Types to: “Ranged, Melee” and then set the Search Order to “Type then Most Injured”. This would check all the ranged allies in order of how injured they are, then check all the melee allies in order of how injured they are.

Another good use for the “Type then…” search type is to avoid creating multiple actions in your rotations. An example would be if you wanted to prioritize using Power Word: Shield on ranged allies before, say, an Orb of Destruction on Krosus, then Tank and Off Tank, then melee. And, you want to do this even if the target is not injured. You could achieve this by doing the following:

Using Healing Phases, Ally Health, and Ally Target Selection, we can now create a “rotation” that plays how humans play in-game. Hit me up at any time for further assistance, tips, pointers, etc. You can also check out the public rotations that I create and/or curate for examples on how to create rotations for your spec.

Questions are welcome in this thread, but I will be moderating it to remain strictly on the topic of how to edit/create healing rotations. If you want to provide feedback on the healing simulations in general, we have topics for that as well!