-- DemonHunterHavoc.lua
-- June 2018
local addon , ns = ...
local Hekili = _G [ addon ]
local class = Hekili.Class
local state = Hekili.State
local PTR = ns.PTR
-- Conduits
-- [-] dancing_with_fate
-- [-] relentless_onslaught
-- [-] growing_inferno
-- [x] serrated_glaive
-- Covenant
-- [-] repeat_decree
-- [x] increased_scrutiny
-- [x] brooding_pool
-- [-] unnatural_malice
-- Endurance
-- [x] fel_defender
-- [-] shattered_restoration
-- [-] viscous_ink
-- Finesse
-- [-] demonic_parole
-- [x] felfire_haste
-- [-] lost_in_darkness
-- [-] ravenous_consumption
if UnitClassBase ( " player " ) == " DEMONHUNTER " then
local spec = Hekili : NewSpecialization ( 577 )
spec : RegisterResource ( Enum.PowerType . Fury , {
prepared = {
talent = " momentum " ,
aura = " prepared " ,
last = function ( )
local app = state.buff . prepared.applied
local t = state.query_time
local step = 0.1
return app + floor ( t - app )
end ,
interval = 1 ,
value = 8
} ,
-- Immolation Aura now grants 20 up front, 60 over 12 seconds (5 fps).
immolation_aura = {
talent = " burning_hatred " ,
aura = " immolation_aura " ,
last = function ( )
local app = state.buff . immolation_aura.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = 1 ,
value = 5
} ,
eye_beam = {
talent = " blind_fury " ,
aura = " eye_beam " ,
last = function ( )
local app = state.buff . eye_beam.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = function ( ) return 1 * state.haste end ,
value = 40 ,
} ,
} )
-- Talents
spec : RegisterTalents ( {
blind_fury = 21854 , -- 203550
demonic_appetite = 22493 , -- 206478
felblade = 22416 , -- 232893
insatiable_hunger = 21857 , -- 258876
burning_hatred = 22765 , -- 320374
demon_blades = 22799 , -- 203555
trail_of_ruin = 22909 , -- 258881
unbound_chaos = 22494 , -- 347461
glaive_tempest = 21862 , -- 342817
soul_rending = 21863 , -- 204909
desperate_instincts = 21864 , -- 205411
netherwalk = 21865 , -- 196555
cycle_of_hatred = 21866 , -- 258887
first_blood = 21867 , -- 206416
essence_break = 21868 , -- 258860
unleashed_power = 21869 , -- 206477
master_of_the_glaive = 21870 , -- 203556
fel_eruption = 22767 , -- 211881
demonic = 21900 , -- 213410
momentum = 21901 , -- 206476
fel_barrage = 22547 , -- 258925
} )
-- PvP Talents
spec : RegisterPvpTalents ( {
blood_moon = 5433 , -- 355995
chaotic_imprint = 809 , -- 356510
cleansed_by_flame = 805 , -- 205625
cover_of_darkness = 1206 , -- 357419
demonic_origins = 810 , -- 235893
detainment = 812 , -- 205596
glimpse = 813 , -- 354489
isolated_prey = 5445 , -- 357300
mortal_dance = 1204 , -- 328725
rain_from_above = 811 , -- 206803
reverse_magic = 806 , -- 205604
unending_hatred = 1218 , -- 213480
} )
-- Auras
spec : RegisterAuras ( {
blade_dance = {
id = 188499 ,
duration = 1 ,
max_stack = 1
} ,
blur = {
id = 212800 ,
duration = 10 ,
max_stack = 1 ,
} ,
chaos_brand = {
id = 1490 ,
duration = 3600 ,
max_stack = 1 ,
} ,
chaos_nova = {
id = 179057 ,
duration = function ( ) return pvptalent.isolated_prey . enabled and active_enemies == 1 and 3 or 2 end ,
type = " Magic " ,
max_stack = 1 ,
} ,
chaotic_blades = {
id = 337567 ,
duration = 8 ,
max_stack = 1 ,
copy = " chaos_theory " -- simc.
} ,
darkness = {
id = 196718 ,
duration = function ( ) return pvptalent.cover_of_darkness . enabled and 10 or 8 end ,
max_stack = 1 ,
} ,
death_sweep = {
id = 210152 ,
duration = 1 ,
max_stack = 1 ,
} ,
demon_blades = {
id = 203555 ,
} ,
demonic_wards = {
id = 278386 ,
} ,
double_jump = {
id = 196055 ,
} ,
essence_break = {
id = 320338 ,
duration = 8 ,
max_stack = 1 ,
copy = " dark_slash " -- Just in case.
} ,
eye_beam = {
id = 198013 ,
duration = function ( ) return ( talent.blind_fury . enabled and 3 or 2 ) * haste end ,
max_stack = 1 ,
generate = function ( t )
if buff.casting . up and buff.casting . v1 == 198013 then
t.applied = buff.casting . applied
t.duration = buff.casting . duration
t.expires = buff.casting . expires
t.stack = 1
t.caster = " player "
forecastResources ( " fury " )
return
end
t.applied = 0
t.duration = class.auras . eye_beam.duration
t.expires = 0
t.stack = 0
t.caster = " nobody "
end ,
} ,
fel_barrage = {
id = 258925 ,
} ,
fel_eruption = {
id = 211881 ,
duration = 4 ,
max_stack = 1 ,
} ,
-- Legendary
fel_bombardment = {
id = 337849 ,
duration = 40 ,
max_stack = 5 ,
} ,
-- Legendary
fel_devastation = {
id = 333105 ,
duration = 2 ,
max_stack = 1 ,
} ,
furious_gaze = {
id = 343312 ,
duration = 12 ,
max_stack = 1 ,
} ,
glide = {
id = 131347 ,
} ,
immolation_aura = {
id = 258920 ,
duration = 12 ,
max_stack = 1 ,
} ,
inner_demon = {
id = 337313 ,
duration = 3600 ,
max_stack = 1 ,
} ,
master_of_the_glaive = {
id = 213405 ,
duration = 6 ,
max_stack = 1 ,
} ,
metamorphosis = {
id = 162264 ,
duration = function ( ) return 30 + ( pvptalent.demonic_origins . enabled and - 15 or 0 ) + ( set_bonus.tier28_4pc > 0 and 6 or 0 ) end ,
max_stack = 1 ,
meta = {
extended_by_demonic = function ( )
return false -- disabled in 8.0: talent.demonic.enabled and ( buff.metamorphosis.up and buff.metamorphosis.duration % 15 > 0 and buff.metamorphosis.duration > ( action.eye_beam.cast + 8 ) )
end ,
} ,
} ,
momentum = {
id = 208628 ,
duration = 6 ,
max_stack = 1 ,
} ,
netherwalk = {
id = 196555 ,
duration = 6 ,
max_stack = 1 ,
} ,
prepared = {
id = 203650 ,
duration = 10 ,
max_stack = 1 ,
} ,
spectral_sight = {
id = 188501 ,
duration = 10 ,
max_stack = 1 ,
} ,
torment = {
id = 185245 ,
duration = 3 ,
max_stack = 1 ,
} ,
trail_of_ruin = {
id = 258883 ,
duration = 4 ,
max_stack = 1 ,
} ,
unbound_chaos = {
id = 347462 ,
duration = 20 ,
max_stack = 1
} ,
unrestrained_fury = {
id = 320770 ,
} ,
vengeful_retreat = {
id = 198793 ,
duration = 3 ,
max_stack = 1 ,
} ,
demon_soul = {
id = 208195 ,
duration = 20 ,
max_stack = 1 ,
} ,
-- PvP Talents
rain_from_above_launch = {
id = 206803 ,
duration = 1 ,
max_stack = 1 ,
} ,
rain_from_above = {
id = 206804 ,
duration = 10 ,
max_stack = 1 ,
} ,
-- Azerite
thirsting_blades = {
id = 278736 ,
duration = 30 ,
max_stack = 40 ,
meta = {
stack = function ( t )
if t.down then return 0 end
local appliedBuffer = ( now - t.applied ) % 1
return min ( 40 , t.count + floor ( offset + delay + appliedBuffer ) )
end ,
}
} ,
-- Conduit
exposed_wound = {
id = 339229 ,
duration = 10 ,
max_stack = 1 ,
} ,
-- PvP Talents
chaotic_imprint_shadow = {
id = 356656 ,
duration = 20 ,
max_stack = 1 ,
} ,
chaotic_imprint_nature = {
id = 356660 ,
duration = 20 ,
max_stack = 1 ,
} ,
chaotic_imprint_arcane = {
id = 356658 ,
duration = 20 ,
max_stack = 1 ,
} ,
chaotic_imprint_fire = {
id = 356661 ,
duration = 20 ,
max_stack = 1 ,
} ,
chaotic_imprint_frost = {
id = 356659 ,
duration = 20 ,
max_stack = 1 ,
} ,
glimpse = {
id = 354610 ,
duration = 8 ,
max_stack = 1 ,
} ,
isolated_prey = {
id = 357305 ,
duration = 6 ,
max_stack = 1 ,
} ,
mortal_dance = {
id = 328725 ,
duration = 5 ,
max_stack = 1 ,
} ,
} )
local last_darkness = 0
local last_metamorphosis = 0
local last_eye_beam = 0
spec : RegisterStateExpr ( " darkness_applied " , function ( )
return max ( class.abilities . darkness.lastCast , last_darkness )
end )
spec : RegisterStateExpr ( " metamorphosis_applied " , function ( )
return max ( class.abilities . darkness.lastCast , last_metamorphosis )
end )
spec : RegisterStateExpr ( " eye_beam_applied " , function ( )
return max ( class.abilities . eye_beam.lastCast , last_eye_beam )
end )
spec : RegisterStateExpr ( " extended_by_demonic " , function ( )
return buff.metamorphosis . up and buff.metamorphosis . extended_by_demonic
end )
spec : RegisterStateExpr ( " meta_cd_multiplier " , function ( )
return 1
end )
local furySpent = 0
local FURY = Enum.PowerType . Fury
local lastFury = - 1
spec : RegisterUnitEvent ( " UNIT_POWER_FREQUENT " , " player " , nil , function ( event , unit , powerType )
if powerType == " FURY " then
local current = UnitPower ( " player " , FURY )
if current < lastFury then
furySpent = ( furySpent + lastFury - current ) % 60
end
lastFury = current
end
end )
spec : RegisterStateExpr ( " fury_spent " , function ( )
return furySpent
end )
spec : RegisterHook ( " spend " , function ( amt , resource )
if set_bonus.tier28_4pc > 0 and resource == " fury " then
fury_spent = fury_spent + amt
if fury_spent > 60 then
cooldown.metamorphosis . expires = cooldown.metamorphosis . expires - floor ( fury_spent / 60 )
fury_spent = fury_spent % 60
end
end
end )
spec : RegisterHook ( " reset_precast " , function ( )
last_darkness = 0
last_metamorphosis = 0
last_eye_beam = 0
local rps = 0
if equipped.convergence_of_fates then
rps = rps + ( 3 / ( 60 / 4.35 ) )
end
if equipped.delusions_of_grandeur then
-- From SimC model, 1/13/2018.
local fps = 10.2 + ( talent.demonic . enabled and 1.2 or 0 )
-- SimC uses base haste, we'll use current since we recalc each time.
fps = fps / haste
-- Chaos Strike accounts for most Fury expenditure.
fps = fps + ( ( fps * 0.9 ) * 0.5 * ( 40 / 100 ) )
rps = rps + ( fps / 30 ) * ( 1 )
end
meta_cd_multiplier = 1 / ( 1 + rps )
fury_spent = nil
end )
spec : RegisterCycle ( function ( )
if active_enemies == 1 then return end
-- For Nemesis, we want to cast it on the lowest health enemy.
if this_action == " nemesis " and Hekili : GetNumTTDsWithin ( target.time_to_die ) > 1 then return " cycle " end
end )
-- Tier 28
spec : RegisterGear ( " tier28 " , 188898 , 188896 , 188894 , 188893 , 188892 )
spec : RegisterSetBonuses ( " tier28_2pc " , 364438 , " tier28_4pc " , 363736 )
-- 2-Set - Deadly Dance - Increases Death Sweep and Annihilation / Blade Dance and Chaos Strike damage by 20%.
-- 4-Set - Deadly Dance - Metamorphosis duration is increased by 6 sec. Every 60 Fury you consume reduces the cooldown of Metamorphosis by 1 sec.
-- Gear Sets
spec : RegisterGear ( " tier19 " , 138375 , 138376 , 138377 , 138378 , 138379 , 138380 )
spec : RegisterGear ( " tier20 " , 147130 , 147132 , 147128 , 147127 , 147129 , 147131 )
spec : RegisterGear ( " tier21 " , 152121 , 152123 , 152119 , 152118 , 152120 , 152122 )
spec : RegisterAura ( " havoc_t21_4pc " , {
id = 252165 ,
duration = 8
} )
spec : RegisterGear ( " class " , 139715 , 139716 , 139717 , 139718 , 139719 , 139720 , 139721 , 139722 )
spec : RegisterGear ( " convergence_of_fates " , 140806 )
spec : RegisterGear ( " achor_the_eternal_hunger " , 137014 )
spec : RegisterGear ( " anger_of_the_halfgiants " , 137038 )
spec : RegisterGear ( " cinidaria_the_symbiote " , 133976 )
spec : RegisterGear ( " delusions_of_grandeur " , 144279 )
spec : RegisterGear ( " kiljaedens_burning_wish " , 144259 )
spec : RegisterGear ( " loramus_thalipedes_sacrifice " , 137022 )
spec : RegisterGear ( " moarg_bionic_stabilizers " , 137090 )
spec : RegisterGear ( " prydaz_xavarics_magnum_opus " , 132444 )
spec : RegisterGear ( " raddons_cascading_eyes " , 137061 )
spec : RegisterGear ( " sephuzs_secret " , 132452 )
spec : RegisterGear ( " the_sentinels_eternal_refuge " , 146669 )
spec : RegisterGear ( " soul_of_the_slayer " , 151639 )
spec : RegisterGear ( " chaos_theory " , 151798 )
spec : RegisterGear ( " oblivions_embrace " , 151799 )
do
local wasWarned = false
spec : RegisterEvent ( " PLAYER_REGEN_DISABLED " , function ( )
if state.talent . demon_blades.enabled and not state.settings . demon_blades_acknowledged and not wasWarned then
Hekili : Notify ( " |cFFFF0000WARNING!|r Demon Blades cannot be forecasted. \n See /hekili > Havoc for more information. " )
wasWarned = true
end
end )
end
-- SimC documentation reflects that there are still the following expressions, which appear unused:
-- greater_soul_fragments, lesser_soul_fragments, blade_dance_worth_using, death_sweep_worth_using
-- They are not implemented becuase that documentation is from mid-2016.
-- Abilities
spec : RegisterAbilities ( {
annihilation = {
id = 201427 ,
known = 162794 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 40 - buff.thirsting_blades . stack end ,
spendType = " fury " ,
startsCombat = true ,
texture = 1303275 ,
bind = " chaos_strike " ,
buff = " metamorphosis " ,
handler = function ( )
removeBuff ( " thirsting_blades " )
if azerite.thirsting_blades . enabled then applyBuff ( " thirsting_blades " , nil , 0 ) end
if buff.chaotic_blades . up then gain ( 20 , " fury " ) end -- legendary
end ,
} ,
blade_dance = {
id = 188499 ,
cast = 0 ,
cooldown = 9 ,
hasteCD = true ,
gcd = " spell " ,
spend = function ( ) return 35 - ( talent.first_blood . enabled and 20 or 0 ) end ,
spendType = " fury " ,
startsCombat = true ,
texture = 1305149 ,
bind = " death_sweep " ,
nobuff = " metamorphosis " ,
handler = function ( )
applyBuff ( " blade_dance " )
setCooldown ( " death_sweep " , 9 * haste )
if pvptalent.mortal_dance . enabled then
applyDebuff ( " target " , " mortal_dance " )
end
end ,
} ,
blur = {
id = 198589 ,
cast = 0 ,
cooldown = function ( ) return 60 + ( conduit.fel_defender . mod * 0.001 ) end ,
gcd = " off " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 1305150 ,
handler = function ( )
applyBuff ( " blur " )
end ,
} ,
chaos_nova = {
id = 179057 ,
cast = 0 ,
cooldown = function ( ) return talent.unleashed_power . enabled and 40 or 60 end ,
gcd = " spell " ,
spend = function ( ) return talent.unleashed_power . enabled and 0 or 30 end ,
spendType = " fury " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 135795 ,
handler = function ( )
applyDebuff ( " target " , " chaos_nova " )
end ,
} ,
chaos_strike = {
id = 162794 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 40 - buff.thirsting_blades . stack end ,
spendType = " fury " ,
startsCombat = true ,
texture = 1305152 ,
bind = " annihilation " ,
nobuff = " metamorphosis " ,
cycle = function ( ) return legendary.burning_wound . enabled and " burning_wound " or nil end ,
handler = function ( )
removeBuff ( " thirsting_blades " )
if azerite.thirsting_blades . enabled then applyBuff ( " thirsting_blades " , nil , 0 ) end
if legendary.burning_wound . enabled then applyDebuff ( " target " , " burning_wound " ) end
if buff.chaotic_blades . up then gain ( 20 , " fury " ) end -- legendary
end ,
auras = {
burning_wound = {
id = 346278 ,
duration = 15 ,
max_stack = 1
}
}
} ,
consume_magic = {
id = 278326 ,
cast = 0 ,
cooldown = 10 ,
gcd = " spell " ,
startsCombat = true ,
texture = 828455 ,
usable = function ( ) return buff.dispellable_magic . up end ,
handler = function ( )
removeBuff ( " dispellable_magic " )
gain ( buff.solitude . up and 22 or 20 , " fury " )
end ,
} ,
darkness = {
id = 196718 ,
cast = 0 ,
cooldown = 180 ,
gcd = " spell " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 1305154 ,
handler = function ( )
last_darkness = query_time
applyBuff ( " darkness " )
end ,
} ,
death_sweep = {
id = 210152 ,
known = 188499 ,
cast = 0 ,
cooldown = 9 ,
hasteCD = true ,
gcd = " spell " ,
spend = function ( ) return talent.first_blood . enabled and 15 or 35 end ,
spendType = " fury " ,
startsCombat = true ,
texture = 1309099 ,
bind = " blade_dance " ,
buff = " metamorphosis " ,
handler = function ( )
applyBuff ( " death_sweep " )
setCooldown ( " blade_dance " , 9 * haste )
if pvptalent.mortal_dance . enabled then
applyDebuff ( " target " , " mortal_dance " )
end
end ,
} ,
demons_bite = {
id = 162243 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return talent.insatiable_hunger . enabled and - 25 or - 20 end ,
spendType = " fury " ,
startsCombat = true ,
texture = 135561 ,
notalent = " demon_blades " ,
handler = function ( )
end ,
} ,
disrupt = {
id = 183752 ,
cast = 0 ,
cooldown = 15 ,
gcd = " off " ,
startsCombat = true ,
texture = 1305153 ,
toggle = " interrupts " ,
debuff = " casting " ,
readyTime = state.timeToInterrupt ,
handler = function ( )
interrupt ( )
gain ( buff.solitude . up and 33 or 30 , " fury " )
end ,
} ,
essence_break = {
id = 258860 ,
cast = 0 ,
cooldown = 20 ,
gcd = " spell " ,
startsCombat = true ,
texture = 136189 ,
handler = function ( )
applyDebuff ( " target " , " essence_break " )
active_dot.essence_break = max ( 1 , active_enemies )
end ,
copy = " dark_slash "
} ,
eye_beam = {
id = 198013 ,
cast = function ( ) return ( talent.blind_fury . enabled and 3 or 2 ) * haste end ,
cooldown = 30 ,
channeled = true ,
gcd = " spell " ,
spend = 30 ,
spendType = " fury " ,
startsCombat = true ,
texture = 1305156 ,
start = function ( )
last_eye_beam = query_time
applyBuff ( " eye_beam " )
if talent.demonic . enabled then
if buff.metamorphosis . up then
buff.metamorphosis . duration = buff.metamorphosis . remains + 8
buff.metamorphosis . expires = buff.metamorphosis . expires + 8
else
applyBuff ( " metamorphosis " , action.eye_beam . cast + 8 )
buff.metamorphosis . duration = action.eye_beam . cast + 8
stat.haste = stat.haste + 25
end
end
if pvptalent.isolated_prey . enabled and active_enemies == 1 then
applyDebuff ( " target " , " isolated_prey " )
end
-- This is likely repeated per tick but it's not worth the CPU overhead to model each tick.
if legendary.agony_gaze . enabled and debuff.sinful_brand . up then
debuff.sinful_brand . expires = debuff.sinful_brand . expires + 0.75
end
end ,
finish = function ( )
if level > 58 then applyBuff ( " furious_gaze " ) end
end ,
} ,
fel_barrage = {
id = 258925 ,
cast = 2 ,
cooldown = 60 ,
channeled = true ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 2065580 ,
talent = " fel_barrage " ,
start = function ( )
applyBuff ( " fel_barrage " , 2 )
end ,
} ,
fel_eruption = {
id = 211881 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = 10 ,
spendType = " fury " ,
startsCombat = true ,
texture = 1118739 ,
talent = " fel_eruption " ,
handler = function ( )
applyDebuff ( " target " , " fel_eruption " )
end ,
} ,
fel_rush = {
id = 195072 ,
cast = 0 ,
charges = 2 ,
cooldown = function ( ) return legendary.erratic_fel_core . enabled and 7 or 10 end ,
recharge = function ( ) return legendary.erratic_fel_core . enabled and 7 or 10 end ,
gcd = " spell " ,
startsCombat = true ,
texture = 1247261 ,
usable = function ( )
if settings.recommend_movement ~= true then return false , " fel_rush movement is disabled " end
return not prev_gcd [ 1 ] . fel_rush
end ,
handler = function ( )
removeBuff ( " unbound_chaos " )
if talent.momentum . enabled then applyBuff ( " momentum " ) end
if cooldown.vengeful_retreat . remains < 1 then setCooldown ( " vengeful_retreat " , 1 ) end
setDistance ( 5 )
setCooldown ( " global_cooldown " , 0.25 )
if conduit.felfire_haste . enabled then applyBuff ( " felfire_haste " ) end
if pvptalent.isolated_prey . enabled and active_enemies == 1 then
gain ( 35 , " fury " )
end
end ,
auras = {
-- Conduit
felfire_haste = {
id = 338804 ,
duration = 8 ,
max_stack = 1
}
}
} ,
felblade = {
id = 232893 , cast = 0 ,
cooldown = 15 ,
hasteCD = true ,
gcd = " spell " ,
spend = - 40 ,
spendType = " fury " ,
startsCombat = true ,
texture = 1344646 ,
-- usable = function () return target.within15 end,
handler = function ( )
setDistance ( 5 )
end ,
} ,
fel_lance = {
id = 206966 ,
cast = 1 ,
cooldown = 0 ,
gcd = " spell " ,
pvptalent = " rain_from_above " ,
buff = " rain_from_above " ,
startsCombat = true ,
} ,
glaive_tempest = {
id = 342817 ,
cast = 0 ,
cooldown = 20 ,
hasteCD = true ,
gcd = " spell " ,
spend = 30 ,
spendType = " fury " ,
startsCombat = true ,
texture = 1455916 ,
handler = function ( )
end ,
} ,
immolation_aura = {
id = 258920 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = - 20 ,
spendType = " fury " ,
startsCombat = true ,
texture = 1344649 ,
handler = function ( )
applyBuff ( " immolation_aura " )
if talent.unbound_chaos . enabled then applyBuff ( " unbound_chaos " ) end
end ,
} ,
imprison = {
id = 217832 ,
cast = 0 ,
cooldown = function ( ) return pvptalent.detainment . enabled and 60 or 45 end ,
gcd = " spell " ,
startsCombat = false ,
texture = 1380368 ,
handler = function ( )
applyDebuff ( " target " , " imprison " )
end ,
auras = {
-- Conduit
demonic_parole = {
id = 339051 ,
duration = 12 ,
max_stack = 1
}
}
} ,
metamorphosis = {
id = 191427 ,
cast = 0 ,
cooldown = function ( ) return ( level > 47 and 240 or 300 ) * ( essence.vision_of_perfection . enabled and 0.87 or 1 ) - ( pvptalent.demonic_origins . up and 120 or 0 ) end ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 1247262 ,
handler = function ( )
applyBuff ( " metamorphosis " )
last_metamorphosis = query_time
setDistance ( 5 )
if IsSpellKnownOrOverridesKnown ( 317009 ) then
applyDebuff ( " target " , " sinful_brand " )
active_dot.sinful_brand = active_enemies
end
if level > 19 then stat.haste = stat.haste + 25 end
if level > 53 or azerite.chaotic_transformation . enabled then
setCooldown ( " eye_beam " , 0 )
setCooldown ( " blade_dance " , 0 )
setCooldown ( " death_sweep " , 0 )
end
end ,
meta = {
adjusted_remains = function ( )
--[[ if level < 116 and ( equipped.delusions_of_grandeur or equipped.convergeance_of_fates ) then
return cooldown.metamorphosis . remains * meta_cd_multiplier
end ] ]
return cooldown.metamorphosis . remains
end
}
} ,
nemesis = {
id = 206491 ,
cast = 0 ,
cooldown = 120 ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 236299 ,
talent = " nemesis " ,
handler = function ( )
applyDebuff ( " target " , " nemesis " )
end ,
} ,
netherwalk = {
id = 196555 ,
cast = 0 ,
cooldown = 180 ,
gcd = " spell " ,
toggle = " defensives " ,
startsCombat = true ,
texture = 463284 ,
talent = " netherwalk " ,
handler = function ( )
applyBuff ( " netherwalk " )
setCooldown ( " global_cooldown " , 6 )
end ,
} ,
rain_from_above = {
id = 206803 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
pvptalent = " rain_from_above " ,
startsCombat = false ,
texture = 1380371 ,
handler = function ( )
applyBuff ( " rain_from_above " )
end ,
} ,
reverse_magic = {
id = 205604 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
-- toggle = "cooldowns",
pvptalent = " reverse_magic " ,
startsCombat = false ,
texture = 1380372 ,
debuff = " reversible_magic " ,
handler = function ( )
if debuff.reversible_magic . up then removeDebuff ( " player " , " reversible_magic " ) end
end ,
} ,
spectral_sight = {
id = 188501 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 1247266 ,
handler = function ( )
end ,
} ,
throw_glaive = {
id = 185123 ,
cast = 0 ,
charges = function ( ) return talent.master_of_the_glaive . enabled and 2 or nil end ,
cooldown = 9 ,
recharge = 9 ,
hasteCD = true ,
gcd = " spell " ,
startsCombat = true ,
texture = 1305159 ,
handler = function ( )
removeBuff ( " fel_bombardment " ) -- legendary
if talent.master_of_the_glaive . enabled then applyDebuff ( " target " , " master_of_the_glaive " ) end
if conduit.serrated_glaive . enabled then applyDebuff ( " target " , " exposed_wound " ) end
end ,
auras = {
-- Conduit: serrated_glaive
exposed_wound = {
id = 339229 ,
duration = 10 ,
max_stack = 1
}
}
} ,
vengeful_retreat = {
id = 198793 ,
cast = 0 ,
cooldown = function ( ) return talent.momentum . enabled and 20 or 25 end ,
gcd = " spell " ,
startsCombat = true ,
texture = 1348401 ,
usable = function ( )
if settings.recommend_movement ~= true then return false , " vengeful_retreat movement is disabled " end
return true
end ,
handler = function ( )
if target.within8 then
applyDebuff ( " target " , " vengeful_retreat " )
if talent.momentum . enabled then applyBuff ( " prepared " ) end
end
if pvptalent.glimpse . enabled then applyBuff ( " glimpse " ) end
end ,
} ,
-- Demon Hunter - Kyrian - 306830 - elysian_decree (Elysian Decree)
elysian_decree = {
id = 306830 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
startsCombat = true ,
texture = 3565443 ,
handler = function ( )
create_sigil ( " elysian_decree " )
if legendary.blind_faith . enabled then applyBuff ( " blind_faith " ) end
end ,
auras = {
blind_faith = {
id = 355894 ,
duration = 20 ,
max_stack = 1 ,
} ,
}
} ,
-- Demon Hunter - Necrolord - 329554 - fodder_to_the_flame (Fodder to the Flame)
--[[ fodder_to_the_flame = {
id = 329554 ,
cast = 0 ,
cooldown = 120 ,
gcd = " spell " ,
toggle = " essences " ,
startsCombat = true ,
texture = 3591588 ,
handler = function ( )
applyDebuff ( " player " , " fodder_to_the_flame_chase " )
applyDebuff ( " player " , " fodder_to_the_flame_cooldown " )
end ,
auras = {
-- The buff from standing in the pool.
fodder_to_the_flame = {
id = 330910 ,
duration = function ( ) return 30 + ( conduit.brooding_pool . mod * 0.001 ) end ,
max_stack = 1 ,
} ,
-- The demon is linked to you.
fodder_to_the_flame_chase = {
id = 328605 ,
duration = 3600 ,
max_stack = 1 ,
} ,
-- This is essentially the countdown before the demon despawns (you can Imprison it for a long time).
fodder_to_the_flame_cooldown = {
id = 342357 ,
duration = 120 ,
max_stack = 1 ,
} ,
}
} , ] ]
-- Demon Hunter - Night Fae - 323639 - the_hunt (The Hunt)
the_hunt = {
id = 323639 ,
cast = 1 ,
cooldown = 180 ,
gcd = " spell " ,
toggle = " essences " ,
startsCombat = true ,
texture = 3636838 ,
handler = function ( )
applyDebuff ( " target " , " the_hunt " )
applyDebuff ( " target " , " the_hunt_dot " )
applyDebuff ( " target " , " the_hunt_root " )
setDistance ( 5 )
if legendary.blazing_slaughter . enabled then
applyBuff ( " immolation_aura " )
applyBuff ( " blazing_slaughter " )
end
end ,
auras = {
the_hunt_root = {
id = 323996 ,
duration = 1.5 ,
max_stack = 1 ,
} ,
the_hunt_dot = {
id = 345335 ,
duration = 6 ,
max_stack = 1 ,
} ,
the_hunt = {
id = 323802 ,
duration = 30 ,
max_stack = 1 ,
} ,
blazing_slaughter = {
id = 355892 ,
duration = 12 ,
max_stack = 20 ,
}
}
} ,
-- Demon Hunter - Venthyr - 317009 - sinful_brand (Sinful Brand)
sinful_brand = {
id = 317009 ,
cast = 0 ,
cooldown = function ( ) return 60 + ( conduit.sinful_brand . mod * 0.001 ) end ,
gcd = " spell " ,
toggle = " essences " ,
startsCombat = true ,
texture = 3565717 ,
handler = function ( )
applyDebuff ( " target " , " sinful_brand " )
end ,
auras = {
sinful_brand = {
id = 317009 ,
duration = 8 ,
max_stack = 1 ,
}
}
}
} )
spec : RegisterOptions ( {
enabled = true ,
aoe = 2 ,
nameplates = true ,
nameplateRange = 7 ,
damage = true ,
damageExpiration = 8 ,
potion = " phantom_fire " ,
package = " Havoc " ,
} )
spec : RegisterSetting ( " recommend_movement " , true , {
name = " Recommend Movement " ,
desc = " If checked, the addon will recommend |T1247261:0|t Fel Rush / |T1348401:0|t Vengeful Retreat when it is a potential DPS gain. \n \n " ..
" These abilities are critical for DPS when using the Momentum or Unbound Chaos talents. \n \n " ..
" If not using Momentum or Unbound Chaos, you may want to disable this to avoid unnecessary movement in combat. " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterSetting ( " demon_blades_head " , nil , {
name = " Demon Blades " ,
type = " header " ,
} )
spec : RegisterSetting ( " demon_blades_text " , nil , {
name = " |cFFFF0000WARNING!|r If using the |T237507:0|t Demon Blades talent, the addon will not be able to predict Fury gains from your auto-attacks. This will result " ..
" in recommendations that jump forward in your display(s). " ,
type = " description " ,
width = " full "
} )
spec : RegisterSetting ( " demon_blades_acknowledged " , false , {
name = " I understand that Demon Blades is unpredictable; don't warn me. " ,
desc = " If checked, the addon will not provide a warning about Demon Blades when entering combat. " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterPack ( " Havoc " , 20211101 , [ [ defPSbqicPfHOQWJOe4siQkAtusFcrzuiKtHqTkfi5vQIAweIBPkPSlk ( LQunmvjoMcAziv9mejMgIuDnkLABQsIVPkPACisPZHOQQ1PkLMhIk3JqTpKk ) drsvhubIwiLOhQa1ePe0frKKnIOQYhvLKmsfi4KiskRermtvP4MiQkTtvr ( PQKunuejvSufi1tjyQkGRQaHSvejv6RisXyPeAVI6VkAWsDyslgspwvnzuDzWMrYNvOrJuoTsRwbc1RPumBIUne7MQFRYWrPoUQKuwoupxKPl56Oy7i47QcJNsjNNs16ruL5Js2VW5H5bYcCTG8t0 ) c9dho8LHg6PN0jD6F9SqzNnKfyRFB0ril4kcKfgeuc3plWwTlpLNhilKog8hYcclcJuR98bJvQklGYSYIuZZOzbUwq ( j6FH ( Hdh ( Yqd90t6Ko9VswiXg ( 5 NS9R ) 6 zbAlNdEgnlWH0plyHaY5rpiW4fGJEqqjC ) GKNocackGJEOirt ) l0pmijizW0uFesVni51IM8fkf5WSPDPnfT68Oj15Q98Ozs6ien5duG6Wq0u7iTkAW5jYhrlVX9hTYheZKkGhDDrRSzlTh95s7rxx0Oxkfn1osRsMGKxl63Cxc8OzyhDYU ) xFm6JkAYxOuKdZM2L2u0eHSE0hv0u7iTkAmGORNIMqYeDHx3gOIEWwy0yTOb4OlAQhnIAlInzb24JALqwWcSGOTqa58Ohey8cWrpiOeUFqIfybr ) 0 raqqbC0dfjA6FH ( HbjbjwGfe9GPP ( iKEBqIfybr ) Art ( cLICy20U0MIwDE0K6C1EE0mjDeIM8bkqDyiAQDKwfn48e5JOL34 ( Jw5dIzsfWJUUOv2SL2J ( CP9ORlA0lLIMAhPvjtqIfybr ) Ar ) M7sGhnd7Ot29 ) 6 JrFurt ( cLICy20U0MIMiK1J ( OIMAhPvrJbeD9u0esMOl862av0d2cJgRfnahDrt9OruBrSjijir ) 1 EEYWgd ) dbvlXOxvsGpPKQDG ) y9XzD2A9Ge9x75jdBm8peuTEw87eu8QOsqexraXfEDBGAMS7 ) zsELieujdiEOilL4cVUnqzgAOPPjtcMOmuuwjs0cVUnqzO3qtttMemrzOOyXQWRBduMHM ) Ds ( 9 WnCgSw750jUWRBdug6n ) 7 K87HB4myT2Zjoir ) 1 EEYWgd ) dbvRNf ) obfVkQeeXveqCHx3gOMj7 ( FMKxjcbvYaIPxKLsCHx3gOm0BOPPjtcMOmuuwjs0cVUnqzgAOPPjtcMOmuuSyv41Tbkd9M ) Ds ( 9 WnCgSw750v41TbkZqZ ) oj ) E4godwR9CIdsSGOheLGOjv2HOTeuKO1kA59iAYpgS9OFSfTOTuUopAYpgS9Ov55Jr ) ylArdBrdWrBHk2MrPIHOpC0wiGCE0wkvoKIMXLqkfntA9XOhKJNBp6xLsEqqI ( R98KHng ( hcQwpl ( DckEvujiIRiGyMemb7WefuKjfd2 ( 8 FoFR9CriOsgqCPsWldQCD ( KIbB3aUIkbUvIWmoqD4rWWvSnJsfdteGRs5EolwLkbVmCa58jQu5qYaUIkbUvrXmoqD4rWOJNBFoQKhqCqI ( R98KHng ( hcQwpl ( Df ) vhM1HXGxbjbjwGfenPYwWNPaE0abaBp6ArGOlAq06VoC0BkALGUsfvcMGe9x75jX8nHzyxbj6V2Ztpl ( 9 ) 5 jgeyIOJ7piXcSGOjnq08ZjRIMFrx02u0LIhHk60dLn71hJUUOv2SL2J2sgSV ( y0KMJX5bjwGfeT ( R980ZIFhdLIhHAQm1n1Q0VnIixhMFU4HIukEeQ5sjgz93Ybugkkdkd2xFC ( 4 yCUbdi66jrwkXyghOo8iyqzW ( 6 JZhhJZTwQe8YWbKZNOsLdjd4kQe4bjwq0KMTODmv0dMMEPOhGgCy7rF4OTqfBZOuXGirBPu5q0wO6Fi6hBrlAYVfNQOTuEhp6dhTwrtkphnr0 ) C0p2Iw0dG1vg9rf9GMzDIJUu8iuPGe9x75PNf ) obfVkQeeXveqmQu5WKR ( hezPelkMXbQdpcMpn9sZIgCy7wffZ4a1HhbdxX2mkvmmraUkL75IqqLmG4sLGxgQfNQjQ8oUbCfvcCwSsSbPCwkEeQKbvQCyYv ) ddPtmrKYRvQe8YuyDLZJAIzw3aUIkboXbjwq0KMTOf9GPPxk6bObh2UirBPu5q0wO6Fi6h0ap6Igenkdfv0BkA ( 9 Wfj6hBrlAYVfNQOTuEhpATIM ( NJMOHph9JTOf9ayDLrFurpOzwN4OpC0p2Iw0KQuc8peTLyqTjATIM0FoAIiLNJ ( Xw0IEaSUYOpQOh0mRtC0LIhHkfKO ) App9S43jO4vrLGiUIaIrLkhMC1 ) GilLymJduhEemFA6LMfn4W2fHGkzaXOmuuMpn9sZIgCy7g ( 9 WzXQuj4LHAXPAIkVJBaxrLa3AIniLZsXJqLmOsLdtU6FyiDIjI ( xRuj4LPW6kNh1eZSUbCfvcCIzXs0sLGxMV9VeMh1KMwyGBaxrLa3AIniLZsXJqLmOsLdtU6FyiDIjI0FTsLGxMcRRCEutmZ6gWvujWjoiXcIM0SfTOTqfBZOuXGirBPu5q0wO6FiATI2pmIkJUu8iur ) pgVI ( bnWJgLHIc4rJApAn6e8pNRy7rduuWVej6dhTkFO2trRv0K ( aphn1HJ2p ) 1 Sqa589hKO ) App9S43jO4vrLGiUIaIrLkhMC1 ) GilLymJduhEemCfBZOuXWeb4QuUNlcbvYaIlvcEzOwCQMOY74gWvujWzXIiugkkdcukYHzt7sBYWWMfRsLGxMcRRCEutmZ6gWvujWzXIdOmuugiLa ) dtumO2yyytS1eBqkNLIhHkzqLkhMC1 ) Wq6eteP8ALkbVmfwx58OMyM1nGROsGtmlwIwQe8YWbKZ3VbCfvcCRj2GuolfpcvYGkvom5Q ) HH0jM0dsSGOheLGOjvPe4FiAlXGAt0Oa1HHOTuQCiAlu9pe9sf9wrVPOvc6kvujeT68OpkQO ) 3 j53dpir ) 1 EE6zXVtqXRIkbrCfbeJkvom5Q ) bro2IXqckrwkXLkbVmqkb ( hMOyqTXaUIkbU1 ) Ds ( 9 Wnqkb ( hMOyqTXGbLBpiXcIM0SfTOhKJNBp6xLsEq0QZJEW2 ) si6Jk6bbTWaxKOvc3YJMjT ( y0wkvoeTfQ ( hI ( bnWJUObyi6nfDrdIM9Lsl6k3YE01fnyRcCE0Qh9G8ivrlSofJmAlXQZds0FTNNEw87eu8QOsqexraXOsLdtU6FqKLsmMXbQdpcgD8C7ZrL8aRLkbVmF7FjmpQjnTWaxecQKbetqXRIkbdQu5WKR ( hSQ ) Ajat ( vM06umYjkwDo5Opir ) 1 EE6zXVtqXRIkbrCfbeZ ( o56JtQdprGsfHGkzaXIwQe8YWbKZ3VbCfvcCR ) 7 K87HBqGsromBAxAtgmGORNi3RyLIbB3WbQ9VfDKYlbj6V2Ztpl ( DckEvujiIRiGy23jxFCsD4jQu5WKR ( heHGkzaXeu8QOsWGkvom5Q ) bRerXGTtUx32VwPsWld1It1evEh3aUIkb ( GI ( xioir ) 1 EE6zXVtqXRIkbrCfbeZ ( o56JtQdpb7WefueriOsgqCPsWldhqoF ) gWvujWTkAPsWldQCD ( KIbB3aUIkbU1 ) Ds ( 9 WnGDyIckIbdi66jYr04NBquBnOONyRumy7goqT ) TOJ ( xcsSGOjnBrl6b5452J ( vPKhis0Avac7k66Ioz3 ) rtQSdrBjOirRop6 ) Ds ( 9 WJMjPJq0uhoAe1wlcds0CgSw75IenJlHukATIMug45Ge9x75PNf ) obfVkQeeXveq8dDR1hNuhEQJNBFoQKhiYsjgZ4a1HhbJoEU95OsEGieujdiwu ( vM06umYjkwDUP2VnRpA9FNKFpCtADkg5efRo3GbeD9e5g ) CdIARbfPBLir ) 3 j53d3GaLICy20U0MmmSzXs ) 1 saMGdilKepKyRj2GuolfpcvYa2HjkOiKtmPeKO ) App9S43jO4vrLGiUIaIFOBT ( 4 K6WtKdb8IbrecQKbexQe8YGCiGxmigWvujWTkkkdfLb5qaVyqmmSds0FTNNEw87FvkN6V2ZNYnvI4
end