Compare commits

...

24 Commits
main ... main

Author SHA1 Message Date
Ryan Crockett 22b48bb284 More optomization 2 years ago
Ryan Crockett 2c7447c410 Add some CDs 2 years ago
Ryan Crockett 0c92f0a236 Add toggle ui 2 years ago
Ryan Crockett 3b2a60e0f6 Draw ROP location 2 years ago
Ryan Crockett 33ca6a0707 basic frost 2 years ago
Ryan Crockett bef53be50e More brew spells 2 years ago
Ryan Crockett 2cadb6e819 Beginings of brm rotation 2 years ago
Ryan Crockett 55c3d9b95a Add zen meditation and purifying brew to tankbuster list 2 years ago
Ryan Crockett 289648b17d Add important aura tracking 2 years ago
Ryan Crockett d65b0c1c26 Add util to check if ability will be cast soon 2 years ago
Ryan Crockett ada2b52ca1 Add monk stuff, more bw stuff 2 years ago
Ryan Crockett 0aa4ec5af8 Hook into bigwigs, tailor M+ utils to monk (wip) 2 years ago
Ryan Crockett b036431ee5 Finish m+ list 2 years ago
Ryan Crockett 7dddf66cb0 Some prot tweaks, modify kick & stop utils 2 years ago
Ryan Crockett dd045cda94 Tweaks 2 years ago
Ryan Crockett ba5da2df6a Some tweaks 2 years ago
Ryan Crockett 1786febbb2 Better SoR logic, add pause command 2 years ago
Ryan Crockett 82af0e2e38 Get started with queue functionality 2 years ago
Ryan Crockett e0c2e15a47 Add HoW 2 years ago
Ryan Crockett df988ab750 Seperate out rebuke logic 2 years ago
Ryan Crockett 9406772384 More prot logic 2 years ago
Ryan Crockett 84f8ab52f8 Consecrate and judgement, auto load spec profile 2 years ago
Ryan Crockett 188e415524 Some org 2 years ago
Ryan Crockett 536eb12be1 Init 2 years ago
  1. 2
      .gitignore
  2. 188
      references/brewmaster.lua
  3. 0
      references/mistweaver.lua
  4. 0
      references/outlaw.lua
  5. 226
      references/paladin/paladin_protection.lua
  6. 69
      references/paladin/shared/hammer-of-wrath.lua
  7. 57
      references/paladin/shared/rebuke.lua
  8. 0
      references/restodruid.lua
  9. 0
      references/subtlety.lua
  10. 0
      references/windwalker.lua
  11. 54
      scripts/arcane.lua
  12. 54
      scripts/fire.lua
  13. 59
      scripts/frost/frost-gui.lua
  14. 206
      scripts/frost/frost.lua
  15. 45
      scripts/shared/draw-rop.lua
  16. 25
      scripts/shared/get-ranged-target-count.lua
  17. 17
      src/AuraTable/AuraTable.lua
  18. 201
      src/ContentUtils/ContentUtils.lua
  19. 193
      src/ContentUtils/lists/important-aura-list.lua
  20. 662
      src/ContentUtils/lists/interrupt-list.lua
  21. 416
      src/ContentUtils/lists/tank-buster-list.lua
  22. 514
      src/MythicPlusUtils/MythicPlusUtils.lua
  23. 1
      src/ObjectManager/ObjectManager.lua
  24. 47
      src/Spell/Spell.lua
  25. 104
      src/ToggleManager/ToggleManager.lua
  26. 9
      src/Unit/Unit.lua
  27. 72
      src/UnitManager/UnitManager.lua
  28. 94
      src/_bastion.lua

2
.gitignore vendored

