Merge pull request 'main' (#1) from Bastion/Bastion:main into main

Reviewed-on: CiscOH/Bastion#1
4n0n-patch-1
CiscOH 2 years ago
commit 72c4c8b87b
  1. 1908
      scripts/outlaw.lua
  2. 559
      scripts/restodruid.lua
  3. 3207
      scripts/subtlety.lua
  4. 40
      src/APL/APL.lua
  5. 470
      src/MythicPlusUtils/MythicPlusUtils.lua
  6. 4
      src/ObjectManager/ObjectManager.lua
  7. 87
      src/Sequencer/Sequencer.lua
  8. 75
      src/Spell/Spell.lua
  9. 98
      src/Unit/Unit.lua
  10. 64
      src/_bastion.lua

File diff suppressed because it is too large Load Diff

@ -111,380 +111,371 @@ local RakeDebuff = Bastion.SpellBook:GetSpell(155722)
local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit) local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit)
local lowest = nil local lowest = nil
local lowestHP = math.huge local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit) Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
local hp = unit:GetHP() local hp = unit:GetHP()
if hp < lowestHP then if hp < lowestHP then
lowest = unit lowest = unit
lowestHP = hp lowestHP = hp
end end
end) end)
if not lowest then if not lowest then
lowest = Player lowest = Player
end end
return lowest return lowest
end) end)
local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit) local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit)
local lowest = nil local lowest = nil
local lowestHP = math.huge local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then
return false
end
if not Player:CanSee(unit) then Bastion.UnitManager:EnumFriends(function(unit)
return false if unit:IsDead() then
end return false
end
if Player:GetDistance(unit) > 40 then if not Player:CanSee(unit) then
return false return false
end end
if not unit:IsDead() and Player:CanSee(unit) and if Player:GetDistance(unit) > 40 then
unit:GetAuras():HasAnyDispelableAura(NaturesCure) then return false
end
local hp = unit:GetHP() if not unit:IsDead() and Player:CanSee(unit) and
if hp < lowestHP then unit:GetAuras():HasAnyDispelableAura(NaturesCure) then
lowest = unit local hp = unit:GetHP()
lowestHP = hp if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end end
end)
if lowest == nil then
lowest = None
end end
return lowest
end) end)
if lowest == nil then local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit)
lowest = None local purge = nil
end
return lowest Bastion.UnitManager:EnumEnemies(function(unit)
end) if unit:IsDead() then
return false
end
local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) if not Player:CanSee(unit) then
local purge = nil return false
end
Bastion.UnitManager:EnumEnemies(function(unit) if Player:GetDistance(unit) > 40 then
if unit:IsDead() then return false
return false end
end
if not Player:CanSee(unit) then if not unit:IsDead() and Player:CanSee(unit) and
return false unit:GetAuras():HasAnyStealableAura() then
end purge = unit
return true
end
end)
if Player:GetDistance(unit) > 40 then if purge == nil then
return false purge = None
end end
if not unit:IsDead() and Player:CanSee(unit) and return purge
unit:GetAuras():HasAnyStealableAura() then
purge = unit
return true
end
end) end)
if purge == nil then local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit)
purge = None local tank = nil
end
return purge Bastion.UnitManager:EnumFriends(function(unit)
end) if Player:GetDistance(unit) > 40 then
return false
end
local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) if not Player:CanSee(unit) then
local tank = nil return false
end
Bastion.UnitManager:EnumFriends(function(unit) if unit:IsDead() then
if Player:GetDistance(unit) > 40 then return false
return false end
end
if not Player:CanSee(unit) then if unit:IsTank() then
return false tank = unit
end return true
end
if unit:IsDead() then
return false return false
end end)
if unit:IsTank() then if tank == nil then
tank = unit tank = Player
return true
end end
return false return tank
end) end)
if tank == nil then
tank = Player
end
return tank
end)
local RejuvUnit = Bastion.UnitManager:CreateCustomUnit('rejuv', function(unit) local RejuvUnit = Bastion.UnitManager:CreateCustomUnit('rejuv', function(unit)
local lowest = nil local lowest = nil
local lowestHP = math.huge local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit) Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if not unit:IsDead() and Player:CanSee(unit) and if not unit:IsDead() and Player:CanSee(unit) and
( (
not unit:GetAuras():FindMy(Rejuvenation):IsUp() or not unit:GetAuras():FindMy(Rejuvenation):IsUp() or
unit:GetAuras():FindMy(Rejuvenation):GetRemainingTime() <= 3.6) then unit:GetAuras():FindMy(Rejuvenation):GetRemainingTime() <= 3.6) then
local hp = unit:GetHP()
local hp = unit:GetHP() if hp < lowestHP then
if hp < lowestHP then lowest = unit
lowest = unit lowestHP = hp
lowestHP = hp end
end end
end end)
end)
if lowest == nil then if lowest == nil then
lowest = Player lowest = Player
end end
return lowest return lowest
end) end)
local SwiftmendUnit = Bastion.UnitManager:CreateCustomUnit('swiftmend', function(unit) local SwiftmendUnit = Bastion.UnitManager:CreateCustomUnit('swiftmend', function(unit)
local lowest = nil local lowest = nil
local lowestHP = math.huge local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit) Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if ( if (
Player:CanSee(unit) and ( Player:CanSee(unit) and (
(unit:GetAuras():FindMy(Regrowth):IsUp()) (unit:GetAuras():FindMy(Regrowth):IsUp())
or or
( (
unit:GetAuras():FindMy(Rejuvenation):IsUp() and unit:GetAuras():FindMy(Rejuvenation):IsUp() and
not unit:GetAuras():FindMy(WildGrowth):IsUp()) not unit:GetAuras():FindMy(WildGrowth):IsUp())
) )
) then ) then
local hp = unit:GetHP()
local hp = unit:GetHP() if hp < lowestHP then
if hp < lowestHP then lowest = unit
lowest = unit lowestHP = hp
lowestHP = hp end
end end
end end)
end)
if lowest == nil then if lowest == nil then
lowest = None lowest = None
end end
return lowest return lowest
end) end)
local WildGrowthUnit = Bastion.UnitManager:CreateCustomUnit('wildgrowth', function(unit) local WildGrowthUnit = Bastion.UnitManager:CreateCustomUnit('wildgrowth', function(unit)
local lowest = nil local lowest = nil
local lowestHP = math.huge local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit) Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if Player:CanSee(unit) and ( if Player:CanSee(unit) and (
( (
Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and
( (
Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or
unit:GetPartyHPAround(30, 90) >= 2)) or unit:GetPartyHPAround(30, 90) >= 2)) or
(unit:GetPartyHPAround(30, 90) >= 3 or unit:GetPartyHPAround(30, 85) >= 2)) (unit:GetPartyHPAround(30, 90) >= 3 or unit:GetPartyHPAround(30, 85) >= 2))
then then
local hp = unit:GetHP() local hp = unit:GetHP()
if hp < lowestHP then if hp < lowestHP then
lowest = unit lowest = unit
lowestHP = hp lowestHP = hp
end
end end
end)
if lowest == nil then
lowest = None
end end
return lowest
end) end)
local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit)
local explosive = nil
if lowest == nil then Bastion.ObjectManager.explosives:each(function(unit)
lowest = None if unit:IsDead() then
end return false
end
return lowest
end)
local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) if not Player:CanSee(unit) then
local explosive = nil return false
end
Bastion.ObjectManager.explosives:each(function(unit) if Player:GetDistance(unit) <= 40 then
if unit:IsDead() then explosive = unit
return false return true
end end
end)
if not Player:CanSee(unit) then if explosive == nil then
return false explosive = None
end end
if Player:GetDistance(unit) <= 40 then return explosive
explosive = unit
return true
end
end) end)
if explosive == nil then
explosive = None
end
return explosive
end)
local RakeTarget = Bastion.UnitManager:CreateCustomUnit('rake', function(unit) local RakeTarget = Bastion.UnitManager:CreateCustomUnit('rake', function(unit)
local rakeTarget = nil local rakeTarget = nil
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and unit:InMelee(Player) and if not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and unit:InMelee(Player) and
Player:IsFacing(unit) and Player:IsFacing(unit) and
( (
not unit:GetAuras():FindMy(RakeDebuff):IsUp() or not unit:GetAuras():FindMy(RakeDebuff):IsUp() or
unit:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 3.6) then unit:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 3.6) then
rakeTarget = unit rakeTarget = unit
end end
end)
end)
if rakeTarget == nil then if rakeTarget == nil then
rakeTarget = None rakeTarget = None
end end
return rakeTarget return rakeTarget
end) end)
local MoonfireTarget = Bastion.UnitManager:CreateCustomUnit('moonfire', function(unit) local MoonfireTarget = Bastion.UnitManager:CreateCustomUnit('moonfire', function(unit)
local moonfireTarget = nil local moonfireTarget = nil
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and if not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and
( (
not unit:GetAuras():FindMy(MoonfireAura):IsUp() or not unit:GetAuras():FindMy(MoonfireAura):IsUp() or
unit:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 3.6) then unit:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 3.6) then
moonfireTarget = unit moonfireTarget = unit
end
end)
if moonfireTarget == nil then
moonfireTarget = None
end end
return moonfireTarget
end) end)
if moonfireTarget == nil then
moonfireTarget = None
end
return moonfireTarget
end)
local SunfireTarget = Bastion.UnitManager:CreateCustomUnit('sunfire', function(unit) local SunfireTarget = Bastion.UnitManager:CreateCustomUnit('sunfire', function(unit)
local sunfireTarget = nil local sunfireTarget = nil
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then if unit:IsDead() then
return false return false
end end
if not Player:CanSee(unit) then if not Player:CanSee(unit) then
return false return false
end end
if Player:GetDistance(unit) > 40 then if Player:GetDistance(unit) > 40 then
return false return false
end end
if not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and if not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and
( (
not unit:GetAuras():FindMy(SunfireAura):IsUp() or not unit:GetAuras():FindMy(SunfireAura):IsUp() or
unit:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 3.6) then unit:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 3.6) then
sunfireTarget = unit sunfireTarget = unit
end
end)
if sunfireTarget == nil then
sunfireTarget = None
end end
return sunfireTarget
end) end)
if sunfireTarget == nil then
sunfireTarget = None
end
return sunfireTarget
end)
local RestoCommands = Bastion.Command:New('resto') local RestoCommands = Bastion.Command:New('resto')
local PLACE_EFFLO = false local PLACE_EFFLO = false
@ -502,7 +493,7 @@ DamageAPL:AddSpell(
return RakeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and return RakeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
( (
not RakeTarget:GetAuras():FindMy(RakeDebuff):IsUp() or not RakeTarget:GetAuras():FindMy(RakeDebuff):IsUp() or
RakeTarget:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 3.6) RakeTarget:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 3.6)
end):SetTarget(RakeTarget) end):SetTarget(RakeTarget)
) )
@ -520,11 +511,6 @@ DamageAPL:AddSpell(
end):SetTarget(Target) end):SetTarget(Target)
) )
DefaultAPL:AddSpell(
Moonfire:CastableIf(function(self)
return Explosive:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Explosive)
)
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
Efflorescence:CastableIf(function(self) Efflorescence:CastableIf(function(self)
@ -545,7 +531,7 @@ end)
DefaultAPL:AddAction( DefaultAPL:AddAction(
'cat_form_shift', 'cat_form_shift',
function() function()
if (IsShiftKeyDown() or not Player:IsAffectingCombat()) and not Player:IsMounted() and if (IsShiftKeyDown()) and not Player:IsMounted() and
not Player:GetAuras():FindMy(CatForm):IsUp() and not Player:GetAuras():FindMy(CatForm):IsUp() and
not Player:IsCastingOrChanneling() then not Player:IsCastingOrChanneling() then
CatForm:Cast(Player) CatForm:Cast(Player)
@ -613,9 +599,9 @@ DefaultAPL:AddSpell(
and Player:CanSee(SwiftmendUnit) and and Player:CanSee(SwiftmendUnit) and
( (
SwiftmendUnit:GetHP() <= 60 or SwiftmendUnit:GetHP() <= 60 or
( (
Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2 Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2
) )
) )
end):SetTarget(SwiftmendUnit) end):SetTarget(SwiftmendUnit)
) )
@ -626,8 +612,8 @@ DefaultAPL:AddSpell(
and Player:CanSee(WildGrowthUnit) and and Player:CanSee(WildGrowthUnit) and
( (
Player:GetAuras():FindMy(SoulOfTheForest):IsUp() Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
or or
(WildGrowthUnit:GetPartyHPAround(30, 90) >= 3 or WildGrowthUnit:GetPartyHPAround(30, 85) >= 2) (WildGrowthUnit:GetPartyHPAround(30, 90) >= 3 or WildGrowthUnit:GetPartyHPAround(30, 85) >= 2)
) and ) and
not Player:IsMoving() not Player:IsMoving()
end):SetTarget(WildGrowthUnit) end):SetTarget(WildGrowthUnit)
@ -647,7 +633,7 @@ DefaultAPL:AddSpell(
and Player:CanSee(Lowest) and Lowest:GetHP() < 70 and and Player:CanSee(Lowest) and Lowest:GetHP() < 70 and
( (
NaturesSwiftness:GetTimeSinceLastCast() < 2 or Player:GetAuras():FindMy(NaturesSwiftness):IsUp() or NaturesSwiftness:GetTimeSinceLastCast() < 2 or Player:GetAuras():FindMy(NaturesSwiftness):IsUp() or
NaturesSwiftness:IsKnownAndUsable()) and not Player:IsMoving() and NaturesSwiftness:IsKnownAndUsable()) and not Player:IsMoving() and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() not Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
end):SetTarget(Lowest) end):SetTarget(Lowest)
) )
@ -680,7 +666,7 @@ DefaultAPL:AddSpell(
and and
( (
not Player:GetAuras():FindMy(LifebloomAura):IsUp() or not Player:GetAuras():FindMy(LifebloomAura):IsUp() or
Player:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Player:IsAffectingCombat() Player:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Player:IsAffectingCombat()
end):SetTarget(Player) end):SetTarget(Player)
) )
@ -690,7 +676,7 @@ DefaultAPL:AddSpell(
and and
( (
not Tank:GetAuras():FindMy(LifebloomAura):IsUp() or not Tank:GetAuras():FindMy(LifebloomAura):IsUp() or
Tank:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Tank:IsAffectingCombat() Tank:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Tank:IsAffectingCombat()
end):SetTarget(Tank) end):SetTarget(Tank)
) )
@ -700,7 +686,7 @@ DefaultAPL:AddSpell(
and Player:CanSee(Lowest) and and Player:CanSee(Lowest) and
( (
not Player:GetAuras():FindMy(Regrowth):IsUp() and Lowest:GetHP() < 70 or not Player:GetAuras():FindMy(Regrowth):IsUp() and Lowest:GetHP() < 70 or
(Lowest:GetHP() <= 85 and Player:GetAuras():FindMy(ClearCasting):IsUp())) and (Lowest:GetHP() <= 85 and Player:GetAuras():FindMy(ClearCasting):IsUp())) and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and
not Player:IsMoving() not Player:IsMoving()
end):SetTarget(Lowest) end):SetTarget(Lowest)
@ -714,13 +700,20 @@ DefaultAPL:AddSpell(
end):SetTarget(RejuvUnit) end):SetTarget(RejuvUnit)
) )
DefaultAPL:AddSpell(
Moonfire:CastableIf(function(self)
return Explosive:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Explosive)
)
DefaultAPL:AddSpell( DefaultAPL:AddSpell(
Sunfire:CastableIf(function(self) Sunfire:CastableIf(function(self)
return SunfireTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() return SunfireTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(SunfireTarget) and and Player:CanSee(SunfireTarget) and
( (
not SunfireTarget:GetAuras():FindMy(SunfireAura):IsUp() or not SunfireTarget:GetAuras():FindMy(SunfireAura):IsUp() or
SunfireTarget:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 5.4) and SunfireTarget:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 5.4) and
SunfireTarget:IsHostile() and SunfireTarget:IsHostile() and
SunfireTarget:IsAffectingCombat() and Player:GetPP() >= 25 SunfireTarget:IsAffectingCombat() and Player:GetPP() >= 25
end):SetTarget(SunfireTarget) end):SetTarget(SunfireTarget)
@ -732,7 +725,7 @@ DefaultAPL:AddSpell(
and Player:CanSee(MoonfireTarget) and and Player:CanSee(MoonfireTarget) and
( (
not MoonfireTarget:GetAuras():FindMy(MoonfireAura):IsUp() or not MoonfireTarget:GetAuras():FindMy(MoonfireAura):IsUp() or
MoonfireTarget:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 5.4) and MoonfireTarget:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 5.4) and
MoonfireTarget:IsHostile() and MoonfireTarget:IsHostile() and
MoonfireTarget:IsAffectingCombat() and Player:GetPP() >= 25 MoonfireTarget:IsAffectingCombat() and Player:GetPP() >= 25
end):SetTarget(MoonfireTarget) end):SetTarget(MoonfireTarget)

File diff suppressed because it is too large Load Diff

@ -82,6 +82,25 @@ end
-- Execute -- Execute
function APLActor:Execute() function APLActor:Execute()
-- If the actor is a sequencer we don't want to continue executing the APL if the sequencer is not finished
if self:GetActor().sequencer then
if self:GetActor().condition and self:GetActor().condition() and not self:GetActor().sequencer:Finished() then
print("Execute?")
self:GetActor().sequencer:Execute()
return true
end
if not self:GetActor().condition and not self:GetActor().sequencer:Finished() then
print("Execute?")
self:GetActor().sequencer:Execute()
return true
end
-- Check if the sequencer can be reset and reset it if it can
if self:GetActor().sequencer:ShouldReset() then
self:GetActor().sequencer:Reset()
end
end
if self:GetActor().apl then if self:GetActor().apl then
if self:GetActor().condition and self:GetActor().condition() then if self:GetActor().condition and self:GetActor().condition() then
-- print("Bastion: APL:Execute: Executing sub APL " .. self:GetActor().apl.name) -- print("Bastion: APL:Execute: Executing sub APL " .. self:GetActor().apl.name)
@ -116,6 +135,7 @@ function APLActor:Execute()
-- print("Bastion: APL:Execute: Setting variable " .. self:GetActor().variable) -- print("Bastion: APL:Execute: Setting variable " .. self:GetActor().variable)
self:GetActor()._apl.variables[self:GetActor().variable] = self:GetActor().cb(self:GetActor()._apl) self:GetActor()._apl.variables[self:GetActor().variable] = self:GetActor().cb(self:GetActor()._apl)
end end
return false
end end
-- has traits -- has traits
@ -229,13 +249,29 @@ end
function APL:Execute() function APL:Execute()
for _, actor in ipairs(self.apl) do for _, actor in ipairs(self.apl) do
if actor:HasTraits() and actor:Evaluate() then if actor:HasTraits() and actor:Evaluate() then
actor:Execute() if actor:Execute() then
print("BREAQK", actor)
break
end
else else
actor:Execute() if actor:Execute() then
print("BREAQK", actor)
break
end
end end
end end
end end
-- Add a Sequencer to the APL
---@param sequencer Sequencer
---@param condition fun(...):boolean
---@return APLActor
function APL:AddSequence(sequencer, condition)
local actor = APLActor:New({ sequencer = sequencer, condition = condition })
table.insert(self.apl, actor)
return actor
end
-- tostring -- tostring
---@return string ---@return string
function APL:__tostring() function APL:__tostring()

@ -18,71 +18,365 @@ function MythicPlusUtils:New()
self.random = math.random(1000000, 9999999) self.random = math.random(1000000, 9999999)
self.kickList = { self.kickList = {
-- Algeth'ar Academy -- Ruby life pools
[396812] = true, -- https://www.wowhead.com/spell=396812/mystic-blast [372735] = { -- Techtonic Slam
[388392] = true, -- https://www.wowhead.com/spell=388392/monotonous-lecture [187969] = {
[388863] = true, -- https://www.wowhead.com/spell=388863/mana-void false, true, true -- Kick, Stun, Disorient
[388862] = true, -- https://www.wowhead.com/spell=388862/surge }
[377389] = true, -- https://www.wowhead.com/spell=377389/call-of-the-flock },
[388623] = true, -- https://www.wowhead.com/spell=388623/branch-out [384933] = { -- Ice Shield
[396640] = true, -- https://www.wowhead.com/spell=396640/healing-touch [188067] = {
[387975] = true, -- https://www.wowhead.com/spell=387975/arcane-missiles true, true, true
[387843] = true, -- https://www.wowhead.com/spell=387843/astral-bomb }
},
-- Court of Stars [372749] = { -- Ice Shield
[211401] = true, -- https://wowhead.com/spell=211401 [188067] = {
[207980] = true, -- https://wowhead.com/spell=207980 true, true, true
[208165] = true, -- https://wowhead.com/spell=208165 }
[207881] = true, -- https://wowhead.com/spell=207881 },
[209413] = true, -- https://wowhead.com/spell=209413 [372743] = { -- Ice Shield
[188067] = {
-- Halls of Valor true, true, true
[198595] = true, -- https://wowhead.com/spell=198595 }
[198959] = true, -- https://wowhead.com/spell=198959 },
[215433] = true, -- https://wowhead.com/spell=215433 [371984] = {
[192288] = true, -- https://wowhead.com/spell=192288 [188067] = {
[199726] = true, -- https://wowhead.com/spell=199726 true, true, true
[198750] = true, -- https://wowhead.com/spell=198750 }
},
-- Ruby Life Pools [373680] = {
[372749] = true, -- https://wowhead.com/spell=372749 [188252] = {
[373803] = true, -- https://wowhead.com/spell=373803 true, false, false
[373017] = true, -- https://wowhead.com/spell=373017 }
[392398] = true, -- https://wowhead.com/spell=392398 },
[392451] = true, -- https://wowhead.com/spell=392451 [373688] = {
[385310] = true, -- https://wowhead.com/spell=385310 [188252] = {
true, false, false
-- Shadowmoon Burial Grounds }
[152818] = true, -- https://wowhead.com/spell=152818 },
[156776] = true, -- https://wowhead.com/spell=156776 [385310] = {
[156722] = true, -- https://wowhead.com/spell=156722 [195119] = {
[398206] = true, -- https://wowhead.com/spell=398206 true, false, false
[153524] = true, -- https://wowhead.com/spell=153524 }
[156718] = true, -- https://wowhead.com/spell=156718 },
[384194] = {
-- Temple of the Jade Serpent [190207] = {
[397888] = true, -- https://wowhead.com/spell=397888 true, true, true
[395859] = true, -- https://wowhead.com/spell=395859 }
[396073] = true, -- https://wowhead.com/spell=396073 },
[397914] = true, -- https://wowhead.com/spell=397914 [384197] = {
[190207] = {
-- The Azure Vault true, true, true
[375602] = true, -- https://wowhead.com/spell=375602 }
[387564] = true, -- https://wowhead.com/spell=387564 },
[373932] = true, -- https://wowhead.com/spell=373932 [373017] = {
[386546] = true, -- https://wowhead.com/spell=386546 [189886] = {
[389804] = true, -- https://wowhead.com/spell=389804 true, false, false
[377488] = true, -- https://wowhead.com/spell=377488 }
[377503] = true, -- https://wowhead.com/spell=377503 },
[392576] = {
-- NO [198047] = {
[384365] = true, -- https://wowhead.com/spell=384365 true, false, false
[386012] = true, -- https://wowhead.com/spell=386012 }
[386024] = true, -- https://wowhead.com/spell=386024 },
[387411] = true, -- https://wowhead.com/spell=387411 [392451] = {
[387606] = true, -- https://wowhead.com/spell=387606 [197985] = {
[373395] = true, -- https://wowhead.com/spell=373395 true, true, false,
[376725] = true, -- https://wowhead.com/spell=376725 }
},
[392452] = {
[197985] = {
true, true, false,
}
},
-- Nokhud
[383823] = {
[192796] = {
false, true, true
}
},
[384492] = {
[192794] = {
false, true, true
}
},
[384365] = {
[192800] = {
true, false, false
},
[191847] = {
true, false, false
}
},
[386012] = {
[194317] = {
true, false, false
},
[195265] = {
true, false, false
},
[194315] = {
true, false, false
},
[194316] = {
true, false, false
}
},
[386028] = {
[195696] = {
true, false, false
}
},
[386024] = {
[194894] = {
true, true, true
}
},
[386025] = {
[194894] = {
true, true, true
}
},
[387629] = {
[195876] = {
false, true, true
}
},
[387608] = {
[195842] = {
false, true, true
}
},
[387611] = {
[195842] = {
false, true, true
}
},
[387440] = {
[195878] = {
false, true, true
}
},
[373395] = {
[199717] = {
true, false, false
}
},
[376725] = {
[190294] = {
true, true, true
},
},
[370764] = {
[187160] = {
false, true, true
},
[196116] = {
false, true, true
},
},
[387564] = {
[196102] = {
true, true, true
}
},
[375596] = {
[196115] = {
true, false, false
},
[191164] = {
true, false, false
},
},
[386549] = {
[186741] = {
true, true, true
}
},
[386546] = {
[186741] = {
true, true, true
}
},
[389804] = {
[187154] = {
true, false, false
}
},
[377488] = {
[187155] = {
true, true, true
}
},
[377105] = {
[190510] = {
false, true, true
}
},
[373932] = {
[190187] = {
true, false, false
}
},
-- AA
[387910] = {
[196200] = {
false, true, true
}
},
[387975] = {
[196202] = {
true, true, true
}
},
[388863] = {
[196045] = {
true, true, true
}
},
[388392] = {
[196044] = {
true, true, true
}
},
[396812] = {
[196576] = {
true, true, true
}
},
[377389] = {
[192333] = {
true, false, false
}
},
[397888] = {
[200126] = {
true, true, true
}
},
[397801] = {
[56448] = {
true, false, false
}
},
[395859] = {
[59555] = {
true, true, true
}
},
[395872] = {
[59546] = {
true, false, false
}
},
[396018] = {
[59552] = {
true, false, false
}
},
[396073] = {
[59544] = {
true, true, false
}
},
[397899] = {
[200131] = {
false, true, true
}
},
[397914] = {
[200137] = {
true, true, true
}
},
-- sbg
[152818] = {
[75713] = {
true, true, false
}
},
[398154] = {
[75451] = {
false, true, true
}
},
[156776] = {
[76446] = {
true, true, true
}
},
[156772] = {
[77700] = {
true, false, false
}
},
[153524] = {
[75459] = {
true, true, true
}
},
[156718] = {
[76104] = {
true, false, false
}
},
[225100] = {
[104270] = {
true, false, false
}
},
[210261] = {
[104251] = {
true, true, true
}
},
[209027] = {
[104246] = {
false, true, true
}
},
[212031] = {
[105705] = {
false, true, false
}
},
[212784] = {
[105715] = {
false, true, false
}
},
[198585] = {
[95842] = {
true, true, true
}
},
[198959] = {
[96664] = {
true, true, true
}
},
[215433] = {
[95834] = {
true, true, true
}
},
[199210] = {
[96640] = {
false, true, true
}
},
[199090] = {
[96611] = {
false, true, true
}
},
[185425] = {
[96677] = {
false, true, false
}
},
} }
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras) Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
@ -101,7 +395,7 @@ function MythicPlusUtils:New()
WriteFile('bastion-MPlusDebuffs-' .. self.random .. '.lua', [[ WriteFile('bastion-MPlusDebuffs-' .. self.random .. '.lua', [[
AuraName: ]] .. aura:GetName() .. [[ AuraName: ]] .. aura:GetName() .. [[
AuraID: ]] .. aura:GetSpell():GetID() .. "\n" .. [[ AuraID: ]] .. aura:GetSpell():GetID() .. "\n" .. [[
]] , true) ]], true)
end end
end end
end end
@ -167,7 +461,49 @@ function MythicPlusUtils:CastingCriticalKick(unit, percent)
if castingSpell then if castingSpell then
local spellID = castingSpell:GetID() local spellID = castingSpell:GetID()
if self.kickList[spellID] and unit:IsInterruptibleAt(percent) then local kickEntry = self.kickList[spellID]
if not kickEntry then
return false
end
local npcTraits = kickEntry[unit:GetID()]
if not npcTraits then
return false
end
local isKick, isStun, isDisorient = unpack(npcTraits)
if isKick and unit:IsInterruptibleAt(percent) then
return true
end
end
return false
end
---@param unit Unit
---@param percent number
---@return boolean
function MythicPlusUtils:CastingCriticalStun(unit, percent)
local castingSpell = unit:GetCastingOrChannelingSpell()
if castingSpell then
local spellID = castingSpell:GetID()
local kickEntry = self.kickList[spellID]
if not kickEntry then
return false
end
local npcTraits = kickEntry[unit:GetID()]
if not npcTraits then
return false
end
local isKick, isStun, isDisorient = unpack(npcTraits)
if (isStun or isDisorient) and not isKick and unit:IsInterruptibleAt(percent, true) then
return true return true
end end
end end

@ -75,7 +75,7 @@ function ObjectManager:Refresh()
for _, object in pairs(objects) do for _, object in pairs(objects) do
self:EnumLists(object) self:EnumLists(object)
if ObjectType(object) == 5 or ObjectType(object) == 6 then if ({ [5] = true,[6] = true,[7] = true })[ObjectType(object)] then
local unit = Bastion.UnitManager:GetObject(ObjectGUID(object)) local unit = Bastion.UnitManager:GetObject(ObjectGUID(object))
if not unit then if not unit then
unit = Bastion.Unit:New(object) unit = Bastion.Unit:New(object)
@ -84,7 +84,7 @@ function ObjectManager:Refresh()
if unit:GetID() == 120651 then if unit:GetID() == 120651 then
self.explosives:push(unit) self.explosives:push(unit)
elseif unit:IsPlayer() and unit:IsInParty() then elseif unit:IsPlayer() and (unit:IsInParty() or unit == Bastion.UnitManager['player']) then
self.friends:push(unit) self.friends:push(unit)
elseif unit:IsEnemy() then elseif unit:IsEnemy() then
self.enemies:push(unit) self.enemies:push(unit)

@ -0,0 +1,87 @@
-- Create a sequencer class that takes a table of actions and executes them in order
---@class Sequencer
local Sequencer = {}
Sequencer.__index = Sequencer
-- Constructor
---@param actions table
---@return Sequencer
function Sequencer:New(actions, resetCondition)
local self = setmetatable({}, Sequencer)
self.actions = actions
self.index = 1
self.resetCondition = resetCondition
return self
end
-- Should we reset the sequencer
---@return boolean
function Sequencer:ShouldReset()
if self.resetCondition then
return self.resetCondition()
end
return false
end
-- Should we abort the sequencer
---@return boolean
function Sequencer:ShouldAbort()
if self.abortCondition then
return self.abortCondition()
end
return false
end
-- Execute the next action in the sequence if it doesn't return true we need to try it again
---@return boolean
function Sequencer:Next()
if self:Finished() then
print("Its finished?")
return false
end
local action = self.actions[self.index]
print("Attempting action: " .. self.index .. "")
if action(self) then
self.index = self.index + 1
return true
end
return false
end
-- Reset the sequencer
---@return nil
function Sequencer:Reset()
self.index = 1
end
function Sequencer:Execute()
if self:Next() then
return true
end
return false
end
function Sequencer:Finished()
return self.index > #self.actions
end
function Sequencer:Abort()
self.index = #self.actions + 1
end
-- tostring
---@return string
function Sequencer:__tostring()
return "Bastion.__Sequencer"
end
return Sequencer

@ -7,8 +7,9 @@ local Spell = {
PreCastFunc = false, PreCastFunc = false,
OnCastFunc = false, OnCastFunc = false,
PostCastFunc = false, PostCastFunc = false,
lastCastAttempt = false,
wasLooking = false, wasLooking = false,
lastCastAt = 0, lastCastAt = false,
conditions = {}, conditions = {},
target = false, target = false,
} }
@ -118,6 +119,13 @@ function Spell:OnCooldown()
return self:GetCooldownRemaining() > 0 return self:GetCooldownRemaining() > 0
end end
-- Clear castable function
---@return Spell
function Spell:ClearCastableFunction()
self.CastableIfFunc = false
return self
end
-- Cast the spell -- Cast the spell
---@param unit Unit ---@param unit Unit
---@param condition string ---@param condition string
@ -162,6 +170,42 @@ function Spell:Cast(unit, condition)
return true return true
end end
-- ForceCast the spell
---@param unit Unit
---@param condition string
---@return boolean
function Spell:ForceCast(unit)
-- Call pre cast function
-- if self:GetPreCastFunction() then
-- self:GetPreCastFunction()(self)
-- end
-- Check if the mouse was looking
self.wasLooking = IsMouselooking()
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast
local u = unit:GetOMToken()
if type(u) == "string" and string.find(u, 'nameplate') then
u = Object(u)
end
-- Cast the spell
CastSpellByName(self:GetName(), u)
SpellCancelQueuedSpell()
Bastion:Debug("Casting", self)
-- Set the last cast time
self.lastCastAttempt = GetTime()
-- -- Call post cast function
-- if self:GetOnCastFunction() then
-- self:GetOnCastFunction()(self)
-- end
return true
end
-- Get post cast func -- Get post cast func
---@return fun(self:Spell) ---@return fun(self:Spell)
function Spell:GetPostCastFunction() function Spell:GetPostCastFunction()
@ -186,7 +230,7 @@ end
---@return boolean ---@return boolean
function Spell:IsUsable() function Spell:IsUsable()
local usable, noMana = IsUsableSpell(self:GetID()) local usable, noMana = IsUsableSpell(self:GetID())
return usable or usableExcludes[self:GetID()] return usable or usableExcludes[self:GetID()] and not noMana
end end
-- Check if the spell is castable -- Check if the spell is castable
@ -305,12 +349,25 @@ function Spell:GetTimeSinceLastCast()
return GetTime() - self:GetLastCastTime() return GetTime() - self:GetLastCastTime()
end end
-- Get the time since the last cast attempt
---@return number
function Spell:GetTimeSinceLastCastAttempt()
if not self.lastCastAttempt then
return math.huge
end
return GetTime() - self.lastCastAttempt
end
-- Get the spells charges -- Get the spells charges
---@return number ---@return number
function Spell:GetCharges() function Spell:GetCharges()
return GetSpellCharges(self:GetID()) return GetSpellCharges(self:GetID())
end end
function Spell:GetMaxCharges()
return select(2, GetSpellCharges(self:GetID()))
end
-- Get the spells charges -- Get the spells charges
---@return number ---@return number
function Spell:GetChargesFractional() function Spell:GetChargesFractional()
@ -404,24 +461,24 @@ end
---@return boolean ---@return boolean
function Spell:IsMagicDispel() function Spell:IsMagicDispel()
return ({ return ({
[88423] = true [88423] = true
})[self:GetID()] })[self:GetID()]
end end
-- IsCurseDispel -- IsCurseDispel
---@return boolean ---@return boolean
function Spell:IsCurseDispel() function Spell:IsCurseDispel()
return ({ return ({
[88423] = true [88423] = true
})[self:GetID()] })[self:GetID()]
end end
-- IsPoisonDispel -- IsPoisonDispel
---@return boolean ---@return boolean
function Spell:IsPoisonDispel() function Spell:IsPoisonDispel()
return ({ return ({
[88423] = true [88423] = true
})[self:GetID()] })[self:GetID()]
end end
-- IsDiseaseDispel -- IsDiseaseDispel
@ -429,7 +486,7 @@ end
function Spell:IsDiseaseDispel() function Spell:IsDiseaseDispel()
return ({ return ({
})[self:GetID()] })[self:GetID()]
end end
-- IsSpell -- IsSpell

@ -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,8 +375,9 @@ 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(
.unit) self
.unit)
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit
@ -408,8 +412,9 @@ 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(
.unit) self
.unit)
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit
@ -429,8 +434,9 @@ 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(
.unit) self
.unit)
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit
@ -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
@ -575,7 +582,7 @@ end
---@return boolean ---@return boolean
function Unit:IsTanking(unit) function Unit:IsTanking(unit)
local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(), local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(),
unit:GetOMToken()) unit:GetOMToken())
return isTanking return isTanking
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 > 20 then if #self.regression_history > 60 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 > 20 then if #self.regression_history > 60 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

