You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

519 lines
28 KiB

--- ============================ 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 Pet = Unit.Pet
local Target = Unit.Target
local Spell = HL.Spell
local Item = HL.Item
-- HeroRotation
local HR = HeroRotation
local AoEON = HR.AoEON
local CDsON = HR.CDsON
local Cast = HR.Cast
-- Num/Bool Helper Functions
local num = HR.Commons.Everyone.num
local bool = HR.Commons.Everyone.bool
-- Lua
--- ============================ CONTENT ============================
--- ======= APL LOCALS =======
-- Commons
local Everyone = HR.Commons.Everyone
-- GUI Settings
local Settings = {
General = HR.GUISettings.General,
Commons = HR.GUISettings.APL.Warlock.Commons,
Affliction = HR.GUISettings.APL.Warlock.Affliction
}
-- Spells
local S = Spell.Warlock.Affliction
-- Items
local I = Item.Warlock.Affliction
local OnUseExcludes = {
I.ConjuredChillglobe:ID(),
I.DesperateInvokersCodex:ID(),
}
-- Enemies
local Enemies40y, Enemies10ySplash, EnemiesCount10ySplash
local VarPSUp, VarVTUp, VarSRUp, VarCDDoTsUp, VarHasCDs, VarCDsActive
local BossFightRemains = 11111
local FightRemains = 11111
-- Register
HL:RegisterForEvent(function()
S.SeedofCorruption:RegisterInFlight()
S.ShadowBolt:RegisterInFlight()
S.Haunt:RegisterInFlight()
end, "LEARNED_SPELL_IN_TAB")
S.SeedofCorruption:RegisterInFlight()
S.ShadowBolt:RegisterInFlight()
S.Haunt:RegisterInFlight()
HL:RegisterForEvent(function()
BossFightRemains = 11111
FightRemains = 11111
end, "PLAYER_REGEN_ENABLED")
local function EvaluateAgony(TargetUnit)
-- target_if=remains<5,if=active_dot.agony<5
return (TargetUnit:DebuffRemains(S.AgonyDebuff) < 5)
end
local function EvaluateAgonyRefreshable(TargetUnit)
-- target_if=refreshable
return (TargetUnit:DebuffRefreshable(S.AgonyDebuff))
end
local function EvaluateSiphonLife(TargetUnit)
-- target_if=remains<5,if=active_dot.siphon_life<3
return (TargetUnit:DebuffRemains(S.SiphonLifeDebuff) < 3)
end
local function EvaluateCorruption(TargetUnit)
-- target_if=remains<5
return (TargetUnit:DebuffRemains(S.CorruptionDebuff) < 5)
end
local function EvaluateCorruptionRefreshable(TargetUnit)
-- target_if=refreshable
return (TargetUnit:DebuffRefreshable(S.CorruptionDebuff))
end
local function Precombat()
-- flask
-- food
-- augmentation
-- summon_pet - Moved to APL()
-- variable,name=cleave_apl,default=0,op=reset
-- grimoire_of_sacrifice,if=talent.grimoire_of_sacrifice.enabled
if S.GrimoireofSacrifice:IsCastable() then
if Cast(S.GrimoireofSacrifice, Settings.Affliction.GCDasOffGCD.GrimoireOfSacrifice) then return "grimoire_of_sacrifice precombat 2"; end
end
-- snapshot_stats
-- seed_of_corruption,if=spell_targets.seed_of_corruption_aoe>3
-- NYI precombat multi target
-- haunt
if S.Haunt:IsReady() then
if Cast(S.Haunt, nil, nil, not Target:IsSpellInRange(S.Haunt)) then return "haunt precombat 6"; end
end
-- unstable_affliction,if=!talent.soul_swap
if S.UnstableAffliction:IsReady() and (not S.SoulSwap:IsAvailable()) then
if Cast(S.UnstableAffliction, nil, nil, not Target:IsSpellInRange(S.UnstableAffliction)) then return "unstable_affliction precombat 8"; end
end
-- shadow_bolt
if S.ShadowBolt:IsReady() then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt precombat 10"; end
end
end
local function Variables()
-- variable,name=ps_up,op=set,value=dot.phantom_singularity.ticking|!talent.phantom_singularity
VarPSUp = (Target:DebuffUp(S.PhantomSingularityDebuff) or not S.PhantomSingularity:IsAvailable())
-- variable,name=vt_up,op=set,value=dot.vile_taint_dot.ticking|!talent.vile_taint
VarVTUp = (Target:DebuffUp(S.VileTaintDebuff) or not S.VileTaint:IsAvailable())
-- variable,name=sr_up,op=set,value=dot.soul_rot.ticking|!talent.soul_rot
VarSRUp = (Target:DebuffUp(S.SoulRotDebuff) or not S.SoulRot:IsAvailable())
-- variable,name=cd_dots_up,op=set,value=variable.ps_up&variable.vt_up&variable.sr_up
VarCDDoTsUp = (VarPSUp and VarVTUp and VarSRUp)
-- variable,name=has_cds,op=set,value=talent.phantom_singularity|talent.vile_taint|talent.soul_rot|talent.summon_darkglare
VarHasCDs = (S.PhantomSingularity:IsAvailable() or S.VileTaint:IsAvailable() or S.SoulRot:IsAvailable() or S.SummonDarkglare:IsAvailable())
-- variable,name=cds_active,op=set,value=!variable.has_cds|(pet.darkglare.active|variable.cd_dots_up|buff.power_infusion.react)
VarCDsActive = ((not VarHasCDs) or (HL.GuardiansTable.DarkglareDuration > 0 or VarCDDoTsUp or Player:BuffUp(S.PowerInfusionBuff)))
end
local function Items()
-- use_items,if=variable.cds_active
if (VarCDsActive) 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
if Settings.Commons.Enabled.Trinkets then
-- use_item,name=desperate_invokers_codex
if I.DesperateInvokersCodex:IsEquippedAndReady() then
if Cast(I.DesperateInvokersCodex, nil, Settings.Commons.DisplayStyle.Trinkets, not Target:IsInRange(45)) then return "desperate_invokers_codex items 2"; end
end
-- use_item,name=conjured_chillglobe
if I.ConjuredChillglobe:IsEquippedAndReady() then
if Cast(I.ConjuredChillglobe, nil, Settings.Commons.DisplayStyle.Trinkets) then return "conjured_chillglobe items 4"; end
end
end
end
local function oGCD()
if VarCDsActive then
-- potion,if=variable.cds_active
if Settings.Commons.Enabled.Potions then
local PotionSelected = Everyone.PotionSelected()
if PotionSelected and PotionSelected:IsReady() then
if Cast(PotionSelected, nil, Settings.Commons.DisplayStyle.Potions) then return "potion ogcd 2"; end
end
end
-- berserking,if=variable.cds_active
if S.Berserking:IsCastable() then
if Cast(S.Berserking, Settings.Commons.OffGCDasOffGCD.Racials) then return "berserking ogcd 4"; end
end
-- blood_fury,if=variable.cds_active
if S.BloodFury:IsCastable() then
if Cast(S.BloodFury, Settings.Commons.OffGCDasOffGCD.Racials) then return "blood_fury ogcd 6"; end
end
-- invoke_external_buff,name=power_infusion,if=variable.cds_active
-- Note: Not handling external buffs
-- fireblood,if=variable.cds_active
if S.Fireblood:IsCastable() then
if Cast(S.Fireblood, Settings.Commons.OffGCDasOffGCD.Racials) then return "fireblood ogcd 8"; end
end
-- ancestral_call,if=variable.cds_active
if S.AncestralCall:IsCastable() then
if Cast(S.AncestralCall, Settings.Commons.OffGCDasOffGCD.Racials) then return "ancestral_call ogcd 10"; end
end
end
end
local function AoE()
-- call_action_list,name=ogcd
if CDsON() then
local ShouldReturn = oGCD(); if ShouldReturn then return ShouldReturn; end
end
-- call_action_list,name=items
if CDsON() and (Settings.Commons.Enabled.Trinkets or Settings.Commons.Enabled.Items) then
local ShouldReturn = Items(); if ShouldReturn then return ShouldReturn; end
end
-- haunt
if S.Haunt:IsReady() then
if Cast(S.Haunt, nil, nil, not Target:IsSpellInRange(S.Haunt)) then return "haunt aoe 2"; end
end
-- vile_taint
if CDsON() and S.VileTaint:IsReady() then
if Cast(S.VileTaint, nil, nil, not Target:IsInRange(40)) then return "vile_taint aoe 4"; end
end
-- phantom_singularity
if CDsON() and S.PhantomSingularity:IsCastable() then
if Cast(S.PhantomSingularity, Settings.Affliction.GCDasOffGCD.PhantomSingularity, nil, not Target:IsSpellInRange(S.PhantomSingularity)) then return "phantom_singularity aoe 6"; end
end
-- soul_rot
if CDsON() and S.SoulRot:IsReady() then
if Cast(S.SoulRot, nil, nil, not Target:IsSpellInRange(S.SoulRot)) then return "soul_rot aoe 8"; end
end
-- unstable_affliction,if=remains<5
if S.UnstableAffliction:IsReady() and (Target:DebuffRemains(S.UnstableAfflictionDebuff) < 5) then
if Cast(S.UnstableAffliction, nil, nil, not Target:IsSpellInRange(S.UnstableAffliction)) then return "unstable_affliction aoe 9"; end
end
-- seed_of_corruption,if=dot.corruption.remains<5
if S.SeedofCorruption:IsReady() and Target:DebuffRemains(S.SeedofCorruptionDebuff) < 5 then
if Cast(S.SeedofCorruption, nil, nil, not Target:IsSpellInRange(S.SeedofCorruption)) then return "soul_rot aoe 10"; end
end
-- malefic_rapture,if=talent.malefic_affliction&buff.malefic_affliction.stack<3&talent.doom_blossom
if S.MaleficRapture:IsReady() and (S.MaleficAffliction:IsAvailable() and Player:BuffStack(S.MaleficAfflictionBuff) < 3 and S.DoomBlossom:IsAvailable()) then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture aoe 11"; end
end
-- agony,target_if=remains<5,if=active_dot.agony<5
if S.Agony:IsReady() then
if Everyone.CastCycle(S.Agony, Enemies40y, EvaluateAgony, not Target:IsSpellInRange(S.Agony)) then return "agony aoe 12"; end
end
-- summon_darkglare
if CDsON() and S.SummonDarkglare:IsCastable() then
if Cast(S.SummonDarkglare, Settings.Affliction.GCDasOffGCD.SummonDarkglare) then return "summon_darkglare aoe 14"; end
end
-- seed_of_corruption,if=talent.sow_the_seeds
if S.SeedofCorruption:IsReady() and S.SowTheSeeds:IsAvailable() then
if Cast(S.SeedofCorruption, nil, nil, not Target:IsSpellInRange(S.SeedofCorruption)) then return "soul_rot aoe 16"; end
end
-- malefic_rapture
if S.MaleficRapture:IsReady() then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture aoe 18"; end
end
-- drain_life,if=(buff.soul_rot.up|!talent.soul_rot)&buff.inevitable_demise.stack>10
if S.DrainLife:IsReady() and (Target:DebuffUp(S.SoulRotDebuff) or not S.SoulRot:IsAvailable()) and Player:BuffStack(S.InevitableDemiseBuff) > 10 then
if Cast(S.DrainLife, nil, nil, not Target:IsSpellInRange(S.DrainLife)) then return "drain_life aoe 20"; end
end
-- summon_soulkeeper,if=buff.tormented_soul.stack=10|buff.tormented_soul.stack>3&fight_remains<10
if S.SummonSoulkeeper:IsReady() and (S.SummonSoulkeeper:Count() == 10 or S.SummonSoulkeeper:Count() > 3 and FightRemains < 10) then
if Cast(S.SummonSoulkeeper) then return "soul_strike aoe 22"; end
end
-- siphon_life,target_if=remains<5,if=active_dot.siphon_life<3
if S.SiphonLife:IsReady() then
if Cast(S.SiphonLife, Enemies40y, EvaluateSiphonLife, not Target:IsSpellInRange(S.SiphonLife)) then return "siphon_life aoe 24"; end
end
-- drain_soul,interrupt_global=1
if S.DrainSoul:IsReady() then
if Cast(S.DrainSoul, nil, nil, not Target:IsSpellInRange(S.DrainSoul)) then return "drain_soul aoe 26"; end
end
-- shadow_bolt
if S.ShadowBolt:IsReady() then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt aoe 28"; end
end
end
local function Cleave()
-- call_action_list,name=ogcd
if CDsON() then
local ShouldReturn = oGCD(); if ShouldReturn then return ShouldReturn; end
end
-- call_action_list,name=items
if CDsON() and (Settings.Commons.Enabled.Trinkets or Settings.Commons.Enabled.Items) then
local ShouldReturn = Items(); if ShouldReturn then return ShouldReturn; end
end
-- haunt
if S.Haunt:IsReady() then
if Cast(S.Haunt, nil, nil, not Target:IsSpellInRange(S.Haunt)) then return "haunt cleave 2"; end
end
-- unstable_affliction,if=remains<5
if S.UnstableAffliction:IsReady() and (Target:DebuffRemains(S.UnstableAfflictionDebuff) < 5) then
if Cast(S.UnstableAffliction, nil, nil, not Target:IsSpellInRange(S.UnstableAffliction)) then return "unstable_affliction cleave 6"; end
end
-- agony,if=remains<5
if S.Agony:IsReady() and Target:DebuffRemains(S.AgonyDebuff) < 5 then
if Cast(S.Agony, nil, nil, not Target:IsSpellInRange(S.Agony)) then return "agony cleave 8"; end
end
-- agony,target_if=!(target=self.target)&remains<5
if S.Agony:IsReady() then
if Everyone.CastCycle(S.Agony, Enemies40y, EvaluateAgony, not Target:IsSpellInRange(S.Agony)) then return "agony cleave 10"; end
end
-- siphon_life,if=remains<5
if S.SiphonLife:IsCastable() and (Target:DebuffRemains(S.SiphonLifeDebuff) < 5) then
if Cast(S.SiphonLife, nil, nil, not Target:IsSpellInRange(S.SiphonLife)) then return "siphon_life cleave 12"; end
end
-- siphon_life,target_if=!(target=self.target)&remains<3
if S.SiphonLife:IsReady() then
if Cast(S.SiphonLife, Enemies40y, EvaluateSiphonLife, not Target:IsSpellInRange(S.SiphonLife)) then return "siphon_life cleave 14"; end
end
-- seed_of_corruption,if=!talent.absolute_corruption&dot.corruption.remains<5
if S.SeedofCorruption:IsReady() and not S.AbsoluteCorruption:IsAvailable() and Target:DebuffRemains(S.CorruptionDebuff) < 5 then
if Cast(S.SeedofCorruption, nil, nil, not Target:IsSpellInRange(S.SeedofCorruption)) then return "seed_of_corruption cleave 16"; end
end
-- corruption,target_if=remains<5&(talent.absolute_corruption|!talent.seed_of_corruption)
if S.Corruption:IsCastable() and (S.AbsoluteCorruption:IsAvailable() or not S.SeedofCorruption:IsAvailable()) then
if Everyone.CastCycle(S.Corruption, Enemies40y, EvaluateCorruption, not Target:IsSpellInRange(S.Corruption)) then return "corruption cleave 18"; end
end
-- phantom_singularity
if CDsON() and S.PhantomSingularity:IsCastable() then
if Cast(S.PhantomSingularity, Settings.Affliction.GCDasOffGCD.PhantomSingularity, nil, not Target:IsSpellInRange(S.PhantomSingularity)) then return "phantom_singularity cleave 20"; end
end
-- vile_taint
if CDsON() and S.VileTaint:IsReady() then
if Cast(S.VileTaint, nil, nil, not Target:IsInRange(40)) then return "vile_taint cleave 22"; end
end
-- soul_rot
if CDsON() and S.SoulRot:IsReady() then
if Cast(S.SoulRot, nil, nil, not Target:IsSpellInRange(S.SoulRot)) then return "soul_rot cleave 24"; end
end
-- summon_darkglare
if CDsON() and S.SummonDarkglare:IsCastable() then
if Cast(S.SummonDarkglare, Settings.Affliction.GCDasOffGCD.SummonDarkglare) then return "summon_darkglare cleave 26"; end
end
-- malefic_rapture,if=talent.malefic_affliction&buff.malefic_affliction.stack<3
if S.MaleficRapture:IsReady() and S.MaleficAffliction:IsAvailable() and Player:BuffStack(S.MaleficAfflictionBuff) < 3 then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture cleave 28"; end
end
-- malefic_rapture,if=talent.dread_touch&debuff.dread_touch.remains<gcd
if S.MaleficRapture:IsReady() and S.DreadTouch:IsAvailable() and Target:DebuffRemains(S.DreadTouchDebuff) < Player:GCD() then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture cleave 30"; end
end
-- malefic_rapture,if=!talent.dread_touch&buff.tormented_crescendo.up
if S.MaleficRapture:IsReady() and not S.DreadTouch:IsAvailable() and Player:BuffUp(S.TormentedCrescendoBuff) then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture cleave 32"; end
end
-- malefic_rapture,if=!talent.dread_touch&(dot.soul_rot.remains>cast_time|dot.phantom_singularity.remains>cast_time|dot.vile_taint_dot.remains>cast_time|pet.darkglare.active)
if S.MaleficRapture:IsReady() and not S.DreadTouch:IsAvailable() and (Target:DebuffRemains(S.SoulRotDebuff) > S.MaleficRapture:CastTime() or Target:DebuffRemains(S.PhantomSingularityDebuff) > S.MaleficRapture:CastTime() or Target:DebuffRemains(S.VileTaintDebuff) > S.MaleficRapture:CastTime() or HL.GuardiansTable.DarkglareDuration > 0) then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture cleave 34"; end
end
-- drain_soul,if=buff.nightfall.react
if S.DrainSoul:IsReady() and Player:BuffUp(S.NightfallBuff) then
if Cast(S.DrainSoul, nil, nil, not Target:IsSpellInRange(S.DrainSoul)) then return "drain_soul cleave 36"; end
end
-- shadow_bolt,if=buff.nightfall.react
if S.ShadowBolt:IsReady() and Player:BuffUp(S.NightfallBuff) then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt cleave 38"; end
end
-- drain_life,if=buff.inevitable_demise.stack>48|buff.inevitable_demise.stack>20&fight_remains<4
if S.DrainLife:IsReady() and (Player:BuffStack(S.InevitableDemiseBuff) > 48 or Player:BuffStack(S.InevitableDemiseBuff) > 20 and FightRemains < 4) then
if Cast(S.DrainLife, nil, nil, not Target:IsSpellInRange(S.DrainLife)) then return "drain_life cleave 40"; end
end
-- drain_life,if=buff.soul_rot.up&buff.inevitable_demise.stack>10
if S.DrainLife:IsReady() and Target:DebuffUp(S.SoulRotDebuff) and Player:BuffStack(S.InevitableDemiseBuff) > 10 then
if Cast(S.DrainLife, nil, nil, not Target:IsSpellInRange(S.DrainLife)) then return "drain_life cleave 42"; end
end
-- agony,target_if=refreshable
if S.Agony:IsReady() then
if Everyone.CastCycle(S.Agony, Enemies40y, EvaluateAgonyRefreshable, not Target:IsSpellInRange(S.Agony)) then return "agony cleave 44"; end
end
-- corruption,target_if=refreshable
if S.Corruption:IsCastable() then
if Everyone.CastCycle(S.Corruption, Enemies40y, EvaluateCorruptionRefreshable, not Target:IsSpellInRange(S.Corruption)) then return "corruption cleave 46"; end
end
-- drain_soul,interrupt_global=1
if S.DrainSoul:IsReady() then
if Cast(S.DrainSoul, nil, nil, not Target:IsSpellInRange(S.DrainSoul)) then return "drain_soul cleave 48"; end
end
-- shadow_bolt
if S.ShadowBolt:IsReady() then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt cleave 50"; end
end
end
--- ======= MAIN =======
local function APL()
-- Unit Update
Enemies40y = Player:GetEnemiesInRange(40)
Enemies10ySplash = Target:GetEnemiesInSplashRange(10)
if AoEON() then
EnemiesCount10ySplash = Target:GetEnemiesInSplashRangeCount(10)
else
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
end
-- summon_pet
if S.SummonPet:IsCastable() then
if Cast(S.SummonPet, Settings.Affliction.GCDasOffGCD.SummonPet) then return "summon_pet ooc"; end
end
if Everyone.TargetIsValid() then
-- Precombat
if (not Player:AffectingCombat()) then
local ShouldReturn = Precombat(); if ShouldReturn then return ShouldReturn; end
end
-- call_action_list,name=variables
Variables()
-- call_action_list,name=cleave,if=active_enemies!=1&active_enemies<4|variable.cleave_apl
if (EnemiesCount10ySplash > 1 and EnemiesCount10ySplash < 4) then
local ShouldReturn = Cleave(); if ShouldReturn then return ShouldReturn; end
end
-- call_action_list,name=aoe,if=active_enemies>3
if (EnemiesCount10ySplash > 3) then
local ShouldReturn = AoE(); if ShouldReturn then return ShouldReturn; end
end
-- call_action_list,name=ogcd
if CDsON() then
local ShouldReturn = oGCD(); if ShouldReturn then return ShouldReturn; end
end
-- call_action_list,name=items
if CDsON() and (Settings.Commons.Enabled.Trinkets or Settings.Commons.Enabled.Items) then
local ShouldReturn = Items(); if ShouldReturn then return ShouldReturn; end
end
-- malefic_rapture,if=talent.dread_touch&debuff.dread_touch.remains<2&(dot.agony.ticking&dot.corruption.ticking&(!talent.siphon_life|dot.siphon_life.ticking))&(!talent.phantom_singularity|!cooldown.phantom_singularity.ready)&(!talent.vile_taint|!cooldown.vile_taint.ready)&(!talent.soul_rot|!cooldown.soul_rot.ready)
if S.MaleficRapture:IsReady() and (S.DreadTouch:IsAvailable() and Target:DebuffRemains(S.DreadTouchDebuff) < 2 and (Target:DebuffUp(S.AgonyDebuff) and Target:DebuffUp(S.CorruptionDebuff) and ((not S.SiphonLife:IsAvailable()) or Target:DebuffUp(S.SiphonLifeDebuff))) and ((not S.PhantomSingularity:IsAvailable()) or S.PhantomSingularity:CooldownDown()) and ((not S.VileTaint:IsAvailable()) or S.VileTaint:CooldownDown()) and ((not S.SoulRot:IsAvailable()) or S.SoulRot:CooldownDown())) then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture main 2"; end
end
-- unstable_affliction,if=remains<5
if S.UnstableAffliction:IsReady() and (Target:DebuffRemains(S.UnstableAfflictionDebuff) < 5) then
if Cast(S.UnstableAffliction, nil, nil, not Target:IsSpellInRange(S.UnstableAffliction)) then return "unstable_affliction main 4"; end
end
-- agony,if=remains<5
if S.Agony:IsCastable() and (Target:DebuffRemains(S.AgonyDebuff) < 5) then
if Cast(S.Agony, nil, nil, not Target:IsSpellInRange(S.Agony)) then return "agony main 6"; end
end
-- corruption,if=remains<5
if S.Corruption:IsCastable() and (Target:DebuffRemains(S.CorruptionDebuff) < 5) then
if Cast(S.Corruption, nil, nil, not Target:IsSpellInRange(S.Corruption)) then return "corruption main 8"; end
end
-- siphon_life,if=remains<5
if S.SiphonLife:IsCastable() and (Target:DebuffRemains(S.SiphonLifeDebuff) < 5) then
if Cast(S.SiphonLife, nil, nil, not Target:IsSpellInRange(S.SiphonLife)) then return "siphon_life main 10"; end
end
-- haunt
if S.Haunt:IsReady() then
if Cast(S.Haunt, nil, nil, not Target:IsSpellInRange(S.Haunt)) then return "haunt main 12"; end
end
-- drain_soul,if=talent.shadow_embrace&(debuff.shadow_embrace.stack<3|debuff.shadow_embrace.remains<3)
if S.DrainSoul:IsReady() and (S.ShadowEmbrace:IsAvailable() and (Target:DebuffStack(S.ShadowEmbraceDebuff) < 3 or Target:DebuffRemains(S.ShadowEmbraceDebuff) < 3)) then
if Cast(S.DrainSoul, nil, nil, not Target:IsSpellInRange(S.DrainSoul)) then return "drain_soul main 14"; end
end
-- shadow_bolt,if=talent.shadow_embrace&(debuff.shadow_embrace.stack<3|debuff.shadow_embrace.remains<3)
if S.ShadowBolt:IsReady() and (S.ShadowEmbrace:IsAvailable() and (Target:DebuffStack(S.ShadowEmbraceDebuff) < 3 or Target:DebuffRemains(S.ShadowEmbraceDebuff) < 3)) then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt main 16"; end
end
-- phantom_singularity,if=!talent.soul_rot|cooldown.soul_rot.remains<=execute_time|cooldown.soul_rot.remains>=25
if CDsON() and S.PhantomSingularity:IsCastable() and ((not S.SoulRot:IsAvailable()) or S.SoulRot:CooldownRemains() <= S.PhantomSingularity:ExecuteTime() or S.SoulRot:CooldownRemains() >= 25) then
if Cast(S.PhantomSingularity, Settings.Affliction.GCDasOffGCD.PhantomSingularity, nil, not Target:IsSpellInRange(S.PhantomSingularity)) then return "phantom_singularity main 18"; end
end
-- vile_taint,if=!talent.soul_rot|cooldown.soul_rot.remains<=execute_time|talent.souleaters_gluttony.rank<2&cooldown.soul_rot.remains>=12
if CDsON() and S.VileTaint:IsReady() and ((not S.SoulRot:IsAvailable()) or S.SoulRot:CooldownRemains() <= S.VileTaint:ExecuteTime() or S.SouleatersGluttony:TalentRank() < 2 and S.SoulRot:CooldownRemains() >= 12) then
if Cast(S.VileTaint, nil, nil, not Target:IsInRange(40)) then return "vile_taint main 20"; end
end
-- soul_rot,if=variable.vt_up&variable.ps_up
if CDsON() and S.SoulRot:IsReady() and (VarVTUp and VarPSUp) then
if Cast(S.SoulRot, nil, nil, not Target:IsSpellInRange(S.SoulRot)) then return "soul_rot main 22"; end
end
-- summon_darkglare,if=variable.ps_up&variable.vt_up&variable.sr_up|cooldown.invoke_power_infusion_0.duration>0&cooldown.invoke_power_infusion_0.up&!talent.soul_rot
-- Note: Not handling Power Infusion
if CDsON() and S.SummonDarkglare:IsCastable() and (VarPSUp and VarVTUp and VarSRUp) then
if Cast(S.SummonDarkglare, Settings.Affliction.GCDasOffGCD.SummonDarkglare) then return "summon_darkglare main 24"; end
end
if S.MaleficRapture:IsReady() and (
-- malefic_rapture,if=soul_shard>4|(talent.tormented_crescendo&buff.tormented_crescendo.stack=1&soul_shard>3)
(Player:SoulShardsP() > 4 or (S.TormentedCrescendo:IsAvailable() and Player:BuffStack(S.TormentedCrescendoBuff) == 1 and Player:SoulShardsP() > 3)) or
-- malefic_rapture,if=talent.tormented_crescendo&buff.tormented_crescendo.react&!debuff.dread_touch.react
(S.TormentedCrescendo:IsAvailable() and Player:BuffUp(S.TormentedCrescendoBuff) and Target:DebuffDown(S.DreadTouchDebuff)) or
-- malefic_rapture,if=talent.tormented_crescendo&buff.tormented_crescendo.stack=2
(S.TormentedCrescendo:IsAvailable() and Player:BuffStack(S.TormentedCrescendoBuff) == 2) or
-- malefic_rapture,if=variable.cd_dots_up|variable.vt_up&soul_shard>1
(VarCDDoTsUp or VarVTUp and Player:SoulShardsP() > 1) or
-- malefic_rapture,if=talent.tormented_crescendo&talent.nightfall&buff.tormented_crescendo.react&buff.nightfall.react
(S.TormentedCrescendo:IsAvailable() and S.Nightfall:IsAvailable() and Player:BuffUp(S.TormentedCrescendoBuff) and Player:BuffUp(S.NightfallBuff))
) then
if Cast(S.MaleficRapture, nil, nil, not Target:IsInRange(100)) then return "malefic_rapture main 26"; end
end
-- drain_life,if=buff.inevitable_demise.stack>48|buff.inevitable_demise.stack>20&fight_remains<4
if S.DrainLife:IsReady() and (Player:BuffStack(S.InevitableDemiseBuff) > 48 or Player:BuffStack(S.InevitableDemiseBuff) > 20 and FightRemains < 4) then
if Cast(S.DrainLife, nil, nil, not Target:IsSpellInRange(S.DrainLife)) then return "drain_life main 28"; end
end
-- drain_soul,if=buff.nightfall.react
if S.DrainSoul:IsReady() and (Player:BuffUp(S.NightfallBuff)) then
if Cast(S.DrainSoul, nil, nil, not Target:IsSpellInRange(S.DrainSoul)) then return "drain_soul main 30"; end
end
-- shadow_bolt,if=buff.nightfall.react
if S.ShadowBolt:IsReady() and (Player:BuffUp(S.NightfallBuff)) then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt main 32"; end
end
-- agony,if=refreshable
if S.Agony:IsCastable() and (Target:DebuffRefreshable(S.AgonyDebuff)) then
if Cast(S.Agony, nil, nil, not Target:IsSpellInRange(S.Agony)) then return "agony main 34"; end
end
-- corruption,if=refreshable
if S.Corruption:IsCastable() and (Target:DebuffRefreshable(S.CorruptionDebuff)) then
if Cast(S.Corruption, nil, nil, not Target:IsSpellInRange(S.Corruption)) then return "corruption main 36"; end
end
-- drain_soul,interrupt=1
if S.DrainSoul:IsReady() then
if Cast(S.DrainSoul, nil, nil, not Target:IsSpellInRange(S.DrainSoul)) then return "drain_soul main 40"; end
end
-- shadow_bolt
if S.ShadowBolt:IsReady() then
if Cast(S.ShadowBolt, nil, nil, not Target:IsSpellInRange(S.ShadowBolt)) then return "shadow_bolt main 42"; end
end
end
end
local function OnInit()
HR.Print("Affliction Warlock rotation is currently a work in progress, but has been updated for patch 10.1.5.")
end
HR.SetAPL(265, APL, OnInit)