-- PriestShadow.lua
-- June 2018
local addon , ns = ...
local Hekili = _G [ addon ]
local class = Hekili.Class
local state = Hekili.State
local FindUnitBuffByID , FindUnitDebuffByID = ns.FindUnitBuffByID , ns.FindUnitDebuffByID
local PTR = ns.PTR
-- Conduits
-- [x] dissonant_echoes
-- [-] haunting_apparitions
-- [x] mind_devourer
-- [x] rabid_shadows
-- Covenant
-- [-] courageous_ascension
-- [x] shattered_perceptions
-- [x] festering_transfusion
-- [x] fae_fermata
-- Endurance
-- [x] charitable_soul
-- [x] lights_inspiration
-- [x] translucent_image
-- Finesse
-- [x] clear_mind
-- [x] mental_recovery
-- [-] move_with_grace
-- [x] power_unto_others
if UnitClassBase ( " player " ) == " PRIEST " then
local spec = Hekili : NewSpecialization ( 258 , true )
spec : RegisterResource ( Enum.PowerType . Insanity , {
mind_flay = {
channel = " mind_flay " ,
last = function ( )
local app = state.buff . casting.applied
local t = state.query_time
return app + floor ( ( t - app ) / class.auras . mind_flay.tick_time ) * class.auras . mind_flay.tick_time
end ,
interval = function ( ) return class.auras . mind_flay.tick_time end ,
value = function ( ) return ( state.talent . fortress_of_the_mind.enabled and 1.2 or 1 ) * 3 end ,
} ,
mind_sear = {
channel = " mind_sear " ,
last = function ( )
local app = state.buff . casting.applied
local t = state.query_time
return app + floor ( ( t - app ) / class.auras . mind_sear.tick_time ) * class.auras . mind_sear.tick_time
end ,
interval = function ( ) return class.auras . mind_sear.tick_time end ,
value = function ( ) return state.active_enemies end ,
} ,
void_torrent = {
channel = " void_torrent " ,
last = function ( )
local app = state.buff . casting.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = function ( ) return class.abilities . void_torrent.tick_time end ,
value = 15 ,
} ,
mindbender = {
aura = " mindbender " ,
last = function ( )
local app = state.buff . mindbender.expires - 15
local t = state.query_time
return app + floor ( ( t - app ) / ( 1.5 * state.haste ) ) * ( 1.5 * state.haste )
end ,
interval = function ( ) return 1.5 * state.haste * ( state.conduit . rabid_shadows.enabled and 0.85 or 1 ) end ,
value = function ( ) return ( state.buff . surrender_to_madness.up and 12 or 6 ) end ,
} ,
shadowfiend = {
aura = " shadowfiend " ,
last = function ( )
local app = state.buff . shadowfiend.expires - 15
local t = state.query_time
return app + floor ( ( t - app ) / ( 1.5 * state.haste ) ) * ( 1.5 * state.haste )
end ,
interval = function ( ) return 1.5 * state.haste * ( state.conduit . rabid_shadows.enabled and 0.85 or 1 ) end ,
value = function ( ) return ( state.buff . surrender_to_madness.up and 6 or 3 ) end ,
} ,
death_and_madness = {
aura = " death_and_madness " ,
last = function ( )
local app = state.buff . death_and_madness.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = 1 ,
value = 10 ,
}
} )
spec : RegisterResource ( Enum.PowerType . Mana )
-- Talents
spec : RegisterTalents ( {
fortress_of_the_mind = 22328 , -- 193195
death_and_madness = 22136 , -- 321291
unfurling_darkness = 22314 , -- 341273
body_and_soul = 22315 , -- 64129
sanlayn = 23374 , -- 199855
intangibility = 21976 , -- 288733
twist_of_fate = 23125 , -- 109142
misery = 23126 , -- 238558
searing_nightmare = 23127 , -- 341385
last_word = 23137 , -- 263716
mind_bomb = 23375 , -- 205369
psychic_horror = 21752 , -- 64044
auspicious_spirits = 22310 , -- 155271
psychic_link = 22311 , -- 199484
shadow_crash = 21755 , -- 205385
damnation = 21718 , -- 341374
mindbender = 21719 , -- 200174
void_torrent = 21720 , -- 263165
ancient_madness = 21637 , -- 341240
hungering_void = 21978 , -- 345218
-- legacy_of_the_void = 21978, -- 193225
surrender_to_madness = 21979 , -- 319952
} )
-- PvP Talents
spec : RegisterPvpTalents ( {
driven_to_madness = 106 , -- 199259
greater_fade = 3753 , -- 213602
improved_mass_dispel = 5380 , -- 341167
megalomania = 5446 , -- 357701
mind_trauma = 113 , -- 199445
psyfiend = 763 , -- 211522
thoughtsteal = 5381 , -- 316262
void_origins = 739 , -- 228630
void_shield = 102 , -- 280749
void_shift = 128 , -- 108968
void_volley = 5447 , -- 357711
} )
spec : RegisterTotem ( " mindbender " , 136214 )
spec : RegisterTotem ( " shadowfiend " , 136199 )
local thought_harvester_consumed = 0
local unfurling_darkness_triggered = 0
local swp_applied = 0
spec : RegisterHook ( " COMBAT_LOG_EVENT_UNFILTERED " , function ( event , _ , subtype , _ , sourceGUID , sourceName , _ , _ , destGUID , destName , destFlags , _ , spellID , spellName )
if sourceGUID == GUID then
if subtype == " SPELL_AURA_REMOVED " then
if spellID == 288343 then
thought_harvester_consumed = GetTime ( )
elseif spellID == 341207 then
Hekili : ForceUpdate ( subtype , true )
end
elseif subtype == " SPELL_AURA_APPLIED " then
if spellID == 341273 then
unfurling_darkness_triggered = GetTime ( )
elseif spellID == 341207 then
Hekili : ForceUpdate ( subtype , true )
end
end
end
end )
local ExpireVoidform = setfenv ( function ( )
applyBuff ( " shadowform " )
if Hekili.ActiveDebug then Hekili : Debug ( " Voidform expired, Shadowform applied. Did it stick? %s. " , buff.voidform . up and " Yes " or " No " ) end
end , state )
spec : RegisterHook ( " reset_precast " , function ( )
if buff.voidform . up or time > 0 then
applyBuff ( " shadowform " )
end
if unfurling_darkness_triggered > 0 and now - unfurling_darkness_triggered < 15 then
applyBuff ( " unfurling_darkness_icd " , now - unfurling_darkness_triggered )
end
if pet.mindbender . active then
applyBuff ( " mindbender " , pet.mindbender . remains )
buff.mindbender . applied = action.mindbender . lastCast
buff.mindbender . duration = 15
buff.mindbender . expires = action.mindbender . lastCast + 15
elseif pet.shadowfiend . active then
applyBuff ( " shadowfiend " , pet.shadowfiend . remains )
buff.shadowfiend . applied = action.shadowfiend . lastCast
buff.shadowfiend . duration = 15
buff.shadowfiend . expires = action.shadowfiend . lastCast + 15
end
if talent.mindbender . enabled then
cooldown.fiend = cooldown.mindbender
pet.fiend = pet.mindbender
else
cooldown.fiend = cooldown.shadowfiend
pet.fiend = pet.mindbender
end
if buff.voidform . up then
state : QueueAuraExpiration ( " voidform " , ExpireVoidform , buff.voidform . expires )
end
if IsActiveSpell ( 356532 ) then
applyBuff ( " direct_mask " , class.abilities . fae_guardians.lastCast + 20 - now )
end
-- If we are channeling Mind Sear, see if it started with Thought Harvester.
local _ , _ , _ , start , finish , _ , _ , spellID = UnitChannelInfo ( " player " )
if spellID == 48045 then
start = start / 1000
finish = finish / 1000
if start - thought_harvester_consumed < 0.1 then
applyBuff ( " mind_sear_th " , finish - start )
buff.mind_sear_th . applied = start
buff.mind_sear_th . expires = finish
else
removeBuff ( " mind_sear_th " )
end
else
removeBuff ( " mind_sear_th " )
end
if settings.pad_void_bolt and cooldown.void_bolt . remains > 0 then
reduceCooldown ( " void_bolt " , latency * 2 )
end
if settings.pad_ascended_blast and cooldown.ascended_blast . remains > 0 then
reduceCooldown ( " ascended_blast " , latency * 2 )
end
end )
spec : RegisterHook ( " pregain " , function ( amount , resource , overcap )
if amount > 0 and resource == " insanity " and state.buff . memory_of_lucid_dreams.up then
amount = amount * 2
end
return amount , resource , overcap
end )
spec : RegisterStateTable ( " priest " , {
self_power_infusion = true
} )
-- Auras
spec : RegisterAuras ( {
body_and_soul = {
id = 65081 ,
duration = 3 ,
type = " Magic " ,
max_stack = 1 ,
} ,
dark_thought = {
id = 341207 ,
duration = 10 ,
max_stack = 1 ,
copy = " dark_thoughts "
} ,
death_and_madness = {
id = 321973 ,
duration = 4 ,
max_stack = 1 ,
} ,
desperate_prayer = {
id = 19236 ,
duration = 10 ,
max_stack = 1 ,
} ,
devouring_plague = {
id = 335467 ,
duration = 6 ,
type = " Disease " ,
max_stack = 1 ,
} ,
dispersion = {
id = 47585 ,
duration = 6 ,
max_stack = 1 ,
} ,
fade = {
id = 586 ,
duration = 10 ,
max_stack = 1 ,
} ,
focused_will = {
id = 45242 ,
duration = 8 ,
max_stack = 2 ,
} ,
levitate = {
id = 111759 ,
duration = 600 ,
type = " Magic " ,
max_stack = 1 ,
} ,
mind_bomb = {
id = 226943 ,
duration = 6 ,
type = " Magic " ,
max_stack = 1 ,
} ,
mind_flay = {
id = 15407 ,
duration = function ( ) return 4.5 * haste end ,
max_stack = 1 ,
tick_time = function ( ) return 0.75 * haste end ,
} ,
mind_sear = {
id = 48045 ,
duration = function ( ) return 4.5 * haste end ,
max_stack = 1 ,
tick_time = function ( ) return 0.75 * haste end ,
} ,
mind_sear_th = {
duration = function ( ) return 4.5 * haste end ,
max_stack = 1 ,
} ,
mind_vision = {
id = 2096 ,
duration = 60 ,
max_stack = 1 ,
} ,
mindbender = {
duration = 15 ,
max_stack = 1 ,
} ,
power_infusion = {
id = 10060 ,
duration = 20 ,
max_stack = 1
} ,
power_word_fortitude = {
id = 21562 ,
duration = 3600 ,
type = " Magic " ,
max_stack = 1 ,
shared = " player " , -- use anyone's buff on the player, not just player's.
} ,
power_word_shield = {
id = 17 ,
duration = 15 ,
type = " Magic " ,
max_stack = 1 ,
} ,
psychic_horror = {
id = 64044 ,
duration = 4 ,
type = " Magic " ,
max_stack = 1 ,
} ,
psychic_scream = {
id = 8122 ,
duration = 8 ,
type = " Magic " ,
max_stack = 1 ,
} ,
shackle_undead = {
id = 9484 ,
duration = 50 ,
type = " Magic " ,
max_stack = 1 ,
} ,
shadow_crash_debuff = {
id = 342385 ,
duration = 15 ,
max_stack = 2
} ,
shadow_mend = {
id = 342992 ,
duration = 15 ,
max_stack = 2
} ,
shadow_word_pain = {
id = 589 ,
duration = 16 ,
type = " Magic " ,
max_stack = 1 ,
tick_time = function ( ) return 2 * haste end ,
} ,
shadowfiend = {
duration = 15 ,
max_stack = 1
} ,
shadowform = {
id = 232698 ,
duration = 3600 ,
max_stack = 1 ,
} ,
shadowy_apparitions = {
id = 78203 ,
} ,
silence = {
id = 15487 ,
duration = 4 ,
type = " Magic " ,
max_stack = 1 ,
} ,
surrender_to_madness = {
id = 319952 ,
duration = 25 ,
max_stack = 1 ,
} ,
twist_of_fate = {
id = 123254 ,
duration = 8 ,
max_stack = 1 ,
} ,
unfurling_darkness = {
id = 341282 ,
duration = 15 ,
max_stack = 1 ,
} ,
unfurling_darkness_icd = {
id = 341291 ,
duration = 15 ,
max_stack = 1
} ,
vampiric_embrace = {
id = 15286 ,
duration = 15 ,
max_stack = 1 ,
} ,
vampiric_touch = {
id = 34914 ,
duration = 21 ,
type = " Magic " ,
max_stack = 1 ,
tick_time = function ( ) return 3 * haste end ,
} ,
void_bolt = {
id = 228266 ,
} ,
void_torrent = {
id = 263165 ,
duration = 3 ,
max_stack = 1 ,
tick_time = 1 ,
} ,
voidform = {
id = 194249 ,
duration = 15 , -- function () return talent.legacy_of_the_void.enabled and 3600 or 15 end,
max_stack = 1 ,
--[[ generate = function( t )
local name , _ , count , _ , duration , expires , caster , _ , _ , spellID , _ , _ , _ , _ , timeMod , v1 , v2 , v3 = FindUnitBuffByID ( " player " , 194249 )
if name then
t.name = name
t.count = max ( 1 , count )
t.applied = max ( action.void_eruption . lastCast , now )
t.expires = t.applied + 3600
t.duration = 3600
t.caster = " player "
t.timeMod = 1
t.v1 = v1
t.v2 = v2
t.v3 = v3
t.unit = " player "
return
end
t.name = nil
t.count = 0
t.expires = 0
t.applied = 0
t.duration = 3600
t.caster = " nobody "
t.timeMod = 1
t.v1 = 0
t.v2 = 0
t.v3 = 0
t.unit = " player "
end ,
meta = {
up = function ( )
return buff.voidform . applied > 0 and buff.voidform . drop_time > query_time
end ,
drop_time = function ( )
if buff.voidform . applied == 0 then return 0 end
local app = buff.voidform . applied
app = app + floor ( query_time - app )
local drain = 6 + ( 0.8 * buff.voidform . stacks )
local amt = insanity.current
while ( amt > 0 ) do
amt = amt - drain
drain = drain + 0.8
app = app + 1
end
return app
end ,
stacks = function ( )
return buff.voidform . applied > 0 and ( buff.voidform . count + floor ( offset + delay ) ) or 0
end ,
remains = function ( )
return max ( 0 , buff.voidform . drop_time - query_time )
end ,
} , ] ]
} ,
weakened_soul = {
id = 6788 ,
duration = function ( ) return 7.5 * haste end ,
max_stack = 1 ,
} ,
-- Azerite Powers
chorus_of_insanity = {
id = 279572 ,
duration = 120 ,
max_stack = 120 ,
} ,
death_denied = {
id = 287723 ,
duration = 10 ,
max_stack = 1 ,
} ,
depth_of_the_shadows = {
id = 275544 ,
duration = 12 ,
max_stack = 30
} ,
--[[ harvested_thoughts = {
id = 273321 ,
duration = 15 ,
max_stack = 1 ,
} , ] ]
searing_dialogue = {
id = 288371 ,
duration = 1 ,
max_stack = 1
} ,
thought_harvester = {
id = 288343 ,
duration = 20 ,
max_stack = 1 ,
copy = " harvested_thoughts " -- SimC uses this name (carryover from Legion?)
} ,
-- Legendaries (Shadowlands)
mind_devourer = {
id = 338333 ,
duration = 15 ,
max_stack = 1 ,
} ,
measured_contemplation = {
id = 341824 ,
duration = 3600 ,
max_stack = 4
} ,
-- Conduits
dissonant_echoes = {
id = 343144 ,
duration = 10 ,
max_stack = 1 ,
} ,
} )
--[[ spec:RegisterHook( "advance_end", function ()
if buff.voidform . up and talent.legacy_of_the_void . enabled and insanity.current == 0 then
insanity.regen = 0
removeBuff ( " voidform " )
applyBuff ( " shadowform " )
end
end ) ] ]
-- Tier 28
spec : RegisterGear ( " tier28 " , 188881 , 188880 , 188879 , 188878 , 188875 )
spec : RegisterSetBonuses ( " tier28_2pc " , 364424 , " tier28_4pc " , 363469 )
-- 2-Set - Darkened Mind - Casting Devouring Plague has a 40% chance to grant Dark Thought. Casting Searing Nightmare has a 25% chance to grant Dark Thought.
-- 4-Set - Living Shadow - Consuming a Dark Thought causes your shadow to animate after a moment, dealing [(34%20.9% of Spell power) * 6] Shadow damage over 6 sec to all enemies within 10 yards of your target.
spec : RegisterAura ( " living_shadow " , {
id = 363574 ,
duration = 8 ,
max_stack = 1 ,
copy = " your_shadow " ,
meta = {
summonTime = function ( t ) return t.applied end ,
} ,
} )
rawset ( state.pet , " your_shadow " , state.buff . living_shadow )
spec : RegisterGear ( " tier21 " , 152154 , 152155 , 152156 , 152157 , 152158 , 152159 )
spec : RegisterGear ( " tier20 " , 147163 , 147164 , 147165 , 147166 , 147167 , 147168 )
spec : RegisterAura ( " empty_mind " , {
id = 247226 ,
duration = 12 ,
max_stack = 10 ,
} )
spec : RegisterGear ( " tier19 " , 138310 , 138313 , 138316 , 138319 , 138322 , 138370 )
spec : RegisterGear ( " anunds_seared_shackles " , 132409 )
spec : RegisterAura ( " anunds_last_breath " , {
id = 215210 ,
duration = 15 ,
max_stack = 50 ,
} )
spec : RegisterGear ( " heart_of_the_void " , 151814 )
spec : RegisterGear ( " mangazas_madness " , 132864 )
spec : RegisterGear ( " mother_shahrazs_seduction " , 132437 )
spec : RegisterGear ( " soul_of_the_high_priest " , 151646 )
spec : RegisterGear ( " the_twins_painful_touch " , 133973 )
spec : RegisterGear ( " zenkaram_iridis_anadem " , 133971 )
spec : RegisterGear ( " zeks_exterminatus " , 144438 )
spec : RegisterAura ( " zeks_exterminatus " , {
id = 236546 ,
duration = 15 ,
max_stack = 1 ,
} )
spec : RegisterStateExpr ( " current_insanity_drain " , function ( )
return buff.voidform . up and ( 6 + ( 0.8 * buff.voidform . stacks ) ) or 0
end )
-- Abilities
spec : RegisterAbilities ( {
damnation = {
id = 341374 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
talent = " damnation " ,
startsCombat = true ,
texture = 236295 ,
-- TODO: Set up cycle.
-- cycle = function ()
handler = function ( )
applyDebuff ( " target " , " shadow_word_pain " )
applyDebuff ( " target " , " vampiric_touch " )
applyDebuff ( " target " , " devouring_plague " )
if talent.unfurling_darkness . enabled and debuff.unfurling_darkness_icd . down then
applyBuff ( " unfurling_darkness " )
applyDebuff ( " player " , " unfurling_darkness_icd " )
end
end ,
} ,
desperate_prayer = {
id = 19236 ,
cast = 0 ,
cooldown = 90 ,
gcd = " spell " ,
toggle = " defensives " ,
startsCombat = true ,
texture = 237550 ,
handler = function ( )
health.max = health.max * 1.25
gain ( 0.8 * health.max , " health " )
if conduit.lights_inspiration . enabled then applyBuff ( " lights_inspiration " ) end
end ,
auras = {
-- Conduit
lights_inspiration = {
id = 337749 ,
duration = 5 ,
max_stack = 1
}
}
} ,
devouring_plague = {
id = 335467 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return buff.mind_devourer . up and 0 or 50 end ,
spendType = " insanity " ,
startsCombat = true ,
texture = 252997 ,
cycle = " devouring_plague " ,
handler = function ( )
removeBuff ( " mind_devourer " )
applyDebuff ( " target " , " devouring_plague " )
end ,
} ,
dispel_magic = {
id = 528 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 0.016 * ( 1 + conduit.clear_mind . mod * 0.01 ) end ,
spendType = " mana " ,
startsCombat = false ,
texture = 136066 ,
usable = function ( ) return buff.dispellable_magic . up end ,
handler = function ( )
removeBuff ( " dispellable_magic " )
if time > 0 then gain ( 6 , " insanity " ) end
end ,
} ,
dispersion = {
id = 47585 ,
cast = 0 ,
cooldown = function ( ) return talent.intangibility . enabled and 90 or 120 end ,
gcd = " spell " ,
toggle = " defensives " ,
defensive = true ,
startsCombat = false ,
texture = 237563 ,
handler = function ( )
applyBuff ( " dispersion " )
setCooldown ( " global_cooldown " , 6 )
end ,
} ,
fade = {
id = 586 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
startsCombat = true ,
texture = 135994 ,
handler = function ( )
applyBuff ( " fade " )
if conduit.translucent_image . enabled then applyBuff ( " translucent_image " ) end
end ,
auras = {
-- Conduit
translucent_image = {
id = 337661 ,
duration = 5 ,
max_stack = 1
}
}
} ,
leap_of_faith = {
id = 73325 ,
cast = 0 ,
cooldown = 90 ,
charges = function ( ) return legendary.vault_of_heavens . enabled and 2 or nil end ,
recharge = function ( ) return legendary.vault_of_heavens . enabled and 90 or nil end ,
gcd = " spell " ,
spend = 0.03 ,
spendType = " mana " ,
startsCombat = false ,
texture = 463835 ,
handler = function ( )
if azerite.death_denied . enabled then applyBuff ( " death_denied " ) end
if legendary.vault_of_heavens . enabled then setDistance ( 5 ) end
end ,
} ,
levitate = {
id = 1706 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 135928 ,
handler = function ( )
applyBuff ( " levitate " )
end ,
} ,
mass_dispel = {
id = 32375 ,
cast = function ( ) return pvptalent.improved_mass_dispel . enabled and 0.5 or 1.5 end ,
cooldown = function ( ) return pvptalent.improved_mass_dispel . enabled and 30 or 45 end ,
gcd = " spell " ,
spend = function ( ) return 0.08 * ( 1 + ( conduit.clear_mind . mod * 0.01 ) ) end ,
spendType = " mana " ,
startsCombat = true ,
texture = 135739 ,
usable = function ( ) return buff.dispellable_magic . up or debuff.dispellable_magic . up end ,
handler = function ( )
removeBuff ( " dispellable_magic " )
removeDebuff ( " player " , " dispellable_magic " )
if time > 0 then gain ( 6 , " insanity " ) end
end ,
} ,
mind_blast = {
id = 8092 ,
cast = function ( ) return buff.dark_thought . up and 0 or ( 1.5 * haste ) end ,
charges = function ( ) return 1 + ( buff.voidform . up and 1 or 0 ) + ( buff.dark_thought . up and 1 or 0 ) end ,
cooldown = function ( )
return 7.5 * haste
end ,
recharge = function ( )
return 7.5 * haste
end ,
gcd = " spell " ,
castableWhileCasting = function ( )
if buff.dark_thought . up and ( buff.casting . v1 == class.abilities . mind_flay.id or buff.casting . v1 == class.abilities . mind_sear.id or buff.casting . v1 == class.abilities . void_torrent.id ) then return true end
return nil
end ,
velocity = 15 ,
spend = function ( ) return ( talent.fortress_of_the_mind . enabled and 1.2 or 1 ) * ( - 8 - buff.empty_mind . stack ) * ( buff.surrender_to_madness . up and 2 or 1 ) end ,
spendType = " insanity " ,
startsCombat = true ,
texture = 136224 ,
handler = function ( )
if buff.dark_thought . up and set_bonus.tier28_4pc > 0 then
applyBuff ( " living_shadow " )
end
removeBuff ( " dark_thought " )
removeBuff ( " harvested_thoughts " )
removeBuff ( " empty_mind " )
end ,
} ,
mind_bomb = {
id = 205369 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
startsCombat = true ,
texture = 136173 ,
talent = " mind_bomb " ,
handler = function ( )
applyDebuff ( " target " , " mind_bomb " )
end ,
} ,
mind_flay = {
id = 15407 ,
cast = 4.5 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0 ,
spendType = " insanity " ,
channeled = true ,
breakable = true ,
breakchannel = function ( )
removeDebuff ( " target " , " mind_flay " )
end ,
prechannel = true ,
tick_time = function ( ) return class.auras . mind_flay.tick_time end ,
startsCombat = true ,
texture = 136208 ,
aura = " mind_flay " ,
nobuff = " boon_of_the_ascended " ,
bind = " ascended_blast " ,
start = function ( )
applyDebuff ( " target " , " mind_flay " )
channelSpell ( " mind_flay " )
forecastResources ( " insanity " )
end ,
} ,
mind_sear = {
id = 48045 ,
cast = 4.5 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0 ,
spendType = " insanity " ,
channeled = true ,
breakable = true ,
breakchannel = function ( )
removeDebuff ( " target " , " mind_sear " )
removeBuff ( " mind_sear_th " )
end ,
prechannel = true ,
tick_time = function ( ) return class.auras . mind_flay.tick_time end ,
startsCombat = true ,
texture = 237565 ,
aura = " mind_sear " ,
start = function ( )
applyDebuff ( " target " , " mind_sear " )
channelSpell ( " mind_sear " )
if azerite.searing_dialogue . enabled then applyDebuff ( " target " , " searing_dialogue " ) end
if buff.thought_harvester . up then
removeBuff ( " thought_harvester " )
applyBuff ( " mind_sear_th " )
end
forecastResources ( " insanity " )
end ,
} ,
-- SimulationCraft module: Mindbender and Shadowfiend are interchangeable.
mindbender = {
id = function ( ) return talent.mindbender . enabled and 200174 or 34433 end ,
cast = 0 ,
cooldown = function ( ) return ( essence.vision_of_perfection . enabled and 0.87 or 1 ) * ( talent.mindbender . enabled and 60 or 180 ) end ,
gcd = " spell " ,
toggle = function ( ) return not talent.mindbender . enabled and " cooldowns " or nil end ,
startsCombat = true ,
texture = function ( ) return talent.mindbender . enabled and 136214 or 136199 end ,
-- talent = "mindbender",
handler = function ( )
summonPet ( talent.mindbender . enabled and " mindbender " or " shadowfiend " , 15 )
applyBuff ( talent.mindbender . enabled and " mindbender " or " shadowfiend " )
end ,
copy = { " shadowfiend " , 200174 , 34433 , 132603 }
} ,
power_infusion = {
id = 10060 ,
cast = 0 ,
cooldown = function ( ) return 120 - ( conduit.power_unto_others . mod and group and conduit.power_unto_others . mod or 0 ) end ,
gcd = " off " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 135939 ,
indicator = function ( ) return group and legendary.twins_of_the_sun_priestess . enabled and " cycle " or nil end ,
handler = function ( )
applyBuff ( " power_infusion " )
stat.haste = stat.haste + 0.25
end ,
} ,
power_word_fortitude = {
id = 21562 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.04 ,
spendType = " mana " ,
startsCombat = false ,
texture = 135987 ,
usable = function ( ) return buff.power_word_fortitude . down end ,
handler = function ( )
applyBuff ( " power_word_fortitude " )
end ,
} ,
power_word_shield = {
id = 17 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
nodebuff = " weakened_soul " ,
startsCombat = false ,
texture = 135940 ,
handler = function ( )
applyBuff ( " power_word_shield " )
applyDebuff ( " weakened_soul " )
if talent.body_and_soul . enabled then applyBuff ( " body_and_soul " ) end
if time > 0 then gain ( 6 , " insanity " ) end
-- charitable_soul triggered by casting on others; not modeled.
end ,
auras = {
-- Conduit
charitable_soul = {
id = 337716 ,
duration = 10 ,
max_stack = 1
}
}
} ,
psychic_horror = {
id = 64044 ,
cast = 0 ,
cooldown = 45 ,
gcd = " spell " ,
startsCombat = true ,
texture = 237568 ,
talent = " psychic_horror " ,
handler = function ( )
applyDebuff ( " target " , " psychic_horror " )
end ,
} ,
psychic_scream = {
id = 8122 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136184 ,
notalent = " mind_bomb " ,
handler = function ( )
applyDebuff ( " target " , " psychic_scream " )
end ,
auras = {
-- Conduit
mental_recovery = {
id = 337956 ,
duration = 5 ,
max_stack = 1
}
}
} ,
purify_disease = {
id = 213634 ,
cast = 0 ,
cooldown = 8 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 135935 ,
usable = function ( ) return debuff.dispellable_disease . up end ,
handler = function ( )
removeBuff ( " dispellable_disease " )
if time > 0 then gain ( 6 , " insanity " ) end
end ,
} ,
searing_nightmare = {
id = 341385 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
castableWhileCasting = true ,
talent = " searing_nightmare " ,
spend = 30 ,
spendType = " insanity " ,
startsCombat = true ,
texture = 1022950 ,
channeling = " mind_sear " ,
handler = function ( )
applyDebuff ( " target " , " shadow_word_pain " )
active_dot.shadow_word_pain = max ( active_enemies , active_dot.shadow_word_pain )
end ,
} ,
shackle_undead = {
id = 9484 ,
cast = 1.275 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.01 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136091 ,
handler = function ( )
applyDebuff ( " target " , " shackle_undead " )
end ,
} ,
shadow_crash = {
id = 205385 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = - 20 ,
spendType = " insanity " ,
startsCombat = true ,
texture = 136201 ,
} ,
shadow_mend = {
id = 186263 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 0.04 ,
spendType = " mana " ,
startsCombat = true ,
texture = 136202 ,
handler = function ( )
removeBuff ( " depth_of_the_shadows " )
if level > 55 then addStack ( " shadow_mend " , nil , 1 ) end
removeBuff ( " measured_contemplation " )
end ,
} ,
shadow_word_death = {
id = 32379 ,
cast = 0 ,
cooldown = 20 ,
hasteCD = true ,
gcd = " spell " ,
startsCombat = true ,
texture = 136149 ,
usable = function ( )
if settings.sw_death_protection == 0 then return true end
return health.percent >= settings.sw_death_protection , " health percent [ " .. health.percent .. " ] is below user setting [ " .. settings.sw_death_protection .. " ] "
end ,
handler = function ( )
removeBuff ( " zeks_exterminatus " )
if legendary.painbreaker_psalm . enabled then
local power = 0
if debuff.shadow_word_pain . up then
power = power + 15 * min ( debuff.shadow_word_pain . remains , 8 ) / 8
if debuff.shadow_word_pain . remains < 8 then removeDebuff ( " shadow_word_pain " )
else debuff.shadow_word_pain . expires = debuff.shadow_word_pain . expires - 8 end
end
if debuff.vampiric_touch . up then
power = power + 15 * min ( debuff.vampiric_touch . remains , 8 ) / 8
if debuff.vampiric_touch . remains <= 8 then removeDebuff ( " vampiric_touch " )
else debuff.vampiric_touch . expires = debuff.vampiric_touch . expires - 8 end
end
if power > 0 then gain ( power , " insanity " ) end
end
if legendary.shadowflame_prism . enabled then
if pet.fiend . active then pet.fiend . expires = pet.fiend . expires + 1 end
end
end ,
} ,
shadow_word_pain = {
id = 589 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = - 4 ,
spendType = " insanity " ,
startsCombat = true ,
texture = 136207 ,
cycle = " shadow_word_pain " ,
handler = function ( )
applyDebuff ( " target " , " shadow_word_pain " )
end ,
} ,
shadowform = {
id = 232698 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = false ,
texture = 136200 ,
essential = true ,
nobuff = function ( ) return buff.voidform . up and " voidform " or " shadowform " end ,
handler = function ( )
applyBuff ( " shadowform " )
end ,
} ,
silence = {
id = 15487 ,
cast = 0 ,
cooldown = 45 ,
gcd = " off " ,
startsCombat = true ,
texture = 458230 ,
toggle = " interrupts " ,
interrupt = true ,
debuff = " casting " ,
readyTime = state.timeToInterrupt ,
handler = function ( )
interrupt ( )
applyDebuff ( " target " , " silence " )
end ,
} ,
surrender_to_madness = {
id = 319952 ,
cast = 0 ,
cooldown = 90 ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 254090 ,
usable = function ( ) return target.time_to_die < settings.stm_timer , format ( " time_to_die %.2f > %.2f " , target.time_to_die , settings.stm_timer ) end ,
handler = function ( )
applyBuff ( " voidform " )
applyBuff ( " surrender_to_madness " )
applyDebuff ( " target " , " surrender_to_madness " )
end ,
} ,
vampiric_embrace = {
id = 15286 ,
cast = 0 ,
cooldown = 120 ,
gcd = " spell " ,
startsCombat = false ,
texture = 136230 ,
handler = function ( )
applyBuff ( " vampiric_embrace " )
if time > 0 then gain ( 6 , " insanity " ) end
end ,
} ,
vampiric_touch = {
id = 34914 ,
cast = function ( ) return buff.unfurling_darkness . up and 0 or 1.5 * haste end ,
cooldown = 0 ,
gcd = " spell " ,
spend = - 5 ,
spendType = " insanity " ,
startsCombat = true ,
texture = 135978 ,
cycle = function ( ) return talent.misery . enabled and " shadow_word_pain " or " vampiric_touch " end ,
handler = function ( )
applyDebuff ( " target " , " vampiric_touch " )
if talent.misery . enabled then
applyDebuff ( " target " , " shadow_word_pain " )
end
if talent.unfurling_darkness . enabled then
if buff.unfurling_darkness . up then
removeBuff ( " unfurling_darkness " )
elseif debuff.unfurling_darkness_icd . down then
applyBuff ( " unfurling_darkness " )
applyDebuff ( " player " , " unfurling_darkness_icd " )
end
end
-- Thought Harvester is a 20% chance to proc, consumed by Mind Sear.
-- if azerite.thought_harvester.enabled then applyBuff( "harvested_thoughts" ) end
end ,
} ,
void_bolt = {
id = 205448 ,
known = 228260 ,
cast = 0 ,
cooldown = function ( )
return haste * 4.5
end ,
gcd = " spell " ,
spend = function ( )
return buff.surrender_to_madness . up and - 24 or - 12
end ,
spendType = " insanity " ,
startsCombat = true ,
texture = 1035040 ,
velocity = 40 ,
buff = function ( ) return buff.dissonant_echoes . up and " dissonant_echoes " or " voidform " end ,
bind = " void_eruption " ,
--[[ cooldown_ready = function ()
return buff.dissonant_echoes . up or buff.voidform . up
end , ] ]
handler = function ( )
removeBuff ( " dissonant_echoes " )
if debuff.shadow_word_pain . up then debuff.shadow_word_pain . expires = debuff.shadow_word_pain . expires + 3 end
if debuff.vampiric_touch . up then debuff.vampiric_touch . expires = debuff.vampiric_touch . expires + 3 end
if talent.legacy_of_the_void . enabled and debuff.devouring_plague . up then debuff.devouring_plague . expires = query_time + debuff.devouring_plague . duration end
removeBuff ( " anunds_last_breath " )
end ,
impact = function ( )
if talent.hungering_void . enabled then
if debuff.hungering_void . up then buff.voidform . expires = buff.voidform . expires + 1 end
applyDebuff ( " target " , " hungering_void " , 6 )
end
end ,
copy = 343355 ,
auras = {
hungering_void = {
id = 345219 ,
duration = 6 ,
max_stack = 1
}
}
} ,
void_eruption = {
id = 228260 ,
cast = function ( )
if pvptalent.void_origins . enabled then return 0 end
return haste * 1.5
end ,
cooldown = 90 ,
gcd = " spell " ,
startsCombat = true ,
texture = 1386548 ,
nobuff = function ( ) return buff.dissonant_echoes . up and " dissonant_echoes " or " voidform " end ,
bind = " void_bolt " ,
toggle = " cooldowns " ,
cooldown_ready = function ( )
return cooldown.void_eruption . remains == 0 and buff.voidform . down
end ,
handler = function ( )
applyBuff ( " voidform " )
end ,
} ,
void_torrent = {
id = 263165 ,
cast = 3 ,
channeled = true ,
fixedCast = true ,
cooldown = 30 ,
gcd = " spell " ,
spend = - 15 ,
spendType = " insanity " ,
startsCombat = true ,
texture = 1386551 ,
aura = " void_torrent " ,
talent = " void_torrent " ,
tick_time = function ( )
return class.auras . void_torrent.tick_time
end ,
breakchannel = function ( )
removeDebuff ( " target " , " void_torrent " )
end ,
start = function ( )
applyDebuff ( " target " , " void_torrent " )
applyDebuff ( " target " , " devouring_plague " )
if debuff.vampiric_touch . up then applyDebuff ( " target " , " vampiric_touch " ) end -- This should refresh/pandemic properly.
if debuff.shadow_word_pain . up then applyDebuff ( " target " , " shadow_word_pain " ) end -- This should refresh/pandemic properly.
end ,
tick = function ( )
if debuff.vampiric_touch . up then applyDebuff ( " target " , " vampiric_touch " ) end -- This should refresh/pandemic properly.
if debuff.shadow_word_pain . up then applyDebuff ( " target " , " shadow_word_pain " ) end -- This should refresh/pandemic properly.
end ,
} ,
-- Priest - Kyrian - 325013 - boon_of_the_ascended (Boon of the Ascended)
boon_of_the_ascended = {
id = 325013 ,
cast = 1.5 ,
cooldown = 180 ,
gcd = " spell " ,
startsCombat = false ,
texture = 3565449 ,
toggle = " essences " ,
handler = function ( )
applyBuff ( " boon_of_the_ascended " )
end ,
auras = {
boon_of_the_ascended = {
id = 325013 ,
duration = 10 ,
max_stack = 20 -- ???
}
}
} ,
ascended_nova = {
id = 325020 ,
known = 325013 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " , -- actually 1s and not 1.5s...
startsCombat = true ,
texture = 3528287 ,
buff = " boon_of_the_ascended " ,
bind = " boon_of_the_ascended " ,
handler = function ( )
addStack ( " boon_of_the_ascended " , nil , active_enemies )
end
} ,
ascended_blast = {
id = 325283 ,
known = 15407 ,
cast = 0 ,
cooldown = 3 ,
hasteCD = true ,
gcd = " totem " , -- actually 1s and not 1.5s...
startsCombat = true ,
texture = 3528286 ,
buff = " boon_of_the_ascended " ,
bind = " mind_flay " ,
handler = function ( )
addStack ( " boon_of_the_ascended " , nil , 5 )
if state.spec . shadow then gain ( 6 , " insanity " ) end
end ,
} ,
-- Priest - Necrolord - 324724 - unholy_nova (Unholy Nova)
unholy_nova = {
id = 324724 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
spend = 0.05 ,
spendType = " mana " ,
startsCombat = true ,
texture = 3578229 ,
toggle = " essences " ,
handler = function ( )
applyDebuff ( " target " , " unholy_transfusion " )
active_dot.unholy_transfusion = active_enemies
if legendary.pallid_command . enabled then applyBuff ( " pallid_command " ) end
if soulbind.kevins_oozeling . enabled then applyBuff ( " kevins_oozeling " ) end
end ,
range = 15 ,
auras = {
unholy_transfusion = {
id = 324724 ,
duration = function ( ) return conduit.festering_transfusion . enabled and 17 or 15 end ,
max_stack = 1 ,
} ,
pallid_command = {
id = 356418 ,
duration = 20 ,
max_stack = 1
}
}
} ,
-- Priest - Night Fae - 327661 - fae_guardians (Fae Guardians)
fae_guardians = {
id = 327661 ,
cast = 0 ,
cooldown = 90 ,
gcd = " spell " ,
spend = 0.02 ,
spendType = " mana " ,
toggle = " essences " ,
nobuff = " direct_mask " ,
handler = function ( )
applyBuff ( " fae_guardians " )
summonPet ( " wrathful_faerie " )
applyDebuff ( " target " , " wrathful_faerie " )
summonPet ( " guardian_faerie " )
applyBuff ( " guardian_faerie " )
summonPet ( " benevolent_faerie " )
applyBuff ( " benevolent_faerie " )
if legendary.bwonsamdis_pact . enabled then
applyBuff ( " direct_mask " )
applyDebuff ( " target " , " haunted_mask " )
end
-- TODO: Check totem/guardian API re: faeries.
end ,
bind = " direct_mask " ,
auras = {
fae_guardians = {
id = 327661 ,
duration = 20 ,
max_stack = 1 ,
} ,
wrathful_faerie = {
id = 342132 ,
duration = 20 ,
max_stack = 1 ,
} ,
wrathful_faerie_fermata = {
id = 345452 ,
duration = function ( ) return conduit.fae_fermata . enabled and ( conduit.fae_fermata . mod * 0.001 ) or 3 end ,
max_stack = 1
} ,
guardian_faerie = {
id = 327694 ,
duration = 20 ,
max_stack = 1 ,
} ,
guardian_faerie_fermata = {
id = 345451 ,
duration = function ( ) return conduit.fae_fermata . enabled and ( conduit.fae_fermata . mod * 0.001 ) or 3 end ,
max_stack = 1
} ,
benevolent_faerie = {
id = 327710 ,
duration = 20 ,
max_stack = 1 ,
} ,
benevolent_faerie_fermata = {
id = 345453 ,
duration = function ( ) return conduit.fae_fermata . enabled and ( conduit.fae_fermata . mod * 0.001 ) or 3 end ,
max_stack = 1
} ,
haunted_mask = {
id = 356968 ,
duration = 20 ,
max_stack = 1 ,
} ,
direct_mask = {
duration = 20 ,
max_stack = 1 ,
}
}
} ,
direct_mask = {
id = 356532 ,
cast = 0 ,
cooldown = 0 ,
gcd = " off " ,
buff = " direct_mask " ,
bind = " fae_guardians " ,
handler = function ( )
applyDebuff ( " target " , " haunted_mask " )
end ,
} ,
-- Priest - Venthyr - 323673 - mindgames (Mindgames)
mindgames = {
id = 323673 ,
cast = 1.5 ,
cooldown = 45 ,
gcd = " spell " ,
spend = 0.002 ,
spendType = " mana " ,
toggle = " essences " ,
handler = function ( )
applyDebuff ( " target " , " mindgames " )
end ,
auras = {
mindgames = {
id = 323673 ,
duration = function ( ) return ( conduit.shattered_perceptions . enabled and 7 or 5 ) + ( legendary.shadow_word_manipulation . enabled and 3 or 0 ) end ,
max_stack = 1 ,
} ,
shadow_word_manipulation = {
id = 357028 ,
duration = 10 ,
max_stack = 1 ,
} ,
} ,
} ,
} )
spec : RegisterOptions ( {
enabled = true ,
aoe = 3 ,
nameplates = false ,
nameplateRange = 8 ,
damage = true ,
damageExpiration = 6 ,
potion = " potion_of_spectral_intellect " ,
package = " Shadow " ,
} )
spec : RegisterSetting ( " pad_void_bolt " , true , {
name = " Pad |T1035040:0|t Void Bolt Cooldown " ,
desc = " If checked, the addon will treat |T1035040:0|t Void Bolt's cooldown as slightly shorter, to help ensure that it is recommended as frequently as possible during Voidform. " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterSetting ( " pad_ascended_blast " , true , {
name = " Pad |T3528286:0|t Ascended Blast Cooldown " ,
desc = " If checked, the addon will treat |T3528286:0|t Ascended Blast's cooldown as slightly shorter, to help ensure that it is recommended as frequently as possible during Boon of the Ascended. " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterSetting ( " sw_death_protection " , 50 , {
name = " |T136149:0|t Shadow Word: Death Health Threshold " ,
desc = " If set above 0, the addon will not recommend |T136149:0|t Shadow Word: Death while your health percentage is below this threshold. This setting can help keep you from killing yourself. " ,
type = " range " ,
min = 0 ,
max = 100 ,
step = 0.1 ,
width = " full " ,
} )
spec : RegisterSetting ( " ignore_solvent " , true , {
name = " Ignore Volatile Solvent for Void Eruption " ,
desc = " If disabled, when you have the Volatile Solvent conduit enabled, the addon will not use Void Eruption unless you currently have a Volatile Solvent buff applied (from casting Fleshcraft). " ,
type = " toggle " ,
width = " full " ,
} )
spec : RegisterSetting ( " stm_timer " , 20 , {
name = " |T254090:0|t Surrender to Madness Time " ,
desc = " |T254090:0|t Surrender to Madness will kill you if your targeted enemy does not die within 25 seconds. The addon is able to estimate time-to-die, but " ..
" these values are estimates and fight circumstances can change quickly. This setting allows you to reserve a little extra time, so you are less likely to cause your own death. \n \n " ..
" Custom priorities can reference |cFFFFD100settings.stm_timer|r for their decisions. " ,
type = " range " ,
min = 5 ,
max = 25 ,
step = 0.1 ,
width = " full " ,
} )
spec : RegisterPack ( " Shadow " , 20220301 , [ [ dm1CYcqivipssvUekviztssFcLQmkjLoLKIvHsL8kiIzrjClQGk7sIFjjyyubogLOLbr1ZKuvMgejxtfkBdLk13GGQXPcvDoQGQwNKO8ouQqQ5HsP7rfTpik ) tfQihusuTqukEiePMOKixKkOSriO8ruQOgPkuHtkjKwjvOxIsfQzQcv6MsQQ2je4NscXqvb1rvHkQLIsf5POYuLu5Qqq2QkiFvsOglkvG9Qk ) vLgmPdl1IPspgKjtXLj2mO ( mQA0uQtlSAuQGETkWSPQBdPDR43knCuCCQGSCGNd10fDDv12vrFhLmEuQQZdHwpkviMpL0 ( r ( z5RUhNPt5HaK7aKJChuFoa5fh4ahWUpgs94sezKhhtdDqZlpUPrLhhNDBwwpoMgr ) 2 MxDpo8 ( bqYJZotgCLvHkWhP93TaTOvahOFFNXoqGgoRaoqHQWJZ9h ( SIop3hNPt5HaK7aKJChuFoa5fh4ahWUpgYFC9pTxWJJlqr6hNDymY8CFCgbd944SBZYI0ddcbNKJ1FdGSj1slif5oa5iNCKCePT7HxWvg5OdhP1Xs6di9qByiTUfaKjjLLTmKMnGxssH2 ) KysBGqk8cGetHC0HJ0ddKugdPMnXK2aH0pdPSSLH0Sb8sIjTbcPq ( flKMlPgeJH3csXlPPDNKo ) demPnqifNH3tkqGwuuzmIP848boXV6ECODmV6EiWYxDpozAxVyES5XbbIuar ) 4 C ) WWf3DN7cFtB52yizmIP8zEC4eeq5dbw ( 4 AOm25Xb1E ) THYyNRpW5JZh48onQ84C3DE5dbi ) v3JtM21lMhBECqGifq0pUJinBaVKLaFz8nIc4X1qzSZJZeygXFrB ( a6LpeuFV6ECY0UEX8yZJRHYyNh35gMRa ( mzSZJZiyiqWKXopoeclKEOnmK6WaFMm2H0DifAxVzznKYSRpgEs7KuV04KuKFmsJb3tKisATlGuKYbKcVaszJFxdPompmP7q6YiJaQHu3Fs6SjPbmPiUFszfEpP7PaGAgsJb3tKisAmKEiewH06Vpqif ) bcPC2TzzbhYyQq9hJXvgJaiThdP1FmgszJVXjPbM0DifAxVzznK6kWlqi9qomsdys5SBZYc23OcPbMuXH ( bdJykKwr5NfiKYSRpgEsbcobbug7GjnGj9JJHNuo72SSG9nQq6HbbgL0EmKYgzmcG0at6 ( ZYJdcePaI ( XD2GOD9sHzx ) fEbxidM0QKwlPXG7jsejfzojf5hJuKqATKA5XiLDrATKcAiP4631CfpmPvjnduHu2sA95asRH0Ai1QvszKSahYyknugNcPvjf8hbEb8sbB3MLfSVrLldiWOfXH ( bdJyiTkPhrk0UEZYAkOXyUU ( gNLpdPvj9isH21BwwtbB3ML1L1cmxJ0PD5ZqAnKwL0AjngCprIiPS1jPh ) Xi1QvsZ2ltwWsdIH ) obVDI2aPit76fdPvj9Sbr76LcwAqm83j4Tt0gixOFUWWKwdPvj9isH21BwwtboKXu ( mKwL0Aj9isX737gJPCU ( odVCXR ) uMSit76fdPwTsQ7hgUCU ( odVCXR ) uMS8zi1QvsXsMXWJlb ) Sa5Ix ) PmjP18YhcqQxDpozAxVyES5X1qzSZJdB3ML1L1cmxMoMhNrWqGGjJDEC1FFGqk ( desrC ) KY8ts ) mKYvXv2HjTY5Q8dt6oKM2cPzd4LK0aM0kg0Pn83tkcRfqiKg4H9ssBOmofszzldPWbVDgdpPw6WvFKMnGxsC5XbbIuar ) 4 C ) WWf4wU8 ) gyIEWLpdPvj9isnI7hgUWc0Pn83FHBbes5ZqAvsXmI3FZgWljUGgJ5ILgqkBjfPE5dbh7v3JtM21lMhBECnug784qJXCXsdECqGifq0pUS9YKfS0Gy4VtWBNOnqkY0UEXqAvsXmI3FZgWljUGgJ5ILgqkYi9Sbr76LcAmMlwAWf6NlmmPvj9isnBwW2TzzDzTaZLPJPKb0bXWtAvspIuOD9ML1uGdzmLpdPvjfZiE ) nBaVK4cAmMlwAaPiZjPi1JdcriVCZgWlj ( HalF5dbS7xDpozAxVyES5X1qzSZJdQ9 ( BdLXoxFGZhNpW5DAu5XbzWV8Hae ( RUhNmTRxmp284AOm25XHgJ5ILg84GqeYl3Sb8sIFiWYhheisbe9JlBVmzblnig ( 7e82 jAdKImTRxmKwLumJ493Sb8sIlOXyUyPbKImspBq0UEPGgJ5ILgCH ( 5 cdtAvspIuZMfSDBwwxwlWCz6ykzaDqm8KwL0JifAxVzznf4qgt5Z84mcgcemzSZJ74i4Tj9WGybrIiP1Fmgs5KgqAdLXoKMlPabgiyBsR0whMuwrAtkwAqm83j4Tt0giV8HGJ ) v3JtM21lMhBECnug784mn60zSZJdcriVCZgWlj ( HalFCqGifq0pUAj1Sz5SrzcqaDZ9dzxacmqW2TRxi1QvsnBwW2TzzDzTaZLPJPaeyGGTBxVqQvRKwlPhrQ7hgUGgJ5AKZ9deq5ZqAvsJb3tKiskBj9yoG0AiTgsRsATK6 ( HHlMgCWnT3pVDwWzdDaPSLu3pmCX0GdUP9 ( 5 TZcAZ ( xC2qhqQvRKEePyjVU78XLmeaYp ( lYzGiTMhNrWqGGjJDEChgiWcG0Cj9JfsRuJoDg7qALZv5hM0aMuUkUYomPlG0dvhPbM0zts ) mKUasrC ) Kc1ZSjPqnojTjDwaA7jTsY5 ( bXWt6H9n ) xiT2yG8Ftm8Kw ) XyiTsY5 ( bcGugWcHRH0EmKI4 ( jLv49KoBskuZqALAWbKwN9 ( 5 TtmP4SHoatAat6hhdpP1H8JNuKZavE5dbo8V6ECY0UEX8yZJRHYyNhh2UnlRlRfyUgPt7hNrWqGGjJDECiewiLZUnllsR4fyiTssN2KgWK ( XXWtkNDBwwW ( gvi9WGaJsApgsDLXiaszfEpPc7ZeaHuZhedpPPTq6iSFskpKP84GarkGOFCmswGdzmLgkJtH0QKc ( JaVaEPGTBZYc23OYLbey0I4q ) GHrmKwLugjlWHmMcqq7yWKYwNKYdziTkPygX7Vzd4LexqJXCXsdiLTojfH ) YhcS0bV6ECY0UEX8yZJRHYyNhhAmMRRVX5JZiyiqWKXopUk3ZQret6hlKIgJX134etAatkuZWigs7XqQ9F4fqm8KEUHH0at6NH0EmK ( XXWtkNDBwwW ( gvi9WGaJsApgsDLXiasdmPFMcPKw5gtKXoT3JOfKc14Ku0ymU ( gNKgWKI4 ( jL1 ( 9 gsDfs ) t76fsZLuEjjnTfsbbCsQlIKYQJmgEsBs5HmLhheisbe9JRwsH21BwwtbngZ1134Saz3aEbtkYi1ssRsATKAe3pmCX ( p8cig ( 75 gMYNHuRwj9isZ2ltwS ) dVaIH ) EUHPit76fdP1qQvRKYizboKXuacAhdMu26KuOgN3mqfsrcP8qgsRH0QKYizboKXuAOmofsRsk4pc8c4Lc2UnllyFJkxgqGrlId9dggXqAvszKSahYykabTJbtkYifQX5nduH0QKIzeV ) MnGxsCbngZflnGu26KueoPwTsQ7hgUyAWb30E ) 82 z5ZqAvsD ) WWLZnmWlaT8ziTkPhrk0UEZYAkNByUURplFgsRsATKEePG ) iWlGxky72SSG9nQCzabgTio0pyyedPwTs6rKYizboKXuAOmofsRH0QKIL86UZhxYqai ) 4 Vifd0lFiWslF194KPD9I5XMhxdLXopUZnmx31NpoJGHabtg784qiSq6H2WqkBwFsANKAh82cGugqSGirKuwrAt6XXF4fqm8KEOnmK ( zinxsrksZgWlj2csxaPBAlasZ2ltIjDhs5QR84GarkGOFCXG7jsejLToj94pgPvjnBVmzX ( p8cig ( 75 gMImTRxmKwL0S9YKfS0Gy4VtWBNOnqkY0UEXqAvsXmI3FZgWljUGgJ5ILgqkBDsk7MuRwjTwsRL0S9YKf7 ) WlGy4VNBykY0UEXqAvspI0S9YKfS0Gy4VtWBNOnqkY0UEXqAnKA1kPygX7Vzd4LexqJXCXsdi1jPwsAnV8Halr ( RUhNmTRxmp284AOm25XzKZ9dIH ) Y4B ( V84mcgcemzSZJJJrGI2t
end