You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
366 lines
11 KiB
366 lines
11 KiB
--- ============================ HEADER ============================
|
|
--- ======= LOCALIZE =======
|
|
-- Addon
|
|
local addonName, addonTable = ...
|
|
-- HeroLib
|
|
local HL = HeroLib
|
|
local Cache, Utils = HeroCache, HL.Utils
|
|
local Unit = HL.Unit
|
|
local Player, Pet, Target = Unit.Player, Unit.Pet, Unit.Target
|
|
local Focus, MouseOver = Unit.Focus, Unit.MouseOver
|
|
local Arena, Boss, Nameplate = Unit.Arena, Unit.Boss, Unit.Nameplate
|
|
local Party, Raid = Unit.Party, Unit.Raid
|
|
local Spell = HL.Spell
|
|
local Item = HL.Item
|
|
-- HeroRotation
|
|
local HR = HeroRotation
|
|
local Rogue = HR.Commons.Rogue
|
|
-- Lua
|
|
local C_Timer = C_Timer
|
|
local mathmax = math.max
|
|
local mathmin = math.min
|
|
local mathabs = math.abs
|
|
local pairs = pairs
|
|
local tableinsert = table.insert
|
|
local UnitAttackSpeed = UnitAttackSpeed
|
|
local GetTime = GetTime
|
|
-- File Locals
|
|
|
|
|
|
|
|
--- ============================ CONTENT ============================
|
|
|
|
--- Roll the Bones Tracking
|
|
--- As buff is "hidden" from the client but we get apply/refresh events for it
|
|
do
|
|
local RtBExpiryTime = GetTime()
|
|
function Rogue.RtBRemains(BypassRecovery)
|
|
local Remains = RtBExpiryTime - GetTime() - HL.RecoveryOffset(BypassRecovery)
|
|
return Remains >= 0 and Remains or 0
|
|
end
|
|
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, SpellID)
|
|
if SpellID == 315508 then
|
|
RtBExpiryTime = GetTime() + 30
|
|
end
|
|
end,
|
|
"SPELL_AURA_APPLIED"
|
|
)
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, SpellID)
|
|
if SpellID == 315508 then
|
|
RtBExpiryTime = GetTime() + mathmin(40, 30 + Rogue.RtBRemains(true))
|
|
end
|
|
end,
|
|
"SPELL_AURA_REFRESH"
|
|
)
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, SpellID)
|
|
if SpellID == 315508 then
|
|
RtBExpiryTime = GetTime()
|
|
end
|
|
end,
|
|
"SPELL_AURA_REMOVED"
|
|
)
|
|
end
|
|
|
|
--- Exsanguinated Tracking
|
|
do
|
|
-- Variables
|
|
-- { [SpellName] = { [GUID] = boolean } }
|
|
local ExsanguinatedByBleed = {
|
|
CrimsonTempest = {},
|
|
Garrote = {},
|
|
Rupture = {},
|
|
}
|
|
|
|
-- Exsanguinated Expression
|
|
function Rogue.Exsanguinated(ThisUnit, ThisSpell)
|
|
local GUID = ThisUnit:GUID()
|
|
if not GUID then return false end
|
|
|
|
local SpellID = ThisSpell:ID()
|
|
if SpellID == 121411 then
|
|
-- Crimson Tempest
|
|
return ExsanguinatedByBleed.CrimsonTempest[GUID] or false
|
|
elseif SpellID == 703 then
|
|
-- Garrote
|
|
return ExsanguinatedByBleed.Garrote[GUID] or false
|
|
elseif SpellID == 1943 then
|
|
-- Rupture
|
|
return ExsanguinatedByBleed.Rupture[GUID] or false
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function Rogue.WillLoseExsanguinate(ThisUnit, ThisSpell)
|
|
if Rogue.Exsanguinated(ThisUnit, ThisSpell) then
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
function Rogue.ExsanguinatedRate(ThisUnit, ThisSpell)
|
|
if Rogue.Exsanguinated(ThisUnit, ThisSpell) then
|
|
return 2.0
|
|
end
|
|
|
|
return 1.0
|
|
end
|
|
|
|
-- Exsanguinate OnCast Listener
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, DestGUID, _, _, _, SpellID)
|
|
-- Exsanguinate
|
|
if SpellID == 200806 then
|
|
for _, ExsanguinatedByGUID in pairs(ExsanguinatedByBleed) do
|
|
for GUID, _ in pairs(ExsanguinatedByGUID) do
|
|
if GUID == DestGUID then
|
|
ExsanguinatedByGUID[GUID] = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end,
|
|
"SPELL_CAST_SUCCESS"
|
|
)
|
|
-- Bleed OnApply/OnRefresh Listener
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, DestGUID, _, _, _, SpellID)
|
|
if SpellID == 121411 then
|
|
-- Crimson Tempest
|
|
ExsanguinatedByBleed.CrimsonTempest[DestGUID] = false
|
|
elseif SpellID == 703 then
|
|
-- Garrote
|
|
ExsanguinatedByBleed.Garrote[DestGUID] = false
|
|
elseif SpellID == 1943 then
|
|
-- Rupture
|
|
ExsanguinatedByBleed.Rupture[DestGUID] = false
|
|
end
|
|
end,
|
|
"SPELL_AURA_APPLIED", "SPELL_AURA_REFRESH"
|
|
)
|
|
-- Bleed OnRemove Listener
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, DestGUID, _, _, _, SpellID)
|
|
if SpellID == 121411 then
|
|
-- Crimson Tempest
|
|
if ExsanguinatedByBleed.CrimsonTempest[DestGUID] ~= nil then
|
|
ExsanguinatedByBleed.CrimsonTempest[DestGUID] = nil
|
|
end
|
|
elseif SpellID == 703 then
|
|
-- Garrote
|
|
if ExsanguinatedByBleed.Garrote[DestGUID] ~= nil then
|
|
ExsanguinatedByBleed.Garrote[DestGUID] = nil
|
|
end
|
|
elseif SpellID == 1943 then
|
|
-- Rupture
|
|
if ExsanguinatedByBleed.Rupture[DestGUID] ~= nil then
|
|
ExsanguinatedByBleed.Rupture[DestGUID] = nil
|
|
end
|
|
end
|
|
end,
|
|
"SPELL_AURA_REMOVED"
|
|
)
|
|
-- Bleed OnUnitDeath Listener
|
|
HL:RegisterForCombatEvent(
|
|
function(_, _, _, _, _, _, _, DestGUID)
|
|
-- Crimson Tempest
|
|
if ExsanguinatedByBleed.CrimsonTempest[DestGUID] ~= nil then
|
|
ExsanguinatedByBleed.CrimsonTempest[DestGUID] = nil
|
|
end
|
|
-- Garrote
|
|
if ExsanguinatedByBleed.Garrote[DestGUID] ~= nil then
|
|
ExsanguinatedByBleed.Garrote[DestGUID] = nil
|
|
end
|
|
-- Rupture
|
|
if ExsanguinatedByBleed.Rupture[DestGUID] ~= nil then
|
|
ExsanguinatedByBleed.Rupture[DestGUID] = nil
|
|
end
|
|
end,
|
|
"UNIT_DIED", "UNIT_DESTROYED"
|
|
)
|
|
end
|
|
|
|
--- Fan the Hammer Tracking
|
|
do
|
|
local OpportunityBuff = Spell(195627)
|
|
local FanCP = 0
|
|
local FanStart = GetTime()
|
|
|
|
function Rogue.FanTheHammerCP()
|
|
if (GetTime() - FanStart) < 0.5 and FanCP > 0 then
|
|
if FanCP > Player:ComboPoints() then
|
|
return FanCP
|
|
else
|
|
FanCP = 0
|
|
end
|
|
end
|
|
|
|
return 0
|
|
end
|
|
|
|
-- Reset counter on energize
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, SpellID, _, _, Amount, Over )
|
|
if SpellID == 185763 then
|
|
if (GetTime() - FanStart) > 0.5 then
|
|
-- Subsequent Fan the Hammer procs are reduced by 1 CP
|
|
FanCP = mathmin(Rogue.CPMaxSpend(), Player:ComboPoints() + Amount + (mathmax(0, Amount - 1) * mathmin(2, Player:BuffStack(OpportunityBuff) - 1)))
|
|
FanStart = GetTime()
|
|
end
|
|
end
|
|
end,
|
|
"SPELL_ENERGIZE"
|
|
)
|
|
end
|
|
|
|
--- Shuriken Tornado Tracking
|
|
do
|
|
local LastEnergizeTime, LastCastTime = 0, 0
|
|
local ShurikenTornadoBuff = Spell(277925)
|
|
function Rogue.TimeToNextTornado()
|
|
if not Player:BuffUp(ShurikenTornadoBuff, nil, true) then
|
|
return 0
|
|
end
|
|
local TimeToNextTick = Player:BuffRemains(ShurikenTornadoBuff, nil, true) % 1
|
|
-- Tick happened in the same tick, we may not have the CP gain yet
|
|
if GetTime() == LastEnergizeTime then
|
|
return 0
|
|
-- Tick happened very recently, slightly before the predicted buff tick
|
|
elseif (GetTime() - LastEnergizeTime) < 0.1 and TimeToNextTick < 0.25 then
|
|
return 1
|
|
-- Tick hasn't happened yet but the predicted buff tick has passed
|
|
elseif (TimeToNextTick > 0.9 or TimeToNextTick == 0) and (GetTime() - LastEnergizeTime) > 0.75 then
|
|
return 0.1
|
|
end
|
|
return TimeToNextTick
|
|
end
|
|
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, SpellID)
|
|
-- Shuriken Storm Energize
|
|
if SpellID == 212743 then
|
|
LastEnergizeTime = GetTime()
|
|
-- Actual Shuriken Storm Cast
|
|
elseif SpellID == 197835 then
|
|
LastCastTime = GetTime()
|
|
end
|
|
-- If the player casts an actual Shuriken Storm, this value is no longer reliable
|
|
if LastCastTime == LastEnergizeTime then
|
|
LastEnergizeTime = 0
|
|
end
|
|
end,
|
|
"SPELL_CAST_SUCCESS"
|
|
)
|
|
end
|
|
|
|
--- Shadow Techniques Tracking
|
|
do
|
|
-- Variables
|
|
local ShadowTechniques = {
|
|
Counter = 0,
|
|
LastMH = 0,
|
|
LastOH = 0,
|
|
}
|
|
-- Return Time to x-th auto attack since last proc
|
|
function Rogue.TimeToSht(Hit)
|
|
if ShadowTechniques.Counter >= Hit then
|
|
return 0
|
|
end
|
|
|
|
local MHSpeed, OHSpeed = UnitAttackSpeed("player")
|
|
-- Generate the base time to use, if we are out of range this is set to the current time
|
|
local LastMH = mathmax(ShadowTechniques.LastMH + MHSpeed, GetTime())
|
|
local LastOH = mathmax(ShadowTechniques.LastOH + OHSpeed, GetTime())
|
|
|
|
local AATable = {}
|
|
for i = 0, 2 do
|
|
tableinsert(AATable, LastMH + i * MHSpeed)
|
|
tableinsert(AATable, LastOH + i * OHSpeed)
|
|
end
|
|
table.sort(AATable)
|
|
|
|
local HitInTable = mathmin(5, mathmax(1, Hit - ShadowTechniques.Counter))
|
|
|
|
return AATable[HitInTable] - GetTime()
|
|
end
|
|
-- Reset on entering world
|
|
HL:RegisterForSelfCombatEvent(
|
|
function ()
|
|
ShadowTechniques.Counter = 0
|
|
ShadowTechniques.LastMH = GetTime()
|
|
ShadowTechniques.LastOH = GetTime()
|
|
end,
|
|
"PLAYER_ENTERING_WORLD"
|
|
)
|
|
-- Reset counter on energize
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, SpellID)
|
|
-- Shadow Techniques
|
|
if SpellID == 196911 then
|
|
ShadowTechniques.Counter = 0
|
|
end
|
|
end,
|
|
"SPELL_ENERGIZE"
|
|
)
|
|
-- Increment counter on successful swings
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, IsOffHand)
|
|
ShadowTechniques.Counter = ShadowTechniques.Counter + 1
|
|
if IsOffHand then
|
|
ShadowTechniques.LastOH = GetTime()
|
|
else
|
|
ShadowTechniques.LastMH = GetTime()
|
|
end
|
|
end,
|
|
"SWING_DAMAGE"
|
|
)
|
|
-- Remember timers on swing misses
|
|
HL:RegisterForSelfCombatEvent(
|
|
function(_, _, _, _, _, _, _, _, _, _, _, _, _, _, _, IsOffHand)
|
|
if IsOffHand then
|
|
ShadowTechniques.LastOH = GetTime()
|
|
else
|
|
ShadowTechniques.LastMH = GetTime()
|
|
end
|
|
end,
|
|
"SWING_MISSED"
|
|
)
|
|
end
|
|
|
|
-- Base Crit Tracker (mainly for Outlaw)
|
|
do
|
|
local BaseCritChance = Player:CritChancePct()
|
|
local BaseCritChecksPending = 0
|
|
local function UpdateBaseCrit()
|
|
if not Player:AffectingCombat() then
|
|
BaseCritChance = Player:CritChancePct()
|
|
HL.Debug("Base Crit Set to: " .. BaseCritChance)
|
|
end
|
|
if BaseCritChecksPending == nil or BaseCritChecksPending < 0 then
|
|
BaseCritChecksPending = 0
|
|
else
|
|
BaseCritChecksPending = BaseCritChecksPending - 1
|
|
end
|
|
if BaseCritChecksPending > 0 then
|
|
C_Timer.After(3, UpdateBaseCrit)
|
|
end
|
|
end
|
|
HL:RegisterForEvent(
|
|
function ()
|
|
if BaseCritChecksPending == 0 then
|
|
C_Timer.After(3, UpdateBaseCrit)
|
|
BaseCritChecksPending = 2
|
|
end
|
|
end,
|
|
"PLAYER_EQUIPMENT_CHANGED"
|
|
)
|
|
|
|
function Rogue.BaseAttackCrit()
|
|
return BaseCritChance
|
|
end
|
|
end
|
|
|