@ -18,6 +18,8 @@ Bastion.List = Bastion.require("List")
Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList") Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList")
---@type Vector3 ---@type Vector3
Bastion.Vector3 = Bastion.require("Vector3") Bastion.Vector3 = Bastion.require("Vector3")
---@type Sequencer
Bastion.Sequencer = Bastion.require("Sequencer")
---@type Command ---@type Command
Bastion.Command = Bastion.require("Command") Bastion.Command = Bastion.require("Command")
---@type Cache ---@type Cache
@ -85,8 +87,24 @@ Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
end end
end) end)
local pguid = UnitGUID("player")
local missed = {}
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 args = { CombatLogGetCurrentEventInfo() }
local subEvent = args[2]
local sourceGUID = args[4]
local destGUID = args[8]
local spellID = args[12]
-- if sourceGUID == pguid then
-- local args = { CombatLogGetCurrentEventInfo() }
-- for i = 1, #args do
-- Log(tostring(args[i]))
-- end
-- end
local u = Bastion.UnitManager[sourceGUID] local u = Bastion.UnitManager[sourceGUID]
local u2 = Bastion.UnitManager[destGUID] local u2 = Bastion.UnitManager[destGUID]
@ -99,23 +117,37 @@ Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
if u2 then if u2 then
u2:SetLastCombatTime(t) u2:SetLastCombatTime(t)
if subEvent == "SPELL_MISSED" and sourceGUID == pguid and spellID == 408 then
local missType = args[15]
if missType == "IMMUNE" then
local castingSpell = u:GetCastingOrChannelingSpell()
if castingSpell then
if not missed[castingSpell:GetID()] then
missed[castingSpell:GetID()] = true
end
end
end
end
end end
end) end)
Bastion.Ticker = C_Timer.NewTicker(0.1, function() Bastion.Ticker = C_Timer.NewTicker(0.1, function()
if not Bastion.CombatTimer:IsRunning() and UnitAffectingCombat("player") then if not Bastion.CombatTimer:IsRunning() and UnitAffectingCombat("player") then
Bastion.CombatTimer:Start() Bastion.CombatTimer:Start()
elseif Bastion.CombatTimer:IsRunning() and not UnitAffectingCombat("player") then elseif Bastion.CombatTimer:IsRunning() and not UnitAffectingCombat("player") then
Bastion.CombatTimer:Reset() Bastion.CombatTimer:Reset()
end end
if Bastion.Enabled then if Bastion.Enabled then
Bastion.ObjectManager:Refresh() Bastion.ObjectManager:Refresh()
for i = 1, #Bastion.modules do for i = 1, #Bastion.modules do
Bastion.modules[i]:Tick() Bastion.modules[i]:Tick()
end
end end
end end)
end)
function Bastion:Register(module) function Bastion:Register(module)
table.insert(Bastion.modules, module) table.insert(Bastion.modules, module)
@ -228,11 +260,17 @@ Command:Register('mplus', 'Toggle m+ module on/off', function(args)
Bastion:Print("casts") Bastion:Print("casts")
end) end)
Command:Register('missed', 'Dump the list of immune kidney shot spells', function()
for k, v in pairs(missed) do
Bastion:Print(k)
end
end)
local files = ListFiles("scripts/bastion/scripts") local files = ListFiles("scripts/bastion/scripts")
for i = 1, #files do for i = 1, #files do
local file = files[i] local file = files[i]
if file:sub(-4) == ".lua" or file:sub(-5) == '.luac' then if file:sub( -4) == ".lua" or file:sub( -5) == '.luac' then
Tinkr:require("scripts/bastion/scripts/" .. file:sub(1, -5), Bastion) Tinkr:require("scripts/bastion/scripts/" .. file:sub(1, -5), Bastion)
end end
end end

Loading…
Cancel
Save