--- Localize Vars
-- 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
-- HeroRotation
local HR = HeroRotation
local AoEON = HR.AoEON
local CDsON = HR.CDsON
-- Num/Bool Helper Functions
local num = HR.Commons . Everyone.num
local bool = HR.Commons . Everyone.bool
-- Lua
local pairs = pairs
local tableinsert = table.insert
local mathmin = math.min
local mathmax = math.max
local mathabs = math.abs
--- APL Local Vars
-- Commons
local Everyone = HR.Commons . Everyone
local Rogue = HR.Commons . Rogue
-- Define S/I for spell and item arrays
local S = Spell.Rogue . Subtlety
local I = Item.Rogue . Subtlety
-- Create table to exclude above trinkets from On Use function
local OnUseExcludes = {
I.ManicGrieftorch : ID ( ) ,
I.BeaconToTheBeyond : ID ( ) ,
}
-- Rotation Var
local MeleeRange , AoERange , TargetInMeleeRange , TargetInAoERange
local Enemies30y , MeleeEnemies10y , MeleeEnemies10yCount , MeleeEnemies5y
local ShouldReturn ; -- Used to get the return string
local PoolingAbility , PoolingEnergy , PoolingFinisher ; -- Used to store an ability we might want to pool for as a fallback in the current situation
local RuptureThreshold , RuptureDMGThreshold
local EffectiveComboPoints , ComboPoints , ComboPointsDeficit , StealthEnergyRequired
local PriorityRotation
S.Eviscerate : RegisterDamageFormula (
-- Eviscerate DMG Formula (Pre-Mitigation):
--- Player Modifier
-- AP * CP * EviscR1_APCoef * Aura_M * NS_M * DS_M * DSh_M * SoD_M * Finality_M * Mastery_M * Versa_M
--- Target Modifier
-- EviscR2_M * Sinful_M
function ( )
return
--- Player Modifier
-- Attack Power
Player : AttackPowerDamageMod ( ) *
-- Combo Points
EffectiveComboPoints *
-- Eviscerate R1 AP Coef
0.176 *
-- Aura Multiplier (SpellID: 137035)
1.21 *
-- Nightstalker Multiplier
( S.Nightstalker : IsAvailable ( ) and Player : StealthUp ( true , false ) and 1.08 or 1 ) *
-- Deeper Stratagem Multiplier
( S.DeeperStratagem : IsAvailable ( ) and 1.05 or 1 ) *
-- Shadow Dance Multiplier
( S.DarkShadow : IsAvailable ( ) and Player : BuffUp ( S.ShadowDanceBuff ) and 1.3 or 1 ) *
-- Symbols of Death Multiplier
( Player : BuffUp ( S.SymbolsofDeath ) and 1.1 or 1 ) *
-- Finality Multiplier
( Player : BuffUp ( S.FinalityEviscerateBuff ) and 1.3 or 1 ) *
-- Mastery Finisher Multiplier
( 1 + Player : MasteryPct ( ) / 100 ) *
-- Versatility Damage Multiplier
( 1 + Player : VersatilityDmgPct ( ) / 100 ) *
--- Target Modifier
-- Eviscerate R2 Multiplier
( Target : DebuffUp ( S.FindWeaknessDebuff ) and 1.5 or 1 )
end
)
S.Rupture : RegisterPMultiplier (
function ( )
return Player : BuffUp ( S.FinalityRuptureBuff ) and 1.3 or 1
end
)
-- GUI Settings
local Settings = {
General = HR.GUISettings . General ,
Commons = HR.GUISettings . APL.Rogue . Commons ,
Commons2 = HR.GUISettings . APL.Rogue . Commons2 ,
Subtlety = HR.GUISettings . APL.Rogue . Subtlety
}
local function SetPoolingAbility ( PoolingSpell , EnergyThreshold )
if not PoolingAbility then
PoolingAbility = PoolingSpell
PoolingEnergy = EnergyThreshold or 0
end
end
local function SetPoolingFinisher ( PoolingSpell )
if not PoolingFinisher then
PoolingFinisher = PoolingSpell
end
end
local function MayBurnShadowDance ( )
if Settings.Subtlety . BurnShadowDance == " On Bosses not in Dungeons " and Player : IsInDungeonArea ( ) then
return false
elseif Settings.Subtlety . BurnShadowDance ~= " Always " and not Target : IsInBossList ( ) then
return false
else
return true
end
end
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
-- Council of Blood
elseif Target : NPCID ( ) == 166969 or Target : NPCID ( ) == 166971 or Target : NPCID ( ) == 166970 then
return true
-- Anduin (Remnant of a Fallen King/Monstrous Soul)
elseif Target : NPCID ( ) == 183463 or Target : NPCID ( ) == 183671 then
return true
end
end
return false
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
HR.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
HR.CastLeftNameplate ( BestUnit , DoTSpell )
end
end
end
-- APL Action Lists (and Variables)
local function Stealth_Threshold ( )
-- actions+=/variable,name=stealth_threshold,value=25+talent.vigor.enabled*20+talent.master_of_shadows.enabled*20+talent.shadow_focus.enabled*25+talent.alacrity.enabled*20+25*(spell_targets.shuriken_storm>=4)
return 25 + num ( S.Vigor : IsAvailable ( ) ) * 20 + num ( S.MasterofShadows : IsAvailable ( ) ) * 20 + num ( S.ShadowFocus : IsAvailable ( ) ) * 25 + num ( S.Alacrity : IsAvailable ( ) ) * 20 + num ( MeleeEnemies10yCount >= 4 ) * 25
end
local function ShD_Threshold ( )
-- actions.stealth_cds=variable,name=shd_threshold,value=cooldown.shadow_dance.charges_fractional>=0.75+talent.shadow_dance
return S.ShadowDance : ChargesFractional ( ) >= 0.75 + BoolToInt ( S.ShadowDanceTalent : IsAvailable ( ) )
end
local function ShD_Combo_Points ( )
-- actions.stealth_cds+=/variable,name=shd_combo_points,value=combo_points<=1
-- actions.stealth_cds+=/variable,name=shd_combo_points,value=combo_points.deficit<=1,if=spell_targets.shuriken_storm>(4-2*talent.shuriken_tornado.enabled)|variable.priority_rotation&spell_targets.shuriken_storm>=4
-- actions.stealth_cds+=/variable,name=shd_combo_points,value=1,if=spell_targets.shuriken_storm=4
if MeleeEnemies10yCount == ( 4 - num ( S.SealFate : IsAvailable ( ) ) ) then
return true
elseif MeleeEnemies10yCount > ( 4 - 2 * BoolToInt ( S.ShurikenTornado : IsAvailable ( ) ) ) or PriorityRotation and MeleeEnemies10yCount >= 4 then
return ComboPointsDeficit <= 1
else
return ComboPoints <= 1
end
end
local function SnD_Condition ( )
-- actions+=/variable,name=snd_condition,value=buff.slice_and_dice.up|spell_targets.shuriken_storm>=cp_max_spend
return Player : BuffUp ( S.SliceandDice ) or MeleeEnemies10yCount >= Rogue.CPMaxSpend ( )
end
local function Skip_Rupture ( ShadowDanceBuff )
-- actions.finish+=/variable,name=skip_rupture,value=buff.thistle_tea.up&spell_targets.shuriken_storm=1|buff.shadow_dance.up&(spell_targets.shuriken_storm=1|dot.rupture.ticking&spell_targets.shuriken_storm>=2)
return Player : BuffUp ( S.ThistleTea ) and MeleeEnemies10yCount == 1
or ShadowDanceBuff and ( MeleeEnemies10yCount == 1 or Target : DebuffUp ( S.Rupture ) and MeleeEnemies10yCount >= 2 )
end
local function Rotten_Condition ( )
-- variable,name=rotten_condition,value=!buff.premeditation.up&spell_targets.shuriken_storm=1|!talent.the_rotten|spell_targets.shuriken_storm>1
return Player : BuffDown ( S.PremeditationBuff ) and MeleeEnemies10yCount == 1 or not S.TheRotten : IsAvailable ( ) or MeleeEnemies10yCount > 1
end
local function Rotten_Threshold ( )
-- variable,name=rotten_threshold,value=!buff.the_rotten.up|spell_targets.shuriken_storm>1|combo_points<=2&buff.the_rotten.up&!set_bonus.tier30_2pc
return not Player : BuffUp ( S.TheRottenBuff ) or MeleeEnemies10yCount > 1 or ( ComboPoints <= 2 and Player : BuffUp ( S.TheRottenBuff ) and not Player : HasTier ( 30 , 2 ) )
end
local function Secret_Condition ( ShadowDanceBuff , PremeditationBuff )
-- actions.finish=variable,name=secret_condition,value=buff.shadow_dance.up&(buff.danse_macabre.stack>=3|!talent.danse_macabre)&(!buff.premeditation.up|spell_targets.shuriken_storm!=2)
return ShadowDanceBuff and ( Player : BuffStack ( S.DanseMacabreBuff ) >= 3 or not S.DanseMacabre : IsAvailable ( ) )
and ( not PremeditationBuff or MeleeEnemies10yCount ~= 2 )
end
local function Used_For_Danse ( Spell )
return Player : BuffUp ( S.ShadowDanceBuff ) and Spell : TimeSinceLastCast ( ) < S.ShadowDance : TimeSinceLastCast ( )
end
-- # Finishers
-- ReturnSpellOnly and StealthSpell parameters are to Predict Finisher in case of Stealth Macros
local function Finish ( ReturnSpellOnly , StealthSpell )
local ShadowDanceBuff = Player : BuffUp ( S.ShadowDanceBuff )
local ShadowDanceBuffRemains = Player : BuffRemains ( S.ShadowDanceBuff )
local SymbolsofDeathBuffRemains = Player : BuffRemains ( S.SymbolsofDeath )
local FinishComboPoints = ComboPoints
-- State changes based on predicted Stealth casts
local PremeditationBuff = Player : BuffUp ( S.PremeditationBuff ) or ( StealthSpell and S.Premeditation : IsAvailable ( ) )
if StealthSpell and StealthSpell : ID ( ) == S.ShadowDance : ID ( ) then
ShadowDanceBuff = true
ShadowDanceBuffRemains = 8 + S.ImprovedShadowDance : TalentRank ( )
if S.TheFirstDance : IsAvailable ( ) then
FinishComboPoints = mathmin ( Player : ComboPointsMax ( ) , ComboPoints + 4 )
end
if Player : HasTier ( 30 , 2 ) then
SymbolsofDeathBuffRemains = mathmax ( SymbolsofDeathBuffRemains , 6 )
end
end
if S.SliceandDice : IsCastable ( ) and HL.FilteredFightRemains ( MeleeEnemies10y , " > " , Player : BuffRemains ( S.SliceandDice ) ) then
-- actions.finish=variable,name=premed_snd_condition,value=talent.premeditation.enabled&spell_targets.shuriken_storm<5
if S.Premeditation : IsAvailable ( ) and MeleeEnemies10yCount < 5 then
-- actions.finish+=/slice_and_dice,if=variable.premed_snd_condition&cooldown.shadow_dance.charges_fractional<1.75&buff.slice_and_dice.remains<cooldown.symbols_of_death.remains&(cooldown.shadow_dance.ready&buff.symbols_of_death.remains-buff.shadow_dance.remains<1.2)
if S.ShadowDance : ChargesFractional ( ) < 1.75 and Player : BuffRemains ( S.SliceandDice ) < S.SymbolsofDeath : CooldownRemains ( )
and ( S.ShadowDance : Charges ( ) >= 1 and SymbolsofDeathBuffRemains - ShadowDanceBuffRemains < 1.2 ) then
if ReturnSpellOnly then
return S.SliceandDice
else
if S.SliceandDice : IsReady ( ) and HR.Cast ( S.SliceandDice ) then return " Cast Slice and Dice (Premed) " end
SetPoolingFinisher ( S.SliceandDice )
end
end
else
-- actions.finish+=/slice_and_dice,if=!variable.premed_snd_condition&spell_targets.shuriken_storm<6&!buff.shadow_dance.up&buff.slice_and_dice.remains<fight_remains&refreshable
if MeleeEnemies10yCount < 6 and not ShadowDanceBuff
and Player : BuffRemains ( S.SliceandDice ) < ( 1 + FinishComboPoints * 1.8 ) then
if ReturnSpellOnly then
return S.SliceandDice
else
if S.SliceandDice : IsReady ( ) and HR.Cast ( S.SliceandDice ) then return " Cast Slice and Dice " end
SetPoolingFinisher ( S.SliceandDice )
end
end
end
end
local SkipRupture = Skip_Rupture ( ShadowDanceBuff )
-- actions.finish+=/rupture,if=(!variable.skip_rupture|variable.priority_rotation)&target.time_to_die-remains>6&refreshable
if ( not SkipRupture or PriorityRotation ) and S.Rupture : IsCastable ( ) then
if TargetInMeleeRange
and ( Target : FilteredTimeToDie ( " > " , 6 , - Target : DebuffRemains ( S.Rupture ) ) or Target : TimeToDieIsNotValid ( ) )
and Rogue.CanDoTUnit ( Target , RuptureDMGThreshold )
and Target : DebuffRefreshable ( S.Rupture , RuptureThreshold ) then
if ReturnSpellOnly then
return S.Rupture
else
if S.Rupture : IsReady ( ) and HR.Cast ( S.Rupture ) then return " Cast Rupture 1 " end
SetPoolingFinisher ( S.Rupture )
end
end
end
-- actions.finish+=/rupture,if=!variable.skip_rupture&buff.finality_rupture.up&cooldown.shadow_dance.remains<12&cooldown.shadow_dance.charges_fractional<=1&spell_targets.shuriken_storm=1&(talent.dark_brew|talent.danse_macabre)
if not SkipRupture and S.Rupture : IsCastable ( ) then
if MeleeEnemies10yCount == 1 and Player : BuffUp ( S.FinalityRuptureBuff ) and ( S.DarkBrew : IsAvailable ( ) or S.DanseMacabre : IsAvailable ( ) )
and S.ShadowDance : CooldownRemains ( ) < 12 and S.ShadowDance : ChargesFractional ( ) <= 1 then
if ReturnSpellOnly then
return S.Rupture
else
if S.Rupture : IsReady ( ) and HR.Cast ( S.Rupture ) then return " Cast Rupture (Finality) " end
SetPoolingFinisher ( S.Rupture )
end
end
end
-- actions.finish+=/cold_blood,if=variable.secret_condition&cooldown.secret_technique.ready
if S.ColdBlood : IsReady ( ) and Secret_Condition ( ShadowDanceBuff , PremeditationBuff ) and S.SecretTechnique : CooldownUp ( ) then
if Settings.Commons . OffGCDasOffGCD.ColdBlood then
HR.Cast ( S.ColdBlood , Settings.Commons . OffGCDasOffGCD.ColdBlood )
else
if ReturnSpellOnly then return S.ColdBlood end
if HR.Cast ( S.ColdBlood ) then return " Cast Cold Blood (SecTec) " end
end
end
-- actions.finish+=/secret_technique,if=variable.secret_condition&(!talent.cold_blood|cooldown.cold_blood.remains>buff.shadow_dance.remains-2)
-- Attention: Due to the SecTec/ColdBlood interaction, this adaption has additional checks not found in the APL string
if S.SecretTechnique : IsReady ( ) and Secret_Condition ( ShadowDanceBuff , PremeditationBuff )
and ( not S.ColdBlood : IsAvailable ( ) or ( Settings.Commons . OffGCDasOffGCD.ColdBlood and S.ColdBlood : IsReady ( ) )
or Player : BuffUp ( S.ColdBlood ) or S.ColdBlood : CooldownRemains ( ) > ShadowDanceBuffRemains - 2 ) then
if ReturnSpellOnly then return S.SecretTechnique end
if HR.Cast ( S.SecretTechnique ) then return " Cast Secret Technique " end
end
if not SkipRupture and S.Rupture : IsCastable ( ) then
-- actions.finish+=/rupture,cycle_targets=1,if=!variable.skip_rupture&!variable.priority_rotation&spell_targets.shuriken_storm>=2&target.time_to_die>=(2*combo_points)&refreshable
if not ReturnSpellOnly and HR.AoEON ( ) and not PriorityRotation and MeleeEnemies10yCount >= 2 then
local function Evaluate_Rupture_Target ( TargetUnit )
return Everyone.CanDoTUnit ( TargetUnit , RuptureDMGThreshold )
and TargetUnit : DebuffRefreshable ( S.Rupture , RuptureThreshold )
end
SuggestCycleDoT ( S.Rupture , Evaluate_Rupture_Target , ( 2 * FinishComboPoints ) , MeleeEnemies5y )
end
-- actions.finish+=/rupture,if=!variable.skip_rupture&remains<cooldown.symbols_of_death.remains+10&cooldown.symbols_of_death.remains<=5&target.time_to_die-remains>cooldown.symbols_of_death.remains+5
if TargetInMeleeRange and Target : DebuffRemains ( S.Rupture ) < S.SymbolsofDeath : CooldownRemains ( ) + 10
and S.SymbolsofDeath : CooldownRemains ( ) <= 5
and Rogue.CanDoTUnit ( Target , RuptureDMGThreshold )
and Target : FilteredTimeToDie ( " > " , 5 + S.SymbolsofDeath : CooldownRemains ( ) , - Target : DebuffRemains ( S.Rupture ) ) then
if ReturnSpellOnly then
return S.Rupture
else
if S.Rupture : IsReady ( ) and HR.Cast ( S.Rupture ) then return " Cast Rupture 2 " end
SetPoolingFinisher ( S.Rupture )
end
end
end
-- actions.finish+=/black_powder,if=!variable.priority_rotation&spell_targets>=3|!used_for_danse&buff.shadow_dance.up&spell_targets.shuriken_storm=2&talent.danse_macabre
if S.BlackPowder : IsCastable ( ) and ( not PriorityRotation and MeleeEnemies10yCount >= 3
or ( MeleeEnemies10yCount == 2 and ShadowDanceBuff and S.DanseMacabre : IsAvailable ( ) and not Used_For_Danse ( S.BlackPowder ) ) ) then
if ReturnSpellOnly then
return S.BlackPowder
else
if S.BlackPowder : IsReady ( ) and HR.Cast ( S.BlackPowder ) then return " Cast Black Powder " end
SetPoolingFinisher ( S.BlackPowder )
end
end
-- actions.finish+=/eviscerate
if S.Eviscerate : IsCastable ( ) and TargetInMeleeRange then
if ReturnSpellOnly then
return S.Eviscerate
else
if S.Eviscerate : IsReady ( ) and HR.Cast ( S.Eviscerate ) then return " Cast Eviscerate " end
SetPoolingFinisher ( S.Eviscerate )
end
end
return false
end
-- # Stealthed Rotation
-- ReturnSpellOnly and StealthSpell parameters are to Predict Finisher in case of Stealth Macros
local function Stealthed ( ReturnSpellOnly , StealthSpell )
local ShadowDanceBuff = Player : BuffUp ( S.ShadowDanceBuff )
local ShadowDanceBuffRemains = Player : BuffRemains ( S.ShadowDanceBuff )
local TheRottenBuff = Player : BuffUp ( S.TheRottenBuff )
local StealthComboPoints , StealthComboPointsDeficit = ComboPoints , ComboPointsDeficit
-- State changes based on predicted Stealth casts
local PremeditationBuff = Player : BuffUp ( S.PremeditationBuff ) or ( StealthSpell and S.Premeditation : IsAvailable ( ) )
local SilentStormBuff = Player : BuffUp ( S.SilentStormBuff ) or ( StealthSpell and S.SilentStorm : IsAvailable ( ) )
local StealthBuff = Player : BuffUp ( Rogue.StealthSpell ( ) ) or ( StealthSpell and StealthSpell : ID ( ) == Rogue.StealthSpell ( ) : ID ( ) )
local VanishBuffCheck = Player : BuffUp ( Rogue.VanishBuffSpell ( ) ) or ( StealthSpell and StealthSpell : ID ( ) == S.Vanish : ID ( ) )
if StealthSpell and StealthSpell : ID ( ) == S.ShadowDance : ID ( ) then
ShadowDanceBuff = true
ShadowDanceBuffRemains = 8 + S.ImprovedShadowDance : TalentRank ( )
if S.TheRotten : IsAvailable ( ) and Player : HasTier ( 30 , 2 ) then
TheRottenBuff = true
end
if S.TheFirstDance : IsAvailable ( ) then
StealthComboPoints = mathmin ( Player : ComboPointsMax ( ) , ComboPoints + 4 )
StealthComboPointsDeficit = Player : ComboPointsMax ( ) - StealthComboPoints
end
end
local StealthEffectiveComboPoints = Rogue.EffectiveComboPoints ( StealthComboPoints )
local ShadowstrikeIsCastable = S.Shadowstrike : IsCastable ( ) or StealthBuff or VanishBuffCheck or ShadowDanceBuff or Player : BuffUp ( S.SepsisBuff )
if StealthBuff or VanishBuffCheck then
ShadowstrikeIsCastable = ShadowstrikeIsCastable and Target : IsInRange ( 25 )
else
ShadowstrikeIsCastable = ShadowstrikeIsCastable and TargetInMeleeRange
end
-- actions.stealthed=shadowstrike,if=(buff.stealth.up|buff.vanish.up)&(spell_targets.shuriken_storm<4|variable.priority_rotation)
if ShadowstrikeIsCastable and ( StealthBuff or VanishBuffCheck ) and ( MeleeEnemies10yCount < 4 or PriorityRotation ) then
if ReturnSpellOnly then
return S.Shadowstrike
else
if HR.Cast ( S.Shadowstrike ) then return " Cast Shadowstrike (Stealth) " end
end
end
-- #Variable to Gloomblade / Backstab when on 4 or 5 combo points with premediation and when the combo point is not anima charged
-- actions.stealthed+=/variable,name=gloomblade_condition,value=buff.danse_macabre.stack<5&(combo_points.deficit=2|combo_points.deficit=3)&(buff.premeditation.up|effective_combo_points<7)&(spell_targets.shuriken_storm<=8|talent.lingering_shadow)
local GloombladeCondition = ( Player : BuffStack ( S.DanseMacabreBuff ) < 5 and ( StealthComboPointsDeficit == 2 or StealthComboPointsDeficit == 3 )
and ( PremeditationBuff or StealthEffectiveComboPoints < 7 ) and ( MeleeEnemies10yCount <= 8 or S.LingeringShadow : IsAvailable ( ) ) )
-- actions.stealthed+=/shuriken_storm,if=variable.gloomblade_condition&buff.silent_storm.up&!debuff.find_weakness.remains&talent.improved_shuriken_storm.enabled|combo_points<=1&!used_for_danse&spell_targets.shuriken_storm=2&talent.danse_macabre
if ( GloombladeCondition and SilentStormBuff and Target : DebuffDown ( S.FindWeaknessDebuff ) and S.ImprovedShurikenStorm : IsAvailable ( ) )
or ( S.DanseMacabre : IsAvailable ( ) and StealthComboPoints <= 1 and MeleeEnemies10yCount == 2 and not Used_For_Danse ( S.ShurikenStorm ) ) then
if ReturnSpellOnly then
return S.ShurikenStorm
else
if HR.Cast ( S.ShurikenStorm ) then return " Cast Shuriken Storm (FW) " end
end
end
-- actions.stealthed+=/gloomblade,if=variable.gloomblade_condition&(!used_for_danse|spell_targets.shuriken_storm!=2)|combo_points<=2&buff.the_rotten.up&spell_targets.shuriken_storm<=3
if S.Gloomblade : IsCastable ( ) and ( ( GloombladeCondition and ( not Used_For_Danse ( S.Gloomblade ) or MeleeEnemies10yCount ~= 2 ) )
or ( StealthComboPoints <= 2 and TheRottenBuff and MeleeEnemies10yCount <= 3 ) ) then
if ReturnSpellOnly then
-- If calling from a Stealth macro, we don't need the PV suggestion since it's already a macro cast
if StealthSpell then
return S.Gloomblade
else
return { S.Gloomblade , S.Stealth }
end
else
if HR.CastQueue ( S.Gloomblade , S.Stealth ) then return " Cast Gloomblade (Stealth) " end
end
end
-- actions.stealthed+=/backstab,if=variable.gloomblade_condition&talent.danse_macabre&buff.danse_macabre.stack<=2&spell_targets.shuriken_storm<=2
if S.Backstab : IsCastable ( ) and GloombladeCondition and S.DanseMacabre : IsAvailable ( ) and not Used_For_Danse ( S.Backstab )
and Player : BuffStack ( S.DanseMacabreBuff ) <= 2 and MeleeEnemies10yCount <= 2 then
if ReturnSpellOnly then
-- If calling from a Stealth macro, we don't need the PV suggestion since it's already a macro cast
if StealthSpell then
return S.Backstab
else
return { S.Backstab , S.Stealth }
end
else
if HR.CastQueue ( S.Backstab , S.Stealth ) then return " Cast Backstab (Stealth) " end
end
end
-- actions.stealthed+=/call_action_list,name=finish,if=effective_combo_points>=cp_max_spend
if StealthEffectiveComboPoints >= Rogue.CPMaxSpend ( ) then
return Finish ( ReturnSpellOnly , StealthSpell )
end
-- actions.stealthed+=/call_action_list,name=finish,if=buff.shuriken_tornado.up&combo_points.deficit<=2
if Player : BuffUp ( S.ShurikenTornado ) and StealthComboPointsDeficit <= 2 then
return Finish ( ReturnSpellOnly , StealthSpell )
end
-- actions.stealthed+=/call_action_list,name=finish,if=spell_targets.shuriken_storm>=4-talent.seal_fate&variable.effective_combo_points>=4
if MeleeEnemies10yCount >= ( 4 - BoolToInt ( S.SealFate : IsAvailable ( ) ) ) and StealthEffectiveComboPoints >= 4 then
return Finish ( ReturnSpellOnly , StealthSpell )
end
-- actions.stealthed+=/call_action_list,name=finish,if=combo_points.deficit<=1+(talent.seal_fate|talent.deeper_stratagem|talent.secret_stratagem)
if StealthComboPointsDeficit <= 1 + num ( S.SealFate : IsAvailable ( ) or S.DeeperStratagem : IsAvailable ( ) or S.SecretStratagem : IsAvailable ( ) ) then
return Finish ( ReturnSpellOnly , StealthSpell )
end
-- As we're in stealth, show a special macro combo with the PV icon to make it clear we are casting Backstab specifically within Shadow Dance
-- actions.stealthed+=/gloomblade,if=buff.perforated_veins.stack>=5&spell_targets.shuriken_storm<3
-- actions.stealthed+=/backstab,if=buff.perforated_veins.stack>=5&spell_targets.shuriken_storm<3
if Player : BuffStack ( S.PerforatedVeinsBuff ) >= 5 and MeleeEnemies10yCount < 3 then
if S.Gloomblade : IsCastable ( ) then
if ReturnSpellOnly then
-- If calling from a Stealth macro, we don't need the PV suggestion since it's already a macro cast
if StealthSpell then
return S.Gloomblade
else
return { S.Gloomblade , S.PerforatedVeins }
end
else
if HR.CastQueue ( S.Gloomblade , S.PerforatedVeins ) then return " Cast Gloomblade (Stealth PV) " end
end
elseif S.Backstab : IsCastable ( ) then
if ReturnSpellOnly then
-- If calling from a Stealth macro, we don't need the PV suggestion since it's already a macro cast
if StealthSpell then
return S.Backstab
else
return { S.Backstab , S.PerforatedVeins }
end
else
if HR.CastQueue ( S.Backstab , S.PerforatedVeins ) then return " Cast Backstab (Stealth PV) " end
end
end
end
-- actions.stealthed+=/shadowstrike,if=stealthed.sepsis&spell_targets.shuriken_storm<4
if ShadowstrikeIsCastable and not Player : StealthUp ( true , false ) and not StealthSpell and Player : BuffUp ( S.SepsisBuff ) and MeleeEnemies10yCount < 4 then
if ReturnSpellOnly then
return S.Shadowstrike
else
if HR.Cast ( S.Shadowstrike ) then return " Cast Shadowstrike (Sepsis) " end
end
end
-- actions.stealthed+=/shuriken_storm,if=spell_targets>=3+buff.the_rotten.up&(!buff.premeditation.up|spell_targets>=7&!variable.priority_rotation)
if HR.AoEON ( ) and S.ShurikenStorm : IsCastable ( )
and MeleeEnemies10yCount >= ( 3 + BoolToInt ( TheRottenBuff ) )
and ( not PremeditationBuff or ( MeleeEnemies10yCount >= 7 and not PriorityRotation ) ) then
if ReturnSpellOnly then
return S.ShurikenStorm
else
if HR.Cast ( S.ShurikenStorm ) then return " Cast Shuriken Storm " end
end
end
-- actions.stealthed+=/shadowstrike,if=debuff.find_weakness.remains<=1|cooldown.symbols_of_death.remains<18&debuff.find_weakness.remains<cooldown.symbols_of_death.remains
if ShadowstrikeIsCastable and ( Target : DebuffRemains ( S.FindWeaknessDebuff ) < 1 or S.SymbolsofDeath : CooldownRemains ( ) < 18
and Target : DebuffRemains ( S.FindWeaknessDebuff ) < S.SymbolsofDeath : CooldownRemains ( ) ) then
if ReturnSpellOnly then
return S.Shadowstrike
else
if HR.Cast ( S.Shadowstrike ) then return " Cast Shadowstrike (FW Refresh) " end
end
end
-- actions.stealthed+=/shadowstrike
if ShadowstrikeIsCastable then
if ReturnSpellOnly then
return S.Shadowstrike
else
if HR.Cast ( S.Shadowstrike ) then return " Cast Shadowstrike 2 " end
end
end
return false
end
-- # Stealth Macros
-- This returns a table with the original Stealth spell and the result of the Stealthed action list as if the applicable buff was present
local function StealthMacro ( StealthSpell , EnergyThreshold )
-- Fetch the predicted ability to use after the stealth spell
local MacroAbility = Stealthed ( true , StealthSpell )
-- Handle StealthMacro GUI options
-- If false, just suggest them as off-GCD and bail out of the macro functionality
if StealthSpell : ID ( ) == S.Vanish : ID ( ) and ( not Settings.Subtlety . StealthMacro.Vanish or not MacroAbility ) then
if HR.Cast ( S.Vanish , Settings.Commons . OffGCDasOffGCD.Vanish ) then return " Cast Vanish " end
return false
elseif StealthSpell : ID ( ) == S.Shadowmeld : ID ( ) and ( not Settings.Subtlety . StealthMacro.Shadowmeld or not MacroAbility ) then
if HR.Cast ( S.Shadowmeld , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Shadowmeld " end
return false
elseif StealthSpell : ID ( ) == S.ShadowDance : ID ( ) and ( not Settings.Subtlety . StealthMacro.ShadowDance or not MacroAbility ) then
if HR.Cast ( S.ShadowDance , Settings.Subtlety . OffGCDasOffGCD.ShadowDance ) then return " Cast Shadow Dance " end
return false
end
local MacroTable = { StealthSpell , MacroAbility }
-- Set the stealth spell only as a pooling fallback if we did not meet the threshold
if EnergyThreshold and Player : EnergyPredicted ( ) < EnergyThreshold then
SetPoolingAbility ( MacroTable , EnergyThreshold )
return false
end
ShouldReturn = HR.CastQueue ( unpack ( MacroTable ) )
if ShouldReturn then return " | " .. MacroTable [ 2 ] : Name ( ) end
return false
end
-- # Cooldowns
local function CDs ( )
if Player : BuffUp ( S.ShurikenTornado ) then
-- actions.cds+=/shadow_dance,off_gcd=1,if=!buff.shadow_dance.up&buff.shuriken_tornado.up&buff.shuriken_tornado.remains<=3.5
-- actions.cds+=/symbols_of_death,use_off_gcd=1,if=buff.shuriken_tornado.up&buff.shuriken_tornado.remains<=3.5
if S.SymbolsofDeath : IsCastable ( ) and S.ShadowDance : IsCastable ( ) and not Player : BuffUp ( S.SymbolsofDeath ) and not Player : BuffUp ( S.ShadowDanceBuff ) then
if HR.CastQueue ( S.SymbolsofDeath , S.ShadowDance ) then return " Dance + Symbols (during Tornado) " end
elseif S.SymbolsofDeath : IsCastable ( ) and not Player : BuffUp ( S.SymbolsofDeath ) then
if HR.Cast ( S.SymbolsofDeath ) then return " Cast Symbols of Death (during Tornado) " end
elseif S.ShadowDance : IsCastable ( ) and not Player : BuffUp ( S.ShadowDanceBuff ) then
if HR.Cast ( S.ShadowDance ) then return " Cast Shadow Dance (during Tornado) " end
end
end
local SnDCondition = SnD_Condition ( )
-- actions.cds+=/vanish,if=buff.danse_macabre.stack>3&combo_points<=2&(cooldown.secret_technique.remains>=30|!talent.secret_technique)
if S.Vanish : IsCastable ( ) and ComboPoints <= 2 and Player : BuffStack ( S.DanseMacabreBuff ) > 3
and ( S.SecretTechnique : CooldownRemains ( ) >= 30 or not S.SecretTechnique : IsAvailable ( ) )
and not ( Everyone.IsSoloMode ( ) and Player : IsTanking ( Target ) ) then
ShouldReturn = StealthMacro ( S.Vanish )
if ShouldReturn then return " Vanish Macro (DM) " .. ShouldReturn end
end
-- actions.cds+=/cold_blood,if=!talent.secret_technique&combo_points>=5
if S.ColdBlood : IsReady ( ) and not S.SecretTechnique : IsAvailable ( ) and ComboPoints >= 5 then
if HR.Cast ( S.ColdBlood , Settings.Commons . OffGCDasOffGCD.ColdBlood ) then return " Cast Cold Blood " end
end
if TargetInMeleeRange then
-- actions.cds+=/flagellation,target_if=max:target.time_to_die,if=variable.snd_condition&combo_points>=5&target.time_to_die>10
if HR.CDsON ( ) and S.Flagellation : IsReady ( ) and SnDCondition and not Player : StealthUp ( false , false ) and ComboPoints >= 5 and Target : FilteredTimeToDie ( " > " , 10 ) then
if HR.Cast ( S.Flagellation , nil , Settings.Commons . CovenantDisplayStyle ) then return " Cast Flagellation " end
end
end
-- actions.cds+=/shuriken_tornado,if=spell_targets.shuriken_storm<=1&energy>=60&variable.snd_condition&cooldown.symbols_of_death.up&cooldown.shadow_dance.charges>=1&(!talent.flagellation.enabled&!cooldown.flagellation.up|buff.flagellation_buff.up|spell_targets.shuriken_storm>=5)&combo_points<=2&!buff.premeditation.up
if S.ShurikenTornado : IsCastable ( ) and MeleeEnemies10yCount <= 1 and SnDCondition and S.SymbolsofDeath : CooldownUp ( ) and S.ShadowDance : Charges ( ) >= 1
and ( not S.Flagellation : IsAvailable ( ) or Player : BuffUp ( S.Flagellation ) or MeleeEnemies10yCount >= 5 )
and ComboPoints <= 2 and not Player : BuffUp ( S.PremeditationBuff ) then
-- actions.cds+=/pool_resource,for_next=1,if=talent.shuriken_tornado.enabled&!talent.shadow_focus.enabled
if Player : Energy ( ) >= 60 then
if HR.Cast ( S.ShurikenTornado , Settings.Subtlety . GCDasOffGCD.ShurikenTornado ) then return " Cast Shuriken Tornado " end
elseif not S.ShadowFocus : IsAvailable ( ) then
if HR.CastPooling ( S.ShurikenTornado ) then return " Pool for Shuriken Tornado " end
end
end
if TargetInMeleeRange then
-- actions.cds+=/sepsis,if=variable.snd_condition&combo_points.deficit>=1&target.time_to_die>=16
if HR.CDsON ( ) and S.Sepsis : IsReady ( ) and SnDCondition and ComboPointsDeficit >= 1 and not Target : FilteredTimeToDie ( " < " , 16 ) then
if HR.Cast ( S.Sepsis , nil , Settings.Commons . CovenantDisplayStyle ) then return " Cast Sepsis " end
end
-- actions.cds+=/symbols_of_death,if=(buff.symbols_of_death.remains<=3&!cooldown.shadow_dance.ready|!set_bonus.tier30_2pc)&variable.rotten_condition&variable.snd_condition&(!talent.flagellation&(combo_points<=1|!talent.the_rotten)|cooldown.flagellation.remains>10|cooldown.flagellation.up&combo_points>=5)
if S.SymbolsofDeath : IsCastable ( ) then
if ( ( Player : BuffRemains ( S.SymbolsofDeath ) <= 3 and not S.ShadowDance : CooldownUp ( ) ) or not Player : HasTier ( 30 , 2 ) ) and Rotten_Condition ( ) and SnDCondition
and ( ( not S.Flagellation : IsAvailable ( ) and ( ComboPoints <= 1 or not S.TheRotten : IsAvailable ( ) ) )
or S.Flagellation : CooldownRemains ( ) > 10 or ( S.Flagellation : CooldownUp ( ) and ComboPoints >= 5 ) ) then
if HR.Cast ( S.SymbolsofDeath , Settings.Subtlety . OffGCDasOffGCD.SymbolsofDeath ) then
return " Cast Symbols of Death "
end
end
end
end
if S.MarkedforDeath : IsCastable ( ) then
-- actions.cds+=/marked_for_death,target_if=min:target.time_to_die,if=target.time_to_die<combo_points.deficit
if Target : FilteredTimeToDie ( " < " , ComboPointsDeficit ) then
if HR.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&!stealthed.all&combo_points.deficit>=cp_max_spend
if not Player : StealthUp ( true , true ) and ComboPointsDeficit >= Rogue.CPMaxSpend ( ) then
if not Settings.Commons . STMfDAsDPSCD then
HR.CastSuggested ( S.MarkedforDeath )
elseif HR.CDsON ( ) then
if HR.Cast ( S.MarkedforDeath , Settings.Commons . OffGCDasOffGCD.MarkedforDeath ) then return " Cast Marked for Death " end
end
end
end
if HR.CDsON ( ) then
-- actions.cds+=/shadow_blades,if=variable.snd_condition&combo_points.deficit>=2&target.time_to_die>=10&(dot.sepsis.ticking|cooldown.sepsis.remains<=8|!talent.sepsis)|fight_remains<=20
if S.ShadowBlades : IsCastable ( ) and ( SnDCondition and ComboPointsDeficit >= 2 and Target : FilteredTimeToDie ( " >= " , 10 )
and ( not S.Sepsis : IsAvailable ( ) or S.Sepsis : CooldownRemains ( ) <= 8 or Target : DebuffUp ( S.Sepsis ) ) or HL.BossFilteredFightRemains ( " <= " , 20 ) ) then
if HR.Cast ( S.ShadowBlades , Settings.Subtlety . OffGCDasOffGCD.ShadowBlades ) then return " Cast Shadow Blades " end
end
-- actions.cds+=/echoing_reprimand,if=variable.snd_condition&combo_points.deficit>=3&(variable.priority_rotation|spell_targets.shuriken_storm<=4|talent.resounding_clarity)&(buff.shadow_dance.up|!talent.danse_macabre)
if S.EchoingReprimand : IsReady ( ) and TargetInMeleeRange and ComboPointsDeficit >= 3
and ( PriorityRotation or MeleeEnemies10yCount <= 4 or S.ResoundingClarity : IsAvailable ( ) )
and ( Player : BuffUp ( S.ShadowDanceBuff ) or not S.DanseMacabre : IsAvailable ( ) ) then
if HR.Cast ( S.EchoingReprimand , nil , Settings.Commons . CovenantDisplayStyle ) then return " Cast Echoing Reprimand " end
end
-- actions.cds+=/shuriken_tornado,if=variable.snd_condition&buff.symbols_of_death.up&combo_points<=2&(!buff.premeditation.up|spell_targets.shuriken_storm>4)
-- actions.cds+=/shuriken_tornado,if=cooldown.shadow_dance.ready&!stealthed.all&spell_targets.shuriken_storm>=3&!talent.flagellation.enabled
if S.ShurikenTornado : IsReady ( ) then
if SnD_Condition and Player : BuffUp ( S.SymbolsofDeath ) and ComboPoints <= 2 and ( not Player : BuffUp ( S.PremeditationBuff ) or MeleeEnemies10yCount > 4 ) then
if HR.Cast ( S.ShurikenTornado , Settings.Subtlety . GCDasOffGCD.ShurikenTornado ) then return " Cast Shuriken Tornado (SoD) " end
end
if not S.Flagellation : IsAvailable ( ) and MeleeEnemies10yCount >= 3 and S.ShadowDance : Charges ( ) >= 1 and not Player : StealthUp ( true , true ) then
if HR.Cast ( S.ShurikenTornado , Settings.Subtlety . GCDasOffGCD.ShurikenTornado ) then return " Cast Shuriken Tornado (Dance) " end
end
end
-- actions.cds+=/shadow_dance,if=!buff.shadow_dance.up&fight_remains<=8+talent.subterfuge.enabled
if S.ShadowDance : IsCastable ( ) and MayBurnShadowDance ( ) and not Player : BuffUp ( S.ShadowDanceBuff ) and HL.BossFilteredFightRemains ( " <= " , 8 ) then
ShouldReturn = StealthMacro ( S.ShadowDance )
if ShouldReturn then return " Shadow Dance Macro (Low TTD) " .. ShouldReturn end
end
-- actions.cds+=/thistle_tea,if=(cooldown.symbols_of_death.remains>=3|buff.symbols_of_death.up)&!buff.thistle_tea.up&(energy.deficit>=100&(combo_points.deficit>=2|spell_targets.shuriken_storm>=3)|cooldown.thistle_tea.charges_fractional>=2.75&buff.shadow_dance.up)|buff.shadow_dance.remains>=4&!buff.thistle_tea.up&spell_targets.shuriken_storm>=3|!buff.thistle_tea.up&fight_remains<=(6*cooldown.thistle_tea.charges)
if S.ThistleTea : IsReady ( ) then
if ( S.SymbolsofDeath : CooldownRemains ( ) >= 3 or Player : BuffUp ( S.SymbolsofDeath ) ) and not Player : BuffUp ( S.ThistleTea )
and ( Player : EnergyDeficitPredicted ( ) >= 100 and ( Player : ComboPointsDeficit ( ) >= 2 or MeleeEnemies10yCount >= 3 )
or S.ThistleTea : ChargesFractional ( ) >= 2.75 and Player : BuffUp ( S.ShadowDanceBuff ) )
or Player : BuffRemains ( S.ShadowDanceBuff ) >= 4 and not Player : BuffUp ( S.ThistleTea ) and MeleeEnemies10yCount >= 3
or not Player : BuffUp ( S.ThistleTea ) and HL.BossFilteredFightRemains ( " <= " , 6 * S.ThistleTea : Charges ( ) ) then
if HR.Cast ( S.ThistleTea , nil , Settings.Commons . TrinketDisplayStyle ) then return " Thistle Tea " ; end
end
end
-- TODO: Add Potion Suggestion
-- actions.cds+=/potion,if=buff.bloodlust.react|fight_remains<30|buff.symbols_of_death.up&(buff.shadow_blades.up|cooldown.shadow_blades.remains<=10)
-- Racials
if Player : BuffUp ( S.SymbolsofDeath ) then
-- actions.cds+=/blood_fury,if=buff.symbols_of_death.up
if S.BloodFury : IsCastable ( ) then
if HR.Cast ( S.BloodFury , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Blood Fury " end
end
-- actions.cds+=/berserking,if=buff.symbols_of_death.up
if S.Berserking : IsCastable ( ) then
if HR.Cast ( S.Berserking , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Berserking " end
end
-- actions.cds+=/fireblood,if=buff.symbols_of_death.up
if S.Fireblood : IsCastable ( ) then
if HR.Cast ( S.Fireblood , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Fireblood " end
end
-- actions.cds+=/ancestral_call,if=buff.symbols_of_death.up
if S.AncestralCall : IsCastable ( ) then
if HR.Cast ( S.AncestralCall , Settings.Commons . OffGCDasOffGCD.Racials ) then return " Cast Ancestral Call " end
end
end
-- Trinkets
if Settings.Commons . UseTrinkets then
-- actions.cds+=/use_item,name=manic_grieftorch,use_off_gcd=1,if=!stealthed.all&(!raid_event.adds.up|!equipped.stormeaters_boon|trinket.stormeaters_boon.cooldown.remains>20)
if I.ManicGrieftorch : IsEquippedAndReady ( ) then
if ( not Player : StealthUp ( true , true )
and ( not I.StormEatersBoon : IsEquipped ( )
or I.StormEatersBoon : CooldownRemains ( ) > 20 ) ) then
if HR.Cast ( I.ManicGrieftorch , nil , Settings.Commons . TrinketDisplayStyle ) then return " Manic Grieftorch " end
end
end
-- actions.cds+=/use_item,name=beacon_to_the_beyond,use_off_gcd=1,if=!stealthed.all&(buff.deeper_daggers.up|!talent.deeper_daggers)&(!raid_event.adds.up|!equipped.stormeaters_boon|trinket.stormeaters_boon.cooldown.remains>20)
if I.BeaconToTheBeyond : IsEquippedAndReady ( ) then
if ( not Player : StealthUp ( true , true )
and ( Player : BuffUp ( S.DeeperDaggersBuff )
or not S.DeeperDaggers : IsAvailable ( ) )
and ( not I.StormEatersBoon : IsEquipped ( )
or I.StormEatersBoon : CooldownRemains ( ) > 20 ) ) then
if HR.Cast ( I.BeaconToTheBeyond , nil , Settings.Commons . TrinketDisplayStyle ) then return " Beacon To The Beyond " end
end
end
-- actions.cds+=/use_items,if=!stealthed.all|fight_remains<10
if not Player : StealthUp ( true , true ) or HL.BossFilteredFightRemains ( " < " , 10 ) then
local TrinketToUse = Player : GetUseableItems ( OnUseExcludes )
if TrinketToUse then
if HR.Cast ( TrinketToUse , nil , Settings.Commons . TrinketDisplayStyle ) then
return " Generic use_items for " .. TrinketToUse : Name ( )
end
end
end
end
end
return false
end
-- # Stealth Cooldowns
local function Stealth_CDs ( EnergyThreshold )
if HR.CDsON ( ) and S.ShadowDance : TimeSinceLastDisplay ( ) > 0.3 and S.Shadowmeld : TimeSinceLastDisplay ( ) > 0.3
and not ( Everyone.IsSoloMode ( ) and Player : IsTanking ( Target ) ) then
-- actions.stealth_cds+=/vanish,if=(!talent.danse_macabre|spell_targets.shuriken_storm>=3)&!variable.shd_threshold&combo_points.deficit>1&(cooldown.flagellation.remains>=60|!talent.flagellation|fight_remains<=(30*cooldown.vanish.charges))
if S.Vanish : IsCastable ( )
and ( not S.DanseMacabre : IsAvailable ( ) or MeleeEnemies10yCount >= 3 ) and not ShD_Threshold ( ) and ComboPointsDeficit > 1
and ( S.Flagellation : CooldownRemains ( ) >= 60 or not S.Flagellation : IsAvailable ( ) or HL.BossFilteredFightRemains ( " <= " , 30 * S.Vanish : Charges ( ) ) ) then
ShouldReturn = StealthMacro ( S.Vanish , EnergyThreshold )
if ShouldReturn then return " Vanish Macro " .. ShouldReturn end
end
-- actions.stealth_cds+=/shadowmeld,if=energy>=40&energy.deficit>=10&!variable.shd_threshold&combo_points.deficit>4
if S.Shadowmeld : IsCastable ( ) and TargetInMeleeRange and not Player : IsMoving ( )
and Player : EnergyDeficitPredicted ( ) > 10 and not ShD_Threshold ( ) and ComboPointsDeficit > 4 then
-- actions.stealth_cds+=/pool_resource,for_next=1,extra_amount=40
if Player : Energy ( ) < 40 then
if HR.CastPooling ( S.Shadowmeld , Player : EnergyTimeToX ( 40 ) ) then return " Pool for Shadowmeld " end
end
ShouldReturn = StealthMacro ( S.Shadowmeld , EnergyThreshold )
if ShouldReturn then return " Shadowmeld Macro " .. ShouldReturn end
end
end
if TargetInMeleeRange and S.ShadowDance : IsCastable ( ) and S.ShadowDance : Charges ( ) >= 1
and S.Vanish : TimeSinceLastDisplay ( ) > 0.3 and S.Shadowmeld : TimeSinceLastDisplay ( ) > 0.3
and ( HR.CDsON ( ) or ( S.ShadowDance : ChargesFractional ( ) >= Settings.Subtlety . ShDEcoCharge - ( not S.ShadowDanceTalent : IsAvailable ( ) and 0.75 or 0 ) ) ) then
-- actions.stealth_cds+=/shadow_dance,if=(variable.shd_combo_points&(!talent.shadow_dance&buff.symbols_of_death.remains>=(2.2-talent.flagellation.enabled)|variable.shd_threshold)|talent.shadow_dance&cooldown.secret_technique.remains<=9&(spell_targets.shuriken_storm<=3|talent.danse_macabre)|buff.flagellation.up|buff.flagellation_persist.remains>=6|spell_targets.shuriken_storm>=4&cooldown.symbols_of_death.remains>10)&variable.rotten_threshold
-- NOTE: |buff.flagellation.up is a dead operation in SimC due to a typo, since the buff we use in-game is buff.flagellation_buff.up, ignoring
if ( ( ShD_Combo_Points ( ) and ( not S.ShadowDanceTalent : IsAvailable ( ) and Player : BuffRemains ( S.SymbolsofDeath ) >= ( 2.2 - BoolToInt ( S.Flagellation : IsAvailable ( ) ) ) or ShD_Threshold ( ) ) )
or ( S.ShadowDanceTalent : IsAvailable ( ) and S.SecretTechnique : CooldownRemains ( ) <= 9 and ( MeleeEnemies10yCount <= 3 or S.DanseMacabre : IsAvailable ( ) ) )
or Player : BuffRemains ( S.FlagellationPersistBuff ) >= 6 or MeleeEnemies10yCount >= 4 and S.SymbolsofDeath : CooldownRemains ( ) > 10 )
and Rotten_Threshold ( ) then
ShouldReturn = StealthMacro ( S.ShadowDance , EnergyThreshold )
if ShouldReturn then return " ShadowDance Macro 1 " .. ShouldReturn end
end
-- actions.stealth_cds+=/shadow_dance,if=variable.shd_combo_points&fight_remains<cooldown.symbols_of_death.remains|!talent.shadow_dance&dot.rupture.ticking&spell_targets.shuriken_storm<=4&variable.rotten_threshold
if MayBurnShadowDance ( ) and ( ShD_Combo_Points ( ) and HL.BossFilteredFightRemains ( " < " , S.SymbolsofDeath : CooldownRemains ( ) )
or not S.ShadowDanceTalent : IsAvailable ( ) and Target : DebuffUp ( S.Rupture ) and MeleeEnemies10yCount <= 4 and Rotten_Threshold ( ) ) then
ShouldReturn = StealthMacro ( S.ShadowDance , EnergyThreshold )
if ShouldReturn then return " ShadowDance Macro 2 " .. ShouldReturn end
end
end
return false
end
-- # Builders
local function Build ( EnergyThreshold )
local ThresholdMet = not EnergyThreshold or Player : EnergyPredicted ( ) >= EnergyThreshold
-- actions.build=shuriken_storm,if=spell_targets>=2+(talent.gloomblade&buff.lingering_shadow.remains>=6|buff.perforated_veins.up)
if HR.AoEON ( ) and S.ShurikenStorm : IsCastable ( )
and MeleeEnemies10yCount >= 2 + BoolToInt ( S.Gloomblade : IsAvailable ( ) and Player : BuffRemains ( S.LingeringShadowBuff ) >= 6 or Player : BuffUp ( S.PerforatedVeinsBuff ) ) then
if ThresholdMet and HR.Cast ( S.ShurikenStorm ) then return " Cast Shuriken Storm " end
SetPoolingAbility ( S.ShurikenStorm , EnergyThreshold )
end
if TargetInMeleeRange then
-- # Build immediately unless the next CP is Animacharged and we won't cap energy waiting for it.
-- actions.build+=/variable,name=anima_helper,value=!talent.echoing_reprimand.enabled|!(variable.is_next_cp_animacharged&(time_to_sht.3.plus<0.5|time_to_sht.4.plus<1)&energy<60)
if S.EchoingReprimand : IsAvailable ( ) and Player : Energy ( ) < 60
and ( ComboPoints == 2 and Player : BuffUp ( S.EchoingReprimand3 )
or ComboPoints == 3 and Player : BuffUp ( S.EchoingReprimand4 )
or ComboPoints == 4 and Player : BuffUp ( S.EchoingReprimand5 ) )
and ( Rogue.TimeToSht ( 3 ) < 0.5 or Rogue.TimeToSht ( 4 ) < 1.0 or Rogue.TimeToSht ( 5 ) < 1.0 ) then
HR.Cast ( S.PoolEnergy )
return " ER Generator Pooling "
end
-- actions.build+=/gloomblade,if=variable.anima_helper
if S.Gloomblade : IsCastable ( ) then
if ThresholdMet and HR.Cast ( S.Gloomblade ) then return " Cast Gloomblade " end
SetPoolingAbility ( S.Gloomblade , EnergyThreshold )
-- actions.build+=/backstab,if=variable.anima_helper
elseif S.Backstab : IsCastable ( ) then
if ThresholdMet and HR.Cast ( S.Backstab ) then return " Cast Backstab " end
SetPoolingAbility ( S.Backstab , EnergyThreshold )
end
end
return false
end
local Interrupts = {
{ S.Blind , " Cast Blind (Interrupt) " , function ( ) return true end } ,
{ S.KidneyShot , " Cast Kidney Shot (Interrupt) " , function ( ) return ComboPoints > 0 end } ,
{ S.CheapShot , " Cast Cheap Shot (Interrupt) " , function ( ) return Player : StealthUp ( true , true ) end }
}
-- APL Main
local function APL ( )
-- Reset pooling cache
PoolingAbility = nil
PoolingFinisher = nil
PoolingEnergy = 0
-- Unit Update
MeleeRange = S.AcrobaticStrikes : IsAvailable ( ) and 8 or 5
AoERange = S.AcrobaticStrikes : IsAvailable ( ) and 13 or 10
TargetInMeleeRange = Target : IsInMeleeRange ( MeleeRange )
TargetInAoERange = Target : IsInMeleeRange ( AoERange )
if AoEON ( ) then
Enemies30y = Player : GetEnemiesInRange ( 30 ) -- Serrated Bone Spike
MeleeEnemies10y = Player : GetEnemiesInMeleeRange ( AoERange ) -- Shuriken Storm & Black Powder
MeleeEnemies10yCount = # MeleeEnemies10y
MeleeEnemies5y = Player : GetEnemiesInMeleeRange ( MeleeRange ) -- Melee cycle
else
Enemies30y = { }
MeleeEnemies10y = { }
MeleeEnemies10yCount = 1
MeleeEnemies5y = { }
end
-- Cache updates
ComboPoints = Player : ComboPoints ( )
EffectiveComboPoints = Rogue.EffectiveComboPoints ( ComboPoints )
ComboPointsDeficit = Player : ComboPointsDeficit ( )
PriorityRotation = UsePriorityRotation ( )
StealthEnergyRequired = Player : EnergyMax ( ) - Stealth_Threshold ( )
-- Adjust Animacharged CP Prediction for Shadow Techniques
-- If we are on a non-optimal Animacharged CP, ignore it if the time to ShT is less than GCD + 500ms, unless the ER buff will expire soon
-- Reduces the risk of queued finishers into ShT procs for non-optimal CP amounts
-- This is an adaptation of the following APL lines:
-- actions+=/variable,name=is_next_cp_animacharged,if=talent.echoing_reprimand.enabled,value=combo_points=1&buff.echoing_reprimand_2.up|combo_points=2&buff.echoing_reprimand_3.up|combo_points=3&buff.echoing_reprimand_4.up|combo_points=4&buff.echoing_reprimand_5.up
-- actions+=/variable,name=effective_combo_points,value=effective_combo_points
-- actions+=/variable,name=effective_combo_points,if=talent.echoing_reprimand.enabled&effective_combo_points>combo_points&combo_points.deficit>2&time_to_sht.4.plus<0.5&!variable.is_next_cp_animacharged,value=combo_points
if EffectiveComboPoints > ComboPoints and ComboPointsDeficit > 2 and Player : AffectingCombat ( ) then
if ComboPoints == 2 and not Player : BuffUp ( S.EchoingReprimand3 )
or ComboPoints == 3 and not Player : BuffUp ( S.EchoingReprimand4 )
or ComboPoints == 4 and not Player : BuffUp ( S.EchoingReprimand5 ) then
local TimeToSht = Rogue.TimeToSht ( 4 )
if TimeToSht == 0 then TimeToSht = Rogue.TimeToSht ( 5 ) end
if TimeToSht < ( mathmax ( Player : EnergyTimeToX ( 35 ) , Player : GCDRemains ( ) ) + 0.5 ) then
EffectiveComboPoints = ComboPoints
end
end
end
-- Shuriken Tornado Combo Point Prediction
if Player : BuffUp ( S.ShurikenTornado , nil , true ) and ComboPoints < Rogue.CPMaxSpend ( ) then
local TimeToNextTornadoTick = Rogue.TimeToNextTornado ( )
if TimeToNextTornadoTick <= Player : GCDRemains ( ) or mathabs ( Player : GCDRemains ( ) - TimeToNextTornadoTick ) < 0.25 then
local PredictedComboPointGeneration = MeleeEnemies10yCount + num ( Player : BuffUp ( S.ShadowBlades ) )
ComboPoints = mathmin ( ComboPoints + PredictedComboPointGeneration , Rogue.CPMaxSpend ( ) )
ComboPointsDeficit = mathmax ( ComboPointsDeficit - PredictedComboPointGeneration , 0 )
if EffectiveComboPoints < Rogue.CPMaxSpend ( ) then
EffectiveComboPoints = ComboPoints
end
end
end
-- Damage Cache updates (after EffectiveComboPoints adjustments)
RuptureThreshold = ( 4 + EffectiveComboPoints * 4 ) * 0.3
RuptureDMGThreshold = S.Eviscerate : Damage ( ) * Settings.Subtlety . EviscerateDMGOffset ; -- Used to check if Rupture is worth to be casted since it's a finisher.
--- 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
-- Stealth
-- Note: Since 7.2.5, Blizzard disallowed Stealth cast under ShD (workaround to prevent the Extended Stealth bug)
if not Player : BuffUp ( S.ShadowDanceBuff ) and 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 ( ) and ( Target : IsSpellInRange ( S.Shadowstrike ) or TargetInMeleeRange ) then
-- Precombat CDs
if HR.CDsON ( ) then
if S.MarkedforDeath : IsCastable ( ) and Player : ComboPointsDeficit ( ) >= Rogue.CPMaxSpend ( ) then
if HR.Cast ( S.MarkedforDeath , Settings.Commons . OffGCDasOffGCD.MarkedforDeath ) then return " Cast Marked for Death (OOC) " end
end
-- TODO: actions.precombat+=/fleshcraft,if=soulbind.pustule_eruption|soulbind.volatile_solvent
end
if Player : StealthUp ( true , true ) then
PoolingAbility = Stealthed ( true )
if PoolingAbility then -- To avoid pooling icon spam
if type ( PoolingAbility ) == " table " and # PoolingAbility > 1 then
if HR.CastQueuePooling ( nil , unpack ( PoolingAbility ) ) then return " Stealthed Macro Cast or Pool (OOC): " .. PoolingAbility [ 1 ] : Name ( ) end
else
if HR.CastPooling ( PoolingAbility ) then return " Stealthed Cast or Pool (OOC): " .. PoolingAbility : Name ( ) end
end
end
elseif ComboPoints >= 5 then
ShouldReturn = Finish ( )
if ShouldReturn then return ShouldReturn .. " (OOC) " end
elseif S.Backstab : IsCastable ( ) then
if HR.Cast ( S.Backstab ) then return " Cast Backstab (OOC) " end
end
end
return
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
-- # Check CDs at first
-- actions=call_action_list,name=cds
ShouldReturn = CDs ( )
if ShouldReturn then return " CDs: " .. ShouldReturn end
-- # Apply Slice and Dice at 4+ CP if it expires within the next GCD or is not up
-- actions+=/slice_and_dice,if=spell_targets.shuriken_storm<cp_max_spend&buff.slice_and_dice.remains<gcd.max&fight_remains>6&combo_points>=4
if S.SliceandDice : IsCastable ( ) and MeleeEnemies10yCount < Rogue.CPMaxSpend ( ) and HL.FilteredFightRemains ( MeleeEnemies10y , " > " , 6 )
and Player : BuffRemains ( S.SliceandDice ) < Player : GCD ( ) and ComboPoints >= 4 then
if S.SliceandDice : IsReady ( ) and HR.Cast ( S.SliceandDice ) then return " Cast Slice and Dice (Low Duration) " end
SetPoolingFinisher ( S.SliceandDice )
end
-- # Run fully switches to the Stealthed Rotation (by doing so, it forces pooling if nothing is available).
-- actions+=/run_action_list,name=stealthed,if=stealthed.all
if Player : StealthUp ( true , true ) then
PoolingAbility = Stealthed ( true )
if PoolingAbility then -- To avoid pooling icon spam
if type ( PoolingAbility ) == " table " and # PoolingAbility > 1 then
if HR.CastQueuePooling ( nil , unpack ( PoolingAbility ) ) then return " Stealthed Macro " .. PoolingAbility [ 1 ] : Name ( ) .. " | " .. PoolingAbility [ 2 ] : Name ( ) end
else
-- Special case for Shuriken Tornado
if Player : BuffUp ( S.ShurikenTornado ) and ComboPoints ~= Player : ComboPoints ( )
and ( PoolingAbility == S.BlackPowder or PoolingAbility == S.Eviscerate or PoolingAbility == S.Rupture or PoolingAbility == S.SliceandDice ) then
if HR.CastQueuePooling ( nil , S.ShurikenTornado , PoolingAbility ) then return " Stealthed Tornado Cast " .. PoolingAbility : Name ( ) end
else
if HR.CastPooling ( PoolingAbility ) then return " Stealthed Cast " .. PoolingAbility : Name ( ) end
end
end
end
HR.Cast ( S.PoolEnergy )
return " Stealthed Pooling "
end
-- actions+=/call_action_list,name=stealth_cds,if=energy.deficit<=variable.stealth_threshold
if Player : EnergyPredicted ( ) >= StealthEnergyRequired then
ShouldReturn = Stealth_CDs ( )
if ShouldReturn then return " Stealth CDs: " .. ShouldReturn end
end
-- actions+=/call_action_list,name=finish,if=variable.effective_combo_points>=cp_max_spend
-- # Finish at maximum or close to maximum combo point value
-- actions+=/call_action_list,name=finish,if=combo_points.deficit<=1+buff.the_rotten.up|fight_remains<=1&variable.effective_combo_points>=3
-- # Finish at 4+ against 4 targets (outside stealth)
-- actions+=/call_action_list,name=finish,if=spell_targets.shuriken_storm>=(4-talent.seal_fate)&variable.effective_combo_points>=4
if EffectiveComboPoints >= Rogue.CPMaxSpend ( )
or ( ComboPointsDeficit <= ( 1 + num ( Player : BuffUp ( S.TheRottenBuff ) ) ) or ( HL.BossFilteredFightRemains ( " < " , 2 ) and EffectiveComboPoints >= 3 ) )
or ( MeleeEnemies10yCount >= ( 4 - num ( S.SealFate : IsAvailable ( ) ) ) and EffectiveComboPoints >= 4 ) then
ShouldReturn = Finish ( )
if ShouldReturn then return " Finish: " .. ShouldReturn end
else
-- NOTE: Duplicated stealth_cds line from above since both this and build have the same energy threshold if condition
-- If we aren't finishing in between, we'll be suggesting to pool something and re-process with StealthEnergyRequired
-- # Consider using a Stealth CD when reaching the energy threshold, called with params to register potential pooling
-- actions+=/call_action_list,name=stealth_cds,if=energy.deficit<=variable.stealth_threshold
ShouldReturn = Stealth_CDs ( StealthEnergyRequired )
if ShouldReturn then return " Stealth CDs: " .. ShouldReturn end
-- # Use a builder when reaching the energy threshold
-- actions+=/call_action_list,name=build,if=energy.deficit<=variable.stealth_threshold
ShouldReturn = Build ( StealthEnergyRequired )
if ShouldReturn then return " Build: " .. ShouldReturn end
end
if HR.CDsON ( ) then
-- # Lowest priority in all of the APL because it causes a GCD
-- actions+=/arcane_torrent,if=energy.deficit>=15+energy.regen
if S.ArcaneTorrent : IsReady ( ) and TargetInMeleeRange and Player : EnergyDeficitPredicted ( ) > 15 + Player : EnergyRegen ( ) then
if HR.Cast ( S.ArcaneTorrent , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Arcane Torrent " end
end
-- actions+=/arcane_pulse
if S.ArcanePulse : IsReady ( ) and TargetInMeleeRange then
if HR.Cast ( S.ArcanePulse , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Arcane Pulse " end
end
-- actions+=/lights_judgment
if S.LightsJudgment : IsReady ( ) then
if HR.Cast ( S.LightsJudgment , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Lights Judgment " end
end
-- actions+=/bag_of_tricks
if S.BagofTricks : IsReady ( ) then
if HR.Cast ( S.BagofTricks , Settings.Commons . GCDasOffGCD.Racials ) then return " Cast Bag of Tricks " end
end
end
-- Show what ever was first stored for pooling
if PoolingFinisher then SetPoolingAbility ( PoolingFinisher ) end
if PoolingAbility and TargetInMeleeRange then
if type ( PoolingAbility ) == " table " and # PoolingAbility > 1 then
if HR.CastQueuePooling ( Player : EnergyTimeToX ( PoolingEnergy ) , unpack ( PoolingAbility ) ) then return " Macro pool towards " .. PoolingAbility [ 1 ] : Name ( ) .. " at " .. PoolingEnergy end
elseif PoolingAbility : IsCastable ( ) then
PoolingEnergy = mathmax ( PoolingEnergy , PoolingAbility : Cost ( ) )
if HR.CastPooling ( PoolingAbility , Player : EnergyTimeToX ( PoolingEnergy ) ) then return " Pool towards: " .. PoolingAbility : Name ( ) .. " at " .. PoolingEnergy end
end
end
-- Shuriken Toss Out of Range
if S.ShurikenToss : IsCastable ( ) and Target : IsInRange ( 30 ) and not TargetInAoERange and not Player : StealthUp ( true , true ) and not Player : BuffUp ( S.Sprint )
and Player : EnergyDeficitPredicted ( ) < 20 and ( ComboPointsDeficit >= 1 or Player : EnergyTimeToMax ( ) <= 1.2 ) then
if HR.CastPooling ( S.ShurikenToss ) then return " Cast Shuriken Toss " end
end
end
end
local function Init ( )
-- Nothing
end
HR.SetAPL ( 261 , APL , Init )
-- Last Update: 2023-05-19
-- # 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+=/stealth
-- actions.precombat+=/marked_for_death,precombat_seconds=15
-- actions.precombat+=/variable,name=algethar_puzzle_box_precombat_cast,value=3
-- actions.precombat+=/use_item,name=algethar_puzzle_box
-- 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
-- # Used to determine whether cooldowns wait for SnD based on targets.
-- actions+=/variable,name=snd_condition,value=buff.slice_and_dice.up|spell_targets.shuriken_storm>=cp_max_spend
-- # Check to see if the next CP (in the event of a ShT proc) is Animacharged
-- actions+=/variable,name=is_next_cp_animacharged,if=talent.echoing_reprimand.enabled,value=combo_points=1&buff.echoing_reprimand_2.up|combo_points=2&buff.echoing_reprimand_3.up|combo_points=3&buff.echoing_reprimand_4.up|combo_points=4&buff.echoing_reprimand_5.up
-- # Account for ShT reaction time by ignoring low-CP animacharged matches in the 0.5s preceeding a potential ShT proc
-- actions+=/variable,name=effective_combo_points,value=effective_combo_points
-- actions+=/variable,name=effective_combo_points,if=talent.echoing_reprimand.enabled&effective_combo_points>combo_points&combo_points.deficit>2&time_to_sht.4.plus<0.5&!variable.is_next_cp_animacharged,value=combo_points
-- # Check CDs at first
-- actions+=/call_action_list,name=cds
-- # Apply Slice and Dice at 4+ CP if it expires within the next GCD or is not up
-- actions+=/slice_and_dice,if=spell_targets.shuriken_storm<cp_max_spend&buff.slice_and_dice.remains<gcd.max&fight_remains>6&combo_points>=4
-- # Run fully switches to the Stealthed Rotation (by doing so, it forces pooling if nothing is available).
-- actions+=/run_action_list,name=stealthed,if=stealthed.all
-- # Only change rotation if we have priority_rotation set.
-- actions+=/variable,name=priority_rotation,value=priority_rotation
-- # Used to define when to use stealth CDs or builders
-- actions+=/variable,name=stealth_threshold,value=25+talent.vigor.enabled*20+talent.master_of_shadows.enabled*20+talent.shadow_focus.enabled*25+talent.alacrity.enabled*20+25*(spell_targets.shuriken_storm>=4)
-- # Consider using a Stealth CD when reaching the energy threshold
-- actions+=/call_action_list,name=stealth_cds,if=energy.deficit<=variable.stealth_threshold
-- actions+=/call_action_list,name=finish,if=variable.effective_combo_points>=cp_max_spend
-- # Finish at maximum or close to maximum combo point value
-- actions+=/call_action_list,name=finish,if=combo_points.deficit<=1+buff.the_rotten.up|fight_remains<=1&variable.effective_combo_points>=3
-- # Finish at 4+ against 4 targets (outside stealth)
-- actions+=/call_action_list,name=finish,if=spell_targets.shuriken_storm>=(4-talent.seal_fate)&variable.effective_combo_points>=4
-- # Use a builder when reaching the energy threshold
-- actions+=/call_action_list,name=build,if=energy.deficit<=variable.stealth_threshold
-- # Lowest priority in all of the APL because it causes a GCD
-- actions+=/arcane_torrent,if=energy.deficit>=15+energy.regen
-- actions+=/arcane_pulse
-- actions+=/lights_judgment
-- actions+=/bag_of_tricks
-- # Builders
-- actions.build=shuriken_storm,if=spell_targets>=2+(talent.gloomblade&buff.lingering_shadow.remains>=6|buff.perforated_veins.up)
-- # Build immediately unless the next CP is Animacharged and we won't cap energy waiting for it.
-- actions.build+=/variable,name=anima_helper,value=!talent.echoing_reprimand.enabled|!(variable.is_next_cp_animacharged&(time_to_sht.3.plus<0.5|time_to_sht.4.plus<1)&energy<60)
-- actions.build+=/gloomblade,if=variable.anima_helper
-- actions.build+=/backstab,if=variable.anima_helper
-- # Cooldowns
-- actions.cds=variable,name=rotten_condition,value=!buff.premeditation.up&spell_targets.shuriken_storm=1|!talent.the_rotten|spell_targets.shuriken_storm>1
-- # Cooldowns Use Dance off-gcd before the first Shuriken Storm from Tornado comes in.
-- actions.cds+=/shadow_dance,use_off_gcd=1,if=!buff.shadow_dance.up&buff.shuriken_tornado.up&buff.shuriken_tornado.remains<=3.5
-- # (Unless already up because we took Shadow Focus) use Symbols off-gcd before the first Shuriken Storm from Tornado comes in.
-- actions.cds+=/symbols_of_death,use_off_gcd=1,if=buff.shuriken_tornado.up&buff.shuriken_tornado.remains<=3.5
-- # Vanish for Shadowstrike with Danse Macabre at adaquate stacks
-- actions.cds+=/vanish,if=buff.danse_macabre.stack>3&combo_points<=2&(cooldown.secret_technique.remains>=30|!talent.secret_technique)
-- # Cold Blood on 5 combo points when not playing Secret Technique
-- actions.cds+=/cold_blood,if=!talent.secret_technique&combo_points>=5
-- actions.cds+=/flagellation,target_if=max:target.time_to_die,if=variable.snd_condition&combo_points>=5&target.time_to_die>10
-- # Pool for Tornado pre-SoD with ShD ready when not running SF.
-- actions.cds+=/pool_resource,for_next=1,if=talent.shuriken_tornado.enabled&!talent.shadow_focus.enabled
-- # Use Tornado pre SoD when we have the energy whether from pooling without SF or just generally.
-- actions.cds+=/shuriken_tornado,if=spell_targets.shuriken_storm<=1&energy>=60&variable.snd_condition&cooldown.symbols_of_death.up&cooldown.shadow_dance.charges>=1&(!talent.flagellation.enabled&!cooldown.flagellation.up|buff.flagellation_buff.up|spell_targets.shuriken_storm>=5)&combo_points<=2&!buff.premeditation.up
-- actions.cds+=/sepsis,if=variable.snd_condition&combo_points.deficit>=1&target.time_to_die>=16
-- # Use Symbols on cooldown (after first SnD) unless we are going to pop Tornado and do not have Shadow Focus.
-- actions.cds+=/symbols_of_death,if=(buff.symbols_of_death.remains<=3&!cooldown.shadow_dance.ready|!set_bonus.tier30_2pc)&variable.rotten_condition&variable.snd_condition&(!talent.flagellation&(combo_points<=1|!talent.the_rotten)|cooldown.flagellation.remains>10|cooldown.flagellation.up&combo_points>=5)
-- # If adds are up, snipe the one with lowest TTD. Use when dying faster than CP deficit or not stealthed without any CP.
-- actions.cds+=/marked_for_death,line_cd=1.5,target_if=min:target.time_to_die,if=raid_event.adds.up&(target.time_to_die<combo_points.deficit|!stealthed.all&combo_points.deficit>=cp_max_spend)
-- # If no adds will die within the next 30s, use MfD on boss without any CP.
-- actions.cds+=/marked_for_death,if=raid_event.adds.in>30-raid_event.adds.duration&combo_points.deficit>=cp_max_spend
-- actions.cds+=/shadow_blades,if=variable.snd_condition&combo_points.deficit>=2&target.time_to_die>=10&(dot.sepsis.ticking|cooldown.sepsis.remains<=8|!talent.sepsis)|fight_remains<=20
-- actions.cds+=/echoing_reprimand,if=variable.snd_condition&combo_points.deficit>=3&(variable.priority_rotation|spell_targets.shuriken_storm<=4|talent.resounding_clarity)&(buff.shadow_dance.up|!talent.danse_macabre)
-- # With SF, if not already done, use Tornado with SoD up.
-- actions.cds+=/shuriken_tornado,if=variable.snd_condition&buff.symbols_of_death.up&combo_points<=2&(!buff.premeditation.up|spell_targets.shuriken_storm>4)
-- actions.cds+=/shuriken_tornado,if=cooldown.shadow_dance.ready&!stealthed.all&spell_targets.shuriken_storm>=3&!talent.flagellation.enabled
-- actions.cds+=/shadow_dance,if=!buff.shadow_dance.up&fight_remains<=8+talent.subterfuge.enabled
-- actions.cds+=/thistle_tea,if=(cooldown.symbols_of_death.remains>=3|buff.symbols_of_death.up)&!buff.thistle_tea.up&(energy.deficit>=100&(combo_points.deficit>=2|spell_targets.shuriken_storm>=3)|cooldown.thistle_tea.charges_fractional>=2.75&buff.shadow_dance.up)|buff.shadow_dance.remains>=4&!buff.thistle_tea.up&spell_targets.shuriken_storm>=3|!buff.thistle_tea.up&fight_remains<=(6*cooldown.thistle_tea.charges)
-- actions.cds+=/potion,if=buff.bloodlust.react|fight_remains<30|buff.symbols_of_death.up&(buff.shadow_blades.up|cooldown.shadow_blades.remains<=10)
-- actions.cds+=/blood_fury,if=buff.symbols_of_death.up
-- actions.cds+=/berserking,if=buff.symbols_of_death.up
-- actions.cds+=/fireblood,if=buff.symbols_of_death.up
-- actions.cds+=/ancestral_call,if=buff.symbols_of_death.up
-- actions.cds+=/use_item,name=beacon_to_the_beyond,use_off_gcd=1,if=!stealthed.all&(buff.deeper_daggers.up|!talent.deeper_daggers)&(!raid_event.adds.up|!equipped.stormeaters_boon|trinket.stormeaters_boon.cooldown.remains>20)
-- actions.cds+=/use_item,name=manic_grieftorch,use_off_gcd=1,if=!stealthed.all&(!raid_event.adds.up|!equipped.stormeaters_boon|trinket.stormeaters_boon.cooldown.remains>20)
-- # Default fallback for usable items: Use with Symbols of Death.
-- actions.cds+=/use_items,if=!stealthed.all|fight_remains<10
-- # Finishers While using Premeditation, avoid casting Slice and Dice when Shadow Dance is soon to be used, except for Kyrian
-- actions.finish=variable,name=secret_condition,value=buff.shadow_dance.up&(buff.danse_macabre.stack>=3|!talent.danse_macabre)&(!buff.premeditation.up|spell_targets.shuriken_storm!=2)
-- actions.finish+=/variable,name=premed_snd_condition,value=talent.premeditation.enabled&spell_targets.shuriken_storm<5
-- actions.finish+=/slice_and_dice,if=!variable.premed_snd_condition&spell_targets.shuriken_storm<6&!buff.shadow_dance.up&buff.slice_and_dice.remains<fight_remains&refreshable
-- actions.finish+=/slice_and_dice,if=variable.premed_snd_condition&cooldown.shadow_dance.charges_fractional<1.75&buff.slice_and_dice.remains<cooldown.symbols_of_death.remains&(cooldown.shadow_dance.ready&buff.symbols_of_death.remains-buff.shadow_dance.remains<1.2)
-- actions.finish+=/variable,name=skip_rupture,value=buff.thistle_tea.up&spell_targets.shuriken_storm=1|buff.shadow_dance.up&(spell_targets.shuriken_storm=1|dot.rupture.ticking&spell_targets.shuriken_storm>=2)
-- # Keep up Rupture if it is about to run out.
-- actions.finish+=/rupture,if=(!variable.skip_rupture|variable.priority_rotation)&target.time_to_die-remains>6&refreshable
-- # Refresh Rupture early for Finality
-- actions.finish+=/rupture,if=!variable.skip_rupture&buff.finality_rupture.up&cooldown.shadow_dance.remains<12&cooldown.shadow_dance.charges_fractional<=1&spell_targets.shuriken_storm=1&(talent.dark_brew|talent.danse_macabre)
-- # Sync Cold Blood with Secret Technique when possible
-- actions.finish+=/cold_blood,if=variable.secret_condition&cooldown.secret_technique.ready
-- actions.finish+=/secret_technique,if=variable.secret_condition&(!talent.cold_blood|cooldown.cold_blood.remains>buff.shadow_dance.remains-2)
-- # Multidotting targets that will live for the duration of Rupture, refresh during pandemic.
-- actions.finish+=/rupture,cycle_targets=1,if=!variable.skip_rupture&!variable.priority_rotation&spell_targets.shuriken_storm>=2&target.time_to_die>=(2*combo_points)&refreshable
-- # Refresh Rupture early if it will expire during Symbols. Do that refresh if SoD gets ready in the next 5s.
-- actions.finish+=/rupture,if=!variable.skip_rupture&remains<cooldown.symbols_of_death.remains+10&cooldown.symbols_of_death.remains<=5&target.time_to_die-remains>cooldown.symbols_of_death.remains+5
-- actions.finish+=/black_powder,if=!variable.priority_rotation&spell_targets>=3|!used_for_danse&buff.shadow_dance.up&spell_targets.shuriken_storm=2&talent.danse_macabre
-- actions.finish+=/eviscerate
-- # Stealth Cooldowns Helper Variable
-- actions.stealth_cds=variable,name=shd_threshold,value=cooldown.shadow_dance.charges_fractional>=0.75+talent.shadow_dance
-- actions.stealth_cds+=/variable,name=rotten_threshold,value=!buff.the_rotten.up|spell_targets.shuriken_storm>1|combo_points<=2&buff.the_rotten.up&!set_bonus.tier30_2pc
-- actions.stealth_cds+=/vanish,if=(!talent.danse_macabre|spell_targets.shuriken_storm>=3)&!variable.shd_threshold&combo_points.deficit>1&(cooldown.flagellation.remains>=60|!talent.flagellation|fight_remains<=(30*cooldown.vanish.charges))
-- # Pool for Shadowmeld + Shadowstrike unless we are about to cap on Dance charges. Only when Find Weakness is about to run out.
-- actions.stealth_cds+=/pool_resource,for_next=1,extra_amount=40,if=race.night_elf
-- actions.stealth_cds+=/shadowmeld,if=energy>=40&energy.deficit>=10&!variable.shd_threshold&combo_points.deficit>4
-- # CP thresholds for entering Shadow Dance Default to start dance with 0 or 1 combo point
-- actions.stealth_cds+=/variable,name=shd_combo_points,value=combo_points<=1
-- # Use stealth cooldowns with high combo points when playing shuriken tornado or with high target counts
-- actions.stealth_cds+=/variable,name=shd_combo_points,value=combo_points.deficit<=1,if=spell_targets.shuriken_storm>(4-2*talent.shuriken_tornado.enabled)|variable.priority_rotation&spell_targets.shuriken_storm>=4
-- # Use stealth cooldowns on any combo point on 4 targets
-- actions.stealth_cds+=/variable,name=shd_combo_points,value=1,if=spell_targets.shuriken_storm=(4-talent.seal_fate)
-- # Dance during Symbols or above threshold.
-- actions.stealth_cds+=/shadow_dance,if=(variable.shd_combo_points&(!talent.shadow_dance&buff.symbols_of_death.remains>=(2.2-talent.flagellation.enabled)|variable.shd_threshold)|talent.shadow_dance&cooldown.secret_technique.remains<=9&(spell_targets.shuriken_storm<=3|talent.danse_macabre)|buff.flagellation.up|buff.flagellation_persist.remains>=6|spell_targets.shuriken_storm>=4&cooldown.symbols_of_death.remains>10)&variable.rotten_threshold
-- # Burn Dances charges if before the fight ends if SoD won't be ready in time.
-- actions.stealth_cds+=/shadow_dance,if=variable.shd_combo_points&fight_remains<cooldown.symbols_of_death.remains|!talent.shadow_dance&dot.rupture.ticking&spell_targets.shuriken_storm<=4&variable.rotten_threshold
-- # Stealthed Rotation If Stealth/vanish are up, use Shadowstrike to benefit from the passive bonus and Find Weakness, even if we are at max CP (unless using Master Assassin)
-- actions.stealthed=shadowstrike,if=(buff.stealth.up|buff.vanish.up)&(spell_targets.shuriken_storm<4|variable.priority_rotation)
-- # Variable to Gloomblade / Backstab when on 4 or 5 combo points with premediation and when the combo point is not anima charged
-- actions.stealthed+=/variable,name=gloomblade_condition,value=buff.danse_macabre.stack<5&(combo_points.deficit=2|combo_points.deficit=3)&(buff.premeditation.up|effective_combo_points<7)&(spell_targets.shuriken_storm<=8|talent.lingering_shadow)
-- actions.stealthed+=/shuriken_storm,if=variable.gloomblade_condition&buff.silent_storm.up&!debuff.find_weakness.remains&talent.improved_shuriken_storm.enabled|combo_points<=1&!used_for_danse&spell_targets.shuriken_storm=2&talent.danse_macabre
-- actions.stealthed+=/gloomblade,if=variable.gloomblade_condition&(!used_for_danse|spell_targets.shuriken_storm!=2)|combo_points<=2&buff.the_rotten.up&spell_targets.shuriken_storm<=3
-- actions.stealthed+=/backstab,if=variable.gloomblade_condition&talent.danse_macabre&buff.danse_macabre.stack<=2&spell_targets.shuriken_storm<=2
-- actions.stealthed+=/call_action_list,name=finish,if=variable.effective_combo_points>=cp_max_spend
-- # Finish earlier with Shuriken tornado up.
-- actions.stealthed+=/call_action_list,name=finish,if=buff.shuriken_tornado.up&combo_points.deficit<=2
-- # Also safe to finish at 4+ CP with exactly 4 targets. (Same as outside stealth.)
-- actions.stealthed+=/call_action_list,name=finish,if=spell_targets.shuriken_storm>=4-talent.seal_fate&variable.effective_combo_points>=4
-- # Finish at lower combo points if you are talented in DS, SS or Seal Fate
-- actions.stealthed+=/call_action_list,name=finish,if=combo_points.deficit<=1+(talent.seal_fate|talent.deeper_stratagem|talent.secret_stratagem)
-- # Use Gloomblade or Backstab when close to hitting max PV stacks
-- actions.stealthed+=/gloomblade,if=buff.perforated_veins.stack>=5&spell_targets.shuriken_storm<3
-- actions.stealthed+=/backstab,if=buff.perforated_veins.stack>=5&spell_targets.shuriken_storm<3
-- actions.stealthed+=/shadowstrike,if=stealthed.sepsis&spell_targets.shuriken_storm<4
-- actions.stealthed+=/shuriken_storm,if=spell_targets>=3+buff.the_rotten.up&(!buff.premeditation.up|spell_targets>=7&!variable.priority_rotation)
-- # Shadowstrike to refresh Find Weakness and to ensure we can carry over a full FW into the next SoD if possible.
-- actions.stealthed+=/shadowstrike,if=debuff.find_weakness.remains<=1|cooldown.symbols_of_death.remains<18&debuff.find_weakness.remains<cooldown.symbols_of_death.remains
-- actions.stealthed+=/shadowstrike