-- RogueSubtlety.lua
-- June 2018
local addon , ns = ...
local Hekili = _G [ addon ]
local class = Hekili.Class
local state = Hekili.State
local insert = table.insert
local PTR = ns.PTR
-- Conduits
-- [x] deeper_daggers
-- [x] perforated_veins
-- [-] planned_execution
-- [-] stiletto_staccato
if UnitClassBase ( " player " ) == " ROGUE " then
local spec = Hekili : NewSpecialization ( 261 )
spec : RegisterResource ( Enum.PowerType . Energy , {
shadow_techniques = {
last = function ( ) return state.query_time end ,
interval = function ( ) return state.time_to_sht [ 5 ] end ,
value = 8 ,
stop = function ( ) return state.time_to_sht [ 5 ] == 0 or state.time_to_sht [ 5 ] == 3600 end ,
} ,
vendetta_regen = {
aura = " vendetta_regen " ,
last = function ( )
local app = state.buff . vendetta_regen.applied
local t = state.query_time
return app + floor ( t - app )
end ,
interval = 1 ,
value = 20 ,
} ,
} )
spec : RegisterResource ( Enum.PowerType . ComboPoints , {
shadow_techniques = {
last = function ( ) return state.query_time end ,
interval = function ( ) return state.time_to_sht [ 5 ] end ,
value = 1 ,
stop = function ( ) return state.time_to_sht [ 5 ] == 0 or state.time_to_sht [ 5 ] == 3600 end ,
} ,
shuriken_tornado = {
aura = " shuriken_tornado " ,
last = function ( )
local app = state.buff . shuriken_tornado.applied
local t = state.query_time
return app + floor ( t - app )
end ,
stop = function ( x ) return state.buff . shuriken_tornado.remains == 0 end ,
interval = 0.95 ,
value = function ( ) return state.active_enemies + ( state.buff . shadow_blades.up and 1 or 0 ) end ,
} ,
} )
-- Talents
spec : RegisterTalents ( {
weaponmaster = 19233 , -- 193537
premeditation = 19234 , -- 343160
gloomblade = 19235 , -- 200758
nightstalker = 22331 , -- 14062
subterfuge = 22332 , -- 108208
shadow_focus = 22333 , -- 108209
vigor = 19239 , -- 14983
deeper_stratagem = 19240 , -- 193531
marked_for_death = 19241 , -- 137619
soothing_darkness = 22128 , -- 200759
cheat_death = 22122 , -- 31230
elusiveness = 22123 , -- 79008
shot_in_the_dark = 23078 , -- 257505
night_terrors = 23036 , -- 277953
prey_on_the_weak = 22115 , -- 131511
dark_shadow = 22335 , -- 245687
alacrity = 19249 , -- 193539
enveloping_shadows = 22336 , -- 238104
master_of_shadows = 22132 , -- 196976
secret_technique = 23183 , -- 280719
shuriken_tornado = 21188 , -- 277925
} )
-- PvP Talents
spec : RegisterPvpTalents ( {
dagger_in_the_dark = 846 , -- 198675
death_from_above = 3462 , -- 269513
dismantle = 5406 , -- 207777
distracting_mirage = 5411 , -- 354661
maneuverability = 3447 , -- 197000
shadowy_duel = 153 , -- 207736
silhouette = 856 , -- 197899
smoke_bomb = 1209 , -- 359053
thick_as_thieves = 5409 , -- 221622
thiefs_bargain = 146 , -- 354825
veil_of_midnight = 136 , -- 198952
} )
-- Auras
spec : RegisterAuras ( {
alacrity = {
id = 193538 ,
duration = 20 ,
max_stack = 5 ,
} ,
blind = {
id = 2094 ,
duration = 60 ,
max_stack = 1 ,
} ,
cheap_shot = {
id = 1833 ,
duration = 4 ,
max_stack = 1 ,
} ,
cloak_of_shadows = {
id = 31224 ,
duration = 5 ,
max_stack = 1 ,
} ,
death_from_above = {
id = 152150 ,
duration = 1 ,
} ,
crimson_vial = {
id = 185311 ,
duration = 4 ,
max_stack = 1 ,
} ,
crippling_poison = {
id = 3408 ,
duration = 3600 ,
max_stack = 1 ,
} ,
crippling_poison_dot = {
id = 3409 ,
duration = 12 ,
max_stack = 1 ,
} ,
evasion = {
id = 5277 ,
duration = 10 ,
max_stack = 1 ,
} ,
feint = {
id = 1966 ,
duration = 5 ,
max_stack = 1 ,
} ,
find_weakness = {
id = 316220 ,
duration = 18 ,
max_stack = 1 ,
} ,
fleet_footed = {
id = 31209 ,
} ,
instant_poison = {
id = 315584 ,
duration = 3600 ,
max_stack = 1 ,
} ,
kidney_shot = {
id = 408 ,
duration = 6 ,
max_stack = 1 ,
} ,
marked_for_death = {
id = 137619 ,
duration = 60 ,
max_stack = 1 ,
} ,
master_of_shadows = {
id = 196980 ,
duration = 3 ,
max_stack = 1 ,
} ,
premeditation = {
id = 343173 ,
duration = 3600 ,
max_stack = 1 ,
} ,
prey_on_the_weak = {
id = 255909 ,
duration = 6 ,
max_stack = 1 ,
} ,
relentless_strikes = {
id = 58423 ,
} ,
--[[ Share Assassination implementation to avoid errors.
rupture = {
id = 1943 ,
duration = function ( ) return talent.deeper_stratagem . enabled and 28 or 24 end ,
max_stack = 1 ,
} , ] ]
shadow_blades = {
id = 121471 ,
duration = 20 ,
max_stack = 1 ,
} ,
shadow_dance = {
id = 185422 ,
duration = function ( ) return talent.subterfuge . enabled and 9 or 8 end ,
max_stack = 1 ,
} ,
shadows_grasp = {
id = 206760 ,
duration = 8 ,
type = " Magic " ,
max_stack = 1 ,
} ,
shadow_techniques = {
id = 196912 ,
} ,
shadowstep = {
id = 36554 ,
duration = 2 ,
max_stack = 1 ,
} ,
shot_in_the_dark = {
id = 257506 ,
duration = 3600 ,
max_stack = 1 ,
} ,
shroud_of_concealment = {
id = 114018 ,
duration = 15 ,
max_stack = 1 ,
} ,
shuriken_tornado = {
id = 277925 ,
duration = 4 ,
max_stack = 1 ,
} ,
slice_and_dice = {
id = 315496 ,
duration = 10 ,
max_stack = 1 ,
} ,
sprint = {
id = 2983 ,
duration = 8 ,
max_stack = 1 ,
} ,
stealth = {
id = function ( ) return talent.subterfuge . enabled and 115191 or 1784 end ,
duration = 3600 ,
max_stack = 1 ,
copy = { 115191 , 1784 }
} ,
subterfuge = {
id = 115192 ,
duration = 3 ,
max_stack = 1 ,
} ,
symbols_of_death = {
id = 212283 ,
duration = 10 ,
max_stack = 1 ,
} ,
symbols_of_death_crit = {
id = 227151 ,
duration = 10 ,
max_stack = 1 ,
copy = " symbols_of_death_autocrit "
} ,
vanish = {
id = 11327 ,
duration = 3 ,
max_stack = 1 ,
} ,
wound_poison = {
id = 8679 ,
duration = 3600 ,
max_stack = 1 ,
} ,
lethal_poison = {
alias = { " instant_poison " , " wound_poison " , " slaughter_poison " } ,
aliasMode = " first " ,
aliasType = " buff " ,
duration = 3600
} ,
nonlethal_poison = {
alias = { " crippling_poison " , " numbing_poison " } ,
aliasMode = " first " ,
aliasType = " buff " ,
duration = 3600
} ,
-- Azerite Powers
blade_in_the_shadows = {
id = 279754 ,
duration = 60 ,
max_stack = 10 ,
} ,
nights_vengeance = {
id = 273424 ,
duration = 8 ,
max_stack = 1 ,
} ,
perforate = {
id = 277720 ,
duration = 12 ,
max_stack = 1
} ,
replicating_shadows = {
id = 286131 ,
duration = 1 ,
max_stack = 50
} ,
the_first_dance = {
id = 278981 ,
duration = function ( ) return buff.shadow_dance . duration end ,
max_stack = 1 ,
} ,
-- Legendaries (Shadowlands)
deathly_shadows = {
id = 341202 ,
duration = 15 ,
max_stack = 1 ,
} ,
master_assassins_mark = {
id = 340094 ,
duration = 4 ,
max_stack = 1
} ,
the_rotten = {
id = 341134 ,
duration = 3 ,
max_stack = 1
}
} )
local true_stealth_change = 0
local emu_stealth_change = 0
spec : RegisterEvent ( " UPDATE_STEALTH " , function ( )
true_stealth_change = GetTime ( )
end )
local stealth = {
rogue = { " stealth " , " vanish " , " shadow_dance " , " subterfuge " } ,
mantle = { " stealth " , " vanish " } ,
sepsis = { " sepsis_buff " } ,
all = { " stealth " , " vanish " , " shadow_dance " , " subterfuge " , " shadowmeld " , " sepsis_buff " }
}
spec : RegisterStateTable ( " stealthed " , setmetatable ( { } , {
__index = function ( t , k )
if k == " rogue " then
return buff.stealth . up or buff.vanish . up or buff.shadow_dance . up or buff.subterfuge . up
elseif k == " rogue_remains " then
return max ( buff.stealth . remains , buff.vanish . remains , buff.shadow_dance . remains , buff.subterfuge . remains )
elseif k == " mantle " then
return buff.stealth . up or buff.vanish . up
elseif k == " mantle_remains " then
return max ( buff.stealth . remains , buff.vanish . remains )
elseif k == " sepsis " then
return buff.sepsis_buff . up
elseif k == " sepsis_remains " then
return buff.sepsis_buff . remains
elseif k == " all " then
return buff.stealth . up or buff.vanish . up or buff.shadow_dance . up or buff.subterfuge . up or buff.shadowmeld . up or buff.sepsis_buff . up
elseif k == " remains " or k == " all_remains " then
return max ( buff.stealth . remains , buff.vanish . remains , buff.shadow_dance . remains , buff.subterfuge . remains , buff.shadowmeld . remains , buff.sepsis_buff . remains )
end
return false
end
} ) )
local last_mh = 0
local last_oh = 0
local last_shadow_techniques = 0
local swings_since_sht = 0
spec : RegisterEvent ( " COMBAT_LOG_EVENT_UNFILTERED " , function ( )
local event , _ , subtype , _ , sourceGUID , sourceName , _ , _ , destGUID , destName , destFlags , _ , spellID , spellName , _ , amount , interrupt , a , b , c , d , offhand , multistrike = CombatLogGetCurrentEventInfo ( )
if sourceGUID == state.GUID then
if subtype == " SPELL_ENERGIZE " and spellID == 196911 then
last_shadow_techniques = GetTime ( )
swings_since_sht = 0
end
if subtype : sub ( 1 , 5 ) == " SWING " and not multistrike then
if subtype == " SWING_MISSED " then
offhand = spellName
end
local now = GetTime ( )
if now > last_shadow_techniques + 3 then
swings_since_sht = swings_since_sht + 1
end
if offhand then last_mh = GetTime ( )
else last_mh = GetTime ( ) end
end
end
end )
local sht = { }
spec : RegisterStateTable ( " time_to_sht " , setmetatable ( { } , {
__index = function ( t , k )
local n = tonumber ( k )
n = n - ( n % 1 )
if not n or n > 5 then return 3600 end
if n <= swings_since_sht then return 0 end
local mh_speed = swings.mainhand_speed
local mh_next = ( swings.mainhand > now - 3 ) and ( swings.mainhand + mh_speed ) or now + ( mh_speed * 0.5 )
local oh_speed = swings.offhand_speed
local oh_next = ( swings.offhand > now - 3 ) and ( swings.offhand + oh_speed ) or now
table.wipe ( sht )
if mh_speed and mh_speed > 0 then
for i = 1 , 4 do
insert ( sht , mh_next + ( i * mh_speed ) )
end
end
if oh_speed and oh_speed > 0 then
for i = 1 , 4 do
insert ( sht , oh_next + ( i * oh_speed ) )
end
end
local i = 1
while ( sht [ i ] ) do
if sht [ i ] < last_shadow_techniques + 3 then
table.remove ( sht , i )
else
i = i + 1
end
end
if # sht > 0 and n - swings_since_sht < # sht then
table.sort ( sht )
return max ( 0 , sht [ n - swings_since_sht ] - query_time )
else
return 3600
end
end
} ) )
spec : RegisterStateExpr ( " bleeds " , function ( )
return ( debuff.garrote . up and 1 or 0 ) + ( debuff.rupture . up and 1 or 0 )
end )
spec : RegisterStateExpr ( " cp_max_spend " , function ( )
return combo_points.max
end )
-- Legendary from Legion, shows up in APL still.
spec : RegisterGear ( " cinidaria_the_symbiote " , 133976 )
spec : RegisterGear ( " denial_of_the_halfgiants " , 137100 )
local function comboSpender ( amt , resource )
if resource == " combo_points " then
if amt > 0 then
gain ( 6 * amt , " energy " )
end
if talent.alacrity . enabled and amt >= 5 then
addStack ( " alacrity " , 20 , 1 )
end
if talent.secret_technique . enabled then
cooldown.secret_technique . expires = max ( 0 , cooldown.secret_technique . expires - amt )
end
reduceCooldown ( " shadow_dance " , amt * ( talent.enveloping_shadows . enabled and 1.5 or 1 ) )
if legendary.obedience . enabled and buff.flagellation_buff . up then
reduceCooldown ( " flagellation " , amt )
end
end
end
spec : RegisterHook ( " spend " , comboSpender )
-- spec:RegisterHook( "spendResources", comboSpender )
spec : RegisterStateExpr ( " mantle_duration " , function ( )
return legendary.mark_of_the_master_assassin . enabled and 4 or 0
end )
spec : RegisterStateExpr ( " master_assassin_remains " , function ( )
if not legendary.mark_of_the_master_assassin . enabled then return 0 end
if stealthed.mantle then return cooldown.global_cooldown . remains + 4
elseif buff.master_assassins_mark . up then return buff.master_assassins_mark . remains end
return 0
end )
spec : RegisterStateExpr ( " priority_rotation " , function ( )
return settings.priority_rotation
end )
-- We need to break stealth when we start combat from an ability.
spec : RegisterHook ( " runHandler " , function ( ability )
local a = class.abilities [ ability ]
if stealthed.mantle and ( not a or a.startsCombat ) then
if talent.subterfuge . enabled and stealthed.mantle then
applyBuff ( " subterfuge " )
end
if legendary.mark_of_the_master_assassin . enabled and stealthed.mantle then
applyBuff ( " master_assassins_mark " , 4 )
end
if buff.stealth . up then
setCooldown ( " stealth " , 2 )
end
removeBuff ( " stealth " )
removeBuff ( " vanish " )
removeBuff ( " shadowmeld " )
end
end )
local ExpireSepsis = setfenv ( function ( )
applyBuff ( " sepsis_buff " )
end , state )
spec : RegisterHook ( " reset_precast " , function ( amt , resource )
if debuff.sepsis . up then
state : QueueAuraExpiration ( " sepsis " , ExpireSepsis , debuff.sepsis . expires )
end
end )
spec : RegisterCycle ( function ( )
if active_enemies == 1 then return end
if this_action == " marked_for_death " then
if active_dot.marked_for_death >= cycle_enemies then return end -- As far as we can tell, MfD is on everything we care about, so we don't cycle.
if debuff.marked_for_death . up then return " cycle " end -- If current target already has MfD, cycle.
if target.time_to_die > 3 + Hekili : GetLowestTTD ( ) and active_dot.marked_for_death == 0 then return " cycle " end -- If our target isn't lowest TTD, and we don't have to worry that the lowest TTD target is already MfD'd, cycle.
end
end )
spec : RegisterGear ( " insignia_of_ravenholdt " , 137049 )
spec : RegisterGear ( " mantle_of_the_master_assassin " , 144236 )
spec : RegisterAura ( " master_assassins_initiative " , {
id = 235027 ,
duration = 5
} )
spec : RegisterStateExpr ( " mantle_duration " , function ( )
if stealthed.mantle then return cooldown.global_cooldown . remains + buff.master_assassins_initiative . duration
elseif buff.master_assassins_initiative . up then return buff.master_assassins_initiative . remains end
return 0
end )
spec : RegisterGear ( " shadow_satyrs_walk " , 137032 )
spec : RegisterStateExpr ( " ssw_refund_offset " , function ( )
return target.distance
end )
spec : RegisterGear ( " soul_of_the_shadowblade " , 150936 )
spec : RegisterGear ( " the_dreadlords_deceit " , 137021 )
spec : RegisterAura ( " the_dreadlords_deceit " , {
id = 228224 ,
duration = 3600 ,
max_stack = 20 ,
copy = 208693
} )
spec : RegisterGear ( " the_first_of_the_dead " , 151818 )
spec : RegisterAura ( " the_first_of_the_dead " , {
id = 248210 ,
duration = 2
} )
spec : RegisterGear ( " will_of_valeera " , 137069 )
spec : RegisterAura ( " will_of_valeera " , {
id = 208403 ,
duration = 5
} )
-- Tier Sets
spec : RegisterGear ( " tier21 " , 152163 , 152165 , 152161 , 152160 , 152162 , 152164 )
spec : RegisterGear ( " tier20 " , 147172 , 147174 , 147170 , 147169 , 147171 , 147173 )
spec : RegisterGear ( " tier19 " , 138332 , 138338 , 138371 , 138326 , 138329 , 138335 )
-- Abilities
spec : RegisterAbilities ( {
backstab = {
id = 53 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 35 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 132090 ,
notalent = " gloomblade " ,
handler = function ( )
applyDebuff ( " target " , " shadows_grasp " , 8 )
if azerite.perforate . enabled and buff.perforate . up then
-- We'll assume we're attacking from behind if we've already put up Perforate once.
addStack ( " perforate " , nil , 1 )
gainChargeTime ( " shadow_blades " , 0.5 )
end
gain ( ( buff.shadow_blades . up and 2 or 1 ) + ( buff.the_rotten . up and 4 or 0 ) , " combo_points " )
removeBuff ( " the_rotten " )
removeBuff ( " symbols_of_death_crit " )
removeBuff ( " perforated_veins " )
end ,
} ,
black_powder = {
id = 319175 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 35 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 608955 ,
usable = function ( ) return combo_points.current > 0 , " requires combo_points " end ,
handler = function ( )
if talent.alacrity . enabled and combo_points.current > 4 then addStack ( " alacrity " , nil , 1 ) end
if combo_points.current == animacharged_cp then removeBuff ( " echoing_reprimand " ) end
if buff.finality_black_powder . up then removeBuff ( " finality_black_powder " )
elseif legendary.finality . enabled then applyBuff ( " finality_black_powder " ) end
spend ( min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current ) , " combo_points " )
if conduit.deeper_daggers . enabled then applyBuff ( " deeper_daggers " ) end
end ,
auras = {
finality_black_powder = {
id = 340603 ,
duration = 30 ,
max_stack = 1
}
}
} ,
blind = {
id = 2094 ,
cast = 0 ,
cooldown = function ( ) return 120 - ( talent.blinding_powder . enabled and 30 or 0 ) end ,
gcd = " spell " ,
startsCombat = true ,
texture = 136175 ,
handler = function ( )
applyDebuff ( " target " , " blind " , 60 )
end ,
} ,
cheap_shot = {
id = 1833 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( )
if buff.shot_in_the_dark . up then return 0 end
return 40 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) * ( 1 + conduit.rushed_setup . mod * 0.01 )
end ,
spendType = " energy " ,
startsCombat = true ,
texture = 132092 ,
cycle = function ( )
if talent.prey_on_the_weak . enabled then return " prey_on_the_weak " end
end ,
usable = function ( )
if boss then return false , " cheap_shot assumed unusable in boss fights " end
return stealthed.all , " not stealthed "
end ,
nodebuff = " cheap_shot " ,
handler = function ( )
applyDebuff ( " target " , " find_weakness " )
if talent.prey_on_the_weak . enabled then
applyDebuff ( " target " , " prey_on_the_weak " )
end
if talent.subterfuge . enabled then
applyBuff ( " subterfuge " )
end
applyDebuff ( " target " , " cheap_shot " )
removeBuff ( " shot_in_the_dark " )
if buff.sepsis_buff . up then removeBuff ( " sepsis_buff " ) end
gain ( buff.shadow_blades . up and 2 or 1 , " combo_points " )
removeBuff ( " symbols_of_death_crit " )
end ,
} ,
cloak_of_shadows = {
id = 31224 ,
cast = 0 ,
cooldown = 120 ,
gcd = " off " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 136177 ,
handler = function ( )
applyBuff ( " cloak_of_shadows " , 5 )
end ,
} ,
crimson_vial = {
id = 185311 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
toggle = " cooldowns " ,
spend = function ( ) return ( 20 - conduit.nimble_fingers . mod ) * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = false ,
texture = 1373904 ,
handler = function ( )
applyBuff ( " crimson_vial " , 6 )
end ,
} ,
crippling_poison = {
id = 3408 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
essential = true ,
startsCombat = false ,
texture = 132274 ,
readyTime = function ( ) return buff.nonlethal_poison . remains - 120 end ,
handler = function ( )
applyBuff ( " crippling_poison " )
end ,
} ,
distract = {
id = 1725 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
spend = function ( ) return 30 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) * ( 1 + conduit.rushed_setup . mod * 0.01 ) end ,
spendType = " energy " ,
startsCombat = false ,
texture = 132289 ,
handler = function ( )
end ,
} ,
evasion = {
id = 5277 ,
cast = 0 ,
cooldown = 120 ,
gcd = " off " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 136205 ,
handler = function ( )
applyBuff ( " evasion " , 10 )
end ,
} ,
eviscerate = {
id = 196819 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 35 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 132292 ,
usable = function ( ) return combo_points.current > 0 end ,
handler = function ( )
if talent.alacrity . enabled and combo_points.current > 4 then
addStack ( " alacrity " , 20 , 1 )
end
removeBuff ( " nights_vengeance " )
if buff.finality_eviscerate . up then removeBuff ( " finality_eviscerate " )
elseif legendary.finality . enabled then applyBuff ( " finality_eviscerate " ) end
if combo_points.current == animacharged_cp then removeBuff ( " echoing_reprimand " ) end
spend ( min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current ) , " combo_points " )
if conduit.deeper_daggers . enabled then applyBuff ( " deeper_daggers " ) end
end ,
auras = {
-- Conduit
deeper_daggers = {
id = 341550 ,
duration = 5 ,
max_stack = 1
} ,
finality_eviscerate = {
id = 340600 ,
duration = 30 ,
max_stack = 1
}
}
} ,
feint = {
id = 1966 ,
cast = 0 ,
cooldown = 15 ,
gcd = " spell " ,
spend = function ( ) return ( 35 - conduit.nimble_fingers . mod ) * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = false ,
texture = 132294 ,
handler = function ( )
applyBuff ( " feint " , 5 )
end ,
} ,
gloomblade = {
id = 200758 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 35 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
talent = " gloomblade " ,
startsCombat = true ,
texture = 1035040 ,
handler = function ( )
applyDebuff ( " target " , " shadows_grasp " , 8 )
if buff.stealth . up then
removeBuff ( " stealth " )
end
gain ( ( buff.shadow_blades . up and 2 or 1 ) + ( buff.the_rotten . up and 4 or 0 ) , " combo_points " )
removeBuff ( " the_rotten " )
removeBuff ( " symbols_of_death_crit " )
end ,
} ,
instant_poison = {
id = 315584 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
essential = true ,
startsCombat = false ,
texture = 132273 ,
readyTime = function ( ) return buff.lethal_poison . remains - 120 end ,
handler = function ( )
applyBuff ( " instant_poison " )
end ,
} ,
kick = {
id = 1766 ,
cast = 0 ,
cooldown = 15 ,
gcd = " off " ,
toggle = " interrupts " ,
interrupt = true ,
startsCombat = true ,
texture = 132219 ,
debuff = " casting " ,
readyTime = state.timeToInterrupt ,
handler = function ( )
interrupt ( )
if conduit.prepared_for_all . enabled and cooldown.cloak_of_shadows . remains > 0 then
reduceCooldown ( " cloak_of_shadows " , 2 * conduit.prepared_for_all . mod )
end
end ,
} ,
kidney_shot = {
id = 408 ,
cast = 0 ,
cooldown = 20 ,
gcd = " spell " ,
spend = function ( ) return 25 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) * ( 1 + conduit.rushed_setup . mod * 0.01 ) end ,
spendType = " energy " ,
toggle = " cooldowns " ,
startsCombat = true ,
texture = 132298 ,
usable = function ( ) return combo_points.current > 0 end ,
handler = function ( )
if talent.alacrity . enabled and combo_points.current > 4 then
addStack ( " alacrity " , 20 , 1 )
end
local combo = min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current )
applyDebuff ( " target " , " kidney_shot " , 2 + 1 * ( combo - 1 ) )
if talent.prey_on_the_weak . enabled then applyDebuff ( " target " , " prey_on_the_weak " ) end
if combo_points.current == animacharged_cp then removeBuff ( " echoing_reprimand " ) end
spend ( min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current ) , " combo_points " )
end ,
} ,
marked_for_death = {
id = 137619 ,
cast = 0 ,
cooldown = 60 ,
gcd = " off " ,
talent = " marked_for_death " ,
startsCombat = false ,
texture = 236364 ,
usable = function ( )
return combo_points.current <= settings.mfd_points , " combo_point ( " .. combo_points.current .. " ) > user preference ( " .. settings.mfd_points .. " ) "
end ,
handler = function ( )
gain ( 5 , " combo_points " )
applyDebuff ( " target " , " marked_for_death " , 60 )
end ,
} ,
numbing_poison = {
id = 5761 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
essential = true ,
startsCombat = false ,
texture = 136066 ,
readyTime = function ( ) return buff.nonlethal_poison . remains - 120 end ,
handler = function ( )
applyBuff ( " numbing_poison " )
end ,
} ,
pick_lock = {
id = 1804 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = false ,
texture = 136058 ,
handler = function ( )
end ,
} ,
pick_pocket = {
id = 921 ,
cast = 0 ,
cooldown = 0.5 ,
gcd = " spell " ,
startsCombat = false ,
texture = 133644 ,
handler = function ( )
end ,
} ,
rupture = {
id = 1943 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 25 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 132302 ,
handler = function ( )
if talent.alacrity . enabled and combo_points.current >= 5 then addStack ( " alacrity " , nil , 1 ) end
applyDebuff ( " target " , " rupture " , 4 + ( 4 * combo_points.current ) )
if buff.finality_rupture . up then removeBuff ( " finality_rupture " )
elseif legendary.finality . enabled then applyBuff ( " finality_rupture " ) end
if combo_points.current == animacharged_cp then removeBuff ( " echoing_reprimand " ) end
spend ( min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current ) , " combo_points " )
end ,
auras = {
finality_rupture = {
id = 340601 ,
duration = 30 ,
max_stack = 1
}
}
} ,
sap = {
id = 6770 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 35 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) * ( 1 + conduit.rushed_setup . mod * 0.01 ) end ,
spendType = " energy " ,
startsCombat = false ,
texture = 132310 ,
handler = function ( )
applyDebuff ( " target " , " sap " , 60 )
end ,
} ,
secret_technique = {
id = 280719 ,
cast = 0 ,
cooldown = function ( ) return 45 - min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current ) end ,
gcd = " spell " ,
spend = function ( ) return 30 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 132305 ,
usable = function ( ) return combo_points.current > 0 , " requires combo_points " end ,
handler = function ( )
if talent.alacrity . enabled and combo_points.current > 4 then addStack ( " alacrity " , nil , 1 ) end
if combo_points.current == animacharged_cp then removeBuff ( " echoing_reprimand " ) end
spend ( min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current ) , " combo_points " )
end ,
} ,
shadow_blades = {
id = 121471 ,
cast = 0 ,
cooldown = function ( ) return ( essence.vision_of_perfection . enabled and 0.87 or 1 ) * 180 end ,
gcd = " off " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 376022 ,
handler = function ( )
applyBuff ( " shadow_blades " )
end ,
} ,
shadow_dance = {
id = 185313 ,
cast = 0 ,
charges = function ( ) return talent.enveloping_shadows . enabled and 2 or nil end ,
cooldown = 60 ,
recharge = function ( ) return talent.enveloping_shadows . enabled and 60 or nil end ,
gcd = " off " ,
startsCombat = false ,
texture = 236279 ,
nobuff = " shadow_dance " ,
usable = function ( ) return not stealthed.all , " not used in stealth " end ,
handler = function ( )
applyBuff ( " shadow_dance " )
if talent.shot_in_the_dark . enabled then applyBuff ( " shot_in_the_dark " ) end
if talent.master_of_shadows . enabled then applyBuff ( " master_of_shadows " , 3 ) end
if azerite.the_first_dance . enabled then
gain ( 2 , " combo_points " )
applyBuff ( " the_first_dance " )
end
end ,
} ,
shadowstep = {
id = 36554 ,
cast = 0 ,
charges = 2 ,
cooldown = function ( ) return 30 * ( 1 - conduit.quick_decisions . mod * 0.01 ) end ,
recharge = function ( ) return 30 * ( 1 - conduit.quick_decisions . mod * 0.01 ) end ,
gcd = " off " ,
startsCombat = false ,
texture = 132303 ,
handler = function ( )
applyBuff ( " shadowstep " )
end ,
} ,
shadowstrike = {
id = 185438 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return ( azerite.blade_in_the_shadows . enabled and 38 or 40 ) * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
cycle = function ( ) return talent.find_weakness . enabled and " find_weakness " or nil end ,
startsCombat = true ,
texture = 1373912 ,
usable = function ( ) return stealthed.all or buff.sepsis_buff . up , " requires stealth or sepsis_buff " end ,
handler = function ( )
gain ( ( buff.shadow_blades . up and 3 or 2 ) + ( buff.the_rotten . up and 4 or 0 ) , " combo_points " )
removeBuff ( " the_rotten " )
removeBuff ( " symbols_of_death_crit " )
if azerite.blade_in_the_shadows . enabled then addStack ( " blade_in_the_shadows " , nil , 1 ) end
if buff.premeditation . up then
if buff.slice_and_dice . up then
gain ( 2 , " combo_points " )
if buff.slice_and_dice . remains < 10 then buff.slice_and_dice . expires = query_time + 10 end
else
applyBuff ( " slice_and_dice " , 10 )
end
removeBuff ( " premeditation " )
end
if conduit.perforated_veins . enabled then
addStack ( " perforated_veins " , nil , 1 )
end
removeBuff ( " sepsis_buff " )
applyDebuff ( " target " , " find_weakness " )
end ,
auras = {
-- Conduit
perforated_veins = {
id = 341572 ,
duration = 12 ,
max_stack = 3
} ,
}
} ,
shiv = {
id = 5938 ,
cast = 0 ,
cooldown = 25 ,
gcd = " spell " ,
spend = function ( )
if legendary.tiny_toxic_blade . enabled then return 0 end
return 20 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 )
end ,
spendType = " energy " ,
startsCombat = true ,
texture = 135428 ,
handler = function ( )
gain ( 1 , " combo_points " )
removeBuff ( " symbols_of_death_crit " )
applyDebuff ( " target " , " crippling_poison_shiv " )
end ,
auras = {
crippling_poison_shiv = {
id = 319504 ,
duration = 9 ,
max_stack = 1 ,
} ,
}
} ,
shroud_of_concealment = {
id = 114018 ,
cast = 0 ,
cooldown = 360 ,
gcd = " spell " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 635350 ,
handler = function ( )
applyBuff ( " shroud_of_concealment " )
if conduit.fade_to_nothing . enabled then applyBuff ( " fade_to_nothing " ) end
end ,
} ,
shuriken_storm = {
id = 197835 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 35 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 1375677 ,
handler = function ( )
gain ( active_enemies + ( buff.shadow_blades . up and 1 or 0 ) , " combo_points " )
removeBuff ( " symbols_of_death_crit " )
end ,
} ,
shuriken_tornado = {
id = 277925 ,
cast = 0 ,
cooldown = 60 ,
gcd = " spell " ,
spend = function ( ) return 60 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
toggle = " cooldowns " ,
talent = " shuriken_tornado " ,
startsCombat = true ,
texture = 236282 ,
handler = function ( )
applyBuff ( " shuriken_tornado " )
end ,
} ,
shuriken_toss = {
id = 114014 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = function ( ) return 40 * ( ( talent.shadow_focus . enabled and ( buff.shadow_dance . up or buff.stealth . up ) ) and 0.8 or 1 ) end ,
spendType = " energy " ,
startsCombat = true ,
texture = 135431 ,
handler = function ( )
gain ( active_enemies + ( buff.shadow_blades . up and 1 or 0 ) , " combo_points " )
removeBuff ( " symbols_of_death_crit " )
end ,
} ,
slice_and_dice = {
id = 315496 ,
cast = 0 ,
cooldown = 0 ,
gcd = " spell " ,
spend = 25 ,
spendType = " energy " ,
startsCombat = false ,
texture = 132306 ,
usable = function ( )
return combo_points.current > 0 , " requires combo_points "
end ,
handler = function ( )
if talent.alacrity . enabled and combo_points.current > 4 then
addStack ( " alacrity " , 20 , 1 )
end
local combo = min ( talent.deeper_stratagem . enabled and 6 or 5 , combo_points.current )
applyBuff ( " slice_and_dice " , 6 + 6 * combo )
spend ( combo , " combo_points " )
end ,
} ,
sprint = {
id = 2983 ,
cast = 0 ,
cooldown = 120 ,
gcd = " off " ,
startsCombat = false ,
texture = 132307 ,
handler = function ( )
applyBuff ( " sprint " )
end ,
} ,
stealth = {
id = function ( ) return talent.subterfuge . enabled and 115191 or 1784 end ,
known = 1784 ,
cast = 0 ,
cooldown = 2 ,
gcd = " off " ,
startsCombat = false ,
texture = 132320 ,
usable = function ( )
if time > 0 then return false , " cannot use in combat " end
if buff.stealth . up then return false , " cannot use in stealth " end
if buff.vanish . up then return false , " cannot use while vanished " end
return true
end ,
readyTime = function ( ) return buff.shadow_dance . remains end ,
handler = function ( )
applyBuff ( " stealth " )
if talent.shot_in_the_dark . enabled then applyBuff ( " shot_in_the_dark " ) end
if talent.premeditation . enabled then applyBuff ( " premeditation " ) end
emu_stealth_change = query_time
if conduit.cloaked_in_shadows . enabled then applyBuff ( " cloaked_in_shadows " ) end
if conduit.fade_to_nothing . enabled then applyBuff ( " fade_to_nothing " ) end
end ,
copy = { 1784 , 115191 }
} ,
symbols_of_death = {
id = 212283 ,
cast = 0 ,
charges = 1 ,
cooldown = 30 ,
recharge = 30 ,
gcd = " off " ,
spend = - 40 ,
spendType = " energy " ,
startsCombat = false ,
texture = 252272 ,
handler = function ( )
applyBuff ( " symbols_of_death " )
applyBuff ( " symbols_of_death_crit " )
if legendary.the_rotten . enabled then applyBuff ( " the_rotten " ) end
end ,
} ,
tricks_of_the_trade = {
id = 57934 ,
cast = 0 ,
cooldown = 30 ,
gcd = " spell " ,
startsCombat = false ,
texture = 236283 ,
usable = function ( ) return group , " requires an ally " end ,
handler = function ( )
applyBuff ( " tricks_of_the_trade " )
end ,
} ,
vanish = {
id = 1856 ,
cast = 0 ,
cooldown = 120 ,
gcd = " off " ,
toggle = " cooldowns " ,
startsCombat = false ,
texture = 132331 ,
disabled = function ( )
return not settings.solo_vanish and not ( boss and group ) , " can only vanish in a boss encounter or with a group "
end ,
handler = function ( )
applyBuff ( " vanish " , 3 )
applyBuff ( " stealth " )
emu_stealth_change = query_time
if legendary.deathly_shadows . enabled then
gain ( 5 , " combo_points " )
applyBuff ( " deathly_shadows " )
end
if legendary.invigorating_shadowdust . enabled then
for name , cd in pairs ( cooldown ) do
if cd.remains > 0 then reduceCooldown ( name , 20 ) end
end
end
if conduit.cloaked_in_shadows . enabled then applyBuff ( " cloaked_in_shadows " ) end
if conduit.fade_to_nothing . enabled then applyBuff ( " fade_to_nothing " ) end
end ,
} ,
wound_poison = {
id = 8679 ,
cast = 1.5 ,
cooldown = 0 ,
gcd = " spell " ,
startsCombat = false ,
texture = 134194 ,
readyTime = function ( ) return buff.lethal_poison . remains - 120 end ,
handler = function ( )
applyBuff ( " wound_poison " )
end ,
} ,
} )
-- Override this for rechecking.
spec : RegisterAbility ( " shadowmeld " , {
id = 58984 ,
cast = 0 ,
cooldown = 120 ,
gcd = " off " ,
usable = function ( ) return boss and group end ,
handler = function ( )
applyBuff ( " shadowmeld " )
end ,
} )
spec : RegisterOptions ( {
enabled = true ,
aoe = 2 ,
nameplates = true ,
nameplateRange = 8 ,
damage = true ,
damageExpiration = 6 ,
potion = " phantom_fire " ,
package = " Subtlety " ,
} )
spec : RegisterSetting ( " mfd_points " , 3 , {
name = " |T236340:0|t Marked for Death Combo Points " ,
desc = " The addon will only recommend |T236364:0|t Marked for Death when you have the specified number of combo points or fewer. " ,
type = " range " ,
min = 0 ,
max = 5 ,
step = 1 ,
width = " full "
} )
spec : RegisterSetting ( " priority_rotation " , false , {
name = " Use Priority Rotation (Funnel Damage) " ,
desc = " If checked, the default priority will recommend building combo points with |T1375677:0|t Shuriken Storm and spending on single-target finishers. " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterSetting ( " solo_vanish " , true , {
name = " Allow |T132331:0|t Vanish when Solo " ,
desc = " If unchecked, the addon will not recommend |T132331:0|t Vanish when you are alone (to avoid resetting combat). " ,
type = " toggle " ,
width = " full "
} )
spec : RegisterSetting ( " allow_shadowmeld " , nil , {
name = " Allow |T132089:0|t Shadowmeld " ,
desc = " If checked, |T132089:0|t Shadowmeld can be recommended for Night Elves when its conditions are met. Your stealth-based abilities can be used in Shadowmeld, even if your action bar does not change. " ..
" Shadowmeld can only be recommended in boss fights or when you are in a group (to avoid resetting combat). " ,
type = " toggle " ,
width = " full " ,
get = function ( ) return not Hekili.DB . profile.specs [ 261 ] . abilities.shadowmeld . disabled end ,
set = function ( _ , val )
Hekili.DB . profile.specs [ 261 ] . abilities.shadowmeld . disabled = not val
end ,
} )
spec : RegisterPack ( " Subtlety " , 20211208 , [ [ de1IgcqiuP8ikPAtuQ ( KIeJsLKtPOQvPiGxPi1SirUfju2Lk ( fQedJeLJrcwguINPsvMgQuDnsuTnvsPVrjfnovsHZHkjADQuvnpvQCpuX ( OK8pkPGoiQKQfcL0dvK0ejHCrujH2OIG ( ijuXijHk1jvjfTsOuVKskWmrLKUjju1ovu5NkcKHsjLwQkPYtrPPQi6QOskBLsk0xvjvnwvQkNLeQK2RK ( RedMQdlAXq6XkmzsDzKndXNHIrJItl1QrLe8AfLztXTjPDd8BLgoQ64kculh0Zv10fUouTDkLVtjgVIqNxLy9KqLy ( Qu2pXvfQtwz1zq15WIYWIckGfLDnokWvQmf4Ucv24cpvz5ZXSedvzbPkvzzXrddfxQS85fZM66Kv2FXHdQYYeb ) F ) CHly6Gbh9mwvU8TkUjJEbdyIeC5B1bxQSO4TjUMGkALvNbvNdlkdlkOawu214OaxPYu4ECLv2epywyLLTvNALLP1AcurRSA6hvwwC0WqXfXVUfdojyRiAqQOeu8RHsIJfLHffeSfSNktcWq ) 9 lyRyIR4xBK42syNOg6G ) uHh2lSJlf4gz0lqCCEX ) v8oeVFXFkehLqwijUfsC8NeVJJGTIj ( uxv0gqIRIBIM3qIpsJPKJOxqX0FiobcytV4XkoK04dsC ( niq0PrCizzHZoc2kM4k ( Cgj ( eAONzatKq8geeeIZhI3aXhRkAgI3iIBHeNRa ( hIRBT4DioYcf32AYOnu5xJnceNkRP ) 4 Rtwz ) GstWq66K15uOozLLajQH0vSwzZr0lOY ( mPET8bSNrvwn9dyZh9cQSxteXzdknbdxSLG ( zepHK448kjo ( tIZYK61YhWEgjESIJsacPdXrGRQ4bdjoF ( FBJehDb4V4jql ( e2aT4xpLZa0 ) kjozJaI3iIBHepHK4ziUAorXNQ1k ( v4ad9V44FdWiUIp ) GGIZ1 ) p ) VbZxzhWoiyNv2Rehfhb58bLMG5GZl ( TBIJIJGCSLG ( zo48IpV42f ) kXFEYykrcXqXFEgCypJaLpwOQ43jo3f ) 2 nXTLWorn0b ) PcpSxyhxkWnYOxG4ZlUDXvZpiyj ) p ) VbfiPMn4fNJ4kRg15WsDYklbsudPRyTYQPFaB ( OxqLDcBq ) mINH4CFAXNQ1kULoyw8qCfXQK4kFAXT0bJ4kIvjXtGw8RvClDWiUIyfprcckU1yc6NPYMJOxqLDKgtjhrVGIP ) OYoGDqWoRS2syNOg6qii0iABuzSQOBHFBq8IBfhXh8f1CILNNaAXVDtCuCeKZZGd7zeOeleK69GZlUDXhRk6w43ge ) rti9OdXVJJ4yr8B3e ) 5 jJPejedf ) 5 zWH9mcu ( yHQIBfhX5U42f3wc7e1qhcbHgrBJkJvfDl8BdIxCR4io3f ) 2 nXhRk6w43ge ) rti9OdXVJJ4kiUIj ( vIhPHaXrtepblFaZiXqQhcKOgslUDXrXrqo2sq ) mhCEXNVYA6pkGuLQSinOFMAuN7E1jRSeirnKUI1k7a2bb7SY ( bLMGH0NN4 ) ( f3U4ppzmLiHyO4ppdoSNrGYhluv87eN7v2Ce9cQSptQxlFa7zunQZX96KvwcKOgsxXALDa7GGDwzJ0qG4aAmmXhPzgbpeirnKwC7IdXbeYcXqNObxkXoXEuqnPMoeirnKwC7I ) 8 KXuIeIHI ) 8 m4WEgbkFSqvXVtCLxzZr0lOY ( mTTAuNt51jRSeirnKUI1kBoIEbv2Nj1RLpG9mQYoUmmujsigk ( 6 CkuzhWoiyNvwUjUTe2jQHo4pv4H9c74sbUrg9ce3U4Acfhb5G0aDXcLZa0 ) hiPMn4f ) oXvqC7I ) 8 KXuIeIHI ) 8 m4WEgbkFSqvXVJJ43tC7IhjedfNOvPsSfDtIRyIdj1SbV4wj ( 1 wz10pGnF0lOYY14fpwXVN4rcXqXl ( vGvCEyVZl ( mI4fhNx8jSbAXVEkNbO ) fh9I4JldtdWioltQxlFa7z0Pg15U26KvwcKOgsxXALnhrVGk7ZK61YhWEgvz10pGnF0lOYoHluCEyVWoUioCJm6fOK44pjoltQxlFa7zK4RnckoBSqvXT0bJ4xVIx8et2GpehNx8yfN7IhjedfV4lu8gr8j86fVFXH4aqdWi ( IGi ( vlq8eCr8uDXbH4lI4rcXqXpFLDa7GGDwzTLWorn0b ) PcpSxyhxkWnYOxG42f ) kX1ekocYbPb6IfkNbO ) pqsnBWl ( DIRG43UjEKgcehluYVa18dcEiqIAiT42f ) 5 jJPejedf ) 5 zWH9mcu ( yHQIFhhX5U4ZxJ6CwZ6KvwcKOgsxXALDa7GGDwzFEYykrcXqXlUvCe ) EIpT4xjokocYjyOcCJGahCEXVDtCioGqwig6KZYe2F5xCtbbMyujqCiqIAiT4ZlUDXVsCuCeKZFrfDnFzrkAkdMsIh7a2XbNx8B3eNBIJIJGC4HKkP7iJEbhCEXVDt8NNmMsKqmu8IBfhXvU4ZxzZr0lOY ( m4WEgbkFSq1AuN7AuNSYsGe1q6kwRS5i6fuzFMuVw ( a2ZOkRM ( bS5JEbvwwMuVw ( a2ZiXJvCiHaPNr8jSbAXVEkNbO ) fpbAXJvCc84qsClK4Jei ( iHWlIV2iO4P4i4gJ4t41lEdIv8GHehqtmeNDvK4nI487 ) nQHov2bSdc2zLvtO4iihKgOlwOCgG ( ) aj1SbV43XrCfe ) 2 nXh7A0RfW5VOIUMVSifnLbZbsQzdEXVtCfUgIBxCnHIJGCqAGUyHYza6 ) dKuZg8IFN4JDn61c48xurxZxwKIMYG5aj1SbFnQZXvwNSYsGe1q6kwRSdyheSZklkocYHNGilmdsxSrn4pFKJzIBfhXvU42fFSanEhhEcISWmiDXg1G ) atWmXTIJ4kCVkBoIEbvwmMDvrnPMQrDofuwDYkBoIEbv2Nj1RLpG9mQYsGe1q6kwRrDofuOozLLajQH0vSwzhWoiyNvwUjEKqmuC6VGU ) lUDXhRk6w43ge ) rti9OdXTIJ4kiUDXrXrqopZgLgucgQOt4SdoV42fNaeeZLt0Quj2c3vM4wjoMH ( OMtSYMJOxqLDWqjF5z2Og1OYQjKe3e1jRZPqDYkBoIEbv2z9ywLLajQH0vSwJ6CyPozLLajQH0vSwzx ( k7trLnhrVGkRTe2jQHQS2sdovzrXrqoVPhujb6IUh0bNx8B3e ) 5 jJPejedf ) 5 zWH9mcu ( yHQIBfhXV2kRM ( bS5JEbvwU2tAXJvCnfeuTbK4wyOGHGIp21OxlGxClzhIJSqXzbksC08jT4lq8iHyO4pvwBjSasvQY ( aDzSaDh9cQrDU7vNSYsGe1q6kwRSlFL9POYMJOxqL1wc7e1qvwBPbNQSeccnI2gvgRk6w43geFLvt ) a28rVGklxFmwCqioYcfNLzsXHuoIEbIhTkjo6fXBmGf2amIBwlk2uTwXtqRMdMeIH0IRMXGHEXBG4bdjUYok ) fNhsdI0naJ4P48BqGOtJ4SmtkopChvwBjSasvQYsii0iABuzSQOBHFBq81Ooh3RtwzjqIAiDfRv2LVY ( uuzZr0lOYAlHDIAOkRT0Gtv2XQIUf ( TbXxzhWoiyNv2XAJajioZUa7eiUDXjeeAeTnQmwv0TWVniEXTs8XQIUf ( TbXlUDXhRk6w43ge ) rti9OdXTsCSiUDXJwLkXwEM4WDXVtCLDuU42fNBIFL4JvfDl8BdIxCoIJfXTlokocYHgmBdWuGepSvtGUCVdoV43Uj ( yvr3c ) 2 G4fNJ43tC7IJIJGCObZ2amfiXdB1eOlC ) GZl ( TBIpwv0TWVniEX5io3f3U4O4iihAWSnatbs8Wwnb6IYp48IpFL1wclGuLQSeccnI2gvgRk6w43geFnQZP86KvwcKOgsxXALD5RSpfv2Ce9cQS2syNOgQYAln4uLLh2lSJlf4gz0lqC7I ) 8 KXuIeIHI ) 8 m4WEgbkFSqvXTIJ4yPYQPFaB ( OxqLDccyUi ( GjbyiXHBKrVaXBeXTqIZK2iX5H9c74sbUrg9ce ) Pq8eOfxf3enVHepsigkEXX5pvwBjSasvQYI ) uHh2lSJlf4gz0lOg15U26KvwcKOgsxXALD5RSpfv2Ce9cQS2s
end