Brian Koponen

Programming and Tech Tips

Custom Paladin Macros for Vanilla World of Warcraft

People often joke about how Paladins can be boring to play in Vanilla World of Warcraft. As such, I think they often get overlooked when it comes to building macros. I created these macros to add some much needed functionality, especially for healing and buffing party members.

These macros are made for the 1.12.1 client version of World of Warcraft and make use of these addons extensively:

Also, many of these macros use modifier keys to alter their functionality. You may want to switch those modifier keys depending on your preference. Those functions are:


Macro Tips

Action Bar Range and Cooldown

A really handy macro trick is to make the first line:

/run --CastSpellByName("Spell")

This will give the macro the cool down and range check on your action bar of the spell you put in the quotes. This doesn't actually cast the spell because the double dash is used as a comment delimiter and prevents the rest of the line from being executed. However, the World of Warcraft client simply looks for the first CastSpellByName reference, even if it is in a comment, to decide how the cool down and range check will be determined.

Target Nearest Enemy

This is a useful line to have in many macros. It simply targets the nearest enemy if you don't already have a target.

/run if GetUnitName("target")==nil then TargetNearestEnemy() end


Attacks

Auto Attack

This macro automatically targets the nearest enemy if you don't have a target and starts the auto attack. Using this repeatedly won't turn off your auto attack. Hold shift to turn off your auto attack.

/run --CastSpellByName("Attack")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if( IsShiftKeyDown() ) then stopAttack() else castAttack() end

Exorcism

This will cast Exorcism only if the target is Undead or a Demon.

/run --CastSpellByName("Exorcism")
/run if isUndead() or isDemon() then CastSpellByName("Exorcism") end


Seals

These will cast Judgement if a Seal is already active before casting the Seal itself.

Seal of Command

/run --CastSpellByName("Seal of Command")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if not isSealActive() or IsShiftKeyDown() then CastSpellByName("Seal of Command") else castJudgement() end
/run castAttack()

Seal of the Crusader

/run --CastSpellByName("Seal of the Crusader")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if not isSealActive() or IsShiftKeyDown() then castSotC() else castJudgement() end
/run castAttack()

Seal of Righteousness

/run --CastSpellByName("Seal of Righteousness")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if not isSealActive() or IsShiftKeyDown() then castSoR() else castJudgement() end
/run castAttack()

Seal of Light

/run --CastSpellByName("Seal of Light")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if not isSealActive() or IsShiftKeyDown() then castSoL() else castJudgement() end
/run castAttack()

Seal of Wisdom

/run --CastSpellByName("Seal of Wisdom")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if not isSealActive() or IsShiftKeyDown() then castSoW() else castJudgement() end
/run castAttack()

Seal of Justice

Unlike the others, this macro will cast Seal of Justice before casting Judgement. This is designed to quickly stop mobs running away from you.

/run --CastSpellByName("Seal of Justice")
/run if GetUnitName("target")==nil then TargetNearestEnemy() end
/run if isSealOfJusticeActive() then castJudgement() else castSoJ() end
/run castAttack()

Combos

You can easily make combos out of the seals using the macros above. As you activate this macro, it will cast Seal of the Crusader, Judgement of the Crusader, Seal of Command, then Judgement of Command.

/run if buffed("judgement of the crusader","target") then RunMacro("Command") else RunMacro("Crusader") end


Blessings

This macro makes it trivial to keep all of your party members properly Blessed. Every 5 minutes, just spam this macro until everyone in your party gets their Blessing applied. This is designed to pick a Blessing based on the class of the party member.

Macro:

/run --CastSpellByName("Blessing of Wisdom")
/run castGroupBlessing()

Extended Lua:

You can easily change which Blessing will be cast in the blessingForUnit() function. The Blessing returned when there is no unit targeted is the one that will be cast on yourself.

The available Blessing that can be used are:

function blessingForUnit(unit)
  local unit = unit or "target"

  if not UnitExists(unit) then
    return LOCALIZATION_ZORLEN.BlessingOfMight
  end

  if isDruid(unit) or isHunter(unit) or isPriest(unit) or isMage(unit) or isWarlock(unit) then
    return LOCALIZATION_ZORLEN.BlessingOfWisdom
  end

  if isPaladin(unit) or isWarrior(unit) or isRogue(unit) then
    return LOCALIZATION_ZORLEN.BlessingOfMight
  end
end

