You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1686 lines
62 KiB
1686 lines
62 KiB
-- 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 ) / 0.95 ) * 0.95
|
|
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 = function() return 1 + effective_combo_points end,
|
|
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 = {
|
|
}, ]]
|
|
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 = function () return 6 * ( 1 + effective_combo_points ) end,
|
|
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" },
|
|
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:RegisterCombatLogEvent( function( _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName, _, amount, interrupt, a, b, c, d, offhand, multistrike )
|
|
|
|
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:RegisterStateTable( "time_to_sht_plus", setmetatable( {}, {
|
|
__index = function( t, k )
|
|
local n = tonumber( k )
|
|
n = n - ( n % 1 )
|
|
|
|
if not n or n > 5 then return 3600 end
|
|
local val = time_to_sht[k]
|
|
|
|
-- Time of next attack instead.
|
|
if val == 0 then
|
|
local last = swings.mainhand
|
|
local speed = swings.mainhand_speed
|
|
local swing = 3600
|
|
|
|
if last > 0 and speed > 0 then
|
|
swing = last + ( ceil( ( query_time - last ) / speed ) * speed ) - query_time
|
|
end
|
|
|
|
last = swings.offhand
|
|
speed = swings.offhand_speed
|
|
|
|
if last > 0 and speed > 0 then
|
|
swing = min( swing, last + ( ceil( ( query_time - last ) / speed ) * speed ) - query_time )
|
|
end
|
|
|
|
return swing
|
|
end
|
|
|
|
return val
|
|
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 )
|
|
|
|
|
|
spec:RegisterStateExpr( "effective_combo_points", function ()
|
|
local c = combo_points.current or 0
|
|
if not covenant.kyrian then return c end
|
|
if c < 2 or c > 5 then return c end
|
|
if buff[ "echoing_reprimand_" .. c ].up then return 7 end
|
|
return c
|
|
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" )
|
|
|
|
if legendary.toxic_onslaught.enabled then
|
|
applyBuff( "adrenaline_rush", 10 )
|
|
applyDebuff( "target", "vendetta", 10 )
|
|
end
|
|
end, state )
|
|
|
|
spec:RegisterHook( "reset_precast", function( amt, resource )
|
|
if debuff.sepsis.up then
|
|
state:QueueAuraExpiration( "sepsis", ExpireSepsis, debuff.sepsis.expires )
|
|
end
|
|
|
|
class.abilities.apply_poison = class.abilities.apply_poison_actual
|
|
if buff.lethal_poison.down or level < 33 then
|
|
class.abilities.apply_poison = state.spec.assassination and level > 12 and class.abilities.deadly_poison or class.abilities.instant_poison
|
|
else
|
|
if level > 32 and buff.nonlethal_poison.down then class.abilities.apply_poison = class.abilities.crippling_poison end
|
|
end
|
|
end )
|
|
|
|
spec:RegisterHook( "runHandler", function ()
|
|
class.abilities.apply_poison = class.abilities.apply_poison_actual
|
|
if buff.lethal_poison.down or level < 33 then
|
|
class.abilities.apply_poison = state.spec.assassination and level > 12 and class.abilities.deadly_poison or class.abilities.instant_poison
|
|
else
|
|
if level > 32 and buff.nonlethal_poison.down then class.abilities.apply_poison = class.abilities.crippling_poison end
|
|
end
|
|
end )
|
|
|
|
spec:RegisterCycle( function ()
|
|
if this_action == "marked_for_death" then
|
|
if cycle_enemies == 1 or 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( "tier28", 188901, 188902, 188903, 188905, 188907 )
|
|
spec:RegisterSetBonuses( "tier28_2pc", 364557, "tier28_4pc", 363949 )
|
|
-- 2-Set - Immortal Technique - Shadowstrike has a 15% chance to grant Shadow Blades for 5 sec.
|
|
-- 4-Set - Immortal Technique - Your finishing moves have a 3% chance per combo point to cast Shadowstrike at up to 5 enemies within 15 yards.
|
|
-- 2/15/22: No mechanics to model.
|
|
|
|
|
|
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 effective_combo_points > 4 then addStack( "alacrity", nil, 1 ) end
|
|
removeBuff( "echoing_reprimand_" .. combo_points.current )
|
|
|
|
if buff.finality_black_powder.up then removeBuff( "finality_black_powder" )
|
|
elseif legendary.finality.enabled then applyBuff( "finality_black_powder" ) end
|
|
|
|
spend( 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
|
|
|
|
removeBuff( "echoing_reprimand_" .. combo_points.current )
|
|
spend( 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,
|
|
|
|
bind = function ()
|
|
if ( buff.lethal_poison.down or level < 33 ) and not ( state.spec.assassination and level > 12 ) then return "apply_poison" end
|
|
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",
|
|
|
|
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
|
|
|
|
applyDebuff( "target", "kidney_shot", 1 + combo_points.current )
|
|
|
|
if talent.prey_on_the_weak.enabled then applyDebuff( "target", "prey_on_the_weak" ) end
|
|
|
|
spend( 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
|
|
|
|
removeBuff( "echoing_reprimand_" .. combo_points.current )
|
|
spend( 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
|
|
removeBuff( "echoing_reprimand_" .. combo_points.current )
|
|
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", 20220911, [[Hekili:vZZAZTTrs(Br1wfdPLenFjfLCskLJ969I34SUIYgV3xeee4qrSceGlaOK1vQ4V9R75fMzW8auwooxT1URm4GbD3t)U7PVC8L)2LxmpUMC5Vmz0KjJ(UXJhoE0rF74zxEr9dRjxEX64KBJVb(J84vW)7fBUUoJu)a(dpKveph3GQInLjWpUSUED13)YxEtA9YnxpmPy1lRsxTjlUoTipPmErn(VtE5LxC9M0S6Fk)YRT)1V8I4n1llkHVx6QxdBC685e2QjvjnqX2R(1IB2q2(UliRRjRUMuU9QXJpy7v42T9DBF3RxgNFdP673(Ud3E17lUJS9Q7IltJVoJmCtfjADzArzA9drLf1uOC7vBwV9QQITxLwd)3k4VjWFCnzrrj8Y3d)x49WFDiDpFDXQRtZHhaOzD66m4VsIZYIItWDlklTcEBsEDzkb2RAyFljZ3KalBDrn8804m8r3xuElBdFF8TWp(2ms1skfd(u51KYYnRP)jS9Wxmg)7fmW5FVb)eZtNZazaobCSaP5zKOQIS7GVYWlVaHKk6HvnjoREjbp5(f65pjhPhZV8hbYofSHfTmEEX9vayFlHr0ltxZ(PFc(UxW2JxExCEA1YTxfJ0MnRpGtCUq5Tzi91KCYck4vwScE0sKaexvLINixxKVbioX5ak82u8)9JK4BZjvvWgsa4VbzPFie7xf)jG4)HTx1FtoqRQWVCA(ni9dqqGe8QQkC)ZhC5faSdpkn(Yl6dFSnlwmKtdgIN1p(i)HmKH(SbBVQhS1WH)AcCywhxEdPUAy1YnikLhvvxucOXPBVAgBdcXuX2WvuylkMdArLKvXP5aSF22RgDznifyCCOr4bsdJydO)S9zy)9GKwXg4bV5cGwD0(SNW)Nuc6XnpcEYckT53PyAZPTjhRkjtIzKfliWsUJeHCGfrRlagta2pha(K1rWbseqTWpPKIkPxa5khyjO02EmwyXgmCoWyKGSgNcB0e2B7NSJFXz0nQtah)iY9hD82RoKEAxhNHclZjK1WXeW)gxdQ(wnKFIq)KTyvyIwrm1JlONr4r5uJJYgkQuaCyfzDfQIPximgz0CiCcFQzoeIVguDxvhFTbF0pYFmO0a(kOmdtEfyrIZrnt3Vef5q22LP3aCoF43b4RgFlopLy9)yw8CuTgIdBwpufjtkYNVbuscesqJeOIFE0DeGxFyzC(TSZLtAONTwf9ZXw2rnlJH4rZrWCOu4bx00wl6AkSj44clkZ2KUll336Ntcu4(5bM3xYQVhYzc64Ibgpuv9YhkrMk4y9iNCqC(0C40PgOuz3sk14rl3KJ2RUHmSon)bq67tPjmiuBzb55osLNl9oeOoUtgmsEibm(W3z0kJcWVxykmaBZjuQhipnp6EU1afQlvOnmki5myRaihRaWQiAEkHkZ3Wdbkknet(NRzMUMkEByz9ZHhGamWdClOLG5TGUnl4Ch8lbPakYwmcZqKc(TDIcQR5hTPlOtauZju2n3cmtLOfAkfGP6)JVheCVbX0AgeJ41mf8c3F0e7B)itygyGYPqFXc4P9XZK4C4lhVQytEn(y5lpWQXc3hS972zlkAWzZHLSUiNz7Cx4FzQ(PIsN4wzS6Ei1LSFJ8nOOgraWxnP3cnIxX3gdyCf4M1MSOfLX3ScbyjmsnLrQJOo4amFKYjNenzDcNLKIDC9dlksGLiFtdDmpaMUYQIkwenNexVmc8oUaXbjiTNqrkqgjZtz0B5VAbjpIA5QH3tL2HuSVB35sB71xjzrjbDzXucHAfbxbjVAJWV6Ky0724YYhemXXG)IBYY48L54lq9CmN8jGj8II3W4AxxaEtHCDQhSbyYotYLLuuKbWDElQSol5jDtTuWDdjUJh5KFSJMd7G51XtBo1UjRa8(bnaq)8uV(TFI2au4cn9hvtloxNkW(FDrvLhh7o)mH6AUfXI6iW7xuYAEme3d3gneIq5npi1rpBevT8njZhc(wQ4S6sqFqeUjxI)h4GErme7LmAgX6Unn5wd8XeDkwdOpPU5De6VW)kBdHFCuLLMqIaEwWUbye3QuLvNuBzsPIW57Nta64kAGJG3waLOSH1biL3hldL7ICGl)6y6BIQqfFpkWY96ScanKJiL(zS485FaiQhOX0)uVqJyxa2kumpccTaC2EvCYs87px3htUFt3(a8VZni2VEjbLzqQDfHW0uOO9Gg7igpn9zyuMC7AXOb1FdT3wKmGzo8vAaaNIPhOHolojzj88BGa8aJGRqc6ej1S17nX37n197n137nZ97nZ37De8E2C(mmhK9qWmouEvscZdcgVnsNljSTeojajFM3tP3KxWcnjR4(dXJkvEamq6AqnqflHi0dWrdpQcpZijeW(h(MXAPyrEKQYJ5aKT4MBy0xhPDYKY005kAvZtl3QufSncTLvlRJwNbUqmJAgcih0Fw1zBNsuDHGy61AycID3)cRlzIblZ)ipd9NMMiVgFFBsj0Yymhsw(A0e3r90Oj3CsFGOHR2G4w9x1MNJ2uX863uXYkZI0YQAFPvrjtbjZR86NLMQytPO1RrAYf4A4O4By)jaet4PgseCpv8GczGkQrizbvoJPwJNnO6LQzucjRihg5tRtlrrmmecPygt35F71VHhZa875f0GkC6uTTalpMYlSaJFnslgmfVe0TfP6Jf3LGwIhY09WZMdvDYPmSg0K)cK3YM3xA02FDtoZRtGaxb4otpJ01ZlejVbt9SGtRpQZAEbLCxvCGmrSj4RUgmRt)fKYceRLS)bYYCxCAgcfdg2CWdbx4kvCnjocyS0yMAYPR09ox43hKHrkG)Fy7v)mP(Baa6gA6ybfNjLXG1J4RPPwK5xgxLnLiW)CCo)(yY8NJCsv1Pz00zdmajykQRhmC7v)p0edJKeQh(xteSCvPRwt9AFNYdPDrvluJiUiwlVydR7AYr0ya5HODx6nfkrEI8rJu)DEADbp95(s7zT2d3dxM2Nmolgr9hSTt4kFrhsP15IaGD7e6cHhO5SNqtNG(PlEOZpGR08YJtKRxIH4bEUsj1MoEQRUSiVkLYOWZvFSuEc)ycabDiyzJQlb7xZhQt8l8Oj0s0RKhQnW7J)X0)vNzLhukDattQsE5zKqwIyOHSIWitjNrk7XmTSPgjrYdHbhOL((mSWulmQdGmZoQz0P9o1nYwNYOE7097n564cm01FQ0F5o9bNQwubVjdPR74SUgAZUuObhLcySPZ0MYJujbPw0)KifqHhk47kNVXLGcD07ZYsqNLbA9Zf3tqMYM0wIoreJ2hkerI9Qp8ZO9GKyEjvrleWFwrPhGtg(WeAsfy6m5)uj5gc1NTXFRAEa4q56nzvTYVXjQlmJMo9O)9M5004zU2VtDTxhFdYawxMMCBL(kRrxFjSQ02kPeXONBittf(VANCc5(xuxd)s0IS4hiZVhJrdZIV574PitfBYUonF(W1BQQ3KrIiyfKH92i7KIL1QIXIDvcslKLKwgtV0zvgJKbWzY1lw9Q4YBruROKjaRqVI4oNE5fGmJVQny4DCdEZt5iZAUT91wWmkVUm1UiuspIxsImQBBBAJwrGy8amXhlLy3ihAF1QKJ7C7zjDXte5JwTCv5KKYISIY5MjsLuYsj51fGGx1ASYhO9cwaNvu)Z5)nM0CkCHXOJFOrdr3B6GQzMVnAPj3vAtpTZ67fQ77qnQ8M9C3YLbbujL(ZMCEQo5uiPNrIHT6HiYNIPUFRYhjkJG5AKzcuSj3sUdG7OII)xcgCJ5MWttoFv3xgxllwUKO1gRAPxrjT1Dt1sWkENUclpc8DXW7eDoIrwbnZ4hpcBmxd3xK)nuRuRBSiJ5QLANMgFuA9qdr8wjaIxVM(HtnJGvOvsEM2KKhAHYSNeOXcbebOIrEpIvsSAwgicPFQDjS5oz5T)oS)JAo)nD4rSSUuSyreeqpTiXgQtPFqBElX7tb4vpeE1TYUZsnvhxW)8YsHY68OFJbmupwzjqCOFLHFzrudFznq2()tohACg4o48hyfVv6WeYrwxuCBtdz8wmSYbIYcZ28Nv6KPITgNdIVb0SYJf3s4eAffqOMOjHgRabem)C96qCTnXo4UyB8ZN9ce3TZGku6cS2(5ZsTQTwk4Cmztx2U9hSueKgAuFvZ8DWTdpPfUPBMS0kteTwzsVq2uKg8mTvcmArau1R4M01ZJQdVDbxZRAcs4bTAkBerx5n4QpaCiCnZsoAWRWdPbIZJjF5BOrBHIxSqVOz0u2cex8wqJo6SkQH2OxwKmxgAammh6HhSbJW8ec0cEV06Z5xJVMnnKQilpTdmCRjJ5AHvklcjt6xMZszEmU4TS8yW620BW3dIL7HH7qAFLITIpkTYHJ0JVUTEIGc5nlqLzt6Dut1NzUV2W0xCn4labxCBfcQk0IOpP7vdTp9dUp1zYxi(SH80VzjynX5C9AneYa7AKACA0DtG023DzRMj7bnorufX5(SzjIjFEX1z1)ZlQT5CBe(8AiWAkhOlnTiwpUXxzx9DqFA2QWmKb)7Fqe0Wan6LlR793P(QXDikuMkrlsyjrym7jbJHBQDdlU7TjVcv(BudxK8XhRE2JDV6L(kILF3B(N6EPK3Wfc0EEXPeEPK)MbnoSl7e8By1CbtD96I1kQ9OESJ)bvxotHNMtsd7iHQVHJk0eyj1s4mqUVebCQapbT7O0(CU98jSMSjMr270vxwbPheuvJD9mQBdwTo3R0shKVTPH2SvDp3PDcmXrIapYVym6JUVEwTmoDEeThrggpFUAxfBtq5uxYxCDTgCtEfh1t1UzTCW7IbcqBvUbgv5PRfM3lOL3H6MtgpFS)2V9MHBVIk6X8iy(dSOx53BI6LymQ0cilGd0UpvoQQP4NshdO9f6R)an6GwvE0nTVfAGDwldvUNwerkL0sTNNoQs03RVFXByQnykxBdsEobP2dWDJ1hWg)68nLXDqxP6HZL2Q0yNuX4E)L5cYTJ2HSTmzKKTt1HC9UJ3EpQoWD(nBxOpTWz6syx21QgwLKmRFFEu0qABddmkzvSXLsQ)75ydifLKfRxbxf6zR(U6sB1zSJo8)rwymV9azlgOKWG5GkaH8I06OsXinU2g97Wjx4ZOjD4mYBSJU8W95XJSlTvtUUL2llsyNOvE)nxd7YIn3qCwKawwTSvxnTJHNRc4ONdqDPHPJeoF02KK9Zhwq4PIBKyu6cLsnzccAluK4vSem2QPKYAVjR464mETAg7Uynuq86SII5zanAiTrcdOrKHZ(t7JQk3wAlnChs9N1lU9inb(1f0)FznhDJpwajL8BJiB0InLpCPSKKp1TIucXEHXyrZ)zGos37wb(OtOqgDNCNp5W7ekAG5QklclQnD7mnPYko99LXRP7bjJKuxwuH6bsIKn8zZwgCL4hXvAnXoOdF3KcW)Z1ytRae)6nGw(w4vnz1AUYiwemb5df4INpccBUSkKeNSeZQCuCY)zdCcmpQgKaQmbU(28exiFlF14p1uKhR(U36nUhSAPKL0GzTvgAPdpXoEuqsMOuwHSDpqrcVfg2leGIHGioA8rJXZgtJi8oNaLfa(mi8I5rG70ZVLuMSmLSWwqgAI2oEjvttyYrsk3qYIUjUeWzIivjuaYzodtIlPsblWOvb3aYWCcNUGy1WNRfR0v5(4SpsqiC)vry1voLWtvaKwz274VHDrpWqvYYUMgNnnXUBQWTal9g8oFVi2gMlonLaz7vVHQUPlkKcIItgXkHMANIzPuAHB0qV5USvXXXelo8BpIgYYi6F8cE6VA3J5oV3zbVHsSV1jSeLzCg8FtYwZUb5LTUXgvlNR3nG7EFxUBKdgTq3ISxCcZELsiHn9wrZD8t61Mh8YL1cEnj0Py)oFejOnbdsIxVMD)kZLvZKJLqW5)14sS80IQ1Xtb2C2Y0B7DmQ5FrjLYOeCnULLkvfe1Wwus7Tmmtz3G9f)VcoATHLZGTSR1ARbNGzGbQ5OsJMinBekh3bsWT7i3e2o2BhlvMTcV5Q(qmQ1kcIp7BEPABL5rEVrJ0tA3ay9GKD1fyPzX8kqsBa8MnPeB7C4F5UwtaZpHrAbxzaRcWkkJJy3i3lVy2ipvsYDSoQLIz2iNbkmwgOGloaVNDMXaHezBv4Qlki8ez)(o8A3SnK)W2M2OSIF6tWOpsBnmem1d85FTyCJbtvpyAD328dhMbv8uHdAOlwZBL7B0D48bmle4BgiZZg4h0N0GGMZ8j7Pjv4kcKJxdPxiLuzEw1x5mGMAz9zvQR2V5PLqZxztCtpox)MnhoXYMPi8R6WnO9ROk6648D5(JoRRy25YBZtVo03FDVkmtfPfY)arY6P9pUPuO8VsQ9NzO(HInBVADw8d6MKRE5R43Vd(9RqVVCav8OcP58nHv9EwNNHxEgEM9O5fpDfXrLSSDC7XJ2oq71nYtYVJKvGEXy20imxJ5T)(tYRy(NqpfFQYabRvAFAbHouX1yLMvvw9x)xG4pUmfdRGFxv(GkWGxEU7kWrUvc44btiV1LXJz33y26qNOyf0B7Ab74euSohNZvFkHGJ4lQ1O)ElL(mAruR7wT3BOVKBW2B3nc5XcALZuIY(b33wpdETEYXcb7ixkGP3s3wCZ2cxUt8Q7bsCQioIoGkDqiPNrV7Q(55cU9cQ09qBKBvaHQrEGpI3oDN7PcDoBlRZPTL1ZGl(9CXf1bv0SOFz3TtJKv)Ia1SiumSCzoEerGB7xCB66gR3MXfXuzl(vXiZIDhTO64PQamdmtFEnaFGOs23ZM)5Y0pYxIo8)3LdEizqCIlTR7Gk00l0oB)MH3PaGDoNB9SxnCZXSulvaIabKmZGMAsDunjzzE6)HoAbKKglUC3Iy11HpLos2RtnHrqvMnfz0vl0WmnXMWqtOCV62PLTNKMAsnoG3J3N95GRHSUFHbkB53Qww5ZZOxL3MRSBtfTXuHXzCoqze9iyOxdSVKvPjdTfwHFgZFvSxs(scltgc2tgOXUz5TCpge8EtHajKGL0bigkQ67ttpbCuLjhU7d5DuF((YGF7WIBAMRoii0H9BF8I7yj8i7OAhzzLbB2K5zWBzG97(5KYMiEe)k5U0QesjTYDQYHuN8UJuwHRIn(wpz03D5f3hxI9nBf4)0R(1F5N(L)23V9QTx9B4Hv6Q1fLcxC(gPsOVbPlS0RJ(gHxAF8kDUcDOFlFWpaChV7NPxw5J(ECORcoFws)5VPjEP)13W06P(iHva4N6p(tdK7YjFM7Y23fa7Kx9SDd7MAaxYARkbRMN4c3EI7bk29x)h)SXM9TpvcL52fKILmVA3OvZEQGw)jki4Xp5DrLmn2KO)e3gtOPJNDCmkinwPEf7gTEYtf9SZv9KPw23UXJEQ7xxzoP3MZDJKn(ZeMSRUt5QEbVeEzVeBwRlb2)sLX4NOOdUfFla1jmtd0zcn8Z08KLGLpCmgPzXIu0LJ)YFbm9AoOTPp0XW2g)TMbUn(V(sm0T577Z7G3waSFrg(2cAZhkjh(A2g8ogywnuANy)ZEP6Ld3(kCDTWTV6MUR5G0fN5SHIE8rNDWJ99vmGqS(JMT65bTUe2Nn(ih7RwuS2ErepuCl2X2OM(F6B0LsgjoKEFmEolNTpIVWz44eewbT8YZ3(mne)KBpa2crKdWyeptlLghqdr)mBPNyZA44ZtejNF2X)b9rOc)Frg5EUrah3Bw8q3i3ECStnuRZg3JIS2hwFp(O2AN4ATtBV2PUw7S2RDMR1IdIpkr9R)iZZn93(0qHtRDmQu21nR9zzp7R8C1)rpBfi68j9AFvPpfOd92tAGYflvB(h6PZxSPtNB6KvtOC4R1Z75xUDIQCR(CStfamTSYaeWlsgh6FchhDQqVHDf0wOhIYPh3tlL1NFCpp5f(u(WOtJF78ZMDyFKt70XJg8cgn(RZCLtLmuQpz54MaeFxkzrlVGVJ6YYF2MwCHzlvcYbrQaUEAAsFhhjAEmUAoDL4IOtoAFBZvUxmz0(HMNCkRXwQUFrZwBo)4W3CYrVOVFTbZgWuh88mN225tk9op50MtUwKsky(fEYR9FT95AYRfMqW2ynUvh24ot9ohP7lrdHB8JpQPcdEsVGB80hF0AzPuNVAH3LzH8uCwh2dHq52NWCrlmXMUHpb(TNJjAMk0Pp(0AdqNdbkTV6SnZYltNQzQp3yiMP(tAZSmr4o)OPESHuYZz4uIcbP98uWVELoNovED740Jm(A0G2uxrlB0GNk96Vx7I5JsafDAmjDy7I)E(z44rkad7Sb967Pltonih)mfNlTAcAGfQrBCbjjEHdGa9zqlovqlcmMO6ThfiApGOuEvhdhQE7zFSqzb)Bg8tujV)yNFtTbhXyLIkqyemYJpUx)qbp0RVAA7MstBhg2XJpQ(8zSNpEqpgOD6XJgqftrlYISki1m(8odKAWzWw8zQfO)aLzxelbm7zRi(CFKnVF4o)bPLPPdpIIu)5AwhPrpqbsdJITPkFw4V(ht9cVFGw1SzFQg7uQ5VP3EMJtPEED3dDkW3SvP3EEC10HVcO0SrKqh1RDTqphc8ZaNz9opID97u66SgFpGshgyWh94JLUg6rABjkg6cf7zvaWr)ZC2i2YTnoJyHy9SmIImPNA9a(bl4TuoJ)jW0JW7bFJcO)WgXqTKfnG7Gb0dm6mG48ZoEuphspEff82ozGFAGVjLThRqCHeBJsOqEj3F8(ZErFVoFTN7wTAWaDPq05aMwBZBnTQtv876hyqZ1O3yqRJcl(PSdkS6WaaYM2JZMmg91Xs(9oRpFy)C(pmdOb75uPCF7KJqUYnaoslQQ6zeD1r(D3DABYgo5C8qzSJCJTsmgFCJm5xRHPtqZLUr1(7PL1jUmJdNC)891Ux)9cO(dCkZNDVa5tvnGeh28pFYaRIDEhnoGjtxRGQFsZKllno)jA4WyYF0QiH8rXZzJhEKnfiTN)oGl1TKfo1MydOotJbZHSLAAvAiFFbgknbjfwqxqF20rh6Ai10bmQTjuJsKUt6HMWJi2ILs76hNa8UArniVAw9SDnvBzMPv1XO(j63)ut1kHY8PB)cCqc8ROX)hKM1Gsptnggl4N7KDPlEo1)ZrBNtQMtxNnDl5PAi2bhnlKvNbPAWxEY(ohBlTJjtTxk6)uAMcrC9nCrth1Zq5KD6gQZ)G02Zsfu5OXutr7rQD9H7w7qeWa(VKrYAmwvSkxpDK7WafQimK31mpQ(tnzQEuRZ2MPEstG2wU0(MVLCaNSlVLCwMSlVK(ylzxEtXSoGLz6qZQe)VT75iIeImgsjoov9)z8mtmOIgMo2W4PnMdh0Gm8Uo(8fzG)Kya(eBXy5XJSIzy(nd4EiNH2aA9xe9X(PxohSh2C0zpNJ3d)FeNtAdfDHENQh2zfyPM75zWB4cbQ8jVy)CCKO6fSsaslFbAU8R9f2Rbfzf07m9kcB72QXlkSVB8O)Gl7F0HTllce6R5mbXa023wBr0e8Knq1pCCCpF5f2EVtODS2ZAJ95bA9dSD9gaEkE7)8cLbVOgQfEP199ZHjD(REyBAM4ZoE4eMxGFDUVAwocm6VbLBwdNl2rkqphD)3xQSCNjlRPAnKBP9NDOMxvVWJlUms8t4k1zH(iigOKKUBD9BKSujBbnhzjHph20wsbeymUgDue9pKBoMhsJntEwjnHsKtO(AZAQY6F0(9N8c1qqgmqNkInQ1xXBXwaMk7uQoRNA)XJ8KMBLSz6HRl47V)rwqc1RyMoM0HdxiojlBzZ9st4mqtlkPvT0wQpL7Ls7hzyPwB(wiB9ZUzqbtEpys5qCaD9I(MTUQT8De(q58ZozWaRaEB1YAGEQA3VxlhOi7mkHyKASBoHtE2O))Xt9k3KzzfkveevP2pzRCdSNLPX92Rt1c1SmI)jzsw5Iq6S0KQt0QZMnILju1HELRDSsIW47iQ13SrTs4cOa0XzNJtaw3xSddmQUlKAPJ5DKRX9TMkLVaFPPi5ZzGdptFMtnQjyhSa0oDTFPGkV5AG1xI70WDYphBt6k7BtlDy7cNE2jk(qAIUUstFJY6HtmEDjKlYeI24CkKJ3ZcdXNpEuGgQlun(MYCz)R9uxQRNSUpDSKzJGKVgtmUN)sg(dPDLcef2I)BCJAQviK1ovAMmyj(a4pPx)aA3BqPzRrRpi)o1HgEGK6whoGDpVmCeG3X28BeMWIepVnTSi3IstMZ7qngasLFyC6SFNL3i8bm0lCAerMUqHI5S)EA1m6t53DfTwr)LnTNo7bIw7Hbhk1WbHCMhkiHNN2k21nMURiCGxhI6nXmYcfaEfTd1RIxWpWm7vDbaHrUa0t0CEtxRdw47Frm9cnxrnER1a7dh0cfN1gfnAP(d48dhRIZ8(qG7Dy7Z19F2BqEUPZ2DaIDJatcRIliamZzB5FyFlDNwSs3P1ZGR1ojYK5VjxdSglXFI6Mz0sTsdz659K5Rh6m4sqff4F5VJ8d4BXpyfR)hPoKWCduRMJQGSAh0I5OBtADB9)LX53cr60ZJ1bSFcDMYSZf38YwvzoqJqonS0VTYzjnc7eI2FILcD48uL1X9FHA4ESfH4QuMQK5hAqqi(oy7v3kZcMPl(49qdtDJcJcJ9ZXXTghQ3C(y37pEJIds9ZJUNdfnjg1pUcmb(YAgtvgA)V5ECiVeLwn)bhFLYogMPl7JVFBZL)rOPw9sarBLCql(B)itWq2QMfOU9(OyfTHqyb7WVajSxUJc9bOG99tcLn5e8tRlYz226wbhoBMtiuDDT8MgKo3Njf14Hl1sDdtD8TXawvfHfTgt)a9kSaAKTLZeF501HJVQ3KjrdPeOXdKntvBpIKj3ZuyHQDexbjVAJy(vqVmJjXLLpiyOIzxhuopsU8MGYsgi19tKdAn4LykEo3n2cVh74fdlOlMNo(e)YFb3b7qAZD6qwWp364dyJy80WudhUxSey4bpMlO9XXECnfaph1BCx9DjxbFrDe44hY(IjXbnSWtWGqtZSrNZVpW05J1L)F)]] )
|
|
|
|
|
|
end
|
|
|