@ -13,6 +13,9 @@ local Unit = {
last_off_attack = 0 ,
last_off_attack = 0 ,
last_main_attack = 0 ,
last_main_attack = 0 ,
last_combat_time = 0 ,
last_combat_time = 0 ,
ttd_ticker = 0 ,
ttd = 0 ,
id = false ,
}
}
function Unit : __index ( k )
function Unit : __index ( k )
@ -372,7 +375,8 @@ end
-- Get Casting or channeling spell
-- Get Casting or channeling spell
---@return Spell | nil
---@return Spell | nil
function Unit : GetCastingOrChannelingSpell ( )
function Unit : GetCastingOrChannelingSpell ( )
local name , text , texture , startTimeMS , endTimeMS , isTradeSkill , castID , notInterruptible , spellId = UnitCastingInfo ( self
local name , text , texture , startTimeMS , endTimeMS , isTradeSkill , castID , notInterruptible , spellId = UnitCastingInfo (
self
. unit )
. unit )
if not name then
if not name then
@ -408,7 +412,8 @@ end
---@return number
---@return number
function Unit : GetChannelOrCastPercentComplete ( )
function Unit : GetChannelOrCastPercentComplete ( )
local name , text , texture , startTimeMS , endTimeMS , isTradeSkill , castID , notInterruptible , spellId = UnitCastingInfo ( self
local name , text , texture , startTimeMS , endTimeMS , isTradeSkill , castID , notInterruptible , spellId = UnitCastingInfo (
self
. unit )
. unit )
if not name then
if not name then
@ -429,7 +434,8 @@ end
-- Check if unit is interruptible
-- Check if unit is interruptible
---@return boolean
---@return boolean
function Unit : IsInterruptible ( )
function Unit : IsInterruptible ( )
local name , text , texture , startTimeMS , endTimeMS , isTradeSkill , castID , notInterruptible , spellId = UnitCastingInfo ( self
local name , text , texture , startTimeMS , endTimeMS , isTradeSkill , castID , notInterruptible , spellId = UnitCastingInfo (
self
. unit )
. unit )
if not name then
if not name then
@ -446,9 +452,10 @@ end
-- Check if unit is interruptible
-- Check if unit is interruptible
---@param percent number
---@param percent number
---@param ignoreInterruptible boolean
---@return boolean
---@return boolean
function Unit : IsInterruptibleAt ( percent )
function Unit : IsInterruptibleAt ( percent , ignoreInterruptible )
if not self : IsInterruptible ( ) then
if not ignoreInterruptible and not self : IsInterruptible ( ) then
return false
return false
end
end
@ -623,6 +630,13 @@ function Unit:IsBehind(unit)
return math.abs ( angle ) > 90
return math.abs ( angle ) > 90
end
end
-- IsInfront
---@param unit Unit
---@return boolean
function Unit : IsInfront ( unit )
return not self : IsBehind ( unit )
end
---@return number
---@return number
function Unit : GetMeleeBoost ( )
function Unit : GetMeleeBoost ( )
if IsPlayerSpell ( 196924 ) then
if IsPlayerSpell ( 196924 ) then
@ -642,15 +656,22 @@ end
---@param unit Unit
---@param unit Unit
---@return boolean
---@return boolean
function Unit : InMelee ( unit )
function Unit : InMelee ( unit )
local x , y , z = ObjectPosition ( self : GetOMToken ( ) )
local x , y , z = ObjectPosition ( self.unit )
local x2 , y2 , z2 = ObjectPosition ( unit : GetOMToken ( ) )
local x2 , y2 , z2 = ObjectPosition ( unit.unit )
if not x or not x2 then
if not x or not x2 then
return false
return false
end
end
local scr = ObjectCombatReach ( self.unit )
local ucr = ObjectCombatReach ( unit.unit )
if not scr or not ucr then
return false
end
local dist = math.sqrt ( ( x - x2 ) ^ 2 + ( y - y2 ) ^ 2 + ( z - z2 ) ^ 2 )
local dist = math.sqrt ( ( x - x2 ) ^ 2 + ( y - y2 ) ^ 2 + ( z - z2 ) ^ 2 )
local maxDist = math.max ( ( ObjectCombatReach ( self : GetOMToken ( ) ) + 1.3333 ) + ObjectCombatReach ( unit : GetOMToken ( ) ) , 5.0 )
local maxDist = math.max ( ( scr + 1.3333 ) + ucr , 5.0 )
maxDist = maxDist + 1.0 + self : GetMeleeBoost ( )
maxDist = maxDist + 1.0 + self : GetMeleeBoost ( )
return dist <= maxDist
return dist <= maxDist
@ -659,7 +680,9 @@ end
-- Get object id
-- Get object id
---@return number
---@return number
function Unit : GetID ( )
function Unit : GetID ( )
return ObjectID ( self : GetOMToken ( ) )
if self.id then return self.id end
self.id = ObjectID ( self : GetOMToken ( ) )
return self.id
end
end
-- In party
-- In party
@ -704,7 +727,7 @@ function Unit:PredictHealth(time)
local x = { }
local x = { }
local y = { }
local y = { }
if # self.regression_history > 2 0 then
if # self.regression_history > 6 0 then
table.remove ( self.regression_history , 1 )
table.remove ( self.regression_history , 1 )
end
end
@ -727,7 +750,7 @@ function Unit:PredictTime(percent)
local x = { }
local x = { }
local y = { }
local y = { }
if # self.regression_history > 2 0 then
if # self.regression_history > 6 0 then
table.remove ( self.regression_history , 1 )
table.remove ( self.regression_history , 1 )
end
end
@ -743,21 +766,50 @@ function Unit:PredictTime(percent)
return ( percent - intercept ) / slope
return ( percent - intercept ) / slope
end
end
-- Start time to die ticker
function Unit : StartTTDTicker ( )
if self.ttd_ticker then
return
end
self.ttd_ticker = C_Timer.NewTicker ( 0.5 , function ( )
local timeto = self : PredictTime ( 0 ) - GetTime ( )
self.ttd = timeto
end )
end
-- Time until death
-- Time until death
---@return number
---@return number
function Unit : TimeToDie ( )
function Unit : TimeToDie ( )
if self : IsDead ( ) then
if self : IsDead ( ) then
self.regression_history = { }
self.regression_history = { }
if self.ttd_ticker then
self.ttd_ticker : Cancel ( )
self.ttd_ticker = nil
end
return 0
return 0
end
end
local timeto = self : PredictTime ( 0 ) - GetTime ( )
if not self.ttd_ticker then
self : StartTTDTicker ( )
end
if timeto ~= timeto or timeto < 0 or timeto == math.huge then
-- If there's not enough data to make a prediction return 0 unless the unit has more than 5 million health
if # self.regression_history < 5 and self : GetMaxHealth ( ) < 5000000 then
return 0
return 0
end
end
return timeto
-- if the unit has more than 5 million health but there's not enough data to make a prediction we can assume there's roughly 250000 damage per second and estimate the time to die
if # self.regression_history < 5 and self : GetMaxHealth ( ) > 5000000 then
return self : GetMaxHealth ( ) /
250000 -- 250000 is an estimate of the average damage per second a well geared group will average
end
if self.ttd ~= self.ttd or self.ttd < 0 or self.ttd == math.huge then
return 0
end
return self.ttd
end
end
-- Set combat time if affecting combat and return the difference between now and the last time
-- Set combat time if affecting combat and return the difference between now and the last time
@ -810,7 +862,12 @@ function Unit:GetMaxGCD()
haste = 50
haste = 50
end
end
return 1.5 / ( 1 + haste / 100 )
-- if the unit uses focus their gcd is 1.0 seconds not 1.5
local base = 1.5
if self : GetPowerType ( ) == 3 then
base = 1.0
end
return base / ( 1 + haste / 100 )
end
end
-- IsStealthed
-- IsStealthed
@ -851,7 +908,8 @@ end
---@return nil
---@return nil
function Unit : WatchForSwings ( )
function Unit : WatchForSwings ( )
Bastion.EventManager : RegisterWoWEvent ( " COMBAT_LOG_EVENT_UNFILTERED " , function ( )
Bastion.EventManager : RegisterWoWEvent ( " COMBAT_LOG_EVENT_UNFILTERED " , function ( )
local _ , subtype , _ , sourceGUID , sourceName , _ , _ , destGUID , destName , destFlags , _ , spellID , spellName , _ , amount , interrupt , a , b , c , d , offhand , multistrike = CombatLogGetCurrentEventInfo ( )
local _ , subtype , _ , sourceGUID , sourceName , _ , _ , destGUID , destName , destFlags , _ , spellID , spellName , _ , amount , interrupt , a , b , c , d , offhand , multistrike =
CombatLogGetCurrentEventInfo ( )
if sourceGUID == self : GetGUID ( ) then
if sourceGUID == self : GetGUID ( ) then
if subtype == " SPELL_ENERGIZE " and spellID == 196911 then
if subtype == " SPELL_ENERGIZE " and spellID == 196911 then