function castBlessing()
  local SpellName = blessingForUnit()
  local SpellID = Zorlen_GetSpellID(SpellName)
  if not Zorlen_checkCooldown(SpellID) then
    return false
  end

  if (UnitIsFriend("player", "target") and not UnitIsUnit("player", "target") and not Zorlen_checkBuffByName(SpellName, "target")) then
    CastSpell(SpellID, 0)
    if SpellIsTargeting() then
      SpellStopTargeting()
      return false
    end
    return true
  end

  if ((not UnitExists("target") or not UnitIsFriend("player", "target")) and not Zorlen_checkBuffByName(SpellName)) then
    if (UnitIsFriend("player", "target") and not UnitIsUnit("player", "target")) then
      retarget = UnitExists("target");
      TargetUnit("player");
    end
    CastSpell(SpellID, 0)
    if (retarget) then
      TargetLastTarget();
    end
    if SpellIsTargeting() then
      if SpellCanTargetUnit("player") then
        SpellTargetUnit("player")
      else
        SpellStopTargeting()
        return false
      end
    end
    return true
  end

  return false
end

function castGroupBlessing()
  if not castBlessing() then
    local NumMembers = GetNumPartyMembers()
    local counter = 0
    local group = "party"
    local PET = ""
    local PLAYER = "player"

    while counter <= NumMembers do
      if counter == 0 then
        u = PLAYER..""..PET
      else
        u = group..""..PET..""..counter
      end
      if UnitExists(u) and UnitIsVisible(u) and not UnitIsDeadOrGhost(u) then
        if not UnitIsUnit("target", u) and not UnitIsUnit("player", u) then
          TargetUnit(u)
          if castBlessing() then
            TargetLastTarget()
            return true
          end
          TargetLastTarget()
        end
      end
      counter = counter + 1
    end
  end
end


Healing

Group Holy Light

This will cast Healing Light on the party member with the lowest health, without losing your current target. If you already have a friendly target, it will cast Holy Light on them directly, so you can still prioritize your healing.

If you are in combat, it will switch to the Concentration Aura to prevent spell pushback.

Macro:

/run --CastSpellByName("Holy Light")
/run if Zorlen_inCombat() and not isCAA() then CastSpellByName("Concentration Aura") end
/run castGroupHolyLight()

Extended Lua:

function castHolyLight(Mode, RankAdj, unit)
  if Zorlen_isMoving() then
    return false
  end
  local SpellName = LOCALIZATION_ZORLEN.HolyLight
  local SpellButton = Zorlen_Button[SpellName]
  local LevelLearnedArray={1,6,14,22,30,38,46,54}
  local ManaArray={35,60,110,190,275,365,465,580}
  local MinHealArray={39,76,159,310,491,698,945,1246}
  local MaxHealArray={47,90,187,356,553,780,1053,1388}
  local TimeArray={2.5,2.5,2.5,2.5,2.5,2.5,2.5,2.5}
  return Zorlen_CastHealingSpell(SpellName, ManaArray, MinHealArray, MaxHealArray, TimeArray, LevelLearnedArray, Mode, RankAdj, unit, SpellButton)
end

function castGroupHolyLight(pet, Mode, RankAdj)
  if Zorlen_isMoving() then
    return false
  end
  local SpellName = LOCALIZATION_ZORLEN.HolyLight
  if UnitExists("target") and castHolyLight(Mode, RankAdj, "target") then
    return true
  elseif not Zorlen_IsSpellKnown(SpellName) or UnitMana("player") < 35 then
    return false
  else
    local u = nil
    local counter = 1
    local notunitarray = {}
    if Zorlen_isCasting(SpellName) then
      u = Zorlen_GiveGroupUnitWithLowestHealth(pet, 0, nil, Zorlen_CastingNotUnitArray)
      if u and Zorlen_CastingUnit == u then
        return false
      elseif not u or Zorlen_CastingUnit then
        SpellStopCasting()
        return true
      end
        return false
    elseif Zorlen_checkCooldownByName(SpellName) then
      while counter do
        u = Zorlen_GiveGroupUnitWithLowestHealth(pet, nil, nil, notunitarray)
        if u then
          if UnitIsUnit("target", u) then
            notunitarray[counter] = u
          elseif UnitIsUnit("player", u) then
            return castHolyLight(Mode, RankAdj, u)
          else
            TargetUnit(u)
            if castHolyLight(Mode, RankAdj, u) then
              Zorlen_CastingUnit = u
              Zorlen_CastingNotUnitArray = notunitarray
              TargetLastTarget()
              return true
            end
            TargetLastTarget()
            notunitarray[counter] = u
          end
          counter = counter + 1
        else
          counter = nil
        end
        if not u and (Zorlen_isCasting(SpellName)) then
          SpellStopCasting()
        end
      end
    end
  end
  return false
