-- RogueSubtlety.lua
-- June 2018
local addon , ns = ...
local Hekili = _G [ addon ]
local class = Hekili.Class
local state = Hekili.State
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
sht [ 1 ] = mh_next + ( 1 * mh_speed )
sht [ 2 ] = mh_next + ( 2 * mh_speed )
sht [ 3 ] = mh_next + ( 3 * mh_speed )
sht [ 4 ] = mh_next + ( 4 * mh_speed )
end
if oh_speed and oh_speed > 0 then
sht [ 5 ] = oh_next + ( 1 * oh_speed )
sht [ 6 ] = oh_next + ( 2 * oh_speed )
sht [ 7 ] = oh_next + ( 3 * oh_speed )
sht [ 8 ] = oh_next + ( 4 * oh_speed )
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 " and target.time_to_die > 3 + Hekili : GetLowestTTD ( ) then return " 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 " , 20210709 , [ [ deLB ( bqiuL6ruPytkrFIkjJsLItPsQvrvj5vuv1SOsCluPYUuXVqLyyOk5yOkwgKkpdvsMgvQ6AuP02usL ( gvsX4uPu6CQukwNkL08uj5EOI9rvX ) qLuLoiQuvlesvpKQsnruPCrLuLAJujvFevsLrIkPQojvLuRes5LkPkYmvPuDtuPk7ujLFQKQWqPQelvjv8uuAQuv5QOskBLkPKVQsjgRsQQZQKQO2Re ) vsdMYHfTyO6Xk1Kj5YiBgIpdjJgkNwQvRKQKxRsmBsDBQYUb ( TIHtfhNkPulh0Zv10fUok2Us47OQgpvQCELK1JkPkMVkv7N4cpf ) kSQmOYAOJxOJhE5A41T5GoEHo37w0vyJvouH1j3xsuuHfKEuHLLbp0uSQW6KR0tQk ( vy ) HbUPclweo ) TYfUGQdmg8ZE84Y3Em6m6bSHjsWLV92CPWIZ06Wxdk4fwvguzn0Xl0XdVCn862CqhVqN7D ) TPWMmb2alSSTNVlSyTsrGcEHvr ) UWYYGhAkwj26mOyibn0y0Re724IyOJxOJhbnbnFJLau0FRcACNyCVzbj2Ie2jUMompvDG9a7yvforg9aeJXrSFeRdX6xSNcXWjKbsIXNeJ5jX64iOXDI57XdVbKyEm6OD0Ky7uRR5o6bu19hIrGa20lwmIbjfZMeZzcceDQfds8h4LJGg3jg3lVqI56A6X2WejeRbbbHmoHynqS94HNHynIy8jXwVy ( qmvReRdXqgOylgDgTMQ ) OxqG4uy19hFXVc7huQdmsv8RSgpf ) kSeiX1KQG ( cBUJEaf2hlvd ) pG9fQWQOFdBNOhqH1xJigBqPoW4YIe0pMyjKeJXXfXyEsmwSun8 ) a2xiXIrmCcqiDigcC8elWiXCY ) 7 fKy4dG5flbkXC9gOe7wO8cG ( 3 fXOfeqSgrm ( KyjKeldX8s3jMV9fXUHbOP ) fJ5BakX4E5heumU ) ) 5 ) n46c7g2bb7SWEJy4miiNpOuhyhghXUFxmCgeKZIe0p2HXrSRfBPyE5heSM ) N ) 3 GkK8Yg8IXrmEvIYAOR4xHLajUMuf0xyv0VHTt0dOW66nOFmXYqm37Vy ( 2 xeJFhydtig3yDrm36Vy87atmUX6Iyjqj26kg ) oWeJBSILibbfZ1kb9JvyZD0dOWUtTUM7Ohqv3Fuy3WoiyNfwcbH2rVGQ7XdFQotdIxmF4i22P6LUR ( oeqj297IHZGGCEmgyFHa1yGGunhghXwk2E8WNQZ0G4pkcP3Di2vCedDID ) UyVdP11iHOO4ppgdSVqG6hd0tmF4iM7fBPyeccTJEbv3Jh ( uDMgeVy ( Wrm3l297IThp8P6mni ( JIq6DhIDfhX4rmUtSBelsnbIJIihcw ) aMrII8oeiX1KsSLIHZGGCwKG ( XomoIDDHv3FubPhvyrAq ) yLOSgxv8RWsGextQc6lSByheSZc7huQdmsDEY57xSLI9oKwxJeIII ) 8 ymW ( cbQFmqpXUsm3xyZD0dOW ( yPA4 ) bSVqLOSM7l ( vyjqIRjvb9f2nSdc2zHnsnbIdOrHfFK6le8qGextkXwkgKbqidefDIgSQgJ76DfxNk6qGextkXwk27qADnsikk ( ZJXa7leO ( Xa9e7kXCBHn3rpGc7J1lkrzn3w8RWsGextQc6lS5o6buyFSun8 ) a2xOc7E1wt1iHOO4lRXtHDd7GGDwy5TylsyN4A6W8u1b2dSJvv4ez0dqSLIPiCgeKdsduv ( uEbq ) FGKx2GxSReJhXwk27qADnsikk ( ZJXa7leO ( Xa9e7koIXvITuSiHOO4eThvJPQAsmUtmi5Ln4fZhXw3cRI ( nSDIEafwUMJyXigxjwKquu8IDdyeZb2Z1IDHihXyCeZ1BGsSBHYla6FXWxj2E1w3auIXILQH ) hW ( cDkrzT1T4xHLajUMuf0xyZD0dOW ( yPA4 ) bSVqfwf9By7e9akSU ( afZb2dSJvIbNiJEaUigZtIXILQH ) hW ( cj2SGGIXgd0tm ( DGj2TW9elrLn4dXyCelgXCVyrcrrXl2afRreZ1VfX6xmida0auIniiIDZaelbRel9ggqi2GiwKquu8xxy3WoiyNf2fjStCnDyEQ6a7b2XQkCIm6bi2sXUrmfHZGGCqAGQYNYla6 ) dK8Yg8IDLy8i297IfPMaXHpLodWl ) GGhcK4Asj2sXEhsRRrcrrXFEmgyFHa1pgONyxXrm3l21LOSMRP4xHLajUMuf0xy3WoiyNf23H06AKquu8I5dhX4kX8xSBedNbb5eyuforqGdJJy3VlgKbqidefDYlzc7V ( dJUIatuEeioeiX1KsSRfBPy3igodcY5x5Hp6VoivfLbwnzIzd74W4i297IXBXWzqqooqYJuDKrpGdJJy3Vl27qADnsikkEX8HJyUvSRlS5o6buyFmgyFHa1pgOxjkRDBl ( vyjqIRjvb9f2Ch9akSpwQg ( Fa7luHvr ) g2orpGcllwQg ( Fa7lKyXigKqG0JjMR3aLy3cLxa0 ) ILaLyXigbEgijgFsSDceBNq4kXMfeuSumegTwmx ) weRbXiwGrIbi3fIXoCtSgrmN5 ) gxtNc7g2bb7SWQiCgeKdsduv ( uEbq ) FGKx2GxSR4igpID ) Uy7z0QHp48R8Wh9xhKQIYa7ajVSbVyxjgp3wXwkMIWzqqoinqv5t5fa9 ) bsEzdEXUsS9mA1WhC ( vE4J ( RdsvrzGDGKx2GVeL1Unf ) kSeiX1KQG ( c7g2bb7SWIZGGCCiiYaZGu1fud ( Zh5 ( Iy ( Wrm3k2sX2dqX0XXHGidmdsvxqn4pWeCrmF4igpCvHn3rpGclk9mE46urLOSgp8Q4xHn3rpGc7JLQH ) hW ( cvyjqIRjvb9LOSgp8u8RWsGextQc6lSByheSZclVflsikko9xXN ) fBPy7XdFQotdI ) OiKE3Hy ( WrmEeBPy4miiNhBIAdQbgvvj8YHXrSLIracIA1jApQgt198smFed1wD8s3vyZD0dOWUXO0P ( ytuIsuyvesYOJIFL14P4xHn3rpGc7LEFPWsGextQc6lrzn0v8RWsGextQc6lSJtH9POWM7OhqHDrc7extf2fPMHkS4miiNx3BQMavv1B6W4i297I9oKwxJeIII ) 8 ymW ( cbQFmqpX8HJyRBHvr ) g2orpGclx7jLyXiMIcc61asm ( yuGrqX2ZOvdFWlg ) SdXqgOySaUjgE ( KsSbiwKquu8Nc7IewbPhvyFGQUhGQJEaLOSgxv8RWsGextQc6lSJtH9POWM7OhqHDrc7extf2fPMHkSoWEGDSQcNiJEaITuS3H06AKquu8NhJb2xiq9Jb6jMpCedDfwf9By7e9akSRha9kX2yjafjgCIm6biwJigFsmSCbjMdShyhRQWjYOhGypfILaLyEm6OD0KyrcrrXlgJZPWUiHvq6rfwMNQoWEGDSQcNiJEaLOSM7l ( vyjqIRjvb9f2XPW ( uuyZD0dOWUiHDIRPc7IuZqfw05wX8xSi1eiolAud8qGextkX8vIHoEjM ) IfPMaXXl ) GG1bP ( yPA4 ) hcK4AsjMVsm0XlX8xSi1eiopwQg ( vKzZ8hcK4AsjMVsm05wX8xSi1eioPo3WowDiqIRjLy ( kXqhVeZFXqNBfZxj2nI9oKwxJeIII ) 8 ymW ( cbQFmqpX8HJyUxSRlSk63W2j6buy5ApPelgXuesdiX4JraXIrmMNe7dk1bMy ( MBVydumCMwRi4xyxKWki9Oc7huQdSAGbPhB0QsuwZTf ) kSeiX1KQG ( cRI ( nSDIEafwFJr7lI5BU9ILHyin8JcBUJEaf2DQ11Ch9aQ6 ( JcRU ) OcspQWUvFjkRTUf ) kSeiX1KQG ( cRI ( nSDIEaf21HbigcJwVsSNFhBm6flgXcmsm2GsDGrkXwNjYOhGy3GVsm10auI9JlI1HyidCtVyoZOBakXAeXatG1auI1Vy5IS1jUMU ( uyZD0dOWcza1Ch9aQ6 ( Jc7g2bb7SW ( bL6aJuNuRlS6 ( Jki9Oc7huQdmsvIYAUMIFfwcK4AsvqFHn3rpGc7R7nvtGQQ6nvyv0VHTt0dOWY9DC0ReJv3BsSeOeJB9MeldXqN ) I5BFrmfdSbOelWiXqA4h
end