-- RogueAssassination.lua -- June 2018 local addon, ns = ... local Hekili = _G[ addon ] local class = Hekili.Class local state = Hekili.State local PTR = ns.PTR local FindUnitBuffByID, FindUnitDebuffByID = ns.FindUnitBuffByID, ns.FindUnitDebuffByID local IterateTargets, ActorHasDebuff = ns.iterateTargets, ns.actorHasDebuff local orderedPairs = ns.orderedPairs local format = string.format -- Conduits -- [-] lethal_poisons -- [-] maim_mangle -- [-] poisoned_katar -- [x] wellplaced_steel -- Covenant -- [-] reverberation -- [-] slaughter_scars -- [-] sudden_fractures -- [-] septic_shock -- Endurance -- [x] cloaked_in_shadows -- [x] nimble_fingers -- may need to double check which reductions come first. -- [-] recuperator -- Finesse -- [x] fade_to_nothing -- [x] prepared_for_all -- [x] quick_decisions -- [x] rushed_setup if UnitClassBase( 'player' ) == 'ROGUE' then local spec = Hekili:NewSpecialization( 259 ) spec:RegisterResource( Enum.PowerType.ComboPoints ) spec:RegisterResource( Enum.PowerType.Energy, { 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, }, garrote_vim = { aura = "garrote", debuff = true, last = function () local app = state.debuff.garrote.last_tick local exp = state.debuff.garrote.expires local tick = state.debuff.garrote.tick_time local t = state.query_time return min( exp, app + ( floor( ( t - app ) / tick ) * tick ) ) end, stop = function () return state.debuff.wound_poison_dot.down and state.debuff.deadly_poison_dot.down end, interval = function () return state.debuff.garrote.tick_time end, value = 8 }, internal_bleeding_vim = { aura = "internal_bleeding", debuff = true, last = function () local app = state.debuff.internal_bleeding.last_tick local exp = state.debuff.internal_bleeding.expires local tick = state.debuff.internal_bleeding.tick_time local t = state.query_time return min( exp, app + ( floor( ( t - app ) / tick ) * tick ) ) end, stop = function () return state.debuff.wound_poison_dot.down and state.debuff.deadly_poison_dot.down end, interval = function () return state.debuff.internal_bleeding.tick_time end, value = 8 }, rupture_vim = { aura = "rupture", debuff = true, last = function () local app = state.debuff.rupture.last_tick local exp = state.debuff.rupture.expires local tick = state.debuff.rupture.tick_time local t = state.query_time return min( exp, app + ( floor( ( t - app ) / tick ) * tick ) ) end, stop = function () return state.debuff.wound_poison_dot.down and state.debuff.deadly_poison_dot.down end, interval = function () return state.debuff.rupture.tick_time end, value = 8 }, crimson_tempest_vim = { aura = "crimson_tempest", debuff = true, last = function () local app = state.debuff.crimson_tempest.last_tick local exp = state.debuff.crimson_tempest.expires local tick = state.debuff.crimson_tempest.tick_time local t = state.query_time return min( exp, app + ( floor( ( t - app ) / tick ) * tick ) ) end, stop = function () return state.debuff.wound_poison_dot.down and state.debuff.deadly_poison_dot.down end, interval = function () return state.debuff.crimson_tempest.tick_time end, value = 8 }, nothing_personal = { aura = "nothing_personal_regen", last = function () local app = state.buff.nothing_personal_regen.applied local exp = state.buff.nothing_personal_regen.expires local tick = state.buff.nothing_personal_regen.tick_time local t = state.query_time return min( exp, app + ( floor( ( t - app ) / tick ) * tick ) ) end, stop = function () return state.buff.nothing_personal_regen.down end, interval = function () return state.buff.nothing_personal_regen.tick_time end, value = 4 } } ) -- Talents spec:RegisterTalents( { master_poisoner = 22337, -- 196864 elaborate_planning = 22338, -- 193640 blindside = 22339, -- 111240 nightstalker = 22331, -- 14062 subterfuge = 22332, -- 108208 master_assassin = 23022, -- 255989 vigor = 19239, -- 14983 deeper_stratagem = 19240, -- 193531 marked_for_death = 19241, -- 137619 leeching_poison = 22340, -- 280716 cheat_death = 22122, -- 31230 elusiveness = 22123, -- 79008 internal_bleeding = 19245, -- 154904 iron_wire = 23037, -- 196861 prey_on_the_weak = 22115, -- 131511 venom_rush = 22343, -- 152152 alacrity = 23015, -- 193539 exsanguinate = 22344, -- 200806 poison_bomb = 21186, -- 255544 hidden_blades = 22133, -- 270061 crimson_tempest = 23174, -- 121411 } ) -- PvP Talents spec:RegisterPvpTalents( { creeping_venom = 141, -- 354895 death_from_above = 3479, -- 269513 dismantle = 5405, -- 207777 flying_daggers = 144, -- 198128 hemotoxin = 830, -- 354124 intent_to_kill = 130, -- 197007 maneuverability = 3448, -- 197000 smoke_bomb = 3480, -- 212182 system_shock = 147, -- 198145 thick_as_thieves = 5408, -- 221622 } ) 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 ) 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 } ) ) spec:RegisterStateExpr( "master_assassin_remains", function () if not ( talent.master_assassin.enabled or legendary.mark_of_the_master_assassin.enabled ) then return 0 end if stealthed.mantle then return cooldown.global_cooldown.remains + ( legendary.mark_of_the_master_assassin.enabled and 4 or 3 ) elseif buff.master_assassin_any.up then return buff.master_assassin_any.remains end return 0 end ) local stealth_dropped = 0 local function isStealthed() return ( GetPlayerAuraBySpellID( 1784 ) or GetPlayerAuraBySpellID( 115191 ) or GetPlayerAuraBySpellID( 115192 ) or GetPlayerAuraBySpellID( 11327 ) or GetTime() - stealth_dropped < 0.2 ) end local calculate_multiplier = setfenv( function( spellID ) local mult = 1 local stealth = isStealthed() if stealth then if talent.nightstalker.enabled then mult = mult * 1.5 end -- Garrote. if talent.subterfuge.enabled and spellID == 703 then mult = mult * 1.8 end end return mult end, state ) -- Bleed Modifiers -- 9.2 adds a set bonus that is essentially a second Exsanguinate. -- We need to be able to tell that an aura has been Exsanguinated via talent *and* Exsanguinated by Vendetta and 4pc. local tier28_vendetta_spells = { garrote = 1, rupture = 1, crimson_tempest = 1, internal_bleeding = 1, deadly_poison_dot = 1, sepsis = 1, serrated_bone_spike = 1, } local tracked_bleeds = {} Hekili.TB = tracked_bleeds local function NewBleed( key, spellID ) tracked_bleeds[ key ] = { id = spellID, exsanguinate = {}, vendetta = {}, rate = {}, last_tick = {}, haste = {} } tracked_bleeds[ spellID ] = tracked_bleeds[ key ] end local function ApplyBleed( key, target, exsanguinate, vendetta ) local bleed = tracked_bleeds[ key ] bleed.rate[ target ] = 1 + ( exsanguinate and 1 or 0 ) + ( vendetta and 1 or 0 ) bleed.last_tick[ target ] = GetTime() bleed.exsanguinate[ target ] = exsanguinate bleed.vendetta[ target ] = vendetta bleed.haste[ target ] = 100 + GetHaste() end local function UpdateBleed( key, target, exsanguinate, vendetta ) local bleed = tracked_bleeds[ key ] if not bleed.rate[ target ] then return end if exsanguinate and not bleed.exsanguinate[ target ] then bleed.rate[ target ] = bleed.rate[ target ] + 1 bleed.exsanguinate[ target ] = true end if vendetta and not bleed.vendetta[ target ] then bleed.rate[ target ] = bleed.rate[ target ] + 1 bleed.vendetta[ target ] = true end bleed.haste[ target ] = 100 + GetHaste() end local function UpdateBleedTick( key, target, time ) local bleed = tracked_bleeds[ key ] if not bleed.rate[ target ] then return end bleed.last_tick[ target ] = time or GetTime() end local function RemoveBleed( key, target ) local bleed = tracked_bleeds[ key ] bleed.rate[ target ] = nil bleed.last_tick[ target ] = nil bleed.exsanguinate[ target ] = nil bleed.vendetta[ target ] = nil bleed.haste[ target ] = nil end local function GetExsanguinateRate( aura, target ) return tracked_bleeds[ aura ] and tracked_bleeds[ aura ].rate[ target ] or 1 end NewBleed( "garrote", 703 ) NewBleed( "rupture", 1943 ) NewBleed( "crimson_tempest", 121411 ) NewBleed( "internal_bleeding", 154904 ) NewBleed( "deadly_poison_dot", 2823 ) NewBleed( "sepsis", 328305 ) NewBleed( "serrated_bone_spike", 324073 ) local application_events = { SPELL_AURA_APPLIED = true, SPELL_AURA_APPLIED_DOSE = true, SPELL_AURA_REFRESH = true, } local removal_events = { SPELL_AURA_REMOVED = true, SPELL_AURA_BROKEN = true, SPELL_AURA_BROKEN_SPELL = true, } local stealth_spells = { [1784 ] = true, [115191] = true, } local tick_events = { SPELL_PERIODIC_DAMAGE = true, } local death_events = { UNIT_DIED = true, UNIT_DESTROYED = true, UNIT_DISSIPATES = true, PARTY_KILL = true, SPELL_INSTAKILL = true, } -- We need to know if a target has Vendetta on it when we apply a bleed, for Tier 28 purposes. local vendetta_info = {} spec:RegisterCombatLogEvent( function( _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName ) if sourceGUID == state.GUID then if removal_events[ subtype ] then if stealth_spells[ spellID ] then stealth_dropped = GetTime() return end end if application_events[ subtype ] then if spellID == 79140 then -- TODO: Review for Vendetta duration extensions. vendetta_info[ destGUID ] = GetTime() + 20 -- We applied Vendetta; if we have Tier28 2pc, we have to modify the applied aura. if state.set_bonus.tier28_4pc > 0 then UpdateBleed( "garrote", destGUID, nil, true ) UpdateBleed( "rupture", destGUID, nil, true ) UpdateBleed( "crimson_tempest", destGUID, nil, true ) UpdateBleed( "internal_bleeding", destGUID, nil, true ) UpdateBleed( "deadly_poison_dot", destGUID, nil, true ) UpdateBleed( "sepsis", destGUID, nil, true ) UpdateBleed( "serrated_bone_spike", destGUID, nil, true ) end return end if tracked_bleeds[ spellID ] then -- TODO: Modernize basic debuff tracking and snapshotting. ns.saveDebuffModifier( spellID, calculate_multiplier( spellID ) ) ns.trackDebuff( spellID, destGUID, GetTime(), true ) ApplyBleed( spellID, destGUID, nil, state.set_bonus.tier28_4pc > 0 and vendetta_info[ destGUID ] and vendetta_info[ destGUID ] > GetTime() ) return end return end if tracked_bleeds[ spellID ] then if tick_events[ subtype ] then UpdateBleedTick( spellID, destGUID, GetTime() ) return elseif removal_events[ subtype ] then RemoveBleed( spellID, destGUID ) return end end -- Exsanguinate was used. if subtype == "SPELL_CAST_SUCCESS" and spellID == 200806 then UpdateBleed( "garrote", destGUID, true, nil ) UpdateBleed( "rupture", destGUID, true, nil ) UpdateBleed( "crimson_tempest", destGUID, true, nil ) UpdateBleed( "internal_bleeding", destGUID, true, nil ) return end end if death_events[ subtype ] then --[[ TODO: Deal with annoying Training Dummy resets. RemoveBleed( "garrote", destGUID ) RemoveBleed( "rupture", destGUID ) RemoveBleed( "crimson_tempest", destGUID ) RemoveBleed( "internal_bleeding", destGUID ) RemoveBleed( "deadly_poison_dot", destGUID ) RemoveBleed( "sepsis", destGUID ) RemoveBleed( "serrated_bone_spike", destGUID ) ]] vendetta_info[ destGUID ] = nil end end, false ) spec:RegisterHook( "UNIT_ELIMINATED", function( guid ) vendetta_info[ guid ] = nil end ) local energySpent = 0 local ENERGY = Enum.PowerType.Energy local lastEnergy = -1 spec:RegisterUnitEvent( "UNIT_POWER_FREQUENT", "player", nil, function( event, unit, powerType ) if powerType == "ENERGY" then local current = UnitPower( "player", ENERGY ) if current < lastEnergy then energySpent = ( energySpent + lastEnergy - current ) % 30 end lastEnergy = current 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:RegisterStateExpr( "energy_spent", function () return energySpent end ) spec:RegisterHook( "spend", function( amt, resource ) if legendary.duskwalkers_patch.enabled and cooldown.vendetta.remains > 0 and resource == "energy" and amt > 0 then energy_spent = energy_spent + amt local reduction = floor( energy_spent / 30 ) energy_spent = energy_spent % 30 if reduction > 0 then reduceCooldown( "vendetta", reduction ) end end if resource == "combo_points" and buff.flagellation_buff.up then if legendary.obedience.enabled then reduceCooldown( "flagellation", amt ) end if debuff.flagellation.up then stat.mod_haste_pct = stat.mod_haste_pct + amt end end end ) spec:RegisterStateExpr( 'persistent_multiplier', function () local mult = 1 if not this_action then return mult end local stealth = buff.stealth.up or buff.subterfuge.up if stealth then if talent.nightstalker.enabled then mult = mult * 2 end if talent.subterfuge.enabled and this_action == "garrote" then mult = mult * 1.8 end end return mult end ) local exsanguinated_spells = { garrote = "garrote", kidney_shot = "internal_bleeding", rupture = "rupture", crimson_tempest = "crimson_tempest", deadly_poison = "deadly_poison_dot", sepsis = "sepsis", serrated_bone_spike = "serrated_bone_spike", } -- Auras that aren't impacted by Tier 28. local true_exsanguinated = { "garrote", "internal_bleeding", "rupture", "crimson_tempest", } spec:RegisterStateExpr( "exsanguinated", function () local aura = this_action and exsanguinated_spells[ this_action ] aura = aura and debuff[ aura ] if not aura or not aura.up then return false end return aura.exsanguinated_rate > 1 end ) spec:RegisterStateExpr( "will_lose_exsanguinate", function () local aura = this_action and exsanguinated_spells[ this_action ] aura = aura and debuff[ aura ] if not aura or not aura.up then return false end return aura.exsanguinated_rate > ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 2 or 1 ) end ) spec:RegisterStateExpr( "exsanguinated_rate", function () local aura = this_action and exsanguinated_spells[ this_action ] aura = aura and debuff[ aura ] if not aura or not aura.up then return 1 end return aura.exsanguinated_rate end ) -- Enemies with either Deadly Poison or Wound Poison applied. spec:RegisterStateExpr( "poisoned_enemies", function () return ns.countUnitsWithDebuffs( "deadly_poison_dot", "wound_poison_dot", "crippling_poison_dot" ) end ) spec:RegisterStateExpr( "poison_remains", function () return debuff.lethal_poison.remains end ) local valid_bleeds = { "garrote", "internal_bleeding", "rupture", "crimson_tempest", "mutilated_flesh", "serrated_bone_spike" } -- Count of bleeds on targets. spec:RegisterStateExpr( "bleeds", function () local n = 0 for _, aura in pairs( valid_bleeds ) do if debuff[ aura ].up then n = n + 1 end end return n end ) -- Count of bleeds on all poisoned (Deadly/Wound) targets. spec:RegisterStateExpr( "poisoned_bleeds", function () return ns.conditionalDebuffCount( "deadly_poison_dot", "wound_poison_dot", "garrote", "internal_bleeding", "rupture" ) end ) spec:RegisterStateExpr( "ss_buffed", function () return false end ) spec:RegisterStateExpr( "non_ss_buffed_targets", function () return active_enemies --[[ local count = ( debuff.garrote.down or not debuff.garrote.exsanguinated ) and 1 or 0 for guid, counted in ns.iterateTargets() do if guid ~= target.unit and counted and ( not ns.actorHasDebuff( guid, 703 ) or not ssG[ guid ] ) then count = count + 1 end end return count ]] end ) spec:RegisterStateExpr( "ss_buffed_targets_above_pandemic", function () --[[ if not debuff.garrote.refreshable and debuff.garrote.ss_buffed then return 1 end ]] return 0 end ) spec:RegisterStateExpr( "pmultiplier", function () if not this_action then return 0 end local a = class.abilities[ this_action ] if not a then return 0 end local aura = a.aura or this_action if not aura then return 0 end if debuff[ aura ] and debuff[ aura ].up then return debuff[ aura ].pmultiplier or 1 end return 0 end ) spec:RegisterStateExpr( "priority_rotation", function () return settings.priority_rotation end ) local ExpireSepsis = setfenv( function () applyBuff( "sepsis_buff" ) if legendary.toxic_onslaught.enabled then applyBuff( "adrenaline_rush", 10 ) applyBuff( "shadow_blades", 10 ) end end, state ) -- Tier 28 spec:RegisterSetBonuses( "tier28_2pc", 364667, "tier28_4pc", 363591 ) -- 2-Set - Grudge Match - Shiv causes enemies within 15 yards to take 40% increased damage from your Poisons and Bleeds for 9 seconds. -- 4-Set - Grudge Match - Vendetta causes Poisons and Bleeds on your target to expire 100% faster. spec:RegisterAuras( { grudge_match = { id = 264668, duration = 9, max_stack = 1, }, } ) local ExpireVendetta = setfenv( function () if debuff.serrated_bone_spike.up and debuff.serrated_bone_spike.vendetta_exsg then debuff.serrated_bone_spike.vendetta_exsg = false debuff.serrated_bone_spike.exsanguinated_rate = max( 1, debuff.serrated_bone_spike.exsanguinated_rate - 1 ) end end, state ) spec:RegisterHook( "reset_precast", function () Hekili.Exsg = "Bleed Snapshots Remains Multip. RateMod Exsang. Tier-28\n" for _, aura in orderedPairs( exsanguinated_spells ) do local d = debuff[ aura ] d.pmultiplier = nil d.exsanguinated_rate = nil d.vendetta_exsg = nil d.exsanguinated = nil Hekili.Exsg = format( "%s%-20s %7.2f %7.2f %7.2f %7s %7s\n", Hekili.Exsg, aura, d.remains, d.pmultiplier, d.exsanguinated_rate, d.exsanguinated and "true" or "false", d.vendetta_exsg and "true" or "false" ) end if Hekili.ActiveDebug then Hekili:Debug( Hekili.Exsg ) end if debuff.sepsis.up then state:QueueAuraExpiration( "sepsis", ExpireSepsis, debuff.sepsis.expires ) end for guid, expiration in pairs( vendetta_info ) do if expiration < now then vendetta_info[ guid ] = nil if tracked_bleeds.serrated_bone_spike.vendetta[ guid ] and tracked_bleeds.serrated_bone_spike.vendetta[ guid ] > 1 then tracked_bleeds.serrated_bone_spike.vendetta[ guid ] = tracked_bleeds.serrated_bone_spike.vendetta[ guid ] - 1 end end end if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then state:QueueAuraExpiration( "vendetta", ExpireVendetta, debuff.vendetta.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 ) -- 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.master_assassin.enabled then applyBuff( "master_assassin" ) end if talent.subterfuge.enabled 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( "shadowmeld" ) removeBuff( "vanish" ) 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 ) -- Auras spec:RegisterAuras( { blind = { id = 2094, duration = 60, max_stack = 1, }, blindside = { id = 121153, duration = 10, max_stack = 1, }, cheap_shot = { id = 1833, duration = 4, max_stack = 1, }, cloak_of_shadows = { id = 31224, duration = 5, max_stack = 1, }, crimson_tempest = { id = 121411, duration = function () return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * ( 2 * ( 1 + effective_combo_points ) ) end, max_stack = 1, meta = { exsanguinated = function( t ) return t.up and tracked_bleeds.crimson_tempest.exsanguinate[ target.unit ] or false end, vendetta_exsg = function( t ) return t.up and tracked_bleeds.crimson_tempest.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.crimson_tempest.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.crimson_tempest.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) if t.down then return haste * 2 end local hasteMod = tracked_bleeds.crimson_tempest.haste[ target.unit ] hasteMod = 2 * ( hasteMod and ( 100 / hasteMod ) or haste ) return hasteMod / t.exsanguinated_rate end, haste_pct = function( t ) return ( 100 / haste ) end, haste_pct_next_tick = function( t ) return t.up and ( tracked_bleeds.crimson_tempest.haste[ target.unit ] or ( 100 / haste ) ) or 0 end, }, }, 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, }, deadly_poison = { id = 2823, duration = 3600, max_stack = 1, }, deadly_poison_dot = { id = 2818, duration = function () return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * ( 12 * haste ) end, max_stack = 1, exsanguinated = false, meta = { vendetta_exsg = function( t ) return t.up and tracked_bleeds.deadly_poison_dot.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.deadly_poison_dot.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.deadly_poison_dot.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) if t.down then return haste * 2 end local hasteMod = tracked_bleeds.deadly_poison_dot.haste[ target.unit ] hasteMod = 2 * ( hasteMod and ( 100 / hasteMod ) or haste ) return hasteMod / t.exsanguinated_rate end, haste_pct = function( t ) return ( 100 / haste ) end, haste_pct_next_tick = function( t ) return t.up and ( tracked_bleeds.deadly_poison_dot.haste[ target.unit ] or ( 100 / haste ) ) or 0 end, }, }, elaborate_planning = { id = 193641, duration = 4, max_stack = 1, }, envenom = { id = 32645, duration = function () return ( 1 + effective_combo_points ) end, type = "Poison", max_stack = 1, }, evasion = { id = 5277, duration = 10, max_stack = 1, }, feint = { id = 1966, duration = 6, max_stack = 1, }, fleet_footed = { id = 31209, }, garrote = { id = 703, duration = function() return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * 18 end, max_stack = 1, ss_buffed = false, meta = { duration = function( t ) return t.up and ( 18 * haste / t.exsanguinated_rate ) or class.auras.garrote.duration end, exsanguinated = function( t ) return t.up and tracked_bleeds.garrote.exsanguinate[ target.unit ] or false end, vendetta_exsg = function( t ) return t.up and tracked_bleeds.garrote.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.garrote.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.garrote.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) if t.down then return haste * 2 end local hasteMod = tracked_bleeds.garrote.haste[ target.unit ] hasteMod = 2 * ( hasteMod and ( 100 / hasteMod ) or haste ) return hasteMod / t.exsanguinated_rate end, haste_pct = function( t ) return ( 100 / haste ) end, haste_pct_next_tick = function( t ) return t.up and ( tracked_bleeds.garrote.haste[ target.unit ] or ( 100 / haste ) ) or 0 end, }, }, garrote_silence = { id = 1330, duration = function () return talent.iron_wire.enabled and 6 or 3 end, max_stack = 1, }, hidden_blades = { id = 270070, duration = 3600, max_stack = 20, }, internal_bleeding = { id = 154953, duration = function () return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * 6 end, max_stack = 1, meta = { exsanguinated = function( t ) return t.up and tracked_bleeds.internal_bleeding.exsanguinate[ target.unit ] or false end, vendetta_exsg = function( t ) return t.up and tracked_bleeds.internal_bleeding.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.internal_bleeding.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.internal_bleeding.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) if t.down then return haste * 2 end local hasteMod = tracked_bleeds.internal_bleeding.haste[ target.unit ] hasteMod = 2 * ( hasteMod and ( 100 / hasteMod ) or haste ) return hasteMod / t.exsanguinated_rate end, haste_pct = function( t ) return ( 100 / haste ) end, haste_pct_next_tick = function( t ) return t.up and ( tracked_bleeds.internal_bleeding.haste[ target.unit ] or ( 100 / haste ) ) or 0 end, }, }, iron_wire = { id = 256148, duration = 8, 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_assassin = { id = 256735, duration = 3, max_stack = 1, }, prey_on_the_weak = { id = 255909, duration = 6, max_stack = 1, }, rupture = { id = 1943, duration = function () return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * 4 * ( 1 + effective_combo_points ) end, tick_time = function () return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * ( debuff.rupture.exsanguinated and haste or ( 2 * haste ) ) end, max_stack = 1, meta = { exsanguinated = function( t ) return t.up and tracked_bleeds.rupture.exsanguinate[ target.unit ] or false end, vendetta_exsg = function( t ) return t.up and tracked_bleeds.rupture.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.rupture.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.rupture.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) if t.down then return haste * 2 end local hasteMod = tracked_bleeds.rupture.haste[ target.unit ] hasteMod = 2 * ( hasteMod and ( 100 / hasteMod ) or haste ) return hasteMod / t.exsanguinated_rate end, haste_pct = function( t ) return ( 100 / haste ) end, haste_pct_next_tick = function( t ) return t.up and ( tracked_bleeds.rupture.haste[ target.unit ] or ( 100 / haste ) ) or 0 end, }, }, shadowstep = { id = 36554, duration = 2, max_stack = 1, }, shroud_of_concealment = { id = 114018, duration = 15, 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, }, tricks_of_the_trade = { id = 57934, duration = 30, max_stack = 1, }, vanish = { id = 11327, duration = 3, max_stack = 1, }, vendetta = { id = 79140, duration = 20, max_stack = 1, }, vendetta_regen = { name = "Vendetta Regen", duration = 3, max_stack = 1, generate = function () local cast = action.vendetta.lastCast or 0 local up = cast + 3 < query_time local vr = buff.vendetta_regen if up then vr.count = 1 vr.expires = cast + 3 vr.applied = cast vr.caster = "player" return end vr.count = 0 vr.expires = 0 vr.applied = 0 vr.caster = "nobody" end, }, venomous_wounds = { id = 79134, }, wound_poison = { id = 8679, duration = 3600, max_stack = 1, }, wound_poison_dot = { id = 8680, duration = 12, max_stack = 1, no_ticks = true, }, lethal_poison = { alias = { "deadly_poison", "wound_poison", "instant_poison" }, aliasMode = "first", aliasType = "buff", duration = 3600 }, nonlethal_poison = { alias = { "crippling_poison", "numbing_poison" }, aliasMode = "first", aliasType = "buff", duration = 3600 }, -- Azerite Powers nothing_personal = { id = 286581, duration = 20, tick_time = 2, max_stack = 1, }, nothing_personal_regen = { id = 289467, duration = 20, tick_time = 2, max_stack = 1, }, scent_of_blood = { id = 277731, duration = 24, }, sharpened_blades = { id = 272916, duration = 20, max_stack = 30 }, -- PvP Talents creeping_venom = { id = 198097, duration = 4, max_stack = 18, }, system_shock = { id = 198222, duration = 2, }, -- Legendaries bloodfang = { id = 23581, duration = 6, max_stack = 1 }, master_assassins_mark = { id = 340094, duration = 4, max_stack = 1 }, master_assassin_any = { alias = { "master_assassin", "master_assassins_mark" }, aliasMode = "longest", aliasType = "buff", duration = function () return legendary.mark_of_the_master_assassin.enabled and 4 or 3 end, } } ) -- Abilities spec:RegisterAbilities( { ambush = { id = 8676, cast = 0, cooldown = 0, gcd = "spell", spend = function () return buff.blindside.up and 0 or 50 end, spendType = "energy", startsCombat = true, texture = 132282, usable = function () return stealthed.all or buff.blindside.up or buff.sepsis_buff.up, "requires stealth or blindside or sepsis proc" end, handler = function () gain( 2, "combo_points" ) if buff.sepsis_buff.up then removeBuff( "sepsis_buff" ) else removeBuff( "blindside" ) end end, }, blind = { id = 2094, cast = 0, cooldown = 120, gcd = "spell", startsCombat = true, texture = 136175, handler = function () applyDebuff( "target", "blind" ) end, }, cheap_shot = { id = 1833, cast = 0, cooldown = 0, gcd = "spell", spend = function () return 40 * ( 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", "cheap_shot" ) gain( 1, "combo_points" ) if buff.sepsis_buff.up then removeBuff( "sepsis_buff" ) end if talent.prey_on_the_weak.enabled then applyDebuff( "target", "prey_on_the_weak" ) end end, }, cloak_of_shadows = { id = 31224, cast = 0, cooldown = 120, gcd = "spell", toggle = "defensives", startsCombat = false, texture = 136177, handler = function () applyBuff( "cloak_of_shadows" ) end, }, crimson_tempest = { id = 121411, cast = 0, cooldown = 0, gcd = "spell", spend = 35, spendType = "energy", startsCombat = true, texture = 464079, talent = "crimson_tempest", aura = "crimson_tempest", cycle = "crimson_tempest", usable = function () return combo_points.current > 0 end, handler = function () applyDebuff( "target", "crimson_tempest", 2 + ( effective_combo_points * 2 ) ) debuff.crimson_tempest.pmultiplier = persistent_multiplier debuff.crimson_tempest.exsanguinated_rate = 1 debuff.crimson_tempest.exsanguinated = false if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then debuff.crimson_tempest.exsanguinated_rate = 2 debuff.crimson_tempest.vendetta_exsg = true end removeBuff( "echoing_reprimand_" .. combo_points.current ) spend( combo_points.current, "combo_points" ) if talent.elaborate_planning.enabled then applyBuff( "elaborate_planning" ) end end, }, crimson_vial = { id = 185311, cast = 0, cooldown = 30, gcd = "spell", spend = function () return 20 + conduit.nimble_fingers.mod end, spendType = "energy", startsCombat = false, texture = 1373904, toggle = "defensives", handler = function () applyBuff( "crimson_vial" ) end, }, crippling_poison = { id = 3408, cast = 1.5, cooldown = 0, gcd = "spell", startsCombat = false, essential = true, texture = 132274, readyTime = function () return buff.nonlethal_poison.remains - 120 end, handler = function () applyBuff( "crippling_poison" ) end, }, deadly_poison = { id = 2823, cast = 0, cooldown = 0, gcd = "spell", startsCombat = false, essential = true, texture = 132290, readyTime = function () return buff.lethal_poison.remains - 120 end, handler = function () applyBuff( "deadly_poison" ) end, }, distract = { id = 1725, cast = 0, cooldown = 30, gcd = "spell", spend = function () return 30 * ( 1 + conduit.rushed_setup.mod * 0.01 ) end, spendType = "energy", startsCombat = false, texture = 132289, handler = function () end, }, envenom = { id = 32645, cast = 0, cooldown = 0, gcd = "spell", spend = 35, spendType = "energy", startsCombat = true, texture = 132287, usable = function () return combo_points.current > 0, "requires combo_points" end, handler = function () if pvptalent.system_shock.enabled then if combo_points.current >= 5 and debuff.garrote.up and debuff.rupture.up and ( debuff.deadly_poison_dot.up or debuff.wound_poison_dot.up ) then applyDebuff( "target", "system_shock", 2 ) end end if pvptalent.creeping_venom.enabled then applyDebuff( "target", "creeping_venom" ) end if level > 55 and buff.slice_and_dice.up then buff.slice_and_dice.expires = buff.slice_and_dice.expires + combo_points.current * 3 end applyBuff( "envenom", 1 + effective_combo_points ) removeBuff( "echoing_reprimand_" .. combo_points.current ) spend( combo_points.current, "combo_points" ) if talent.elaborate_planning.enabled then applyBuff( "elaborate_planning" ) end end, }, evasion = { id = 5277, cast = 0, cooldown = 120, gcd = "spell", startsCombat = false, texture = 136205, handler = function () applyBuff( "evasion" ) end, }, exsanguinate = { id = 200806, cast = 0, cooldown = 45, gcd = "spell", spend = 25, spendType = "energy", startsCombat = true, texture = 538040, talent = "exsanguinate", handler = function () local rate for i, aura in ipairs( true_exsanguinated ) do local deb = debuff[ aura ] if deb.up and not deb.exsanguinated then deb.exsanguinated = true rate = deb.exsanguinated_rate deb.exsanguinated_rate = deb.exsanguinated_rate + 1 deb.expires = query_time + ( deb.remains * rate / deb.exsanguinated_rate ) deb.duration = deb.expires - deb.applied end end end, }, fan_of_knives = { id = 51723, cast = 0, cooldown = 0, gcd = "spell", spend = 35, spendType = "energy", startsCombat = true, texture = 236273, cycle = function () return buff.deadly_poison.up and "deadly_poison" or nil end, handler = function () gain( 1, "combo_points" ) removeBuff( "hidden_blades" ) if buff.deadly_poison.up then applyDebuff( "target", "deadly_poison" ) active_dot.deadly_poison = min( active_enemies, active_dot.deadly_poison + 8 ) end end, }, feint = { id = 1966, cast = 0, cooldown = 15, gcd = "spell", spend = function () return 35 + conduit.nimble_fingers.mod end, spendType = "energy", startsCombat = false, texture = 132294, handler = function () applyBuff( "feint" ) end, }, garrote = { id = 703, cast = 0, cooldown = function () return ( talent.subterfuge.enabled and ( buff.stealth.up or buff.subterfuge.up ) ) and 0 or 6 end, gcd = "spell", spend = 45, spendType = "energy", startsCombat = true, texture = 132297, aura = "garrote", cycle = "garrote", handler = function () applyDebuff( "target", "garrote" ) debuff.garrote.pmultiplier = persistent_multiplier debuff.garrote.exsanguinated_rate = 1 debuff.garrote.exsanguinated = false if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then debuff.garrote.exsanguinated_rate = 2 debuff.garrote.vendetta_exsg = true end gain( 1, "combo_points" ) if stealthed.rogue then if level > 45 then applyDebuff( "target", "garrote_silence" ) end if talent.iron_wire.enabled then applyDebuff( "target", "iron_wire" ) end if azerite.shrouded_suffocation.enabled then gain( 2, "combo_points" ) debuff.garrote.ss_buffed = true end end end, }, kick = { id = 1766, cast = 0, cooldown = 15, gcd = "off", startsCombat = true, texture = 132219, toggle = "interrupts", interrupt = true, 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 * ( 1 + conduit.rushed_setup.mod * 0.01 ) end, spendType = "energy", startsCombat = true, texture = 132298, aura = "internal_bleeding", cycle = "internal_bleeding", 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 if talent.internal_bleeding.enabled then applyDebuff( "target", "internal_bleeding" ) debuff.internal_bleeding.pmultiplier = persistent_multiplier debuff.internal_bleeding.exsanguinated = false debuff.internal_bleeding.exsanguinated_rate = 1 if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then debuff.internal_bleeding.exsanguinated_rate = 2 debuff.internal_bleeding.vendetta_exsg = true end end applyDebuff( "target", "kidney_shot", 1 + combo_points.current ) spend( combo_points.current, "combo_points" ) if talent.elaborate_planning.enabled then applyBuff( "elaborate_planning" ) end end, }, marked_for_death = { id = 137619, cast = 0, cooldown = 30, gcd = "off", -- toggle = "cooldowns", 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" ) end, }, mutilate = { id = 1329, cast = 0, cooldown = 0, gcd = "spell", spend = 50, spendType = "energy", startsCombat = true, texture = 132304, handler = function () gain( 2, "combo_points" ) if talent.venom_rush.enabled and ( debuff.deadly_poison_dot.up or debuff.wound_poison_dot.up or debuff.crippling_poison_dot.up ) then gain( 8, "energy" ) end if legendary.doomblade.enabled then applyDebuff( "target", "mutilated_flesh" ) end end, auras = { mutilated_flesh = { id = 340431, duration = 6, max_stack = 1 } } }, numbing_poison = { id = 5761, cast = 1, cooldown = 0, gcd = "spell", 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 = true, texture = 136058, handler = function () end, }, pick_pocket = { id = 921, cast = 0, cooldown = 0.5, gcd = "spell", startsCombat = true, texture = 133644, handler = function () end, }, ]] poisoned_knife = { id = 185565, cast = 0, cooldown = 0, gcd = "spell", spend = 40, spendType = "energy", startsCombat = true, texture = 1373909, handler = function () removeBuff( "sharpened_blades" ) gain( 1, "combo_points" ) end, }, rupture = { id = 1943, cast = 0, cooldown = 0, gcd = "spell", spend = 25, spendType = "energy", startsCombat = true, texture = 132302, aura = "rupture", cycle = "rupture", usable = function () return combo_points.current > 0, "requires combo_points" end, handler = function () applyDebuff( "target", "rupture", ( 4 + ( 4 * effective_combo_points ) ) * ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ), nil, nil, true ) debuff.rupture.pmultiplier = persistent_multiplier debuff.rupture.exsanguinated = false debuff.rupture.exsanguinated_rate = 1 if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then debuff.rupture.exsanguinated_rate = 2 debuff.rupture.vendetta_exsg = true end if azerite.scent_of_blood.enabled then applyBuff( "scent_of_blood", dot.rupture.remains ) end removeBuff( "echoing_reprimand_" .. combo_points.current ) spend( combo_points.current, "combo_points" ) end, }, sap = { id = 6770, cast = 0, cooldown = 0, gcd = "spell", spend = function () return 35 * ( 1 + conduit.rushed_setup.mod * 0.01 ) end, spendType = "energy", startsCombat = true, texture = 132310, usable = function () return stealthed.all or buff.sepsis_buff.up, "requires stealth" end, handler = function () applyDebuff( "target", "sap" ) removeBuff( "sepsis_buff" ) end, }, shadowstep = { id = 36554, cast = 0, charges = 1, cooldown = function () return 30 * ( 1 - conduit.quick_decisions.mod * 0.01 ) * ( pvptalent.intent_to_kill.enabled and debuff.vendetta.up and 0.1 or 1 ) end, recharge = function () if pvptalent.intent_to_kill.enabled and debuff.vendetta.up then return 10 end return 30 * ( 1 - conduit.quick_decisions.mod * 0.01 ) end, gcd = "spell", startsCombat = false, texture = 132303, handler = function () applyBuff( "shadowstep" ) setDistance( 5 ) end, }, shiv = { id = 5938, cast = 0, cooldown = 25, gcd = "spell", spend = function () return legendary.tiny_toxic_blade.enabled and 0 or 20 end, spendType = "energy", startsCombat = true, texture = 135428, handler = function () gain( 1, "combo_points" ) applyDebuff( "target", "crippling_poison_shiv" ) if level > 57 then applyDebuff( "target", "shiv" ) end if conduit.wellplaced_steel.enabled and debuff.envenom.up then debuff.envenom.expires = debuff.envenom.expires + conduit.wellplaced_steel.mod end end, auras = { crippling_poison_shiv = { id = 115196, duration = 9, max_stack = 1, }, shiv = { id = 319504, duration = 9, max_stack = 1, }, } }, shroud_of_concealment = { id = 114018, cast = 0, cooldown = 360, gcd = "spell", startsCombat = false, texture = 635350, usable = function () return stealthed.all, "requires stealth" end, handler = function () applyBuff( "shroud_of_concealment" ) if conduit.fade_to_nothing.enabled then applyBuff( "fade_to_nothing" ) end end, }, sprint = { id = 2983, cast = 0, cooldown = 120, gcd = "spell", startsCombat = false, texture = 132307, handler = function () applyBuff( "sprint" ) end, }, stealth = { id = 1784, cast = 0, cooldown = 2, gcd = "spell", startsCombat = false, texture = 132320, usable = function () return time == 0 and not buff.stealth.up and not buff.vanish.up, "requires out of combat and not stealthed" end, handler = function () applyBuff( "stealth" ) 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, auras = { -- Conduit cloaked_in_shadows = { id = 341530, duration = 3600, max_stack = 1 }, -- Conduit fade_to_nothing = { id = 341533, duration = 3, max_stack = 1 } } }, tricks_of_the_trade = { id = 57934, cast = 0, cooldown = 30, gcd = "spell", startsCombat = false, texture = 236283, handler = function () applyBuff( "tricks_of_the_trade" ) end, }, vanish = { id = 1856, cast = 0, cooldown = 120, gcd = "spell", 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" ) applyBuff( "stealth" ) if conduit.cloaked_in_shadows.enabled then applyBuff( "cloaked_in_shadows" ) end -- ??? if conduit.fade_to_nothing.enabled then applyBuff( "fade_to_nothing" ) 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 end, }, vendetta = { id = 79140, cast = 0, cooldown = function () return ( essence.vision_of_perfection.enabled and 0.87 or 1 ) * 120 end, gcd = "off", toggle = "cooldowns", startsCombat = false, texture = 458726, aura = "vendetta", handler = function () applyDebuff( "target", "vendetta" ) applyBuff( "vendetta_regen" ) if azerite.nothing_personal.enabled then applyDebuff( "target", "nothing_personal" ) applyBuff( "nothing_personal_regen" ) end if set_bonus.tier28_4pc > 0 then for k, v in pairs( tier28_vendetta_spells ) do local bleed = debuff[ k ] if bleed.up and not bleed.vendetta_exsg then local rate = bleed.exsanguinated_rate bleed.exsanguinated_rate = rate + 1 bleed.vendetta_exsg = true local rem = bleed.remains bleed.expires = query_time + rem * rate / bleed.exsanguinated_rate end end end end, }, wound_poison = { id = 8679, cast = 1.5, cooldown = 0, gcd = "spell", startsCombat = false, essential = true, texture = 134197, readyTime = function () return buff.lethal_poison.remains - 120 end, handler = function () applyBuff( "wound_poison" ) end, }, apply_poison = { name = _G.MINIMAP_TRACKING_VENDOR_POISON, cast = 1.5, cooldown = 0, gcd = "spell", startsCombat = false, essential = true, texture = function () if buff.lethal_poison.down or level < 33 then return state.spec.assassination and level > 12 and class.abilities.deadly_poison.texture or class.abilities.instant_poison.texture end if level > 32 and buff.nonlethal_poison.down then return class.abilities.crippling_poison.texture end end, bind = function () if buff.lethal_poison.down or level < 33 then return state.spec.assassination and level > 12 and "deadly_poison" or "instant_poison" end if level > 32 and "nonlethal_poison" then return "crippling_poison" end end, usable = function () return buff.lethal_poison.down or level > 32 and buff.nonlethal_poison.down, "requires missing poison" end, handler = function () if buff.lethal_poison.down then applyBuff( state.spec.assassination and level > 12 and "deadly_poison" or "instant_poison" ) elseif level > 32 then applyBuff( "crippling_poison" ) end end, copy = "apply_poison_actual" }, -- Covenant Abilities -- Rogue - Kyrian - 323547 - echoing_reprimand (Echoing Reprimand) echoing_reprimand = { id = 323547, cast = 0, cooldown = 45, gcd = "spell", spend = function () return 10 * ( ( talent.shadow_focus.enabled and ( buff.shadow_dance.up or buff.stealth.up ) ) and 0.8 or 1 ) end, spendType = "energy", startsCombat = true, texture = 3565450, toggle = "essences", cp_gain = function () return ( buff.shadow_blades.up and 1 or 0 ) + ( buff.broadside.up and 1 or 0 ) + 2 end, handler = function () -- Can't predict the Animacharge, unless you have the legendary. if legendary.resounding_clarity.enabled then applyBuff( "echoing_reprimand_2", nil, 2 ) applyBuff( "echoing_reprimand_3", nil, 3 ) applyBuff( "echoing_reprimand_4", nil, 4 ) applyBuff( "echoing_reprimand_5", nil, 5 ) end gain( action.echoing_reprimand.cp_gain, "combo_points" ) end, disabled = function () return covenant.kyrian and not IsSpellKnownOrOverridesKnown( 323547 ), "you have not finished your kyrian covenant intro" end, auras = { echoing_reprimand_2 = { id = 323558, duration = 45, max_stack = 6, }, echoing_reprimand_3 = { id = 323559, duration = 45, max_stack = 6, }, echoing_reprimand_4 = { id = 323560, duration = 45, max_stack = 6, copy = 354835, }, echoing_reprimand_5 = { id = 354838, duration = 45, max_stack = 6, }, echoing_reprimand = { alias = { "echoing_reprimand_2", "echoing_reprimand_3", "echoing_reprimand_4", "echoing_reprimand_5" }, aliasMode = "first", aliasType = "buff", meta = { stack = function () if combo_points.current > 1 and combo_points.current < 6 and buff[ "echoing_reprimand_" .. combo_points.current ].up then return combo_points.current end if buff.echoing_reprimand_2.up then return 2 end if buff.echoing_reprimand_3.up then return 3 end if buff.echoing_reprimand_4.up then return 4 end if buff.echoing_reprimand_5.up then return 5 end return 0 end } } } }, -- Rogue - Necrolord - 328547 - serrated_bone_spike (Serrated Bone Spike) serrated_bone_spike = { id = 328547, cast = 0, charges = function () return legendary.deathspike.equipped and 5 or 3 end, cooldown = 30, recharge = 30, gcd = "spell", startsCombat = true, texture = 3578230, toggle = "essences", cycle = "serrated_bone_spike", cp_gain = function () return ( buff.broadside.up and 1 or 0 ) + active_dot.serrated_bone_spike end, handler = function () applyDebuff( "target", "serrated_bone_spike" ) debuff.serrated_bone_spike.exsanguinated_rate = 1 if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then debuff.serrated_bone_spike.exsanguinated_rate = 2 debuff.serrated_bone_spike.vendetta_exsg = true end gain( ( buff.broadside.up and 1 or 0 ) + active_dot.serrated_bone_spike, "combo_points" ) if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end end, auras = { serrated_bone_spike = { id = 324073, duration = 3600, max_stack = 1, exsanguinated = false, meta = { vendetta_exsg = function( t ) return t.up and tracked_bleeds.serrated_bone_spike.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.serrated_bone_spike.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.serrated_bone_spike.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) return t.up and ( haste * 2 / t.exsanguinated_rate ) or ( haste * 2 ) end, }, copy = "serrated_bone_spike_dot", }, } }, -- Rogue - Night Fae - 328305 - sepsis (Sepsis) sepsis = { id = 328305, cast = 0, cooldown = 90, gcd = "spell", startsCombat = true, texture = 3636848, toggle = "essences", handler = function () applyDebuff( "target", "sepsis" ) debuff.sepsis.exsanguinated_rate = 1 if set_bonus.tier28_4pc > 0 and debuff.vendetta.up then debuff.sepsis.exsanguinated_rate = 2 debuff.sepsis.vendetta_exsg = true end end, auras = { sepsis = { id = 328305, duration = function () return ( set_bonus.tier28_4pc > 0 and debuff.vendetta.up and 0.5 or 1 ) * 10 end, max_stack = 1, exsanguinated = false, meta = { vendetta_exsg = function( t ) return t.up and tracked_bleeds.sepsis.vendetta[ target.unit ] or false end, exsanguinated_rate = function( t ) return t.up and tracked_bleeds.sepsis.rate[ target.unit ] or 1 end, last_tick = function( t ) return t.up and ( tracked_bleeds.sepsis.last_tick[ target.unit ] or t.applied ) or 0 end, tick_time = function( t ) return t.up and ( haste * 2 / t.exsanguinated_rate ) or ( haste * 2 ) end, }, }, sepsis_buff = { id = 347037, duration = 5, max_stack = 1 } } }, -- Rogue - Venthyr - 323654 - flagellation (Flagellation) flagellation = { id = 323654, cast = 0, cooldown = 90, gcd = "spell", spend = 0, spendType = "energy", startsCombat = true, texture = 3565724, toggle = "essences", indicator = function () if settings.cycle and args.cycle_targets == 1 and active_enemies > 1 and target.time_to_die < longest_ttd then return "cycle" end end, handler = function () applyBuff( "flagellation" ) applyDebuff( "target", "flagellation", 30 ) end, auras = { flagellation = { id = 323654, duration = 12, max_stack = 30, generate = function( t, aType ) local unit, func if aType == "debuff" then unit = "target" func = FindUnitDebuffByID else unit = "player" func = FindUnitBuffByID end local name, _, count, _, duration, expires, caster = func( unit, 323654 ) if name then t.count = 1 t.expires = expires t.applied = expires - duration t.caster = "player" return end t.count = 0 t.expires = 0 t.applied = 0 t.caster = "nobody" end, copy = "flagellation_buff" }, }, }, -- PvP Talents shadowy_duel = { id = 207736, cast = 0, cooldown = 120, gcd = "off", pvptalent = "shadowy_duel", startsCombat = false, texture = 1020341, usable = function () return target.is_player, "requires a player target" end, handler = function () applyBuff( "shadowy_duel" ) end, auras = { shadowy_duel = { id = 210558, duration = 6, max_stack = 1, }, } }, smoke_bomb = { id = 212182, cast = 0, cooldown = 180, gcd = "spell", pvptalent = "smoke_bomb", startsCombat = false, texture = 458733, handler = function () applyDebuff( "player", "smoke_bomb" ) if target.within8 then applyDebuff( "target", "smoke_bomb" ) end end, auras = { smoke_bomb = { id = 212183, duration = 5, max_stack = 1, }, } }, } ) spec:RegisterOptions( { enabled = true, aoe = 3, nameplates = true, nameplateRange = 8, damage = true, damageExpiration = 6, potion = "phantom_fire", package = "Assassination", } ) spec:RegisterSetting( "priority_rotation", false, { name = "Funnel AOE -> Target", desc = "If checked, the addon's default priority list will focus on funneling damage into your primary target when multiple enemies are present.", type = "toggle", width = 1.5 } ) spec:RegisterSetting( "envenom_pool_pct", 50, { name = "Energy % for |T132287:0|t Envenom", desc = "If set above 0, the addon will pool to this Energy threshold before recommending |T132287:0|t Envenom.", type = "range", min = 0, max = 100, step = 1, width = 1.5 } ) spec:RegisterStateExpr( "envenom_pool_deficit", function () return energy.max * ( ( 100 - ( settings.envenom_pool_pct or 100 ) ) / 100 ) end ) 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( "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[ 259 ].abilities.shadowmeld.disabled end, set = function ( _, val ) Hekili.DB.profile.specs[ 259 ].abilities.shadowmeld.disabled = not val end, } ) spec:RegisterPack( "Assassination", 20220911, [[Hekili:T33AZTnYXI(BrvQLMupOjHi961hjLYR1MKDZU(SLLV3nFsqGKGIicKGhaqjRtPI)2VD3ZdmZGzgaQhX(Mkvs5vepMPNU7PFpnUC4LF(YlMfvgF5hdgeem4hgoS)WbJhheC5fL3Vo(Ylwhn9MORH)yv0s4FFFrrurrYQOYKSv4DVpnlAgokfzBYNcpXIYY1fV71V(6KYfBM0FA2YxxKSCtk9gtZJMxI)E6RV8IjBssl)5vxoXoim8YlI2uUil)YlUiz5hGroz2Sy2Jhxm1aw2E1NYUEt82F5I41LXlNeNV9QHdpC7v4GU9x2(lFyr0QRJlE32F5OTx9(1RtVF7vVQOmpzA5R2E180OR3Evz22RkYwgdpWV)RWfZYVPGF1OBHRUopBAmoL4ZMSmUpnyNNT6vLBV6)B0QKIfBVAZQu4z2E1fPjtH3jA1m4rO)8UK00TxLgvapDAganamwcG12R(n4A4Velj2a)P40OVS9Q)caBXPP8L5RHzkE1S4YYiaSUF10Tx1nzU6ftiy(6RtJHzoBoCVmyOX3nInVBV6dN3Jnf)w0nX4mexSGOnWBVcGK8nRP)C7va9Bse(3WWCh8O)Zni0plbg6KscfT9QBZqGlnoSil924vLSH()tb84x8JxS9QLz5XC04MLlra5VhFBYQxbW5)D2)BCkHpXj4(SnBVAbHQlxeJ0InPtswnJnGFiB1TX5WKMVzvC4Dz5Z6pppdbhCONSz(8(Y7es3P)M1Sx99aNdaX)bmwftJaq(pwexwuMTItc)8DXaIaEIF6lfaBYgKPIdYxhdtWMc8MCq8vi8jq3hv98Z6FjqZlklOTdLXrPWIa3B8rABw8QOjaj5YFe4RNY2(mnpzzr2QqGHDDCrjJXopzn7UFm56ffLrP3eZjFh)53byb27aWS4LG3aizjrWw2Oue9VcEXs(B2NpTBVQdGpxd8rHLr5WIcq(NDkmM0nqQCw46mG4ZV(i66ShTpYRhwMfolbw5a6kpEzuYk8j3E1BUSe256ybI8rBYJBAHnexyGeM1flYaS9NeVv7xzp1fWXowaRZYsdZJfc30wgxSzcaDZ3CD87KsuWTdFkEo8glW97LlymMiu(xJYZZkJlU8cytt4Q4VuImgW)mYXCFn7fSGgkKZSgsypMiMW0SI4Wynoz4UDvw2Na48aswI6JnlmNE4hEae1bISltwNMG0Ota85WTx1RTOuq7X07Ncce4SA8f64NesE7vhq7pVwHNrIQ5ixqexjx2r8ks6hW0Z2jQSBEjkvD2MCUe1jXZjbuQ79RrKEJbS3wAc)UQOzT7J4GBbQ1Q4LjXa2JHP7WLNPmOA0U(hxNnPe(FxClPeYMmNS1GqP4YQ372ia8H7I)v6g4)0L4GqrOWAhMXzXrLlsVpSyr0SS7k0a7yGNMb5135nDDiGHdbHnijOhJFQBBhz9XdwSaFApdMI)sctvlBdgro)WVZvg9rfrehwPnEdtR95SzgK2WMAcneYSTrv4siSiMMTAwcnHEKWXr46WNWuag8X4zpqh0EhQ79lGHrlzWozMa7jZJJMDpF1GCGa39ho3chNtEQMewkO89DUI5uISuahTsFI04d9j6SfygvecddLXqcwXovw4Sz9UOByVM3f2smYSyAx5TCabmVWN8BdXAaLmpkmAz2MvWUWrJRjIXu8yBfXiPtCjbay5aAf6bMLvkF4CMiuC4y7uvVPBTaQBtH9ZZtMIMdIB)7AyNr)5rRcZMhEZkqCbkI4ptkNzdtEuYSWyYOXOzZk6JMCEgQCQgFKfzW7aNMMb2jREN0iJdKAowLXn5nxOujh9y4qsvbj8qWaEa4kYT9bziK13SjaC7Oan8EoWCbg5ViHBZ86mWEEKZsLE2vYrUKGRWiHD)s6ksjQeyUmk)gehcA2cD(k9eC7ifKB5Lo5TJjPVsL9XnXYWUtbUYXRsCkgpTQOHrvmtfeQbmyy6neASNeq43r99oMPhd4OIawpPImbP(gyqu5bSizwJl4tXs1)QudyjTce1C7M0vX5mCJudCLxp9QMx(OytExZkw9UD4eYCPkTqOEkPLt2eV088blL8RVhqRxhVI0pNSc5pqQ8yd8ZhK39NOxcnPcEluXtm5YeZ53s0yPyvWKn4I7mZM8RMbuPKR6Sq7RiuwC3WPZYvHHQRZhNq(R73GnPRz9ZXqgGxaJcaYPv5zweqXy)ie9PJ5zNGej9TdEPV3LVD(gHPZiy8ToE3cm0bHGspW66Awe)7BkrNLohvdICRG6jyQegm8HnC)HjJG)WIOcucwUUlj)e4i9QSLS9eru0iURYIyvC1Ecdr1GiHCH62(rkY(bhlRy20ASE(BGkxu0868KmyEVxb8McI6k4lm(sGKGqRE8I1T2xnyjQlepRcB3svK0yxEyIR1HdCYNrJ7Cf4jKUI6Sw)UAMx57bbJuMEdtK5ahayqDupcXoJgHpowqCn9YUSd2)lNKdElqVVP0Zk0fxQLMTedhtMfAvGw1ChLpnAf6mAEoOtLMMrQkn43F9M0IydLhdhR(GPK1FH)ZnZUEjosgp7BuF2jrxtAKbrh3uO)KWlUgwWKoKAAWIWafGKPcgDXutMC8ZklH7ecu(7JNDhW3dlWVKy(oUS(gTyaFBWetYBlJn1tZa(HiWae00Rf3N71ySXkRNWIy0MyyfdC(w0oPiMve8U1Bkk3aQ0IrBsaGt3ih5JvlkIIrvUMMldt5JtDZqkqf6EIUP4M7iR9lcxhvoDrfWTp9Od6pIXdIM1gxgojB1g0wM48G3goA9u65g0NzxCpYvvBkOafxw0j9Oa5SjXZsIxnnwdub4CuDhL)mkBm6wWghm(jF48pXIo8G(bbOGfmsUBIv0x8Pp(xbv)Os9yM4u05lythgh9VGpd(2eOXgOmyEwI27sIOXOrtYNtZUozQkIqt8fhzyQ40lYqmqW2Tv3aKbCQclsZ0d)j7M9lh2h07fcsibg5v3hoBDHWmw0xqXtfy5PqosLHrAGsv0IqXskdHLNOxTHjb3lbKLnL46j8)zdSZBZsyJ5TuEb4XhX6EYMa4oMtelMkxNhvSgeyugVSoxX5XacBjidfShyrY0f8miaJzpmCzOsvufJCKzr()oa0ySkrLyaR5)OqXjiHds9R4FTzMtZS9bwjRow)gKvBeFn6HvI2znr3nPQbpxuvhmR6tKDQQV9fwSdRz8UDjCmCSnaEb6lkg1yAB)UX1Rb)BkIdfRHHHG2MqHStRgz9mTscA1kPrkTZvsq9vIMTacVknSZWLnwMUfOPrVPOGPeia7QZ5oEZmvZtsRSBfV0MDu6XiuF54deIfo)IEOMK(BV6NxUeuEboms5kb2vfCWwEcfko0ukYHmB8zx8Z)i(tqjv(DjfK0iz6hzzSLBJ42REFArM4(OynwqvjRJzkTG)KiYBwRfugFH0EezcahfploEDCoW2aClGATLAO5U2dIIs4s0cHIhFeWBBAtmgcUap2eZER9u8DwlCc8b11c1sO7ReV6lcQ4O2q0IpJhiFlbSP5nZoc2i5H0laAYC788K004CFwBxeNtHbbf5agWUo5gtV255hKYhoPdflfazMyrE8m0CSibBc8Nfv(eFi(NXZHzHv7bI3B290VlYGPfDhEEjRMcIPxf8b4EXdRYPlx1vlo1qhAzXeIxNhapBw97fp8SnX7usfBMK83H9XGiKCwKgwVMqLtxGJEbt0ZKn5RSfKH5Kq22SYmcwBOQ79d6)wXgSjzfmT(0iR8uNsHFqB)LLfwFCtReYpsUkcNNZqhOj64ybg(pMVl0vW0BgV9hlWae(BVNjnLcBoIIwaGEvqKou3YWzrljxqqwBv(DW4O87aSiH7)14iabpbJa0xIwUo1ufxnD6bCD6prSV0f0uaacNCFymB6nZIejA28z8KSfPa(Lya3VdWPG2cQQtcJxTicCHdJYG8r9i2UtDTh9S5dLI33Ve4komCD(MzxJjIa9xMV(Ba94i0cJ9Z35leP2yttbH7G7LGXmJTgjIBWYoQimJx0rA0xc6nFaCdNb3Fvzn9ukOjFHjvlbbMz(gRwmue0FNN(aCN3WF4aYVTPy1QH38Vrvk32R(X0OzczzrxJKtUXzICr0gryD54gw53foHgtr4hPG09dsND2RsLvyEwj31PonLrqwDiDGSwneHMNhrfBEa8OqwKIxMIywa4kyKjWwfYGiEvYTsQ1ewueUdVcmZGTRAQFzY2QcCTyj3Jrxo(ZQ4x2uqk0MbBsKbbeVcTlJ)aYer1Xdk1mrTYKRjUHi)FgzDZfrUz6J1qh3m3JLK4yKSgMHCduCWy6ImmCN5XKnmRMz1POMN6UEeQH6cfYTuIgygyGiYFRBAlX(pjflEWKzXCPVvb0D5KnS8txl42YGXUPmjLsGwnlyAX6WlTHfMF9G(fn5OFJpJA88NtSDBV63j(ogpAGKDUkU5TflBzbs(qIz3YJdK1dnDLC7(J9JJm1JuLG6gC6XwvRXZDI0nhhpJDVp2hldlPTzUkgcdhQmOu)mAMpSmG)fd46M1OvtRswluxKrHOJeYKc2pGsV)8NpVpV2AVJSdJ7cWCEboWQNymSTs4q4enZbduug4lUnhXCtGQb1y2ZzqoROMjCfojOIDc2zvU0Xdq)63GG7Vn)CUd78c2c9v3vKe1R5zNfsHdZkqQ6r1n6OkyEDApntYDvpqECLEn4dV0WD1hrZXymlxkmHKJ2t1lnyDbs7P6oaAqiVKXeAl39suOPaiXN1NAuayzt9isSZ(ngqfZsbL4pQQcS7abYyERRxlzhOxg3uWTbrGjtfR5AjOrVUVul8VDV8lQWtERUhNQVTgXy7SROAmtVZOaFHm5vykAxNSqN4ve17vQosm((KiiuLGE9uQNqDlOULr6iSDkVCwkLd2Ixzmv2Vlrn3Cp87vg4RvXtZZsZYNzEDqoAJ8s1L6qfDOOUUWOXWo7erC81DSJ9bwS1uCBMsNRd0HwUeqYxE1YQqnNi7EQaNNMH0JU0)3iob4okMMSxt3Ei(lxLqJivLNHh7LghZJAuc2UnRDAC8iZbBv4r5(JQnmshVDfsfCzzLNIVDRTSL7Akm)wHiAw0D728jKCzwuFpouoj2nCEuRLfyjIa6U8PCsMiXx)oR4d2E1pXR(aM6akQvsVnz2iHgpevHOG)cne9s96285QQg2wvUMvo9sbfMDrhvIzZo00vgZBHDSuWbvRcWaP(86bv6c55)kmzUsOtmxhApOiRoOSnBvRHYZEDA2KOuUqqNrartNJfb36cQ1tB0ZHSAUF)6NRoMLUtPSnnHI(bftLxBEE7613mULMr1GrMAJRXNiQm36pSQEx6rAibwDS7GJI0wNXW0Z(6rUcqtJyGMb3UnlBBKI8lg3ty1(xvzzMvGYoi)Ce9FAd16Pbk9CSbWwGxQcdkkg26EeHLlp)7ooKx7qSusPepmW5ZsX(KukEAuXLgtJ3WbfTGRWQvXmEbpvlSSyjF(5WXxTxdrGY9E8g4Tgk13xJJPv8MdhSJ8M2MME1yNSflRMTTXdfTw0i9QKR(YXU7zTvWOn0tDVm0p9iYTl0zlow9iaRB7WHYJsclFyuqCixhiT5ujliCi81I5fzM5tCb3ZRcyRvX8e0RxRMcPybuTO0XE47YJwxGXgmofeSIN)zyiNcYvlHPobv6kiDn(KLIkJ1Iihmf047ondeqTgd1w48n46xB7nryPG7WmlNXT1WETJhiwlEMKsrL4Ab24fEZqRcefPfNhSE7f6wTkqPQKtOOyHSuIDpgMgUNr41TxptmKGnwsvw2gf3f4ECSeED)L22Rz1MbBeTKb6a1GKkNXALCMYHxTRBrTTgf5gq4XDRbijqj2D2Q9pEmZ2bSiF67jnF2heW0z4obTEyyc(gIHXAXs(CYWe8OyySHIEQmmdTXWm8FHmmMIXciwixzaxm1wIop)Wo)5G3cMsVEkEegJtJUhJ3h4dNwa6pp7ZWVtqVzeT4JPyG1qpBKDUdkNcaw(4VdZMDbgeXcz3AaSEFnObJFwXOHE6g6KyigyrvCvpxsUzMTvUhujCYPLQ2jYQDZ1t5XV31DPZABidKoI8RQNcBQQzk2hWA311aY2678uqP1Mb0lIsTWwZYVJzKkitlMKHQPK2DqxRQfiWYLepaimt9JxLT56fT0X0926(ecByqNKBNDAjgYYIVRZjHTT4m7Czfy5i6P0Z)IiGt2MXbyc4oZUv)985paD0TcCvtby4qmTGaUeBhcSHPqnFD41(iZ)O)se5XJJuAj8hdjkknRN5A(GDhB3L6(UfrPZX(vqYTy9LyZ2d1KtjJOMHn2Yq84XhlxwQ3vn3pnLyQMl8ilzNfNzZbUQYfiIL7sp4LET31wWtLQcCgxv6ySt8Hh4u98UQqHpGNYlMwDowel52hvErmKLnlDdGDH5EAzlmi3LFZvZn41a3VKa3hUpFdabuG5953tdI7sqW7GeNxeNlQp1a39sbFdY8K8ycAOXW9ji23yGL1hMjZ0q8irsdKBJaTg9xVX4TAI8EIlRAAcbUSF4oEZWk8orVWsfY8xqqihrbGSMv1mPq2ehPJ)ndAKr77LoXwhQWfHlLPtJMUigbTOP)pByaeWzxy1Bu5Je9LyLchPPYRHzwydlTMqxdLwb6lyQJfybFllm)(MAUyVfX6cU1Ib6pe0Dm7M48PlsINFPLAQrB3NJxsLpLjIEtCAOH4tcGCgFsGusrAyowTM5RGTf4PTFESXOZKt76Hdh1Mi1DSe95Ewry1vzVG(xaG0sZWPEoRxuOhZgY2QnfmJIO3QVfjetIlVloEf13oIVpUO5Lb50YLSJItM1ZHtZbIZEjy210QUkmNu1K4MvU6PPCYOPpSTwbn9RjlXQpGDOIOtFGORNXXy1RvYktGy1j7XJfjWcpZsuVOWO6fqXKXFzDsopCm2pUdvbrR4MK1HSnav9kRhXPqXnATUzgnjpW0B8hbMLB9)tgZYnTTG5ZaDOS(8boWFI(53JQgJ2TMlJtTGNrz6SNBxroREjrYXEvOm1m7rTZNxB2coRqjK3vvOXGdeKHcm3gyMMLCHspYaVeQHcvqEn2Y)0D(yD80e8iCu5IhcsYjdq7iCsCam)o0DKeZNJQWPM6gw7T1xFeCwd9hQoTPkWcAExsJQERbn1Cn8eBoaEPLAu65e3i4hDGB6UlnpVwHlh6ERGxeHMhekmNUcUUV20OKZCZ6BIJPuGqhdtqRROW7PWNJ7cX0ogreFgx91OqPOsK3LKKr7KyDjxSk0eTNrSo2O((hw4XgTon3wyBg8c3L9PK8yJWrw3QQKgn(jerMnZiZpEdYin5HfY30SFR21yoz2i7QDRtz4xAVJhkkrn9TqLS4GVP0DzFyx63pfLNEVslmsYQrNCJsMC6AnXiyXt5OMcF29yCgwdcnJxMmT3wlNOpZkbC06PAEd560K7qNMVoJgREo0C(5BfoZ2XSWclTPliTJ07SqgS0SBvJxEDB44yX)ZU7MjyddAZ2BBhUw3T7jZoxTVdTHL2ondIAUeGC3k7cgOGX9gBnphhk1DHDjO6acbVF9idiHkwm(LX7M1EE2PqEl0k3RUz(MD3BMTLCw24TvLAJOnlGAizjkiFZQv0sJoNhOrsbhnUWw9(WSJLLsaPRaSswipMcdnSCyhjxzAeecijUcxNZj7T7B25SghPQewWHEQmR1oj8DzlM0KBtyDsOQSxWHrwcr4PFQNxbg5I5Rv8AMct(3kHfnjP4mcEgjcwTYzIlIA6EHftZ2SAwECQwBNASsaU9Eo6mEw3o(UpzzVmlAU8uYoRMPTcvvMZ3a2k0mh4ZI1eV9)FLEZQRaIQ7S2J2u7ldGdReu36)FKl8VxYfSBQIl3wRzQIXzepnLvm3Vp7NQ)11qShND69JqKm1C74EXoj72yr1WMC9QmE7zHjUGfPn1GglIhNqnlVonS89U4jyYKrywAHbne6ZzFS1l6ZU9kx8zX5QVsuQq7DmtenDiZe4i6ouVAs0fP4PdFwMix4IgiISFf(5F81)279GYovVGEAfJlfzEuuB9VJmI9l(W)DndFZrBRAy6Msr8hiPUwZFTsQy16bhAH(Ch2zpIs0agS848cKEY(em9w8BV0Dr5O5KfxEXF8(p9XF(J)13T9kaRJS2jlxNHFhEiddFLmbMVchCw6Q2YBkxrBkZwY2)oL99xQ)2F5xPop4O3j)I(G3g)epr1lrjYy8DmA8VUb7hsGPGfKw6S1I4e191WAS7WVGFfJ2(lnaGSCHUBq3qDO7vvSp)JxXGn1lj4GGB1nGak2O8Mh9O0YLMSpUTBRodC)RKNYhjyvDL6q1tAmqZP(P)7F1yWg)eruppOB2O89gJIOuahwnkkvhiETUJAXRhy51zxR7XkV(B39zVnVUNzxf4)HDF2v53hoy3N(G2XPZ6lH7gBUjYOLSOACdddEImvnUYMoRy3wwV5Pl5mWnd7tD4ESiDF8rpvyYKi(n349mqsh6z8AZ(4w9(E2hR9(pd8u(gVDE98iek3YDVZY2rHsdp(rlqrJL5zZ(j7J3JfSE8AYd8rWA)W4LUlTRx7DOHZWU))byWTkBWptuDCq((T8trhgLYIsKYsoFmfl7WFaltKS5jyzp8N(tvvtapot8VrP4DC9DkfVx13Qu8xh9C(9kLpGVmFZs5d(l93Tu(08s9TlLp8pVF)s5dkNzC7U(nmvWh477yk)zEM(wMk4f)PVepDdX4lseVaToj(AWrsSlonDkWmxGbqD1rlIYxoFtQytsbsbtVhgo(V7l9z5u1pfgwU)bN(AxFemS)0M9TQdR1MRpD4GdtMF6EM98Jo1RXKZgo2(Su1xdWHYz)G4HhC27eSpUIqFDiwYqNwvh4ZYpKkGQthEuxLOyy(HRy)U4NQId6Al8Z7pO)WE96beuUoSVsFsiAZc380XBBXl)eySp(zVGTUE5)Og0gOV256sa(iVsLvlMD8)oD3RYKeZB(WdvVyTdJ3zNw9I1Uzp1x1BtS)Hh6wBpHhqQJ2Ww7JtqVNcQkqfvzzQRqv1XJvRxlyJZ8Gg7P(MpEmLfkR2W(4WuUpPSCuMT98D6AbOu)kh0AEJDggd2Dy0XxIHwtvCaJ8yt64MAFofSPSG0v4PV41rnOU10xcQtOcsMsndjlfGcuOiLeYBJssPmDibUtXpUHKSSN2xTq5aAbbuJUPDySeKk3zp6Kaca3PpBGUNCJK8WNER18Xzhp29448qtXhrNNm69TCURMLRorMNYgowt29hywKZyvmpoLnomtNvqOZx8pPFASe6S9ilolldMFC86OMHJZoDibQVCFT(uHr(3yee4CaB2UmNOEYydiFKVH2w3LRJJ7iMGHUEaQRyFYXdmaGGMzfWMaDZpefeu1Nt)tFhUO0)wyaKTXhyViQQpk0hip1RB89Wt9wAF(7K(TY3NX4Q)k2DBRe4dBXoTMFb8(n8Pd7p(qTCjZ2hxVjddgDy)4RckOQLMnG(3d0Sv)62QoX9by4HhSDNZovnzI9KO0VITExn0QnhUSG8apPoEWrUAfVDAELd4EtdUaBWCU3frMCP92AVKGBDeXX3zz(Hh2R6u9kBVUC)NEM6cTMOshk0S34J4Q18zAcSg8Cuguqs2owbNm(OG9B6RDeImEjB2STedP2TNmWloA)Up8GLdboIrmSQ3gR5PdCWe8I3(yBj6OENTc3uA03CvwSYEMR61ajZc)wFEMwokLpOutnRBxTJ(4rbd69DDhgmWUjz96DMJ36iVB39pMDAquXGhEWJCg7DFw4vaXLD0aZtcg88GhLD4GxwmPERmW)OX2nP3oyDId(t6XRL2Z)K7qRMOw9a019XeHUED2t7C53zV6LMzhh1t)Pd601thy9Saq0SEH907ql9sv0ueJUMQ2LuJ9y9amkLr9nENsToTRcCjxtm8PQtxlSHgo9DYXDQ)qQQj6yViUahf6u3QnskqDQVDw8r925fKByPR99PJG9JILC1MsZWM6CB(OJAgd2Yzq1GOVb72OMKcMmkReblge0PRZOga()904tgoOgFIhGZoFamgFNacDqRhoOv0A5O07azwB(wcQiUSVInKZwR62WkuBmzgNdjR6vQJDS7fqts5SHLnTUWCTj6bgS1wt9ju)VDEIRE4Pm4lgniuRBwow1K7YN7E4P5sGpkuCauC63Di4F4bB4zbnWJqKa7VP((aF5S47caJV0XvNgGHEqoS1sXYPd701MmQwToRpvGF4EMRaTSAjbFYz6wT2HzaMI9Cpf1KIYFIapuVGVguplzJ6Xq9c2bQN56CxPEd1s02le1JhsRVf7xL1K6lKNaSwE5z2ZwMUaCT1Es5zNA)6vTwYJog8tTR1(pj)L90xkXxMHMFXBTKM4l14kz3aI9C0Kj7yPvwSF3HhyTYgQVFO3zJoy0(QrqK5IQ5ioO)4ZQBgxpBM2nIPbYEpG8)A7xREaznBiH7RxElY4f0XWLtRw8AZaLU71ySsT1XhRm(WiC5E60J1TjMVEEoxoDRhKdsGMJyFC2WGEhyvnqBfdcdWPb1wASMMO0oiJ(YOd7GS5FH5ax1mfXbVfpVSVj2UNx2IeB3JR3neB37yp3xStXJHKem0moddtd22wRrdsPW1DQVpZOLh(Wdws1XBgyL299(HfpT7pjlIr3lStxpGkO82bS5B9nSN9GAm2pW7SbcAl1A75Snc6FsC2r)uYFT3UhO9TuJ5Low7AWFUaXcf(AlD8pRt9Py4qzzn9CwwDLhXXOjjPaueZYIQm96IEMq3XhiuNC(f94zZ7NfHneRAa0)MGQVYZhAZLOCXf)8p(FP5H0DrYAJLvAX8yzU9Q3tD0j29PdR5xkUM3YvyEAd)zcRPrOGQy5S(uL0U7mIxh0qkNSB9RUMaVrSxp34anWrUXvnB1q)LdGxlvTvknFe5E7mvLfmKxTyou9j4vwGlwYI6WNHfmXcYR(C5h9C9o2rvY3fhovwS(eEPFOi0D5S6qx8E8u7xKbIEXIdzEPmcGgnZrl4dlF(31mmVcd1zpw(jQ980xI7AcF8odweO94NX)ewg5yNrzEoR6zwVMqitxGJp3iXjBYxT1sHZqctkAjy7amDO48Kb9FBNU2eznwZmPAZtFknhmO)i()ffgtGyu6jNoOFWyM3i)b5)XV9EMCdYpeCnUib7Ylkb(v12zH4rKdtLTdJC8DaJgH8(vWUm4rWYs6lrlxRMCOMqrnyhBBrCYKYKcGs4K7dJzaIuigtBL5DrByTf8y8klrnL3bamilKSujmE1c0YkSOCOhZXU3og5zQ9iJNfCHTJQoSoDIaSyZctbD1rX4PDimA1QxwCqJpunhB3GZgyFbFUyvaMXfzFLJOSCIETHBu)78MUbYEp8hoGczjwXu0n)BjZMHBe(rSNyW3XtzkRGRRxyOMfqxZ2nxBXzLjZcAAcPoVrbRg1o70H)Wdp0DVA902o(Sq80JpWWlEE0fQTCj1em1gSZ3rXwEhHiI0EYpxpRQKXZYJiDfyragZOPSGjcOEpUL3GEo(ZTbdXMAsk8m6tHp)SNGx5e(n5fuBhlOgh1nIf3m7zDRIFmRLfq80fzyuZZJjfIRM5Im3g3qhyzcIwozdZNkRmpUtW9Ew6PlCHvtsrhRsMfZe4GN4KOjh9BBktsLNdjoNX5ery7v)o7earuYGkIED4DjFuARIyFUif4B8TpEsd25AI45SKQVBDJ2)MPpyRSiZkpv3kY69urU1K1fk01sB29ePTaMDnCL4IyuH39CGDEr7L1A4GA2sxVNr5blOh0jFBOdAjs4FPTK6wGj0XbwYozxt)TCiL4Sb9Sfi3ZyhGGNJwAT5QrWedBE94(vN9S32D0cHOs)96Kth2XRdBNe4voJ9qX0XYgkmI0Nup6ZMltbf6jSmT0IQXLzxxEMRvoU(rgdTg9E7Rm2gH)126OnXMADO2dNZBITQ19ynMl1uvyV8LbuPoQ9HhKorjBxBnypagfb7KqBd2(EzczdxDcWr8bQ3zJAtyuBcJHrR6RuZA2d5Y6bpZIGnhDzXtoUJmkUVSKBF0NG2tfSJiSfl2kNgRzjqN)DKjFyGpUC88R8TE331K6AKjnBuznm2zNg4PGdTFW7cgqNadhzaZQ3pcsx3GdoE)UgqWOE92V7WJSFq1RVVe1DW9Y8fTrc7rnRXgf(D8Giv388n4Me37roR7Od66PvjU)4EA3x47hDDx27U)Be1GWxPUZRhsRvLdUjSpk1gV9Rbc)7QPXqSQBRMabB()H7)jtm4Xi7BMonBtQr2n1gs)GSkS3JI1)L1My311lv8BEyeaop923JWRREortDv9H6OH902L4YxTU7z6STTqQl9S2lV9ir0RUOQGVEpdRqKiS(L4LymJ6C8NFNfcNetkde8PwWM(6zcgCihBEyT9TiEJDqDicQxiRWVAC5sy1Q7ZwHYDgOUyZeWYY5BUo(DYmMIMU9j9ZPps(INPepqRazZw3R(0ESXNVelKaxJXhOR0t(GVt9gZWwYbiZvxGUxwUaded1mQzvnGKQ9PAEkwxLEHOzyi7HvKyHkdee(5RfJONreRxePVGWONuHt59naLbrIS7FSy)jVNSrQmsyTNngRJ(rLv9KgRC4A5c6phpo2us7PooJcBgRoQmcfS(bmyDTZYGM0q2qhY6LnfoL4Pvsep8qx3JG67Dsa)8EePU0zmnhOVQFh2c4(sYYnlzOfs9a7j5F9xiefRUH)RF4CtCafaurrL5Jk6toHuTVtuytbmZYYvDvYw2SaGET1LCLfVQ1sN71O11HVkwPnlsFL1NeuCT77q4FZJcJwc6xlpD042SkSStSwcjSMuDZKzivdRNFpt5C2dcIVQW7S)8OEwBSCbwi5AX6pz17KAXoqkyKCacTvs6avo2MipKKes761sFqFXzMrV0JNdK2uSulk36Q5oybP3TPJuF1UBSPqqTgKfObkApEpwPXufDy9A(2iE4NDSDQNDtIQ5a2iBNmzwoj0RX3toMAc(x()l]] ) end