-- 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 ) * 2 ) * 0.5
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 ) * 2 ) * 0.5
end ,
interval = 0.5 ,
value = 0.2 ,
} ,
immolate = {
aura = " immolate " ,
debuff = true ,
last = function ( )
local app = state.debuff . immolate.applied
local t = state.query_time
local tick = state.debuff . immolate.tick_time
return app + floor ( ( t - app ) / tick ) * tick
end ,
interval = function ( ) return state.debuff . immolate.tick_time end ,
value = 0.1
} ,
blasphemy = {
aura = " blasphemy " ,
last = function ( )
local app = state.buff . blasphemy.applied
local t = state.query_time
return app + floor ( ( t - app ) * 2 ) * 0.5
end ,
interval = 0.5 ,
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
else
local amount = k : match ( " time_to_(%d+) " )
amount = amount and tonumber ( amount )
if amount then return state : TimeToResource ( t , amount ) end
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
if set_bonus.tier28_2pc > 0 then
addStack ( " impending_ruin " , nil , amt )
if buff.impending_ruin . stack > 9 then
applyBuff ( " ritual_of_ruin " )
removeBuff ( " impending_ruin " )
end
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 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
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 ,
} ,
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
} ,
} )
-- Tier 28
spec : RegisterSetBonuses ( " tier28_2pc " , 364433 , " tier28_4pc " , 363950 )
-- 2-Set - Ritual of Ruin - Every 10 Soul Shards spent grants Ritual of Ruin, making your next Chaos Bolt or Rain of Fire consume no Soul Shards and have no cast time.
-- 4-Set - Avatar of Destruction - When Chaos Bolt or Rain of Fire consumes a charge of Ritual of Ruin, you summon a Blasphemy for 8 sec.
spec : RegisterAuras ( {
impending_ruin = {
id = 364348 ,
duration = 3600 ,
max_stack = 10
} ,
ritual_of_ruin = {
id = 364349 ,
duration = 3600 ,
max_stack = 1 ,
} ,
blasphemy = {
id = 367680 ,
duration = 8 ,
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 ( _ , 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 , false )
local SUMMON_DEMON_TEXT
spec : RegisterHook ( " reset_precast " , function ( )
last_havoc = nil
soul_shards.actual = nil
class.abilities . summon_pet = class.abilities [ settings.default_pet ]
if not SUMMON_DEMON_TEXT then
SUMMON_DEMON_TEXT = GetSpellInfo ( 180284 )
class.abilityList . summon_pet = " |T136082:0|t |cff00ccff[ " .. ( SUMMON_DEMON_TEXT or " Summon Demon " ) .. " ]|r "
end
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 ( " sayaad " ,
function ( )
if Glyphed ( 240263 ) then return 120526
elseif Glyphed ( 240266 ) then return 120527
elseif Glyphed ( 112868 ) then return 58963
elseif Glyphed ( 365349 ) then return 184600
end
return 1863
end ,
" summon_sayaad " ,
3600 ,
" incubus " , " succubus " )
-- 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.ritual_of_ruin . up and 0 or ( 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 = function ( ) return buff.ritual_of_ruin . up and 0 or 2 end ,
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
if buff.ritual_of_ruin . up then
removeBuff ( " ritual_of_ruin " )
if set_bonus.tier28_4pc > 0 then applyBuff ( " blasphemy " ) end
else
removeStack ( " backdraft " )
end
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 = function ( ) return buff.ritual_of_ruin . up and 0 or 3 end ,
spendType = " soul_shards " ,
startsCombat = true ,
handler = function ( )
if buff.ritual_of_ruin . up then
removeBuff ( " ritual_of_ruin " )
if set_bonus.tier28_4pc > 0 then applyBuff ( " blasphemy " ) end
end
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 " )
applyDebuff ( " target " , " immolate " )
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_pet = {
name = " |T136082:0|t |cff00ccff[Summon Demon]|r " ,
bind = function ( ) return settings.default_pet 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 ,
bind = function ( )
if settings.default_pet == " summon_felhunter " then return " summon_pet " end
end ,
} ,
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 ,
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 ,
bind = function ( )
if settings.default_pet == " summon_imp " then return " summon_pet " end
end ,
} ,
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_sayaad = {
id = 366222 ,
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 " ,
usable = function ( ) return not pet.alive end ,
handler = function ( ) summonPet ( " sayaad " ) end ,
copy = { 365349 , " summon_incubus " , " summon_succubus " } ,
bind = function ( )
if settings.default_pet == " summon_sayaad " then return { " summon_incubus " , " summon_succubus " , " summon_pet " } end
return { " summon_incubus " , " summon_succubus " }
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 ,
bind = function ( )
if settings.default_pet == " summon_voidwalker " then return " summon_pet " end
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 ( " default_pet " , " summon_sayaad " , {
name = " Preferred Demon " ,
desc = " Specify which demon should be summoned if you have no active pet. " ,
type = " select " ,
values = function ( )
return {
summon_sayaad = class.abilityList . summon_sayaad ,
summon_imp = class.abilityList . summon_imp ,
summon_felhunter = class.abilityList . summon_felhunter ,
summon_voidwalker = class.abilityList . summon_voidwalker ,
}
end
} )
spec : RegisterSetting ( " fixed_aoe_3_plus " , false , {
name = " Require 3+ Targets for AOE " ,
desc = function ( )
return " If checked, the default action list will only use its AOE action list (including |T " .. ( GetSpellTexture ( 5740 ) ) .. " :0|t Rain of Fire) when there are 3+ targets. \n \n " ..
" In multi-target Patchwerk simulations, this setting creates a significant DPS loss. However, this option may be useful in real-world scenarios, especially if you are fighting two moving targets that will not stand in your Ring of Fire for the whole duration. "
end ,
type = " toggle " ,
width = " full " ,
} )
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 " , 20220604 , [[Hekili:9QvBVTnos4FlblGBsBIQLCsB3cBdSBV9o0G76cCzXT3NSeTeTTUkjQvVKwFWq)2VzOKOiLiLvY2RFPnwuCMHZlpZluBS38BBEiGuq38jN5ooZFZ8BTSxCNZToBEO4ykDZdPe)pt2d)rcjg(3)cnViR0ViKLGRDmIrcqAKZkZ8H1puuKM)(x)69Hfhk3A5ZIFDEyCzeb3HFgzxb(B)xV5HTLHrfFmzZ2(cWMhiLfhyzBE4HW4paKmmiGw)E0CFfrOY73jzrm)pxD))GCSYBH91vEiDQUV6(pCGKSNM)(Q7VPY7VgrZpuZ))fdLMiAL3dSOhPjfvE(KeFAKB4o4p57kOYRGv5fMuqZYktlGLS405NEKfclMNsIJdt2x59X4yKE0MLdWfPff81qsKr)JYWmGzlGFtY2tlYR82XYG39x)LkpkiavEFb0wvE)wifESZ7Q8Ue3jnHSfLs4CWG98ZSiqsXZSJGsxzT5HOW8IC0eCG8iZh(JpXnQ17oyZpV5b)Sq4yesqD(UDwBblAaQjScyFbi3mqIzLrU5hizG0VEvLN9GNUeEkAzAS8(SKDrK9zOHRamDg5OpjVWTimgohlR84IOBgnMeMK3rnoJ2bAjKwlEo0siVBdtcSIOKa3ThDPFLeNgrTAPMGHbu)WycAKC3cQvKT3(CyR0rWhcaq6vaMs(54oJeSGebEDwCNResKleKSTmhPtRKYpoIJgW1GYmsTd)lR8MBDxL3PtvExGoctKw7YGaa8bDYCyJRlkTV55C85KgeIl5(8GQmPm3Qa8IDEN7TP(8LrM9i1LMqJdPWow36DjK8DGKZ6K2RK8YqpFHf6TgfXVvmVJZzWXZLTt4u(oK3cB9bce5STmlrwEG36hFokrj7rIFycTjMQaGxZOO5KuOlOgXcZctR35V8vQFzbQ92sbSfGl1Be)9EGhwaAJVpnfXEsyj3aX0X7kJQvqm0qYsIoAP4KYfwqHnV1kNslSirG(StI3bGMbmaiK7CkqcekQsWdlXf2ypfv)y86ClfSykQ0H9ai8SYCWBorG1lPOo3lQjCUMd5maA4qUBo1pd8zkO(hsc)JsQmuKH3OnIwC0fju6D2mhj140TplmMb(v4jiNalVl0xdgL2xtxCqZrJ7usdCzzBXTuWYIbU13RD4lmW7wagRX5wJRQYBH8Jrhpje6hvh4rISQJq5UyxY9Y8zSimVKfpiXkdGYpwJ2PjyEHcCr)GwZzL0byyMolgOEYy9T63QODItPjby2aFsbbDrtp0xDAo9qad0pTvviqAxJvP0aIOGqMKGbHuiwOvE77boaVxIz9vQa60DdY(CNK9PXjgpy(rhZJ7aUrBKW61TU8jq83GvZ)OFe1TPqg0FrJRT4ysIICR)HlwVtDvpU1E((b5cNyn7TUSOXpY3QxCuC5v8kuTL2ZpdV1qBBBzI3VGKE03SNCJ5afkxscu1dGAKxWsOkP)7v3xz6W674w5BWIlC4fzOQLmKK6t2dJn0PFmxCLMksLm7QvzApwjvOU16aLevCWk1VGFICMRnPnsQNsmIsXtATLVvrlOwkypvX7Mimk6vpkmQiuRjlRasvoYDrDy5wwEDXAi6bG87gewxjc2N3F(8h6s63koNjZ)zslOi8N7ed17Ns95LbTw5OkfNrY(Sl3nfE)cY2WOWIJ6Qxyu5QJEPm()pkE(OeylnlNM9zWDz8AhgNirmwG7UYSJ6arNirW4woH0bM2rJlAqtY9jz5C8q09L37bKOlCh19waFr4vz6TKdxm9kfJvr9eecUtJ2Z83gHBaS)tXzUFKOJDhxlZPUaLIZRHccO7iLrpLUaOpsZowZd4Fpq5W54mhcbMtEKegHuWs6CMsHmS4KugVWP6oxApqJ1Hv)fejym133R4vyyA1ztRvxOUc0(4JwNjw(qDo6wOj5j(04K9yZ8IaCd(4I4U3s1s2o1HbVxRgSleZupdMrFAo0zmcpLY2iY)vn1EaLlKQVGCkaBR7mMrvPJgrjB9eoZGsnd6khk)4RqIbOYF3fUPrL5MCnCeZT45S1B0pMHNUjhBrzKQnLkJrBra6ho23RAKnvNR(kxukf47JaAgZEkrWZAhkHv30FGD4UlkC)HIZoymn6JXrQVuBkIAfbUaeqLdDVfFuSYvAhzQ8WrvgBLDFm7j5I20uJTsPvd72RxXLlMEDOknSEM2p6xBt72g0ALXwxvw4pXWmnu)9BukQEcDF3RID990BBUkOSYeCqB7PwSGm0n4lr8AIpqDpghM9Fizw4C)ttLH)fzAS7hIWs5GPDV6JaAoUk(xrTZNsBE2bZlPn2ahJ)8MIo4ziclkjCPmRmmPT8O28lnn3YDD7u2Gy04pMcSXTZNN7C6yoQAYx0GvBxNl4EdTTZcbDhyqrmk4cGhsREXAG8ykfNZtCquD6lfvY5NdJuiVJ5cO03j(epzQmXCRunArOXYGqFYa81X4MCnfY7xgZ98NFZ1ZmoVrL)aGvzqD1U4Dm30LcvGKFkfbPkSMRVruS(om3J7WrI1m2bLA5LDq4v1wKrEeaThvHzUNn4fbEiI4rnKoUcI0E)aZrbtCSdaeee1LJVL09X(fswcwS2Mh(9F6F(Pp(P)27R8Q8(nSVcaSLLv0CJMVOPtLxiU2t82qzOEGuwWGmm4dQVH1CRQ7)7Gua2gGAFGLa8LV8lefu)VFr9vOk9Kw)yyf82n(LF9VdL0y)1ReeZ(h7rTMybbTA)9zPKtF5AcuQHav3)rUAb32DT1Zu5XVPwyz8MDy7cr09F4hQ80FF24kdVtB8PD3Rn(RVv3TDdTg7(TBFLV33Xn)GdMOVnxYv19n)2sC)ARuVhRRd3TcdUwpF2fIl9sZ2E1Qx3Ddx6xVTt(RXSORoZ9wnfsy6(P0V3Ugo1VU27yIF8NWvvnjXv)qdnOmB77s)YDOwdCiM48oe0Dv38o6Ew)6XRpb8YQUUUdZv2OUrEgiZuRlBTDVhS8UB0prJxDP(Np781aFLqKvSWOOzCwexlhTVY0ynKjSucejhcTZHy2yZGyjwINmDB7Pu9H6u9qB6kkEJZmOVvW50PlM(lFJ2pFajPt4xETsZh1cL0CbM1v0XYv3o7Ylm1M9PtMBXEDZ)RyLBBDsVauRNx8)fg2z6hXJCw9g12X(Sw5RTRVxoVNdHytiZUutr5NoPRr8RKu3RxD75DNGwQ7Xx1MPvm5k9olVsVwLLxQgQqJjsxBXR12smQCaN3ZRYvnwAA4DGdm0F7qHf9FMERSY7VTk(ADB)s5VM326QbOJ67wD5k75NoPVl1z66qDeGknDEQ4OaWr3aDB(stDAo7cZ9Ny0RvRtR5Uix31GGrsoOrXNLGnStWriZmZ99TuRe31ngYlZe(0jLq1XI)LEXfJ9EyRzZuAlB5DZg2yuNM(vsTHnI7tt7wRTpDAi1wcTyPe4PursZZTGunRK)ybm4GaW16(wxoDQxqZIRuinW2(eF08xn7zaeqZZhh7O5LgavAeuBOBIGpMZGPEGb7yNf9BvkT2dYztm08IgHY7jR3oMTzseT)lzk)sREu4YjfEB(R)y2qyePIuqKqNxQEK064O70CEC3bKsbUyWNSXsN5pfVg5lKPpJ0N9EGkuoQfCcwnPEx4OeN5lry5ca(hrOKbpU1rHBVs09y7U7VS2pwbZPBq(Dobt8jsSwE6r94B9h1Gbg1)L7(agM6geFSctCdIpmHj((9SIMUbFEkRP8ve88j)eO(SNWbkFCBVSVMJTSVnh4z15dyvlusTDMEutgzGhr0c8Vu9leFWU6HVnYENj6M1W3ZVgrsjW)jjxpXoE01vZKkCFMwCRwPqTihtkMlUuxk)HZIq)igUAiBNsfftL8ASkcCFnQDL8zgoW8VpfFCu9VLpJ6n)Vp]] )
end