-- WarlockDestruction.lua
-- June 2018
local addon , ns = ...
local Hekili = _G [ addon ]
local class = Hekili.Class
local state = Hekili.State
-- Conduits
-- [-] ashen_remains
-- [x] combusting_engine
-- [-] duplicitous_havoc
-- [-] infernal_brand
if UnitClassBase ( ' player ' ) == ' WARLOCK ' then
local spec = Hekili : NewSpecialization ( 267 , true )
spec : RegisterResource ( Enum.PowerType . SoulShards , {
infernal = {
aura = " infernal " ,
last = function ( )
local app = state.buff . infernal.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = 0.5 ,
value = 0.1
} ,
chaos_shards = {
aura = " chaos_shards " ,
last = function ( )
local app = state.buff . chaos_shards.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = 0.5 ,
value = 0.2 ,
} ,
immolate = {
aura = " immolate " ,
last = function ( )
local app = state.debuff . immolate.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = function ( ) return state.debuff . immolate.tick_time end ,
value = 0.1
}
} , setmetatable ( {
actual = nil ,
max = nil ,
active_regen = 0 ,
inactive_regen = 0 ,
forecast = { } ,
times = { } ,
values = { } ,
fcount = 0 ,
regen = 0 ,
regenerates = false ,
} , {
__index = function ( t , k )
if k == ' count ' or k == ' current ' then return t.actual
elseif k == ' actual ' then
t.actual = UnitPower ( " player " , Enum.PowerType . SoulShards , true ) / 10
return t.actual
elseif k == ' max ' then
t.max = UnitPowerMax ( " player " , Enum.PowerType . SoulShards , true ) / 10
return t.max
end
end
} ) )
spec : RegisterResource ( Enum.PowerType . Mana )
spec : RegisterHook ( " spend " , function ( amt , resource )
if resource == " soul_shards " and amt > 0 then
if legendary.wilfreds_sigil_of_superior_summoning . enabled then
reduceCooldown ( " summon_infernal " , amt * 1.5 )
end
end
end )
-- Talents
spec : RegisterTalents ( {
flashover = 22038 , -- 267115
eradication = 22090 , -- 196412
soul_fire = 22040 , -- 6353
reverse_entropy = 23148 , -- 205148
internal_combustion = 21695 , -- 266134
shadowburn = 23157 , -- 17877
demon_skin = 19280 , -- 219272
burning_rush = 19285 , -- 111400
dark_pact = 19286 , -- 108416
inferno = 22480 , -- 270545
fire_and_brimstone = 22043 , -- 196408
cataclysm = 23143 , -- 152108
darkfury = 22047 , -- 264874
mortal_coil = 19291 , -- 6789
howl_of_terror = 23465 , -- 5484
roaring_blaze = 23155 , -- 205184
rain_of_chaos = 23156 , -- 266086
grimoire_of_sacrifice = 19295 , -- 108503
soul_conduit = 19284 , -- 215941
channel_demonfire = 23144 , -- 196447
dark_soul_instability = 23092 , -- 113858
} )
-- PvP Talents
spec : RegisterPvpTalents ( {
amplify_curse = 3504 , -- 328774
bane_of_fragility = 3502 , -- 199954
bane_of_havoc = 164 , -- 200546
bonds_of_fel = 5401 , -- 353753
casting_circle = 3510 , -- 221703
cremation = 159 , -- 212282
demon_armor = 3741 , -- 285933
essence_drain = 3509 , -- 221711
fel_fissure = 157 , -- 200586
gateway_mastery = 5382 , -- 248855
nether_ward = 3508 , -- 212295
shadow_rift = 5393 , -- 353294
} )
-- Auras
spec : RegisterAuras ( {
active_havoc = {
duration = function ( ) return level > 53 and 12 or 10 end ,
max_stack = 1 ,
generate = function ( ah )
if active_enemies > 1 then
if pvptalent.bane_of_havoc . enabled and debuff.bane_of_havoc . up and query_time - last_havoc < ah.duration then
ah.count = 1
ah.applied = last_havoc
ah.expires = last_havoc + ah.duration
ah.caster = " player "
return
elseif not pvptalent.bane_of_havoc . enabled and active_dot.havoc > 0 and query_time - last_havoc < ah.duration then
ah.count = 1
ah.applied = last_havoc
ah.expires = last_havoc + ah.duration
ah.caster = " player "
return
end
end
ah.count = 0
ah.applied = 0
ah.expires = 0
ah.caster = " nobody "
end
} ,
backdraft = {
id = 117828 ,
duration = 10 ,
type = " Magic " ,
max_stack = 2 ,
} ,
-- Going to need to keep an eye on this. active_dot.bane_of_havoc won't work due to no SPELL_AURA_APPLIED event.
bane_of_havoc = {
id = 200548 ,
duration = function ( ) return level > 53 and 12 or 10 end ,
max_stack = 1 ,
generate = function ( boh )
boh.applied = action.bane_of_havoc . lastCast
boh.expires = boh.applied > 0 and ( boh.applied + boh.duration ) or 0
end ,
} ,
blood_pact = {
id = 6307 ,
duration = 3600 ,
max_stack = 1 ,
} ,
burning_rush = {
id = 111400 ,
duration = 3600 ,
max_stack = 1 ,
} ,
channel_demonfire = {
id = 196447 ,
} ,
conflagrate = {
id = 265931 ,
duration = 8 ,
type = " Magic " ,
max_stack = 1 ,
copy = " roaring_blaze "
} ,
corruption = {
id = 146739 ,
duration = 14 ,
type = " Magic " ,
max_stack = 1 ,
} ,
curse_of_exhaustion = {
id = 334275 ,
duration = 8 ,
type = " Curse " ,
max_stack = 1 ,
} ,
curse_of_tongues = {
id = 1714 ,
duration = 60 ,
type = " Curse " ,
max_stack = 1 ,
} ,
curse_of_weakness = {
id = 702 ,
duration = 120 ,
type = " Curse " ,
max_stack = 1 ,
} ,
dark_pact = {
id = 108416 ,
duration = 20 ,
max_stack = 1 ,
} ,
dark_soul_instability = {
id = 113858 ,
duration = 20 ,
type = " Magic " ,
max_stack = 1 ,
copy = " dark_soul "
} ,
demonic_circle = {
id = 48018 ,
duration = 900 ,
max_stack = 1 ,
} ,
demonic_circle_teleport = {
id = 48020 ,
} ,
drain_life = {
id = 234153 ,
duration = function ( ) return 5 * haste * ( legendary.claw_of_endereth . enabled and 0.5 or 1 ) end ,
tick_time = function ( ) return haste * ( legendary.claw_of_endereth . enabled and 0.5 or 1 ) end ,
max_stack = 1 ,
} ,
eradication = {
id = 196414 ,
duration = 7 ,
max_stack = 1 ,
} ,
eye_of_kilrogg = {
id = 126 ,
} ,
fear = {
id = 118699 ,
duration = 20 ,
type = " Magic " ,
max_stack = 1 ,
} ,
fel_domination = {
id = 333889 ,
duration = 15 ,
type = " Magic " ,
max_stack = 1 ,
} ,
grimoire_of_sacrifice = {
id = 196099 ,
duration = 3600 ,
max_stack = 1 ,
} ,
havoc = {
id = 80240 ,
duration = function ( ) return level > 53 and 12 or 10 end ,
type = " Curse " ,
max_stack = 1 ,
} ,
howl_of_terror = {
id = 5484 ,
duration = 20 ,
type = " Magic " ,
max_stack = 1 ,
} ,
immolate = {
id = 157736 ,
duration = 18 ,
tick_time = function ( ) return 3 * haste end ,
type = " Magic " ,
max_stack = 1 ,
} ,
infernal = {
duration = 30 ,
generate = function ( )
local inf = buff.infernal
if pet.infernal . alive then
inf.count = 1
inf.applied = pet.infernal . expires - 30
inf.expires = pet.infernal . expires
inf.caster = " player "
return
end
inf.count = 0
inf.applied = 0
inf.expires = 0
inf.caster = " nobody "
end ,
} ,
infernal_awakening = {
id = 22703 ,
duration = 2 ,
max_stack = 1 ,
} ,
mana_divining_stone = {
id = 227723 ,
duration = 3600 ,
max_stack = 1 ,
} ,
mortal_coil = {
id = 6789 ,
duration = 3 ,
type = " Magic " ,
max_stack = 1 ,
} ,
rain_of_chaos = {
id = 266087 ,
duration = 30 ,
max_stack = 1
} ,
rain_of_fire = {
id = 5740 ,
} ,
reverse_entropy = {
id = 266030 ,
duration = 8 ,
type = " Magic " ,
max_stack = 1 ,
} ,
ritual_of_summoning = {
id = 698 ,
} ,
shadowburn = {
id = 17877 ,
duration = 5 ,
max_stack = 1 ,
} ,
shadowfury = {
id = 30283 ,
duration = 3 ,
type = " Magic " ,
max_stack = 1 ,
} ,
soul_leech = {
id = 108366 ,
duration = 15 ,
max_stack = 1 ,
} ,
soul_shards = {
id = 246985 ,
} ,
soulstone = {
id = 20707 ,
duration = 900 ,
max_stack = 1 ,
} ,
unending_breath = {
id = 5697 ,
duration = 600 ,
max_stack = 1 ,
} ,
unending_resolve = {
id = 104773 ,
duration = 8 ,
max_stack = 1 ,
} ,
-- Azerite Powers
chaos_shards = {
id = 287660 ,
duration = 2 ,
max_stack = 1
} ,
} )
spec : RegisterStateExpr ( " last_havoc " , function ( )
return pvptalent.bane_of_havoc . enabled and action.bane_of_havoc . lastCast or action.havoc . lastCast
end )
spec : RegisterStateExpr ( " havoc_remains " , function ( )
return buff.active_havoc . remains
end )
spec : RegisterStateExpr ( " havoc_active " , function ( )
return buff.active_havoc . up
end )
spec : RegisterHook ( " TimeToReady " , function ( wait , action )
local ability = action and class.abilities [ action ]
if ability and ability.spend and ability.spendType == " soul_shards " and ability.spend > soul_shard then
wait = 3600
end
return wait
end )
spec : RegisterStateExpr ( " soul_shard " , function ( ) return soul_shards.current end )
local lastTarget
spec : RegisterHook ( " COMBAT_LOG_EVENT_UNFILTERED " , function ( event , _ , subtype , _ , sourceGUID , sourceName , _ , _ , destGUID , destName , destFlags , _ , spellID , spellName )
if sourceGUID == GUID and subtype == " SPELL_CAST_SUCCESS " and destGUID ~= nil and destGUID ~= " " then
lastTarget = destGUID
end
end )
spec : RegisterHook ( " reset_precast " , function ( )
last_havoc = nil
soul_shards.actual = nil
for i = 1 , 5 do
local up , _ , start , duration , id = GetTotemInfo ( i )
if up and id == 136219 then
summonPet ( " infernal " , start + duration - now )
break
end
end
if pvptalent.bane_of_havoc . enabled then
class.abilities . havoc = class.abilities . bane_of_havoc
else
class.abilities . havoc = class.abilities . real_havoc
end
end )
spec : RegisterCycle ( function ( )
if active_enemies == 1 then return end
-- For Havoc, we want to cast it on a different target.
if this_action == " havoc " and class.abilities . havoc.key == " havoc " then return " cycle " end
if ( debuff.havoc . up or FindUnitDebuffByID ( " target " , 80240 , " PLAYER " ) ) and not legendary.odr_shawl_of_the_ymirjar . enabled then
return " cycle "
end
end )
local Glyphed = IsSpellKnownOrOverridesKnown
-- Fel Imp 58959
spec : RegisterPet ( " imp " ,
function ( ) return Glyphed ( 112866 ) and 58959 or 416 end ,
" summon_imp " ,
3600 )
-- Voidlord 58960
spec : RegisterPet ( " voidwalker " ,
function ( ) return Glyphed ( 112867 ) and 58960 or 1860 end ,
" summon_voidwalker " ,
3600 )
-- Observer 58964
spec : RegisterPet ( " felhunter " ,
function ( ) return Glyphed ( 112869 ) and 58964 or 417 end ,
" summon_felhunter " ,
3600 )
-- Fel Succubus 120526
-- Shadow Succubus 120527
-- Shivarra 58963
spec : RegisterPet ( " succubus " ,
function ( )
if Glyphed ( 240263 ) then return 120526
elseif Glyphed ( 240266 ) then return 120527
elseif Glyphed ( 112868 ) then return 58963 end
return 1863
end ,
3600 )
-- Wrathguard 58965
spec : RegisterPet ( " felguard " ,
function ( ) return Glyphed ( 112870 ) and 58965 or 17252 end ,
" summon_felguard " ,
3600 )
-- Abilities
spec : RegisterAbilities ( {
banish = {
id = 710 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = false ,
handler = function ( )
if debuff.banish . up then removeDebuff ( " target " , " banish " )
else applyDebuff ( " target " , " banish " ) end
end ,
} ,
burning_rush = {
id = 111400 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = true ,
handler = function ( )
if buff.burning_rush . up then removeBuff ( " burning_rush " )
else applyBuff ( " burning_rush " ) end
end ,
} ,
cataclysm = {
id = 152108 ,
cast = 2 ,
cooldown = 30 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
talent = " cataclysm " ,
handler = function ( )
applyDebuff ( " target " , " immolate " )
active_dot.immolate = max ( active_dot.immolate , true_active_enemies )
removeDebuff ( " target " , " combusting_engine " )
end ,
} ,
channel_demonfire = {
id = 196447 ,
cast = 3 ,
channeled = true ,
cooldown = 25 ,
hasteCD = true ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
talent = " channel_demonfire " ,
usable = function ( ) return active_dot.immolate > 0 end ,
} ,
chaos_bolt = {
id = 116858 ,
cast = function ( ) return ( buff.backdraft . up and 0.7 or 1 ) * ( buff.madness_of_the_azjaqir . up and 0.8 or 1 ) * 3 * haste end ,
cooldown = 0 ,
gcd = " spell " ,
spend = 2 ,
spendType = " soul_shards " ,
startsCombat = true ,
cycle = function ( ) return talent.eradication . enabled and " eradication " or nil end ,
velocity = 16 ,
handler = function ( )
if talent.eradication . enabled then
applyDebuff ( " target " , " eradication " )
active_dot.eradication = max ( active_dot.eradication , active_dot.bane_of_havoc )
end
if talent.internal_combustion . enabled and debuff.immolate . up then
if debuff.immolate . remains <= 5 then removeDebuff ( " target " , " immolate " )
else debuff.immolate . expires = debuff.immolate . expires - 5 end
end
if legendary.madness_of_the_azjaqir . enabled then
applyBuff ( " madness_of_the_azjaqir " )
end
removeStack ( " backdraft " )
removeStack ( " crashing_chaos " )
end ,
auras = {
madness_of_the_azjaqir = {
id = 337170 ,
duration = 3 ,
max_stack = 1
}
}
} ,
--[[ command_demon = {
id = 119898 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = true ,
handler = function ( )
end ,
} , ] ]
conflagrate = {
id = 17962 ,
cast = 0 ,
charges = function ( ) return legendary.cinders_of_the_azjaqir . enabled and 3 or 2 end ,
cooldown = function ( ) return ( legendary.cinders_of_the_azjaqir . enabled and 10 or 13 ) * haste end ,
recharge = function ( ) return ( legendary.cinders_of_the_azjaqir . enabled and 10 or 13 ) * haste end ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
cycle = function ( ) return talent.roaring_blaze . enabled and " conflagrate " or nil end ,
handler = function ( )
gain ( 0.5 , " soul_shards " )
applyBuff ( " backdraft " , nil , talent.flashover . enabled and 2 or 1 )
if talent.roaring_blaze . enabled then
applyDebuff ( " target " , " conflagrate " )
active_dot.conflagrate = max ( active_dot.conflagrate , active_dot.bane_of_havoc )
end
if conduit.combusting_engine . enabled then
applyDebuff ( " target " , " combusting_engine " )
end
end ,
auras = {
-- Conduit
combusting_engine = {
id = 339986 ,
duration = 30 ,
max_stack = 1
}
}
} ,
corruption = {
id = 172 ,
cast = 1.885 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136118 ,
handler = function ( )
applyDebuff ( " target " , " corruption " )
end ,
} ,
--[[ create_healthstone = {
id = 6201 ,
cast = 2.97 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
end ,
} ,
create_soulwell = {
id = 29893 ,
cast = 2.97 ,
cooldown = 120 ,
gcd = " spell " ,
spend = 0.05 ,
spendType = " mana " ,
toggle = " cooldowns " ,
startsCombat = true ,
handler = function ( )
end ,
} , ] ]
curse_of_exhaustion = {
id = 334275 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = true ,
texture = 136162 ,
handler = function ( )
applyDebuff ( " target " , " curse_of_exhaustion " )
removeDebuff ( " target " , " curse_of_tongues " )
removeDebuff ( " target " , " curse_of_weakness " )
end ,
} ,
curse_of_tongues = {
id = 1714 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136140 ,
handler = function ( )
removeDebuff ( " target " , " curse_of_exhaustion " )
applyDebuff ( " target " , " curse_of_tongues " )
removeDebuff ( " target " , " curse_of_weakness " )
end ,
} ,
curse_of_weakness = {
id = 702 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136138 ,
handler = function ( )
removeDebuff ( " target " , " curse_of_exhaustion " )
removeDebuff ( " target " , " curse_of_tongues " )
applyDebuff ( " target " , " curse_of_weakness " )
end ,
} ,
dark_pact = {
id = 108416 ,
cast = 0 ,
cooldown = 60 ,
gcd = " off " ,
defensive = true ,
startsCombat = true ,
talent = " dark_pact " ,
usable = function ( ) return health.pct > 20 , " insufficient health " end ,
handler = function ( )
applyBuff ( " dark_pact " )
spend ( 0.2 * health.max , " health " )
end ,
} ,
dark_soul_instability = {
id = 113858 ,
cast = 0 ,
cooldown = 120 ,
gcd = " off " ,
toggle = " cooldowns " ,
startsCombat = false ,
talent = " dark_soul_instability " ,
handler = function ( )
applyBuff ( " dark_soul_instability " )
end ,
} ,
--[[ demonic_circle = {
id = 48018 ,
cast = 0.49995 ,
cooldown = 10 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
-- applies demonic_circle (48018)
end ,
} ,
demonic_circle_teleport = {
id = 48020 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = 0.03 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
-- applies demonic_circle_teleport (48020)
end ,
} ,
demonic_gateway = {
id = 111771 ,
cast = 1.98 ,
cooldown = 10 ,
gcd = " spell " ,
spend = 0.2 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
end ,
} , ] ]
drain_life = {
id = 234153 ,
cast = function ( ) return 5 * haste * ( legendary.claw_of_endereth . enabled and 0.5 or 1 ) end ,
channeled = true ,
breakable = true ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return debuff.soul_rot . up and 0 or 0.03 end ,
spendType = " mana " ,
startsCombat = true ,
start = function ( )
applyDebuff ( " target " , " drain_life " )
end ,
finish = function ( )
if conduit.accrued_vitality . enabled then applyBuff ( " accrued_vitality " ) end
end ,
} ,
--[[ enslave_demon = {
id = 1098 ,
cast = 2.97 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
end ,
} ,
eye_of_kilrogg = {
id = 126 ,
cast = 1.98 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.03 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
end ,
} , ] ]
fear = {
id = 5782 ,
cast = 1.7 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.15 ,
spendType = " mana " ,
startsCombat = true ,
handler = function ( )
applyDebuff ( " target " , " fear " )
end ,
} ,
fel_domination = {
id = 333889 ,
cast = 0 ,
cooldown = function ( ) return 180 + conduit.fel_celerity . mod * 0.001 end ,
gcd = " spell " ,
startsCombat = true ,
texture = 237564 ,
essential = true ,
nomounted = true ,
nobuff = " grimoire_of_sacrifice " ,
handler = function ( )
applyBuff ( " fel_domination " )
end ,
} ,
grimoire_of_sacrifice = {
id = 108503 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
startsCombat = false ,
talent = " grimoire_of_sacrifice " ,
nobuff = " grimoire_of_sacrifice " ,
essential = true ,
usable = function ( ) return pet.active end ,
handler = function ( )
if pet.felhunter . alive then dismissPet ( " felhunter " )
elseif pet.imp . alive then dismissPet ( " imp " )
elseif pet.succubus . alive then dismissPet ( " succubus " )
elseif pet.voidawalker . alive then dismissPet ( " voidwalker " ) end
applyBuff ( " grimoire_of_sacrifice " )
end ,
} ,
havoc = {
id = 80240 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
texture = 460695 ,
indicator = function ( ) return active_enemies > 1 and ( lastTarget == " lastTarget " or target.unit == lastTarget ) and " cycle " or nil end ,
cycle = " havoc " ,
bind = " bane_of_havoc " ,
usable = function ( ) return not pvptalent.bane_of_havoc . enabled and active_enemies > 1 , " requires multiple targets and no bane_of_havoc " end ,
handler = function ( )
if class.abilities . havoc.indicator == " cycle " then
active_dot.havoc = active_dot.havoc + 1
if legendary.odr_shawl_of_the_ymirjar . enabled then active_dot.odr_shawl_of_the_ymirjar = 1 end
else
applyDebuff ( " target " , " havoc " )
if legendary.odr_shawl_of_the_ymirjar . enabled then applyDebuff ( " target " , " odr_shawl_of_the_ymirjar " ) end
end
applyBuff ( " active_havoc " )
end ,
copy = " real_havoc " ,
auras = {
odr_shawl_of_the_ymirjar = {
id = 337164 ,
duration = function ( ) return class.auras . havoc.duration end ,
max_stack = 1
}
}
} ,
bane_of_havoc = {
id = 200546 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
startsCombat = true ,
texture = 1380866 ,
cycle = " DoNotCycle " ,
bind = " havoc " ,
pvptalent = " bane_of_havoc " ,
usable = function ( ) return active_enemies > 1 , " requires multiple targets " end ,
handler = function ( )
applyDebuff ( " target " , " bane_of_havoc " )
active_dot.bane_of_havoc = active_enemies
applyBuff ( " active_havoc " )
end ,
} ,
health_funnel = {
id = 755 ,
cast = 5 ,
channeled = true ,
breakable = true ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = true ,
texture = 136168 ,
usable = function ( ) return pet.active and pet.alive and pet.health_pct < 100 , " requires pet " end ,
start = function ( )
applyBuff ( " health_funnel " )
end ,
} ,
howl_of_terror = {
id = 5484 ,
cast = 0 ,
cooldown = 40 ,
gcd = " spell " ,
startsCombat = true ,
texture = 607852 ,
talent = " howl_of_terror " ,
handler = function ( )
applyDebuff ( " target " , " howl_of_terror " )
end ,
} ,
immolate = {
id = 348 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
cycle = function ( ) return not debuff.immolate . refreshable and " immolate " or nil end ,
handler = function ( )
applyDebuff ( " target " , " immolate " )
active_dot.immolate = max ( active_dot.immolate , active_dot.bane_of_havoc )
removeDebuff ( " target " , " combusting_engine " )
end ,
} ,
incinerate = {
id = 29722 ,
cast = function ( )
if buff.chaotic_inferno . up then return 0 end
return ( buff.backdraft . up and 0.7 or 1 ) * 2 * haste
end ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
velocity = 25 ,
handler = function ( )
removeBuff ( " chaotic_inferno " )
removeStack ( " backdraft " )
removeStack ( " decimating_bolt " )
-- Using true_active_enemies for resource predictions' sake.
gain ( ( 0.2 + ( talent.fire_and_brimstone . enabled and ( ( true_active_enemies - 1 ) * 0.1 ) or 0 ) ) * ( legendary.embers_of_the_diabolic_raiment . enabled and 2 or 1 ) , " soul_shards " )
end ,
} ,
mortal_coil = {
id = 6789 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
texture = 607853 ,
talent = " mortal_coil " ,
handler = function ( )
applyDebuff ( " target " , " mortal_coil " )
active_dot.mortal_coil = max ( active_dot.mortal_coil , active_dot.bane_of_havoc )
gain ( 0.2 * health.max , " health " )
end ,
} ,
rain_of_fire = {
id = 5740 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 3 ,
spendType = " soul_shards " ,
startsCombat = true ,
handler = function ( )
-- establish that RoF is ticking?
-- need a CLEU handler?
end ,
} ,
--[[ ritual_of_doom = {
id = 342601 ,
cast = 0 ,
cooldown = 3600 ,
gcd = " spell " ,
spend = 1 ,
spendType = " soul_shards " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 538538 ,
handler = function ( )
end ,
} ,
ritual_of_summoning = {
id = 698 ,
cast = 0 ,
cooldown = 120 ,
gcd = " spell " ,
spend = 0 ,
spendType = " mana " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 136223 ,
handler = function ( )
end ,
} , ] ]
shadowburn = {
id = 17877 ,
cast = 0 ,
charges = 2 ,
cooldown = 12 ,
recharge = 12 ,
gcd = " spell " ,
spend = 1 ,
spendType = " soul_shards " ,
startsCombat = true ,
texture = 136191 ,
talent = " shadowburn " ,
handler = function ( )
gain ( 0.3 , " soul_shards " )
applyDebuff ( " target " , " shadowburn " )
active_dot.shadowburn = max ( active_dot.shadowburn , active_dot.bane_of_havoc )
end ,
} ,
shadowfury = {
id = 30283 ,
cast = 1.5 ,
cooldown = function ( ) return talent.darkfury . enabled and 45 or 60 end ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 607865 ,
handler = function ( )
applyDebuff ( " target " , " shadowfury " )
end ,
} ,
singe_magic = {
id = 132411 ,
known = function ( ) return IsSpellKnownOrOverridesKnown ( 132411 ) or IsSpellKnownOrOverridesKnown ( 119905 ) end ,
cast = 0 ,
cooldown = 15 ,
gcd = " spell " ,
spend = 0.03 ,
spendType = " mana " ,
startsCombat = true ,
buff = " dispellable_magic " ,
usable = function ( )
return pet.imp . alive or buff.grimoire_of_sacrifice . up , " requires imp or grimoire_of_sacrifice "
end ,
handler = function ( )
removeBuff ( " dispellable_magic " )
end ,
} ,
soul_fire = {
id = 6353 ,
cast = function ( ) return 4 * haste end ,
cooldown = 20 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
talent = " soul_fire " ,
handler = function ( )
gain ( 0.4 , " soul_shards " )
end ,
} ,
soulstone = {
id = 20707 ,
cast = 3 ,
cooldown = 600 ,
gcd = " spell " ,
startsCombat = false ,
handler = function ( )
applyBuff ( " soulstone " )
end ,
} ,
spell_lock = {
id = 19647 ,
known = function ( ) return IsSpellKnownOrOverridesKnown ( 119910 ) or IsSpellKnownOrOverridesKnown ( 132409 ) end ,
cast = 0 ,
cooldown = 24 ,
gcd = " off " ,
startsCombat = true ,
-- texture = ?
toggle = " interrupts " ,
interrupt = true ,
debuff = " casting " ,
readyTime = state.timeToInterrupt ,
handler = function ( )
interrupt ( )
end ,
} ,
subjugate_demon = {
id = 1098 ,
cast = 3 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136154 ,
usable = function ( ) return target.is_demon and target.level < level + 2 , " requires demon target " end ,
handler = function ( )
summonPet ( " controlled_demon " )
end ,
} ,
summon_felhunter = {
id = 691 ,
cast = function ( ) return ( buff.fel_domination . up and 0.5 or 6 ) * haste end ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.fel_domination . up and 0 or 1 end ,
spendType = " soul_shards " ,
essential = true ,
usable = function ( )
if pet.alive then return false , " pet is alive "
elseif buff.grimoire_of_sacrifice . up then return false , " grimoire_of_sacrifice is up " end
return true
end ,
handler = function ( )
summonPet ( " felhunter " )
removeBuff ( " fel_domination " )
end ,
copy = { 112869 }
} ,
summon_imp = {
id = 688 ,
cast = function ( ) return ( buff.fel_domination . up and 0.5 or 6 ) * haste end ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.fel_domination . up and 0 or 1 end ,
spendType = " soul_shards " ,
essential = true ,
bind = " summon_pet " ,
nomounted = true ,
usable = function ( )
if pet.alive then return false , " pet is alive "
elseif buff.grimoire_of_sacrifice . up then return false , " grimoire_of_sacrifice is up " end
return true
end ,
handler = function ( )
summonPet ( " imp " )
removeBuff ( " fel_domination " )
end ,
copy = " summon_pet "
} ,
summon_infernal = {
id = 1122 ,
cast = 0 ,
cooldown = function ( ) return ( essence.vision_of_perfection . enabled and 0.87 or 1 ) * 180 - ( azerite.crashing_chaos . enabled and 15 or 0 ) end ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
toggle = " cooldowns " ,
startsCombat = true ,
handler = function ( )
summonPet ( " infernal " , 30 )
if talent.rain_of_chaos . enabled then applyBuff ( " rain_of_chaos " ) end
if azerite.crashing_chaos . enabled then applyBuff ( " crashing_chaos " , 3600 , 8 ) end
end ,
} ,
summon_voidwalker = {
id = 697 ,
cast = function ( ) return ( buff.fel_domination . up and 0.5 or 6 ) * haste end ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.fel_domination . up and 0 or 1 end ,
spendType = " soul_shards " ,
startsCombat = true ,
texture = 136221 ,
usable = function ( )
if pet.alive then return false , " pet is alive "
elseif buff.grimoire_of_sacrifice . up then return false , " grimoire_of_sacrifice is up " end
return true
end ,
handler = function ( )
summonPet ( " voidwalker " )
removeBuff ( " fel_domination " )
end ,
} ,
unending_breath = {
id = 5697 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
startsCombat = false ,
handler = function ( )
applyBuff ( " unending_breath " )
end ,
} ,
unending_resolve = {
id = 104773 ,
cast = 0 ,
cooldown = 180 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
defensive = true ,
toggle = " defensives " ,
startsCombat = false ,
handler = function ( )
applyBuff ( " unending_resolve " )
end ,
} ,
} )
spec : RegisterOptions ( {
enabled = true ,
aoe = 3 ,
cycle = true ,
nameplates = false ,
nameplateRange = 8 ,
damage = true ,
damageDots = false ,
damageExpiration = 6 ,
potion = " spectral_intellect " ,
package = " Destruction " ,
} )
spec : RegisterSetting ( " havoc_macro_text " , nil , {
name = " When |T460695:0|t Havoc is shown with a |TInterface \\ Addons \\ Hekili \\ Textures \\ Cycle:0|t indicator, the addon is recommending that you cast Havoc on a different target (without swapping). A mouseover macro is useful for this and an example is included below. " ,
type = " description " ,
width = " full " ,
fontSize = " medium "
} )
spec : RegisterSetting ( " havoc_macro " , nil , {
name = " |T460695:0|t Havoc Macro " ,
type = " input " ,
width = " full " ,
multiline = 2 ,
get = function ( ) return " #showtooltip \n /use [@mouseover,harm,nodead][] " .. class.abilities . havoc.name end ,
set = function ( ) end ,
} )
spec : RegisterSetting ( " immolate_macro_text " , nil , {
name = function ( ) return " When |T " .. GetSpellTexture ( 348 ) .. " :0|t Immolate is shown with a |TInterface \\ Addons \\ Hekili \\ Textures \\ Cycle:0|t indicator, the addon is recommending that you cast Immolate on a different target (without swapping). A mouseover macro is useful for this and an example is included below. " end ,
type = " description " ,
width = " full " ,
fontSize = " medium "
} )
spec : RegisterSetting ( " immolate_macro " , nil , {
name = function ( ) return " |T " .. GetSpellTexture ( 348 ) .. " :0|t Immolate Macro " end ,
type = " input " ,
width = " full " ,
multiline = 2 ,
get = function ( ) return " #showtooltip \n /use [@mouseover,harm,nodead][] " .. class.abilities . immolate.name end ,
set = function ( ) end ,
} )
spec : RegisterPack ( " Destruction " , 20211207 , [ [ dOK ( 1 aqiIsEKev1Mik ( eLsPrrjvNIsjRsIk9kvjZcu6wsuXUK0VuPAykvCmvklJsYZafmnkPCnqrBJsP6BeLY4avW5avuRtIQ08OuCpvY ( uQ0bjkvzHQs9qqfQjsuQ0fbfIncQq6Jsuf5KsufwjLQzckKANsKFkrvulLOurpvKPQuARGkeFLOuvJLOuH9k1Ff1GvXHrTyqEmWKvYLH2mr ( SsXOvfNwy1GcjVguPzJ42QQDt1VvmCIQJtPuSCcphPPt66sy7uIVlrz8Gc15bvTEqfz ( kvTFkUV1B70IvSlz1owD7Mv7iBvRGbRGZwt26KcVCStYzaC5nyNC ( JDs2fPQOaOX4Dsodpz4vVTt0PqaWo9OQCA59 ( 9 nH ( uavbZ ) on ( fewJXbcwsVtJp4ENGkcIwE4nuNwSIDjR2XQB3SAhzRAfmyfC2Aw1jUqFgrNsXhoUtpXAHEd1PfsbDs2fPQOaOX4MJSplidaUg7Lgl4hcfMZnyawZXQDS6MXUXoC8d7BqA51yVCmh4OeK ( aeSKEhoYqyniO5KgIf0vZbWoaj5qYCapSVbxMJoMt4kkefY1Civ7ejOkT32jKsrhG0EBx6wVTtmqJX7uzJGSSGHNfiDC2byNqNHi4QF3AxYQEBNyGgJ3Pp ( hb85rktkaXkVei ) PDcDgIGR ( DRDjyO32jgOX4DcImZkpsz9bZOJF47e6mebx97w7swR32jgOX4DAtblwb75rkZWjum6tNqNHi4QF3AxcM92oXangVtIqUCcMdptLZaStOZqeC1VBTlz792oXangVtsdOGIRmdNqrOygc5FNqNHi4QF3Axs26TDIbAmENKxicj4dFtgIWuTtOZqeC1VBTlbh6TDcDgIGR ( DNaIqrrWDszXguRpit0NSCGAo7AoWHDmN97nhLfBqT ( GmrFYYbQ5yJ5y1oMZ ( 9 MJuS5rZc8ZHtnhBmhR2XC2V3CuwSb1QgFmRtwoqZwTJ5SR5yTD6ed0y8ojqwE4BYse ( J0w7sW5EBNyGgJ3jW4a0vbR4klr4p2j0zicU63T2LUTtVTtOZqeC1V7eqekkcUtqfssvbcGlbP0S0iayvGFoCANyGgJ3j9bZfo0u4RS0iayRT2Ph2Ya6TDPB92oHodrWv ) UtarOOi4obvijvHyaCxcwsRRPm3CKXCOtbjtFyXYC29YCUzoYyo0PGKPpSyzo2CzowRtmqJX7eyCjcVrWk2AxYQEBNqNHi4QF3jGiuueCNamvZA8rZXgZ5HTmGSa ) C40oXangVt0PGKLcb2Axcg6TDcDgIGR ( DNaIqrrWDcWunRXhnhBmNh2YaYc8ZHtnhzmh6uqGcFvjiVYqWNrym ) LtWk6mebxDIbAmENwii ( Sg ( Mm0q0w7swR32j0zicU63Dcicffb3jat1SgF0CSXCEyldilWphoTtmqJX7efmfIW3K1qFWw7sWS32j0zicU63Dcicffb3jLjOR1Wvu4mjdMpubvJXROZqeCzoYyoc8ZHtnhBmNvHG1yCZPCnNDQW0C2V3CKL5OmbDTgUIcNjzW8HkOAmEfDgIGlZrgZrGscK ( WqeStmqJX7u8 ) dHvS1UKT3B7e6mebx97obeHIIG7eGPAwJpAo2yopSLbKf4NdN2jgOX4Dc8WdndneT1UKS1B7ed0y8orF41uguHW7e6mebx97w7sWHEBNqNHi4QF3jGiuueCNamvZA8rZXgZ5HTmGSa ) C40oXangVtHdchfSIT2ANKlqW8HyT32LU1B7e6mebx97oXangVtsijVMF4SgJ3PfsbIqUgJ3jyeymckuCzoqO0iqZbmFiwnhiCt40Q5i7baOCLAo ( 4 LZdl ( sfeZHbAmo1CgNaFTtarOOi4oPXhnNDnNDmhzmhzzoYrTYKWc2AxYQEBNyGgJ3jAX ) pEo ( Y7e6mebx97w7sWqVTtOZqeC1V7KZFSt68X8iL ) JtvXuqZGXPQOaOX40oXangVt68X8iL ) JtvXuqZGXPQOaOX40w7swR32j0zicU63DY5p2j6qq ( HMPiqGAwrWJh2McStmqJX7eDii ) qZueiqnRi4XdBtb2AxcM92oXangVtseK ( aeSK2j0zicU63T2LS9EBNqNHi4QF3jGiuueCNuMGUw3iI ) ecmpszkdeHuaWk6mebxDIbAmEN2iI ) ecmpszkdeHuaWw7sYwVTtOZqeC1V7KZFSt0hEnLHR8iGYJuwhXhDTtmqJX7e9Hxtz4kpcO8iL1r8rxBTlbh6TDIbAmENOtbjlfcStOZqeC1VBTlbN7TDIbAmENcheokyf7e6mebx97wBTt8G92U0TEBNqNHi4QF3jGiuueCNKJAnCju4mPYanSGMJmMJ1nhzzoGziRPmV ( WwgqvG8cEZz ) EZHbAybZOJ ) aPMZUMdmyo2QtmqJX7KGdppszPqGT2LSQ32jgOX4DIofKSy0oHodrWv ) U1Uem0B7e6mebx97obeHIIG70A0A8 ) dHvSkWpho1C21CamvZA8XoXangVtGh2DKKx4FCPqGT2LSwVTtOZqeC1V7ed0y8of ) ) qyf7eqekkcUtc8ZHtnhBmhyAoYyow3CKL5OmbDTcyLbe4P ) k6mebxMZ ( 9 MdygYAkZRawzabE6VkWpho1C21Ce4NdNAo2Qta4bemRSydQ0U0Tw7sWS32j0zicU63DIbAmENamHKzGgJNjbv7ejOA25p2jWI2AxY27TDcDgIGR ( DNyGgJ3jatizgOX4zsq1orcQMD ( JDcPu0biT1UKS1B7e6mebx97oXangVtpSLb0jGiuueCNyGgwWm64pqQ5yJ5yTobGhqWSYInOs7s3ATlbh6TDIbAmENeC45rklfcStOZqeC1VBTlbN7TDcDgIGR ( DNyGgJ3Ph2Ya6eaEabZkl2GkTlDR1U0TD6TDcDgIGR ( DNaIqrrWDY6MdDkiqHVQeKxzi4ZimM ) YjyfDgIGlZz ) EZrwMJYe01QuiWm7RmKi ( uDCSIodrWL5yRoXangVtleeFwdFtgAiARDPB36TDcDgIGR ( DNaIqrrWDszc6AvkeyM9vgseFQoowrNHi4YCKXCGkKKQqmaUlblP1c5MJmMdDkiz6dlwMJnMdmnNYXC2PAL5uUMdd0WcMrh ) bs7ed0y8ofoiCuWk2Ax6Mv92oXangVt0PGKLcb2j0zicU63T2LUbd92oHodrWv ) UtarOOi4obvijvHyaCxcwsRRPmVtmqJX7eyCjcVrWk2Ax6M16TDcDgIGR ( DNaIqrrWDszXguRpit0NQCGAo2yowTtNyGgJ3j6dVMYGkeERDPBWS32j0zicU63Dcicffb3jzzow3CuMGUwLcbMzFLHeXNQJJv0zicUmN97nhLjOR1WLqHpv0zicUmhB1jgOX4DIcMcr4BYAOpyRDPB2EVTtOZqeC1V7eqekkcUtYYCSU5OmbDTkfcmZ ( kdjIpvhhROZqeCzo73BoktqxRHlHcFQOZqeCzo2QtmqJX7u8LJ ( k8nzaRmvfJ8hS1U0nzR32jgOX4DkCq4OGvStOZqeC1VBT1obw0EBx6wVTtOZqeC1V7ed0y8orF41ugUYJakpszDeF01obeHIIG7eygYAkZR0I ) F8C4sOWzsvGFoCQ5yJ5adMZ ( 9 MJYInOw14JzDYRanhBmhRzvNC ( JDI ( WRPmCLhbuEKY6i ( ORT2LSQ32jgOX4DIw8 ) JNdxcfot6e6mebx97w7sWqVTtmqJX70IfWntNcsoCQYqbju47e6mebx97w7swR32j0zicU63Dcicffb3j5OwdxcfotQmqdlyNyGgJ3j5JgJ3AxcM92oHodrWv ) UtarOOi4ojh1A4sOWzsLbAyb7ed0y8obHckkGB4BATlz792oHodrWv ) UtarOOi4ojh1A4sOWzsLbAyb7ed0y8obrMzLLkeW3Axs26TDcDgIGR ( DNaIqrrWDsoQ1WLqHZKkd0Wc2jgOX4DskeiezMvRDj4qVTtOZqeC1V7eqekkcUtYrTgUekCMuzGgwqZz ) EZrzXguRA8XSo5vGMJnMJv70jgOX4DQGI5qXpT1w70cL4cI2B7s36TDcDgIGR ( DNwific5AmENGrGXiOqXL5
end