@ -6,7 +6,7 @@ DS_Store
!.gitkeep
## ignore all files in scripts
scripts/*
# scripts/*
## ignore vscode settings
.vscode/*

@ -0,0 +1,188 @@
local Tinkr, _Bastion = ...
---@type Bastion
local Bastion = _Bastion
local BrewModule = Bastion.Module:New('brewmaster')
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
local None = Bastion.UnitManager:Get('none')
local PurifyingBrew = Bastion.SpellBook:GetSpell(119582)
local CelestialBrew = Bastion.SpellBook:GetSpell(322507)
local PurifiedChi = Bastion.SpellBook:GetSpell(325092)
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local ImpCelestialBrew = Bastion.SpellBook:GetSpell(322510)
local ChiWave = Bastion.SpellBook:GetSpell(115098)
local RushingJadeWind = Bastion.SpellBook:GetSpell(116847)
local BreathOfFire = Bastion.SpellBook:GetSpell(115181)
local KegSmash = Bastion.SpellBook:GetSpell(121253)
local WeaponsOfOrder = Bastion.SpellBook:GetSpell(387184)
local BlackoutKick = Bastion.SpellBook:GetSpell(205523)
local RisingSunKick = Bastion.SpellBook:GetSpell(107428)
local TigerPalm = Bastion.SpellBook:GetSpell(100780)
local SpinningCraneKick = Bastion.SpellBook:GetSpell(322729)
local ExpelHarm = Bastion.SpellBook:GetSpell(322101)
local GiftOfTheOx = Bastion.SpellBook:GetSpell(124502)
local CDsEnabled = false
local Command = Bastion.Command:New('brewmaster')
Command:Register('cooldowns', 'Toggle Brewmaster CDs', function()
CDsEnabled = not CDsEnabled
Bastion:Print('Brewmaster Cooldowns ' .. (CDsEnabled and 'enabled' or 'disabled'))
end)
---@return number | nil
local function GetExpelHarmHealAmount()
-- Gift of the Ox orbs
local OrbCount = GetSpellCount(ExpelHarm:GetID()) or 0
local OrbHeal = tonumber((GetSpellDescription(GiftOfTheOx:GetID()):match("%d+%S+%d"):gsub("%D","")))
print(GetSpellDescription(GiftOfTheOx:GetID()):match("%d+%S+%d"))
-- expel harm
local ExpelHeal = tonumber((GetSpellDescription(ExpelHarm:GetID()):match("%d+%S+%d"):gsub("%D","")))
return ExpelHeal + (OrbHeal * OrbCount)
end
---@return boolean
local function CombatRotation()
local isAoe = Player:GetEnemies(8) > 1
-- Attack the target is auto attack isnt active.
if AutoAttack:IsKnownAndUsable() and not IsCurrentSpell(AutoAttack:GetID()) and Player:InMelee(Target) and not Target:IsDead() then
AutoAttack:Cast(Target)
end
local Stagger = UnitStagger("player")
if Stagger > 10000 and PurifyingBrew:IsKnownAndUsable() and PurifyingBrew:GetChargesRemaining() > 1 or (PurifyingBrew:GetNextChargeCooldown() <= 2.5 and PurifyingBrew:GetChargesRemaining() == 1) then
PurifyingBrew:Cast(Player)
end
if CelestialBrew:IsKnownAndUsable() and (not ImpCelestialBrew:IsKnown() or (ImpCelestialBrew:IsKnown() and Player:GetAuras():FindMy(PurifiedChi):IsUp())) then
return CelestialBrew:Cast(Player)
end
local ExpelHarmHeal = GetExpelHarmHealAmount()
if not Player:InMelee(Target) then
if not Player:GetAuras():FindMy(RushingJadeWind):IsUp() and RushingJadeWind:IsKnownAndUsable() then
return RushingJadeWind:Cast(Player)
end
if ChiWave:IsKnownAndUsable() and Target:Exists() and ChiWave:IsInRange(Target) and Player:IsFacing(Target) then
return ChiWave:Cast(Target)
end
end
if BreathOfFire:IsKnownAndUsable() and BreathOfFire:IsInRange(Target) and Player:IsFacing(Target) then
return BreathOfFire:Cast(Player)
end
if KegSmash:IsKnownAndUsable() and Target:Exists() and KegSmash:IsInRange(Target) and Player:IsFacing(Target) then
if KegSmash:GetChargesRemaining() == KegSmash:GetMaxCharges() then
return KegSmash:Cast(Target)
end
if KegSmash:GetChargesRemaining() == KegSmash:GetMaxCharges() - 1 and KegSmash:GetNextChargeCooldown() <= 3 then
return KegSmash:Cast(Target)
end
end
if WeaponsOfOrder:IsKnownAndUsable() and CDsEnabled then
return WeaponsOfOrder:Cast(Player)
end
-- Exploding Keg in AOE here
-- Rising Sun Kick In AOE here
if BlackoutKick:IsKnownAndUsable() and Target:Exists() and BlackoutKick:IsInRange(Target) and Player:IsFacing(Target) then
return BlackoutKick:Cast(Target)
end
if RisingSunKick:IsKnownAndUsable() and Target:Exists() and RisingSunKick:IsInRange(Target) and Player:IsFacing(Target) then
return RisingSunKick:Cast(Target)
end
-- Exploding Keg in ST here
if ChiWave:IsKnownAndUsable() and Target:Exists() and ChiWave:IsInRange(Target) then
return ChiWave:Cast(Target)
end
if RushingJadeWind:IsKnownAndUsable() and (not Player:GetAuras():FindMy(RushingJadeWind):IsUp() or Player:GetAuras():FindMy(RushingJadeWind):GetRemainingTime() <= 2) then
return RushingJadeWind:Cast(Player)
end
if not isAoe and TigerPalm:IsKnownAndUsable() and Target:Exists() and TigerPalm:IsInRange(Target) and Player:IsFacing(Target) then
if not KegSmash:IsKnown() then
return TigerPalm:Cast(Target)
else
local TigerPalmCost = GetSpellPowerCost(TigerPalm:GetID())[1]['cost']
local KegSmashCost = GetSpellPowerCost(KegSmash:GetID())[1]['cost']
local CurrentEnergy = Player:GetPower(Enum.PowerType.Energy)
local CanKegSmash = KegSmash:GetChargesRemaining() > 0 or KegSmash:GetNextChargeCooldown() >= 2.5
if CanKegSmash then
if (CurrentEnergy - KegSmashCost) >= TigerPalmCost then
return TigerPalm:Cast(Target)
end
else
return TigerPalm:Cast(Target)
end
end
end
if isAoe and SpinningCraneKick:IsKnownAndUsable() then
if not KegSmash:IsKnown() then
return SpinningCraneKick:Cast(Player)
else
local SpinningCraneKickCost = GetSpellPowerCost(SpinningCraneKick:GetID())[1]['cost']
local KegSmashCost = GetSpellPowerCost(KegSmash:GetID())[1]['cost']
local CurrentEnergy = Player:GetPower(Enum.PowerType.Energy)
local CanKegSmash = KegSmash:GetChargesRemaining() > 0 or KegSmash:GetNextChargeCooldown() >= 2.5
if CanKegSmash then
if (CurrentEnergy - KegSmashCost) >= SpinningCraneKickCost then
return SpinningCraneKick:Cast(Player)
end
else
return SpinningCraneKick:Cast(Player)
end
end
end
return false
end
---@return boolean
local function OutOfCombatRotation()
return false
end
local isRunning = false
BrewModule:Sync(function()
if not isRunning then
Bastion:Print('Brewmaster Started')
isRunning = true
end
if not Player:IsAlive() or Player:IsMounted() then
return false
end
if Player:IsAffectingCombat() or IsCurrentSpell(AutoAttack:GetID()) then
-- Combat Rotation
return CombatRotation()
else
-- Out Of Combat Rotation
return OutOfCombatRotation()
end
end)
Bastion:Register(BrewModule)

@ -0,0 +1,226 @@
local Tinkr, _Bastion = ...
---@type Bastion
local Bastion = _Bastion
local ProtModule = Bastion.Module:New('paladin_protection')
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
local None = Bastion.UnitManager:Get('none')
local CrusaderAura = Bastion.SpellBook:GetSpell(32223)
local DevoAura = Bastion.SpellBook:GetSpell(465)
local Consecrate = Bastion.SpellBook:GetSpell(26573)
local ConsecrateAura = Bastion.SpellBook:GetSpell(188370)
local Judgement = Bastion.SpellBook:GetSpell(275779)
local AvengersShield = Bastion.SpellBook:GetSpell(31935)
local OfDuskAndDawn = Bastion.SpellBook:GetSpell(385125)
local BlessingOfTheDusk = Bastion.SpellBook:GetSpell(385126)
local BlessingOfTheDawn = Bastion.SpellBook:GetSpell(385127)
local ShieldOfTheRighteous = Bastion.SpellBook:GetSpell(53600)
local BlessedHammer = Bastion.SpellBook:GetSpell(204019)
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local WoG = Bastion.SpellBook:GetSpell(85673)
local ShinningLight = Bastion.SpellBook:GetSpell(327510)
local HolyPower = Enum.PowerType.HolyPower
---@type Rebuke
local Rebuke = Tinkr:require("scripts/bastion/scripts/paladin/shared/rebuke", Bastion)
---@type HammerOfWrath
local HammerOfWrath = Tinkr:require("scripts/bastion/scripts/paladin/shared/hammer-of-wrath", Bastion)
local AvengersShieldTarget = Bastion.UnitManager:CreateCustomUnit('avengersshield', function()
local target = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then
return false
end
if not Player:CanSee(unit) then
return false
end
if not Player:IsFacing(unit) then
return false
end
if not AvengersShield:IsInRange(unit) then
return false
end
if unit:IsCastingOrChanneling() and unit:IsInterruptibleAt(5) then
target = unit
return true
end
return false
end)
if target == nil then
if Target:Exists() then
target = Target
else
target = None
end
end
return target
end)
local WoGTarget = Bastion.UnitManager:CreateCustomUnit('wordofglory', function()
if not WoG:IsKnownAndUsable() then return None end
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then
return false
end
if WoG:IsInRange(unit) then
return false
end
if not Player:CanSee(unit) then
return false
end
if not unit:GetHealthPercent() <= 55 then
return false
end
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
return false
end)
if not lowest then
lowest = None
end
return lowest
end)
---@return boolean
local function CombatRotation()
-- Attack the target is auto attack isnt active.
if AutoAttack:IsKnownAndUsable() and not IsCurrentSpell(AutoAttack:GetID()) and Player:InMelee(Target) and not Target:IsDead() then
AutoAttack:Cast(Target)
end
-- Kick
Rebuke()
-- Cast Consecrate if an enemy is in range, and the player isnt moving.
if Consecrate:IsKnownAndUsable() and not Player:IsMoving() and not Player:GetAuras():FindMy(ConsecrateAura):IsUp() and Player:GetEnemies(10) >= 1 then
return Consecrate:Cast(Player)
end
if WoG:IsKnownAndUsable() and Player:GetAuras():FindMy(ShinningLight):IsUp() and Player:GetHealthPercent() <= 50 and not Player:IsCastingOrChanneling() then
return WoG:Cast(Player)
end
-- Handle SoR specially if DuskAndDawn is known.
if OfDuskAndDawn:IsKnown() then
local HpToSor = 3
if Player:GetAuras():FindMy(BlessingOfTheDusk):IsUp() and Player:GetAuras():FindMy(BlessingOfTheDawn):GetRemainingTime() < 9 then
HpToSor = 5
end
if Player:GetPower(HolyPower) > 3 then
HpToSor = 5
end
if Player:GetPower(HolyPower) == HpToSor and ShieldOfTheRighteous:IsKnownAndUsable() and ShieldOfTheRighteous:IsInRange(Target) and not Player:IsCastingOrChanneling() then
ShieldOfTheRighteous:Cast(Target)
end
else
-- If Dusk And Dawn isnt known, rip SoR normally.
if ShieldOfTheRighteous:IsKnownAndUsable() and ShieldOfTheRighteous:IsInRange(Target) and not Player:IsCastingOrChanneling() then
ShieldOfTheRighteous:Cast(Target)
end
end
-- Avengers shield with higher prio than Judgement in AOE. Prefer targets that are casting.
if Player:GetEnemies(10) > 1 and AvengersShield:IsKnownAndUsable() and AvengersShieldTarget:Exists() then
return AvengersShield:Cast(AvengersShieldTarget)
end
-- Judgement
if Judgement:IsKnownAndUsable() and Player:GetPower(HolyPower) < 5 and Target:Exists() and Judgement:IsInRange(Target) then
return Judgement:Cast(Target)
end
-- Hammer of Wrath
if HammerOfWrath() then
return true
end
-- Avengers shield with lower prio than Judgement in ST. Prefer targets that are casting.
if Player:GetEnemies(10) == 1 and AvengersShield:IsKnownAndUsable() and AvengersShieldTarget:Exists() then
return AvengersShield:Cast(AvengersShieldTarget)
end
-- Blessed Hammer if there are more than 1 enemy in 10 yds.
if BlessedHammer:IsKnownAndUsable() and Player:GetPower(HolyPower) < 5 and Player:GetEnemies(10) >= 1 then
return BlessedHammer:Cast(Player)
end
if Player:GetAuras():FindMy(ShinningLight):IsUp() and WoGTarget:Exists() and not Player:IsCastingOrChanneling() then
return WoG:Cast(WoGTarget)
end
-- Refresh Consecrate if we've got nothing else to cast.
if Consecrate:IsKnownAndUsable() and Player:GetEnemies(10) >= 1 and not Player:IsMoving() then
return Consecrate:Cast(Player)
end
return false
end
---@return boolean
local function OutOfCombatRotation()
return false
end
local isRunning = false
ProtModule:Sync(function()
if not isRunning then
Bastion:Print('Prot Paladin Started')
isRunning = true
end
if not Player:IsAlive() then
return false
end
-- Cast Crusader Aura if talented and the player is mounted.
if Player:IsMounted() and CrusaderAura:IsKnownAndUsable() and not Player:GetAuras():FindMy(CrusaderAura):IsUp() and not Player:IsCastingOrChanneling() then
return CrusaderAura:Cast(Player)
end
-- Cast Devo Aura if the player is not mounted.
if not Player:IsMounted() and DevoAura:IsKnownAndUsable() and not Player:GetAuras():FindMy(DevoAura):IsUp() and not Player:IsCastingOrChanneling() then
return DevoAura:Cast(Player)
end
if Player:IsAffectingCombat() then
-- Combat Rotation
return CombatRotation()
else
-- Out Of Combat Rotation
return OutOfCombatRotation()
end
end)
Bastion:Register(ProtModule)

@ -0,0 +1,69 @@
local Tinkr, _Bastion = ...
---@type Bastion
local Bastion = _Bastion
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
local None = Bastion.UnitManager:Get('none')
local _HammerOfWrath = Bastion.SpellBook:GetSpell(24275)
local AvengingWrath = Bastion.SpellBook:GetSpell(389539)
local HammerOfWrathTarget = Bastion.UnitManager:CreateCustomUnit('hammerofwrath', function()
if not _HammerOfWrath:IsKnownAndUsable() then return None end
if Player:GetAuras():FindMy(AvengingWrath):IsUp() and _HammerOfWrath:IsKnownAndUsable() and Target:Exists() and Target:IsEnemy() and Target:IsAffectingCombat() and _HammerOfWrath:IsInRange(Target) then
return Target
end
local how = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then
return false
end
if not Player:CanSee(unit) then
return false
end
if not Player:IsFacing(unit) then
return false
end
if not _HammerOfWrath:IsInRange(unit) then
return false
end
if Player:GetAuras():FindMy(AvengingWrath):IsUp() then
how = unit
return true
end
if unit:GetHealthPercent() <= 20 then
how = unit
return true
end
return false
end)
if how == nil then
how = None
end
return how
end)
---@alias HammerOfWrath fun():boolean
---@type HammerOfWrath
function HammerOfWrath()
if HammerOfWrathTarget:Exists() and not Player:IsCastingOrChanneling() then
return _HammerOfWrath:Cast(HammerOfWrathTarget)
end
return false
end
return HammerOfWrath

@ -0,0 +1,57 @@
local Tinkr, _Bastion = ...
---@type Bastion
local Bastion = _Bastion
local Player = Bastion.UnitManager:Get('player')
local None = Bastion.UnitManager:Get('none')
local _Rebuke = Bastion.SpellBook:GetSpell(96231)
local RebukeTarget = Bastion.UnitManager:CreateCustomUnit('rebuke', function()
local kick = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then
return false
end
if not Player:CanSee(unit) then
return false
end
if Player:GetDistance(unit) > 40 then
return false
end
if unit:IsInterruptibleAt(10) and _Rebuke:IsInRange(unit) and Player:IsFacing(unit) then
kick = unit
return true
end
-- TODO: Only consider this if the player is in an M+ dungeon
-- if Player:InMelee(unit) and Player:IsFacing(unit) and Bastion.MythicPlusUtils:CastingCriticalKick(unit, 5) then
-- kick = unit
-- return true
-- end
return false
end)
if kick == nil then
kick = None
end
return kick
end)
---@alias Rebuke fun()
---@type Rebuke
function Rebuke()
if RebukeTarget:Exists() and not Player:IsCastingOrChanneling() and _Rebuke:IsKnownAndUsable() then
_Rebuke:Cast(RebukeTarget)
end
end
return Rebuke

@ -0,0 +1,54 @@
local
Tinkr,
---@type Bastion
Bastion = ...
local ArcaneModule = Bastion.Module:New('arcane')
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
local None = Bastion.UnitManager:Get('none')
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local CDsEnabled = false
local Command = Bastion.Command:New('arcane')
Command:Register('cooldowns', 'Toggle Arcane CDs', function()
CDsEnabled = not CDsEnabled
Bastion:Print('Arcane Cooldowns ' .. (CDsEnabled and 'enabled' or 'disabled'))
end)
---@return boolean
local function CombatRotation()
return false
end
---@return boolean
local function OutOfCombatRotation()
return false
end
local isRunning = false
ArcaneModule:Sync(function()
if not isRunning then
Bastion:Print('Arcane Started')
isRunning = true
end
if not Player:IsAlive() or Player:IsMounted() then
return false
end
if Player:IsAffectingCombat() or IsCurrentSpell(AutoAttack:GetID()) then
-- Combat Rotation
return CombatRotation()
else
-- Out Of Combat Rotation
return OutOfCombatRotation()
end
end)
Bastion:Register(ArcaneModule)

@ -0,0 +1,54 @@
local
Tinkr,
---@type Bastion
Bastion = ...
local FireModule = Bastion.Module:New('fire')
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
local None = Bastion.UnitManager:Get('none')
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local CDsEnabled = false
local Command = Bastion.Command:New('fire')
Command:Register('cooldowns', 'Toggle Fire CDs', function()
CDsEnabled = not CDsEnabled
Bastion:Print('Fire Cooldowns ' .. (CDsEnabled and 'enabled' or 'disabled'))
end)
---@return boolean
local function CombatRotation()
return false
end
---@return boolean
local function OutOfCombatRotation()
return false
end
local isRunning = false
FireModule:Sync(function()
if not isRunning then
Bastion:Print('Fire Started')
isRunning = true
end
if not Player:IsAlive() or Player:IsMounted() then
return false
end
if Player:IsAffectingCombat() or IsCurrentSpell(AutoAttack:GetID()) then
-- Combat Rotation
return CombatRotation()
else
-- Out Of Combat Rotation
return OutOfCombatRotation()
end
end)
Bastion:Register(FireModule)

@ -0,0 +1,59 @@
local
Tinkr,
---@type Bastion
Bastion = ...
local IcyVeins = Bastion.SpellBook:GetSpell(12472)
local FrozenOrb = Bastion.SpellBook:GetSpell(84714)
local Blizzard = Bastion.SpellBook:GetSpell(190356)
Bastion.ToggleManager:Add({
id = 'Frost_CdsEnabled',
icon = IcyVeins:GetIcon(),
state = false
})
Bastion.ToggleManager:Add({
id = 'Frost_FrozenOrbEnabled',
icon = FrozenOrb:GetIcon(),
state = false
})
Bastion.ToggleManager:Add({
id = 'Frost_AutoAOE',
icon = Blizzard:GetIcon(),
state = true
})
---@alias GetFrostOption fun(option: "CdsEnabled" | "FrozenOrbEnabled" | 'AutoAOE'): boolean
---@type GetFrostOption
local function GetFrostOption(option)
return Bastion.ToggleManager:GetState("Frost_" .. option)
end
local Command = Bastion.Command:New('frost')
Command:Register('aoe', 'Toggle bastion on/off', function()
Bastion.ToggleManager:Toggle("Frost_AutoAOE")
end)
Command:Register('cds', 'Toggle bastion on/off', function()
Bastion.ToggleManager:Toggle("Frost_CdsEnabled")
if GetFrostOption("FrozenOrbEnabled") and not GetFrostOption("CdsEnabled") then
Bastion.ToggleManager:Toggle("Frost_FrozenOrbEnabled")
elseif not GetFrostOption("FrozenOrbEnabled") and GetFrostOption("CdsEnabled") then
Bastion.ToggleManager:Toggle("Frost_FrozenOrbEnabled")
end
end)
Command:Register('orb', 'Toggle bastion on/off', function()
Bastion.ToggleManager:Toggle("Frost_FrozenOrbEnabled")
if not GetFrostOption("FrozenOrbEnabled") and GetFrostOption("CdsEnabled") then
Bastion.ToggleManager:Toggle("Frost_CdsEnabled")
end
end)
return GetFrostOption

@ -0,0 +1,206 @@
local
Tinkr,
---@type Bastion
Bastion = ...
local FrostModule = Bastion.Module:New('frost')
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
local None = Bastion.UnitManager:Get('none')
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local Frostbolt = Bastion.SpellBook:GetSpell(116)
local IceLance = Bastion.SpellBook:GetSpell(30455)
local Flurry = Bastion.SpellBook:GetSpell(44614)
local BrainFreeze = Bastion.SpellBook:GetSpell(190446)
local FingersOfFrost = Bastion.SpellBook:GetSpell(44544)
local WintersChill = Bastion.SpellBook:GetSpell(228358)
local FrostNova = Bastion.SpellBook:GetSpell(122)
local IceNova = Bastion.SpellBook:GetSpell(157997)
local IcyVeins = Bastion.SpellBook:GetSpell(12472)
local FrozenOrb = Bastion.SpellBook:GetSpell(84714)
local Blizzard = Bastion.SpellBook:GetSpell(190356)
local RuneOfPower = Bastion.SpellBook:GetSpell(116011)
---@type GetRangedTargetCount
local GetRangedTargetCount = Tinkr:require("scripts/bastion/scripts/shared/get-ranged-target-count", Bastion)
local EnableDrawROP = Tinkr:require("scripts/bastion/scripts/shared/draw-rop", Bastion)
---@type GetFrostOption
local GetFrostOption = Tinkr:require("scripts/bastion/scripts/frost/frost-gui", Bastion)
---@param unit Unit
local function isUnitFrozen(unit)
if not unit:Exists() then return false end
if unit:GetAuras():FindMy(IceNova):IsUp() or unit:GetAuras():FindMy(FrostNova):IsUp() then
return true
end
return false
end
local IceLanceTarget = Bastion.UnitManager:CreateCustomUnit('icelance', function()
if not IceLance:IsKnownAndUsable() then return None end
local icelance_target = nil
Bastion.UnitManager:EnumEnemies(function(unit)
if unit:IsDead() then
return false
end
if not Player:CanSee(unit) then
return false
end
if not IceLance:IsInRange(unit) then
return false
end
if not Player:IsFacing(unit) then
return false
end
if IceLance:IsInFlightToUnit(unit) then
return false
end
if isUnitFrozen(unit) then
icelance_target = unit
return true
end
return false
end)
if icelance_target == nil then
icelance_target = None
end
return icelance_target
end)
local FrozenOrbTarget = Bastion.UnitManager:CreateCustomUnit('frozen_orb', function()
local unit = Bastion.UnitManager:GetEnemyWithMostEnemies(40, 10)
if not unit then
if Target:Exists() and Target:GetDistance(Player) <= 40 then
unit = Target
else
unit = None
end
end
return unit
end)
local HasRotatedForOrb = false
FrozenOrb:OnCast(function()
HasRotatedForOrb = false
end)
---@return boolean
local function CombatRotation()
local targetCount = GetRangedTargetCount(40)
if GetFrostOption("CdsEnabled") and IcyVeins:IsKnownAndUsable() and not Player:IsMoving() and Target:Exists() then
return IcyVeins:Cast(Player)
end
if GetFrostOption("CdsEnabled") and RuneOfPower:IsKnownAndUsable() and not Player:GetAuras():FindMy(RuneOfPower):IsUp() and not Player:IsMoving() and Target:Exists() and IcyVeins:GetLastCastTime() ~= false and GetTime() - IcyVeins:GetLastCastTime() > 5 then
return RuneOfPower:Cast(Player)
end
if FrozenOrb:IsKnownAndUsable() and GetFrostOption("FrozenOrbEnabled") and FrozenOrbTarget:Exists() then
if not HasRotatedForOrb then
print("TURNING TO ORB TARGET")
FaceObject(FrozenOrbTarget:GetGUID())
SendMovementHeartbeat()
HasRotatedForOrb = true
else
return FrozenOrb:Cast(Player)
end
end
if IceLanceTarget:Exists() then
return IceLance:Cast(IceLanceTarget)
end
if Target:Exists() and Player:CanSee(Target) and IceLance:IsKnownAndUsable() and IceLance:IsInRange(Target) then
-- Winters Chill
if Target:GetAuras():FindMy(WintersChill):IsUp() then
local winters_chill_stacks = Target:GetAuras():FindMy(WintersChill):GetCount()
print("WC STACKS: ", winters_chill_stacks)
if winters_chill_stacks == 1 and not IceLance:IsInFlightToUnit(Target) then
return IceLance:Cast(Target)
end
if winters_chill_stacks > 1 then
return IceLance:Cast(Target)
end
end
-- Fingers of Frost
if Player:GetAuras():FindMy(FingersOfFrost):IsUp() then
local fof_stacks = Player:GetAuras():FindMy(FingersOfFrost):GetCount()
print("FOF STACKS: ", fof_stacks)
if fof_stacks == 1 and not IceLance:IsInFlightToUnit(Target) then
return IceLance:Cast(Target)
end
if fof_stacks > 1 then
return IceLance:Cast(Target)
end
end
end
if Target:Exists() and Player:CanSee(Target) and Flurry:IsKnownAndUsable() and Player:GetAuras():FindMy(BrainFreeze):IsUp() then
local CastingSpell = Player:GetCastingOrChannelingSpell()
if Player:IsMoving() or (CastingSpell ~= nil and CastingSpell:GetID() == Frostbolt:GetID()) then
return Flurry:Cast(Target)
end
end
if Target:Exists() and Player:CanSee(Target) and not Player:IsMoving() and Frostbolt:IsKnownAndUsable() and Frostbolt:IsInRange(Target) then
return Frostbolt:Cast(Target)
end
return false
end
---@return boolean
local function OutOfCombatRotation()
return false
end
local isRunning = false
FrostModule:Sync(function()
if not isRunning then
Bastion:Print('Frost Started')
isRunning = true
end
EnableDrawROP()
if not Player:IsAlive() or Player:IsMounted() then
return false
end
if Player:IsAffectingCombat() or IsCurrentSpell(AutoAttack:GetID()) then
-- Combat Rotation
return CombatRotation()
else
-- Out Of Combat Rotation
return OutOfCombatRotation()
end
end)
Bastion:Register(FrostModule)

@ -0,0 +1,45 @@
local
Tinkr,
---@type Bastion
Bastion = ...
local Player = Bastion.UnitManager:Get('player')
local RuneOfPower = Bastion.SpellBook:GetSpell(116011)
local Draw = Tinkr.Util.Draw:New()
Draw:Sync(function(draw)
for i, object in ipairs(Objects()) do
local name = ObjectName(object)
if name == RuneOfPower:GetName() then
local creator = ObjectCreator(object)
if tostring(creator) == tostring(Player:GetGUID()) then
local distance = ObjectDistance('player', object)
local ropX, ropY, ropZ = ObjectPosition(object)
draw:Circle(ropX, ropY, ropZ, 8)
draw:SetColor(69, 7, 58)
if distance > 8 then
draw:SetColor(252, 3, 207)
end
end
end
end
end)
local isEnabled = false
-- Draws a circle around the players Rune of Power to make it easier to stay within range.
local function EnableDrawROP()
if not isEnabled then
isEnabled = true
Draw:Enable()
end
end
return EnableDrawROP

@ -0,0 +1,25 @@
local
Tinkr,
---@type Bastion
Bastion = ...
local Player = Bastion.UnitManager:Get('player')
---@alias GetRangedTargetCount fun(range: number): number
---@type GetRangedTargetCount
local function GetRangedTargetCount(range)
local count = 0
Bastion.UnitManager:EnumEnemies(function(unit)
if not unit:IsDead() and unit:IsAffectingCombat() and Player:CanSee(unit) and Player:GetDistance(unit) <= range and Player:IsFacing(unit) then
count = count + 1
return false
end
return false
end)
return count
end
return GetRangedTargetCount

@ -422,4 +422,21 @@ function AuraTable:HasAnyDispelableAura(spell)
return false
end
-- Loop over a units auras
---@param cb fun(spellId: number): boolean | nil
---@return boolean
function AuraTable:ForEach(cb)
for _, auras in pairs(self:GetUnitAuras()) do
for _, aura in pairs(auras) do
if aura:IsUp() then -- Handle expired and non refreshed dropoffs not coming in UNIT_AURA
cb(aura.spellId)
else
self:RemoveInstanceID(aura:GetAuraInstanceID())
end
end
end
return false
end
return AuraTable

@ -0,0 +1,201 @@
local Tinkr, _Bastion = ...
---@class Bastion
local Bastion = _Bastion
---@alias BigWigsBars table<string, { spellId: number, duration: number, startTime: number, pauseTime: number | nil }>
---@class ContentUtils
local ContentUtils = {
---@type InterruptList
interruptList = {},
---@type TankBusterList
tankBusterList = {},
---@type ImportantAuraList
importantAuras = {},
---@type BigWigsBars
bars = {},
isBigWigsEnabled = false
}
ContentUtils.__index = ContentUtils
local function BWEventCallBack(event, ...)
if event == "BigWigs_StartBar" then
local addon, spellId, text, duration, icon, isCD = ...
local now = GetTime()
print('START BAR', text)
ContentUtils.bars[text] = ContentUtils.bars[text] or {}
ContentUtils.bars[text].duration = duration
ContentUtils.bars[text].startTime = now
ContentUtils.bars[text].spellId = spellId
elseif event == "BigWigs_StopBar" then
local addon, text = ...
print('STOP BAR', text)
if ContentUtils.bars[text] then
ContentUtils.bars[text] = nil
end
elseif event == "BigWigs_PauseBar" then
local addon, text = ...
print('PAUSE BAR', text)
if ContentUtils.bars[text] then
ContentUtils.bars[text].pauseTime = GetTime()
end
elseif event == "BigWigs_ResumeBar" then
local addon, text = ...
print('RESUME BAR', text)
if ContentUtils.bars[text] and ContentUtils.bars[text].pauseTime then
local pauseTime = ContentUtils.bars[text].pauseTime
local startTime = ContentUtils.bars[text].startTime
local duration = ContentUtils.bars[text].duration
local newDuration = duration - (pauseTime - startTime)
ContentUtils.bars[text].pauseTime = nil
ContentUtils.bars[text].duration = newDuration
ContentUtils.bars[text].startTime = GetTime()
end
elseif event == "BigWigs_StopBars" or event == "BigWigs_OnBossDisable" then
ContentUtils.bars = {}
end
end
---@type InterruptList
local _interruptList = Tinkr:require("scripts/bastion/ContentUtils/lists/interrupt-list", Bastion)
---@type TankBusterList
local _tankBusterList = Tinkr:require("scripts/bastion/ContentUtils/lists/tank-buster-list", Bastion)
---@type ImportantAuraList
local _importantAuraList = Tinkr:require("scripts/bastion/ContentUtils/lists/important-aura-list", Bastion)
---@return ContentUtils
function ContentUtils:New()
local self = setmetatable({}, ContentUtils)
self.interruptList = _interruptList
self.tankBusterList = _tankBusterList
self.importantAuras = _importantAuraList
if BigWigsLoader then
print("BigWigs Enabled!")
self.isBigWigsEnabled = true
BigWigsLoader.RegisterMessage({}, 'BigWigs_StartBar', BWEventCallBack)
BigWigsLoader.RegisterMessage({}, 'BigWigs_StopBar', BWEventCallBack)
BigWigsLoader.RegisterMessage({}, 'BigWigs_PauseBar', BWEventCallBack)
BigWigsLoader.RegisterMessage({}, 'BigWigs_ResumeBar', BWEventCallBack)
BigWigsLoader.RegisterMessage({}, 'BigWigs_StopBars', BWEventCallBack)
BigWigsLoader.RegisterMessage({}, 'BigWigs_OnBossDisable', BWEventCallBack)
end
return self
end
---@param unit Unit
---@param percent number
---@return Interrupt | nil
function ContentUtils:CastingCriticalStop(unit, percent)
local castingSpell = unit:GetCastingOrChannelingSpell()
local npcSpells = self.interruptList[unit:GetID()]
if npcSpells and castingSpell then
local spellID = castingSpell:GetID()
local spellTraits = npcSpells[spellID]
if not spellTraits then
return nil
end
local castPercent = unit:GetChannelOrCastPercentComplete()
if castPercent >= percent then
return spellTraits
end
end
return nil
end
function ContentUtils:RefreshBars()
if self.isBigWigsEnabled then
for key, value in pairs(self.bars) do
local expireTime = value.duration + value.startTime
if expireTime < GetTime() then
self.bars[key] = nil
end
end
end
end
---@param spellId number
---@param timeUntilCast number
---@return boolean
function ContentUtils:IsSpellCastSoon(spellId, timeUntilCast)
if self.isBigWigsEnabled then
self:RefreshBars()
for key, value in pairs(self.bars) do
if value.spellId == spellId and not value.pauseTime then
local expireTime = value.duration + value.startTime
local timeUntilSpell = expireTime - GetTime()
if timeUntilSpell > 0 and timeUntilSpell <= timeUntilCast then
return true
end
end
end
end
return false
end
---@param unit Unit
---@param timeUntilCast? number
---@return TankBuster | nil
function ContentUtils:CastingTankBuster(unit, timeUntilCast)
local npcSpells = self.tankBusterList[unit:GetID()]
if npcSpells then
-- If timeUntilCast is provided, check if any of this NPC
if timeUntilCast then
for key, value in pairs(self.bars) do
local spellTraits = npcSpells[value.spellId]
if spellTraits and not value.pauseTime and ((value.startTime + value.duration) - GetTime()) <= timeUntilCast then
return spellTraits
end
end
end
local castingSpell = unit:GetCastingOrChannelingSpell()
if castingSpell then
if npcSpells[castingSpell:GetID()] then return npcSpells[castingSpell:GetID()] end
end
end
return nil
end
---@param friend Unit
---@return ImportantAura | nil
function ContentUtils:HasImportantAura(friend)
local traits = nil
friend:GetAuras():ForEach(function(spellId)
local auraTraits = self.importantAuras[spellId]
if auraTraits then traits = auraTraits end
end)
return traits
end
return ContentUtils

@ -0,0 +1,193 @@
---@alias ImportantAura { isMagicDispel: boolean | nil, isPoisonDispel: boolean | nil, isDiseaseDispel: boolean | nil, isDiffusable: boolean | nil }
---@alias ImportantAuraList table<number, ImportantAura>
---@type ImportantAuraList
local _ImportantAuraList = {
-- Primal Chill
[372682] = {
isMagicDispel = true
},
-- Primal Chill
[373589] = {
isMagicDispel = true
},
-- Rolling Thunder
[392641] = {
isMagicDispel = true
},
-- Burning Touch
[373869] = {
isMagicDispel = true,
isDiffusable = true
},
-- Shock Blast
[392924] = {
isMagicDispel = true,
isDiffusable = true
},
-- Storm Slam
[381515] = {
isMagicDispel = true
},
-- Thunder Clap
[386028] = {
isMagicDispel = true
},
-- Tempest
[386025] = {
isMagicDispel = true,
isDiffusable = true
},
-- Frightful Roar
[386063] = {
isMagicDispel = true
},
-- Bloodcurdling Shout
[373395] = {
isMagicDispel = true
},
-- Conductive Strike
[376827] = {
isMagicDispel = true
},
-- Storm Shock
[381530] = {
isMagicDispel = true
},
-- Mystic Vapors
[387564] = {
isMagicDispel = true,
isDiffusable = true
},
-- Erratic Growth
[375602] = {
isMagicDispel = true
},
-- Crystaline Rupture
[370766] = {
isMagicDispel = true
},
-- Waking Bane
[386546] = {
isMagicDispel = true
},
-- Waking Bane
[386549] = {
isMagicDispel = true
},
-- Forbiden Knowledge
[371352] = {
isMagicDispel = true
},
-- Icy Bindings
[377488] = {
isMagicDispel = true
},
-- Absolute Zero
[396722] = {
isMagicDispel = true
},
-- Dragon Strike
[384978] = {
isMagicDispel = true,
isDiffusable = true
},
-- Monotonous Lecture
[388392] = {
isMagicDispel = true
},
-- Thunderstrike
[215429] = {
isMagicDispel = true
},
-- Seal Magic
[209404] = {
isMagicDispel = true
},
-- Mana Fang
[209516] = {
isMagicDispel = true,
isDiffusable = true
},
-- Suppress
[209413] = {
isMagicDispel = true,
},
-- Bewitch
[211470] = {
isMagicDispel = true,
isDiffusable = true
},
-- Impending Doom
[397907] = {
isMagicDispel = true
},
-- Withering Soul
[208165] = {
isMagicDispel = true
},
-- Criple
[214690] = {
isMagicDispel = true
},
-- Carrion Swarm
[214688] = {
isMagicDispel = true
},
-- Shadow Word: Frailty
[152819] = {
isMagicDispel = true,
isDiffusable = true
},
-- Tainted Ripple
[397878] = {
isMagicDispel = true
},
-- Haunting Scream
[395859] = {
isMagicDispel = true,
},
-- Throw Torch
[114803] = {
isMagicDispel = true,
isDiffusable = true
},
-- Sleepy Soliloquy
[395872] = {
isMagicDispel = true,
},
-- Serpent Strike
[106823] = {
isMagicDispel = true,
},
-- Necrotic Burst
[156718] = {
isDiseaseDispel = true
},
-- Plague Spit
[153524] = {
isDiseaseDispel = true
},
-- Death Venom
[156717] = {
isPoisonDispel = true
},
-- Lasher Toxin
[389033] = {
isPoisonDispel = true
},
-- Touch of Nothingness
[106113] = {
isMagicDispel = true,
},
-- Arcane Lockdown
[207278] = {
isDiffusable = true
},
-- Hinder
[215204] = {
isDiffusable = true
}
}
return _ImportantAuraList

@ -0,0 +1,662 @@
---@alias Interrupt { kickable: boolean, sweepable: boolean, paraliseable: boolean }
---@alias InterruptList table<number, table<number, Interrupt>>
---@type InterruptList
local _InterruptList = {
-- kick, stun, disorient, incap
--
-- HoV
--
-- Thunder Caller
[95842] = {
-- Thunderous Blast
[198585] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Mystic
[95834] = {
-- Holy Radiance
[215433] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Runcarver
[96664] = {
-- Etch
[198959] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Marksman
[96640] = {
-- Penetrating Shot
[199210] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Shieldmaiden
[95832] = {
-- Mortal Hew
[199050] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Shieldmaiden 2
[101639] = {
-- Mortal Hew
[199050] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Odyn Add
[102019] = {
-- Surge
[198750] = {
kickable = true, sweepable = false, paraliseable = false
}
},
--
-- Court of Stars
--
-- Duskwatch Guard
[111563] = {
-- Fortification
[209033] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Duskwatch Guard 2
[104246] = {
-- Fortification
[209033] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Sentry
[104251] = {
-- Sound the Alarm
[210261] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Construct
[104270] = {
-- Suppress
[209413] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Charging Station
[225100] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Bound Energy
[105705] = {
-- Charged Blast
[212031] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Arcane Manifestation
[105704] = {
-- Drain Magic
[209485] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Watchful Inquisitor
[105715] = {
-- Searing Glare
[211299] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Eye Storm
[212784] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Blazing Imp
[104295] = {
-- Drifting Embers
[211401] = {
kickable = true, sweepable = true, paraliseable = false
},
-- Drifting Embers 2
[211406] = {
kickable = true, sweepable = true, paraliseable = false
},
-- Fireball
[211412] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Shadow Mistress
[104300] = {
-- Bewitch
[211470] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Balgarr
[104274] = {
-- Disintegration Beam
[207980] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Disintigration Beam 2
[207981] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Impending Doom
[397907] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Impending Doom 2
[397908] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Talixae Flamewreath
[104217] = {
-- Withering Soul
[208165] = {
kickable = true, sweepable = false, paraliseable = false
}
},
--
-- RLP
--
-- Earthshaper
[187969] = {
-- Tectonic Slam
[372735] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Chillweaver
[188067] = {
-- Ice Shield
[384933] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Ice Shield 2
[372749] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Ice Shield 3
[372743] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Meledrusa
[188252] = {
-- Frost Overload
[373680] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Frost Overload 2
[373688] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Cinderweaver
[190207] = {
-- Cinderbolt 1
[384194] = {
kickable = true, sweepable = false, paraliseable = false
},
-- Cinderbolt 2
[384197] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Flame Dancer
[190206] = {
-- Flame Dance
[385536] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Blazebound Firestorm
[189886] = {
-- Roaring Blaze
[373017] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Tempest Channeler
[198047] = {
-- Thunder Bolt
[392576] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Flame Channeler
[197985] = {
-- Flashfire
[392451] = {
kickable = true, sweepable = true, paraliseable = false
},
-- Flashfire
[392452] = {
kickable = true, sweepable = true, paraliseable = false
},
},
--
-- NO
--
-- Hornsounder
[192796] = {
-- Rally The Clan
[383823] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Plainstomper
[191847] = {
-- Disruptive Shout
[384365] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Lancemaster
[192800] = {
-- Disruptive Shout
[384365] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Arcblade
[194898] = {
-- Arcing Strike
[387135] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Stormcaller Botoo
[194317] = {
-- Stormbolt
[386012] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Thunderbeast
[195696] = {
-- Chain Lightning
[387127] = {
kickable = true, sweepable = false, paraliseable = false
}
},
[194894] = {
-- Tempest
[386025] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Tempest 2
[386024] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Summon Squall
[386015] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Stormbolt
[386012] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Squall
[194895] = {
-- Surge
[386026] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Stormcaller Arynga
[195265] = {
-- Stormbolt
[386012] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Nokhud Neophyte
[196263] = {
-- Stormbolt
[386012] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Stormcaller Solongo
[194315] = {
-- Stormbolt
[386012] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Stormcaller Zarii
[194316] = {
-- Stormbolt
[386012] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Soulharvester Galtmaa
[195927] = {
-- Death Bolt Volley
[387411] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Soulharvester Tumen
[195929] = {
-- Death Bolt Volley
[387411] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Soulharvester Mandakh
[195930] = {
-- Death Bolt Volley
[387411] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Soulharvester Duuren
[195928] = {
-- Death Bolt Volley
[387411] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Beastcaller
[195878] = {
-- Desecrated Roar
[387440] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Heavy Slash
[387826] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Mystic
[195877] = {
-- Swift Wind
[387596] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Risen Warrior
[195855] = {
-- Mortal Strike
[388801] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Teera
[186339] = {
-- Guardian Wind
[384808] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Batak
[193462] = {
-- Bloodcurdling Shout
[373395] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Balakar Add
[190294] = {
-- Storm Bolt
[376725] = {
kickable = true, sweepable = false, paraliseable = false
}
},
--
-- AV
--
-- Lasher
[196102] = {
-- Mystic Vapors
[387564] = {
kickable = false, sweepable = true, paraliseable = true
}
},
--Arcane Tender
[196115] = {
-- Erratic Growth
[375596] = {
kickable = true, sweepable = false, paraliseable = false
}
},
--Arcane Tender 2
[191164] = {
-- Erratic Growth
[375596] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Crystal Fury
[187160] = {
-- Shards
[370764] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Arcane Elemental
[186741] = {
-- Waking Bane
[386546] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Curator
[187154] = {
-- Heavy Tome
[389804] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Rune Seal Keeper
[187155] = {
-- Icy Bindings
[377488] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Crystal Fury
[196116] = {
-- Shards
[370764] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Vault Guard
[190510] = {
-- Ice Cutter
[377105] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Frog
[187246] = {
-- Null Stomp
[386526] = {
kickable = false, sweepable = true, paraliseable = false
}
},
--
-- AA
--
-- Scepter
[196576] = {
-- Mystic Blast
[396812] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Arcane Rain
[388886] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Textbook
[196044] = {
-- Lecture
[388392] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Eagle
[192333] = {
-- Call The Flock
[377389] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Echoknight
[196200] = {
-- Whirlwind
[387910] = {
kickable = false, sweepable = true, paraliseable = true
}
},
-- Invoker
[196202] = {
-- Arcane Missiles
[387975] = {
kickable = true, sweepable = true, paraliseable = true
}
},
--
-- SMBG
--
-- Bone Mender
[75713] = {
-- Shadow Mend
[152818] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Ritual Bones
[75715] = {
-- Void Slash
[164907] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Defiled Spirit
[75451] = {
-- Cry of Anguish
[398154] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Dominator
[76446] = {
-- Voidlash
[156776] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Spider
[76104] = {
-- Necrotic Burst
[156718] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Bat
[75459] = {
-- Plague Spit
[153524] = {
kickable = true, sweepable = true, paraliseable = true
}
},
--
-- TJS
--
-- Waterspeaker
[200126] = {
-- Hydrolance
[397888] = {
kickable = true, sweepable = true, paraliseable = true
},
-- Tidal Burst
[397889] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Wise Mari
[56448] = {
-- Hydrolance
[397801] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Haunting Sha
[59555] = {
-- Gaze
[114646] = {
kickable = false, sweepable = true, paraliseable = true
},
-- Scream
[398859] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Fish
[59546] = {
-- Sleepy
[395872] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Hozen
[59552] = {
-- Fit of Rage
[396018] = {
kickable = true, sweepable = false, paraliseable = false
}
},
-- Golden Beetle
[59545] = {
-- Staggering Blow
[396019] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Tiger
[59544] = {
-- Cat Nap
[396073] = {
kickable = true, sweepable = true, paraliseable = false
}
},
-- Mistweaver
[200137] = {
-- Defilling Mist
[397914] = {
kickable = true, sweepable = true, paraliseable = true
}
},
-- Guardian
[200131] = {
-- Leg Sweep
[397899] = {
kickable = false, sweepable = true, paraliseable = false
}
},
-- Minion of Doubt
[57109] = {
-- Dark Claw
[397931] = {
kickable = false, sweepable = true, paraliseable = false
}
}
}
return _InterruptList

@ -0,0 +1,416 @@
---@alias TankBuster { shouldZen: boolean | nil, shouldDampen: boolean | nil, shouldDiffuse: boolean | nil, shouldFort: boolean | nil, shouldBubble: boolean | nil, shouldPurify: boolean | nil }
---@alias TankBusterList table<number, table<number, TankBuster>>
---@type TankBusterList
local _TankBusterList = {
--
-- RLP
--
-- Primal Juggernaut
[188244] = {
-- Crushing Smash
[372730] = {
shouldDampen = true,
shouldPurify = true
}
},
-- Defier Draghar
[187897] = {
-- Steel Barrage
[372047] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- Thunderhead
[197698] = {
-- Thunder Jaw
[392395] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldPurify = true,
shouldZen = true
}
},
-- Flamegullet
[197697] = {
-- Fire Maw
[392394] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldPurify = true,
shouldZen = true
}
},
-- Kokia
[189232] = {
-- Searing Blows
[372858] = {
shouldBubble = true,
shouldDampen = true,
shouldFort = true,
shouldPurify = true,
shouldZen = true
},
},
-- Erkhart
[190485] = {
-- Stormslam
[381512] = {
shouldPurify = true,
shouldZen = true,
shouldDampen = true,
shouldDiffuse = true,
shouldFort = true
}
},
--
-- NO
--
-- Raging Tempest
[186615] = {
-- Energy Surge
[384686] = {
shouldDampen = true,
shouldPurify = true,
shouldFort = true
}
},
-- Beastcaller
[195878] = {
-- Heavy Slash
[387826] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- Maruuk
[186338] = {
-- Brutalize
[382836] = {
shouldDampen = true,
shouldZen = true,
shouldPurify = true
}
},
-- Balakar
[186151] = {
-- Rending Strike
[375937] = {
shouldDampen = true,
shouldZen = true,
shouldPurify = true
}
},
--
-- AV
--
-- Leymore
[186644] = {
-- Infused Strike
[374789] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldZen = true,
shouldPurify = true
}
},
-- Vault Guard
[190510] = {
-- Ice Cutter
[377105] = {
shouldPurify = true,
shouldDampen = true,
shouldDiffuse = true,
}
},
-- Scalebane
[191739] = {
-- Ice Cutter
[377105] = {
shouldPurify = true,
shouldDampen = true,
shouldDiffuse = true,
},
-- Spellfrost Breath
[391120] = {
shouldPurify = true,
shouldZen = true,
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
}
},
-- Azureblade
[186739] = {
-- Arcane Cleave
[372222] = {
shouldZen = true,
shouldPurify = true,
shouldDampen = true,
shouldDiffuse = true,
}
},
-- Umbreskul
[186738] = {
-- Dragon Strike
[384978] = {
shouldZen = true,
shouldPurify = true,
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldFort = true
}
},
--
-- AA
--
-- Battleaxe
[196577] = {
-- Severing Slash
[388911] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- Crawth
[191736] = {
-- Savage Peck
[376997] = {
shouldBubble = true,
shouldDampen = true,
shouldZen = true,
shouldPurify = true,
shouldFort = true
}
},
-- Overgrown Ancient
[196482] = {
-- Barkbreaker
[388544] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true,
shouldFort = true
}
},
--
-- HOV
--
-- Storm Drake
[97068] = {
-- Lightning Breath
[198888] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldPurify = true
}
},
-- Hymdall
[94960] = {
-- Bloodletting Sweep
[193092] = {
shouldBubble = true,
shouldDampen = true,
shouldZen = true,
shouldPurify = true,
shouldFort = true
}
},
-- Hyrja
[95833] = {
[192018] = {
shouldZen = true,
shouldPurify = true,
shouldDampen = true,
shouldDiffuse = true,
shouldFort = true
}
},
-- Storm Drake
[99891] = {
-- Lightning Breath
[198888] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldPurify = true,
}
},
-- Angerhoof Bull
[96611] = {
-- Piercing Horns
[199151] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- Steeljaw Grizzly
[96677] = {
-- Rending Claws
[185425] = {
shouldBubble = true,
shouldDampen = true,
shouldFort = true,
shouldPurify = true
}
},
-- King Ranulf
[97083] = {
-- Sever
[199652] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- King Haldor
[95843] = {
-- Sever
[199652] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- King Bjorn
[97081] = {
-- Sever
[199652] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- King Tor
[97084] = {
-- Sever
[199652] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- God-King Skovald
[95675] = {
-- Savaga Slash
[193668] = {
shouldBubble = true,
shouldDampen = true,
shouldZen = true,
shouldPurify = true
}
},
--
-- CoS
--
-- Sentry
[104270] = {
-- Charged Smash
[209495] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true,
}
},
-- Gereth the Vile
[108151] = {
-- Vampiric Claws
[373364] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldZen = true,
shouldPurify = true
}
},
--
-- SMBG
--
-- Reanimated Ritual Bones
[75715] = {
-- Shadow Slash
[75715] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldFort = true,
shouldPurify = true
}
},
--
-- TJS
--
-- Haunting Sha
[59555] = {
-- Haunting Gaze
[114646] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldFort = true,
shouldPurify = true
}
},
-- Songbird
[59553] = {
-- Vicious Peck
[396007] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = false,
shouldPurify = true
}
},
-- The Golden Beetle
[59545] = {
[396019] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- Sha-Touched Guardian
[200131] = {
-- Setting Sun Kick
[397094] = {
shouldBubble = true,
shouldDampen = true,
shouldPurify = true
}
},
-- Minion of Doubt
[57109] = {
-- Dark Claw
[397931] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldPurify = true
}
},
-- Liu Flameheart
[56732] = {
-- Jade Serpent Strike
[106841] = {
shouldBubble = true,
shouldDampen = true,
shouldDiffuse = true,
shouldFort = true,
shouldPurify = true,
shouldZen = true
}
}
}
return _TankBusterList

@ -1,514 +0,0 @@
local Tinkr, Bastion = ...
---@class MythicPlusUtils
local MythicPlusUtils = {
debuffLogging = false,
castLogging = false,
random = '',
loggedCasts = {},
loggedDebuffs = {},
kickList = {},
}
MythicPlusUtils.__index = MythicPlusUtils
---@return MythicPlusUtils
function MythicPlusUtils:New()
local self = setmetatable({}, MythicPlusUtils)
self.random = math.random(1000000, 9999999)
self.kickList = {
-- Ruby life pools
[372735] = { -- Techtonic Slam
[187969] = {
false, true, true -- Kick, Stun, Disorient
}
},
[384933] = { -- Ice Shield
[188067] = {
true, true, true
}
},
[372749] = { -- Ice Shield
[188067] = {
true, true, true
}
},
[372743] = { -- Ice Shield
[188067] = {
true, true, true
}
},
[371984] = {
[188067] = {
true, true, true
}
},
[373680] = {
[188252] = {
true, false, false
}
},
[373688] = {
[188252] = {
true, false, false
}
},
[385310] = {
[195119] = {
true, false, false
}
},
[384194] = {
[190207] = {
true, true, true
}
},
[384197] = {
[190207] = {
true, true, true
}
},
[373017] = {
[189886] = {
true, false, false
}
},
[392576] = {
[198047] = {
true, false, false
}
},
[392451] = {
[197985] = {
true, true, false,
}
},
[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)
if not self.debuffLogging then
return
end
if auras.addedAuras then
local addedAuras = auras.addedAuras
if #addedAuras > 0 then
for i = 1, #addedAuras do
local aura = Bastion.Aura:CreateFromUnitAuraInfo(addedAuras[i])
if not self.loggedDebuffs[aura:GetSpell():GetID()] and not aura:IsBuff() then
WriteFile('bastion-MPlusDebuffs-' .. self.random .. '.lua', [[
AuraName: ]] .. aura:GetName() .. [[
AuraID: ]] .. aura:GetSpell():GetID() .. "\n" .. [[
]], true)
end
end
end
end
end)
Bastion.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_START', function(unitTarget, castGUID, spellID)
if not self.castLogging then
return
end
if self.loggedCasts[spellID] then
return
end
local name = GetSpellInfo(spellID)
self.loggedCasts[spellID] = true
WriteFile('bastion-MPlusCasts-' .. self.random .. '.lua', [[
CastName: ]] .. name .. [[
CastID: ]] .. spellID .. "\n" .. [[
]], true)
end)
Bastion.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_CHANNEL_START', function(unitTarget, castGUID, spellID)
if not self.castLogging then
return
end
if self.loggedCasts[spellID] then
return
end
local name = GetSpellInfo(spellID)
self.loggedCasts[spellID] = true
WriteFile('bastion-MPlusCasts-' .. self.random .. '.lua', [[
CastName: ]] .. name .. [[
CastID: ]] .. spellID .. "\n" .. [[
]], true)
end)
return self
end
---@return nil
function MythicPlusUtils:ToggleDebuffLogging()
self.debuffLogging = not self.debuffLogging
end
---@return nil
function MythicPlusUtils:ToggleCastLogging()
self.castLogging = not self.castLogging
end
---@param unit Unit
---@param percent number
---@return boolean
function MythicPlusUtils:CastingCriticalKick(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 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
end
end
return false
end
return MythicPlusUtils

@ -88,7 +88,6 @@ function ObjectManager:Refresh()
self.friends:push(unit)
elseif unit:IsEnemy() then
self.enemies:push(unit)
if unit:InCombatOdds() > 80 then
self.activeEnemies:push(unit)
end

@ -128,7 +128,7 @@ end
-- Cast the spell
---@param unit Unit
---@param condition string
---@param condition? string
---@return boolean
function Spell:Cast(unit, condition)
if condition and not self:EvaluateCondition(condition) then
@ -396,6 +396,18 @@ function Spell:GetChargesRemaining()
return charges
end
-- Get a spells charge cooldown if applicable
---@return number | nil
function Spell:GetNextChargeCooldown()
local charges, maxCharges, start, duration = GetSpellCharges(self:GetID())
if not maxCharges then return nil end
local nextChargeTime = start + duration
return nextChargeTime - GetTime()
end
-- Create a condition for the spell
---@param name string
---@param func fun(self:Spell):boolean
@ -496,4 +508,37 @@ function Spell:IsSpell(spell)
return self:GetID() == spell:GetID()
end
---@alias Missile table<{ spellId: number, target: string, source: number, my: number, mx: number, mz: number, px: number, py: number, pz: number, uy: number, ux: number, uz: number, hx: number, hy: number, hz: number, iy: number, ix: number, iz: number, px: number, py: number, pz: number }>
-- IsInFlight
---@return Missile | nil
function Spell:Missile()
local inFlight = nil
local missiles = Missiles()
for i, missile in ipairs(missiles) do
if missile.spellId == self:GetID() then
inFlight = missile
end
end
return inFlight
end
---@param unit Unit
---@return boolean
function Spell:IsInFlightToUnit(unit)
local missile = self:Missile()
local isInFlight = false
if missile then
if tostring(missile.target) == tostring(unit:GetGUID()) then
isInFlight = true
end
end
return isInFlight
end
return Spell

@ -0,0 +1,104 @@
local
Tinkr,
---@type Bastion
Bastion = ...
---@alias Toggle { x: number, icon: number, state: boolean, id: string, texture?: Texture }
local BUTTON_SIZE = 54
---@class ToggleManager
local ToggleManager = {
---@type table<string, Toggle>
toggles = {}
}
---@return number
function ToggleManager:NumActiveToggles()
local count = 0
for i, toggle in pairs(self.toggles) do
count = count + 1
end
return count
end
---@param newToggle { icon: number, state: boolean, id: string }
function ToggleManager:Add(newToggle)
if self.toggles[newToggle.id] then
self.toggles[newToggle.id].icon = newToggle.icon
self.toggles[newToggle.id].state = newToggle.state
else
local highestX = nil
for i, toggle in pairs(self.toggles) do
if highestX == nil or toggle.x > highestX then
highestX = toggle.x
end
end
if highestX == nil then
highestX = 2
else
highestX = highestX + BUTTON_SIZE
end
self.toggles[newToggle.id] = {
x = highestX,
state = newToggle.state,
icon = newToggle.icon,
id = newToggle.id
}
end
end
function ToggleManager:Refresh()
for i, toggle in pairs(self.toggles) do
if not toggle.texture then
local frame = CreateFrame("Frame", nil, UIParent)
frame:SetSize(BUTTON_SIZE, BUTTON_SIZE)
frame:SetPoint("BOTTOMLEFT", toggle.x, 230)
local Texture = frame:CreateTexture()
Texture:SetAllPoints(frame)
Texture:SetTexture(toggle.icon)
if not toggle.state then
Texture:SetDesaturated(true)
end
self.toggles[toggle.id].texture = Texture
end
-- else
-- local desaturation = toggle.texture:GetDesaturation()
-- if not toggle.state and desaturation == 0 then
-- toggle.texture:SetDesaturated(true)
-- elseif toggle.state and desaturation == 1 then
-- toggle.texture:SetDesaturated(false)
-- end
-- end
end
end
---@param id string
function ToggleManager:Toggle(id)
if self.toggles[id] then
self.toggles[id].state = not self.toggles[id].state
local desaturation = self.toggles[id].texture:GetDesaturation()
if not self.toggles[id].state and desaturation == 0 then
self.toggles[id].texture:SetDesaturated(true)
elseif self.toggles[id].state and desaturation == 1 then
self.toggles[id].texture:SetDesaturated(false)
end
end
end
---@param id string
---@return boolean
function ToggleManager:GetState(id)
if self.toggles[id] then return self.toggles[id].state else return false end
end
return ToggleManager

@ -452,7 +452,7 @@ end
-- Check if unit is interruptible
---@param percent number
---@param ignoreInterruptible boolean
---@param ignoreInterruptible? boolean
---@return boolean
function Unit:IsInterruptibleAt(percent, ignoreInterruptible)
if not ignoreInterruptible and not self:IsInterruptible() then
@ -588,12 +588,17 @@ end
-- IsFacing
---@param unit Unit
---@param fov? number
---@return boolean
function Unit:IsFacing(unit)
function Unit:IsFacing(unit, fov)
local rot = ObjectRotation(self:GetOMToken())
local x, y, z = ObjectPosition(self:GetOMToken())
local x2, y2, z2 = ObjectPosition(unit:GetOMToken())
local fovToUse = 90
if fov then fovToUse = fov end
if not x or not x2 or not rot then
return false
end

@ -310,4 +310,76 @@ function UnitManager:FindFriendsCentroid(radius, range)
return centroid
end
-- Get the enemy with the most enemies within a given radius
---@param distanceFromPlayer number
---@param radius number
---@return Unit
---@return table
function UnitManager:GetEnemyWithMostEnemies(distanceFromPlayer, radius)
local unit = nil
local count = 0
local enemies = {}
self:EnumEnemies(function(u)
if u:IsAlive() and u:IsAffectingCombat() and u:GetDistance(self:Get("player")) <= distanceFromPlayer then
local c = 0
self:EnumEnemies(function(other)
if other:IsAlive() and other:IsAffectingCombat() and u:GetDistance(other) <= radius then
c = c + 1
end
end)
if c > count then
unit = u
count = c
enemies = {}
self:EnumEnemies(function(other)
if other:IsAlive() and other:IsAffectingCombat() and u:GetDistance(other) <= radius then
table.insert(enemies, other)
end
end)
end
end
end)
return unit, enemies
end
-- Find the centroid of the most dense area of friends (party/raid members) of a given radius within a given range
---@param distanceFromPlayer number
---@param radius number
---@return Vector3 | nil
function UnitManager:FindEnemiesCentroid(distanceFromPlayer, radius)
local unit, enemies = self:GetEnemyWithMostEnemies(distanceFromPlayer, radius)
if unit == nil then
return nil
end
local centroid = Bastion.Vector3:New(0, 0, 0)
local zstart = -math.huge
for i = 1, #enemies do
local p = enemies[i]:GetPosition()
centroid = centroid + p
zstart = p.z > zstart and p.z or zstart
end
centroid = centroid / #enemies
if unit:GetPosition():Distance(centroid) > distanceFromPlayer then
return unit:GetPosition()
end
local _, _, z = TraceLine(
centroid.x,
centroid.y,
centroid.z + 5,
centroid.x,
centroid.y,
centroid.z - 5,
0x100151
)
centroid.z = z + 0.01
return centroid
end
return UnitManager

@ -10,6 +10,8 @@ function Bastion.require(class)
return Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
end
---@type ToggleManager
Bastion.ToggleManager = Bastion.require("ToggleManager")
---@type ClassMagic
Bastion.ClassMagic = Bastion.require("ClassMagic")
---@type List
@ -34,6 +36,7 @@ Bastion.Unit = Bastion.require("Unit")
Bastion.Aura = Bastion.require("Aura")
---@type APL, APLActor, APLTrait
Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL")
---@type Module
Bastion.Module = Bastion.require("Module")
---@type UnitManager
Bastion.UnitManager = Bastion.require("UnitManager"):New()
@ -57,12 +60,14 @@ Bastion.Class = Bastion.require("Class")
Bastion.Timer = Bastion.require("Timer")
---@type Timer
Bastion.CombatTimer = Bastion.Timer:New('combat')
---@type MythicPlusUtils
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type ContentUtils
Bastion.ContentUtils = Bastion.require("ContentUtils"):New()
---@type NotificationsList
Bastion.Notifications = Bastion.NotificationsList:New()
Bastion.modules = {}
---@type table<string, { duration: number, requestTime: number }>
Bastion.pausedModules = {}
Bastion.Enabled = false
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
@ -88,23 +93,12 @@ Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
end)
local pguid = UnitGUID("player")
local missed = {}
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
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 u2 = Bastion.UnitManager[destGUID]
@ -117,20 +111,6 @@ Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
if u2 then
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)
@ -141,12 +121,23 @@ Bastion.Ticker = C_Timer.NewTicker(0.1, function()
Bastion.CombatTimer:Reset()
end
for k, v in pairs(Bastion.pausedModules) do
if (v.duration + v.requestTime) < GetTime() then
Bastion:Print('Module ', k, ' unpaused.')
Bastion.pausedModules[k] = nil
end
end
Bastion.ToggleManager:Refresh()
if Bastion.Enabled then
Bastion.ObjectManager:Refresh()
for i = 1, #Bastion.modules do
if not Bastion.pausedModules[Bastion.modules[i].name] then
Bastion.modules[i]:Tick()
end
end
end
end)
function Bastion:Register(module)
@ -240,37 +231,32 @@ Command:Register('module', 'Toggle a module on/off', function(args)
end
end)
Command:Register('mplus', 'Toggle m+ module on/off', function(args)
local cmd = args[2]
if cmd == 'debuffs' then
Bastion.MythicPlusUtils:ToggleDebuffLogging()
Bastion:Print("Debuff logging", Bastion.MythicPlusUtils.debuffLogging and "enabled" or "disabled")
return
end
if cmd == 'casts' then
Bastion.MythicPlusUtils:ToggleCastLogging()
Bastion:Print("Cast logging", Bastion.MythicPlusUtils.castLogging and "enabled" or "disabled")
return
end
Command:Register('pause', 'Pause a module for X seconds', function (args)
if Bastion.Enabled then
local duration = args[3]
local moduleName = args[2]
Bastion:Print("[MythicPlusUtils] Unknown command")
Bastion:Print("Available commands:")
Bastion:Print("debuffs")
Bastion:Print("casts")
end)
local RequestedModule = Bastion:FindModule(moduleName)
Command:Register('missed', 'Dump the list of immune kidney shot spells', function()
for k, v in pairs(missed) do
Bastion:Print(k)
if RequestedModule and not Bastion.pausedModules[moduleName] then
Bastion:Print("Pausing module ", moduleName, " for ", duration)
Bastion.pausedModules[moduleName] = { duration = duration, requestTime = GetTime() }
elseif not Bastion.pausedModules[moduleName] then
Bastion:Print("Unable to find module ", moduleName)
end
end
end)
local files = ListFiles("scripts/bastion/scripts")
end)
for i = 1, #files do
local file = files[i]
if file:sub( -4) == ".lua" or file:sub( -5) == '.luac' then
Tinkr:require("scripts/bastion/scripts/" .. file:sub(1, -5), Bastion)
if UnitClass('player') == 'Mage' then
if GetSpecialization() == 1 then
Tinkr:require("scripts/bastion/scripts/arcane", Bastion)
Eval('RunMacroText("/bastion module arcane")', 'bastion')
elseif GetSpecialization() == 2 then
Tinkr:require("scripts/bastion/scripts/fire", Bastion)
Eval('RunMacroText("/bastion module fire")', 'bastion')
elseif GetSpecialization() == 3 then
Tinkr:require("scripts/bastion/scripts/frost/frost", Bastion)
Eval('RunMacroText("/bastion module frost")', 'bastion')
end
end

Loading…
Cancel
Save