Add list utility class, add linear regression TTD and some other updates

4n0n-patch-1
4n0n 2 years ago
parent b94ac8fe8a
commit 34460ba87c
  1. 103
      scripts/subtlety.lua
  2. 4
      src/List/List.lua
  3. 124
      src/MythicPlusUtils/MythicPlusUtils.lua
  4. 19
      src/Spell/Spell.lua
  5. 165
      src/Unit/Unit.lua

@ -37,6 +37,7 @@ local CrimsonVial = Bastion.SpellBook:GetSpell(185311)
local Shiv = Bastion.SpellBook:GetSpell(5938) local Shiv = Bastion.SpellBook:GetSpell(5938)
local KidneyShot = Bastion.SpellBook:GetSpell(408) local KidneyShot = Bastion.SpellBook:GetSpell(408)
local InstantPoison = Bastion.SpellBook:GetSpell(315584) local InstantPoison = Bastion.SpellBook:GetSpell(315584)
local Sanguine = Bastion.SpellBook:GetSpell(326509)
local AtrophicPosion = Bastion.SpellBook:GetSpell(381637) local AtrophicPosion = Bastion.SpellBook:GetSpell(381637)
local Evasion = Bastion.SpellBook:GetSpell(5277) local Evasion = Bastion.SpellBook:GetSpell(5277)
local TricksOfTheTrade = Bastion.SpellBook:GetSpell(57934) local TricksOfTheTrade = Bastion.SpellBook:GetSpell(57934)
@ -58,10 +59,23 @@ local BlackPowder = Bastion.SpellBook:GetSpell(319175)
local SecretTechnique = Bastion.SpellBook:GetSpell(280719) local SecretTechnique = Bastion.SpellBook:GetSpell(280719)
local DarkBrew = Bastion.SpellBook:GetSpell(310454) local DarkBrew = Bastion.SpellBook:GetSpell(310454)
local Premeditation = Bastion.SpellBook:GetSpell(343173) local Premeditation = Bastion.SpellBook:GetSpell(343173)
local DanseMacabre = Bastion.SpellBook:GetSpell(393969)
local IrideusFragment = Bastion.ItemBook:GetItem(193743) local IrideusFragment = Bastion.ItemBook:GetItem(193743)
local Healthstone = Bastion.ItemBook:GetItem(5512) local Healthstone = Bastion.ItemBook:GetItem(5512)
local WindscarWhetstone = Bastion.ItemBook:GetItem(137486) local WindscarWhetstone = Bastion.ItemBook:GetItem(137486)
local DarkMoonRime = Bastion.ItemBook:GetItem(198477)
local RimeCards = {
One = Bastion.SpellBook:GetSpell(382844),
Two = Bastion.SpellBook:GetSpell(382845),
Three = Bastion.SpellBook:GetSpell(382846),
Four = Bastion.SpellBook:GetSpell(382847),
Five = Bastion.SpellBook:GetSpell(382848),
Six = Bastion.SpellBook:GetSpell(382849),
Seven = Bastion.SpellBook:GetSpell(382850),
Eight = Bastion.SpellBook:GetSpell(382851),
}
local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit)
local purge = nil local purge = nil
@ -93,7 +107,7 @@ local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit)
end) end)
local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit) local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit)
local purge = nil local kick = nil
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then if unit:IsDead() then
@ -108,17 +122,17 @@ local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit)
return false return false
end end
if Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then if Player:InMelee(unit) and Player:IsFacing(unit) and Bastion.MythicPlusUtils:CastingCriticalKick(unit, 5) then
purge = unit kick = unit
return true return true
end end
end) end)
if purge == nil then if kick == nil then
purge = None kick = None
end end
return purge return kick
end) end)
local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit)
@ -176,6 +190,7 @@ local RuptureTarget = Bastion.UnitManager:CreateCustomUnit('rupture', function()
not unit:GetAuras():FindMy(Rupture):IsUp() or not unit:GetAuras():FindMy(Rupture):IsUp() or
unit:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 unit:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
) )
and unit:TimeToDie() > 12
then then
target = unit target = unit
return true return true
@ -212,6 +227,7 @@ SpecialAPL:AddSpell(
( (
Player:GetComboPoints(Target) >= 4 and Player:GetComboPoints(Target) >= 4 and
(Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp()))) (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp())))
and not Target:GetAuras():Find(Sanguine):IsUp()
end):SetTarget(KickTarget) end):SetTarget(KickTarget)
) )
@ -310,12 +326,29 @@ SpecialAPL:AddItem(
end):SetTarget(Player) end):SetTarget(Player)
) )
SpecialAPL:AddItem(
DarkMoonRime:UsableIf(function(self)
return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and
not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) and
(Player:GetAuras():FindMy(RimeCards.One):IsUp() or
Player:GetAuras():FindMy(RimeCards.Two):IsUp() or
Player:GetAuras():FindMy(RimeCards.Three):IsUp() or
Player:GetAuras():FindMy(RimeCards.Four):IsUp() or
Player:GetAuras():FindMy(RimeCards.Five):IsUp() or
Player:GetAuras():FindMy(RimeCards.Six):IsUp() or
Player:GetAuras():FindMy(RimeCards.Seven):IsUp() or
Player:GetAuras():FindMy(RimeCards.Eight):IsUp()
)
end):SetTarget(Target)
)
-- Use Shadowstrike during Shadow Dance. -- Use Shadowstrike during Shadow Dance.
SpecialAPL:AddSpell( SpecialAPL:AddSpell(
Shadowstrike:CastableIf(function(self) Shadowstrike:CastableIf(function(self)
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Premeditation):IsUp() not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Premeditation):IsUp() and
Player:GetEnemies(10) <= 3
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -333,7 +366,11 @@ DefaultAPL:AddSpell(
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() not Player:IsCastingOrChanneling()
end):SetTarget(Player) end):SetTarget(Player):OnCast(function()
SpellCancelQueuedSpell()
ShurikenTornado:Cast(Target)
SpellCancelQueuedSpell()
end)
) )
-- Use Shadow Blades on cooldown. -- Use Shadow Blades on cooldown.
@ -375,12 +412,17 @@ DefaultAPL:AddSpell(
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
ShadowDance:CastableIf(function(self) ShadowDance:CastableIf(function(self)
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and Gloomblade:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) <= 2
end):SetTarget(Player) end):SetTarget(Player):OnCast(function()
SpellCancelQueuedSpell()
Gloomblade:Cast(Target) -- We want to cast gloomblade immediately with shadow dance to trigger 1 stack of danse macabre
SpellCancelQueuedSpell()
end)
) )
-- Use Thistle Tea when low on energy. -- Use Thistle Tea when low on energy.
-- actions.cds+=/thistle_tea,if=cooldown.symbols_of_death.remains>=3&!buff.thistle_tea.up&(energy.deficit>=100|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)
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
ThistleTea:CastableIf(function(self) ThistleTea:CastableIf(function(self)
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
@ -389,6 +431,7 @@ DefaultAPL:AddSpell(
Player:GetPowerDeficit() >= 100 and Player:GetPowerDeficit() >= 100 and
ThistleTea:GetTimeSinceLastCast() >= 3 ThistleTea:GetTimeSinceLastCast() >= 3
end):SetTarget(Player) end):SetTarget(Player)
) )
-- Use Finishing moves with 6 or more combo points (5 or more during Shadow Dance) with the following priority: -- Use Finishing moves with 6 or more combo points (5 or more during Shadow Dance) with the following priority:
@ -419,7 +462,7 @@ DefaultAPL:AddSpell(
Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and ( Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and (
not Target:GetAuras():FindMy(Rupture):IsUp() or not Target:GetAuras():FindMy(Rupture):IsUp() or
Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
) ) and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -431,7 +474,7 @@ DefaultAPL:AddSpell(
not Player:IsCastingOrChanneling() and not Player:IsCastingOrChanneling() and
(Player:GetComboPoints(Target) >= 6 or (Player:GetComboPoints(Target) >= 6 or
(Player:GetComboPoints(Target) >= 5 and (Player:GetComboPoints(Target) >= 5 and
Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and Target:GetAuras():FindMy(Rupture):IsUp()
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -486,7 +529,11 @@ AOEAPL:AddSpell(
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() not Player:IsCastingOrChanneling()
end):SetTarget(Player) end):SetTarget(Player):OnCast(function()
SpellCancelQueuedSpell()
ShurikenTornado:Cast(Target)
SpellCancelQueuedSpell()
end)
) )
-- Use Shadow Blades on cooldown. -- Use Shadow Blades on cooldown.
@ -528,9 +575,13 @@ AOEAPL:AddSpell(
AOEAPL:AddSpell( AOEAPL:AddSpell(
ShadowDance:CastableIf(function(self) ShadowDance:CastableIf(function(self)
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and Gloomblade:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) <= 2
end):SetTarget(Player) end):SetTarget(Player):OnCast(function()
SpellCancelQueuedSpell()
Gloomblade:Cast(Target) -- We want to cast gloomblade immediately with shadow dance to trigger 1 stack of danse macabre
SpellCancelQueuedSpell()
end)
) )
-- Use Thistle Tea with Shadow Dance. -- Use Thistle Tea with Shadow Dance.
@ -539,7 +590,7 @@ AOEAPL:AddSpell(
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() and not Player:IsCastingOrChanneling() and
Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and Player:GetPowerDeficit() >= 70 and Player:GetPowerDeficit() >= 100 and
ThistleTea:GetTimeSinceLastCast() >= 3 ThistleTea:GetTimeSinceLastCast() >= 3
end):SetTarget(Player) end):SetTarget(Player)
) )
@ -556,6 +607,7 @@ AOEAPL:AddSpell(
not Player:GetAuras():FindMy(SliceAndDice):IsUp() or not Player:GetAuras():FindMy(SliceAndDice):IsUp() or
Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 6 Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 6
) )
and Player:GetEnemies(10) < 6
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -569,6 +621,9 @@ AOEAPL:AddSpell(
not Target:GetAuras():FindMy(Rupture):IsUp() or not Target:GetAuras():FindMy(Rupture):IsUp() or
Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
) )
and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
and not Player:GetAuras():FindMy(SymbolsOfDeath):IsUp()
and not Player:GetAuras():FindMy(ThistleTea):IsUp()
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -582,6 +637,7 @@ AOEAPL:AddSpell(
not RuptureTarget:GetAuras():FindMy(Rupture):IsUp() or not RuptureTarget:GetAuras():FindMy(Rupture):IsUp() or
RuptureTarget:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 RuptureTarget:GetAuras():FindMy(Rupture):GetRemainingTime() < 6
) )
and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp()
end):SetTarget(RuptureTarget) end):SetTarget(RuptureTarget)
) )
@ -590,7 +646,9 @@ AOEAPL:AddSpell(
return Target:Exists() and Player:InMelee(Target) and return Target:Exists() and Player:InMelee(Target) and
self:IsKnownAndUsable() and self:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() and not Player:IsCastingOrChanneling() and
(Player:GetComboPoints(Target) >= 5) (Player:GetComboPoints(Target) >= 6 or
(Player:GetComboPoints(Target) >= 5 and
Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and Target:GetAuras():FindMy(Rupture):IsUp()
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -601,8 +659,8 @@ AOEAPL:AddSpell(
self:IsKnownAndUsable() and self:IsKnownAndUsable() and
not Player:IsCastingOrChanneling() and not Player:IsCastingOrChanneling() and
(Player:GetComboPoints(Target) >= 5) and (Player:GetComboPoints(Target) >= 5) and
(Player:GetMeleeAttackers() >= 3 or (Player:GetEnemies(10) >= 3 or
(Player:GetMeleeAttackers() >= 2 and (Player:GetEnemies(10) >= 2 and
DarkBrew:IsKnown())) DarkBrew:IsKnown()))
end):SetTarget(Target) end):SetTarget(Target)
) )
@ -659,10 +717,9 @@ AOEAPL:AddSpell(
end):SetTarget(Player) end):SetTarget(Player)
) )
SubModulue:Sync(function() SubModulue:Sync(function()
SpecialAPL:Execute() SpecialAPL:Execute()
if Player:GetMeleeAttackers() > 1 then if Player:GetEnemies(10) >= 2 then
AOEAPL:Execute() AOEAPL:Execute()
else else
DefaultAPL:Execute() DefaultAPL:Execute()

@ -3,9 +3,9 @@ local Tinkr, Bastion = ...
local List = {} local List = {}
List.__index = List List.__index = List
function List:New() function List:New(from)
local self = setmetatable({}, List) local self = setmetatable({}, List)
self._list = {} self._list = from or {}
return self return self
end end

@ -11,6 +11,119 @@ function MythicPlusUtils:New()
local self = setmetatable({}, MythicPlusUtils) local self = setmetatable({}, MythicPlusUtils)
self.random = math.random(1000000, 9999999) self.random = math.random(1000000, 9999999)
self.kickList = {
-- Algeth'ar Academy
[388392] = true, -- Monotonous Lecture
[396812] = true, -- Mystic Blast
[377389] = true, -- Call of the Flock
[396640] = true, -- Healing Touch
[387843] = true, -- Astral Bomb
[387955] = true, -- Celestial Shield
[387910] = true, -- Astral Whirlwind
-- Azure Vault
-- [375602] = true, -- Erratic Growth
[387564] = true, -- Mystic Vapors
-- [386546] = true, -- Waking Bane
[389804] = true, -- Heavy Tome
[377488] = true, -- Icy Bindings
-- Brackenhide
[382249] = true, -- Earth Bolt
[367500] = true, -- Hideous Cackle
[377950] = true, -- Greater Healing Rapids
[385029] = true, -- Screech
[373804] = true, -- Touch of Decay
[381770] = true, -- Gushing Ooze
[374544] = true, -- Burst of Decay
-- Halls of Infusion
[374066] = true, -- Earth Shield
[374339] = true, -- Demoralizing Shout
[374045] = true, -- Expulse
[374080] = true, -- Blasting Gust
[389443] = true, -- Purifying Blast
[395694] = true, -- Elemental Focus
[374563] = true, -- Dazzle
[385141] = true, -- Thunderstorm
[374706] = true, -- Pyretic Burst
[375384] = true, -- Rumbling Earth
[375950] = true, -- Ice Shards
[377348] = true, -- Tidal Divergence
[377402] = true, -- Aqueous Barrier
[387618] = true, -- Infuse
-- Neltharus
[378282] = true, -- Molten Core
[372615] = true, -- Ember Reach
[395427] = true, -- Burning Roar
[372538] = true, -- Melt
[384161] = true, -- Mote of Combustion
[382795] = true, -- Molten Barrier
-- Nokhud
[384365] = true, -- Disruptive Shout
[386024] = true, -- Tempest
[387411] = true, -- Death Bolt Volley
[387606] = true, -- Dominate
[376725] = true, -- Storm Bolt
[384808] = true, -- Guardian Wind
[383823] = true, -- Rally the Clan (CC to interrupt)
[387135] = true, -- Arcing Strike (CC to interrupt)
[373395] = true, -- Bloodcurdling Shout
-- Ruby Life Pools
[373017] = true, -- Roaring Blaze
[392398] = true, -- Crackling Detonation
[392451] = true, -- Flashfire
[385310] = true, -- Lightning Bolt
[375602] = true, -- Erratic Growth
-- [386546] = true, -- Waking Bane
-- [387564] = true, -- Mystic Vapors
[373932] = true, -- Illusionary Bolt
[386546] = true, -- Waking Bane
-- Uldaman
[369675] = true, -- Chain Lightning
[369674] = true, -- Stone Spike
[369823] = true, -- Spiked Carapace
[369603] = true, -- Defensive Bulwark
[369399] = true, -- Stone Bolt
[369400] = true, -- Earthen Ward
-- Court of Stars
[211401] = true, -- Drifting Embers
[211464] = true, -- Fel Detonation
[207980] = true, -- Disintegration Beam
[208165] = true, -- Withering Soul
[207881] = true, -- Infernal Eruption
-- Halls of Valor
[198595] = true, -- Thunderous Bolt
[198959] = true, -- Etch
[192288] = true, -- Searing Light
[199726] = true, -- Unruly Yell
[198750] = true, -- Surge
-- Shadowmoon Burial Grounds
[152818] = true, -- Shadow Mend
[153153] = true, -- Dark Communion (CC to interrupt)
[156776] = true, -- Rending Voidlash
[156722] = true, -- Void Bolt
[398206] = true, -- Death Blast
[156718] = true, -- Necrotic Burst
[153524] = true, -- Plague Spit
-- Temple of the Jade Serpent
[397888] = true, -- Hydrolance
[114646] = true, -- Haunting Gaze
[395859] = true, -- Haunting Scream
[396073] = true, -- Cat Nap
[397914] = true, -- Defiling Mist
[315584] = true
}
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras) Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
if not self.debuffLogging then if not self.debuffLogging then
@ -41,8 +154,17 @@ function MythicPlusUtils:ToggleDebuffLogging()
self.debuffLogging = not self.debuffLogging self.debuffLogging = not self.debuffLogging
end end
function MythicPlusUtils:HasCriticalDispel(unit) function MythicPlusUtils:CastingCriticalKick(unit, percent)
local castingSpell = unit:GetCastingOrChannelingSpell()
if castingSpell then
local spellID = castingSpell:GetID()
if self.kickList[spellID] and unit:IsInterruptibleAt(percent) then
return true
end
end
return false
end end
return MythicPlusUtils return MythicPlusUtils

@ -252,6 +252,25 @@ function Spell:GetCharges()
return GetSpellCharges(self:GetID()) return GetSpellCharges(self:GetID())
end end
function Spell:GetChargesFractional()
local charges, maxCharges, start, duration = GetSpellCharges(self:GetID())
if charges == maxCharges then
return maxCharges
end
if charges == 0 then
return 0
end
local timeSinceStart = GetTime() - start
local timeLeft = duration - timeSinceStart
local timePerCharge = duration / maxCharges
local chargesFractional = charges + (timeLeft / timePerCharge)
return chargesFractional
end
-- Get the spells charges remaining -- Get the spells charges remaining
function Spell:GetChargesRemaining() function Spell:GetChargesRemaining()
local charges, maxCharges, start, duration = GetSpellCharges(self:GetID()) local charges, maxCharges, start, duration = GetSpellCharges(self:GetID())

@ -4,7 +4,7 @@ local Tinkr, Bastion = ...
local Unit = { local Unit = {
cache = nil, cache = nil,
aura_table = nil, aura_table = nil,
unit = nil unit = nil,
} }
function Unit:__index(k) function Unit:__index(k)
@ -28,10 +28,11 @@ end
-- Constructor -- Constructor
function Unit:New(unit) function Unit:New(unit)
local self = setmetatable({}, Unit) local self = setmetatable({}, Unit)
self.unit = unit self.unit = unit
self.cache = Bastion.Cache:New() self.cache = Bastion.Cache:New()
self.aura_table = Bastion.AuraTable:New(self) self.aura_table = Bastion.AuraTable:New(self)
self.regression_history = {}
return self return self
end end
@ -281,6 +282,22 @@ function Unit:IsCasting()
return UnitCastingInfo(self.unit) ~= nil return UnitCastingInfo(self.unit) ~= nil
end end
-- Get Casting or channeling spell
function Unit:GetCastingOrChannelingSpell()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self
.unit)
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit)
end
if name then
return Bastion.SpellBook:GetSpell(spellId)
end
return nil
end
-- Check if the unit is channeling a spell -- Check if the unit is channeling a spell
function Unit:IsChanneling() function Unit:IsChanneling()
return UnitChannelInfo(self.unit) ~= nil return UnitChannelInfo(self.unit) ~= nil
@ -296,10 +313,25 @@ function Unit:CanAttack(unit)
return UnitCanAttack(self.unit, unit.unit) return UnitCanAttack(self.unit, unit.unit)
end end
-- Check if unit is interruptible function Unit:GetChannelOrCastPercentComplete()
function Unit:IsInterruptible(percent) local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self
local percent = percent or math.random(2, 5) .unit)
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit)
end
if name and startTimeMS and endTimeMS then
local start = startTimeMS / 1000
local finish = endTimeMS / 1000
local current = GetTime()
print(((current - start) / (finish - start)) * 100)
return ((current - start) / (finish - start)) * 100
end
return 0
end
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)
@ -307,13 +339,26 @@ function Unit:IsInterruptible(percent)
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit) name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit)
end end
if name and startTimeMS and endTimeMS and not notInterruptible then if name then
local castTimeRemaining = endTimeMS / 1000 - GetTime() return not notInterruptible
local castTimeTotal = (endTimeMS - startTimeMS) / 1000 end
if castTimeTotal > 0 and castTimeRemaining / castTimeTotal * 100 >= percent then
return true return false
end end
-- Check if unit is interruptible
function Unit:IsInterruptibleAt(percent)
if not self:IsInterruptible() then
return false
end
local percent = percent or math.random(2, 5)
local castPercent = self:GetChannelOrCastPercentComplete()
if castPercent >= percent then
return true
end end
return false return false
end end
@ -375,8 +420,12 @@ function Unit:IsMoving()
return GetUnitSpeed(self.unit) > 0 return GetUnitSpeed(self.unit) > 0
end end
function Unit:GetComboPoints(unit) function Unit:IsMovingAtAll()
return GetComboPoints(self.unit, unit.unit) return ObjectMovementFlag(self.unit) ~= 0
end
function Unit:GetComboPoints()
return UnitPower(self.unit, 4)
end end
-- IsUnit -- IsUnit
@ -470,4 +519,88 @@ function Unit:IsInParty()
return UnitInParty(self.unit) return UnitInParty(self.unit)
end end
-- Linear regression between time and percent to something
function Unit:LinearRegression(time, percent)
local x = time
local y = percent
local n = #x
local sum_x = 0
local sum_y = 0
local sum_xy = 0
local sum_xx = 0
local sum_yy = 0
for i = 1, n do
sum_x = sum_x + x[i]
sum_y = sum_y + y[i]
sum_xy = sum_xy + x[i] * y[i]
sum_xx = sum_xx + x[i] * x[i]
sum_yy = sum_yy + y[i] * y[i]
end
local slope = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x)
local intercept = (sum_y - slope * sum_x) / n
return slope, intercept
end
-- Use linear regression to get the health percent at a given time in the future
function Unit:PredictHealth(time)
local x = {}
local y = {}
if #self.regression_history > 10 then
table.remove(self.regression_history, 1)
end
table.insert(self.regression_history, { time = GetTime(), percent = self:GetHP() })
for i = 1, #self.regression_history do
local entry = self.regression_history[i]
table.insert(x, entry.time)
table.insert(y, entry.percent)
end
local slope, intercept = self:LinearRegression(x, y)
return slope * time + intercept
end
-- Use linear regression to guess the time until a given health percent
function Unit:PredictTime(percent)
local x = {}
local y = {}
if #self.regression_history > 10 then
table.remove(self.regression_history, 1)
end
table.insert(self.regression_history, { time = GetTime(), percent = self:GetHP() })
for i = 1, #self.regression_history do
local entry = self.regression_history[i]
table.insert(x, entry.time)
table.insert(y, entry.percent)
end
local slope, intercept = self:LinearRegression(x, y)
return (percent - intercept) / slope
end
-- Time until death
function Unit:TimeToDie()
if self:IsDead() then
self.regression_history = {}
return 0
end
local timeto = GetTime() - self:PredictTime(0)
if timeto ~= timeto or timeto == math.huge or timeto < 0 then
return 0
end
return timeto
end
return Unit return Unit

Loading…
Cancel
Save