--- ============================ HEADER ============================
--- ======= LOCALIZE =======
-- Addon
local addonName , addonTable = ...
-- HeroDBC
local DBC = HeroDBC.DBC
-- HeroLib
local HL = HeroLib
local Cache = HeroCache
local Unit = HL.Unit
local Player = Unit.Player
local Target = Unit.Target
local Spell = HL.Spell
local MultiSpell = HL.MultiSpell
local Item = HL.Item
local BoolToInt = HL.Utils . BoolToInt
local IntToBool = HL.Utils . IntToBool
local ValueIsInArray = HL.Utils . ValueIsInArray
-- HeroRotation
local HR = HeroRotation
local AoEON = HR.AoEON
local CDsON = HR.CDsON
local Cast = HR.Cast
local CastPooling = HR.CastPooling
local CastLeftNameplate = HR.CastLeftNameplate
-- Num/Bool Helper Functions
local num = HR.Commons . Everyone.num
local bool = HR.Commons . Everyone.bool
-- Lua
local pairs = pairs
local mathfloor = math.floor
local mathmax = math.max
--- ============================ CONTENT ============================
--- ======= APL LOCALS =======
-- Commons
local Everyone = HR.Commons . Everyone
local Rogue = HR.Commons . Rogue
-- GUI Settings
local Settings = {
General = HR.GUISettings . General ,
Commons = HR.GUISettings . APL.Rogue . Commons ,
Commons2 = HR.GUISettings . APL.Rogue . Commons2 ,
Assassination = HR.GUISettings . APL.Rogue . Assassination
}
-- Spells
local S = Spell.Rogue . Assassination
-- Items
local I = Item.Rogue . Assassination
local OnUseExcludeTrinkets = {
I.AlgetharPuzzleBox ,
}
-- Enemies
local MeleeRange , AoERange , TargetInMeleeRange , TargetInAoERange
local Enemies30y , MeleeEnemies10y , MeleeEnemies10yCount , MeleeEnemies5y
-- Rotation Variables
local ShouldReturn
local BleedTickTime , ExsanguinatedBleedTickTime = 2 * Player : SpellHaste ( ) , 1 * Player : SpellHaste ( )
local ComboPoints , ComboPointsDeficit
local RuptureThreshold , CrimsonTempestThreshold , RuptureDMGThreshold , GarroteDMGThreshold , RuptureDurationThreshold , RuptureTickTime , GarroteTickTime
local PriorityRotation
local ExsanguinateSyncRemains , PoisonedBleeds , EnergyRegenCombined , EnergyTimeToMaxCombined , EnergyRegenSaturated , SingleTarget
local TrinketSyncSlot = 0
-- Covenant and Legendaries
local Equipment = Player : GetEquipment ( )
local TrinketItem1 = Equipment [ 13 ] and Item ( Equipment [ 13 ] ) or Item ( 0 )
local TrinketItem2 = Equipment [ 14 ] and Item ( Equipment [ 14 ] ) or Item ( 0 )
local function SetTrinketVariables ( )
-- actions.precombat+=/variable,name=trinket_sync_slot,value=1,if=trinket.1.has_stat.any_dps&(!trinket.2.has_stat.any_dps|trinket.1.cooldown.duration>=trinket.2.cooldown.duration)
-- actions.precombat+=/variable,name=trinket_sync_slot,value=2,if=trinket.2.has_stat.any_dps&(!trinket.1.has_stat.any_dps|trinket.2.cooldown.duration>trinket.1.cooldown.duration)
if TrinketItem1 : HasStatAnyDps ( ) and ( not TrinketItem2 : HasStatAnyDps ( ) or TrinketItem1 : Cooldown ( ) >= TrinketItem2 : Cooldown ( ) ) then
TrinketSyncSlot = 1
elseif TrinketItem2 : HasStatAnyDps ( ) and ( not TrinketItem1 : HasStatAnyDps ( ) or TrinketItem2 : Cooldown ( ) > TrinketItem1 : Cooldown ( ) ) then
TrinketSyncSlot = 2
else
TrinketSyncSlot = 0
end
end
SetTrinketVariables ( )
HL : RegisterForEvent ( function ( )
Equipment = Player : GetEquipment ( )
TrinketItem1 = Equipment [ 13 ] and Item ( Equipment [ 13 ] ) or Item ( 0 )
TrinketItem2 = Equipment [ 14 ] and Item ( Equipment [ 14 ] ) or Item ( 0 )
SetTrinketVariables ( )
end , " PLAYER_EQUIPMENT_CHANGED " )
-- Interrupts
local Interrupts = {
{ S.Blind , " Cast Blind (Interrupt) " , function ( ) return true end } ,
{ S.KidneyShot , " Cast Kidney Shot (Interrupt) " , function ( ) return ComboPoints > 0 end }
}
-- Spells Damage
S.Envenom : RegisterDamageFormula (
-- Envenom DMG Formula:
-- AP * CP * Env_APCoef * Aura_M * ToxicB_M * DS_M * Mastery_M * Versa_M
function ( )
return
-- Attack Power
Player : AttackPowerDamageMod ( ) *
-- Combo Points
ComboPoints *
-- Envenom AP Coef
0.22 *
-- Aura Multiplier (SpellID: 137037)
1.0 *
-- Shiv Multiplier
( Target : DebuffUp ( S.ShivDebuff ) and 1.3 or 1 ) *
-- Deeper Stratagem Multiplier
( S.DeeperStratagem : IsAvailable ( ) and 1.05 or 1 ) *
-- Mastery Finisher Multiplier
( 1 + Player : MasteryPct ( ) / 100 ) *
-- Versatility Damage Multiplier
( 1 + Player : VersatilityDmgPct ( ) / 100 )
end
)
S.Mutilate : RegisterDamageFormula (
function ( )
return
-- Attack Power (MH Factor + OH Factor)
( Player : AttackPowerDamageMod ( ) + Player : AttackPowerDamageMod ( true ) ) *
-- Mutilate Coefficient
0.485 *
-- Aura Multiplier (SpellID: 137037)
1.0 *
-- Versatility Damage Multiplier
( 1 + Player : VersatilityDmgPct ( ) / 100 )
end
)
-- Master Assassin Remains Check
local function MasterAssassinRemains ( )
-- Currently stealthed (i.e. Aura)
if Player : BuffRemains ( S.MasterAssassinBuff ) == 9999 then
return Player : GCDRemains ( ) + 3
end
-- Broke stealth recently (i.e. Buff)
return Player : BuffRemains ( S.MasterAssassinBuff )
end
-- Improved Garrote Remains Check
local function ImprovedGarroteRemains ( )
-- Currently stealthed (i.e. Aura)
if Player : BuffUp ( S.ImprovedGarroteAura ) then
return Player : GCDRemains ( ) + 3
end
-- Broke stealth recently (i.e. Buff)
return Player : BuffRemains ( S.ImprovedGarroteBuff )
end
local function ComputeImprovedGarrotePMultiplier ( )
if S.ImprovedGarrote : IsAvailable ( ) and ( Player : BuffUp ( S.ImprovedGarroteAura , nil , true )
or Player : BuffUp ( S.ImprovedGarroteBuff , nil , true ) or Player : BuffUp ( S.SepsisBuff , nil , true ) ) then
return 1.5
end
return 1
end
S.Garrote : RegisterPMultiplier ( ComputeImprovedGarrotePMultiplier )
--- ======= HELPERS =======
-- Check if the Priority Rotation variable should be set
local function UsePriorityRotation ( )
if MeleeEnemies10yCount < 2 then
return false
elseif Settings.Commons . UsePriorityRotation == " Always " then
return true
elseif Settings.Commons . UsePriorityRotation == " On Bosses " and Target : IsInBossList ( ) then
return true
elseif Settings.Commons . UsePriorityRotation == " Auto " then
-- Zul Mythic
if Player : InstanceDifficulty ( ) == 16 and Target : NPCID ( ) == 138967 then
return true
end
end
return false
end
-- actions+=/variable,name=exsang_sync_remains,op=setif,condition=cooldown.deathmark.remains>cooldown.exsanguinate.remains&cooldown.deathmark.remains<fight_remains,value=cooldown.deathmark.remains,value_else=cooldown.exsanguinate.remains
local function ExsangSyncRemains ( )
if S.Deathmark : CooldownRemains ( ) > S.Exsanguinate : CooldownRemains ( )
and ( HL.BossFightRemainsIsNotValid ( ) or HL.BossFilteredFightRemains ( " > " , S.Deathmark : CooldownRemains ( ) ) ) then
return S.Deathmark : CooldownRemains ( )
end
return S.Exsanguinate : CooldownRemains ( )
end
-- Custom Override for Handling 4pc Pandemics
local function IsDebuffRefreshable ( TargetUnit , Spell , PandemicThreshold )
local PandemicThreshold = PandemicThreshold or Spell : PandemicThreshold ( )
--if Tier284pcEquipped and TargetUnit:DebuffUp(S.Vendetta) then
-- PandemicThreshold = PandemicThreshold * 0.5
--end
return TargetUnit : DebuffRefreshable ( Spell , PandemicThreshold )
end
-- Handle CastLeftNameplate Suggestions for DoT Spells
local function SuggestCycleDoT ( DoTSpell , DoTEvaluation , DoTMinTTD , Enemies )
-- Prefer melee cycle units
local BestUnit , BestUnitTTD = nil , DoTMinTTD
local TargetGUID = Target : GUID ( )
for _ , CycleUnit in pairs ( Enemies ) do
if CycleUnit : GUID ( ) ~= TargetGUID and Everyone.UnitIsCycleValid ( CycleUnit , BestUnitTTD , - CycleUnit : DebuffRemains ( DoTSpell ) )
and DoTEvaluation ( CycleUnit ) then
BestUnit , BestUnitTTD = CycleUnit , CycleUnit : TimeToDie ( )
end
end
if BestUnit then
CastLeftNameplate ( BestUnit , DoTSpell )
-- Check ranged units next, if the RangedMultiDoT option is enabled
elseif Settings.Commons . RangedMultiDoT then
BestUnit , BestUnitTTD = nil , DoTMinTTD
for _ , CycleUnit in pairs ( MeleeEnemies10y ) do
if CycleUnit : GUID ( ) ~= TargetGUID and Everyone.UnitIsCycleValid ( CycleUnit , BestUnitTTD , - CycleUnit : DebuffRemains ( DoTSpell ) )
and DoTEvaluation ( CycleUnit ) then
BestUnit , BestUnitTTD = CycleUnit , CycleUnit : TimeToDie ( )
end
end
if BestUnit then
CastLeftNameplate ( BestUnit , DoTSpell )
end
end
end
-- Target If handler
-- Mode is "min", "max", or "first"
-- ModeEval the target_if condition (function with a target as param)
-- IfEval the condition on the resulting target (function with a target as param)
local function CheckTargetIfTarget ( Mode , ModeEvaluation , IfEvaluation )
-- First mode: Only check target if necessary
local TargetsModeValue = ModeEvaluation ( Target )
if Mode == " first " and TargetsModeValue ~= 0 then
return Target
end
local BestUnit , BestValue = nil , 0
local function RunTargetIfCycler ( Enemies )
for _ , CycleUnit in pairs ( Enemies ) do
local ValueForUnit = ModeEvaluation ( CycleUnit )
if not BestUnit and Mode == " first " then
if ValueForUnit ~= 0 then
BestUnit , BestValue = CycleUnit , ValueForUnit
end
elseif Mode == " min " then
if not BestUnit or ValueForUnit < BestValue then
BestUnit , BestValue = CycleUnit , ValueForUnit
end
elseif Mode == " max " then
if not BestUnit or ValueForUnit > BestValue then
BestUnit , BestValue = CycleUnit , ValueForUnit
end
end
-- Same mode value, prefer longer TTD
if BestUnit and ValueForUnit == BestValue and CycleUnit : TimeToDie ( ) > BestUnit : TimeToDie ( ) then
BestUnit , BestValue = CycleUnit , ValueForUnit
end
end
end
-- Prefer melee cycle units over ranged
RunTargetIfCycler ( MeleeEnemies5y )
if Settings.Commons . RangedMultiDoT then
RunTargetIfCycler ( MeleeEnemies10y )
end
-- Prefer current target if equal mode value results to prevent "flickering"
if BestUnit and BestValue == TargetsModeValue and IfEvaluation ( Target ) then
return Target
end
if BestUnit and IfEvaluation ( BestUnit ) then
return BestUnit
end
return nil
end
-- # Determine if we should be be casting our pre-Exsanguinate Rupture with Echoing Reprimand CP
local function ExsanguinateRuptureCP ( )
-- actions.precombat+=/variable,name=exsanguinate_rupture_cp,value=cp_max_spend<?(talent.resounding_clarity*7)
return S.ResoundingClarity : IsAvailable ( ) and 7 or Rogue.CPMaxSpend ( )
end
local function CheckWillWasteCooldown ( ThisCooldownLength , OtherCooldownRemains , EffectDuration )
local FightRemains = Target : TimeToDie ( )
if not HL.BossFightRemainsIsNotValid ( ) then
FightRemains = HL.BossFightRemains ( )
elseif FightRemains < EffectDuration then
return false -- Bail out if we are not in a boss encounter and fighting a low-HP target
end
-- e.g. if=floor((fight_remains-30)%cooldown)>floor((fight_remains-30-cooldown.vendetta.remains)%cooldown)
if mathfloor ( ( FightRemains - EffectDuration ) / ThisCooldownLength ) >
mathfloor ( ( FightRemains - EffectDuration - OtherCooldownRemains ) / ThisCooldownLength ) then
return true
end
return false
end
-- Serrated Bone Spike Cycle Targets
-- actions.direct+=/serrated_bone_spike,target_if=min:target.time_to_die+(dot.serrated_bone_spike_dot.ticking*600),if=variable.use_filler&!dot.serrated_bone_spike_dot.ticking
local function EvaluateSBSTargetIfConditionCondition ( TargetUnit )
if TargetUnit : DebuffUp ( S.SerratedBoneSpikeDebuff ) then
return 1000000 -- Random big number
end
return TargetUnit : TimeToDie ( )
end
local function EvaluateSBSCondition ( TargetUnit )
return not TargetUnit : DebuffUp ( S.SerratedBoneSpikeDebuff )
end
--- ======= ACTION LISTS =======
local function Racials ( )
-- actions.cds+=/blood_fury,if=debuff.vendetta.up
if S.BloodFury : IsCastable ( ) then
if Cast ( S.BloodFury , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Blood Fury " end
end
-- actions.cds+=/berserking,if=debuff.vendetta.up
if S.Berserking : IsCastable ( ) then
if Cast ( S.Berserking , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Berserking " end
end
-- actions.cds+=/fireblood,if=debuff.vendetta.up
if S.Fireblood : IsCastable ( ) then
if Cast ( S.Fireblood , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Fireblood " end
end
-- actions.cds+=/ancestral_call,if=debuff.vendetta.up
if S.AncestralCall : IsCastable ( ) then
if Cast ( S.AncestralCall , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Ancestral Call " end
end
return false
end
-- # Vanish Handling
local function Vanish ( )
if S.Vanish : IsCastable ( ) and not Player : IsTanking ( Target ) then
if S.ImprovedGarrote : IsAvailable ( ) and S.Garrote : CooldownUp ( ) and not Rogue.Exsanguinated ( Target , S.Garrote )
and ( Target : PMultiplier ( S.Garrote ) <= 1 or IsDebuffRefreshable ( Target , S.Garrote ) ) then
-- actions.vanish+=/vanish,if=talent.improved_garrote&cooldown.garrote.up&!exsanguinated.garrote&(dot.garrote.pmultiplier<=1|dot.garrote.refreshable)&(debuff.deathmark.up|cooldown.deathmark.remains<4)&combo_points.deficit>=(spell_targets.fan_of_knives>?4)
if ( S.Deathmark : AnyDebuffUp ( ) or S.Deathmark : CooldownRemains ( ) < 4 ) and ComboPointsDeficit >= math.min ( MeleeEnemies10yCount , 4 ) then
-- actions.cds+=/pool_resource,for_next=1,extra_amount=45
if Settings.Commons . ShowPooling and Player : EnergyPredicted ( ) < 45 then
if Cast ( S.PoolEnergy ) then return " Pool for Vanish (Garrote Deathmark) " end
end
if Cast ( S.Vanish , Settings.Commons . OffGCDasOffGCD.Vanish ) then return " Cast Vanish (Garrote Deathmark) " end
end
-- actions.vanish+=/vanish,if=talent.improved_garrote&cooldown.garrote.up&!exsanguinated.garrote&(dot.garrote.pmultiplier<=1|dot.garrote.refreshable)&spell_targets.fan_of_knives>(3-talent.indiscriminate_carnage)&(!talent.indiscriminate_carnage|cooldown.indiscriminate_carnage.ready)
if MeleeEnemies10yCount > ( 3 - BoolToInt ( S.IndiscriminateCarnage : IsAvailable ( ) ) ) and ( not S.IndiscriminateCarnage : IsAvailable ( ) or S.IndiscriminateCarnage : CooldownUp ( ) ) then
-- actions.cds+=/pool_resource,for_next=1,extra_amount=45
if Settings.Commons . ShowPooling and Player : EnergyPredicted ( ) < 45 then
if Cast ( S.PoolEnergy ) then return " Pool for Vanish (Garrote) " end
end
if Cast ( S.Vanish , Settings.Commons . OffGCDasOffGCD.Vanish ) then return " Cast Vanish (Garrote) " end
end
end
-- actions.vanish+=/vanish,if=!talent.improved_garrote&talent.master_assassin&!dot.rupture.refreshable&dot.garrote.remains>3&debuff.deathmark.up&(debuff.shiv.up|debuff.deathmark.remains<4|dot.sepsis.ticking)&dot.sepsis.remains<3
if not S.ImprovedGarrote : IsAvailable ( ) and S.MasterAssassin : IsAvailable ( ) and not IsDebuffRefreshable ( Target , S.Rupture ) and Target : DebuffRemains ( S.Garrote ) > 3
and Target : DebuffUp ( S.Deathmark ) and ( Target : DebuffUp ( S.ShivDebuff ) or Target : DebuffRemains ( S.Deathmark ) < 4 or Target : DebuffUp ( S.Sepsis ) ) and Target : DebuffRemains ( S.Sepsis ) < 3 then
if Cast ( S.Vanish , Settings.Commons . OffGCDasOffGCD.Vanish ) then return " Cast Vanish (Master Assassin) " end
end
end
if S.ShadowDance : IsCastable ( ) then
-- actions.vanish+=/shadow_dance,if=talent.improved_garrote&cooldown.garrote.up&!exsanguinated.garrote&(dot.garrote.pmultiplier<=1|dot.garrote.refreshable)&(debuff.deathmark.up|cooldown.deathmark.remains<12|cooldown.deathmark.remains>60)&combo_points.deficit>=(spell_targets.fan_of_knives>?4)
if S.ImprovedGarrote : IsAvailable ( ) and S.Garrote : CooldownUp ( ) and not Rogue.Exsanguinated ( Target , S.Garrote ) and Target : PMultiplier ( S.Garrote ) <= 1
and ( S.Deathmark : AnyDebuffUp ( ) or S.Deathmark : CooldownRemains ( ) < 12 or S.Deathmark : CooldownRemains ( ) > 60 ) and ComboPointsDeficit >= math.min ( MeleeEnemies10yCount , 4 ) then
if Cast ( S.ShadowDance , Settings.Commons . OffGCDasOffGCD.ShadowDance ) then return " Cast Shadow Dance (Garrote) " end
end
-- actions.vanish+=/shadow_dance,if=!talent.improved_garrote&talent.master_assassin&!dot.rupture.refreshable&dot.garrote.remains>3&(debuff.deathmark.up|cooldown.deathmark.remains>60)&(debuff.shiv.up|debuff.deathmark.remains<4|dot.sepsis.ticking)&dot.sepsis.remains<3
if not S.ImprovedGarrote : IsAvailable ( ) and S.MasterAssassin : IsAvailable ( ) and not IsDebuffRefreshable ( Target , S.Rupture )
and Target : DebuffRemains ( S.Garrote ) > 3 and ( Target : DebuffUp ( S.Deathmark ) or S.Deathmark : CooldownRemains ( ) > 60 )
and ( Target : DebuffUp ( S.ShivDebuff ) or Target : DebuffRemains ( S.Deathmark ) < 4 or Target : DebuffUp ( S.Sepsis ) ) and Target : DebuffRemains ( S.Sepsis ) < 3 then
if Cast ( S.ShadowDance , Settings.Commons . OffGCDasOffGCD.ShadowDance ) then return " Cast Shadow Dance (Master Assassin) " end
end
end
end
-- # Cooldowns
local function CDs ( )
if S.MarkedforDeath : IsCastable ( ) then
-- actions.cds+=/marked_for_death,target_if=min:target.time_to_die,if=raid_event.adds.up&(target.time_to_die<combo_points.deficit*1.5|combo_points.deficit>=cp_max_spend)
if Target : FilteredTimeToDie ( " < " , Player : ComboPointsDeficit ( ) * 1.5 ) then
if Cast ( S.MarkedforDeath , Settings.Commons . OffGCDasOffGCD.MarkedforDeath ) then return " Cast Marked for Death " end
end
-- actions.cds+=/marked_for_death,if=raid_event.adds.in>30-raid_event.adds.duration&combo_points.deficit>=cp_max_spend
if ComboPointsDeficit >= Rogue.CPMaxSpend ( ) then
if not Settings.Commons . STMfDAsDPSCD then
HR.CastSuggested ( S.MarkedforDeath )
elseif HR.CDsON ( ) then
if Cast ( S.MarkedforDeath , Settings.Commons . OffGCDasOffGCD.MarkedforDeath ) then return " Cast Marked for Death " end
end
end
end
if not TargetInAoERange then
return
end
if not Player : StealthUp ( true , false ) and HR.CDsON ( ) then
-- actions.cds+=/sepsis,if=!stealthed.rogue&!stealthed.improved_garrote&(!talent.improved_garrote&dot.garrote.ticking|talent.improved_garrote&cooldown.garrote.up)&(target.time_to_die>10|fight_remains<10)
if S.Sepsis : IsReady ( ) and ImprovedGarroteRemains ( ) == 0 and ( S.ImprovedGarrote : IsAvailable ( ) and S.Garrote : CooldownUp ( ) or Target : DebuffUp ( S.Garrote ) )
and ( Target : FilteredTimeToDie ( " > " , 10 ) or HL.BossFilteredFightRemains ( " <= " , 10 ) ) then
if Cast ( S.Sepsis , nil , Settings.Commons . CovenantDisplayStyle ) then return " Cast Sepsis " end
end
-- actions.cds+=/use_item,name=algethar_puzzle_box,use_off_gcd=1,if=(!talent.exsanguinate|cooldown.exsanguinate.remains>15|exsanguinated.rupture|exsanguinated.garrote)&dot.rupture.ticking&cooldown.deathmark.remains<2|fight_remains<=22
-- actions.cds+=/use_items,slots=trinket1,if=(variable.trinket_sync_slot=1&(debuff.deathmark.up|fight_remains<=20)|(variable.trinket_sync_slot=2&(!trinket.2.cooldown.ready|!debuff.deathmark.up&cooldown.deathmark.remains>20))|!variable.trinket_sync_slot)
-- actions.cds+=/use_items,slots=trinket2,if=(variable.trinket_sync_slot=2&(debuff.deathmark.up|fight_remains<=20)|(variable.trinket_sync_slot=1&(!trinket.1.cooldown.ready|!debuff.deathmark.up&cooldown.deathmark.remains>20))|!variable.trinket_sync_slot)
if Settings.Commons . UseTrinkets then
if I.AlgetharPuzzleBox : IsEquippedAndReady ( ) and ( ( ( not S.Exsanguinate : IsAvailable ( ) or S.Exsanguinate : CooldownRemains ( ) > 15
or Rogue.Exsanguinated ( Target , S.Rupture ) or Rogue.Exsanguinated ( Target , S.Garrote ) ) and Target : DebuffUp ( S.Rupture )
and S.Deathmark : CooldownRemains ( ) <= 2 ) or HL.BossFilteredFightRemains ( " < " , 22 ) ) then
if HR.Cast ( I.AlgetharPuzzleBox , nil , Settings.Commons . TrinketDisplayStyle ) then return " Algethar Puzzle Box " ; end
end
if TrinketItem1 : IsReady ( ) and not Player : IsItemBlacklisted ( TrinketItem1 ) and not ValueIsInArray ( OnUseExcludeTrinkets , TrinketItem1 : ID ( ) )
and ( TrinketSyncSlot == 1 and ( S.Deathmark : AnyDebuffUp ( ) or HL.BossFilteredFightRemains ( " < " , 20 ) )
or ( TrinketSyncSlot == 2 and ( not TrinketItem2 : IsReady ( ) or not S.Deathmark : AnyDebuffUp ( ) and S.Deathmark : CooldownRemains ( ) > 20 ) ) or TrinketSyncSlot == 0 ) then
if Cast ( TrinketItem1 , nil , Settings.Commons . TrinketDisplayStyle ) then return " Trinket 1 " ; end
elseif TrinketItem2 : IsReady ( ) and not Player : IsItemBlacklisted ( TrinketItem2 ) and not ValueIsInArray ( OnUseExcludeTrinkets , TrinketItem2 : ID ( ) )
and ( TrinketSyncSlot == 2 and ( S.Deathmark : AnyDebuffUp ( ) or HL.BossFilteredFightRemains ( " < " , 20 ) )
or ( TrinketSyncSlot == 1 and ( not TrinketItem1 : IsReady ( ) or not S.Deathmark : AnyDebuffUp ( ) and S.Deathmark : CooldownRemains ( ) > 20 ) ) or TrinketSyncSlot == 0 ) then
if Cast ( TrinketItem2 , nil , Settings.Commons . TrinketDisplayStyle ) then return " Trinket 2 " ; end
end
end
if S.Deathmark : IsCastable ( ) then
-- actions.cds+=/variable,name=deathmark_exsanguinate_condition,value=!talent.exsanguinate|cooldown.exsanguinate.remains>15|exsanguinated.rupture|exsanguinated.garrote
-- actions.cds+=/variable,name=deathmark_ma_condition,value=!talent.master_assassin.enabled|dot.garrote.ticking
-- actions.cds+=/variable,name=deathmark_condition,value=!stealthed.rogue&dot.rupture.ticking&!debuff.deathmark.up&variable.deathmark_exsanguinate_condition&variable.deathmark_ma_condition
-- actions.cds+=/deathmark,if=variable.deathmark_condition
if Target : DebuffUp ( S.Rupture ) and not S.Deathmark : AnyDebuffUp ( )
and ( not S.Exsanguinate : IsAvailable ( ) or S.Exsanguinate : CooldownRemains ( ) > 15 or Rogue.Exsanguinated ( Target , S.Rupture ) or Rogue.Exsanguinated ( Target , S.Garrote ) )
and ( not S.MasterAssassin : IsAvailable ( ) or Target : DebuffUp ( S.Garrote ) ) then
if Cast ( S.Deathmark , Settings.Assassination . OffGCDasOffGCD.Deathmark ) then return " Cast Deathmark " end
end
end
-- actions.cds+=/kingsbane,if=(debuff.shiv.up|cooldown.shiv.remains<6)&buff.envenom.up&(cooldown.deathmark.remains>=50|dot.deathmark.ticking)
if S.Kingsbane : IsReady ( ) and ( Target : DebuffUp ( S.ShivDebuff ) or S.Shiv : CooldownRemains ( ) < 6 ) and Player : BuffUp ( S.Envenom )
and ( S.Deathmark : CooldownRemains ( ) >= 50 or Target : DebuffUp ( S.Deathmark ) ) then
if Cast ( S.Kingsbane , Settings.Assassination . GCDasOffGCD.Kingsbane ) then return " Cast Kingsbane " end
end
-- actions.cds+=/variable,name=exsanguinate_condition,value=talent.exsanguinate&!stealthed.rogue&!stealthed.improved_garrote&!dot.deathmark.ticking&target.time_to_die>variable.exsang_sync_remains+4&variable.exsang_sync_remains<4
-- actions.cds+=/echoing_reprimand,if=talent.exsanguinate&talent.resounding_clarity&(variable.exsanguinate_condition&combo_points<=2&variable.exsang_sync_remains<=2&!dot.garrote.refreshable&dot.rupture.remains>9.6|variable.exsang_sync_remains>40)
-- actions.cds+=/exsanguinate,if=variable.exsanguinate_condition&(!dot.garrote.refreshable&dot.rupture.remains>4+4*variable.exsanguinate_rupture_cp|dot.rupture.remains*0.5>target.time_to_die)
if S.Exsanguinate : IsAvailable ( ) then
if ImprovedGarroteRemains ( ) == 0 and Target : DebuffDown ( S.Deathmark ) and Target : FilteredTimeToDie ( " > " , ExsanguinateSyncRemains + 4 ) and ExsanguinateSyncRemains < 4 then
if S.ResoundingClarity : IsAvailable ( ) and S.EchoingReprimand : IsReady ( )
and Player : ComboPoints ( ) <= 2 and ExsanguinateSyncRemains <= 2
and not IsDebuffRefreshable ( Target , S.Garrote ) and Target : DebuffRemains ( S.Rupture ) > 9.6 then
if Cast ( S.EchoingReprimand , nil , Settings.Commons . CovenantDisplayStyle , not TargetInMeleeRange ) then return " Cast Echoing Reprimand (Exsang Sync) " end
end
if S.Exsanguinate : IsReady ( ) and not IsDebuffRefreshable ( Target , S.Garrote ) and Target : DebuffRemains ( S.Rupture ) > 4 + 4 * ExsanguinateRuptureCP ( )
or Target : FilteredTimeToDie ( " < " , Target : DebuffRemains ( S.Rupture ) * 0.5 ) then
if Cast ( S.Exsanguinate , Settings.Assassination . GCDasOffGCD.Exsanguinate ) then return " Cast Exsanguinate " end
end
end
end
end
-- actions.cds+=/shiv,if=talent.kingsbane&!debuff.shiv.up&dot.kingsbane.ticking&dot.garrote.ticking&dot.rupture.ticking&(!talent.crimson_tempest.enabled|variable.single_target|dot.crimson_tempest.ticking)
-- actions.cds+=/shiv,if=talent.arterial_precision&!debuff.shiv.up&dot.garrote.ticking&dot.rupture.ticking&(debuff.deathmark.up|cooldown.shiv.charges_fractional>max_charges-0.5&cooldown.deathmark.remains>10)
-- actions.cds+=/shiv,if=talent.sepsis&!talent.kingsbane&!talent.arterial_precision&!debuff.shiv.up&dot.garrote.ticking&dot.rupture.ticking&((cooldown.sepsis.ready|cooldown.sepsis.remains>12)+(cooldown.deathmark.ready|cooldown.deathmark.remains>12)=2)
-- actions.cds+=/shiv,if=!talent.kingsbane&!talent.arterial_precision&!talent.sepsis&!debuff.shiv.up&dot.garrote.ticking&dot.rupture.ticking&(!talent.crimson_tempest.enabled|variable.single_target|dot.crimson_tempest.ticking)&(!talent.exsanguinate|variable.exsang_sync_remains>2)
if S.Shiv : IsCastable ( )
and not Target : DebuffUp ( S.ShivDebuff ) and Target : DebuffUp ( S.Garrote ) and Target : DebuffUp ( S.Rupture ) then
if S.Kingsbane : IsAvailable ( ) then
if Target : DebuffUp ( S.Kingsbane ) and ( not S.CrimsonTempest : IsAvailable ( ) or SingleTarget or Target : DebuffUp ( S.CrimsonTempest ) ) then
if Cast ( S.Shiv , Settings.Assassination . GCDasOffGCD.Shiv ) then return " Cast Shiv (Kingsbane) " end
end
end
if S.ArterialPrecision : IsAvailable ( ) then
if S.Deathmark : AnyDebuffUp ( ) or S.Shiv : ChargesFractional ( ) > ( S.Shiv : MaxCharges ( ) - 0.5 ) and S.Deathmark : CooldownRemains ( ) > 10 then
if Cast ( S.Shiv , Settings.Assassination . GCDasOffGCD.Shiv ) then return " Cast Shiv (Arterial Precision) " end
end
end
if not S.ArterialPrecision : IsAvailable ( ) and not S.ArterialPrecision : IsAvailable ( ) then
if S.Sepsis : IsAvailable ( ) then
if ( BoolToInt ( S.Sepsis : CooldownUp ( ) or S.Sepsis : CooldownRemains ( ) > 12 ) + BoolToInt ( S.Deathmark : CooldownUp ( ) or S.Deathmark : CooldownRemains ( ) > 12 ) == 2 ) then
if Cast ( S.Shiv , Settings.Assassination . GCDasOffGCD.Shiv ) then return " Cast Shiv (Sepsis) " end
end
else
if ( not S.CrimsonTempest : IsAvailable ( ) or SingleTarget or Target : DebuffUp ( S.CrimsonTempest ) ) and ( not S.Exsanguinate : IsAvailable ( ) or ExsanguinateSyncRemains > 2 ) then
if Cast ( S.Shiv , Settings.Assassination . GCDasOffGCD.Shiv ) then return " Cast Shiv " end
end
end
end
end
-- actions.cds+=/thistle_tea,if=!buff.thistle_tea.up&(energy.deficit>=100|charges=3&(dot.kingsbane.ticking|debuff.deathmark.up)|fight_remains<charges*6)
if S.ThistleTea : IsCastable ( ) and not Player : BuffUp ( S.ThistleTea )
and ( Player : EnergyDeficit ( ) >= 100 + EnergyRegenCombined or S.ThistleTea : Charges ( ) == 3 and ( Target : DebuffUp ( S.Kingsbane ) or S.Deathmark : AnyDebuffUp ( ) )
or HL.BossFilteredFightRemains ( " < " , S.ThistleTea : Charges ( ) * 6 ) ) then
if HR.Cast ( S.ThistleTea , Settings.Commons . OffGCDasOffGCD.ThistleTea ) then return " Cast Thistle Tea " end
end
if not HR.CDsON ( ) then
return
end
-- actions.cds+=/indiscriminate_carnage,if=(spell_targets.fan_of_knives>desired_targets|spell_targets.fan_of_knives>1&raid_event.adds.in>60)&(!talent.improved_garrote|cooldown.vanish.remains>45)
if S.IndiscriminateCarnage : IsReady ( ) and MeleeEnemies10yCount > 1 and ( not S.ImprovedGarrote : IsAvailable ( ) or S.Vanish : CooldownRemains ( ) > 45 ) then
if Cast ( S.IndiscriminateCarnage , Settings.Assassination . OffGCDasOffGCD.IndiscriminateCarnage ) then return " Cast Indiscriminate Carnage " end
end
-- actions.cds=potion,if=buff.bloodlust.react|target.time_to_die<=60|debuff.vendetta.up&cooldown.vanish.remains<5
-- Racials
if S.Deathmark : AnyDebuffUp ( ) and ( not ShouldReturn or Settings.Commons . OffGCDasOffGCD.Racials ) then
if ShouldReturn then
Racials ( )
else
ShouldReturn = Racials ( )
end
end
-- actions.cds+=/call_action_list,name=vanish,if=!stealthed.all&master_assassin_remains=0
if not Player : StealthUp ( true , true ) and ImprovedGarroteRemains ( ) <= 0 and MasterAssassinRemains ( ) <= 0 then
if ShouldReturn then
Vanish ( )
else
ShouldReturn = Vanish ( )
end
end
-- actions.cds+=/cold_blood,if=combo_points>=4
if S.ColdBlood : IsReady ( ) and Player : DebuffDown ( S.ColdBlood ) and ComboPoints >= 4
and ( Settings.Commons . OffGCDasOffGCD.ColdBlood or not ShouldReturn ) then
if Cast ( S.ColdBlood , Settings.Commons . OffGCDasOffGCD.ColdBlood ) then return " Cast Cold Blood " end
end
return ShouldReturn
end
-- # Stealthed
local function Stealthed ( )
-- actions.stealthed=indiscriminate_carnage,if=spell_targets.fan_of_knives>desired_targets|spell_targets.fan_of_knives>1&raid_event.adds.in>60
if S.IndiscriminateCarnage : IsReady ( ) and MeleeEnemies10yCount > 1 then
if Cast ( S.IndiscriminateCarnage , Settings.Assassination . OffGCDasOffGCD.IndiscriminateCarnage ) then return " Cast Indiscriminate Carnage " end
end
if S.Garrote : IsCastable ( ) and ImprovedGarroteRemains ( ) > 0 then
-- actions.stealthed+=/garrote,target_if=min:remains,if=stealthed.improved_garrote&!will_lose_exsanguinate&(remains<12%exsanguinated_rate|pmultiplier<=1)&target.time_to_die-remains>2
local function GarroteTargetIfFunc ( TargetUnit )
return TargetUnit : DebuffRemains ( S.Garrote )
end
local function GarroteIfFunc ( TargetUnit )
return not Rogue.WillLoseExsanguinate ( TargetUnit , S.Garrote ) and
( TargetUnit : PMultiplier ( S.Garrote ) <= 1 or TargetUnit : DebuffRemains ( S.Garrote ) < ( 12 / Rogue.ExsanguinatedRate ( TargetUnit , S.Garrote ) ) )
and ( TargetUnit : FilteredTimeToDie ( " > " , 2 , - TargetUnit : DebuffRemains ( S.Garrote ) ) or TargetUnit : TimeToDieIsNotValid ( ) )
and Rogue.CanDoTUnit ( TargetUnit , GarroteDMGThreshold )
end
if HR.AoEON ( ) then
local TargetIfUnit = CheckTargetIfTarget ( " min " , GarroteTargetIfFunc , GarroteIfFunc )
if TargetIfUnit and TargetIfUnit : GUID ( ) ~= Target : GUID ( ) then
CastLeftNameplate ( TargetIfUnit , S.Garrote )
end
end
if GarroteIfFunc ( Target ) then
-- actions.stealthed+=/pool_resource,for_next=1
if CastPooling ( S.Garrote , nil , not TargetInMeleeRange ) then return " Cast Garrote (Improved Garrote) " end
end
-- actions.stealthed+=/garrote,if=talent.exsanguinate.enabled&stealthed.improved_garrote&active_enemies=1&!will_lose_exsanguinate&(remains<18%exsanguinated_rate|pmultiplier<=1)&variable.exsang_sync_remains<18&improved_garrote_remains<1.3
if S.Exsanguinate : IsAvailable ( ) and MeleeEnemies10yCount == 1 and not Rogue.WillLoseExsanguinate ( Target , S.Garrote )
and ( Target : DebuffRemains ( S.Garrote ) < ( 18 / Rogue.ExsanguinatedRate ( Target , S.Garrote ) ) or Target : PMultiplier ( S.Garrote ) <= 1 )
and ExsanguinateSyncRemains < 18 and ImprovedGarroteRemains ( ) < 1.3 then
-- actions.stealthed+=/pool_resource,for_next=1
if CastPooling ( S.Garrote , nil , not TargetInMeleeRange ) then return " Pool for Garrote (Exsanguinate Refresh) " end
end
end
end
-- # Damage over time abilities
local function Dot ( )
local SkipCycleGarrote , SkipCycleRupture , SkipRupture = false , false , false
if PriorityRotation then
-- actions.dot=variable,name=skip_cycle_garrote,value=priority_rotation&(dot.garrote.remains<cooldown.garrote.duration|variable.regen_saturated)
SkipCycleGarrote = MeleeEnemies10yCount > 3 and ( Target : DebuffRemains ( S.Garrote ) < 6 or EnergyRegenSaturated )
-- actions.dot+=/variable,name=skip_cycle_rupture,value=priority_rotation&(debuff.shiv.up&spell_targets.fan_of_knives>2|variable.regen_saturated)
SkipCycleRupture = ( Target : DebuffUp ( S.ShivDebuff ) and MeleeEnemies10yCount > 2 ) or EnergyRegenSaturated
end
-- actions.dot+=/variable,name=skip_rupture,value=0
-- SkipRupture = false
if HR.CDsON ( ) and S.Exsanguinate : IsAvailable ( ) and ExsanguinateSyncRemains < 2 then
-- actions.dot+=/garrote,if=talent.exsanguinate.enabled&!will_lose_exsanguinate&dot.garrote.pmultiplier<=1&cooldown.exsanguinate.remains<2&spell_targets.fan_of_knives=1&raid_event.adds.in>6&dot.garrote.remains*0.5<target.time_to_die
if S.Garrote : IsCastable ( ) and TargetInMeleeRange and MeleeEnemies10yCount == 1
and not Rogue.WillLoseExsanguinate ( Target , S.Garrote ) and Target : PMultiplier ( S.Garrote ) <= 1
and Target : FilteredTimeToDie ( " > " , Target : DebuffRemains ( S.Garrote ) * 0.5 ) then
if CastPooling ( S.Garrote ) then return " Cast Garrote (Pre-Exsanguinate) " end
end
-- actions.dot+=/rupture,if=talent.exsanguinate.enabled&!will_lose_exsanguinate&dot.rupture.pmultiplier<=1&cooldown.exsanguinate.remains<1&effective_combo_points>=variable.exsanguinate_rupture_cp&dot.rupture.remains*0.5<target.time_to_die
if S.Rupture : IsReady ( ) and TargetInMeleeRange and Target : PMultiplier ( S.Rupture ) <= 1 and not Rogue.WillLoseExsanguinate ( Target , S.Rupture )
and ( ComboPoints >= ExsanguinateRuptureCP ( ) and ExsanguinateSyncRemains < 1 and Target : FilteredTimeToDie ( " > " , Target : DebuffRemains ( S.Rupture ) * 0.5 ) ) then
if Cast ( S.Rupture ) then return " Cast Rupture (Pre-Exsanguinate) " end
end
end
-- actions.dot+=/garrote,if=refreshable&combo_points.deficit>=1&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&(target.time_to_die-remains)>4&master_assassin_remains=0
-- actions.dot+=/garrote,cycle_targets=1,if=!variable.skip_cycle_garrote&target!=self.target&refreshable&combo_points.deficit>=1&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&(target.time_to_die-remains)>12&master_assassin_remains=0
if S.Garrote : IsCastable ( ) and ComboPointsDeficit >= 1 then
local function Evaluate_Garrote_Target ( TargetUnit )
GarroteTickTime = Rogue.Exsanguinated ( TargetUnit , S.Garrote ) and ExsanguinatedBleedTickTime or BleedTickTime
return IsDebuffRefreshable ( TargetUnit , S.Garrote ) and MasterAssassinRemains ( ) <= 0
and ( TargetUnit : PMultiplier ( S.Garrote ) <= 1 or ( MeleeEnemies10yCount >= 3 and TargetUnit : DebuffRemains ( S.Garrote ) <= GarroteTickTime ) )
and ( not Rogue.WillLoseExsanguinate ( TargetUnit , S.Garrote ) or TargetUnit : DebuffRemains ( S.Garrote ) <= GarroteTickTime * ( 1 + BoolToInt ( MeleeEnemies10yCount >= 3 ) ) )
end
if Evaluate_Garrote_Target ( Target ) and Rogue.CanDoTUnit ( Target , GarroteDMGThreshold )
and ( Target : FilteredTimeToDie ( " > " , 4 , - Target : DebuffRemains ( S.Garrote ) ) or Target : TimeToDieIsNotValid ( ) ) then
-- actions.dot+=/pool_resource,for_next=1
if CastPooling ( S.Garrote , nil , not TargetInMeleeRange ) then return " Pool for Garrote (ST) " end
end
if HR.AoEON ( ) and not SkipCycleGarrote then
SuggestCycleDoT ( S.Garrote , Evaluate_Garrote_Target , 12 , MeleeEnemies5y )
end
end
-- actions.dot+=/crimson_tempest,target_if=min:remains,if=spell_targets>=2&effective_combo_points>=4&energy.regen_combined>20&(!cooldown.deathmark.ready|dot.rupture.ticking)&remains<(2+3*(spell_targets>=4))
if HR.AoEON ( ) and S.CrimsonTempest : IsReady ( ) and MeleeEnemies10yCount >= 2 and ComboPoints >= 4
and EnergyRegenCombined > 20 and ( not S.Deathmark : CooldownUp ( ) or Target : DebuffUp ( S.Rupture ) ) then
for _ , CycleUnit in pairs ( MeleeEnemies10y ) do
if CycleUnit : DebuffRemains ( S.CrimsonTempest ) < ( 2 + 3 * BoolToInt ( MeleeEnemies10yCount >= 4 ) ) then
if Cast ( S.CrimsonTempest ) then return " Cast Crimson Tempest (AoE) " end
end
end
end
-- actions.dot+=/rupture,if=!variable.skip_rupture&effective_combo_points>=4&refreshable&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&target.time_to_die-remains>(4+(talent.dashing_scoundrel*5)+(talent.doomblade*5)+(variable.regen_saturated*6))
-- actions.dot+=/rupture,cycle_targets=1,if=!variable.skip_cycle_rupture&!variable.skip_rupture&target!=self.target&effective_combo_points>=4&refreshable&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&target.time_to_die-remains>(4+(talent.dashing_scoundrel*5)+(talent.doomblade*5)+(variable.regen_saturated*6))
if S.Rupture : IsReady ( ) and ComboPoints >= 4 then
-- target.time_to_die-remains>(4+(talent.dashing_scoundrel*5)+(talent.doomblade*5)+(variable.regen_saturated*6))
RuptureDurationThreshold = 4 + BoolToInt ( S.DashingScoundrel : IsAvailable ( ) ) * 5 + BoolToInt ( S.Doomblade : IsAvailable ( ) ) * 5 + BoolToInt ( EnergyRegenSaturated ) * 6
local function Evaluate_Rupture_Target ( TargetUnit )
RuptureTickTime = Rogue.Exsanguinated ( TargetUnit , S.Rupture ) and ExsanguinatedBleedTickTime or BleedTickTime
return IsDebuffRefreshable ( TargetUnit , S.Rupture , RuptureThreshold )
and ( TargetUnit : PMultiplier ( S.Rupture ) <= 1 or TargetUnit : DebuffRemains ( S.Rupture ) <= RuptureTickTime and MeleeEnemies10yCount >= 3 )
and ( not Rogue.WillLoseExsanguinate ( TargetUnit , S.Rupture ) or TargetUnit : DebuffRemains ( S.Rupture ) <= RuptureTickTime * ( 1 + BoolToInt ( MeleeEnemies10yCount >= 3 ) ) )
and ( TargetUnit : FilteredTimeToDie ( " > " , RuptureDurationThreshold , - TargetUnit : DebuffRemains ( S.Rupture ) ) or TargetUnit : TimeToDieIsNotValid ( ) )
end
if not SkipRupture and Evaluate_Rupture_Target ( Target ) and Rogue.CanDoTUnit ( Target , RuptureDMGThreshold ) then
if Cast ( S.Rupture , nil , nil , not TargetInMeleeRange ) then return " Cast Rupture (Refresh) " end
end
if not SkipRupture and not SkipCycleRupture and HR.AoEON ( ) then
SuggestCycleDoT ( S.Rupture , Evaluate_Rupture_Target , RuptureDurationThreshold , MeleeEnemies5y )
end
end
-- actions.dot+=/crimson_tempest,if=spell_targets>=2&effective_combo_points>=4&remains<2+3*(spell_targets>=4)
if HR.AoEON ( ) and S.CrimsonTempest : IsReady ( ) and MeleeEnemies10yCount >= 2 and ComboPoints >= 4 then
for _ , CycleUnit in pairs ( MeleeEnemies10y ) do
-- Note: The APL does not do this due to target_if mechanics, just to determine if any targets are low on duration of the AoE Bleed
if CycleUnit : DebuffRemains ( S.CrimsonTempest ) < 2 + 3 * BoolToInt ( MeleeEnemies10yCount >= 4 ) then
if Cast ( S.CrimsonTempest ) then return " Cast Crimson Tempest (AoE Fallback) " end
end
end
end
-- actions.dot+=/crimson_tempest,if=spell_targets=1&!talent.dashing_scoundrel&effective_combo_points>=(cp_max_spend-1)&refreshable&!will_lose_exsanguinate&!debuff.shiv.up&debuff.amplifying_poison.stack<15&(!talent.kingsbane|buff.envenom.up|!cooldown.kingsbane.up)&target.time_to_die-remains>4
if S.CrimsonTempest : IsReady ( ) and TargetInAoERange and MeleeEnemies10yCount == 1
and not S.DashingScoundrel : IsAvailable ( ) and ComboPoints >= ( Rogue.CPMaxSpend ( ) - 1 ) and IsDebuffRefreshable ( Target , S.CrimsonTempest , CrimsonTempestThreshold )
and not Rogue.WillLoseExsanguinate ( Target , S.CrimsonTempest ) and not Target : DebuffUp ( S.ShivDebuff ) and Target : DebuffStack ( S.AmplifyingPoisonDebuff ) < 15
and ( not S.Kingsbane : IsAvailable ( ) or Player : BuffUp ( S.Envenom ) or not S.Kingsbane : CooldownUp ( ) )
and ( Target : FilteredTimeToDie ( " > " , 4 , - Target : DebuffRemains ( S.CrimsonTempest ) ) or Target : TimeToDieIsNotValid ( ) )
and Rogue.CanDoTUnit ( Target , RuptureDMGThreshold ) then
if Cast ( S.CrimsonTempest ) then return " Cast Crimson Tempest (ST) " end
end
return false
end
-- # Direct damage abilities
local function Direct ( )
-- actions.direct=envenom,if=effective_combo_points>=4+talent.deeper_stratagem.enabled&(debuff.deathmark.up|debuff.shiv.up|debuff.amplifying_poison.stack>=10|energy.deficit<=25+energy.regen_combined|!variable.single_target|effective_combo_points>cp_max_spend)&(!talent.exsanguinate.enabled|variable.exsang_sync_remains>2|talent.resounding_clarity&(cooldown.echoing_reprimand.ready&combo_points>2|effective_combo_points>5))
if S.Envenom : IsReady ( ) and ComboPoints >= 4 + BoolToInt ( S.DeeperStratagem : IsAvailable ( ) )
and ( Target : DebuffUp ( S.Deathmark ) or Target : DebuffUp ( S.ShivDebuff ) or Target : DebuffStack ( S.AmplifyingPoisonDebuff ) >= 10
or Player : EnergyDeficit ( ) <= ( 25 + EnergyRegenCombined ) or not SingleTarget or ComboPoints > Rogue.CPMaxSpend ( ) )
and ( not S.Exsanguinate : IsAvailable ( ) or ExsanguinateSyncRemains > 2 or not HR.CDsON ( )
or S.ResoundingClarity : IsAvailable ( ) and ( S.EchoingReprimand : IsReady ( ) and Player : ComboPoints ( ) > 2 or ComboPoints > 5 ) ) then
if Cast ( S.Envenom , nil , nil , not TargetInMeleeRange ) then return " Cast Envenom " end
end
--- !!!! ---
-- actions.direct+=/variable,name=use_filler,value=combo_points.deficit>1|energy.deficit<=25+variable.energy_regen_combined|!variable.single_target
-- Note: This is used in all following fillers, so we just return false if not true and won't consider these.
if not ( ComboPointsDeficit > 1 or Player : EnergyDeficit ( ) <= 25 + EnergyRegenCombined or not SingleTarget ) then
return false
end
--- !!!! ---
if S.SerratedBoneSpike : IsReady ( ) then
-- actions.direct+=/serrated_bone_spike,if=variable.use_filler&!dot.serrated_bone_spike_dot.ticking
if not Target : DebuffUp ( S.SerratedBoneSpikeDebuff ) then
if Cast ( S.SerratedBoneSpike , nil , Settings.Commons . CovenantDisplayStyle , not TargetInAoERange ) then return " Cast Serrated Bone Spike " end
else
-- actions.direct+=/serrated_bone_spike,target_if=min:target.time_to_die+(dot.serrated_bone_spike_dot.ticking*600),if=variable.use_filler&!dot.serrated_bone_spike_dot.ticking
if HR.AoEON ( ) then
if Everyone.CastTargetIf ( S.SerratedBoneSpike , Enemies30y , " min " , EvaluateSBSTargetIfConditionCondition , EvaluateSBSCondition ) then
return " Cast Serrated Bone (AoE) "
end
end
-- actions.direct+=/serrated_bone_spike,if=variable.use_filler&master_assassin_remains<0.8&(fight_remains<=5|cooldown.serrated_bone_spike.max_charges-charges_fractional<=0.25)
-- actions.direct+=/serrated_bone_spike,if=variable.use_filler&master_assassin_remains<0.8&!variable.single_target&debuff.shiv.up
if MasterAssassinRemains ( ) < 0.8 then
if ( HL.BossFightRemains ( ) <= 5 or ( S.SerratedBoneSpike : MaxCharges ( ) - S.SerratedBoneSpike : ChargesFractional ( ) <= 0.25 ) ) then
if Cast ( S.SerratedBoneSpike , nil , Settings.Commons . SerratedBoneSpikeDumpDisplayStyle , not TargetInAoERange ) then return " Cast Serrated Bone Spike (Dump Charge) " end
elseif not SingleTarget and Target : DebuffUp ( S.ShivDebuff ) then
if Cast ( S.SerratedBoneSpike , nil , Settings.Commons . SerratedBoneSpikeDumpDisplayStyle , not TargetInAoERange ) then return " Cast Serrated Bone Spike (Shiv) " end
end
end
end
end
-- actions.direct+=/echoing_reprimand,if=(!talent.exsanguinate|!talent.resounding_clarity)&variable.use_filler&cooldown.deathmark.remains>10|fight_remains<20
if CDsON ( ) and S.EchoingReprimand : IsReady ( ) and ( ( not S.Exsanguinate : IsAvailable ( ) or not S.ResoundingClarity : IsAvailable ( ) or ExsanguinateSyncRemains > 40 )
and ( not S.Deathmark : IsAvailable ( ) or S.Deathmark : CooldownRemains ( ) > 10 ) or HL.BossFilteredFightRemains ( " <= " , 20 ) ) then
if Cast ( S.EchoingReprimand , nil , Settings.Commons . CovenantDisplayStyle , not TargetInMeleeRange ) then return " Cast Echoing Reprimand " end
end
if S.FanofKnives : IsCastable ( ) then
-- actions.direct+=/fan_of_knives,if=variable.use_filler&(!priority_rotation&spell_targets.fan_of_knives>=3+stealthed.rogue+talent.dragontempered_blades)
if HR.AoEON ( ) and MeleeEnemies10yCount >= 1 and ( not PriorityRotation
and MeleeEnemies10yCount >= 3 + BoolToInt ( Player : StealthUp ( true , false ) ) + BoolToInt ( S.DragonTemperedBlades : IsAvailable ( ) ) ) then
if CastPooling ( S.FanofKnives ) then return " Cast Fan of Knives " end
end
-- actions.direct+=/fan_of_knives,target_if=!dot.deadly_poison_dot.ticking&(!priority_rotation|dot.garrote.ticking|dot.rupture.ticking),if=variable.use_filler&spell_targets.fan_of_knives>=3
if HR.AoEON ( ) and Player : BuffUp ( S.DeadlyPoison ) and MeleeEnemies10yCount >= 3 then
for _ , CycleUnit in pairs ( MeleeEnemies10y ) do
if not CycleUnit : DebuffUp ( S.DeadlyPoisonDebuff , true ) and ( not PriorityRotation or CycleUnit : DebuffUp ( S.Garrote ) or CycleUnit : DebuffUp ( S.Rupture ) ) then
if CastPooling ( S.FanofKnives ) then return " Cast Fan of Knives (DP Refresh) " end
end
end
end
end
-- actions.direct+=/ambush,if=variable.use_filler
if S.Ambush : IsCastable ( ) and ( Player : StealthUp ( true , true ) or Player : BuffUp ( S.BlindsideBuff ) ) then
if CastPooling ( S.Ambush , nil , not TargetInMeleeRange ) then return " Cast Ambush " end
end
-- actions.direct+=/mutilate,target_if=!dot.deadly_poison_dot.ticking&!debuff.amplifying_poison.up,if=variable.use_filler&spell_targets.fan_of_knives=2
if S.Mutilate : IsCastable ( ) and MeleeEnemies10yCount == 2 and Target : DebuffDown ( S.DeadlyPoisonDebuff , true )
and Target : DebuffDown ( S.AmplifyingPoisonDebuff , true ) then
local TargetGUID = Target : GUID ( )
for _ , CycleUnit in pairs ( MeleeEnemies5y ) do
-- Note: The APL does not do this due to target_if mechanics, but since we are cycling we should check to see if the unit has a bleed
if CycleUnit : GUID ( ) ~= TargetGUID and ( CycleUnit : DebuffUp ( S.Garrote ) or CycleUnit : DebuffUp ( S.Rupture ) )
and not CycleUnit : DebuffUp ( S.DeadlyPoisonDebuff , true ) and not CycleUnit : DebuffUp ( S.AmplifyingPoisonDebuff , true ) then
CastLeftNameplate ( CycleUnit , S.Mutilate )
break
end
end
end
-- actions.direct+=/mutilate,if=variable.use_filler
if S.Mutilate : IsCastable ( ) then
if CastPooling ( S.Mutilate , nil , not TargetInMeleeRange ) then return " Cast Mutilate " end
end
return false
end
--- ======= MAIN =======
local function APL ( )
-- Enemies Update
MeleeRange = S.AcrobaticStrikes : IsAvailable ( ) and 8 or 5
AoERange = S.AcrobaticStrikes : IsAvailable ( ) and 10 or 13
TargetInMeleeRange = Target : IsInMeleeRange ( MeleeRange )
TargetInAoERange = Target : IsInMeleeRange ( AoERange )
if AoEON ( ) then
Enemies30y = Player : GetEnemiesInRange ( 30 ) -- Poisoned Knife & Serrated Bone Spike
MeleeEnemies10y = Player : GetEnemiesInMeleeRange ( AoERange ) -- Fan of Knives & Crimson Tempest
MeleeEnemies10yCount = # MeleeEnemies10y
MeleeEnemies5y = Player : GetEnemiesInMeleeRange ( MeleeRange ) -- Melee cycle
else
Enemies30y = { }
MeleeEnemies10y = { }
MeleeEnemies10yCount = 1
MeleeEnemies5y = { }
end
-- Rotation Variables Update
BleedTickTime , ExsanguinatedBleedTickTime = 2 * Player : SpellHaste ( ) , 1 * Player : SpellHaste ( )
ComboPoints = Rogue.EffectiveComboPoints ( Player : ComboPoints ( ) )
ComboPointsDeficit = Player : ComboPointsMax ( ) - ComboPoints
RuptureThreshold = ( 4 + ComboPoints * 4 ) * 0.3
CrimsonTempestThreshold = ( 2 + ComboPoints * 2 ) * 0.3
RuptureDMGThreshold = S.Envenom : Damage ( ) * Settings.Assassination . EnvenomDMGOffset ; -- Used to check if Rupture is worth to be casted since it's a finisher.
GarroteDMGThreshold = S.Mutilate : Damage ( ) * Settings.Assassination . MutilateDMGOffset ; -- Used as TTD Not Valid fallback since it's a generator.
PriorityRotation = UsePriorityRotation ( )
-- Defensives
-- Crimson Vial
ShouldReturn = Rogue.CrimsonVial ( )
if ShouldReturn then return ShouldReturn end
-- Feint
ShouldReturn = Rogue.Feint ( )
if ShouldReturn then return ShouldReturn end
-- Poisons
Rogue.Poisons ( )
-- Out of Combat
if not Player : AffectingCombat ( ) then
-- actions=stealth
if not Player : BuffUp ( Rogue.VanishBuffSpell ( ) ) then
ShouldReturn = Rogue.Stealth ( Rogue.StealthSpell ( ) )
if ShouldReturn then return ShouldReturn end
end
-- Flask
-- Food
-- Rune
-- PrePot w/ Bossmod Countdown
-- Opener
if Everyone.TargetIsValid ( ) then
-- Precombat CDs
if HR.CDsON ( ) then
-- actions.precombat+=/marked_for_death,precombat_seconds=10,if=raid_event.adds.in>15
if S.MarkedforDeath : IsCastable ( ) and Player : ComboPointsDeficit ( ) >= Rogue.CPMaxSpend ( ) and Everyone.TargetIsValid ( ) then
if Cast ( S.MarkedforDeath , Settings.Commons . OffGCDasOffGCD.MarkedforDeath ) then return " Cast Marked for Death (OOC) " end
end
end
-- actions.precombat+=/slice_and_dice,precombat_seconds=1
if not Player : BuffUp ( S.SliceandDice ) then
if S.SliceandDice : IsReady ( ) and ComboPoints >= 2 then
if Cast ( S.SliceandDice ) then return " Cast Slice and Dice " end
end
end
end
end
-- In Combat
-- MfD Sniping
Rogue.MfDSniping ( S.MarkedforDeath )
if Everyone.TargetIsValid ( ) then
-- Interrupts
ShouldReturn = Everyone.Interrupt ( 5 , S.Kick , Settings.Commons2 . OffGCDasOffGCD.Kick , Interrupts )
if ShouldReturn then return ShouldReturn end
PoisonedBleeds = Rogue.PoisonedBleeds ( )
-- TODO: Make this match the updated code version
EnergyRegenCombined = Player : EnergyRegen ( ) + PoisonedBleeds * 6 / ( 2 * Player : SpellHaste ( ) )
EnergyTimeToMaxCombined = Player : EnergyDeficit ( ) / EnergyRegenCombined
-- actions+=/variable,name=regen_saturated,value=energy.regen_combined>35
EnergyRegenSaturated = EnergyRegenCombined > 35
ExsanguinateSyncRemains = ExsangSyncRemains ( )
-- actions+=/variable,name=single_target,value=spell_targets.fan_of_knives<2
SingleTarget = MeleeEnemies10yCount < 2
-- actions+=/call_action_list,name=stealthed,if=stealthed.rogue|stealthed.improved_garrote
if Player : StealthUp ( true , false ) or ImprovedGarroteRemains ( ) > 0 then
ShouldReturn = Stealthed ( )
if ShouldReturn then return ShouldReturn .. " (Stealthed) " end
end
-- actions+=/call_action_list,name=cds
ShouldReturn = CDs ( )
if ShouldReturn then return ShouldReturn end
-- # Put SnD up initially for Cut to the Chase, refresh with Envenom if at low duration
-- actions+=/slice_and_dice,if=!buff.slice_and_dice.up&combo_points>=2|!talent.cut_to_the_chase&refreshable&combo_points>=4
if not Player : BuffUp ( S.SliceandDice ) then
if S.SliceandDice : IsReady ( ) and Player : ComboPoints ( ) >= 2
or not S.CutToTheChase : IsAvailable ( ) and Player : ComboPoints ( ) >= 4 and Player : BuffRemains ( S.SliceandDice ) < ( 1 + Player : ComboPoints ( ) ) * 1.8 then
if Cast ( S.SliceandDice ) then return " Cast Slice and Dice " end
end
elseif TargetInAoERange and S.CutToTheChase : IsAvailable ( ) then
-- actions+=/envenom,if=talent.cut_to_the_chase&buff.slice_and_dice.up&buff.slice_and_dice.remains<5&combo_points>=4
if S.Envenom : IsReady ( ) and Player : BuffRemains ( S.SliceandDice ) < 5 and Player : ComboPoints ( ) >= 4 then
if Cast ( S.Envenom , nil , nil , not TargetInMeleeRange ) then return " Cast Envenom (CttC) " end
end
else
--- !!!! ---
-- Special fallback Poisoned Knife Out of Range [EnergyCap] or [PoisonRefresh]
-- Only if we are about to cap energy, not stealthed, and completely out of range
--- !!!! ---
if S.PoisonedKnife : IsCastable ( ) and Target : IsInRange ( 30 ) and not Player : StealthUp ( true , true )
and MeleeEnemies10yCount == 0 and Player : EnergyTimeToMax ( ) <= Player : GCD ( ) * 1.5 then
if Cast ( S.PoisonedKnife ) then return " Cast Poisoned Knife " end
end
end
-- actions+=/call_action_list,name=dot
ShouldReturn = Dot ( )
if ShouldReturn then return ShouldReturn end
-- actions+=/call_action_list,name=direct
ShouldReturn = Direct ( )
if ShouldReturn then return ShouldReturn end
-- Racials
if HR.CDsON ( ) then
-- actions+=/arcane_torrent,if=energy.deficit>=15+variable.energy_regen_combined
if S.ArcaneTorrent : IsCastable ( ) and TargetInMeleeRange and Player : EnergyDeficit ( ) > 15 then
if Cast ( S.ArcaneTorrent , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Arcane Torrent " end
end
-- actions+=/arcane_pulse
if S.ArcanePulse : IsCastable ( ) and TargetInMeleeRange then
if Cast ( S.ArcanePulse , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Arcane Pulse " end
end
-- actions+=/lights_judgment
if S.LightsJudgment : IsCastable ( ) and TargetInMeleeRange then
if Cast ( S.LightsJudgment , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Lights Judgment " end
end
-- actions+=/bag_of_tricks
if S.BagofTricks : IsCastable ( ) and TargetInMeleeRange then
if Cast ( S.BagofTricks , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Bag of Tricks " end
end
end
-- Trick to take in consideration the Recovery Setting
if S.Mutilate : IsCastable ( ) and TargetInAoERange then
if Cast ( S.PoolEnergy ) then return " Normal Pooling " end
end
end
end
local function Init ( )
S.Deathmark : RegisterAuraTracking ( )
S.Sepsis : RegisterAuraTracking ( )
end
HR.SetAPL ( 259 , APL , Init )
--- ======= SIMC =======
-- Last Update: 2023-02-06
-- # Executed before combat begins. Accepts non-harmful actions only.
-- actions.precombat=apply_poison
-- actions.precombat+=/flask
-- actions.precombat+=/augmentation
-- actions.precombat+=/food
-- # Snapshot raid buffed stats before combat begins and pre-potting is done.
-- actions.precombat+=/snapshot_stats
-- actions.precombat+=/marked_for_death,precombat_seconds=10,if=raid_event.adds.in>15
-- # Determine which (if any) stat buff trinket we want to attempt to sync with Deathmark.
-- actions.precombat+=/variable,name=trinket_sync_slot,value=1,if=trinket.1.has_stat.any_dps&(!trinket.2.has_stat.any_dps|trinket.1.cooldown.duration>=trinket.2.cooldown.duration)
-- actions.precombat+=/variable,name=trinket_sync_slot,value=2,if=trinket.2.has_stat.any_dps&(!trinket.1.has_stat.any_dps|trinket.2.cooldown.duration>trinket.1.cooldown.duration)
-- # Determine if we should be be casting our pre-Exsanguinate Rupture with Echoing Reprimand CP
-- actions.precombat+=/variable,name=exsanguinate_rupture_cp,value=cp_max_spend<?(talent.resounding_clarity*7)
-- actions.precombat+=/stealth
-- actions.precombat+=/slice_and_dice,precombat_seconds=1
-- # Executed every time the actor is available.
-- # Restealth if possible (no vulnerable enemies in combat)
-- actions=stealth
-- # Interrupt on cooldown to allow simming interactions with that
-- actions+=/kick
-- actions+=/variable,name=single_target,value=spell_targets.fan_of_knives<2
-- # Combined Energy Regen needed to saturate
-- actions+=/variable,name=regen_saturated,value=energy.regen_combined>35
-- # Next Exsanguinate cooldown time based on Deathmark syncing logic and remaining fight duration
-- actions+=/variable,name=exsang_sync_remains,op=setif,condition=cooldown.deathmark.remains>cooldown.exsanguinate.remains&cooldown.deathmark.remains<fight_remains,value=cooldown.deathmark.remains,value_else=cooldown.exsanguinate.remains
-- actions+=/call_action_list,name=stealthed,if=stealthed.rogue|stealthed.improved_garrote
-- actions+=/call_action_list,name=cds
-- # Put SnD up initially for Cut to the Chase, refresh with Envenom if at low duration
-- actions+=/slice_and_dice,if=!buff.slice_and_dice.up&combo_points>=2|!talent.cut_to_the_chase&refreshable&combo_points>=4
-- actions+=/envenom,if=talent.cut_to_the_chase&buff.slice_and_dice.up&buff.slice_and_dice.remains<5&combo_points>=4
-- actions+=/call_action_list,name=dot
-- actions+=/call_action_list,name=direct
-- actions+=/arcane_torrent,if=energy.deficit>=15+energy.regen_combined
-- actions+=/arcane_pulse
-- actions+=/lights_judgment
-- actions+=/bag_of_tricks
-- # Cooldowns
-- # If adds are up, snipe the one with lowest TTD. Use when dying faster than CP deficit or without any CP.
-- actions.cds=marked_for_death,line_cd=1.5,target_if=min:target.time_to_die,if=raid_event.adds.up&(!variable.single_target|target.time_to_die<30)&(target.time_to_die<combo_points.deficit*1.5|combo_points.deficit>=cp_max_spend)
-- # If no adds will die within the next 30s, use MfD for max CP.
-- actions.cds+=/marked_for_death,if=raid_event.adds.in>30-raid_event.adds.duration&combo_points.deficit>=cp_max_spend
-- # Sync Deathmark window with Exsanguinate if applicable
-- actions.cds+=/variable,name=deathmark_exsanguinate_condition,value=!talent.exsanguinate|cooldown.exsanguinate.remains>15|exsanguinated.rupture|exsanguinated.garrote
-- # Wait on Deathmark for Garrote with MA
-- actions.cds+=/variable,name=deathmark_ma_condition,value=!talent.master_assassin.enabled|dot.garrote.ticking
-- actions.cds+=/sepsis,if=!stealthed.rogue&!stealthed.improved_garrote&(!talent.improved_garrote&dot.garrote.ticking|talent.improved_garrote&cooldown.garrote.up)&(target.time_to_die>10|fight_remains<10)
-- # Deathmark to be used if not stealthed, Rupture is up, and all other talent conditions are satisfied
-- actions.cds+=/variable,name=deathmark_condition,value=!stealthed.rogue&dot.rupture.ticking&!debuff.deathmark.up&variable.deathmark_exsanguinate_condition&variable.deathmark_ma_condition
-- # Sync the priority stat buff trinket with Deathmark, otherwise use on cooldown
-- actions.cds+=/use_item,name=algethar_puzzle_box,use_off_gcd=1,if=(!talent.exsanguinate|cooldown.exsanguinate.remains>15|exsanguinated.rupture|exsanguinated.garrote)&dot.rupture.ticking&cooldown.deathmark.remains<2|fight_remains<=22
-- actions.cds+=/use_items,slots=trinket1,if=(variable.trinket_sync_slot=1&(debuff.deathmark.up|fight_remains<=20)|(variable.trinket_sync_slot=2&(!trinket.2.cooldown.ready|!debuff.deathmark.up&cooldown.deathmark.remains>20))|!variable.trinket_sync_slot)
-- actions.cds+=/use_items,slots=trinket2,if=(variable.trinket_sync_slot=2&(debuff.deathmark.up|fight_remains<=20)|(variable.trinket_sync_slot=1&(!trinket.1.cooldown.ready|!debuff.deathmark.up&cooldown.deathmark.remains>20))|!variable.trinket_sync_slot)
-- actions.cds+=/deathmark,if=variable.deathmark_condition
-- actions.cds+=/kingsbane,if=(debuff.shiv.up|cooldown.shiv.remains<6)&buff.envenom.up&(cooldown.deathmark.remains>=50|dot.deathmark.ticking)
-- # Exsanguinate when not stealthed and both Rupture and Garrote are up for long enough. Attempt to sync with Deathmark and also Echoing Reprimand if using Resounding Clarity.
-- actions.cds+=/variable,name=exsanguinate_condition,value=talent.exsanguinate&!stealthed.rogue&!stealthed.improved_garrote&!dot.deathmark.ticking&target.time_to_die>variable.exsang_sync_remains+4&variable.exsang_sync_remains<4
-- actions.cds+=/echoing_reprimand,if=talent.exsanguinate&talent.resounding_clarity&(variable.exsanguinate_condition&combo_points<=2&variable.exsang_sync_remains<=2&!dot.garrote.refreshable&dot.rupture.remains>9.6|variable.exsang_sync_remains>40)
-- actions.cds+=/exsanguinate,if=variable.exsanguinate_condition&(!dot.garrote.refreshable&dot.rupture.remains>4+4*variable.exsanguinate_rupture_cp|dot.rupture.remains*0.5>target.time_to_die)
-- # Shiv if DoTs are up; Always Shiv with Kingsbane, otherwise attempt to sync with Sepsis or Deathmark if we won't waste more than half Shiv's cooldown
-- actions.cds+=/shiv,if=talent.kingsbane&!debuff.shiv.up&dot.kingsbane.ticking&dot.garrote.ticking&dot.rupture.ticking&(!talent.crimson_tempest.enabled|variable.single_target|dot.crimson_tempest.ticking)
-- actions.cds+=/shiv,if=talent.arterial_precision&!debuff.shiv.up&dot.garrote.ticking&dot.rupture.ticking&(debuff.deathmark.up|cooldown.shiv.charges_fractional>max_charges-0.5&cooldown.deathmark.remains>10)
-- actions.cds+=/shiv,if=talent.sepsis&!talent.kingsbane&!talent.arterial_precision&!debuff.shiv.up&dot.garrote.ticking&dot.rupture.ticking&((cooldown.sepsis.ready|cooldown.sepsis.remains>12)+(cooldown.deathmark.ready|cooldown.deathmark.remains>12)=2)
-- actions.cds+=/shiv,if=!talent.kingsbane&!talent.arterial_precision&!talent.sepsis&!debuff.shiv.up&dot.garrote.ticking&dot.rupture.ticking&(!talent.crimson_tempest.enabled|variable.single_target|dot.crimson_tempest.ticking)
-- actions.cds+=/thistle_tea,if=!buff.thistle_tea.up&(energy.deficit>=100|charges=3&(dot.kingsbane.ticking|debuff.deathmark.up)|fight_remains<charges*6)
-- actions.cds+=/indiscriminate_carnage,if=(spell_targets.fan_of_knives>desired_targets|spell_targets.fan_of_knives>1&raid_event.adds.in>60)&(!talent.improved_garrote|cooldown.vanish.remains>45)
-- actions.cds+=/potion,if=buff.bloodlust.react|fight_remains<30|debuff.deathmark.up
-- actions.cds+=/blood_fury,if=debuff.deathmark.up
-- actions.cds+=/berserking,if=debuff.deathmark.up
-- actions.cds+=/fireblood,if=debuff.deathmark.up
-- actions.cds+=/ancestral_call,if=debuff.deathmark.up
-- actions.cds+=/call_action_list,name=vanish,if=!stealthed.all&master_assassin_remains=0
-- actions.cds+=/cold_blood,if=combo_points>=4
-- # Direct damage abilities
-- # Envenom at 4+ (5+ with DS) CP. Immediately on 2+ targets, with Deathmark, or with TB; otherwise wait for some energy. Also wait if Exsg combo is coming up.
-- actions.direct=envenom,if=effective_combo_points>=4+talent.deeper_stratagem.enabled&(debuff.deathmark.up|debuff.shiv.up|debuff.amplifying_poison.stack>=10|energy.deficit<=25+energy.regen_combined|!variable.single_target|effective_combo_points>cp_max_spend)&(!talent.exsanguinate.enabled|variable.exsang_sync_remains>2|talent.resounding_clarity&(cooldown.echoing_reprimand.ready&combo_points>2|effective_combo_points>5))
-- actions.direct+=/variable,name=use_filler,value=combo_points.deficit>1|energy.deficit<=25+energy.regen_combined|!variable.single_target
-- # Apply SBS to all targets without a debuff as priority, preferring targets dying sooner after the primary target
-- actions.direct+=/serrated_bone_spike,if=variable.use_filler&!dot.serrated_bone_spike_dot.ticking
-- actions.direct+=/serrated_bone_spike,target_if=min:target.time_to_die+(dot.serrated_bone_spike_dot.ticking*600),if=variable.use_filler&!dot.serrated_bone_spike_dot.ticking
-- # Keep from capping charges or burn at the end of fights
-- actions.direct+=/serrated_bone_spike,if=variable.use_filler&master_assassin_remains<0.8&(fight_remains<=5|cooldown.serrated_bone_spike.max_charges-charges_fractional<=0.25)
-- # When MA is not at high duration, sync with Shiv
-- actions.direct+=/serrated_bone_spike,if=variable.use_filler&master_assassin_remains<0.8&!variable.single_target&debuff.shiv.up
-- actions.direct+=/echoing_reprimand,if=(!talent.exsanguinate|!talent.resounding_clarity)&variable.use_filler&cooldown.deathmark.remains>10|fight_remains<20
-- # Fan of Knives at 3+ targets or 4+ with DTB
-- actions.direct+=/fan_of_knives,if=variable.use_filler&(!priority_rotation&spell_targets.fan_of_knives>=3+stealthed.rogue+talent.dragontempered_blades)
-- # Fan of Knives to apply poisons if inactive on any target (or any bleeding targets with priority rotation) at 3T
-- actions.direct+=/fan_of_knives,target_if=!dot.deadly_poison_dot.ticking&(!priority_rotation|dot.garrote.ticking|dot.rupture.ticking),if=variable.use_filler&spell_targets.fan_of_knives>=3
-- actions.direct+=/ambush,if=variable.use_filler
-- # Tab-Mutilate to apply Deadly Poison at 2 targets
-- actions.direct+=/mutilate,target_if=!dot.deadly_poison_dot.ticking&!debuff.amplifying_poison.up,if=variable.use_filler&spell_targets.fan_of_knives=2
-- actions.direct+=/mutilate,if=variable.use_filler
-- # Damage over time abilities
-- # Limit secondary Garrotes for priority rotation if we have 35 energy regen or Garrote will expire on the primary target
-- actions.dot=variable,name=skip_cycle_garrote,value=priority_rotation&(dot.garrote.remains<cooldown.garrote.duration|variable.regen_saturated)
-- # Limit secondary Ruptures for priority rotation if we have 35 energy regen or Shiv is up on 2T+
-- actions.dot+=/variable,name=skip_cycle_rupture,value=priority_rotation&(debuff.shiv.up&spell_targets.fan_of_knives>2|variable.regen_saturated)
-- # Limit Ruptures when appropriate, not currently used
-- actions.dot+=/variable,name=skip_rupture,value=0
-- # Special Garrote and Rupture setup prior to Exsanguinate cast
-- actions.dot+=/garrote,if=talent.exsanguinate.enabled&!will_lose_exsanguinate&dot.garrote.pmultiplier<=1&variable.exsang_sync_remains<2&spell_targets.fan_of_knives=1&raid_event.adds.in>6&dot.garrote.remains*0.5<target.time_to_die
-- actions.dot+=/rupture,if=talent.exsanguinate.enabled&!will_lose_exsanguinate&dot.rupture.pmultiplier<=1&variable.exsang_sync_remains<1&effective_combo_points>=variable.exsanguinate_rupture_cp&dot.rupture.remains*0.5<target.time_to_die
-- # Garrote upkeep, also tries to use it as a special generator for the last CP before a finisher
-- actions.dot+=/pool_resource,for_next=1
-- actions.dot+=/garrote,if=refreshable&combo_points.deficit>=1&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&(target.time_to_die-remains)>4&master_assassin_remains=0
-- actions.dot+=/pool_resource,for_next=1
-- actions.dot+=/garrote,cycle_targets=1,if=!variable.skip_cycle_garrote&target!=self.target&refreshable&combo_points.deficit>=1&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&(target.time_to_die-remains)>12&master_assassin_remains=0
-- # Crimson Tempest on multiple targets at 4+ CP when running out in 2-5s as long as we have enough regen and aren't setting up for Deathmark
-- actions.dot+=/crimson_tempest,target_if=min:remains,if=spell_targets>=2&effective_combo_points>=4&energy.regen_combined>20&(!cooldown.deathmark.ready|dot.rupture.ticking)&remains<(2+3*(spell_targets>=4))
-- # Keep up Rupture at 4+ on all targets (when living long enough and not snapshot)
-- actions.dot+=/rupture,if=!variable.skip_rupture&effective_combo_points>=4&refreshable&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&target.time_to_die-remains>(4+(talent.dashing_scoundrel*5)+(talent.doomblade*5)+(variable.regen_saturated*6))
-- actions.dot+=/rupture,cycle_targets=1,if=!variable.skip_cycle_rupture&!variable.skip_rupture&target!=self.target&effective_combo_points>=4&refreshable&(pmultiplier<=1|remains<=tick_time&spell_targets.fan_of_knives>=3)&(!will_lose_exsanguinate|remains<=tick_time*2&spell_targets.fan_of_knives>=3)&target.time_to_die-remains>(4+(talent.dashing_scoundrel*5)+(talent.doomblade*5)+(variable.regen_saturated*6))
-- # Fallback AoE Crimson Tempest with the same logic as above, but ignoring the energy conditions if we aren't using Rupture
-- actions.dot+=/crimson_tempest,if=spell_targets>=2&effective_combo_points>=4&remains<2+3*(spell_targets>=4)
-- # Crimson Tempest on ST if in pandemic and nearly max energy and if Envenom won't do more damage due to TB/MA
-- actions.dot+=/crimson_tempest,if=spell_targets=1&!talent.dashing_scoundrel&effective_combo_points>=(cp_max_spend-1)&refreshable&!will_lose_exsanguinate&!debuff.shiv.up&debuff.amplifying_poison.stack<15&(!talent.kingsbane|buff.envenom.up|!cooldown.kingsbane.up)&target.time_to_die-remains>4
-- # Stealthed Actions
-- actions.stealthed=indiscriminate_carnage,if=spell_targets.fan_of_knives>desired_targets|spell_targets.fan_of_knives>1&raid_event.adds.in>60
-- # Improved Garrote: Apply or Refresh with buffed Garrotes
-- actions.stealthed+=/pool_resource,for_next=1
-- actions.stealthed+=/garrote,target_if=min:remains,if=stealthed.improved_garrote&!will_lose_exsanguinate&(remains<12%exsanguinated_rate|pmultiplier<=1)&target.time_to_die-remains>2
-- # Improved Garrote + Exsg on 1T: Refresh Garrote at the end of stealth to get max duration before Exsanguinate
-- actions.stealthed+=/pool_resource,for_next=1
-- actions.stealthed+=/garrote,if=talent.exsanguinate.enabled&stealthed.improved_garrote&active_enemies=1&!will_lose_exsanguinate&(remains<18%exsanguinated_rate|pmultiplier<=1)&variable.exsang_sync_remains<18&improved_garrote_remains<1.3
-- # Stealth Cooldowns
-- # Vanish Sync for Improved Garrote with Deathmark
-- actions.vanish=pool_resource,for_next=1,extra_amount=45
-- actions.vanish+=/vanish,if=talent.improved_garrote&cooldown.garrote.up&!exsanguinated.garrote&(dot.garrote.pmultiplier<=1|dot.garrote.refreshable)&(debuff.deathmark.up|cooldown.deathmark.remains<4)&combo_points.deficit>=(spell_targets.fan_of_knives>?4)
-- # Vanish for Indiscriminate Carnage or Improved Garrote at 2-3+ targets
-- actions.vanish+=/pool_resource,for_next=1,extra_amount=45
-- actions.vanish+=/vanish,if=talent.improved_garrote&cooldown.garrote.up&!exsanguinated.garrote&(dot.garrote.pmultiplier<=1|dot.garrote.refreshable)&spell_targets.fan_of_knives>(3-talent.indiscriminate_carnage)&(!talent.indiscriminate_carnage|cooldown.indiscriminate_carnage.ready)
-- # Vanish with Master Assassin: Rupture+Garrote not in refresh range, during Deathmark+Shiv. Sync with Sepsis final hit if possible.
-- actions.vanish+=/vanish,if=!talent.improved_garrote&talent.master_assassin&!dot.rupture.refreshable&dot.garrote.remains>3&debuff.deathmark.up&(debuff.shiv.up|debuff.deathmark.remains<4|dot.sepsis.ticking)&dot.sepsis.remains<3
-- actions.vanish+=/pool_resource,for_next=1,extra_amount=45
-- # Shadow Dance for Improved Garrote with Deathmark
-- actions.vanish+=/shadow_dance,if=talent.improved_garrote&cooldown.garrote.up&!exsanguinated.garrote&(dot.garrote.pmultiplier<=1|dot.garrote.refreshable)&(debuff.deathmark.up|cooldown.deathmark.remains<12|cooldown.deathmark.remains>60)&combo_points.deficit>=(spell_targets.fan_of_knives>?4)
-- # Shadow Dance with Master Assassin: Rupture+Garrote not in refresh range, during Deathmark+Shiv. Sync with Sepsis final hit if possible.
-- actions.vanish+=/shadow_dance,if=!talent.improved_garrote&talent.master_assassin&!dot.rupture.refreshable&dot.garrote.remains>3&(debuff.deathmark.up|cooldown.deathmark.remains>60)&(debuff.shiv.up|debuff.deathmark.remains<4|dot.sepsis.ticking)&dot.sepsis.remains<3