end


Group Flash of Light

This will cast Flash of Light on the party member with the lowest health, without losing your current target. If you already have a friendly target, it will cast Flash of Light on them directly, so you can still prioritize your healing.

Macro:

/run --CastSpellByName("Flash Of Light")
/run castGroupFlashOfLight()

Extended Lua:

function castFlashOfLight(Mode, RankAdj, unit)
  if Zorlen_isMoving() then
    return false
  end
  local SpellName = LOCALIZATION_ZORLEN.FlashOfLight
  local SpellButton = Zorlen_Button[SpellName]
  local LevelLearnedArray={20,26,34,42,50,58}
  local ManaArray={35,50,70,90,115,140}
  local MinHealArray={62,96,145,197,267,343}
  local MaxHealArray={72,110,163,221,299,383}
  local TimeArray={1.5,1.5,1.5,1.5,1.5,1.5}
  return Zorlen_CastHealingSpell(SpellName, ManaArray, MinHealArray, MaxHealArray, TimeArray, LevelLearnedArray, Mode, RankAdj, unit, SpellButton)
end

function castGroupFlashOfLight(pet, Mode, RankAdj)
  if Zorlen_isMoving() then
    return false
  end
  local SpellName = LOCALIZATION_ZORLEN.FlashOfLight
  if UnitExists("target") and castFlashOfLight(Mode, RankAdj, "target") then
    return true
  elseif not Zorlen_IsSpellKnown(SpellName) or UnitMana("player") < 35 then
    return false
  else
    local u = nil
    local counter = 1
    local notunitarray = {}
    if Zorlen_isCasting(SpellName) then
      u = Zorlen_GiveGroupUnitWithLowestHealth(pet, 0, nil, Zorlen_CastingNotUnitArray)
      if u and Zorlen_CastingUnit == u then
        return false
      elseif not u or Zorlen_CastingUnit then
        SpellStopCasting()
        return true
      end
      return false
    elseif Zorlen_checkCooldownByName(SpellName) then
      while counter do
        u = Zorlen_GiveGroupUnitWithLowestHealth(pet, nil, nil, notunitarray)
        if u then
          if UnitIsUnit("target", u) then
            notunitarray[counter] = u
          elseif UnitIsUnit("player", u) then
            return castFlashOfLight(Mode, RankAdj, u)
          else
            TargetUnit(u)
            if castFlashOfLight(Mode, RankAdj, u) then
              Zorlen_CastingUnit = u
              Zorlen_CastingNotUnitArray = notunitarray
              TargetLastTarget()
              return true
            end
            TargetLastTarget()
            notunitarray[counter] = u
          end
          counter = counter + 1
        else
          counter = nil
        end
        if not u and (Zorlen_isCasting(SpellName)) then
          SpellStopCasting()
        end
      end
    end
  end
  return false
end


Divine Protection / Blessing of Protection

This macro will cast Blessing of Protection if you are targeting a friendly unit. If you have no target or are targeting an enemy, it will find the party member with the lowest health (including yourself) and cast Blessing of Protection or Divine Protection if they have less than 40% health. You can also change this to cast Divine Shield on yourself once you learn it. It will do all of this without losing your current target.

Macro:

/run castProtection()

Extended Lua:

function castProtection()
  if UnitExists("target") and UnitIsFriend("player", "target") then
    CastSpellByName("Blessing Of Protection")
    return true
  end

  local u = Zorlen_GiveGroupUnitWithLowestHealth()
  if u then
    TargetUnit(u)
    local unitHealth = UnitHealth("target") / UnitHealthMax("target")
    if(unitHealth <= 0.4) then
      if UnitIsUnit("player", u) then
        CastSpellByName("Divine Protection")
      else
        CastSpellByName("Blessing Of Protection")
      end
      TargetLastTarget()
      return true
    end
    TargetLastTarget()
  end

  return false
end


Conclusion

I did not expect the Paladin class to require such elaborate macros, but it turned out there was a lot that could be done. These also demonstrate the immense power that is available when you can use raw Lua code in a macro. It's no wonder that Blizzard would remove this entire system starting with The Burning Crusade expansion.

You may also be interested in these World of Warcraft related pages:

World of Warcraft Macros

Question or Comment?