-- DeathKnightUnholy.lua
-- June 2018
local addon , ns = ...
local Hekili = _G [ addon ]
local class = Hekili.Class
local state = Hekili.State
local roundUp = ns.roundUp
local FindUnitBuffByID = ns.FindUnitBuffByID
local PTR = ns.PTR
-- Conduits
-- [x] Convocation of the Dead
-- [-] Embrace Death
-- [x] Eternal Hunger
-- [x] Lingering Plague
if UnitClassBase ( " player " ) == " DEATHKNIGHT " then
local spec = Hekili : NewSpecialization ( 252 )
spec : RegisterResource ( Enum.PowerType . Runes , {
rune_regen = {
last = function ( )
return state.query_time
end ,
interval = function ( time , val )
local r = state.runes
if val == 6 then return - 1 end
return r.expiry [ val + 1 ] - time
end ,
stop = function ( x )
return x == 6
end ,
value = 1 ,
} ,
} , setmetatable ( {
expiry = { 0 , 0 , 0 , 0 , 0 , 0 } ,
cooldown = 10 ,
regen = 0 ,
max = 6 ,
forecast = { } ,
fcount = 0 ,
times = { } ,
values = { } ,
resource = " runes " ,
reset = function ( )
local t = state.runes
for i = 1 , 6 do
local start , duration , ready = GetRuneCooldown ( i )
start = start or 0
duration = duration or ( 10 * state.haste )
start = roundUp ( start , 2 )
t.expiry [ i ] = ready and 0 or start + duration
t.cooldown = duration
end
table.sort ( t.expiry )
t.actual = nil
end ,
gain = function ( amount )
local t = state.runes
for i = 1 , amount do
t.expiry [ 7 - i ] = 0
end
table.sort ( t.expiry )
t.actual = nil
end ,
spend = function ( amount )
local t = state.runes
for i = 1 , amount do
if t.expiry [ 4 ] > state.query_time then
t.expiry [ 1 ] = t.expiry [ 4 ] + t.cooldown
else
t.expiry [ 1 ] = state.query_time + t.cooldown
end
table.sort ( t.expiry )
end
if amount > 0 then
state.gain ( amount * 10 , " runic_power " )
if state.set_bonus . tier20_4pc == 1 then
state.cooldown . army_of_the_dead.expires = max ( 0 , state.cooldown . army_of_the_dead.expires - 1 )
end
end
t.actual = nil
end ,
timeTo = function ( x )
return state : TimeToResource ( state.runes , x )
end ,
} , {
__index = function ( t , k , v )
if k == " actual " then
local amount = 0
for i = 1 , 6 do
if t.expiry [ i ] <= state.query_time then
amount = amount + 1
end
end
return amount
elseif k == " current " then
-- If this is a modeled resource, use our lookup system.
if t.forecast and t.fcount > 0 then
local q = state.query_time
local index , slice
if t.values [ q ] then return t.values [ q ] end
for i = 1 , t.fcount do
local v = t.forecast [ i ]
if v.t <= q then
index = i
slice = v
else
break
end
end
-- We have a slice.
if index and slice then
t.values [ q ] = max ( 0 , min ( t.max , slice.v ) )
return t.values [ q ]
end
end
return t.actual
elseif k == " deficit " then
return t.max - t.current
elseif k == " time_to_next " then
return t [ " time_to_ " .. t.current + 1 ]
elseif k == " time_to_max " then
return t.current == 6 and 0 or max ( 0 , t.expiry [ 6 ] - state.query_time )
elseif k == " add " then
return t.gain
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 . RunicPower , {
swarming_mist = {
aura = " swarming_mist " ,
last = function ( )
local app = state.buff . swarming_mist.applied
local t = state.query_time
return app + floor ( ( t - app ) / class.auras . swarming_mist.tick_time ) * class.auras . swarming_mist.tick_time
end ,
interval = function ( ) return class.auras . swarming_mist.tick_time end ,
value = function ( ) return min ( 15 , state.true_active_enemies * 3 ) end ,
} ,
} )
spec : RegisterStateFunction ( " apply_festermight " , function ( n )
if azerite.festermight . enabled then
if buff.festermight . up then
addStack ( " festermight " , buff.festermight . remains , n )
else
applyBuff ( " festermight " , nil , n )
end
end
end )
local spendHook = function ( amt , resource , noHook )
if amt > 0 and resource == " runes " and active_dot.shackle_the_unworthy > 0 then
reduceCooldown ( " shackle_the_unworthy " , 4 * amt )
end
end
spec : RegisterHook ( " spend " , spendHook )
-- Talents
spec : RegisterTalents ( {
infected_claws = 22024 , -- 207272
all_will_serve = 22025 , -- 194916
clawing_shadows = 22026 , -- 207311
bursting_sores = 22027 , -- 207264
ebon_fever = 22028 , -- 207269
unholy_blight = 22029 , -- 115989
grip_of_the_dead = 22516 , -- 273952
deaths_reach = 22518 , -- 276079
asphyxiate = 22520 , -- 108194
pestilent_pustules = 22522 , -- 194917
harbinger_of_doom = 22524 , -- 276023
soul_reaper = 22526 , -- 343294
spell_eater = 22528 , -- 207321
wraith_walk = 22529 , -- 212552
death_pact = 23373 , -- 48743
pestilence = 22532 , -- 277234
unholy_pact = 22534 , -- 319230
defile = 22536 , -- 152280
army_of_the_damned = 22030 , -- 276837
summon_gargoyle = 22110 , -- 49206
unholy_assault = 22538 , -- 207289
} )
-- PvP Talents
spec : RegisterPvpTalents ( {
dark_simulacrum = 41 , -- 77606
deaths_echo = 5428 , -- 356367
dome_of_ancient_shadow = 5367 , -- 328718
doomburst = 5436 , -- 356512
life_and_death = 40 , -- 288855
necromancers_bargain = 3746 , -- 288848
necrotic_aura = 3437 , -- 199642
necrotic_wounds = 149 , -- 356520
raise_abomination = 3747 , -- 288853
reanimation = 152 , -- 210128
spellwarden = 5423 , -- 356332
strangulate = 5430 , -- 47476
} )
-- Auras
spec : RegisterAuras ( {
antimagic_shell = {
id = 48707 ,
duration = function ( ) return ( talent.spell_eater . enabled and 10 or 5 ) + ( conduit.reinforced_shell . mod * 0.001 ) end ,
max_stack = 1 ,
} ,
antimagic_zone = {
id = 145629 ,
duration = 8 ,
max_stack = 1 ,
} ,
army_of_the_dead = {
id = 42650 ,
duration = 4 ,
max_stack = 1 ,
} ,
asphyxiate = {
id = 108194 ,
duration = 4 ,
max_stack = 1 ,
} ,
chains_of_ice = {
id = 45524 ,
duration = 8 ,
max_stack = 1 ,
} ,
dark_command = {
id = 56222 ,
duration = 3 ,
max_stack = 1 ,
} ,
dark_succor = {
id = 101568 ,
duration = 20 ,
} ,
dark_transformation = {
id = 63560 ,
duration = function ( ) return 15 + ( conduit.eternal_hunger . mod * 0.001 ) end ,
generate = function ( t )
local name , _ , count , _ , duration , expires , caster , _ , _ , spellID , _ , _ , _ , _ , timeMod , v1 , v2 , v3 = FindUnitBuffByID ( " pet " , 63560 )
if name then
t.name = t.name or name or class.abilities . dark_transformation.name
t.count = count > 0 and count or 1
t.expires = expires
t.duration = duration
t.applied = expires - duration
t.caster = " player "
return
end
t.name = t.name or class.abilities . dark_transformation.name
t.count = 0
t.expires = 0
t.duration = class.auras . dark_transformation.duration
t.applied = 0
t.caster = " nobody "
end ,
} ,
death_and_decay = {
id = 188290 ,
duration = 10 ,
max_stack = 1 ,
} ,
death_pact = {
id = 48743 ,
duration = 15 ,
max_stack = 1 ,
} ,
deaths_advance = {
id = 48265 ,
duration = 10 ,
max_stack = 1 ,
} ,
defile = {
id = 152280 ,
duration = 10 ,
} ,
festering_wound = {
id = 194310 ,
duration = 30 ,
max_stack = 6 ,
--[[ meta = {
stack = function ( )
-- Designed to work with Unholy Frenzy, time until 4th Festering Wound would be applied.
local actual = debuff.festering_wound . up and debuff.festering_wound . count or 0
if buff.unholy_frenzy . down or debuff.festering_wound . down then
return actual
end
local slot_time = query_time
local swing , speed = state.swings . mainhand , state.swings . mainhand_speed
local last = swing + ( speed * floor ( slot_time - swing ) / swing )
local window = min ( buff.unholy_frenzy . expires , query_time ) - last
local bonus = floor ( window / speed )
return min ( 6 , actual + bonus )
end
} ] ]
} ,
frostbolt = {
id = 317792 ,
duration = 4 ,
max_stack = 1 ,
} ,
gnaw = {
id = 91800 ,
duration = 0.5 ,
max_stack = 1 ,
} ,
grip_of_the_dead = {
id = 273977 ,
duration = 3600 ,
max_stack = 1 ,
} ,
icebound_fortitude = {
id = 48792 ,
duration = 8 ,
max_stack = 1 ,
} ,
lichborne = {
id = 49039 ,
duration = 10 ,
max_stack = 1 ,
} ,
on_a_pale_horse = {
id = 51986 ,
} ,
path_of_frost = {
id = 3714 ,
duration = 600 ,
max_stack = 1 , k
} ,
runic_corruption = {
id = 51460 ,
duration = function ( ) return 3 * haste end ,
max_stack = 1 ,
} ,
soul_reaper = {
id = 343294 ,
duration = 5 ,
type = " Magic " ,
max_stack = 1 ,
} ,
sudden_doom = {
id = 81340 ,
duration = 10 ,
max_stack = function ( ) return talent.harbinger_of_doom . enabled and 2 or 1 end ,
} ,
unholy_assault = {
id = 207289 ,
duration = 12 ,
max_stack = 1 ,
} ,
unholy_blight_buff = {
id = 115989 ,
duration = 6 ,
max_stack = 1 ,
dot = " buff " ,
} ,
unholy_blight = {
id = 115994 ,
duration = 14 ,
tick_time = function ( ) return 2 * haste end ,
max_stack = 4 ,
copy = { " unholy_blight_debuff " , " unholy_blight_dot " }
} ,
unholy_pact = {
id = 319230 ,
duration = 15 ,
max_stack = 1 ,
} ,
unholy_strength = {
id = 53365 ,
duration = 15 ,
max_stack = 1 ,
} ,
virulent_plague = {
id = 191587 ,
duration = function ( ) return 27 * ( talent.ebon_fever . enabled and 0.5 or 1 ) end ,
tick_time = function ( ) return 3 * ( talent.ebon_fever . enabled and 0.5 or 1 ) end ,
type = " Disease " ,
max_stack = 1 ,
} ,
wraith_walk = {
id = 212552 ,
duration = 4 ,
type = " Magic " ,
max_stack = 1 ,
} ,
-- PvP Talents
crypt_fever = {
id = 288849 ,
duration = 4 ,
max_stack = 1 ,
} ,
doomburst = {
id = 356518 ,
duration = 3 ,
max_stack = 2 ,
} ,
necrotic_wound = {
id = 223929 ,
duration = 18 ,
max_stack = 1 ,
} ,
-- Azerite Powers
cold_hearted = {
id = 288426 ,
duration = 8 ,
max_stack = 1
} ,
festermight = {
id = 274373 ,
duration = 20 ,
max_stack = 99 ,
} ,
helchains = {
id = 286979 ,
duration = 15 ,
max_stack = 1
}
} )
spec : RegisterStateTable ( " death_and_decay " ,
setmetatable ( { onReset = function ( self ) end } ,
{ __index = function ( t , k )
if k == " ticking " then
return buff.death_and_decay . up
elseif k == " remains " then
return buff.death_and_decay . remains
end
return false
end } ) )
spec : RegisterStateTable ( " defile " ,
setmetatable ( { onReset = function ( self ) end } ,
{ __index = function ( t , k )
if k == " ticking " then
return buff.death_and_decay . up
elseif k == " remains " then
return buff.death_and_decay . remains
end
return false
end } ) )
spec : RegisterStateExpr ( " dnd_ticking " , function ( )
return death_and_decay.ticking
end )
spec : RegisterStateExpr ( " dnd_remains " , function ( )
return death_and_decay.remains
end )
spec : RegisterStateExpr ( " spreading_wounds " , function ( )
if talent.infected_claws . enabled and buff.dark_transformation . up then return false end -- Ghoul is dumping wounds for us, don't bother.
return azerite.festermight . enabled and settings.cycle and settings.festermight_cycle and cooldown.death_and_decay . remains < 9 and active_dot.festering_wound < spell_targets.festering_strike
end )
spec : RegisterStateFunction ( " time_to_wounds " , function ( x )
if debuff.festering_wound . stack >= x then return 0 end
return 3600
--[[ No timeable wounds mechanic in SL?
if buff.unholy_frenzy . down then return 3600 end
local deficit = x - debuff.festering_wound . stack
local swing , speed = state.swings . mainhand , state.swings . mainhand_speed
local last = swing + ( speed * floor ( query_time - swing ) / swing )
local fw = last + ( speed * deficit ) - query_time
if fw > buff.unholy_frenzy . remains then return 3600 end
return fw ] ]
end )
spec : RegisterHook ( " step " , function ( time )
if Hekili.ActiveDebug then Hekili : Debug ( " Rune Regeneration Time: 1=%.2f, 2=%.2f, 3=%.2f, 4=%.2f, 5=%.2f, 6=%.2f \n " , runes.time_to_1 , runes.time_to_2 , runes.time_to_3 , runes.time_to_4 , runes.time_to_5 , runes.time_to_6 ) end
end )
-- Tier 28
spec : RegisterSetBonuses ( " tier28_2pc " , 364392 , " tier28_4pc " , 363560 )
-- 2-Set - Every 5 Scourge Strikes casts Soul Reaper on your target. Soul Reaper grants your pet 20% Attack Speed for 10 seconds.
-- 4-Set - Your minions deal 5% increased damage. When Soul Reaper's secondary effect triggers, this bonus is increased to 25% for 8 seconds.
spec : RegisterAuras ( {
harvest_time_stack = {
id = 363885 ,
duration = 3600 ,
max_stack = 5
} ,
harvest_time = {
id = 363887 ,
duration = 3600 ,
max_stack = 1
} ,
harvest_time_pet = {
id = 367954 ,
duration = 8 ,
max_stack = 1 ,
generate = function ( t )
local name , _ , count , _ , duration , expires , caster = FindUnitBuffByID ( " pet " , 367954 )
if name then
t.name = name
t.count = count
t.expires = expires
t.applied = expires - duration
t.caster = caster
return
end
t.count = 0
t.expires = 0
t.applied = 0
t.caster = " nobody "
end ,
}
} )
spec : RegisterGear ( " tier19 " , 138355 , 138361 , 138364 , 138349 , 138352 , 138358 )
spec : RegisterGear ( " tier20 " , 147124 , 147126 , 147122 , 147121 , 147123 , 147125 )
spec : RegisterAura ( " master_of_ghouls " , {
id = 246995 ,
duration = 3 ,
max_stack = 1
} )
spec : RegisterGear ( " tier21 " , 152115 , 152117 , 152113 , 152112 , 152114 , 152116 )
spec : RegisterAura ( " coils_of_devastation " , {
id = 253367 ,
duration = 4 ,
max_stack = 1
} )
spec : RegisterGear ( " acherus_drapes " , 132376 )
spec : RegisterGear ( " cold_heart " , 151796 ) -- chilled_heart stacks NYI
spec : RegisterAura ( " cold_heart_item " , {
id = 235599 ,
duration = 3600 ,
max_stack = 20
} )
spec : RegisterGear ( " consorts_cold_core " , 144293 )
spec : RegisterGear ( " death_march " , 144280 )
-- spec:RegisterGear( "death_screamers", 151797 )
spec : RegisterGear ( " draugr_girdle_of_the_everlasting_king " , 132441 )
spec : RegisterGear ( " koltiras_newfound_will " , 132366 )
spec : RegisterGear ( " lanathels_lament " , 133974 )
spec : RegisterGear ( " perseverance_of_the_ebon_martyr " , 132459 )
spec : RegisterGear ( " rethus_incessant_courage " , 146667 )
spec : RegisterGear ( " seal_of_necrofantasia " , 137223 )
spec : RegisterGear ( " shackles_of_bryndaor " , 132365 ) -- NYI
spec : RegisterGear ( " soul_of_the_deathlord " , 151740 )
spec : RegisterGear ( " soulflayers_corruption " , 151795 )
spec : RegisterGear ( " the_instructors_fourth_lesson " , 132448 )
spec : RegisterGear ( " toravons_whiteout_bindings " , 132458 )
spec : RegisterGear ( " uvanimor_the_unbeautiful " , 137037 )
spec : RegisterPet ( " ghoul " , 26125 , " raise_dead " , 3600 )
spec : RegisterTotem ( " gargoyle " , 458967 )
spec : RegisterTotem ( " abomination " , 298667 )
spec : RegisterPet ( " apoc_ghoul " , 24207 , " apocalypse " , 15 )
spec : RegisterPet ( " army_ghoul " , 24207 , " army_of_the_dead " , 30 )
local ForceVirulentPlagueRefresh = setfenv ( function ( )
StoreMatchingAuras ( " target " , { count = 1 , [ 191587 ] = " virulent_plague " } , " HARMFUL " , select ( 2 , UnitAuraSlots ( " target " , " HARMFUL " ) ) )
Hekili : ForceUpdate ( " VIRULENT_PLAGUE_REFRESH " )
end , state )
local After = C_Timer.After
spec : RegisterHook ( " COMBAT_LOG_EVENT_UNFILTERED " , function ( _ , subtype , _ , sourceGUID , sourceName , _ , _ , destGUID , destName , destFlags , _ , spellID )
if sourceGUID == GUID and subtype == " SPELL_CAST_SUCCESS " and spellID == 77575 then
After ( state.latency , ForceVirulentPlagueRefresh )
After ( state.latency * 2 , ForceVirulentPlagueRefresh )
end
end , false )
local any_dnd_set , wound_spender_set = false , false
local ExpireRunicCorruption = setfenv ( function ( )
local debugstr
if Hekili.ActiveDebug then debugstr = format ( " Runic Corruption expired; updating regen from %.2f to %.2f at %.2f + %.2f. " , rune.cooldown , rune.cooldown * 2 , offset , delay ) end
rune.cooldown = rune.cooldown * 2
for i = 1 , 6 do
local exp = rune.expiry [ i ] - query_time
if exp > 0 then
rune.expiry [ i ] = rune.expiry [ i ] + exp
if Hekili.ActiveDebug then debugstr = format ( " %s \n - rune %d extended by %.2f [%.2f]. " , debugstr , i , exp , rune.expiry [ i ] - query_time ) end
end
end
table.sort ( rune.expiry )
rune.actual = nil
if Hekili.ActiveDebug then debugstr = format ( " %s \n - %d, %.2f %.2f %.2f %.2f %.2f %.2f. " , debugstr , rune.current , rune.expiry [ 1 ] - query_time , rune.expiry [ 2 ] - query_time , rune.expiry [ 3 ] - query_time , rune.expiry [ 4 ] - query_time , rune.expiry [ 5 ] - query_time , rune.expiry [ 6 ] - query_time ) end
forecastResources ( " runes " )
if Hekili.ActiveDebug then debugstr = format ( " %s \n - %d, %.2f %.2f %.2f %.2f %.2f %.2f. " , debugstr , rune.current , rune.expiry [ 1 ] - query_time , rune.expiry [ 2 ] - query_time , rune.expiry [ 3 ] - query_time , rune.expiry [ 4 ] - query_time , rune.expiry [ 5 ] - query_time , rune.expiry [ 6 ] - query_time ) end
if debugstr then Hekili : Debug ( debugstr ) end
end , state )
spec : RegisterHook ( " reset_precast " , function ( )
if buff.runic_corruption . up then
state : QueueAuraExpiration ( " runic_corruption " , ExpireRunicCorruption , buff.runic_corruption . expires )
end
local expires = action.summon_gargoyle . lastCast + 35
if expires > now then
summonPet ( " gargoyle " , expires - now )
end
local control_expires = action.control_undead . lastCast + 300
if control_expires > now and pet.up and not pet.ghoul . up then
summonPet ( " controlled_undead " , control_expires - now )
end
local apoc_expires = action.apocalypse . lastCast + 15
if apoc_expires > now then
summonPet ( " apoc_ghoul " , apoc_expires - now )
end
local army_expires = action.army_of_the_dead . lastCast + 30
if army_expires > now then
summonPet ( " army_ghoul " , army_expires - now )
end
if talent.all_will_serve . enabled and pet.ghoul . up then
summonPet ( " skeleton " )
end
rawset ( cooldown , " army_of_the_dead " , nil )
rawset ( cooldown , " raise_abomination " , nil )
if pvptalent.raise_abomination . enabled then
cooldown.army_of_the_dead = cooldown.raise_abomination
else
cooldown.raise_abomination = cooldown.army_of_the_dead
end
if state : IsKnown ( " deaths_due " ) then
class.abilities . any_dnd = class.abilities . deaths_due
cooldown.any_dnd = cooldown.deaths_due
setCooldown ( " death_and_decay " , cooldown.deaths_due . remains )
elseif state : IsKnown ( " defile " ) then
class.abilities . any_dnd = class.abilities . defile
cooldown.any_dnd = cooldown.defile
setCooldown ( " death_and_decay " , cooldown.defile . remains )
else
class.abilities . any_dnd = class.abilities . death_and_decay
cooldown.any_dnd = cooldown.death_and_decay
end
if not any_dnd_set then
class.abilityList . any_dnd = " |T136144:0|t |cff00ccff[Any]|r " .. class.abilities . death_and_decay.name
any_dnd_set = true
end
if state : IsKnown ( " clawing_shadows " ) then
class.abilities . wound_spender = class.abilities . clawing_shadows
cooldown.wound_spender = cooldown.clawing_shadows
else
class.abilities . wound_spender = class.abilities . scourge_strike
cooldown.wound_spender = cooldown.scourge_strike
end
if not wound_spender_set then
class.abilityList . wound_spender = " |T237530:0|t |cff00ccff[Wound Spender]|r "
wound_spender_set = true
end
if state : IsKnown ( " deaths_due " ) and cooldown.deaths_due . remains then setCooldown ( " death_and_decay " , cooldown.deaths_due . remains )
elseif talent.defile . enabled and cooldown.defile . remains then setCooldown ( " death_and_decay " , cooldown.defile . remains ) end
-- Reset CDs on any Rune abilities that do not have an actual cooldown.
for action in pairs ( class.abilityList ) do
local data = class.abilities [ action ]
if data.cooldown == 0 and data.spendType == " runes " then
setCooldown ( action , 0 )
end
end
end )
local mt_runeforges = {
__index = function ( t , k )
return false
end ,
}
-- Not actively supporting this since we just respond to the player precasting AOTD as they see fit.
spec : RegisterStateTable ( " death_knight " , setmetatable ( {
disable_aotd = false ,
delay = 6 ,
runeforge = setmetatable ( { } , mt_runeforges )
} , {
__index = function ( t , k )
if k == " fwounded_targets " then return state.active_dot . festering_wound end
if k == " disable_iqd_execute " then return state.settings . disable_iqd_execute and 1 or 0 end
return 0
end ,
} ) )
local runeforges = {
[ 6243 ] = " hysteria " ,
[ 3370 ] = " razorice " ,
[ 6241 ] = " sanguination " ,
[ 6242 ] = " spellwarding " ,
[ 6245 ] = " apocalypse " ,
[ 3368 ] = " fallen_crusader " ,
[ 3847 ] = " stoneskin_gargoyle " ,
[ 6244 ] = " unending_thirst "
}
local function ResetRuneforges ( )
table.wipe ( state.death_knight . runeforge )
end
local function UpdateRuneforge ( slot , item )
if ( slot == 16 or slot == 17 ) then
local link = GetInventoryItemLink ( " player " , slot )
local enchant = link : match ( " item:%d+:(%d+) " )
if enchant then
enchant = tonumber ( enchant )
local name = runeforges [ enchant ]
if name then
state.death_knight . runeforge [ name ] = true
if name == " razorice " and slot == 16 then
state.death_knight . runeforge.razorice_mh = true
elseif name == " razorice " and slot == 17 then
state.death_knight . runeforge.razorice_oh = true
end
end
end
end
end
Hekili : RegisterGearHook ( ResetRuneforges , UpdateRuneforge )
-- Abilities
spec : RegisterAbilities ( {
antimagic_shell = {
id = 48707 ,
cast = 0 ,
cooldown = 60 ,
gcd = " off " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 136120 ,
handler = function ( )
applyBuff ( " antimagic_shell " )
end ,
} ,
antimagic_zone = {
id = 51052 ,
cast = 0 ,
cooldown = 180 ,
gcd = " spell " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 237510 ,
handler = function ( )
applyBuff ( " antimagic_zone " )
end ,
} ,
apocalypse = {
id = 275699 ,
cast = 0 ,
cooldown = function ( ) return ( essence.vision_of_perfection . enabled and 0.87 or 1 ) * ( ( pvptalent.necromancers_bargain . enabled and 45 or 90 ) - ( level > 48 and 15 or 0 ) ) end ,
gcd = " spell " ,
toggle = function ( ) return not talent.army_of_the_damned . enabled and " cooldowns " or nil end ,
startsCombat = true ,
texture = 1392565 ,
debuff = " festering_wound " ,
handler = function ( )
summonPet ( " apoc_ghoul " , 15 )
if pvptalent.necrotic_wounds . enabled and debuff.festering_wound . up and debuff.necrotic_wound . down then
applyDebuff ( " target " , " necrotic_wound " )
end
if debuff.festering_wound . stack > 4 then
applyDebuff ( " target " , " festering_wound " , debuff.festering_wound . remains , debuff.festering_wound . remains - 4 )
apply_festermight ( 4 )
if conduit.convocation_of_the_dead . enabled and cooldown.apocalypse . remains > 0 then
reduceCooldown ( " apocalypse " , 4 * conduit.convocation_of_the_dead . mod * 0.1 )
end
gain ( 12 , " runic_power " )
else
gain ( 3 * debuff.festering_wound . stack , " runic_power " )
apply_festermight ( debuff.festering_wound . stack )
if conduit.convocation_of_the_dead . enabled and cooldown.apocalypse . remains > 0 then
reduceCooldown ( " apocalypse " , debuff.festering_wound . stack * conduit.convocation_of_the_dead . mod * 0.1 )
end
removeDebuff ( " target " , " festering_wound " )
end
if level > 57 then gain ( 2 , " runes " ) end
if pvptalent.necromancers_bargain . enabled then applyDebuff ( " target " , " crypt_fever " ) end
end ,
auras = {
frenzied_monstrosity = {
id = 334895 ,
duration = 15 ,
max_stack = 1 ,
} ,
frenzied_monstrosity_pet = {
id = 334896 ,
duration = 15 ,
max_stack = 1
}
}
} ,
army_of_the_dead = {
id = function ( ) return pvptalent.raise_abomination . enabled and 288853 or 42650 end ,
cast = 0 ,
cooldown = function ( ) return pvptalent.raise_abomination . enabled and 120 or 480 end ,
gcd = " spell " ,
spend = function ( ) return pvptalent.raise_abomination . enabled and 0 or 3 end ,
spendType = " runes " ,
toggle = " cooldowns " ,
-- nopvptalent = "raise_abomination",
startsCombat = false ,
texture = function ( ) return pvptalent.raise_abomination . enabled and 298667 or 237511 end ,
handler = function ( )
if pvptalent.raise_abomination . enabled then
summonPet ( " abomination " )
else
applyBuff ( " army_of_the_dead " , 4 )
end
end ,
copy = { 288853 , 42650 , " army_of_the_dead " , " raise_abomination " }
} ,
asphyxiate = {
id = 108194 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
startsCombat = true ,
texture = 538558 ,
toggle = " interrupts " ,
talent = " asphyxiate " ,
debuff = " casting " ,
readyTime = state.timeToInterrupt ,
handler = function ( )
applyDebuff ( " target " , " asphyxiate " )
end ,
} ,
chains_of_ice = {
id = 45524 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 135834 ,
handler = function ( )
applyDebuff ( " target " , " chains_of_ice " )
removeBuff ( " cold_heart_item " )
end ,
} ,
clawing_shadows = {
id = 207311 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 615099 ,
talent = " clawing_shadows " ,
handler = function ( )
if debuff.festering_wound . up then
if debuff.festering_wound . stack > 1 then
applyDebuff ( " target " , " festering_wound " , debuff.festering_wound . remains , debuff.festering_wound . stack - 1 )
if set_bonus.tier28_2pc > 0 then
if buff.harvest_time . up then
applyDebuff ( " target " , " soul_reaper " )
removeBuff ( " harvest_time " )
summonPet ( " army_ghoul " , 15 )
else
addStack ( " harvest_time_stack " , nil , 1 )
if buff.harvest_time_stack . stack == 5 then
removeBuff ( " harvest_time_stack " )
applyBuff ( " harvest_time " )
end
end
end
else removeDebuff ( " target " , " festering_wound " ) end
if conduit.convocation_of_the_dead . enabled and cooldown.apocalypse . remains > 0 then
reduceCooldown ( " apocalypse " , conduit.convocation_of_the_dead . mod * 0.1 )
end
apply_festermight ( 1 )
end
gain ( 3 , " runic_power " )
end ,
bind = { " scourge_strike " , " wound_spender " }
} ,
control_undead = {
id = 111673 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 237273 ,
usable = function ( ) return target.is_undead and target.level <= level + 1 end ,
handler = function ( )
dismissPet ( " ghoul " )
summonPet ( " controlled_undead " , 300 )
end ,
} ,
dark_command = {
id = 56222 ,
cast = 0 ,
cooldown = 8 ,
gcd = " off " ,
startsCombat = true ,
texture = 136088 ,
handler = function ( )
applyDebuff ( " target " , " dark_command " )
end ,
} ,
dark_simulacrum = {
id = 77606 ,
cast = 0 ,
cooldown = 20 ,
gcd = " spell " ,
spend = 0 ,
spendType = " runic_power " ,
startsCombat = true ,
texture = 135888 ,
pvptalent = " dark_simulacrum " ,
usable = function ( )
if not target.is_player then return false , " target is not a player " end
return true
end ,
handler = function ( )
applyDebuff ( " target " , " dark_simulacrum " )
end ,
} ,
dark_transformation = {
id = 63560 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
startsCombat = false ,
texture = 342913 ,
usable = function ( ) return pet.ghoul . alive end ,
handler = function ( )
applyBuff ( " dark_transformation " )
if azerite.helchains . enabled then applyBuff ( " helchains " ) end
if talent.unholy_pact . enabled then applyBuff ( " unholy_pact " ) end
if legendary.frenzied_monstrosity . enabled then
applyBuff ( " frenzied_monstrosity " )
applyBuff ( " frenzied_monstrosity_pet " )
end
end ,
auras = {
frenzied_monstrosity = {
id = 334895 ,
duration = 15 ,
max_stack = 1 ,
} ,
frenzied_monstrosity_pet = {
id = 334896 ,
duration = 15 ,
max_stack = 1
}
}
} ,
death_and_decay = {
id = 43265 ,
noOverride = 324128 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 136144 ,
notalent = " defile " ,
handler = function ( )
applyBuff ( " death_and_decay " , 10 )
if talent.grip_of_the_dead . enabled then applyDebuff ( " target " , " grip_of_the_dead " ) end
end ,
bind = { " defile " , " any_dnd " } ,
copy = " any_dnd "
} ,
death_coil = {
id = 47541 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.sudden_doom . up and 0 or ( legendary.deadliest_coil . enabled and 30 or 40 ) end ,
spendType = " runic_power " ,
startsCombat = true ,
texture = 136145 ,
handler = function ( )
if pvptalent.doomburst . enabled and buff.sudden_doom . up and debuff.festering_wound . up then
if debuff.festering_wound . stack > 2 then
applyDebuff ( " target " , " festering_wound " , debuff.festering_wound . remains , debuff.festering_wound . stack - 2 )
applyDebuff ( " target " , " doomburst " , debuff.doomburst . up and debuff.doomburst . remains or nil , 2 )
else
removeDebuff ( " target " , " festering_wound " )
applyDebuff ( " target " , " doomburst " , debuff.doomburst . up and debuff.doomburst . remains or nil , debuff.doomburst . stack + 1 )
end
end
removeStack ( " sudden_doom " )
if cooldown.dark_transformation . remains > 0 then setCooldown ( " dark_transformation " , max ( 0 , cooldown.dark_transformation . remains - 1 ) ) end
if legendary.deadliest_coil . enabled and buff.dark_transformation . up then buff.dark_transformation . expires = buff.dark_transformation . expires + 2 end
if legendary.deaths_certainty . enabled then
local spell = covenant.night_fae and " deaths_due " or ( talent.defile . enabled and " defile " or " death_and_decay " )
if cooldown [ spell ] . remains > 0 then reduceCooldown ( spell , 2 ) end
end
end ,
} ,
death_grip = {
id = 49576 ,
cast = 0 ,
charges = function ( )
if not pvptalent.deaths_echo . enabled then return end
return 2
end ,
cooldown = 25 ,
recharge = function ( )
if not pvptalent.deaths_echo . enabled then return end
return 25
end ,
gcd = " spell " ,
startsCombat = true ,
texture = 237532 ,
handler = function ( )
applyDebuff ( " target " , " death_grip " )
setDistance ( 5 )
if conduit.unending_grip . enabled then applyDebuff ( " target " , " unending_grip " ) end
end ,
} ,
death_pact = {
id = 48743 ,
cast = 0 ,
cooldown = 120 ,
gcd = " spell " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 136146 ,
talent = " death_pact " ,
handler = function ( )
gain ( health.max * 0.5 , " health " )
applyDebuff ( " player " , " death_pact " )
end ,
} ,
death_strike = {
id = 49998 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.dark_succor . up and 0 or ( ( buff.transfusion . up and 0.5 or 1 ) * 35 ) end ,
spendType = " runic_power " ,
startsCombat = true ,
texture = 237517 ,
handler = function ( )
removeBuff ( " dark_succor " )
if legendary.deaths_certainty . enabled then
local spell = conduit.night_fae and " deaths_due " or ( talent.defile . enabled and " defile " or " death_and_decay " )
if cooldown [ spell ] . remains > 0 then reduceCooldown ( spell , 2 ) end
end
end ,
} ,
deaths_advance = {
id = 48265 ,
cast = 0 ,
charges = function ( )
if not pvptalent.deaths_echo . enabled then return end
return 2
end ,
cooldown = 45 ,
recharge = function ( )
if not pvptalent.deaths_echo . enabled then return end
return 45
end ,
gcd = " spell " ,
startsCombat = false ,
texture = 237561 ,
handler = function ( )
applyBuff ( " deaths_advance " )
if conduit.fleeting_wind . enabled then applyBuff ( " fleeting_wind " ) end
end ,
} ,
defile = {
id = 152280 ,
cast = 0 ,
charges = function ( )
if not pvptalent.deaths_echo . enabled then return end
return 2
end ,
cooldown = 20 ,
recharge = function ( )
if not pvptalent.deaths_echo . enabled then return end
return 20
end ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
talent = " defile " ,
startsCombat = true ,
texture = 1029008 ,
handler = function ( )
applyBuff ( " death_and_decay " )
setCooldown ( " death_and_decay " , 20 )
applyDebuff ( " target " , " defile " , 1 )
end ,
bind = { " defile " , " any_dnd " } ,
} ,
epidemic = {
id = 207317 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.sudden_doom . up and 0 or 30 end ,
spendType = " runic_power " ,
startsCombat = true ,
texture = 136066 ,
targets = {
count = function ( ) return active_dot.virulent_plague end ,
} ,
usable = function ( ) return active_dot.virulent_plague > 0 end ,
handler = function ( )
removeBuff ( " sudden_doom " )
end ,
} ,
festering_strike = {
id = 85948 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 2 ,
spendType = " runes " ,
startsCombat = true ,
texture = 879926 ,
aura = " festering_wound " ,
cycle = " festering_wound " ,
min_ttd = function ( ) return min ( cooldown.death_and_decay . remains + 3 , 8 ) end , -- don't try to cycle onto targets that will die too fast to get consumed.
handler = function ( )
applyDebuff ( " target " , " festering_wound " , nil , debuff.festering_wound . stack + 2 )
end ,
} ,
icebound_fortitude = {
id = 48792 ,
cast = 0 ,
cooldown = function ( ) return 180 - ( azerite.cold_hearted . enabled and 15 or 0 ) + ( conduit.chilled_resilience . mod * 0.001 ) end ,
gcd = " spell " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 237525 ,
handler = function ( )
applyBuff ( " icebound_fortitude " )
if azerite.cold_hearted . enabled then applyBuff ( " cold_hearted " ) end
end ,
} ,
lichborne = {
id = 49039 ,
cast = 0 ,
cooldown = 60 ,
gcd = " off " ,
toggle = " defensives " ,
startsCombat = false ,
texture = 136187 ,
handler = function ( )
applyBuff ( " lichborne " )
if conduit.hardened_bones . enabled then applyBuff ( " hardened_bones " ) end
end ,
} ,
mind_freeze = {
id = 47528 ,
cast = 0 ,
cooldown = 15 ,
gcd = " spell " ,
spend = 0 ,
spendType = " runic_power " ,
startsCombat = true ,
texture = 237527 ,
toggle = " interrupts " ,
debuff = " casting " ,
readyTime = state.timeToInterrupt ,
handler = function ( )
if conduit.spirit_drain . enabled then gain ( conduit.spirit_drain . mod * 0.1 , " runic_power " ) end
interrupt ( )
end ,
} ,
outbreak = {
id = 77575 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 348565 ,
cycle = " virulent_plague " ,
handler = function ( )
applyDebuff ( " target " , " virulent_plague " )
active_dot.virulent_plague = active_enemies
end ,
} ,
path_of_frost = {
id = 3714 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = false ,
texture = 237528 ,
handler = function ( )
applyBuff ( " path_of_frost " )
end ,
} ,
--[[ raise_ally = {
id = 61999 ,
cast = 0 ,
cooldown = 600 ,
gcd = " spell " ,
spend = 30 ,
spendType = " runic_power " ,
startsCombat = false ,
texture = 136143 ,
handler = function ( )
end ,
} , ] ]
raise_dead = {
id = function ( ) return IsActiveSpell ( 46584 ) and 46584 or 46585 end ,
cast = 0 ,
cooldown = function ( ) return level < 29 and 120 or 30 end ,
icd = 5 ,
gcd = " spell " ,
startsCombat = false ,
texture = 1100170 ,
essential = true , -- new flag, will allow recasting even in precombat APL.
nomounted = true ,
usable = function ( ) return not pet.alive end ,
handler = function ( )
summonPet ( " ghoul " , level > 28 and 3600 or 30 )
if talent.all_will_serve . enabled then summonPet ( " skeleton " , level > 28 and 3600 or 30 ) end
end ,
copy = { 46584 , 46585 }
} ,
sacrificial_pact = {
id = 327574 ,
cast = 0 ,
cooldown = 120 ,
gcd = " spell " ,
spend = 20 ,
spendType = " runic_power " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 136133 ,
usable = function ( ) return pet.alive , " requires an undead pet " end ,
handler = function ( )
dismissPet ( " ghoul " )
gain ( 0.25 * health.max , " health " )
end ,
} ,
scourge_strike = {
id = 55090 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 237530 ,
notalent = " clawing_shadows " ,
handler = function ( )
gain ( 3 , " runic_power " )
if debuff.festering_wound . stack > 1 then
applyDebuff ( " target " , " festering_wound " , debuff.festering_wound . remains , debuff.festering_wound . stack - 1 )
if set_bonus.tier28_2pc > 0 then
if buff.harvest_time . up then
applyDebuff ( " target " , " soul_reaper " )
removeBuff ( " harvest_time " )
summonPet ( " army_ghoul " , 15 )
else
addStack ( " harvest_time_stack " , nil , 1 )
if buff.harvest_time_stack . stack == 5 then
removeBuff ( " harvest_time_stack " )
applyBuff ( " harvest_time " )
end
end
end
else removeDebuff ( " target " , " festering_wound " ) end
apply_festermight ( 1 )
if conduit.lingering_plague . enabled and debuff.virulent_plague . up then
debuff.virulent_plague . expires = debuff.virulent_plague . expires + ( conduit.lingering_plague . mod * 0.001 )
end
end ,
bind = { " clawing_shadows " , " wound_spender " }
} ,
soul_reaper = {
id = 343294 ,
cast = 0 ,
cooldown = 6 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 636333 ,
aura = " soul_reaper " ,
talent = " soul_reaper " ,
handler = function ( )
applyDebuff ( " target " , " soul_reaper " )
end ,
} ,
summon_gargoyle = {
id = 49206 ,
cast = 0 ,
cooldown = 180 ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 458967 ,
talent = " summon_gargoyle " ,
handler = function ( )
summonPet ( " gargoyle " , 30 )
end ,
} ,
transfusion = {
id = 288977 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
spend = - 20 ,
spendType = " runic_power " ,
startsCombat = false ,
texture = 237515 ,
pvptalent = " transfusion " ,
handler = function ( )
applyBuff ( " transfusion " )
end ,
} ,
unholy_assault = {
id = 207289 ,
cast = 0 ,
cooldown = 75 ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 136224 ,
talent = " unholy_assault " ,
cycle = " festering_wound " ,
handler = function ( )
applyDebuff ( " target " , " festering_wound " , nil , min ( 6 , debuff.festering_wound . stack + 4 ) )
applyBuff ( " unholy_frenzy " )
stat.haste = stat.haste + 0.1
end ,
} ,
unholy_blight = {
id = 115989 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
spend = 1 ,
spendType = " runes " ,
startsCombat = true ,
texture = 136132 ,
talent = " unholy_blight " ,
handler = function ( )
applyBuff ( " unholy_blight_buff " )
applyDebuff ( " target " , " unholy_blight " )
applyDebuff ( " target " , " virulent_plague " )
active_dot.virulent_plague = active_enemies
end ,
} ,
wraith_walk = {
id = 212552 ,
cast = 4 ,
channeled = true ,
cooldown = 60 ,
gcd = " spell " ,
startsCombat = false ,
texture = 1100041 ,
talent = " wraith_walk " ,
start = function ( )
applyBuff ( " wraith_walk " )
end ,
} ,
-- Stub.
any_dnd = {
name = function ( ) return " |T136144:0|t |cff00ccff[Any]|r " .. ( class.abilities . death_and_decay and class.abilities . death_and_decay.name or " Death and Decay " ) end ,
} ,
wound_spender = {
name = " |T237530:0|t |cff00ccff[Wound Spender]|r " ,
}
} )
spec : RegisterOptions ( {
enabled = true ,
aoe = 2 ,
nameplates = true ,
nameplateRange = 8 ,
damage = true ,
damageExpiration = 8 ,
cycle = true ,
cycleDebuff = " festering_wound " ,
enhancedRecheck = true ,
potion = " potion_of_spectral_strength " ,
package = " Unholy " ,
} )
spec : RegisterSetting ( " disable_iqd_execute " , false , {
name = " Disable |T2000857:0|t Inscrutable Quantum Device Execute " ,
desc = " If checked, the default Unholy priority will not try to use Inscrutable Quantum Device solely because your enemy is in execute range. " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterPack ( " Unholy " , 20220911 , [ [ Hekili : T3tAZnUnY ( BXvQOrY2r2u2YzY8S8wjJZB3KnBMPIN94tMMIIsI7qjQWd74uU0V9x34IaGaGqYEMnVQ2ARSJmjoA0OV7gG3gC7hU9Mzrvj3 ( ZJoD0Ot ) MGGHbbNE ( 5 b3Et1JBsU9MnrXFmAb8J1rRG ) ) ) ( 6 L5zpIp ( XS8Ozy3lZRlIHxTSQAt5Bo5KfPvlRNomoF1jLPRQZIQsZxhxenVc ) 74 tU9MP1Pzv ) W6BNAEUHHCtsm80XJGrnD2SeAttkJV9MRtIQwU9U ) 660 flR2EhfE2 ( J3KSPkz10KIT3feC827WrC7pU9hF7YO1lskFZ2F8R2E33o7FxxwTkzDv527QY3E31RV ( KRtMNMLCczCHhFDDc9D5RHX9U4861W0ufvSib71dlHxfvaTj53aGSkzgT1zP3dpllF9c4nRZRxSK ( 8 vrvviq1pkpjSmPQEZ27 ( IGbdjWZ1PLrtZWH4MO4I05PXPrzBV79rXWuonjoQUeg0zjrqlwSmVoRehWpcpBEomML4ZV ( VwshS ) Ezko5 ) taGXbCtY6z4ehnnnlTknb7Aoc3ZZs ( T0P4ARFemnNFemqvWMm0G01LvKjlF ( 27 gF0G ) hy4F ) nBVBDsDvbczr4qVkc67MIK7tqmtz ( kyqxN ) vfjXWFLwvt2VvbQ ) 3 eyGli ) 8 MQIuCfKUg2pE33d ) aMRhZR3E3YO7jJu1ssdtYkz7eZYzZ8d5RFfmL53NuehTb ) ByTIWDPkGsNB6wpbGxdq8RMJumHfjRIG15RWT21ZsrynkJroeTzdIxqqBAEj8qsxk50c9ZsWh ( 2 RHzocwq4ZHowevUKTH ( TaT6meyHfbI8FxD10IKOpI0evfpsNfGky7DiDqzvbqVGT773KolzvAmdR9ljzr ) gGQ ( W27UhEefpLLViLGHxhJOpymksYi7RiualM5rtrMneakIsNfs2GyJ4hE31V7nBVd2jaAlK2b2gswVa5K2uKht27ryd2Cw ) re ( iZ2WBVjlTSQe5Yf0VWF8ZeXgjRjeV3 ( D3Edq8I7VritkWgf ( rc35W5KnOKzHc2NRMaG6JXzjHjRHfmc9p9eHeVREnM2YdqEXmyHnCADrzfqPewcK1LdzqdTvSMmJWA38QE0n960QHpKI7qy3xuGtNA ) BWFdJMndg8FdrdKbq ) vm6PT3D5eu0dT ) uQNEmcOqT2aiZyKUd ( 31 pgoB9SBRaHDwrPWs ( ( i4xWBgUjppdb6I61PXHBYFaPEG5PpmN1ZNpCwuXhdbkY1LGeIveoXHiPgc41RtGhUizikqbPCQcJZtZuqpiGDV0MdcWNrxt6VcEZOT3nOzXq3eXHexpNTVRNMbmHXvGd35AdhVnZ5swcljcwuPfj4KMMqiSgsK2jwAeAI8Sz5pSEy0M84OSh3uMiTTcTcgucnlJGeP ) bqA8lpibuhMNRl2J5c3WhwLUcgR8WZjJEZkLs4XxMMN0V2YKsG5WsQggQE5I0n039pxMSMRUeew9q0MsM ( YzmLDGYzqcpQknnduPKHTJi ) NmGu60YteI2Lvj9quAfr3ar9NUY7xr1Epugh0NiXaKBH9RCizv6TihnbvdielU3bVIttPG8pZnYVc ( Fe5RerAULVAIbDCZwtCw0deQHLrWuv6wWIbo6lyRXoXnN5CoTZ83HWBQOmpM ) 9 tjYzKDrjneoGdwl0wDknXJzmWgBJbzAkKTeQSYA043Wz55RaQfIzHmLGU1im4pOkf0fzQiSId ( e12XOboxswdbNU9UJ6ezFiF3T79Ll505DIhj6YjBepevScF9kWqaa5zwjLUu6UxEJg7daFfX8NUbyZqLUyCfnXDmZDpNuKKUDoc712llPuePjVuEnXsuZQHAwHqd ) MxuRpaXZlas8cOrGyAtcOlb ) vVriGh8iodqgrBGDBHgwbydkXmiruUpQIHWTcHQKnXvHNnMJHBBHPGuXfWW5SwepJW4C6WVEmN3XiZUB9i99KiXMiT ) OkTYUwSNHWz ) mVvXMQFyEJfvOEpIjqOfusMuvco0edEZnLygcXvULjeVfxKL8v8EZOIXWvuwjmUAydmbaAi93HytacBWSY4kKGxA5RP8LQMdBFinirSPXLrYAmVketoThhiWdg7YsmKwHAoqhgQDUeLxB5xcGGstebq7SK4OhBGKJOQMmi ( 7 QowddE5mY ) LYhNX ( G0S7FInxfCb6DTn7CxUti61Edrk8AFG4Ac ( FjZNNqKxGH ( b8YidfJ9WYuKlRmDvPAiNogLEHE8GHPBMu8XIGNfdWrrAo29e0vicRjOZddCZYOsPGEHmUfjX5RwbmgdnyS ( S8kDCHrdI9bZDUIQn3 ( D0PaEjj4wemA2giugm1aV ( Yc4lIwTjADftraG1whlfiNdjuh6gf207LpswZr8woqy3 ) gqx6cGqj ) ryHqXPcvlk6kAlraz2P2d1HKhMgDWIXhdZNhc72HZIwTozMj ) lQjbWoCAgzji3agiX5GLfmQ2jzjsCv6uBHV0jJFipe22ua6uLVLySHiG4CG2h29gs2xcNhLiOayBqBwMevKVkFTStxcPX2nOG7R3ZjaFdCyY6aJXMZUHaDZJ5L0x ( U2ihYPC6WOaxnRE1MqEWt6uiaXd1UGWWMmDWu ( ( vAmR2SgBGlhiskD4ASDhf7uT ( zUNuN65y2z2M0OFxyYdzBId8rp6vEqxOI17IZxIWh ( 37 HbJydhx6dAKhhBAhPB39vpjZ9yN2ezTthu9ZwAv5w66 ( 9 DH0WoiK9oQlQPDzxmyKDSp6titSzPBvXcib432KfTEnXYHEwuWIt7fTuGysoKA6v6q7vJ5XgCxRXq3oshIA2qucYIj9pMBOQbC3G ( 5 GMrrYDA08kssqHLl1Qo0KlmbEOvxyRe2PXsQ4gcY8xEpXupmwXSyhtZy3BV ( vLEBclfLR7Z5vt4evMCuDS7nsHHs699qsiTOYBf0hyuvyiBZ ( ) Evxt45NIQeDQb0k1ig3DWw4PPa ) vgiakC6Ja3aywNMc7 ) Zqw2tle9NtwVeHduKrCErr9g1 ( 1 fT8i ( 2 JyH ) XK7rPa55 ) Eck0qXc2rVw1EJP5anDeZ37vt9uHVoA3xCo6iHIYeIRfkUzhOJ8TBRQN4D3Rw7k97E1Afr3ZdL3ipPqdwJznsWyz4CWfKF ) rT0e7xt7YaWoql2vlBMtFpiTDda21pBDFPlM6RAIDYlpVDxSPsbxUCjqaGXoaMY61pKxuT8r3wqyfNlYBvtOIQsJ ) ibJyks4nGeA1X5kKb2al7H1wWiSPUSQgJeaFlwHIv0S7ZX6YcAxzEgbMKX0hWix0Bu4Y6vrRZtNrYas6AyUXzjmDUeaO3jLgUAvYSuSKVUrsV ) 8 SKYLKcetUTlYYNgLHnKyC0gsyrMg1KFuE33Kt ) 3 g8c30brunJslPwbR1QZKBLmyO0kB1EGc9kzWvSCLhy ) 6 vGFUHIGoOHOB7RS0Ane5HV9MZDfj1Npu4diCMjjr5BWmFuHB ) s5ZGwcrdRcgUmQmemdlejM48isVUHHUUGWfV9UVeeepMerzILhQM2993hLvdepLKOQXgOYgbdhJre ) ( usjyn9rCKgU9oAa9EaT3lEzEjwDc4VPQYWsiR4JmZcXjUmbDJcj ) lkzrBpPKhBVLuJgZ5vuw8ScEyczwtUGeoPijzv ) M0IdbUsSYfszXDF7DyO9tRWkxaxBjIWbIrbefpqkHVIKQ6cO ) yDnsRko ( toD44HersSaVZWjHbHLpUoUHgHl0c ) fmnH4OC7nqVzpGYL1sqV0 ( RPrsFwhXMvd0cJCtlmYhAbNGUUaBJKMhOq ( 1 cIScTKio5EP05Q5e92G9oS59x9NWTZtd4bR8qAUVhsJZP2StNzrRe6Ku3j4d1vTGqJ8ENO3M9dcd8ccdKGqD28sqKJb2CghYusv5UAtubr1kjw68vdTmHipI1pDzcK31SMenNcVOBpBkYX2J1D5FjIcdLIILCv ( S05PitPc7 ( sqWj ( WnfP5a1gVqpfDJVioMk85HeSYNydEZqsQQjSCyBw28QKcFd9xRZXSmqWDyioBw7LgLeWHOUKfeiyNgzYqdg7K1rrKg7oTHZZGuztWFCDrbTUJP1snbNr2
end