--- ============================ HEADER ============================ --- ======= LOCALIZE ======= -- Addon local addonName, addonTable = ... -- HeroDBC local DBC = HeroDBC.DBC -- HeroLib local HL = HeroLib local Cache = HeroCache local Unit = HL.Unit local Player = Unit.Player local Target = Unit.Target local Pet = Unit.Pet local Spell = HL.Spell local Item = HL.Item -- HeroRotation local HR = HeroRotation local AoEON = HR.AoEON local CDsON = HR.CDsON local Cast = HR.Cast local CastSuggested = HR.CastSuggested -- Num/Bool Helper Functions local num = HR.Commons.Everyone.num local bool = HR.Commons.Everyone.bool -- lua local mathmin = math.min --- ============================ CONTENT =========================== --- ======= APL LOCALS ======= -- luacheck: max_line_length 9999 -- Define S/I for spell and item arrays local S = Spell.Priest.Shadow local I = Item.Priest.Shadow -- Create table to exclude above trinkets from On Use function local OnUseExcludes = { I.BeacontotheBeyond:ID(), I.DesperateInvokersCodex:ID(), I.DMDDance:ID(), I.DMDDanceBox:ID(), I.DMDInferno:ID(), I.DMDInfernoBox:ID(), I.DMDRime:ID(), I.DMDRimeBox:ID(), I.EruptingSpearFragment:ID(), I.VoidmendersShadowgem:ID(), } -- Rotation Var local Enemies30y, Enemies40y, Enemies10ySplash local EnemiesCount30y, EnemiesCount10ySplash -- GUI Settings local Everyone = HR.Commons.Everyone local Settings = { General = HR.GUISettings.General, Commons = HR.GUISettings.APL.Priest.Commons, Shadow = HR.GUISettings.APL.Priest.Shadow } -- Variables local BossFightRemains = 11111 local FightRemains = 11111 local VarDotsUp = false local VarAllDotsUp = false local VarMaxVTs = 0 local VarIsVTPossible = false local VarVTsApplied = false local VarHoldingCrash = false local VarManualVTsApplied = false local VarPoolForCDs = false local Fiend = (S.Mindbender:IsAvailable()) and S.Mindbender or S.Shadowfiend local VarFiendUp local VarFiendRemains local Flay local GCDMax HL:RegisterForEvent(function() BossFightRemains = 11111 FightRemains = 11111 VarDotsUp = false VarAllDotsUp = false VarMindSearCutoff = 2 VarPoolAmount = 60 VarMaxVTs = 0 VarIsVTPossible = false VarVTsApplied = false VarHoldingCrash = false VarManualVTsApplied = false VarPoolForCDs = false end, "PLAYER_REGEN_ENABLED") HL:RegisterForEvent(function() Fiend = (S.Mindbender:IsAvailable()) and S.Mindbender or S.Shadowfiend end, "SPELLS_CHANGED", "LEARNED_SPELL_IN_TAB") HL:RegisterForEvent(function() S.ShadowCrash:RegisterInFlightEffect(205386) S.ShadowCrash:RegisterInFlight() end, "LEARNED_SPELL_IN_TAB") S.ShadowCrash:RegisterInFlightEffect(205386) S.ShadowCrash:RegisterInFlight() local function DotsUp(tar, all) if all then return (tar:DebuffUp(S.ShadowWordPainDebuff) and tar:DebuffUp(S.VampiricTouchDebuff) and tar:DebuffUp(S.DevouringPlagueDebuff)) else return (tar:DebuffUp(S.ShadowWordPainDebuff) and tar:DebuffUp(S.VampiricTouchDebuff)) end end local function HighestTTD(enemies, checkVT) if not enemies then return nil end local HighTTD = 0 local HighTTDTar = nil for _, enemy in pairs(enemies) do local TTD = enemy:TimeToDie() if checkVT then if TTD * num(enemy:DebuffRefreshable(S.VampiricTouchDebuff)) > HighTTD then HighTTD = TTD HighTTDTar = enemy end else if TTD > HighTTD then HighTTD = TTD HighTTDTar = enemy end end end return HighTTDTar end local function EvaluateTargetIfFilterTTD(TargetUnit) -- target_if=min:target.time_to_die return (TargetUnit:TimeToDie()) end local function EvaluateTargetIfFilterVTRemains(TargetUnit) -- target_if=min:remains return (TargetUnit:DebuffRemains(S.VampiricTouchDebuff)) end local function EvaluateTargetIfSWDFiller(TargetUnit) -- if=target.health.pct<20 return (TargetUnit:HealthPercentage() < 20) end local function EvaluateTargetIfVTMain(TargetUnit) -- target_if=min:remains,if=refreshable&target.time_to_die>=12&(cooldown.shadow_crash.remains>=dot.vampiric_touch.remains&!action.shadow_crash.in_flight|variable.holding_crash|!talent.whispering_shadows) return (TargetUnit:DebuffRefreshable(S.VampiricTouchDebuff) and TargetUnit:TimeToDie() >= 12 and (S.ShadowCrash:CooldownRemains() >= TargetUnit:DebuffRemains(S.VampiricTouchDebuff) and (not S.ShadowCrash:InFlight()) or VarHoldingCrash or not S.WhisperingShadows:IsAvailable())) end local function EvaluateCycleDP(TargetUnit) -- target_if=!talent.distorted_reality|active_enemies=1|remains<=gcd.max return ((not S.DistortedReality:IsAvailable()) or EnemiesCount10ySplash == 1 or TargetUnit:DebuffRemains(S.DevouringPlagueDebuff) <= GCDMax) end local function EvaluateCycleDP2(TargetUnit) -- target_if=remains<=gcd.max|!talent.distorted_reality return (TargetUnit:DebuffRemains(S.DevouringPlagueDebuff) <= GCDMax or not S.DistortedReality:IsAvailable()) end local function EvaluateCycleMindBlastMain(TargetUnit) -- target_if=(dot.devouring_plague.ticking&(cooldown.mind_blast.full_recharge_time<=gcd.max+cast_time)|pet.fiend.remains<=cast_time+gcd.max)&pet.fiend.active&talent.inescapable_torment&pet.fiend.remains>cast_time&active_enemies<=7 return ((TargetUnit:DebuffUp(S.DevouringPlagueDebuff) and (S.MindBlast:FullRechargeTime() <= GCDMax + S.MindBlast:CastTime()) or VarFiendRemains <= S.MindBlast:CastTime() + GCDMax) and VarFiendUp and S.InescapableTorment:IsAvailable() and VarFiendRemains > S.MindBlast:CastTime() and EnemiesCount10ySplash <= 7) end local function EvaluateCycleMindGamesMain(TargetUnit) -- target_if=variable.all_dots_up&dot.devouring_plague.remains>=cast_time return (DotsUp(TargetUnit, true) and TargetUnit:DebuffRemains(S.DevouringPlagueDebuff) >= S.Mindgames:CastTime()) end local function EvaluateCycleShadowCrashAoE(TargetUnit) -- target_if=dot.vampiric_touch.refreshable|dot.vampiric_touch.remains<=target.time_to_die&!buff.voidform.up&(raid_event.adds.in-dot.vampiric_touch.remains)<15 return (TargetUnit:DebuffRefreshable(S.VampiricTouchDebuff) or TargetUnit:DebuffRemains(S.VampiricTouchDebuff) <= TargetUnit:TimeToDie() and Player:BuffDown(S.VoidformBuff)) end local function EvaluateCycleSWD(TargetUnit) -- target_if=target.health.pct<20&(cooldown.fiend.remains>=10|!talent.inescapable_torment)|pet.fiend.active>1&talent.inescapable_torment|buff.deathspeaker.up return (TargetUnit:HealthPercentage() < 20 and (Fiend:CooldownRemains() >= 10 or not S.InescapableTorment:IsAvailable()) or VarFiendUp and S.InescapableTorment:IsAvailable() or Player:BuffUp(S.DeathspeakerBuff)) end local function EvaluateCycleSWDFiller(TargetUnit) -- target_if=target.health.pct<20|buff.deathspeaker.up return (TargetUnit:HealthPercentage() < 20 or Player:BuffUp(S.DeathspeakerBuff)) end local function EvaluateCycleSWPFiller(TargetUnit) -- target_if=min:remains return (TargetUnit:DebuffRemains(S.ShadowWordPainDebuff)) end local function EvaluateCycleVoidTorrentMain(TargetUnit) -- target_if=dot.vampiric_touch.ticking&dot.shadow_word_pain.ticking&dot.devouring_plague.remains>=2.5 return (DotsUp(TargetUnit) and TargetUnit:DebuffRemains(S.DevouringPlagueDebuff) >= 2) end local function EvaluateCycleVTAoE(TargetUnit) -- target_if=refreshable&target.time_to_die>=18&(dot.vampiric_touch.ticking|!variable.vts_applied) return (TargetUnit:DebuffRefreshable(S.VampiricTouchDebuff) and TargetUnit:TimeToDie() >= 18 and (TargetUnit:DebuffUp(S.VampiricTouchDebuff) or not VarVTsApplied)) end local function EvaluateCycleVTAoE2(TargetUnit) -- target_if=refreshable&target.time_to_die>=18&(dot.vampiric_touch.ticking|!variable.vts_applied) -- if=variable.max_vts>0&(cooldown.shadow_crash.remains>=dot.vampiric_touch.remains|variable.holding_crash)&!action.shadow_crash.in_flight if S.ShadowCrash:CooldownRemains() >= TargetUnit:DebuffRemains(S.VampiricTouchDebuff) or VarHoldingCrash then return (TargetUnit:DebuffRefreshable(S.VampiricTouchDebuff) and TargetUnit:TimeToDie() >= 18 and (TargetUnit:DebuffUp(S.VampiricTouchDebuff) or not VarVTsApplied)) end return nil end local function EvaluateCycleVTAoE3(TargetUnit) -- target_if=variable.dots_up return (DotsUp(TargetUnit, false)) end local function Precombat() -- flask -- food -- augmentation -- snapshot_stats -- Manually added: Group buff check if S.PowerWordFortitude:IsCastable() and (Player:BuffDown(S.PowerWordFortitudeBuff, true) or Everyone.GroupBuffMissing(S.PowerWordFortitudeBuff)) then if Cast(S.PowerWordFortitude, Settings.Commons.GCDasOffGCD.PowerWordFortitude) then return "power_word_fortitude precombat 2"; end end -- shadowform,if=!buff.shadowform.up if S.Shadowform:IsCastable() and (Player:BuffDown(S.ShadowformBuff)) then if Cast(S.Shadowform, Settings.Shadow.GCDasOffGCD.Shadowform) then return "shadowform precombat 4"; end end -- arcane_torrent if S.ArcaneTorrent:IsCastable() and CDsON() then if Cast(S.ArcaneTorrent, nil, nil, not Target:IsSpellInRange(S.ArcaneTorrent)) then return "arcane_torrent precombat 6"; end end -- shadow_crash,if=raid_event.adds.in>=25&spell_targets.shadow_crash<=8&!fight_style.dungeonslice&(spell_targets.shadow_crash>1|talent.mental_decay) -- Note: Can't do target counts in Precombat local DungeonSlice = Player:IsInParty() and not Player:IsInRaid() if S.ShadowCrash:IsCastable() and (not DungeonSlice) then if Cast(S.ShadowCrash, Settings.Shadow.GCDasOffGCD.ShadowCrash, nil, not Target:IsInRange(40)) then return "shadow_crash precombat 8"; end end -- vampiric_touch,if=!talent.shadow_crash.enabled|raid_event.adds.in<25|spell_targets.shadow_crash>8|spell_targets.shadow_crash=1&!talent.mental_decay|fight_style.dungeonslice -- Note: Manually added VT suggestion if Shadow Crash is on CD and wasn't just used. if S.VampiricTouch:IsCastable() and ((not S.ShadowCrash:IsAvailable()) or (S.ShadowCrash:CooldownDown() and not S.ShadowCrash:InFlight()) or DungeonSlice) then if Cast(S.VampiricTouch, nil, nil, not Target:IsSpellInRange(S.VampiricTouch)) then return "vampiric_touch precombat 14"; end end -- Manually added: shadow_word_pain,if=!talent.misery.enabled if S.ShadowWordPain:IsCastable() and (not S.Misery:IsAvailable()) then if Cast(S.ShadowWordPain, nil, nil, not Target:IsSpellInRange(S.ShadowWordPain)) then return "shadow_word_pain precombat 16"; end end end local function MainVariables() -- variable,name=dots_up,op=set,value=(dot.shadow_word_pain.ticking&dot.vampiric_touch.ticking)|action.shadow_crash.in_flight&talent.whispering_shadows VarDotsUp = DotsUp(Target, false) or S.ShadowCrash:InFlight() and S.WhisperingShadows:IsAvailable() -- variable,name=all_dots_up,op=set,value=dot.shadow_word_pain.ticking&dot.vampiric_touch.ticking&dot.devouring_plague.ticking VarAllDotsUp = DotsUp(Target, true) -- variable,name=pool_for_cds,op=set,value=(cooldown.void_eruption.remains<=gcd.max*3&talent.void_eruption|cooldown.dark_ascension.up&talent.dark_ascension)|talent.void_torrent&talent.psychic_link&cooldown.void_torrent.remains<=4&(!raid_event.adds.exists&spell_targets.vampiric_touch>1|raid_event.adds.in<=5|raid_event.adds.remains>=6&!variable.holding_crash)&!buff.voidform.up VarPoolForCDs = ((S.VoidEruption:CooldownRemains() <= Player:GCD() * 3 and S.VoidEruption:IsAvailable() or S.DarkAscension:CooldownUp() and S.DarkAscension:IsAvailable()) or S.VoidTorrent:IsAvailable() and S.PsychicLink:IsAvailable() and S.VoidTorrent:CooldownRemains() <= 4 and Player:BuffDown(S.VoidformBuff)) end local function AoEVariables() -- variable,name=max_vts,op=set,default=12,value=spell_targets.vampiric_touch>?12 VarMaxVTs = mathmin(EnemiesCount10ySplash, 12) -- variable,name=is_vt_possible,op=set,value=0,default=1 VarIsVTPossible = false -- variable,name=is_vt_possible,op=set,value=1,target_if=max:(target.time_to_die*dot.vampiric_touch.refreshable),if=target.time_to_die>=18 local HighTTDTar = HighestTTD(Enemies10ySplash, true) if HighTTDTar and HighTTDTar:TimeToDie() >= 18 then VarIsVTPossible = true end -- variable,name=vts_applied,op=set,value=(active_dot.vampiric_touch+8*(action.shadow_crash.in_flight&talent.whispering_shadows))>=variable.max_vts|!variable.is_vt_possible VarVTsApplied = ((S.VampiricTouchDebuff:AuraActiveCount() + 8 * num(S.ShadowCrash:InFlight() and S.WhisperingShadows:IsAvailable())) >= VarMaxVTs or not VarIsVTPossible) -- variable,name=holding_crash,op=set,value=(variable.max_vts-active_dot.vampiric_touch)<4|raid_event.adds.in<10&raid_event.adds.count>(variable.max_vts-active_dot.vampiric_touch),if=variable.holding_crash&talent.whispering_shadows if VarHoldingCrash and S.WhisperingShadows:IsAvailable() then VarHoldingCrash = (VarMaxVTs - S.VampiricTouchDebuff:AuraActiveCount()) < 4 end -- variable,name=manual_vts_applied,op=set,value=(active_dot.vampiric_touch+8*!variable.holding_crash)>=variable.max_vts|!variable.is_vt_possible VarManualVTsApplied = ((S.VampiricTouchDebuff:AuraActiveCount() + 8 * num(not VarHoldingCrash)) >= VarMaxVTs or not VarIsVTPossible) end local function Trinkets() if Settings.Commons.Enabled.Trinkets then -- use_item,name=voidmenders_shadowgem,if=buff.power_infusion.up|fight_remains<20 if I.VoidmendersShadowgem:IsEquippedAndReady() and (Player:BuffUp(S.PowerInfusionBuff) or FightRemains < 20) then if Cast(I.VoidmendersShadowgem, nil, Settings.Commons.DisplayStyle.Trinkets) then return "voidmenders_shadowgem trinkets 2"; end end -- use_item,name=darkmoon_deck_box_inferno if I.DMDInferno:IsEquippedAndReady() then if Cast(I.DMDInferno, nil, Settings.Commons.DisplayStyle.Trinkets) then return "dmd_inferno trinkets 4"; end end if I.DMDInfernoBox:IsEquippedAndReady() then if Cast(I.DMDInfernoBox, nil, Settings.Commons.DisplayStyle.Trinkets) then return "dmd_inferno_box trinkets 6"; end end -- use_item,name=darkmoon_deck_box_rime if I.DMDRime:IsEquippedAndReady() then if Cast(I.DMDRime, nil, Settings.Commons.DisplayStyle.Trinkets) then return "dmd_rime trinkets 8"; end end if I.DMDRimeBox:IsEquippedAndReady() then if Cast(I.DMDRimeBox, nil, Settings.Commons.DisplayStyle.Trinkets) then return "dmd_rime_box trinkets 10"; end end -- use_item,name=darkmoon_deck_box_dance if I.DMDDance:IsEquippedAndReady() then if Cast(I.DMDDance, nil, Settings.Commons.DisplayStyle.Trinkets) then return "dmd_dance trinkets 12"; end end if I.DMDDanceBox:IsEquippedAndReady() then if Cast(I.DMDDanceBox, nil, Settings.Commons.DisplayStyle.Trinkets) then return "dmd_dance_box trinkets 14"; end end -- use_item,name=erupting_spear_fragment,if=buff.power_infusion.up|raid_event.adds.up|fight_remains<20 if I.EruptingSpearFragment:IsEquippedAndReady() and (Player:BuffUp(S.PowerInfusionBuff) or FightRemains < 20) then if Cast(I.EruptingSpearFragment, nil, Settings.Commons.DisplayStyle.Trinkets, not Target:IsInRange(40)) then return "erupting_spear_fragment trinkets 16"; end end -- use_item,name=beacon_to_the_beyond,if=!raid_event.adds.exists|raid_event.adds.up|spell_targets.beacon_to_the_beyond>=5|fight_remains<20 if I.BeacontotheBeyond:IsEquippedAndReady() then if Cast(I.BeacontotheBeyond, nil, Settings.Commons.DisplayStyle.Trinkets, not Target:IsInRange(45)) then return "beacon_to_the_beyond trinkets 18"; end end end -- use_items,if=buff.voidform.up|buff.power_infusion.up|buff.dark_ascension.up|(cooldown.void_eruption.remains>10&trinket.cooldown.duration<=60)|fight_remains<20 if (Player:BuffUp(S.VoidformBuff) or Player:BuffUp(S.PowerInfusionBuff) or Player:BuffUp(S.DarkAscensionBuff) or FightRemains < 20) then local ItemToUse, ItemSlot, ItemRange = Player:GetUseableItems(OnUseExcludes) if ItemToUse then local DisplayStyle = Settings.Commons.DisplayStyle.Trinkets if ItemSlot ~= 13 and ItemSlot ~= 14 then DisplayStyle = Settings.Commons.DisplayStyle.Items end if ((ItemSlot == 13 or ItemSlot == 14) and Settings.Commons.Enabled.Trinkets) or (ItemSlot ~= 13 and ItemSlot ~= 14 and Settings.Commons.Enabled.Items) then if Cast(ItemToUse, nil, DisplayStyle, not Target:IsInRange(ItemRange)) then return "Generic use_items for " .. ItemToUse:Name(); end end end end -- use_item,name=desperate_invokers_codex if Settings.Commons.Enabled.Trinkets and I.DesperateInvokersCodex:IsEquippedAndReady() then if Cast(I.DesperateInvokersCodex, nil, Settings.Commons.DisplayStyle.Trinkets) then return "desperate_invokers_codex trinkets 20"; end end end local function Filler() -- vampiric_touch,target_if=min:remains,if=buff.unfurling_darkness.up if S.VampiricTouch:IsCastable() and (Player:BuffUp(S.UnfurlingDarknessBuff)) then if Everyone.CastTargetIf(S.VampiricTouch, Enemies40y, "min", EvaluateTargetIfFilterVTRemains, nil, not Target:IsSpellInRange(S.VampiricTouch)) then return "vampiric_touch filler 2"; end end -- shadow_word_death,target_if=target.health.pct<20|buff.deathspeaker.up if S.ShadowWordDeath:IsReady() then if Everyone.CastCycle(S.ShadowWordDeath, Enemies40y, EvaluateCycleSWDFiller, not Target:IsSpellInRange(S.ShadowWordDeath), Settings.Shadow.GCDasOffGCD.ShadowWordDeath) then return "shadow_word_death filler 3"; end end -- mind_spike_insanity if S.MindSpikeInsanity:IsReady() then if Cast(S.MindSpikeInsanity, nil, nil, not Target:IsSpellInRange(S.MindSpikeInsanity)) then return "mind_spike_insanity filler 4"; end end -- mind_flay,if=buff.mind_flay_insanity.up if S.MindFlay:IsCastable() and (Player:BuffUp(S.MindFlayInsanityBuff)) then if Cast(S.MindSpike, nil, nil, not Target:IsSpellInRange(S.MindSpike)) then return "mind_flay filler 6"; end end -- halo,if=raid_event.adds.in>20 if S.Halo:IsReady() then if Cast(S.Halo, Settings.Shadow.GCDasOffGCD.Halo, nil, not Target:IsInRange(30)) then return "halo filler 10"; end end -- shadow_word_death,target_if=min:target.time_to_die,if=talent.inescapable_torment&pet.fiend.active if S.ShadowWordDeath:IsReady() and (S.InescapableTorment:IsAvailable() and VarFiendUp) then if Everyone.CastTargetIf(S.ShadowWordDeath, Enemies40y, "min", EvaluateTargetIfFilterTTD, nil, not Target:IsSpellInRange(S.ShadowWordDeath), Settings.Shadow.GCDasOffGCD.ShadowWordDeath) then return "shadow_word_death filler 12"; end end -- divine_star,if=raid_event.adds.in>10 if S.DivineStar:IsReady() then if Cast(S.DivineStar, Settings.Shadow.GCDasOffGCD.DivineStar, nil, not Target:IsInRange(30)) then return "divine_star filler 14"; end end -- devouring_plague,if=buff.voidform.up if S.DevouringPlague:IsReady() and (Player:BuffUp(S.VoidformBuff)) then if Cast(S.DevouringPlague, nil, nil, not Target:IsSpellInRange(S.DevouringPlague)) then return "devouring_plague filler 16"; end end -- mind_spike if S.MindSpike:IsCastable() then if Cast(S.MindSpike, nil, nil, not Target:IsSpellInRange(S.MindSpike)) then return "mind_spike filler 18"; end end -- mind_flay,chain=1,interrupt_immediate=1,interrupt_if=ticks>=2 if Flay:IsCastable() then if Cast(Flay, nil, nil, not Target:IsSpellInRange(Flay)) then return "mind_flay filler 20"; end end -- shadow_crash,if=raid_event.adds.in>20 if S.ShadowCrash:IsCastable() then if Cast(S.ShadowCrash, Settings.Shadow.GCDasOffGCD.ShadowCrash, nil, not Target:IsInRange(40)) then return "shadow_crash filler 22"; end end -- shadow_word_death,target_if=target.health.pct<20 if S.ShadowWordDeath:IsReady() then if Everyone.CastCycle(S.ShadowWordDeath, Enemies40y, EvaluateTargetIfSWDFiller, not Target:IsSpellInRange(S.ShadowWordDeath), Settings.Shadow.GCDasOffGCD.ShadowWordDeath) then return "shadow_word_death filler 22"; end end -- divine_star -- Note: Handled in above divine_star line, as we're not checking raid_event.adds.in -- shadow_word_death -- Note: APL comments reference using this while moving if S.ShadowWordDeath:IsReady() and Player:IsMoving() then if Cast(S.ShadowWordDeath, Settings.Shadow.GCDasOffGCD.ShadowWordDeath, nil, not Target:IsSpellInRange(S.ShadowWordDeath)) then return "shadow_word_death movement filler 24"; end end -- shadow_word_pain,target_if=min:remains if S.ShadowWordPain:IsReady() then if Everyone.CastCycle(S.ShadowWordPain, Enemies40y, EvaluateCycleSWPFiller, not Target:IsSpellInRange(S.ShadowWordPain)) then return "shadow_word_pain filler 26"; end end end local function CDs() -- potion,if=buff.voidform.up|buff.power_infusion.up|buff.dark_ascension.up&(fight_remains<=cooldown.power_infusion.remains+15)|fight_remains<=30 if Settings.Commons.Enabled.Potions and (Player:BuffUp(S.VoidformBuff) or Player:BuffUp(S.PowerInfusionBuff) or Player:BuffUp(S.DarkAscensionBuff) and (FightRemains <= S.PowerInfusion:CooldownRemains() + 15) or FightRemains <= 30) then local PotionSelected = Everyone.PotionSelected() if PotionSelected and PotionSelected:IsReady() then if Cast(PotionSelected, nil, Settings.Commons.DisplayStyle.Potions) then return "potion cds 2"; end end end -- fireblood,if=buff.power_infusion.up|fight_remains<=8 if S.Fireblood:IsCastable() and (Player:BuffUp(S.PowerInfusionBuff) or FightRemains <= 8) then if Cast(S.Fireblood, Settings.Commons.OffGCDasOffGCD.Racials) then return "fireblood cds 4"; end end -- berserking,if=buff.power_infusion.up|fight_remains<=12 if S.Berserking:IsCastable() and (Player:BuffUp(S.PowerInfusionBuff) or FightRemains <= 12) then if Cast(S.Berserking, Settings.Commons.OffGCDasOffGCD.Racials) then return "berserking cds 6"; end end -- blood_fury,if=buff.power_infusion.up|fight_remains<=15 if S.BloodFury:IsCastable() and (Player:BuffUp(S.PowerInfusionBuff) or FightRemains <= 15) then if Cast(S.BloodFury, Settings.Commons.OffGCDasOffGCD.Racials) then return "blood_fury cds 8"; end end -- ancestral_call,if=buff.power_infusion.up|fight_remains<=15 if S.AncestralCall:IsCastable() and (Player:BuffUp(S.PowerInfusionBuff) or FightRemains <= 15) then if Cast(S.AncestralCall, Settings.Commons.OffGCDasOffGCD.Racials) then return "ancestral_call cds 10"; end end -- power_infusion,if=(buff.voidform.up|buff.dark_ascension.up) if S.PowerInfusion:IsCastable() and Settings.Shadow.SelfPI and (Player:BuffUp(S.VoidformBuff) or Player:BuffUp(S.DarkAscension)) then if Cast(S.PowerInfusion, Settings.Shadow.OffGCDasOffGCD.PowerInfusion) then return "power_infusion cds 12"; end end -- invoke_external_buff,name=power_infusion,if=(buff.voidform.up|buff.dark_ascension.up)&!buff.power_infusion.up -- Note: Not handling external buffs -- void_eruption,if=!cooldown.fiend.up&(pet.fiend.active&cooldown.fiend.remains>=4|!talent.mindbender|active_enemies>2&!talent.inescapable_torment.rank)&(cooldown.mind_blast.charges=0|time>15) if S.VoidEruption:IsCastable() and (Fiend:CooldownDown() and (VarFiendUp and Fiend:CooldownRemains() >= 4 or (not S.Mindbender:IsAvailable()) or EnemiesCount10ySplash > 2 and (not S.InescapableTorment:IsAvailable())) and (S.MindBlast:Charges() == 0 or HL.CombatTime() > 15)) then if Cast(S.VoidEruption, Settings.Shadow.GCDasOffGCD.VoidEruption) then return "void_eruption cds 14"; end end -- dark_ascension,if=pet.fiend.active&cooldown.fiend.remains>=4|!talent.mindbender&!cooldown.fiend.up|active_enemies>2&!talent.inescapable_torment if S.DarkAscension:IsCastable() and (not Player:IsCasting(S.DarkAscension)) and (VarFiendUp and Fiend:CooldownRemains() >= 4 or (not S.Mindbender:IsAvailable()) and Fiend:CooldownDown() or EnemiesCount10ySplash > 2 and not S.InescapableTorment:IsAvailable()) then if Cast(S.DarkAscension, Settings.Shadow.GCDasOffGCD.DarkAscension) then return "dark_ascension cds 16"; end end -- call_action_list,name=trinkets if Settings.Commons.Enabled.Trinkets or Settings.Commons.Enabled.Items then local ShouldReturn = Trinkets(); if ShouldReturn then return ShouldReturn; end end -- desperate_prayer,if=health.pct<=75 if S.DesperatePrayer:IsCastable() and (Player:HealthPercentage() <= Settings.Shadow.DesperatePrayerHP) then if Cast(S.DesperatePrayer) then return "desperate_prayer cds 80"; end end end local function Main() -- call_action_list,name=main_variables MainVariables() -- call_action_list,name=cds,if=fight_remains<30|time_to_die>15&(!variable.holding_crash|active_enemies>2) if CDsON() and (FightRemains < 30 or Target:TimeToDie() > 15 and ((not VarHoldingCrash) or EnemiesCount10ySplash > 2)) then local ShouldReturn = CDs(); if ShouldReturn then return ShouldReturn; end end -- mindbender,if=variable.dots_up&(fight_remains<30|time_to_die>15)&(!talent.dark_ascension|cooldown.dark_ascension.remains 15) and ((not S.DarkAscension:IsAvailable()) or S.DarkAscension:CooldownRemains() < GCDMax or FightRemains < 15)) then if Cast(Fiend, Settings.Shadow.GCDasOffGCD.Mindbender) then return "mindbender main 2"; end end -- mind_blast,target_if=(dot.devouring_plague.ticking&(cooldown.mind_blast.full_recharge_time<=gcd.max+cast_time)|pet.fiend.remains<=cast_time+gcd.max)&pet.fiend.active&talent.inescapable_torment&pet.fiend.remains>cast_time&active_enemies<=7 if S.MindBlast:IsCastable() then if Everyone.CastCycle(S.MindBlast, Enemies40y, EvaluateCycleMindBlastMain, not Target:IsSpellInRange(S.MindBlast)) then return "mind_blast main 4"; end end -- void_bolt,if=variable.dots_up if S.VoidBolt:IsCastable() and (VarDotsUp) then if Cast(S.VoidBolt, nil, nil, not Target:IsInRange(40)) then return "void_bolt main 6"; end end -- devouring_plague,if=fight_remains<=duration+4 if S.DevouringPlague:IsReady() and (FightRemains <= S.DevouringPlagueDebuff:BaseDuration() + 4) then if Cast(S.DevouringPlague, nil, nil, not Target:IsSpellInRange(S.DevouringPlague)) then return "devouring_plague main "; end end -- devouring_plague,target_if=!talent.distorted_reality|active_enemies=1|remains<=gcd.max,if=(remains<=gcd.max|remains<3&cooldown.void_torrent.up)|insanity.deficit<=20|buff.voidform.up&cooldown.void_bolt.remains>buff.voidform.remains&cooldown.void_bolt.remains<=buff.voidform.remains+2 if S.DevouringPlague:IsReady() and (Player:InsanityDeficit() <= 20 or Player:BuffUp(S.VoidformBuff) and S.VoidBolt:CooldownRemains() > Player:BuffRemains(S.VoidformBuff) and S.VoidBolt:CooldownRemains() <= Player:BuffRemains(S.VoidformBuff) + 2) then if Everyone.CastCycle(S.DevouringPlague, Enemies40y, EvaluateCycleDP, not Target:IsSpellInRange(S.DevouringPlague)) then return "devouring_plague main 8"; end end -- shadow_crash,if=!variable.holding_crash&dot.vampiric_touch.refreshable if S.ShadowCrash:IsCastable() and ((not VarHoldingCrash) and Target:DebuffRefreshable(S.VampiricTouchDebuff)) then if Cast(S.ShadowCrash, Settings.Shadow.GCDasOffGCD.ShadowCrash, nil, not Target:IsInRange(40)) then return "shadow_crash main 10"; end end -- vampiric_touch,target_if=min:remains,if=refreshable&target.time_to_die>=12&(cooldown.shadow_crash.remains>=dot.vampiric_touch.remains&!action.shadow_crash.in_flight|variable.holding_crash|!talent.whispering_shadows) if S.VampiricTouch:IsCastable() then if Everyone.CastTargetIf(S.VampiricTouch, Enemies40y, "min", EvaluateTargetIfFilterVTRemains, EvaluateTargetIfVTMain, not Target:IsSpellInRange(S.VampiricTouch)) then return "vampiric_touch main 12"; end end -- mind_spike_insanity,if=variable.dots_up&cooldown.mind_blast.full_recharge_time>=gcd*3&talent.idol_of_cthun&(!cooldown.void_torrent.up|!talent.void_torrent) if S.MindSpikeInsanity:IsReady() and (VarDotsUp and S.MindBlast:FullRechargeTime() >= Player:GCD() * 3 and S.IdolOfCthun:IsAvailable() and (S.VoidTorrent:CooldownDown() or not S.VoidTorrent:IsAvailable())) then if Cast(S.MindSpikeInsanity, nil, nil, not Target:IsInRange(40)) then return "mind_spike_insanity main 22"; end end -- mind_flay,if=buff.mind_flay_insanity.up&variable.dots_up&cooldown.mind_blast.full_recharge_time>=gcd*3&talent.idol_of_cthun&(!cooldown.void_torrent.up|!talent.void_torrent) if Flay:IsCastable() and (Player:BuffUp(S.MindFlayInsanityBuff) and VarDotsUp and S.MindBlast:FullRechargeTime() >= Player:GCD() * 3 and S.IdolOfCthun:IsAvailable() and (S.VoidTorrent:CooldownDown() or not S.VoidTorrent:IsAvailable())) then if Cast(Flay, nil, nil, not Target:IsSpellInRange(Flay)) then return "mind_flay main 24"; end end -- mind_blast,if=variable.dots_up&(!buff.mind_devourer.up|cooldown.void_eruption.up&talent.void_eruption) if S.MindBlast:IsCastable() and (VarDotsUp and (Player:BuffDown(S.MindDevourerBuff) or S.VoidEruption:CooldownUp() and S.VoidEruption:IsAvailable())) then if Cast(S.MindBlast, nil, nil, not Target:IsSpellInRange(S.MindBlast)) then return "mind_blast main 26"; end end -- void_torrent,if=!variable.holding_crash,target_if=dot.vampiric_touch.ticking&dot.shadow_word_pain.ticking&dot.devouring_plague.remains>=2.5 if S.VoidTorrent:IsCastable() and (not VarHoldingCrash) then if Everyone.CastCycle(S.VoidTorrent, Enemies40y, EvaluateCycleVoidTorrentMain, not Target:IsSpellInRange(S.VoidTorrent), Settings.Shadow.GCDasOffGCD.VoidTorrent) then return "void_torrent main 28"; end end -- mindgames,target_if=variable.all_dots_up&dot.devouring_plague.remains>=cast_time if S.Mindgames:IsReady() then if Everyone.CastCycle(S.Mindgames, Enemies40y, EvaluateCycleMindGamesMain, not Target:IsSpellInRange(S.Mindgames), nil, Settings.Commons.DisplayStyle.Signature) then return "mindgames main 30"; end end -- call_action_list,name=filler local ShouldReturn = Filler(); if ShouldReturn then return ShouldReturn; end end local function PLTorrent() -- void_bolt if S.VoidBolt:IsCastable() then if Cast(S.VoidBolt, nil, nil, not Target:IsInRange(40)) then return "void_bolt pl_torrent 2"; end end -- vampiric_touch,if=remains<=6&cooldown.void_torrent.remains=3&!prev_gcd.1.mind_blast if S.MindBlast:IsReady() and ((not S.Mindgames:IsAvailable()) or S.Mindgames:CooldownRemains() >= 3 and not Player:PrevGCD(1, S.MindBlast)) then if Cast(S.MindBlast, nil, nil, not Target:IsSpellInRange(S.MindBlast)) then return "mind_blast pl_torrent 8"; end end -- void_torrent,if=dot.vampiric_touch.ticking&dot.shadow_word_pain.ticking|buff.voidform.up if S.VoidTorrent:IsCastable() and (DotsUp(Target, false) or Player:BuffUp(S.VoidformBuff)) then if Cast(S.VoidTorrent, Settings.Shadow.GCDasOffGCD.VoidTorrent, nil, not Target:IsSpellInRange(S.VoidTorrent)) then return "void_torrent pl_torrent 10"; end end -- mindgames,if=dot.vampiric_touch.ticking&dot.shadow_word_pain.ticking&dot.devouring_plague.ticking|buff.voidform.up if S.Mindgames:IsReady() and (VarAllDotsUp or Player:BuffUp(S.VoidformBuff)) then if Cast(S.Mindgames, nil, Settings.Commons.DisplayStyle.Signature, not Target:IsInRange(40)) then return "mindgames pl_torrent 12"; end end end local function AoE() -- call_action_list,name=aoe_variables AoEVariables() -- vampiric_touch,target_if=refreshable&target.time_to_die>=18&(dot.vampiric_touch.ticking|!variable.vts_applied),if=variable.max_vts>0&!variable.manual_vts_applied&!action.shadow_crash.in_flight|!talent.whispering_shadows if S.VampiricTouch:IsCastable() and (VarMaxVTs > 0 and (not VarManualVTsApplied) and (not S.ShadowCrash:InFlight()) or not S.WhisperingShadows:IsAvailable()) then if Everyone.CastCycle(S.VampiricTouch, Enemies40y, EvaluateCycleVTAoE, not Target:IsSpellInRange(S.VampiricTouch)) then return "vampiric_touch aoe 2"; end end -- shadow_crash,if=!variable.holding_crash,target_if=dot.vampiric_touch.refreshable|dot.vampiric_touch.remains<=target.time_to_die&!buff.voidform.up&(raid_event.adds.in-dot.vampiric_touch.remains)<15 if S.ShadowCrash:IsCastable() and (not VarHoldingCrash) then if Everyone.CastCycle(S.ShadowCrash, Enemies40y, EvaluateCycleShadowCrashAoE, not Target:IsInRange(40), Settings.Shadow.GCDasOffGCD.ShadowCrash) then return "shadow_crash aoe 4"; end end -- call_action_list,name=cds,if=fight_remains<30|time_to_die>15&(!variable.holding_crash|active_enemies>2) if CDsON() and (FightRemains < 30 or Target:TimeToDie() > 15 and ((not VarHoldingCrash) or EnemiesCount10ySplash > 2)) then local ShouldReturn = CDs(); if ShouldReturn then return ShouldReturn; end end -- mindbender,if=(dot.shadow_word_pain.ticking&variable.vts_applied|action.shadow_crash.in_flight&talent.whispering_shadows)&(fight_remains<30|time_to_die>15)&(!talent.dark_ascension|cooldown.dark_ascension.remains 15) and ((not S.DarkAscension:IsAvailable()) or S.DarkAscension:CooldownRemains() < GCDMax or FightRemains < 15)) then if Cast(Fiend, Settings.Shadow.GCDasOffGCD.Mindbender) then return "mindbender aoe 6"; end end -- mind_blast,if=(cooldown.mind_blast.full_recharge_time<=gcd.max+cast_time|pet.fiend.remains<=cast_time+gcd.max)&pet.fiend.active&talent.inescapable_torment&pet.fiend.remains>cast_time&active_enemies<=7&!buff.mind_devourer.up if S.MindBlast:IsReady() and ((S.MindBlast:FullRechargeTime() <= GCDMax + S.MindBlast:CastTime() or VarFiendRemains <= S.MindBlast:CastTime() + GCDMax) and VarFiendUp and S.InescapableTorment:IsAvailable() and VarFiendRemains > S.MindBlast:CastTime() and EnemiesCount10ySplash <= 7 and Player:BuffDown(S.MindDevourerBuff)) then if Cast(S.MindBlast, nil, nil, not Target:IsSpellInRange(S.MindBlast)) then return "mind_blast aoe 8"; end end -- void_bolt if S.VoidBolt:IsCastable() then if Cast(S.VoidBolt, nil, nil, not Target:IsInRange(40)) then return "void_bolt aoe 10"; end end -- devouring_plague,target_if=remains<=gcd.max|!talent.distorted_reality,if=remains<=gcd.max&!variable.pool_for_cds|insanity.deficit<=20|buff.voidform.up&cooldown.void_bolt.remains>buff.voidform.remains&cooldown.void_bolt.remains<=buff.voidform.remains+2 if S.DevouringPlague:IsReady() and ((not VarPoolForCDs) or Player:InsanityDeficit() <= 20 or Player:BuffUp(S.VoidformBuff) and S.VoidBolt:CooldownRemains() > Player:BuffRemains(S.VoidformBuff) and S.VoidBolt:CooldownRemains() <= Player:BuffRemains(S.VoidformBuff) + 2) then if Everyone.CastCycle(S.DevouringPlague, Enemies40y, EvaluateCycleDP2, not Target:IsSpellInRange(S.DevouringPlague)) then return "devouring_plague aoe 12"; end end -- vampiric_touch,target_if=refreshable&target.time_to_die>=18&(dot.vampiric_touch.ticking|!variable.vts_applied),if=variable.max_vts>0&(cooldown.shadow_crash.remains>=dot.vampiric_touch.remains|variable.holding_crash)&!action.shadow_crash.in_flight|!talent.whispering_shadows if S.VampiricTouch:IsCastable() and (VarMaxVTs > 0 and (not S.ShadowCrash:InFlight()) or not S.WhisperingShadows:IsAvailable()) then if Everyone.CastCycle(S.VampiricTouch, Enemies40y, EvaluateCycleVTAoE2, not Target:IsSpellInRange(S.VampiricTouch)) then return "vampiric_touch aoe 14"; end end -- mind_spike_insanity,if=variable.dots_up&cooldown.mind_blast.full_recharge_time>=gcd*3&talent.idol_of_cthun&(!cooldown.void_torrent.up|!talent.void_torrent) if S.MindSpikeInsanity:IsReady() and (VarDotsUp and S.MindBlast:FullRechargeTime() >= Player:GCD() * 3 and S.IdolOfCthun:IsAvailable() and (S.VoidTorrent:CooldownDown() or not S.VoidTorrent:IsAvailable())) then if Cast(S.MindSpikeInsanity, nil, nil, not Target:IsInRange(40)) then return "mind_spike_insanity aoe 18"; end end -- mind_flay,if=buff.mind_flay_insanity.up&variable.dots_up&cooldown.mind_blast.full_recharge_time>=gcd*3&talent.idol_of_cthun&(!cooldown.void_torrent.up|!talent.void_torrent) if Flay:IsCastable() and (Player:BuffUp(S.MindFlayInsanityBuff) and VarDotsUp and S.MindBlast:FullRechargeTime() >= Player:GCD() * 3 and S.IdolOfCthun:IsAvailable() and (S.VoidTorrent:CooldownDown() or not S.VoidTorrent:IsAvailable())) then if Cast(Flay, nil, nil, not Target:IsSpellInRange(Flay)) then return "mind_flay aoe 20"; end end -- mind_blast,if=variable.vts_applied&(!buff.mind_devourer.up|cooldown.void_eruption.up&talent.void_eruption) if S.MindBlast:IsReady() and (VarVTsApplied and (Player:BuffDown(S.MindDevourerBuff) or S.VoidEruption:CooldownUp() and S.VoidEruption:IsAvailable())) then if Cast(S.MindBlast, nil, nil, not Target:IsSpellInRange(S.MindBlast)) then return "mind_blast aoe 22"; end end -- call_action_list,name=pl_torrent,target_if=talent.void_torrent&talent.psychic_link&cooldown.void_torrent.remains<=3&(!variable.holding_crash|raid_event.adds.count%(active_dot.vampiric_touch+raid_event.adds.count)<1.5)&((insanity>=50|dot.devouring_plague.ticking|buff.dark_reveries.up)|buff.voidform.up|buff.dark_ascension.up) if S.VoidTorrent:IsAvailable() and S.PsychicLink:IsAvailable() and S.VoidTorrent:CooldownRemains() <= 3 and ((not VarHoldingCrash) or EnemiesCount10ySplash / (S.VampiricTouchDebuff:AuraActiveCount() + EnemiesCount10ySplash) < 1.5) and ((Player:Insanity() >= 50 or Target:DebuffUp(S.DevouringPlagueDebuff) or Player:BuffUp(S.DarkReveriesBuff)) or Player:BuffUp(S.VoidformBuff) or Player:BuffUp(S.DarkAscensionBuff)) then local ShouldReturn = PLTorrent(); if ShouldReturn then return ShouldReturn; end end -- mindgames,if=active_enemies<5&dot.devouring_plague.ticking|talent.psychic_link if S.Mindgames:IsReady() and (EnemiesCount10ySplash < 5 and Target:DebuffUp(S.DevouringPlagueDebuff) or S.PsychicLink:IsAvailable()) then if Cast(S.Mindgames, nil, Settings.Commons.DisplayStyle.Signature, not Target:IsSpellInRange(S.Mindgames)) then return "mindgames aoe 24"; end end -- void_torrent,if=!talent.psychic_link,target_if=variable.dots_up if S.VoidTorrent:IsCastable() and (not S.PsychicLink:IsAvailable()) then if Everyone.CastCycle(S.VoidTorrent, Enemies40y, EvaluateCycleVTAoE3, not Target:IsSpellInRange(S.VoidTorrent), Settings.Shadow.GCDasOffGCD.VoidTorrent) then return "void_torrent aoe 26"; end end -- mind_flay,if=buff.mind_flay_insanity.up&talent.idol_of_cthun,interrupt_if=ticks>=2,interrupt_immediate=1 if Flay:IsCastable() and (Player:BuffUp(S.MindFlayInsanityBuff) and S.IdolOfCthun:IsAvailable()) then if Cast(Flay, nil, nil, not Target:IsSpellInRange(Flay)) then return "mind_flay aoe 28"; end end -- call_action_list,name=filler local ShouldReturn = Filler(); if ShouldReturn then return ShouldReturn; end end --- ======= ACTION LISTS ======= local function APL() Enemies30y = Player:GetEnemiesInRange(30) -- Silence, for Sephuz Enemies40y = Player:GetEnemiesInRange(40) -- Multiple CastCycle Spells Enemies10ySplash = Target:GetEnemiesInSplashRange(10) if AoEON() then EnemiesCount30y = #Enemies30y EnemiesCount10ySplash = Target:GetEnemiesInSplashRangeCount(10) else EnemiesCount30y = 1 EnemiesCount10ySplash = 1 end if Everyone.TargetIsValid() or Player:AffectingCombat() then -- Calculate fight_remains BossFightRemains = HL.BossFightRemains(nil, true) FightRemains = BossFightRemains if FightRemains == 11111 then FightRemains = HL.FightRemains(Enemies10ySplash, false) end -- Check our fiend status VarFiendUp = Fiend:TimeSinceLastCast() <= 15 VarFiendRemains = 15 - Fiend:TimeSinceLastCast() if VarFiendRemains < 0 then VarFiendRemains = 0 end -- If MF:Insanity buff is up, change which flay we use Flay = (Player:BuffUp(S.MindFlayInsanityBuff)) and S.MindFlayInsanity or S.MindFlay -- Calculate GCDMax for gcd.max GCDMax = Player:GCD() + 0.25 end if Everyone.TargetIsValid() then -- call precombat if not Player:AffectingCombat() then local ShouldReturn = Precombat(); if ShouldReturn then return ShouldReturn; end end -- Manually Added: Use Dispersion if dying if S.Dispersion:IsCastable() and Player:HealthPercentage() < Settings.Shadow.DispersionHP then if Cast(S.Dispersion, Settings.Shadow.OffGCDasOffGCD.Dispersion) then return "dispersion low_hp"; end end -- Interrupts local ShouldReturn = Everyone.Interrupt(30, S.Silence, Settings.Commons.OffGCDasOffGCD.Silence, false); if ShouldReturn then return ShouldReturn; end -- variable,name=holding_crash,op=set,value=raid_event.adds.in<15 -- Note: We have no way of knowing if adds are coming, so don't ever purposely hold crash VarHoldingCrash = false -- variable,name=pool_for_cds,op=set,value=(cooldown.void_eruption.remains<=gcd.max*3&talent.void_eruption|cooldown.dark_ascension.up&talent.dark_ascension)|talent.void_torrent&talent.psychic_link&cooldown.void_torrent.remains<=4&(!raid_event.adds.exists&spell_targets.vampiric_touch>1|raid_event.adds.in<=5|raid_event.adds.remains>=6&!variable.holding_crash)&!buff.voidform.up VarPoolForCDs = ((S.VoidEruption:CooldownRemains() <= Player:GCD() * 3 and S.VoidEruption:IsAvailable() or S.DarkAscension:CooldownUp() and S.DarkAscension:IsAvailable()) or S.VoidTorrent:IsAvailable() and S.PsychicLink:IsAvailable() and S.VoidTorrent:CooldownRemains() <= 4 and Player:BuffDown(S.VoidformBuff)) -- run_action_list,name=aoe,if=active_enemies>2|spell_targets.vampiric_touch>3 if (EnemiesCount10ySplash > 2 or EnemiesCount30y > 3) then local ShouldReturn = AoE(); if ShouldReturn then return ShouldReturn; end if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Pool for AoE()"; end end -- run_action_list,name=main if (true) then local ShouldReturn = Main(); if ShouldReturn then return ShouldReturn; end if HR.CastAnnotated(S.Pool, false, "WAIT") then return "Pool for Main()"; end end end end local function Init() S.VampiricTouchDebuff:RegisterAuraTracking() HR.Print("Shadow Priest rotation is currently a work in progress, but has been updated for patch 10.1.5.") end HR.SetAPL(258, APL, Init)