Compare commits

..

68 Commits
main ... main

Author SHA1 Message Date
4n0n c0c8ab8362 Fix? 6 months ago
4n0n fad3d76d8a 11.0 ItemName 6 months ago
4n0n e6b2b8f42f Merge pull request '11.0' (#27) from 4n0n/Bastion:main into main 6 months ago
4n0n 86e7aeccec 11.0 6 months ago
4n0n f9583a85d8 11.0 6 months ago
4n0n d0cad5746d 11.0 6 months ago
4n0n 010d2b4111 11.0 6 months ago
4n0n 7442498326 11.0 6 months ago
4n0n 83086a9b57 11.0 6 months ago
4n0n 878d5db7ae Merge pull request 'fix: C_UnitAuras.GetAuraDataByAuraInstanceID' (#21) from vibe/Bastion:fix_getauradatabyaurainstanceid into main 6 months ago
João Fidalgo 4d08a04958 fix: C_UnitAuras.GetAuraDataByAuraInstanceID 7 months ago
4n0n a22aa67d2a Merge pull request 'main' (#19) from CiscOH/Bastion:main into main 1 year ago
Ciscoh Bellic 3877c58b0c Removed return in load(dir) function. it was only loading the first file and no more 1 year ago
CiscOH 8a6308c584 Merge pull request 'main' (#7) from Bastion/Bastion:main into main 1 year ago
4n0n b43166be76 Era 1 year ago
4n0n ede085dd13 Merge pull request 'Bug Fix on APL traits' (#18) from LyLoLoq/Bastion:main into main 1 year ago
LyLoLoq 15920cecb3 Typing fix 1 year ago
LyLoLoq 8e5268229a Bug Fix on APL traits 1 year ago
CiscOH b1d34f113d Merge pull request 'main' (#6) from Bastion/Bastion:main into main 1 year ago
4n0n cadb7df64e Merge pull request 'Fix APL + Change to Spell:Cast' (#17) from LyLoLoq/Bastion:main into main 1 year ago
LyLoLoq a84930cef2 Change Spell:Cast 1 year ago
LyLoLoq 1bdf96e5ca Fix APL 1 year ago
CiscOH 4680344e6f Merge pull request 'main' (#5) from Bastion/Bastion:main into main 1 year ago
4n0n f722d17306 Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 1 year ago
4n0n 5db2f059d8 Add Spell:Fresh() 1 year ago
4n0n 910387c775 Merge pull request 'main' (#16) from CiscOH/Bastion:main into main 1 year ago
CiscOH 2994e8d1dd remove UnitManager:Validate(token) method 1 year ago
CiscOH eab3bb3a8a Merge pull request 'main' (#4) from Bastion/Bastion:main into main 1 year ago
4n0n e4d4270b60 add bastion requires 1 year ago
4n0n 025c0c5c88 remove 1 year ago
CiscOH 5dc90ce113 Merge pull request 'main' (#3) from Bastion/Bastion:main into main 1 year ago
4n0n bfdd70359d Fix typo 1 year ago
4n0n 5472aa1998 Merge pull request 'main' (#15) from CiscOH/Bastion:main into main 1 year ago
CiscOH ba9de7753f added line break to dump spells 1 year ago
CiscOH 94b584a99b Merge pull request 'Update 'README.md'' (#1) from Bastion/Bastion:main into main 1 year ago
4n0n 6924780785 Update 'README.md' 1 year ago
4n0n febee39182 Add highest/leastof functions to auratable, remove print 1 year ago
4n0n a99e71dd8e Aura table updates 1 year ago
4n0n 36ab641a13 Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 1 year ago
4n0n e8913d3ab8 Spellbook get list 1 year ago
4n0n 9d594366dd Merge pull request 'Change Unit.ttd_ticker initialization state' (#13) from abunai/Bastion:abunai-ttd-fix into main 1 year ago
abunai 2200a36bdf Change Unit.ttd_ticker initialization state 1 year ago
4n0n 559c4339a4 Fix aura list finding 1 year ago
4n0n 7b3b714d39 Add findanyof 1 year ago
4n0n 1cc8b541f8 Cleanup 1 year ago
4n0n 677229738d Move spellbook and itembook out of globals, remove prints 1 year ago
4n0n 9d47758f8c Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 1 year ago
4n0n 06916f6bc6 Spellbook and Itembook no longer shared through the engine 1 year ago
4n0n 4c138f1c9f Merge pull request 'More types' (#11) from lyloloq/Bastion:main into main 1 year ago
4n0n f5367327e6 Updated library handling 1 year ago
LyLoLoq 630fcfc3d9 More types 1 year ago
4n0n d5b0ccf42b Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 1 year ago
4n0n 5f0affd907 Add Bastion Libraries 1 year ago
4n0n 356cc78dd1 Merge pull request 'Remove rogue print' (#9) from 4n0n-patch-1 into main 1 year ago
4n0n 5bfc4f5097 Remove rogue print 1 year ago
4n0n 031e7e302d Merge pull request 'Fix Types + Changed `dumpspells` command' (#8) from lyloloq/Bastion:main into main 2 years ago
LyLoLoq 8cf6ffcb8e Fix types 2 years ago
4n0n 596e72d80a Merge pull request 'main' (#7) from CiscOH/Bastion:main into main 2 years ago
Ciscoh Bellic 81f7e371cd readded gitkeep 2 years ago
Ciscoh Bellic 728d427e41 empowered stage check 2 years ago
Ciscoh Bellic c634b58a38 ... 2 years ago
4n0n e3d52b41c4 Methods 2 years ago
4n0n fae312acfc Scripts gitkeep 2 years ago
4n0n 241d79d229 Merge branch 'main' of https://git.tinkr.site/4n0n/bastion 2 years ago
4n0n 147549310c Updates to bastion 2 years ago
4n0n da7ca9b082 Merge pull request 'main' (#4) from CiscOH/Bastion:main into main 2 years ago
OverHealz Jenkins f019304f81 added enemies w/most enemies. enemies centroid 2 years ago
CiscOH 72c4c8b87b Merge pull request 'main' (#1) from Bastion/Bastion:main into main 2 years ago
  1. 9
      .gitignore
  2. 6
      README.md
  3. 188
      references/brewmaster.lua
  4. 954
      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. 758
      references/restodruid.lua
  9. 1379
      references/subtlety.lua
  10. 0
      references/windwalker.lua
  11. 0
      scripts/.gitkeep
  12. 24
      scripts/ExampleModule.lua
  13. 21
      scripts/Libraries/ExampleDependency.lua
  14. 15
      scripts/Libraries/ExampleDependencyError.lua
  15. 25
      scripts/Libraries/ExampleLibrary.lua
  16. 54
      scripts/arcane.lua
  17. 54
      scripts/fire.lua
  18. 59
      scripts/frost/frost-gui.lua
  19. 206
      scripts/frost/frost.lua
  20. 45
      scripts/shared/draw-rop.lua
  21. 25
      scripts/shared/get-ranged-target-count.lua
  22. 69
      src/APL/APL.lua
  23. 54
      src/Aura/Aura.lua
  24. 377
      src/AuraTable/AuraTable.lua
  25. 15
      src/Class/Class.lua
  26. 7
      src/Command/Command.lua
  27. 201
      src/ContentUtils/ContentUtils.lua
  28. 193
      src/ContentUtils/lists/important-aura-list.lua
  29. 662
      src/ContentUtils/lists/interrupt-list.lua
  30. 416
      src/ContentUtils/lists/tank-buster-list.lua
  31. 51
      src/Item/Item.lua
  32. 115
      src/Library/Library.lua
  33. 26
      src/List/List.lua
  34. 585
      src/MythicPlusUtils/MythicPlusUtils.lua
  35. 8
      src/ObjectManager/ObjectManager.lua
  36. 6
      src/Sequencer/Sequencer.lua
  37. 217
      src/Spell/Spell.lua
  38. 17
      src/SpellBook/SpellBook.lua
  39. 104
      src/ToggleManager/ToggleManager.lua
  40. 305
      src/Unit/Unit.lua
  41. 127
      src/UnitManager/UnitManager.lua
  42. 46
      src/Vector3/Vector3.lua
  43. 568
      src/_bastion.lua

9
.gitignore vendored

@ -6,7 +6,14 @@ DS_Store
!.gitkeep
## ignore all files in scripts
# scripts/*
scripts/*
!scripts/Libraries
scripts/Libraries/*
!scripts/.gitkeep
!scripts/ExampleModule.lua
!scripts/Libraries/ExampleLibrary.lua
!scripts/Libraries/ExampleDependency.lua
!scripts/Libraries/ExampleDependencyError.lua
## ignore vscode settings
.vscode/*

@ -10,4 +10,8 @@ Feel free to browse around the [Wiki](https://git.tinkr.site/4n0n/bastion/wiki)
## Quick Start
- [Download](https://git.tinkr.site/4n0n/bastion/archive/main.zip) or clone the repository.
- Move the bastion folder to your `Tinkr/scripts` folder.
- Once in-game type `/bastion toggle` to enable the engine.
- Once in-game type `/bastion toggle` to enable the engine.
## Developer Information
- [Issue Template](https://git.tinkr.site/Bastion/Bastion/wiki/Issues)
- [Pull Requests](https://git.tinkr.site/Bastion/Bastion/wiki/Pull-Requests)

@ -1,188 +0,0 @@
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)

@ -1,954 +0,0 @@
-- local Tinkr, Bastion = ...
-- local OutlawModule = Bastion.Module:New('outlaw')
-- local Evaluator = Tinkr.Util.Evaluator
-- local Player = Bastion.UnitManager:Get('player')
-- local None = Bastion.UnitManager:Get('none')
-- local Target = Bastion.UnitManager:Get('target')
-- local Stealth = Bastion.SpellBook:GetSpell(115191)
-- local Kick = Bastion.SpellBook:GetSpell(1766)
-- local CountTheOdds = Bastion.SpellBook:GetSpell(381982)
-- local Shadowmeld = Bastion.SpellBook:GetSpell(58984)
-- local ShadowDance = Bastion.SpellBook:GetSpell(185313)
-- local ShadowDanceAura = Bastion.SpellBook:GetSpell(185422)
-- local HiddenOpportunity = Bastion.SpellBook:GetSpell(383281)
-- local RollTheBones = Bastion.SpellBook:GetSpell(315508)
-- local FanTheHammer = Bastion.SpellBook:GetSpell(381846)
-- local ImprovedAmbush = Bastion.SpellBook:GetSpell(381620)
-- local SummarilyDispatched = Bastion.SpellBook:GetSpell(381990)
-- local BladeFlurry = Bastion.SpellBook:GetSpell(13877)
-- local KillingSpree = Bastion.SpellBook:GetSpell(51690)
-- local ArcaneTorrent = Bastion.SpellBook:GetSpell(25046)
-- local ArcanePulse = Bastion.SpellBook:GetSpell(260364)
-- local LightsJudgment = Bastion.SpellBook:GetSpell(255647)
-- local BagOfTricks = Bastion.SpellBook:GetSpell(312411)
-- local Sepsis = Bastion.SpellBook:GetSpell(385408)
-- local BetweenTheEyes = Bastion.SpellBook:GetSpell(315341)
-- local GhostlyStrike = Bastion.SpellBook:GetSpell(196937)
-- local Dreadblades = Bastion.SpellBook:GetSpell(343142)
-- local Subterfuge = Bastion.SpellBook:GetSpell(108208)
-- local EchoingReprimand = Bastion.SpellBook:GetSpell(385616)
-- local Ambush = Bastion.SpellBook:GetSpell(8676)
-- local KeepItRolling = Bastion.SpellBook:GetSpell(381989)
-- local Audacity = Bastion.SpellBook:GetSpell(381845)
-- local FindWeakness = Bastion.SpellBook:GetSpell(91023)
-- local PistolShot = Bastion.SpellBook:GetSpell(185763)
-- local Opportunity = Bastion.SpellBook:GetSpell(279876)
-- local OpportunityAura = Bastion.SpellBook:GetSpell(195627)
-- local GreenskinsWickers = Bastion.SpellBook:GetSpell(386823)
-- local QuickDraw = Bastion.SpellBook:GetSpell(196938)
-- local Weaponmaster = Bastion.SpellBook:GetSpell(200733)
-- local SinisterStrike = Bastion.SpellBook:GetSpell(193315)
-- local AdrenalineRush = Bastion.SpellBook:GetSpell(13750)
-- local ImprovedAdrenalineRush = Bastion.SpellBook:GetSpell(395422)
-- local BladeRush = Bastion.SpellBook:GetSpell(271877)
-- local MarkedForDeath = Bastion.SpellBook:GetSpell(137619)
-- local ThistleTea = Bastion.SpellBook:GetSpell(381623)
-- local PotionOfUnbridledFury = Bastion.SpellBook:GetSpell(169299)
-- local Bloodlust = Bastion.SpellBook:GetSpell(2825)
-- local BloodFury = Bastion.SpellBook:GetSpell(20572)
-- local Berserking = Bastion.SpellBook:GetSpell(26297)
-- local Fireblood = Bastion.SpellBook:GetSpell(265221)
-- local AncestralCall = Bastion.SpellBook:GetSpell(274738)
-- local SliceAndDice = Bastion.SpellBook:GetSpell(315496)
-- local SwiftSlasher = Bastion.SpellBook:GetSpell(381988)
-- local ColdBlood = Bastion.SpellBook:GetSpell(382245)
-- local Dispatch = Bastion.SpellBook:GetSpell(2098)
-- local Vanish = Bastion.SpellBook:GetSpell(1856)
-- local Broadside = Bastion.SpellBook:GetSpell(193356)
-- local GrandMelee = Bastion.SpellBook:GetSpell(193358)
-- local SkullAndCrossbones = Bastion.SpellBook:GetSpell(199603)
-- local TrueBearing = Bastion.SpellBook:GetSpell(193359)
-- local LoadedDice = Bastion.SpellBook:GetSpell(256171)
-- local BuriedTreasure = Bastion.SpellBook:GetSpell(199600)
-- local RuthlessPrecision = Bastion.SpellBook:GetSpell(193357)
-- local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit)
-- local purge = 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:GetAuras():HasAnyStealableAura() then
-- purge = unit
-- return true
-- end
-- end)
-- if purge == nil then
-- purge = None
-- end
-- return purge
-- end)
-- local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit)
-- local purge = 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 Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then
-- purge = unit
-- return true
-- end
-- end)
-- if purge == nil then
-- purge = None
-- end
-- return purge
-- end)
-- local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit)
-- local tank = nil
-- Bastion.UnitManager:EnumFriends(function(unit)
-- if Player:GetDistance(unit) > 40 then
-- return false
-- end
-- if not Player:CanSee(unit) then
-- return false
-- end
-- if unit:IsDead() then
-- return false
-- end
-- if unit:IsTank() then
-- tank = unit
-- return true
-- end
-- return false
-- end)
-- if tank == nil then
-- tank = None
-- end
-- return tank
-- end)
-- local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit)
-- local explosive = 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 Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then
-- explosive = unit
-- return true
-- end
-- end)
-- if explosive == nil then
-- explosive = None
-- end
-- return explosive
-- end)
-- local DefaultAPL = Bastion.APL:New('default')
-- local StealthAPL = Bastion.APL:New('stealth')
-- local CDsAPL = Bastion.APL:New('cds')
-- local FinishAPL = Bastion.APL:New('finish')
-- local BuildAPL = Bastion.APL:New('build')
-- local StealthCDsAPL = Bastion.APL:New('stealthcds')
-- -- # Executed every time the actor is available.
-- -- # Restealth if possible (no vulnerable enemies in combat)
-- -- actions=stealth
-- DefaultAPL:AddSpell(
-- Stealth:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(Stealth):IsUp() and
-- not Player:IsAffectingCombat() and not IsMounted()
-- end
-- ):SetTarget(Player)
-- )
-- -- # Interrupt on cooldown to allow simming interactions with that
-- -- actions+=/kick
-- DefaultAPL:AddSpell(
-- Kick:CastableIf(function(self)
-- return self:IsKnownAndUsable() and KickTarget:Exists() and self:IsInRange(KickTarget) and
-- self:IsKnownAndUsable() and
-- not Player:IsCastingOrChanneling() and Player:IsFacing(Target)
-- end):SetTarget(KickTarget)
-- )
-- -- # Checks if we are in an appropriate Stealth state for triggering the Count the Odds bonus
-- -- actions+=/variable,name=stealthed_cto,value=talent.count_the_odds&(stealthed.basic|buff.shadowmeld.up|buff.shadow_dance.up)
-- DefaultAPL:AddVariable(
-- 'stealthed_cto',
-- function()
-- return CountTheOdds:IsKnown() and (Player:GetAuras():FindMy(Stealth):IsUp() or
-- Player:GetAuras():FindMy(Shadowmeld):IsUp() or
-- Player:GetAuras():FindMy(ShadowDanceAura):IsUp())
-- end
-- )
-- local function GetRTBCount()
-- local count = 0
-- if Player:GetAuras():FindMy(Broadside):IsUp() then
-- count = count + 1
-- end
-- if Player:GetAuras():FindMy(GrandMelee):IsUp() then
-- count = count + 1
-- end
-- if Player:GetAuras():FindMy(SkullAndCrossbones):IsUp() then
-- count = count + 1
-- end
-- if Player:GetAuras():FindMy(TrueBearing):IsUp() then
-- count = count + 1
-- end
-- if Player:GetAuras():FindMy(LoadedDice):IsUp() then
-- count = count + 1
-- end
-- if Player:GetAuras():FindMy(BuriedTreasure):IsUp() then
-- count = count + 1
-- end
-- if Player:GetAuras():FindMy(RuthlessPrecision):IsUp() then
-- count = count + 1
-- end
-- return count
-- end
-- -- # Roll the Bones Reroll Conditions
-- -- actions+=/variable,name=rtb_reroll,if=!talent.hidden_opportunity,value=rtb_buffs<2&(!buff.broadside.up&(!talent.fan_the_hammer|!buff.skull_and_crossbones.up)&!buff.true_bearing.up|buff.loaded_dice.up)|rtb_buffs=2&(buff.buried_treasure.up&buff.grand_melee.up|!buff.broadside.up&!buff.true_bearing.up&buff.loaded_dice.up)
-- DefaultAPL:AddVariable(
-- 'rtb_reroll',
-- function()
-- if not HiddenOpportunity:IsKnown() then
-- return GetRTBCount() < 2 and
-- (not Player:GetAuras():FindMy(Broadside):IsUp() and
-- (not FanTheHammer:IsKnown() or
-- not Player:GetAuras():FindMy(SkullAndCrossbones):IsUp()) and
-- not Player:GetAuras():FindMy(TrueBearing):IsUp() or
-- Player:GetAuras():FindMy(LoadedDice):IsUp()) or
-- GetRTBCount() == 2 and
-- (Player:GetAuras():FindMy(BuriedTreasure):IsUp() and
-- Player:GetAuras():FindMy(GrandMelee):IsUp() or
-- not Player:GetAuras():FindMy(Broadside):IsUp() and
-- not Player:GetAuras():FindMy(TrueBearing):IsUp() and
-- Player:GetAuras():FindMy(LoadedDice):IsUp())
-- end
-- end
-- )
-- -- # Additional Reroll Conditions for Keep it Rolling or Count the Odds
-- -- actions+=/variable,name=rtb_reroll,if=!talent.hidden_opportunity&(talent.keep_it_rolling|talent.count_the_odds),value=variable.rtb_reroll|((rtb_buffs.normal=0&rtb_buffs.longer>=1)&!(buff.broadside.up&buff.true_bearing.up&buff.skull_and_crossbones.up)&!(buff.broadside.remains>39|buff.true_bearing.remains>39|buff.ruthless_precision.remains>39|buff.skull_and_crossbones.remains>39))
-- DefaultAPL:AddVariable(
-- 'rtb_reroll',
-- function()
-- if not HiddenOpportunity:IsKnown() then
-- return GetRTBCount() < 2 and
-- (not Player:GetAuras():FindMy(Broadside):IsUp() and
-- (not FanTheHammer:IsKnown() or
-- not Player:GetAuras():FindMy(SkullAndCrossbones):IsUp()) and
-- not Player:GetAuras():FindMy(TrueBearing):IsUp() or
-- Player:GetAuras():FindMy(LoadedDice):IsUp()) or
-- GetRTBCount() == 2 and
-- (Player:GetAuras():FindMy(BuriedTreasure):IsUp() and
-- Player:GetAuras():FindMy(GrandMelee):IsUp() or
-- not Player:GetAuras():FindMy(Broadside):IsUp() and
-- not Player:GetAuras():FindMy(TrueBearing):IsUp() and
-- Player:GetAuras():FindMy(LoadedDice):IsUp())
-- end
-- end
-- )
-- -- # With Hidden Opportunity, prioritize rerolling for Skull and Crossbones over everything else
-- -- actions+=/variable,name=rtb_reroll,if=talent.hidden_opportunity,value=!rtb_buffs.will_lose.skull_and_crossbones&(rtb_buffs.will_lose-rtb_buffs.will_lose.grand_melee)<2+buff.loaded_dice.up
-- DefaultAPL:AddVariable(
-- 'rtb_reroll',
-- function()
-- if HiddenOpportunity:IsKnown() then
-- return not Player:GetAuras():FindMy(SkullAndCrossbones):IsUp() and
-- ((Player:GetAuras():FindMy(SkullAndCrossbones):IsUp() and 1 or 0) -
-- (Player:GetAuras():FindMy(GrandMelee):IsUp() and 1 or 0)) <
-- 2 + (Player:GetAuras():FindMy(LoadedDice):IsUp() and 1 or 0)
-- end
-- end
-- )
-- -- # Avoid rerolls when we will not have time remaining on the fight or add wave to recoup the opportunity cost of the global
-- -- actions+=/variable,name=rtb_reroll,op=reset,if=!(raid_event.adds.remains>12|raid_event.adds.up&(raid_event.adds.in-raid_event.adds.remains)<6|target.time_to_die>12)|fight_remains<12
-- DefaultAPL:AddVariable(
-- 'rtb_reroll',
-- function()
-- if not HiddenOpportunity:IsKnown() then
-- return GetRTBCount() < 2 and
-- (not Player:GetAuras():FindMy(Broadside):IsUp() and
-- (not FanTheHammer:IsKnown() or
-- not Player:GetAuras():FindMy(SkullAndCrossbones):IsUp()) and
-- not Player:GetAuras():FindMy(TrueBearing):IsUp() or
-- Player:GetAuras():FindMy(LoadedDice):IsUp()) or
-- GetRTBCount() == 2 and
-- (Player:GetAuras():FindMy(BuriedTreasure):IsUp() and
-- Player:GetAuras():FindMy(GrandMelee):IsUp() or
-- not Player:GetAuras():FindMy(Broadside):IsUp() and
-- not Player:GetAuras():FindMy(TrueBearing):IsUp() and
-- Player:GetAuras():FindMy(LoadedDice):IsUp())
-- end
-- end
-- )
-- -- # Ensure we get full Ambush CP gains and aren't rerolling Count the Odds buffs away
-- -- actions+=/variable,name=ambush_condition,value=combo_points.deficit>=2+talent.improved_ambush+buff.broadside.up&energy>=50&(!talent.count_the_odds|buff.roll_the_bones.remains>=10)
-- DefaultAPL:AddVariable(
-- 'ambush_condition',
-- function()
-- return Player:GetComboPointsDeficit() >= 2 + (ImprovedAmbush:IsKnown() and 1 or 0) +
-- (Player:GetAuras():FindMy(Broadside):IsUp() and 1 or 0) and
-- Player:GetPower() >= 50 and
-- (not CountTheOdds:IsKnown() or
-- Player:GetAuras():FindMy(RollTheBones):GetRemainingTime() >= 10)
-- end
-- )
-- -- # Finish at 6 (5 with Summarily Dispatched talented) CP or CP Max-1, whichever is greater of the two
-- -- actions+=/variable,name=finish_condition,value=combo_points>=((cp_max_spend-1)<?(6-talent.summarily_dispatched))|effective_combo_points>=cp_max_spend
-- DefaultAPL:AddVariable(
-- 'finish_condition',
-- function()
-- return Player:GetComboPoints() >=
-- math.min(Player:GetComboPointsMax() - 1, 6 - (SummarilyDispatched:IsKnown() and 1 or 0)) or
-- Player:GetComboPoints() >= Player:GetComboPointsMax()
-- end
-- )
-- -- # With multiple targets, this variable is checked to decide whether some CDs should be synced with Blade Flurry
-- -- actions+=/variable,name=blade_flurry_sync,value=spell_targets.blade_flurry<2&raid_event.adds.in>20|buff.blade_flurry.remains>1+talent.killing_spree.enabled
-- DefaultAPL:AddVariable(
-- 'blade_flurry_sync',
-- function()
-- return Player:GetMeleeAttackers() < 2 and
-- (not Player:GetAuras():FindMy(BladeFlurry):IsUp() or
-- Player:GetAuras():FindMy(BladeFlurry):GetRemainingTime() > 1 +
-- (KillingSpree:IsKnown() and 1 or 0))
-- end
-- )
-- -- # Higher priority Stealth list for Count the Odds or true Stealth/Vanish that will break in a single global
-- -- actions+=/call_action_list,name=stealth,if=stealthed.basic|buff.shadowmeld.up
-- DefaultAPL:AddAPL(
-- StealthAPL,
-- function()
-- return Player:IsStealthed() or Player:GetAuras():FindMy(Shadowmeld):IsUp()
-- end
-- )
-- -- actions+=/call_action_list,name=cds
-- DefaultAPL:AddAPL(
-- CDsAPL,
-- function()
-- return true
-- end
-- )
-- -- # Lower priority Stealth list for Shadow Dance
-- -- actions+=/call_action_list,name=stealth,if=variable.stealthed_cto
-- DefaultAPL:AddAPL(
-- StealthAPL,
-- function()
-- return DefaultAPL:GetVariable('stealthed_cto')
-- end
-- )
-- -- actions+=/run_action_list,name=finish,if=variable.finish_condition
-- DefaultAPL:AddAPL(
-- FinishAPL,
-- function()
-- return DefaultAPL:GetVariable('finish_condition')
-- end
-- )
-- -- actions+=/call_action_list,name=build
-- DefaultAPL:AddAPL(
-- BuildAPL,
-- function()
-- return true
-- end
-- )
-- -- actions+=/arcane_torrent,if=energy.base_deficit>=15+energy.regen
-- DefaultAPL:AddSpell(
-- ArcaneTorrent:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Player:GetPowerDeficit() >= 15 + Player:GetPowerRegen()
-- end
-- ):SetTarget(Target)
-- )
-- -- actions+=/arcane_pulse
-- DefaultAPL:AddSpell(
-- ArcanePulse:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- actions+=/lights_judgment
-- DefaultAPL:AddSpell(
-- LightsJudgment:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- actions+=/bag_of_tricks
-- DefaultAPL:AddSpell(
-- BagOfTricks:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- # Builders
-- -- actions.build=sepsis,target_if=max:target.time_to_die*debuff.between_the_eyes.up,if=target.time_to_die>11&debuff.between_the_eyes.up|fight_remains<11
-- BuildAPL:AddSpell(
-- Sepsis:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and ((Target:TimeToDie() > 11 and
-- Target:GetAuras():FindMy(BetweenTheEyes):IsUp()) or
-- Player:TimeToDie() < 11)
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.build+=/ghostly_strike,if=debuff.ghostly_strike.remains<=3&(spell_targets.blade_flurry<=2|buff.dreadblades.up)&!buff.subterfuge.up&target.time_to_die>=5
-- BuildAPL:AddSpell(
-- GhostlyStrike:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Target:GetAuras():FindMy(GhostlyStrike):GetRemainingTime() <= 3 and
-- (Player:GetMeleeAttackers() <= 2 or Player:GetAuras():FindMy(Dreadblades):IsUp()) and
-- not Player:GetAuras():FindMy(Subterfuge):IsUp() and Target:TimeToDie() >= 5
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.build+=/echoing_reprimand,if=!buff.dreadblades.up
-- BuildAPL:AddSpell(
-- EchoingReprimand:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(Dreadblades):IsUp()
-- end
-- ):SetTarget(Target)
-- )
-- -- # High priority Ambush line to apply Find Weakness or consume Audacity/Sepsis buff before Pistol Shot
-- -- actions.build+=/ambush,if=(talent.hidden_opportunity|talent.keep_it_rolling)&(buff.audacity.up|buff.sepsis_buff.up|buff.subterfuge.up&cooldown.keep_it_rolling.ready)|talent.find_weakness&debuff.find_weakness.down
-- BuildAPL:AddSpell(
-- Ambush:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and ((HiddenOpportunity:IsKnown() or KeepItRolling:IsKnown()) and
-- (Player:GetAuras():FindMy(Audacity):IsUp() or Player:GetAuras():FindMy(Sepsis):IsUp() or
-- (Player:GetAuras():FindMy(Subterfuge):IsUp() and KeepItRolling:OnCooldown())) or
-- (FindWeakness:IsKnown() and Target:GetAuras():FindMy(FindWeakness):IsDown()))
-- end
-- ):SetTarget(Target)
-- )
-- -- # With Audacity + Hidden Opportunity + Fan the Hammer, use Pistol Shot to proc Audacity any time Ambush is not available
-- -- actions.build+=/pistol_shot,if=talent.fan_the_hammer&talent.audacity&talent.hidden_opportunity&buff.opportunity.up&!buff.audacity.up&!buff.subterfuge.up&!buff.shadow_dance.up
-- BuildAPL:AddSpell(
-- PistolShot:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and FanTheHammer:IsKnown() and Audacity:IsKnown() and
-- HiddenOpportunity:IsKnown() and
-- Player:GetAuras():FindMy(OpportunityAura):IsUp() and not Player:GetAuras():FindMy(Audacity):IsUp() and
-- not Player:GetAuras():FindMy(Subterfuge):IsUp() and not Player:GetAuras():FindMy(ShadowDance):IsUp()
-- end
-- ):SetTarget(Target)
-- )
-- -- # Use Greenskins Wickers buff immediately with Opportunity unless running Fan the Hammer
-- -- actions.build+=/pistol_shot,if=buff.greenskins_wickers.up&(!talent.fan_the_hammer&buff.opportunity.up|buff.greenskins_wickers.remains<1.5)
-- BuildAPL:AddSpell(
-- PistolShot:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Player:GetAuras():FindMy(GreenskinsWickers):IsUp() and
-- (not FanTheHammer:IsKnown() and Player:GetAuras():FindMy(OpportunityAura):IsUp() or
-- Player:GetAuras():FindMy(GreenskinsWickers):GetRemainingTime() < 1.5)
-- end
-- ):SetTarget(Target)
-- )
-- -- const int stacks = 1 + as<int>( p()->talent.outlaw.fan_the_hammer->effectN( 1 ).base_value() );
-- local function MaxOpportunity()
-- return 1 + (FanTheHammer:IsKnown() and 1 or 0)
-- end
-- -- # With Fan the Hammer, consume Opportunity at max stacks or if we will get max 4+ CP and Dreadblades is not up
-- -- actions.build+=/pistol_shot,if=talent.fan_the_hammer&buff.opportunity.up&(buff.opportunity.stack>=buff.opportunity.max_stack|buff.opportunity.remains<2)
-- BuildAPL:AddSpell(
-- PistolShot:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and FanTheHammer:IsKnown() and
-- Player:GetAuras():FindMy(OpportunityAura):IsUp() and
-- (
-- Player:GetAuras():FindMy(OpportunityAura):GetCount() >=
-- MaxOpportunity()
-- or
-- Player:GetAuras():FindMy(OpportunityAura):GetRemainingTime() < 2)
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.build+=/pistol_shot,if=talent.fan_the_hammer&buff.opportunity.up&combo_points.deficit>((1+talent.quick_draw)*talent.fan_the_hammer.rank)&!buff.dreadblades.up&(!talent.hidden_opportunity|!buff.subterfuge.up&!buff.shadow_dance.up)
-- BuildAPL:AddSpell(
-- PistolShot:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and FanTheHammer:IsKnown() and
-- Player:GetAuras():FindMy(OpportunityAura):IsUp() and
-- Player:GetComboPointsDeficit() > ((1 + (QuickDraw:IsKnown() and 1 or 0)) * 1) and
-- not Player:GetAuras():FindMy(Dreadblades):IsUp() and
-- (not HiddenOpportunity:IsKnown() or not Player:GetAuras():FindMy(Subterfuge):IsUp() and
-- not Player:GetAuras():FindMy(ShadowDance):IsUp())
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.build+=/pool_resource,for_next=1
-- -- actions.build+=/ambush,if=talent.hidden_opportunity|talent.find_weakness&debuff.find_weakness.down
-- BuildAPL:AddSpell(
-- Ambush:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and (HiddenOpportunity:IsKnown() or
-- (FindWeakness:IsKnown() and Target:GetAuras():FindMy(FindWeakness):IsDown()))
-- end
-- ):SetTarget(Target)
-- )
-- -- # Use Pistol Shot with Opportunity if Combat Potency won't overcap energy, when it will exactly cap CP, or when using Quick Draw
-- -- actions.build+=/pistol_shot,if=!talent.fan_the_hammer&buff.opportunity.up&(energy.base_deficit>energy.regen*1.5|!talent.weaponmaster&combo_points.deficit<=1+buff.broadside.up|talent.quick_draw.enabled|talent.audacity.enabled&!buff.audacity.up)
-- BuildAPL:AddSpell(
-- PistolShot:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not FanTheHammer:IsKnown() and
-- Player:GetAuras():FindMy(OpportunityAura):IsUp() and
-- (Player:GetPowerDeficit() > Player:GetPowerRegen() * 1.5 or
-- (not Weaponmaster:IsKnown() and
-- Player:GetComboPointsDeficit() <= 1 + (Player:GetAuras():FindMy(Broadside):IsUp() and 1 or 0)) or
-- QuickDraw:IsKnown() or
-- (Audacity:IsKnown() and not Player:GetAuras():FindMy(Audacity):IsUp()))
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.build+=/sinister_strike
-- BuildAPL:AddSpell(
-- SinisterStrike:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- # Cooldowns
-- -- actions.cds=adrenaline_rush,if=!buff.adrenaline_rush.up&(!talent.improved_adrenaline_rush|combo_points<=2)
-- CDsAPL:AddSpell(
-- AdrenalineRush:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(AdrenalineRush):IsUp() and
-- (not ImprovedAdrenalineRush:IsKnown() or Player:GetComboPoints() <= 2)
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/blade_flurry,if=spell_targets>=2&buff.blade_flurry.remains<gcd
-- CDsAPL:AddSpell(
-- BladeFlurry:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Target:GetMeleeAttackers() >= 2 and
-- Player:GetAuras():FindMy(BladeFlurry):GetRemainingTime() < Player:GetGCD()
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/roll_the_bones,if=buff.dreadblades.down&(rtb_buffs.total=0|variable.rtb_reroll)
-- CDsAPL:AddSpell(
-- RollTheBones:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Player:GetAuras():FindMy(Dreadblades):IsDown() and
-- (GetRTBCount() == 0 or
-- DefaultAPL:GetVariable("rtb_reroll"))
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/keep_it_rolling,if=!variable.rtb_reroll&(buff.broadside.up+buff.true_bearing.up+buff.skull_and_crossbones.up+buff.ruthless_precision.up)>2&(buff.shadow_dance.down|rtb_buffs>=6)
-- CDsAPL:AddSpell(
-- RollTheBones:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not DefaultAPL:GetVariable("rtb_reroll") and
-- ((Player:GetAuras():FindMy(Broadside):IsUp() and 1 or 0) +
-- (Player:GetAuras():FindMy(TrueBearing):IsUp() and 1 or 0) +
-- (Player:GetAuras():FindMy(SkullAndCrossbones):IsUp() and 1 or 0) +
-- (Player:GetAuras():FindMy(RuthlessPrecision):IsUp() and 1 or 0)) > 2 and
-- (Player:GetAuras():FindMy(ShadowDance):IsDown() or
-- GetRTBCount() >= 6)
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/blade_rush,if=variable.blade_flurry_sync&!buff.dreadblades.up&(energy.base_time_to_max>4+stealthed.rogue-spell_targets%3)
-- CDsAPL:AddSpell(
-- BladeRush:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and DefaultAPL:GetVariable("blade_flurry_sync") and
-- not Player:GetAuras():FindMy(Dreadblades):IsUp() and
-- (Player:GetTimeToPowerPercent() > 4 + (Player:GetAuras():FindMy(Subterfuge):IsUp() and 1 or 0) +
-- (Player:GetAuras():FindMy(ShadowDance):IsUp() and 1 or 0) - Target:GetMeleeAttackers() % 3)
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/call_action_list,name=stealth_cds,if=!stealthed.all|talent.count_the_odds&!variable.stealthed_cto
-- CDsAPL:AddAPL(
-- StealthCDsAPL,
-- function()
-- return not Player:GetAuras():FindMy(Stealth):IsUp() or
-- (CountTheOdds:IsKnown() and not DefaultAPL:GetVariable("stealthed_cto"))
-- end
-- )
-- -- actions.cds+=/dreadblades,if=!(variable.stealthed_cto|stealthed.basic|talent.hidden_opportunity&stealthed.rogue)&combo_points<=2&(!talent.marked_for_death|!cooldown.marked_for_death.ready)&target.time_to_die>=10
-- CDsAPL:AddSpell(
-- Dreadblades:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and
-- not (DefaultAPL:GetVariable("stealthed_cto") or Player:GetAuras():FindMy(Stealth):IsUp() or
-- (HiddenOpportunity:IsKnown() and Player:GetAuras():FindMy(Subterfuge):IsUp())) and
-- Player:GetComboPoints() <= 2 and
-- (not MarkedForDeath:IsKnown() or not MarkedForDeath:CooldownUp()) and
-- Target:TimeToDie() >= 10
-- end
-- ):SetTarget(Target)
-- )
-- -- # If adds are up, snipe the one with lowest TTD. Use when dying faster than CP deficit or without any CP.
-- -- actions.cds+=/marked_for_death,line_cd=1.5,target_if=min:target.time_to_die,if=raid_event.adds.up&(target.time_to_die<combo_points.deficit|combo_points.deficit>=cp_max_spend-1)&!buff.dreadblades.up
-- -- # If no adds will die within the next 30s, use MfD on boss without any CP.
-- -- actions.cds+=/marked_for_death,if=raid_event.adds.in>30-raid_event.adds.duration&combo_points.deficit>=cp_max_spend-1&!buff.dreadblades.up
-- -- actions.cds+=/thistle_tea,if=!buff.thistle_tea.up&(energy.base_deficit>=100|fight_remains<charges*6)
-- CDsAPL:AddSpell(
-- ThistleTea:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Player:GetAuras():FindMy(ThistleTea):IsDown() and
-- (Player:GetPowerDeficit() >= 100 or Target:TimeToDie() < ThistleTea:Charges() * 6)
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/killing_spree,if=variable.blade_flurry_sync&!stealthed.rogue&debuff.between_the_eyes.up&energy.base_time_to_max>4
-- CDsAPL:AddSpell(
-- KillingSpree:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and DefaultAPL:GetVariable("blade_flurry_sync") and
-- not Player:GetAuras():FindMy(Subterfuge):IsUp() and
-- Target:GetAuras():FindMy(BetweenTheEyes):IsUp() and
-- Player:GetTimeToPowerPercent() > 4
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/shadowmeld,if=!stealthed.all&(talent.count_the_odds&variable.finish_condition|!talent.weaponmaster.enabled&variable.ambush_condition)
-- CDsAPL:AddSpell(
-- Shadowmeld:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(Stealth):IsUp() and
-- (CountTheOdds:IsKnown() and DefaultAPL:GetVariable("finish_condition") or
-- not Weaponmaster:IsKnown() and DefaultAPL:GetVariable("ambush_condition"))
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/potion,if=buff.bloodlust.react|fight_remains<30|buff.adrenaline_rush.up
-- -- CDsAPL:AddItem(
-- -- PotionOfUnbridledFury:CastableIf(
-- -- function(self)
-- -- return self:IsKnownAndUsable() and Player:GetAuras():FindMy(Bloodlust):IsUp() or Target:TimeToDie() < 30 or
-- -- Player:GetAuras():FindMy(AdrenalineRush):IsUp()
-- -- end
-- -- ):SetTarget(Target)
-- -- )
-- -- actions.cds+=/blood_fury
-- CDsAPL:AddSpell(
-- BloodFury:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/berserking
-- CDsAPL:AddSpell(
-- Berserking:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/fireblood
-- CDsAPL:AddSpell(
-- Fireblood:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.cds+=/ancestral_call
-- CDsAPL:AddSpell(
-- AncestralCall:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- # Default conditions for usable items.
-- -- actions.cds+=/use_item,name=manic_grieftorch,if=!stealthed.all&!buff.adrenaline_rush.up|fight_remains<5
-- -- actions.cds+=/use_item,name=stormeaters_boon,if=spell_targets.blade_flurry>desired_targets|raid_event.adds.in>60|fight_remains<10
-- -- actions.cds+=/use_item,name=windscar_whetstone,if=spell_targets.blade_flurry>desired_targets|raid_event.adds.in>60|fight_remains<7
-- -- actions.cds+=/use_items,slots=trinket1,if=debuff.between_the_eyes.up|trinket.1.has_stat.any_dps|fight_remains<=20
-- -- actions.cds+=/use_items,slots=trinket2,if=debuff.between_the_eyes.up|trinket.2.has_stat.any_dps|fight_remains<=20
-- -- # Finishers BtE to keep the Crit debuff up, if RP is up, or for Greenskins, unless the target is about to die.
-- -- actions.finish=between_the_eyes,if=target.time_to_die>3&(debuff.between_the_eyes.remains<4|talent.greenskins_wickers&!buff.greenskins_wickers.up|!talent.greenskins_wickers&buff.ruthless_precision.up)
-- FinishAPL:AddSpell(
-- BetweenTheEyes:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Target:TimeToDie() > 3 and
-- (Target:GetAuras():FindMy(BetweenTheEyes):GetRemainingTime() < 4 or
-- GreenskinsWickers:IsKnown() and Player:GetAuras():FindMy(GreenskinsWickers):IsDown() or
-- not GreenskinsWickers:IsKnown() and Player:GetAuras():FindMy(RuthlessPrecision):IsUp())
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.finish+=/slice_and_dice,if=buff.slice_and_dice.remains<fight_remains&refreshable&(!talent.swift_slasher|combo_points>=cp_max_spend)
-- FinishAPL:AddSpell(
-- SliceAndDice:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and
-- Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < Target:TimeToDie() and
-- Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 6 and
-- (not SwiftSlasher:IsKnown() or Player:GetComboPoints() >= Player:GetComboPointsMax())
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.finish+=/cold_blood
-- FinishAPL:AddSpell(
-- ColdBlood:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.finish+=/dispatch
-- FinishAPL:AddSpell(
-- Dispatch:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and true
-- end
-- ):SetTarget(Target)
-- )
-- -- # Stealth
-- -- actions.stealth=blade_flurry,if=talent.subterfuge&talent.hidden_opportunity&spell_targets>=2&!buff.blade_flurry.up
-- StealthAPL:AddSpell(
-- BladeFlurry:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and Subterfuge:IsKnown() and HiddenOpportunity:IsKnown() and
-- Target:GetMeleeAttackers() >= 2 and
-- not Player:GetAuras():FindMy(BladeFlurry):IsUp()
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth+=/cold_blood,if=variable.finish_condition
-- StealthAPL:AddSpell(
-- ColdBlood:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and DefaultAPL:GetVariable('finish_condition')
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth+=/dispatch,if=variable.finish_condition
-- StealthAPL:AddSpell(
-- Dispatch:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and DefaultAPL:GetVariable('finish_condition')
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth+=/ambush,if=variable.stealthed_cto|stealthed.basic&talent.find_weakness&!debuff.find_weakness.up|talent.hidden_opportunity
-- StealthAPL:AddSpell(
-- Ambush:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and (DefaultAPL:GetVariable('stealthed_cto') or
-- Player:IsStealthed() and FindWeakness:IsKnown() and not Target:GetAuras():FindMy(FindWeakness):IsUp() or
-- HiddenOpportunity:IsKnown())
-- end
-- ):SetTarget(Target)
-- )
-- -- # Stealth Cooldowns
-- -- actions.stealth_cds=variable,name=vanish_condition,value=talent.hidden_opportunity|!talent.shadow_dance|!cooldown.shadow_dance.ready
-- StealthAPL:AddVariable(
-- 'vanish_condition',
-- function()
-- return HiddenOpportunity:IsKnown() or not ShadowDance:IsKnown() or not ShadowDance:CooldownUp()
-- end
-- )
-- -- actions.stealth_cds+=/variable,name=vanish_opportunity_condition,value=!talent.shadow_dance&talent.fan_the_hammer.rank+talent.quick_draw+talent.audacity<talent.count_the_odds+talent.keep_it_rolling
-- StealthAPL:AddVariable(
-- 'vanish_opportunity_condition',
-- function()
-- return not ShadowDance:IsKnown() and
-- (FanTheHammer:IsKnown() and 1 or 0) + (QuickDraw:IsKnown() and 1 or 0) + (Audacity:IsKnown() and 1 or 0) <
-- (CountTheOdds:IsKnown() and 1 or 0) + (KeepItRolling:IsKnown() and 1 or 0)
-- end
-- )
-- -- actions.stealth_cds+=/vanish,if=talent.find_weakness&!talent.audacity&debuff.find_weakness.down&variable.ambush_condition&variable.vanish_condition
-- StealthAPL:AddSpell(
-- Vanish:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and FindWeakness:IsKnown() and not Audacity:IsKnown() and
-- not Target:GetAuras():FindMy(FindWeakness):IsUp() and
-- DefaultAPL:GetVariable('ambush_condition') and StealthAPL:GetVariable('vanish_condition')
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth_cds+=/vanish,if=talent.hidden_opportunity&!buff.audacity.up&(variable.vanish_opportunity_condition|buff.opportunity.stack<buff.opportunity.max_stack)&variable.ambush_condition&variable.vanish_condition
-- StealthAPL:AddSpell(
-- Vanish:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and HiddenOpportunity:IsKnown() and
-- not Player:GetAuras():FindMy(Audacity):IsUp() and
-- (StealthAPL:GetVariable('vanish_opportunity_condition') or
-- Player:GetAuras():FindMy(OpportunityAura):GetCount() <
-- MaxOpportunity()) and
-- DefaultAPL:GetVariable('ambush_condition') and StealthAPL:GetVariable('vanish_condition')
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth_cds+=/vanish,if=(!talent.find_weakness|talent.audacity)&!talent.hidden_opportunity&variable.finish_condition&variable.vanish_condition
-- StealthAPL:AddSpell(
-- Vanish:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and (not FindWeakness:IsKnown() or Audacity:IsKnown()) and
-- not HiddenOpportunity:IsKnown() and
-- DefaultAPL:GetVariable('finish_condition') and StealthAPL:GetVariable('vanish_condition')
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth_cds+=/variable,name=shadow_dance_condition,value=talent.shadow_dance&debuff.between_the_eyes.up&(!talent.ghostly_strike|debuff.ghostly_strike.up)&(!talent.dreadblades|!cooldown.dreadblades.ready)&(!talent.hidden_opportunity|!buff.audacity.up&(talent.fan_the_hammer.rank<2|!buff.opportunity.up))
-- StealthAPL:AddVariable(
-- 'shadow_dance_condition',
-- function()
-- return ShadowDance:IsKnown() and Target:GetAuras():FindMy(BetweenTheEyes):IsUp() and
-- (not GhostlyStrike:IsKnown() or Target:GetAuras():FindMy(GhostlyStrike):IsUp()) and
-- (not Dreadblades:IsKnown() or not Dreadblades:CooldownUp()) and
-- (not HiddenOpportunity:IsKnown() or not Player:GetAuras():FindMy(Audacity):IsUp() and
-- (1 < 2 or not Player:GetAuras():FindMy(OpportunityAura):IsUp()))
-- end
-- )
-- -- actions.stealth_cds+=/shadow_dance,if=!talent.keep_it_rolling&variable.shadow_dance_condition&buff.slice_and_dice.up&(variable.finish_condition|talent.hidden_opportunity)&(!talent.hidden_opportunity|!cooldown.vanish.ready)
-- StealthAPL:AddSpell(
-- ShadowDance:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and not KeepItRolling:IsKnown() and
-- StealthAPL:GetVariable('shadow_dance_condition') and
-- Player:GetAuras():FindMy(SliceAndDice):IsUp() and
-- (DefaultAPL:GetVariable('finish_condition') or HiddenOpportunity:IsKnown()) and
-- (not HiddenOpportunity:IsKnown() or not Vanish:CooldownUp())
-- end
-- ):SetTarget(Target)
-- )
-- -- actions.stealth_cds+=/shadow_dance,if=talent.keep_it_rolling&variable.shadow_dance_condition&(cooldown.keep_it_rolling.remains<=30|cooldown.keep_it_rolling.remains>120&(variable.finish_condition|talent.hidden_opportunity))
-- StealthAPL:AddSpell(
-- ShadowDance:CastableIf(
-- function(self)
-- return self:IsKnownAndUsable() and KeepItRolling:IsKnown() and
-- StealthAPL:GetVariable('shadow_dance_condition') and
-- (KeepItRolling:GetCooldownRemaining() <= 30 or
-- KeepItRolling:GetCooldownRemaining() > 120 and
-- (DefaultAPL:GetVariable('finish_condition') or HiddenOpportunity:IsKnown()))
-- end
-- ):SetTarget(Target)
-- )
-- OutlawModule:Sync(function()
-- print(BetweenTheEyes:IsKnownAndUsable())
-- DefaultAPL:Execute()
-- end)
-- Bastion:Register(OutlawModule)

@ -1,226 +0,0 @@
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)

@ -1,69 +0,0 @@
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

@ -1,57 +0,0 @@
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

@ -1,758 +0,0 @@
local Tinkr, Bastion = ...
local RestoModule = Bastion.Module:New('resto_druid')
local Evaluator = Tinkr.Util.Evaluator
local Player = Bastion.UnitManager:Get('player')
local None = Bastion.UnitManager:Get('none')
local Target = Bastion.UnitManager:Get('target')
local AnomalyDetectionMarkI = Bastion.SpellBook:GetSpell(382499)
local AutoAttack = Bastion.SpellBook:GetSpell(6603)
local MechanismBypass = Bastion.SpellBook:GetSpell(382501)
local OverloadElementalDeposit = Bastion.SpellBook:GetSpell(388213)
local ReviveBattlePets = Bastion.SpellBook:GetSpell(125439)
local WarStomp = Bastion.SpellBook:GetSpell(20549)
local ArmorSkills = Bastion.SpellBook:GetSpell(76275)
local Brawn = Bastion.SpellBook:GetSpell(154743)
local Cultivation = Bastion.SpellBook:GetSpell(20552)
local Endurance = Bastion.SpellBook:GetSpell(20550)
local Languages = Bastion.SpellBook:GetSpell(79746)
local MasterRiding = Bastion.SpellBook:GetSpell(90265)
local NatureResistance = Bastion.SpellBook:GetSpell(20551)
local WeaponSkills = Bastion.SpellBook:GetSpell(76300)
local ActivateEmpowerment = Bastion.SpellBook:GetSpell(357857)
local BlessingofOhnara = Bastion.SpellBook:GetSpell(384522)
local BronzeTimelock = Bastion.SpellBook:GetSpell(374990)
local ChampionAbility = Bastion.SpellBook:GetSpell(356550)
local CenarionWard = Bastion.SpellBook:GetSpell(102351)
local CombatAlly = Bastion.SpellBook:GetSpell(211390)
local ConstructAbility = Bastion.SpellBook:GetSpell(347013)
local CovenantAbility = Bastion.SpellBook:GetSpell(313347)
local GarrisonAbility = Bastion.SpellBook:GetSpell(161691)
local HeartEssence = Bastion.SpellBook:GetSpell(296208)
local HuntingCompanion = Bastion.SpellBook:GetSpell(376280)
local SanityRestorationOrb = Bastion.SpellBook:GetSpell(314955)
local SignatureAbility = Bastion.SpellBook:GetSpell(326526)
local SkywardAscent = Bastion.SpellBook:GetSpell(372610)
local SummonPocopoc = Bastion.SpellBook:GetSpell(360078)
local SurgeForward = Bastion.SpellBook:GetSpell(372608)
local Throw = Bastion.SpellBook:GetSpell(385265)
local VenthyrAbility = Bastion.SpellBook:GetSpell(315594)
local WartimeAbility = Bastion.SpellBook:GetSpell(264739)
local WhirlingSurge = Bastion.SpellBook:GetSpell(361584)
local PocopocZoneAbilitySkill = Bastion.SpellBook:GetSpell(363942)
local DragonridingBasics = Bastion.SpellBook:GetSpell(376777)
local LiftOff = Bastion.SpellBook:GetSpell(383363)
local ThrilloftheSkies = Bastion.SpellBook:GetSpell(383366)
local Vigor = Bastion.SpellBook:GetSpell(383359)
local WindsoftheIsles = Bastion.SpellBook:GetSpell(373586)
local Barkskin = Bastion.SpellBook:GetSpell(22812)
local BearForm = Bastion.SpellBook:GetSpell(5487)
local CatForm = Bastion.SpellBook:GetSpell(768)
local Cyclone = Bastion.SpellBook:GetSpell(33786)
local EntanglingRoots = Bastion.SpellBook:GetSpell(339)
local FerociousBite = Bastion.SpellBook:GetSpell(22568)
local FrenziedRegeneration = Bastion.SpellBook:GetSpell(22842)
local Growl = Bastion.SpellBook:GetSpell(6795)
local Innervate = Bastion.SpellBook:GetSpell(29166)
local Mangle = Bastion.SpellBook:GetSpell(33917)
local MarkoftheWild = Bastion.SpellBook:GetSpell(1126)
local Moonfire = Bastion.SpellBook:GetSpell(8921)
local MoonfireAura = Bastion.SpellBook:GetSpell(164812)
local Prowl = Bastion.SpellBook:GetSpell(5215)
local Rebirth = Bastion.SpellBook:GetSpell(20484)
local Regrowth = Bastion.SpellBook:GetSpell(8936)
local Rejuvenation = Bastion.SpellBook:GetSpell(774)
local RejuvenationAura = Bastion.SpellBook:GetSpell(25299)
local Revive = Bastion.SpellBook:GetSpell(50769)
local Rip = Bastion.SpellBook:GetSpell(1079)
local Shred = Bastion.SpellBook:GetSpell(5221)
local Soothe = Bastion.SpellBook:GetSpell(2908)
local StampedingRoar = Bastion.SpellBook:GetSpell(106898)
local Sunfire = Bastion.SpellBook:GetSpell(93402)
local SunfireAura = Bastion.SpellBook:GetSpell(164815)
local Swiftmend = Bastion.SpellBook:GetSpell(18562)
local TeleportMoonglade = Bastion.SpellBook:GetSpell(18960)
local Thrash = Bastion.SpellBook:GetSpell(106832)
local TigerDash = Bastion.SpellBook:GetSpell(252216)
local TravelForm = Bastion.SpellBook:GetSpell(783)
local UrsolsVortex = Bastion.SpellBook:GetSpell(102793)
local WildGrowth = Bastion.SpellBook:GetSpell(48438)
local Wrath = Bastion.SpellBook:GetSpell(5176)
local AquaticForm = Bastion.SpellBook:GetSpell(276012)
local FlightForm = Bastion.SpellBook:GetSpell(276029)
local TigerDash = Bastion.SpellBook:GetSpell(252216)
local Efflorescence = Bastion.SpellBook:GetSpell(145205)
local IncarnationTreeofLife = Bastion.SpellBook:GetSpell(33891)
local Ironbark = Bastion.SpellBook:GetSpell(102342)
local Lifebloom = Bastion.SpellBook:GetSpell(33763)
local LifebloomAura = Bastion.SpellBook:GetSpell(188550)
local NaturesCure = Bastion.SpellBook:GetSpell(88423)
local NaturesSwiftness = Bastion.SpellBook:GetSpell(132158)
local Revitalize = Bastion.SpellBook:GetSpell(212040)
local Tranquility = Bastion.SpellBook:GetSpell(740)
local MasteryHarmony = Bastion.SpellBook:GetSpell(77495)
local Moonfire = Bastion.SpellBook:GetSpell(8921)
local Wrath = Bastion.SpellBook:GetSpell(5176)
local BearForm = Bastion.SpellBook:GetSpell(5487)
local AdaptiveSwarm = Bastion.SpellBook:GetSpell(391888)
local AdaptiveSwarmBuff = Bastion.SpellBook:GetSpell(391891)
local ClearCasting = Bastion.SpellBook:GetSpell(16870)
local ConvokeTheSpirits = Bastion.SpellBook:GetSpell(391528)
local Flourish = Bastion.SpellBook:GetSpell(197721)
local SoulOfTheForest = Bastion.SpellBook:GetSpell(114108)
local Bursting = Bastion.SpellBook:GetSpell(240443)
local Rake = Bastion.SpellBook:GetSpell(1822)
local RakeAura = Bastion.SpellBook:GetSpell(155722)
local Starsurge = Bastion.SpellBook:GetSpell(197626)
local NaturesVigil = Bastion.SpellBook:GetSpell(124974)
local SpringBlossoms = Bastion.SpellBook:GetSpell(207386)
local RakeDebuff = Bastion.SpellBook:GetSpell(155722)
local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit)
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() then
return false
end
if Player:GetDistance(unit) > 40 then
return false
end
if not Player:CanSee(unit) then
return false
end
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end)
if not lowest then
lowest = Player
end
return lowest
end)
local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit)
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(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 not unit:IsDead() and Player:CanSee(unit) and
unit:GetAuras():HasAnyDispelableAura(NaturesCure) then
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end
end)
if lowest == nil then
lowest = None
end
return lowest
end)
local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit)
local purge = 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 not unit:IsDead() and Player:CanSee(unit) and
unit:GetAuras():HasAnyStealableAura() then
purge = unit
return true
end
end)
if purge == nil then
purge = None
end
return purge
end)
local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit)
local tank = nil
Bastion.UnitManager:EnumFriends(function(unit)
if Player:GetDistance(unit) > 40 then
return false
end
if not Player:CanSee(unit) then
return false
end
if unit:IsDead() then
return false
end
if unit:IsTank() then
tank = unit
return true
end
return false
end)
if tank == nil then
tank = Player
end
return tank
end)
local RejuvUnit = Bastion.UnitManager:CreateCustomUnit('rejuv', function(unit)
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(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 not unit:IsDead() and Player:CanSee(unit) and
(
not unit:GetAuras():FindMy(Rejuvenation):IsUp() or
unit:GetAuras():FindMy(Rejuvenation):GetRemainingTime() <= 3.6) then
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end
end)
if lowest == nil then
lowest = Player
end
return lowest
end)
local SwiftmendUnit = Bastion.UnitManager:CreateCustomUnit('swiftmend', function(unit)
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(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 (
Player:CanSee(unit) and (
(unit:GetAuras():FindMy(Regrowth):IsUp())
or
(
unit:GetAuras():FindMy(Rejuvenation):IsUp() and
not unit:GetAuras():FindMy(WildGrowth):IsUp())
)
) then
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end
end)
if lowest == nil then
lowest = None
end
return lowest
end)
local WildGrowthUnit = Bastion.UnitManager:CreateCustomUnit('wildgrowth', function(unit)
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(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 Player:CanSee(unit) and (
(
Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and
(
Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or
unit:GetPartyHPAround(30, 90) >= 2)) or
(unit:GetPartyHPAround(30, 90) >= 3 or unit:GetPartyHPAround(30, 85) >= 2))
then
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end
end)
if lowest == nil then
lowest = None
end
return lowest
end)
local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit)
local explosive = nil
Bastion.ObjectManager.explosives:each(function(unit)
if unit:IsDead() then
return false
end
if not Player:CanSee(unit) then
return false
end
if Player:GetDistance(unit) <= 40 then
explosive = unit
return true
end
end)
if explosive == nil then
explosive = None
end
return explosive
end)
local RakeTarget = Bastion.UnitManager:CreateCustomUnit('rake', function(unit)
local rakeTarget = 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 not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and unit:InMelee(Player) and
Player:IsFacing(unit) and
(
not unit:GetAuras():FindMy(RakeDebuff):IsUp() or
unit:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 3.6) then
rakeTarget = unit
end
end)
if rakeTarget == nil then
rakeTarget = None
end
return rakeTarget
end)
local MoonfireTarget = Bastion.UnitManager:CreateCustomUnit('moonfire', function(unit)
local moonfireTarget = 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 not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and
(
not unit:GetAuras():FindMy(MoonfireAura):IsUp() or
unit:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 3.6) then
moonfireTarget = unit
end
end)
if moonfireTarget == nil then
moonfireTarget = None
end
return moonfireTarget
end)
local SunfireTarget = Bastion.UnitManager:CreateCustomUnit('sunfire', function(unit)
local sunfireTarget = 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 not unit:IsDead() and Player:CanSee(unit) and unit:InCombatOdds() > 80 and
(
not unit:GetAuras():FindMy(SunfireAura):IsUp() or
unit:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 3.6) then
sunfireTarget = unit
end
end)
if sunfireTarget == nil then
sunfireTarget = None
end
return sunfireTarget
end)
local RestoCommands = Bastion.Command:New('resto')
local PLACE_EFFLO = false
RestoCommands:Register('efflo', 'Request the engine to place an Efflorescence', function()
PLACE_EFFLO = true
Bastion.Notifications:AddNotification(Efflorescence:GetIcon(), "Efflorescence requested")
end)
local DefaultAPL = Bastion.APL:New('default')
local DamageAPL = Bastion.APL:New('damage')
DamageAPL:AddSpell(
Rake:CastableIf(function(self)
return RakeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
(
not RakeTarget:GetAuras():FindMy(RakeDebuff):IsUp() or
RakeTarget:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 3.6)
end):SetTarget(RakeTarget)
)
DamageAPL:AddSpell(
FerociousBite:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
Player:GetComboPoints() >= 5
end):SetTarget(Target)
)
DamageAPL:AddSpell(
Shred:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
Player:GetComboPoints() < 5
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
Efflorescence:CastableIf(function(self)
return PLACE_EFFLO and Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(None):OnCast(function(self)
local loc = Bastion.UnitManager:FindFriendsCentroid(10, 40)
PLACE_EFFLO = false
self:Click(loc)
end)
)
CatForm:OnCast(function(self)
if not Player:GetAuras():FindMy(Prowl):IsUp() and not Player:IsAffectingCombat() then
Prowl:Cast(Player)
end
end)
DefaultAPL:AddAction(
'cat_form_shift',
function()
if (IsShiftKeyDown()) and not Player:IsMounted() and
not Player:GetAuras():FindMy(CatForm):IsUp() and
not Player:IsCastingOrChanneling() then
CatForm:Cast(Player)
elseif (not IsShiftKeyDown() and Player:IsAffectingCombat()) and Player:GetAuras():FindMy(CatForm):IsUp() then
CancelShapeshiftForm()
end
end
)
DefaultAPL:AddSpell(
NaturesCure:CastableIf(function(self)
return DispelTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(DispelTarget) and DispelTarget:GetAuras():HasAnyDispelableAura(NaturesCure)
end):SetTarget(DispelTarget)
)
DefaultAPL:AddSpell(
Soothe:CastableIf(function(self)
return PurgeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(PurgeTarget) and PurgeTarget:GetAuras():HasAnyStealableAura()
end):SetTarget(PurgeTarget)
)
DefaultAPL:AddSpell(
NaturesSwiftness:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() < 70
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
ConvokeTheSpirits:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(Player) and (Player:GetPartyHPAround(40, 70) >= 2 or Player:GetPartyHPAround(40, 75) >= 3)
and (Flourish:IsKnownAndUsable() or Flourish:GetTimeSinceLastCast() > 10)
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
Flourish:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(Player) and (Player:GetPartyHPAround(40, 70) >= 2 or Player:GetPartyHPAround(40, 75) >= 3) and
(not ConvokeTheSpirits:IsKnownAndUsable() and ConvokeTheSpirits:GetTimeSinceLastCast() > 10) and
WildGrowth:GetTimeSinceLastCast() <= 6
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
NaturesVigil:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and
self:IsInRange(Player) and Flourish:GetTimeSinceLastCast() <= 5
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
AdaptiveSwarm:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Swiftmend:CastableIf(function(self)
return SwiftmendUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(SwiftmendUnit) and
(
SwiftmendUnit:GetHP() <= 60 or
(
Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2
)
)
end):SetTarget(SwiftmendUnit)
)
DefaultAPL:AddSpell(
WildGrowth:CastableIf(function(self)
return WildGrowthUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(WildGrowthUnit) and
(
Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
or
(WildGrowthUnit:GetPartyHPAround(30, 90) >= 3 or WildGrowthUnit:GetPartyHPAround(30, 85) >= 2)
) and
not Player:IsMoving()
end):SetTarget(WildGrowthUnit)
)
DefaultAPL:AddSpell(
Regrowth:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() < 65 and
Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and not Player:IsMoving()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Regrowth:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() < 70 and
(
NaturesSwiftness:GetTimeSinceLastCast() < 2 or Player:GetAuras():FindMy(NaturesSwiftness):IsUp() or
NaturesSwiftness:IsKnownAndUsable()) and not Player:IsMoving() and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
CenarionWard:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() <= 90
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Ironbark:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and Lowest:GetHP() <= 70 and not Lowest:GetAuras():FindMy(CenarionWard):IsUp()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Rejuvenation:CastableIf(function(self)
return RejuvUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(RejuvUnit) and (RejuvUnit:GetHP() <= 94) and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
end):SetTarget(RejuvUnit)
)
DefaultAPL:AddSpell(
Lifebloom:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and
(
not Player:GetAuras():FindMy(LifebloomAura):IsUp() or
Player:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Player:IsAffectingCombat()
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
Lifebloom:CastableIf(function(self)
return Tank:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and
(
not Tank:GetAuras():FindMy(LifebloomAura):IsUp() or
Tank:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Tank:IsAffectingCombat()
end):SetTarget(Tank)
)
DefaultAPL:AddSpell(
Regrowth:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Lowest) and
(
not Player:GetAuras():FindMy(Regrowth):IsUp() and Lowest:GetHP() < 70 or
(Lowest:GetHP() <= 85 and Player:GetAuras():FindMy(ClearCasting):IsUp())) and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and
not Player:IsMoving()
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
Rejuvenation:CastableIf(function(self)
return RejuvUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(RejuvUnit) and (RejuvUnit:GetHP() <= 94 or Player:GetPartyHPAround(40, 90) >= 2) and
not Player:GetAuras():FindMy(SoulOfTheForest):IsUp()
end):SetTarget(RejuvUnit)
)
DefaultAPL:AddSpell(
Moonfire:CastableIf(function(self)
return Explosive:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Explosive)
)
DefaultAPL:AddSpell(
Sunfire:CastableIf(function(self)
return SunfireTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(SunfireTarget) and
(
not SunfireTarget:GetAuras():FindMy(SunfireAura):IsUp() or
SunfireTarget:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 5.4) and
SunfireTarget:IsHostile() and
SunfireTarget:IsAffectingCombat() and Player:GetPP() >= 25
end):SetTarget(SunfireTarget)
)
DefaultAPL:AddSpell(
Moonfire:CastableIf(function(self)
return MoonfireTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(MoonfireTarget) and
(
not MoonfireTarget:GetAuras():FindMy(MoonfireAura):IsUp() or
MoonfireTarget:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 5.4) and
MoonfireTarget:IsHostile() and
MoonfireTarget:IsAffectingCombat() and Player:GetPP() >= 25
end):SetTarget(MoonfireTarget)
)
DefaultAPL:AddSpell(
Starsurge:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Target) and Target:IsHostile() and
Target:IsAffectingCombat() and Player:GetPP() >= 25
end):SetTarget(Target)
)
DefaultAPL:AddSpell(
Wrath:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:CanSee(Target) and not Player:IsMoving() and
Target:IsHostile() and
Target:IsAffectingCombat() and Player:GetPP() >= 25
end):SetTarget(Target)
)
RestoModule:Sync(function()
if IsShiftKeyDown() and Player:GetAuras():FindMy(CatForm):IsUp() then
return DamageAPL:Execute()
end
DefaultAPL:Execute()
end)
Bastion:Register(RestoModule)

File diff suppressed because it is too large Load Diff

@ -0,0 +1,24 @@
local Tinkr, Bastion = ...
local ExampleModule = Bastion.Module:New('ExampleModule')
local Player = Bastion.UnitManager:Get('player')
-- Create a local spellbook
local SpellBook = Bastion.SpellBook:New()
local FlashHeal = SpellBook:GetSpell(2061)
-- Get a global spell (this can collide with other modules, so be careful)
-- This is useful for caching common spells that you might not actually cast, and to avoid needless spell creation inline
local FlashHeal = Bastion.Globals.SpellBook:GetSpell(2061)
local AdvancedMath = Bastion:Import('AdvancedMath')
print(AdvancedMath:Add(1, 2))
ExampleModule:Sync(function()
if Player:GetHP() <= 50 then
FlashHeal:Cast(Player)
end
end)
Bastion:Register(ExampleModule)

@ -0,0 +1,21 @@
local Tinkr, Bastion = ...
local Player = Bastion.UnitManager:Get('player')
Bastion:RegisterLibrary(Bastion.Library:New({
name = 'Dependable',
exports = {
default = function()
local Dependable = {}
Dependable.__index = Dependable
function Dependable:Test(a)
print(a)
end
return Dependable
end,
Test = 5
}
}))

@ -0,0 +1,15 @@
local Tinkr, Bastion = ...
Bastion:RegisterLibrary(Bastion.Library:New({
name = 'Circular',
exports = {
default = function(self)
-- Return default first, and then the remaining exports
local Math, OtherExports = self:Import('AdvancedMath')
print(Math:Add(1, 2))
return 'Circular'
end
}
}))

@ -0,0 +1,25 @@
local Tinkr, Bastion = ...
Bastion:RegisterLibrary(Bastion.Library:New({
name = 'AdvancedMath',
exports = {
default = function(self) -- Function exports are called when the library is loaded
-- Return default first, and then the remaining exports
local Dependable, OtherExports = self:Import('Dependable')
local CircularDependency = self:Import('Circular') -- Causes a circular dependency error
Dependable:Test(OtherExports.Test)
local AdvancedMath = {}
AdvancedMath.__index = AdvancedMath
function AdvancedMath:Add(a, b)
return a + b
end
return AdvancedMath
end
}
}))

@ -1,54 +0,0 @@
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)

@ -1,54 +0,0 @@
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)

@ -1,59 +0,0 @@
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

@ -1,206 +0,0 @@
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)

@ -1,45 +0,0 @@
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

@ -1,25 +0,0 @@
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

@ -1,5 +1,4 @@
-- Document with emmy lua: https://emmylua.github.io/
-- Create an APL trait for the APL class
---@class APLTrait
local APLTrait = {}
@ -55,7 +54,7 @@ end
---@param ... APLTrait
---@return APLActor
function APLActor:AddTraits(...)
for _, trait in ipairs({ ... }) do
for _, trait in ipairs({...}) do
table.insert(self.traits, trait)
end
@ -85,13 +84,11 @@ 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
@ -110,22 +107,23 @@ function APLActor:Execute()
if self:GetActor().spell then
if self:GetActor().condition then
-- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):Cast(self:GetActor().target,
self:GetActor().condition)
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast(
self:GetActor().target, self:GetActor().condition)
else
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast(
self:GetActor().target)
end
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):Cast(self:GetActor().target)
end
if self:GetActor().item then
if self:GetActor().condition then
-- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target,
self:GetActor().condition)
else
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target)
end
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target)
end
if self:GetActor().action then
-- print("Bastion: APL:Execute: Executing action " .. self:GetActor().action)
@ -187,7 +185,11 @@ end
---@param cb fun(...):any
---@return APLActor
function APL:AddVariable(name, cb)
local actor = APLActor:New({ variable = name, cb = cb, _apl = self })
local actor = APLActor:New({
variable = name,
cb = cb,
_apl = self
})
table.insert(self.apl, actor)
return actor
end
@ -197,20 +199,30 @@ end
---@param cb fun(...):any
---@return APLActor
function APL:AddAction(action, cb)
local actor = APLActor:New({ action = action, cb = cb })
local actor = APLActor:New({
action = action,
cb = cb
})
table.insert(self.apl, actor)
return actor
end
-- Add a spell to the APL
---@param spell Spell
---@param condition fun(...):boolean
---@param condition? string|fun(...):boolean
---@return APLActor
function APL:AddSpell(spell, condition)
local castableFunc = spell.CastableIfFunc
local onCastFunc = spell.OnCastFunc
local target = spell:GetTarget()
local actor = APLActor:New({ spell = spell, condition = condition, castableFunc = castableFunc, target = target })
local actor = APLActor:New({
spell = spell,
condition = condition,
castableFunc = castableFunc,
target = target,
onCastFunc = onCastFunc
})
table.insert(self.apl, actor)
@ -219,13 +231,18 @@ end
-- Add an item to the APL
---@param item Item
---@param condition fun(...):boolean
---@param condition? fun(...):boolean
---@return APLActor
function APL:AddItem(item, condition)
local usableFunc = item.UsableIfFunc
local target = item:GetTarget()
local actor = APLActor:New({ item = item, condition = condition, usableFunc = usableFunc, target = target })
local actor = APLActor:New({
item = item,
condition = condition,
usableFunc = usableFunc,
target = target
})
table.insert(self.apl, actor)
@ -240,7 +257,10 @@ function APL:AddAPL(apl, condition)
if not condition then
error("Bastion: APL:AddAPL: No condition for APL " .. apl.name)
end
local actor = APLActor:New({ apl = apl, condition = condition })
local actor = APLActor:New({
apl = apl,
condition = condition
})
table.insert(self.apl, actor)
return actor
end
@ -248,14 +268,12 @@ end
-- Execute the APL
function APL:Execute()
for _, actor in ipairs(self.apl) do
if actor:HasTraits() and actor:Evaluate() then
if actor:Execute() then
print("BREAQK", actor)
if actor:HasTraits() then
if actor:Evaluate() and actor:Execute() then
break
end
else
if actor:Execute() then
print("BREAQK", actor)
break
end
end
@ -267,7 +285,10 @@ end
---@param condition fun(...):boolean
---@return APLActor
function APL:AddSequence(sequencer, condition)
local actor = APLActor:New({ sequencer = sequencer, condition = condition })
local actor = APLActor:New({
sequencer = sequencer,
condition = condition
})
table.insert(self.apl, actor)
return actor
end

@ -1,5 +1,4 @@
-- Document with emmy lua: https://emmylua.github.io/
local Tinkr, Bastion = ...
-- Create a new Aura class
@ -66,19 +65,52 @@ function Aura:New(unit, index, type)
timeMod = 0,
index = nil,
type = nil,
type = nil
}
if self.aura.spellId then
Bastion.SpellBook:GetSpell(self.aura.spellId)
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
end
return self
end
local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal,
spellId, canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod = UnitAura(unit:GetOMToken(), index,
type)
if C_UnitAuras.GetAuraDataByIndex then
local unitAuraInfo = C_UnitAuras.GetAuraDataByIndex(unit:GetOMToken(), index, type)
if not unitAuraInfo then
local self = setmetatable({}, Aura)
self.aura = {
name = nil,
icon = nil,
count = 0,
dispelType = nil,
duration = 0,
expirationTime = 0,
source = nil,
isStealable = false,
nameplateShowPersonal = false,
spellId = 0,
canApplyAura = false,
isBossDebuff = false,
castByPlayer = false,
nameplateShowAll = false,
timeMod = 0,
index = nil,
type = nil
}
if self.aura.spellId then
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
end
return self
end
return Aura:CreateFromUnitAuraInfo(unitAuraInfo)
end
local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal, spellId,
canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod = UnitAura(unit:GetOMToken(), index, type)
local self = setmetatable({}, Aura)
self.aura = {
@ -100,10 +132,10 @@ function Aura:New(unit, index, type)
auraInstanceID = nil,
index = index,
type = type,
type = type
}
if self.aura.spellId then
Bastion.SpellBook:GetSpell(self.aura.spellId)
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
end
return self
end
@ -132,11 +164,11 @@ function Aura:CreateFromUnitAuraInfo(unitAuraInfo)
auraInstanceID = unitAuraInfo.auraInstanceID,
index = nil,
type = unitAuraInfo.isHarmful and "HARMFUL" or "HELPFUL",
type = unitAuraInfo.isHarmful and "HARMFUL" or "HELPFUL"
}
-- Register spell in spellbook
Bastion.SpellBook:GetSpell(self.aura.spellId)
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
return self
end
@ -239,7 +271,7 @@ end
-- Get the auras spell id
---@return Spell
function Aura:GetSpell()
return Bastion.SpellBook:GetSpell(self.aura.spellId)
return Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
end
-- Get the auras can apply aura status

@ -53,7 +53,7 @@ function AuraTable:OnUpdate(auras)
if updatedAuras and #updatedAuras > 0 then
for i = 1, #updatedAuras do
local id = updatedAuras[i]
local newAura = C_UnitAuras_GetAuraDataByAuraInstanceID(self.unit:GetOMToken(), id);
local newAura = C_UnitAuras.GetAuraDataByAuraInstanceID(self.unit:GetOMToken(), id);
if newAura then
local aura = Bastion.Aura:CreateFromUnitAuraInfo(newAura)
self:AddOrUpdateAuraInstanceID(aura:GetAuraInstanceID(), aura)
@ -118,7 +118,7 @@ end
-- Get a units buffs
---@return nil
function AuraTable:GetUnitBuffs()
if Tinkr.classic then
if Tinkr.classic or Tinkr.era then
for i = 1, 40 do
local aura = Bastion.Aura:New(self.unit, i, 'HELPFUL')
@ -157,7 +157,7 @@ end
-- Get a units debuffs
---@return nil
function AuraTable:GetUnitDebuffs()
if Tinkr.classic then
if Tinkr.classic or Tinkr.era then
for i = 1, 40 do
local aura = Bastion.Aura:New(self.unit, i, 'HARMFUL')
@ -275,12 +275,11 @@ function AuraTable:Find(spell)
end
for k, a in pairs(aurasub) do
print(a)
if a ~= nil then
if a:IsUp() then -- Handle expired and non refreshed dropoffs not coming in UNIT_AURA
return a
else
if not Tinkr.classic then
if not Tinkr.classic or Tinkr.era then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -306,7 +305,7 @@ function AuraTable:FindMy(spell)
if a:IsUp() then -- Handle expired and non refreshed dropoffs not coming in UNIT_AURA
return a
else
if not Tinkr.classic then
if not Tinkr.classic or Tinkr.era then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -335,7 +334,7 @@ function AuraTable:FindFrom(spell, source)
return a
end
else
if not Tinkr.classic then
if not Tinkr.classic or Tinkr.era then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -363,7 +362,7 @@ function AuraTable:FindTheirs(spell)
return a
end
else
if not Tinkr.classic then
if not Tinkr.classic or Tinkr.era then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -385,6 +384,351 @@ function AuraTable:FindAny(spell)
return self:FindMy(spell)
end
-- FindAnyOf
---@param spells List
---@return Aura
function AuraTable:FindAnyOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindAny(cur)
if aura:IsValid() then
return aura, true
end
return acc
end, Bastion.Aura:New())
end
-- FindAnyOfMy
---@param spells List
---@return Aura
function AuraTable:FindAnyOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
if aura:IsValid() then
return aura, true
end
return acc
end, Bastion.Aura:New())
end
-- FindAnyOfTheirs
---@param spells List
---@return Aura
function AuraTable:FindAnyOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
if aura:IsValid() then
return aura, true
end
return acc
end, Bastion.Aura:New())
end
-- FindAnyFrom
---@param spells List
---@param source Unit
---@return Aura
function AuraTable:FindAnyFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
if aura:IsValid() then
return aura, true
end
return acc
end, Bastion.Aura:New())
end
-- FindLongestOf
---@param spells List
---@return Aura
function AuraTable:FindLongestOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() > acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLongestOfMy
---@param spells List
---@return Aura
function AuraTable:FindLongestOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() > acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLongestOfTheirs
---@param spells List
---@return Aura
function AuraTable:FindLongestOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() > acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLongestOfFrom
---@param spells List
---@param source Unit
---@return Aura
function AuraTable:FindLongestOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() > acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindShortestOf
---@param spells List
---@return Aura
function AuraTable:FindShortestOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() < acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindShortestOfMy
---@param spells List
---@return Aura
function AuraTable:FindShortestOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() < acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindShortestOfTheirs
---@param spells List
---@return Aura
function AuraTable:FindShortestOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() < acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindShortestOfFrom
---@param spells List
---@param source Unit
---@return Aura
function AuraTable:FindShortestOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetRemainingTime() < acc:GetRemainingTime() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindMostOf
---@param spells List
---@return Aura
function AuraTable:FindMostOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() > acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindMostOfMy
---@param spells List
---@return Aura
function AuraTable:FindMostOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() > acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindMostOfTheirs
---@param spells List
---@return Aura
function AuraTable:FindMostOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() > acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindMostOfFrom
---@param spells List
---@param source Unit
---@return Aura
function AuraTable:FindMostOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() > acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLeastOf
---@param spells List
---@return Aura
function AuraTable:FindLeastOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() < acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLeastOfMy
---@param spells List
---@return Aura
function AuraTable:FindLeastOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() < acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLeastOfTheirs
---@param spells List
---@return Aura
function AuraTable:FindLeastOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() < acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- FindLeastOfFrom
---@param spells List
---@param source Unit
---@return Aura
function AuraTable:FindLeastOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
if aura:IsValid() then
if not acc:IsValid() then
return aura
end
if aura:GetCount() < acc:GetCount() then
return aura
end
end
return acc
end, Bastion.Aura:New())
end
-- Has any stealable aura
---@return boolean
function AuraTable:HasAnyStealableAura()
@ -422,21 +766,4 @@ 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

@ -18,12 +18,21 @@ function Class:__index(k)
return response
end
---@class Class
---@field class Class.class
---@class Class.class
---@field locale string
---@field name string
---@field id number
-- Constructor
---@param locale string
---@param name string
---@param id number
function Class:New(locale, name, id)
local self = setmetatable({}, Class)
self.class = {
locale = locale,
name = name,
@ -50,10 +59,16 @@ function Class:GetID()
return self.class.id
end
---@class ColorMixin
---@field r number
---@field g number
---@field b number
-- Return the classes color
---@return ColorMixin classColor
function Class:GetColor()
return C_ClassColor.GetClassColor(self.class.name)
end
return Class

@ -1,6 +1,13 @@
-- Create a wow command handler class
---@class Command
---@field command string
---@field commands Command.commands[]
local Command = {}
---@class Command.commands
---@field helpmsg string
---@field cb fun(args: table)
Command.__index = Command
---@return string

@ -1,201 +0,0 @@
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

@ -1,193 +0,0 @@
---@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

@ -1,662 +0,0 @@
---@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

@ -1,416 +0,0 @@
---@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

@ -51,10 +51,16 @@ function Item:New(id)
self.ItemID = id
-- Register spell in spellbook
local name, spellID = GetItemSpell(self:GetID())
local name, spellID
if C_Item.GetItemSpell then
name, spellID = C_Item.GetItemSpell(self:GetID())
else
name, spellID = GetItemSpell(self:GetID())
end
if spellID then
self.spellID = spellID
Bastion.SpellBook:GetSpell(spellID)
Bastion.Globals.SpellBook:GetSpell(spellID)
end
return self
@ -69,18 +75,27 @@ end
-- Get the Items name
---@return string
function Item:GetName()
if C_Item.GetItemInfo then
return C_Item.GetItemInfo(self:GetID())
end
return GetItemInfo(self:GetID())
end
-- Get the Items icon
---@return number
function Item:GetIcon()
if C_Item.GetItemIconByID then
return C_Item.GetItemIconByID(self:GetID())
end
return select(3, GetItemInfo(self:GetID()))
end
-- Get the Items cooldown
---@return number
function Item:GetCooldown()
if C_Item.GetItemCooldown then
return select(2, C_Item.GetItemCooldown(self:GetID()))
end
return select(2, C_Container.GetItemCooldown(self:GetID()))
end
@ -105,6 +120,10 @@ end
-- Get the Items cooldown remaining
---@return number
function Item:GetCooldownRemaining()
if C_Item.GetItemCooldown then
local start, duration = C_Item.GetItemCooldown(self:GetID())
return start + duration - GetTime()
end
local start, duration = C_Container.GetItemCooldown(self:GetID())
return start + duration - GetTime()
end
@ -131,7 +150,11 @@ function Item:Use(unit, condition)
self.wasLooking = IsMouselooking()
-- Use the Item
UseItemByName(self:GetName(), unit:GetOMToken())
if C_Item.UseItemByName then
C_Item.UseItemByName(self:GetName(), unit:GetOMToken())
else
UseItemByName(self:GetName(), unit:GetOMToken())
end
Bastion:Debug("Using", self)
@ -161,18 +184,28 @@ end
-- Check if the Item is known
---@return boolean
function Item:IsEquipped()
if C_Item.IsEquippedItem then
return C_Item.IsEquippedItem(self:GetID())
end
return IsEquippedItem(self:GetID())
end
-- Check if the Item is on cooldown
---@return boolean
function Item:IsOnCooldown()
if C_Item.GetItemCooldown then
return select(2, C_Item.GetItemCooldown(self:GetID())) > 0
end
return select(2, C_Container.GetItemCooldown(self:GetID())) > 0
end
-- Check if the Item is usable
---@return boolean
function Item:IsUsable()
if C_Item.IsUsableItem then
local usable, noMana = C_Item.IsUsableItem(self:GetID())
return usable or usableExcludes[self:GetID()]
end
local usable, noMana = IsUsableItem(self:GetID())
return usable or usableExcludes[self:GetID()]
end
@ -187,6 +220,9 @@ end
-- Is equippable
---@return boolean
function Item:IsEquippable()
if C_Item.IsEquippableItem then
return C_Item.IsEquippableItem(self:GetID())
end
return IsEquippableItem(self:GetID())
end
@ -239,7 +275,7 @@ function Item:Click(x, y, z)
if type(x) == 'table' then
x, y, z = x.x, x.y, x.z
end
if IsItemPending() == 64 then
if IsSpellPending() == 64 then
MouselookStop()
Click(x, y, z)
if self:GetWasLooking() then
@ -302,7 +338,7 @@ end
-- Get the last use time
---@return number
function Item:GetLastUseTime()
return Bastion.SpellBook:GetSpell(self:GetID()):GetLastCastTime()
return Bastion.Globals.SpellBook:GetSpell(self:GetID()):GetLastCastTime()
end
-- Get time since last use
@ -317,6 +353,9 @@ end
-- Get the Items charges
---@return number
function Item:GetCharges()
if C_Item.GetItemCount then
return C_Item.GetItemCount(self:GetID())
end
return GetItemCharges(self:GetID())
end
@ -430,7 +469,7 @@ end
---@return Spell | nil
function Item:GetSpell()
if self.spellID then
return Bastion.SpellBook:GetSpell(self.spellID)
return Bastion.Globals.SpellBook:GetSpell(self.spellID)
end
return nil

@ -0,0 +1,115 @@
local Tinkr, Bastion = ...
---@class Library
---@field name string
---@field dependencies table
---@field exports table
---@field resolved table
local Library = {
name = nil,
dependencies = {},
exports = {
default = function()
return nil
end
},
resolved = nil
}
Library.__index = Library
---@param name string
---@param library table
---@return Library
function Library:New(library)
local self = {
name = library.name or nil,
dependencies = {},
exports = library.exports or {
default = function()
return nil
end
},
resolved = nil
}
self = setmetatable(self, Library)
return self
end
function Library:ResolveExport(export)
if type(export) == 'function' then
return export(self)
end
return export
end
function Library:Resolve()
if not self.exports then
error("Library " .. self.name .. " has no exports")
end
if self.resolved then
if self.exports.default then
return self.resolved[1], self.resolved[2]
end
return unpack(self.resolved)
end
if self.exports.default then
-- return default first if it exists
local default = self.exports.default
local remaining = {}
for k, v in pairs(self.exports) do
if k ~= 'default' then
remaining[k] = self:ResolveExport(v)
end
end
self.resolved = {self:ResolveExport(default), remaining}
return self.resolved[1], self.resolved[2]
end
self.resolved = {}
for k, v in pairs(self.exports) do
self.resolved[k] = self:ResolveExport(v)
end
return unpack(self.resolved)
end
function Library:DependsOn(other)
for _, dependency in pairs(self.dependencies) do
if dependency == other then
return true
end
end
return false
end
---@param library string
function Library:Import(library)
local lib = Bastion:GetLibrary(library)
if not lib then
error("Library " .. library .. " does not exist")
end
if not table.contains(self.dependencies, library) then
table.insert(self.dependencies, library)
end
if lib:DependsOn(self.name) then
error("Circular dependency detected between " .. self.name .. " and " .. library)
end
return lib:Resolve()
end
return Library

@ -1,5 +1,3 @@
local Tinkr, Bastion = ...
---@class List
local List = {
-- Add overload
@ -90,9 +88,9 @@ function List:each(callback)
end
---@param callback fun(value: any): boolean
---@return boolean
---@return List
function List:map(callback)
local newList = List.new()
local newList = List:New()
for _, v in ipairs(self._list) do
newList:push(callback(v))
end
@ -100,9 +98,9 @@ function List:map(callback)
end
---@param callback fun(value: any): boolean
---@return boolean
---@return List
function List:filter(callback)
local newList = List.new()
local newList = List:New()
for _, v in ipairs(self._list) do
if callback(v) then
newList:push(v)
@ -111,13 +109,17 @@ function List:filter(callback)
return newList
end
---@param callback fun(value: any): boolean
---@param callback fun(result: any, value: any): boolean
---@param initialValue any
---@return boolean
function List:reduce(callback, initialValue)
local result = initialValue
local done = false
for _, v in ipairs(self._list) do
result = callback(result, v)
result, done = callback(result, v)
if done then
break
end
end
return result
end
@ -144,7 +146,7 @@ function List:findIndex(callback)
return nil
end
---@param callback fun(value: any): boolean
---@param callback fun(...): boolean
---@return nil
function List:sort(callback)
table.sort(self._list, callback)
@ -152,7 +154,7 @@ end
---@return List
function List:reverse()
local newList = List.new()
local newList = List:New()
for i = #self._list, 1, -1 do
newList:push(self._list[i])
end
@ -161,7 +163,7 @@ end
---@return List
function List:clone()
local newList = List.new()
local newList = List:New()
for _, v in ipairs(self._list) do
newList:push(v)
end
@ -171,7 +173,7 @@ end
---@param list List
---@return List
function List:concat(list)
local newList = List.new()
local newList = List:New()
for _, v in ipairs(self._list) do
newList:push(v)
end

@ -0,0 +1,585 @@
local Tinkr, Bastion = ...
---@class MythicPlusUtils
local MythicPlusUtils = {
debuffLogging = false,
castLogging = false,
random = '',
loggedCasts = {},
loggedDebuffs = {},
kickList = {},
aoeBosses = {}
}
MythicPlusUtils.__index = MythicPlusUtils
---@return MythicPlusUtils
function MythicPlusUtils:New()
local self = setmetatable({}, MythicPlusUtils)
---@diagnostic disable-next-line: assign-type-mismatch
self.random = math.random(1000000, 9999999)
self.aoeBosses = {
[196482] = true,
[188252] = true,
[186644] = true,
[104217] = true,
}
self.tankBusters = {
[397931] = true, -- https://www.wowhead.com/spell=397931/dark-claw
[396019] = true, -- https://www.wowhead.com/spell=396019/staggering-blow
[372730] = true, -- https://www.wowhead.com/spell=372730/crushing-smash
[395303] = true, -- https://www.wowhead.com/spell=395303/thunder-jaw
[392395] = true, -- https://www.wowhead.com/spell=392395/thunder-jaw
[372858] = true, -- https://www.wowhead.com/spell=372858/searing-blows
[372859] = true, -- https://www.wowhead.com/spell=372859/searing-blows
[387135] = true, -- https://www.wowhead.com/spell=387135/arcing-strike
[388801] = true, -- https://www.wowhead.com/spell=388801/mortal-strike
[387826] = true, -- https://www.wowhead.com/spell=387826/heavy-slash
[370764] = true, -- https://www.wowhead.com/spell=370764/piercing-shards
[377105] = true, -- https://www.wowhead.com/spell=377105/ice-cutter
[388911] = true, -- https://www.wowhead.com/spell=388911/severing-slash
[388912] = true, -- https://www.wowhead.com/spell=388912/severing-slash
[199050] = true, -- https://www.wowhead.com/spell=199050/mortal-hew
[164907] = true, -- https://www.wowhead.com/spell=164907/void-slash
[377991] = true, -- https://www.wowhead.com/spell=377991/storm-slash
[376997] = true, -- https://www.wowhead.com/spell=376997/savage-peck
[192018] = true, -- https://www.wowhead.com/spell=192018/shield-of-light
[106823] = true, -- https://www.wowhead.com/spell=106823/serpent-strike
[106841] = true, -- https://www.wowhead.com/spell=106841/jade-serpent-strike
[381512] = true, -- https://www.wowhead.com/spell=381512/stormslam
[381514] = true, -- https://www.wowhead.com/spell=381514/stormslam
[381513] = true, -- https://www.wowhead.com/spell=381513/stormslam
[381515] = true, -- https://www.wowhead.com/spell=381515/stormslam
[372222] = true, -- https://www.wowhead.com/spell=372222/arcane-cleave
[385958] = true, -- https://www.wowhead.com/spell=385958/arcane-expulsion
[382836] = true, -- https://www.wowhead.com/spell=382836/brutalize
[376827] = true, -- https://www.wowhead.com/spell=376827/conductive-strike not sure if we defensive on these or the other strike NO final boss
[375937] = true, -- https://www.wowhead.com/spell=375937/rending-strike not sure if we defensive on these or the other strike NO final boss
[198888] = true, -- https://www.wowhead.com/spell=198888/lightning-breath
[384978] = true, -- https://www.wowhead.com/spell=384978/dragon-strike
[388923] = true, -- https://www.wowhead.com/spell=388923/burst-forth
}
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
}
},
[195696] = {
[387125] = {
true, false, false
}
}
}
Bastion.Globals.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.Globals.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
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(spellID)
name = info and info.name or ''
else
name = GetSpellInfo(spellID)
end
self.loggedCasts[spellID] = true
WriteFile('bastion-MPlusCasts-' .. self.random .. '.lua', [[
CastName: ]] .. name .. [[
CastID: ]] .. spellID .. "\n" .. [[
]], true)
end)
Bastion.Globals.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
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(spellID)
name = info and info.name or ''
else
name = GetSpellInfo(spellID)
end
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
---@param unit Unit
---@return boolean
function MythicPlusUtils:IsAOEBoss(unit)
return self.aoeBosses[unit:GetID()]
end
return MythicPlusUtils

@ -1,6 +1,11 @@
local Tinkr, Bastion = ...
---@class ObjectManager
---@field _lists table
---@field enemies List
---@field friends List
---@field activeEnemies List
---@field explosives List
local ObjectManager = {}
ObjectManager.__index = ObjectManager
@ -20,7 +25,7 @@ end
-- Register a custom list with a callback
---@param name string
---@param cb function
---@return List
---@return List | false
function ObjectManager:RegisterList(name, cb)
if self._lists[name] then
return false
@ -88,6 +93,7 @@ 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

@ -1,6 +1,8 @@
-- Create a sequencer class that takes a table of actions and executes them in order
---@class Sequencer
---@field resetCondition fun(): boolean
---@field abortCondition fun(): boolean
---@field actions fun(sequencer: Sequencer)[]
local Sequencer = {}
Sequencer.__index = Sequencer
@ -42,12 +44,10 @@ end
---@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

@ -12,10 +12,11 @@ local Spell = {
lastCastAt = false,
conditions = {},
target = false,
release_at = false
}
local usableExcludes = {
[18562] = true,
[18562] = true
}
function Spell:__index(k)
@ -56,6 +57,12 @@ function Spell:New(id)
return self
end
-- Duplicator
---@return Spell
function Spell:Fresh()
return Spell:New(self:GetID())
end
-- Get the spells id
---@return number
function Spell:GetID()
@ -73,21 +80,70 @@ end
-- Get the spells name
---@return string
function Spell:GetName()
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(self:GetID())
return info and info.name or nil
end
return GetSpellInfo(self:GetID())
end
-- Get the spells icon
---@return number
function Spell:GetIcon()
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(self:GetID())
return info and info.iconID or nil
end
return select(3, GetSpellInfo(self:GetID()))
end
-- Get the spells cooldown
---@return number
function Spell:GetCooldown()
if C_Spell.GetSpellCooldown then
local info = C_Spell.GetSpellCooldown(self:GetID())
return info and info.duration or nil
end
return select(2, GetSpellCooldown(self:GetID()))
end
-- Get the full cooldown (time until all charges are available)
---@return number
function Spell:GetFullRechargeTime()
if C_Spell.GetSpellCooldown then
local info = C_Spell.GetSpellCooldown(self:GetID())
if info.isEnabled == 0 then
return 0
end
local chargeInfo = C_Spell.GetSpellCharges(self:GetID())
if chargeInfo.currentCharges == chargeInfo.maxCharges then
return 0
end
if chargeInfo.currentCharges == 0 then
return info.startTime + info.duration - GetTime()
end
return chargeInfo.cooldownStartTime + chargeInfo.cooldownDuration - GetTime()
end
local start, duration, enabled = GetSpellCooldown(self:GetID())
if enabled == 0 then
return 0
end
local charges, maxCharges, chargeStart, chargeDuration = GetSpellCharges(self:GetID())
if charges == maxCharges then
return 0
end
if charges == 0 then
return start + duration - GetTime()
end
return chargeStart + chargeDuration - GetTime()
end
-- Return the castable function
---@return fun(self:Spell):boolean
function Spell:GetCastableFunction()
@ -109,10 +165,23 @@ end
-- Get the spells cooldown remaining
---@return number
function Spell:GetCooldownRemaining()
if C_Spell.GetSpellCooldown then
local info = C_Spell.GetSpellCooldown(self:GetID())
return info and info.startTime + info.duration - GetTime() or nil
end
local start, duration = GetSpellCooldown(self:GetID())
return start + duration - GetTime()
end
-- Get the spell count
---@return number
function Spell:GetCount()
if C_Spell.GetSpellCastCount then
return C_Spell.GetSpellCastCount(self:GetID())
end
return GetSpellCount(self:GetID())
end
-- On cooldown
---@return boolean
function Spell:OnCooldown()
@ -128,11 +197,15 @@ end
-- Cast the spell
---@param unit Unit
---@param condition? string
---@param condition? string|function
---@return boolean
function Spell:Cast(unit, condition)
if condition and not self:EvaluateCondition(condition) then
return false
if condition then
if type(condition) == "string" and not self:EvaluateCondition(condition) then
return false
elseif type(condition) == "function" and not condition(self) then
return false
end
end
if not self:Castable() then
@ -215,6 +288,8 @@ end
-- Check if the spell is known
---@return boolean
function Spell:IsKnown()
local IsSpellKnown = C_Spell.IsSpellKnown and C_Spell.IsSpellKnown or IsSpellKnown
local IsPlayerSpell = C_Spell.IsPlayerSpell and C_Spell.IsPlayerSpell or IsPlayerSpell
local isKnown = IsSpellKnown(self:GetID())
local isPlayerSpell = IsPlayerSpell(self:GetID())
return isKnown or isPlayerSpell
@ -223,12 +298,20 @@ end
-- Check if the spell is on cooldown
---@return boolean
function Spell:IsOnCooldown()
if C_Spell.GetSpellCooldown then
local info = C_Spell.GetSpellCooldown(self:GetID())
return info and info.duration > 0
end
return select(2, GetSpellCooldown(self:GetID())) > 0
end
-- Check if the spell is usable
---@return boolean
function Spell:IsUsable()
if C_Spell.IsSpellUsable then
local usable, noMana = C_Spell.IsSpellUsable(self:GetID())
return usable or usableExcludes[self:GetID()] and not noMana
end
local usable, noMana = IsUsableSpell(self:GetID())
return usable or usableExcludes[self:GetID()] and not noMana
end
@ -280,9 +363,9 @@ function Spell:GetWasLooking()
end
-- Click the spell
---@param x number
---@param y number
---@param z number
---@param x number|Vector3
---@param y? number
---@param z? number
---@return boolean
function Spell:Click(x, y, z)
if type(x) == 'table' then
@ -313,13 +396,29 @@ end
-- Check if the spell is castable and cast it
---@return boolean
function Spell:HasRange()
if C_Spell.SpellHasRange then
return C_Spell.SpellHasRange(self:GetID())
end
return SpellHasRange(self:GetName())
end
-- Get the range of the spell
---@return number
---@return number
function Spell:GetRange()
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(self:GetID())
return info and info.minRange or nil, info and info.maxRange or nil
end
local name, rank, icon, castTime, minRange, maxRange, spellID, originalIcon = GetSpellInfo(self:GetID())
return maxRange, minRange
end
-- Check if the spell is in range of the unit
---@param unit Unit
---@return boolean
function Spell:IsInRange(unit)
local IsSpellInRange = C_Spell.IsSpellInRange and C_Spell.IsSpellInRange or IsSpellInRange
local hasRange = self:HasRange()
local inRange = IsSpellInRange(self:GetName(), unit:GetOMToken())
@ -361,16 +460,50 @@ end
-- Get the spells charges
---@return number
function Spell:GetCharges()
if C_Spell.GetSpellCharges then
local info = C_Spell.GetSpellCharges(self:GetID())
return info and info.currentCharges or nil
end
return GetSpellCharges(self:GetID())
end
function Spell:GetMaxCharges()
if C_Spell.GetSpellCharges then
local info = C_Spell.GetSpellCharges(self:GetID())
return info and info.maxCharges or nil
end
return select(2, GetSpellCharges(self:GetID()))
end
function Spell:GetCastLength()
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(self:GetID())
return info and info.castTime or nil
end
return select(4, GetSpellInfo(self:GetID()))
end
-- Get the spells charges
---@return number
function Spell:GetChargesFractional()
if C_Spell.GetSpellCharges then
local info = C_Spell.GetSpellCharges(self:GetID())
if info.currentCharges == info.maxCharges then
return info.maxCharges
end
if info.currentCharges == 0 then
return 0
end
local timeSinceStart = GetTime() - info.cooldownStartTime
local timeLeft = info.cooldownDuration - timeSinceStart
local timePerCharge = info.cooldownDuration / info.maxCharges
local chargesFractional = info.currentCharges + (timeLeft / timePerCharge)
return chargesFractional
end
local charges, maxCharges, start, duration = GetSpellCharges(self:GetID())
if charges == maxCharges then
@ -392,22 +525,14 @@ end
-- Get the spells charges remaining
---@return number
function Spell:GetChargesRemaining()
if C_Spell.GetSpellCharges then
local info = C_Spell.GetSpellCharges(self:GetID())
return info and info.currentCharges or nil
end
local charges, maxCharges, start, duration = GetSpellCharges(self:GetID())
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
@ -473,32 +598,30 @@ end
---@return boolean
function Spell:IsMagicDispel()
return ({
[88423] = true
})[self:GetID()]
[88423] = true
})[self:GetID()]
end
-- IsCurseDispel
---@return boolean
function Spell:IsCurseDispel()
return ({
[88423] = true
})[self:GetID()]
[88423] = true
})[self:GetID()]
end
-- IsPoisonDispel
---@return boolean
function Spell:IsPoisonDispel()
return ({
[88423] = true
})[self:GetID()]
[88423] = true
})[self:GetID()]
end
-- IsDiseaseDispel
---@return boolean
function Spell:IsDiseaseDispel()
return ({
})[self:GetID()]
return ({})[self:GetID()]
end
-- IsSpell
@ -508,37 +631,21 @@ 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
-- GetCost
---@return number
function Spell:GetCost()
if C_Spell.GetSpellPowerCost then
local info = C_Spell.GetSpellPowerCost(self:GetID())
return info and info.cost or 0
end
return inFlight
local cost = GetSpellPowerCost(self:GetID())
return cost and cost.cost or 0
end
---@param unit Unit
-- IsFree
---@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
function Spell:IsFree()
return self:GetCost() == 0
end
return Spell

@ -27,16 +27,31 @@ end
---@return Spell, ... Spell
function SpellBook:GetSpells(...)
local spells = {}
for _, id in ipairs({ ... }) do
for _, id in ipairs({...}) do
table.insert(spells, self:GetSpell(id))
end
return unpack(spells)
end
---@param ... number[]
---@return List
function SpellBook:GetList(...)
local spells = {}
for _, id in ipairs({...}) do
table.insert(spells, self:GetSpell(id))
end
return Bastion.List:New(spells)
end
---@param name string
---@return Spell
function SpellBook:GetSpellByName(name)
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(name)
return self:GetSpell(info.spellID)
end
local _, rank, icon, castTime, minRange, maxRange, spellID, originalIcon = GetSpellInfo(name)
return self:GetSpell(spellID)
end

@ -1,104 +0,0 @@
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

@ -13,7 +13,7 @@ local Unit = {
last_off_attack = 0,
last_main_attack = 0,
last_combat_time = 0,
ttd_ticker = 0,
ttd_ticker = false,
ttd = 0,
id = false,
}
@ -114,6 +114,24 @@ function Unit:GetHP()
return self:GetHealth() / self:GetMaxHealth() * 100
end
-- Get realized health
---@return number
function Unit:GetRealizedHealth()
return self:GetHealth() - self:GetHealAbsorbedHealth()
end
-- get realized health percentage
---@return number
function Unit:GetRealizedHP()
return self:GetRealizedHealth() / self:GetMaxHealth() * 100
end
-- Get the abosorbed unit health
---@return number
function Unit:GetHealAbsorbedHealth()
return UnitGetTotalHealAbsorbs(self:GetOMToken())
end
-- Get the units health deficit
---@return number
function Unit:GetHealthPercent()
@ -372,25 +390,62 @@ function Unit:IsCasting()
return UnitCastingInfo(self:GetOMToken()) ~= nil
end
function Unit:GetTimeCastIsAt(percent)
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(
self:GetOMToken())
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self
:GetOMToken())
end
if name and startTimeMS and endTimeMS then
local castLength = endTimeMS - startTimeMS
local startTime = startTimeMS / 1000
local timeUntil = (castLength / 1000) * (percent / 100)
return startTime + timeUntil
end
return 0
end
-- Get Casting or channeling spell
---@return Spell | nil
function Unit:GetCastingOrChannelingSpell()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(
self
.unit)
self:GetOMToken())
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit
:unit())
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self
:GetOMToken())
end
if name then
return Bastion.SpellBook:GetSpell(spellId)
return Bastion.Globals.SpellBook:GetSpell(spellId)
end
return nil
end
-- Get the end time of the cast or channel
---@return number
function Unit:GetCastingOrChannelingEndTime()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(
self:GetOMToken())
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self
:GetOMToken())
end
if name then
return endTimeMS / 1000
end
return 0
end
-- Check if the unit is channeling a spell
---@return boolean
function Unit:IsChanneling()
@ -413,12 +468,11 @@ end
---@return number
function Unit:GetChannelOrCastPercentComplete()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(
self
.unit)
self:GetOMToken())
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit
:unit())
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self
:GetOMToken())
end
if name and startTimeMS and endTimeMS then
@ -435,12 +489,11 @@ end
---@return boolean
function Unit:IsInterruptible()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(
self
.unit)
self:GetOMToken())
if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self.unit
:unit())
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self
:GetOMToken())
end
if name then
@ -481,7 +534,7 @@ function Unit:GetEnemies(range)
local count = 0
Bastion.UnitManager:EnumEnemies(function(unit)
if not self:IsUnit(unit) and unit:GetDistance(self) <= range and unit:IsAlive() and self:CanSee(unit) and
if not self:IsUnit(unit) and self:IsWithinCombatDistance(unit, range) and unit:IsAlive() and self:CanSee(unit) and
unit:IsEnemy() then
count = count + 1
end
@ -503,7 +556,7 @@ function Unit:GetMeleeAttackers()
Bastion.UnitManager:EnumEnemies(function(unit)
if not self:IsUnit(unit) and unit:IsAlive() and self:CanSee(unit) and
self:InMelee(unit) and unit:IsEnemy() and unit:InCombatOdds() > 80 then
self:InMelee(unit) and unit:IsEnemy() then
count = count + 1
end
end)
@ -543,7 +596,7 @@ end
---@param unit Unit | nil
---@return number
function Unit:GetComboPoints(unit)
if Tinkr.classic then
if Tinkr.classic or Tinkr.era then
if not unit then
return 0
end
@ -554,7 +607,7 @@ end
---@return number
function Unit:GetComboPointsMax()
if Tinkr.classic then
if Tinkr.classic or Tinkr.era then
return 5
end
return UnitPowerMax(self:GetOMToken(), 4)
@ -564,7 +617,7 @@ end
---@param unit Unit | nil
---@return number
function Unit:GetComboPointsDeficit(unit)
if Tinkr.classic then
if Tinkr.classic or Tinkr.era then
return self:GetComboPointsMax() - self:GetComboPoints(unit)
end
return self:GetComboPointsMax() - self:GetComboPoints()
@ -582,23 +635,18 @@ end
---@return boolean
function Unit:IsTanking(unit)
local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(),
unit:GetOMToken())
unit:GetOMToken())
return isTanking
end
-- IsFacing
---@param unit Unit
---@param fov? number
---@return boolean
function Unit:IsFacing(unit, fov)
function Unit:IsFacing(unit)
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
@ -778,9 +826,9 @@ function Unit:StartTTDTicker()
end
self.ttd_ticker = C_Timer.NewTicker(0.5, function()
local timeto = self:PredictTime(0) - GetTime()
self.ttd = timeto
end)
local timeto = self:PredictTime(0) - GetTime()
self.ttd = timeto
end)
end
-- Time until death
@ -790,7 +838,7 @@ function Unit:TimeToDie()
self.regression_history = {}
if self.ttd_ticker then
self.ttd_ticker:Cancel()
self.ttd_ticker = nil
self.ttd_ticker = false
end
return 0
end
@ -878,13 +926,12 @@ end
-- IsStealthed
---@return boolean
function Unit:IsStealthed()
local Stealth = Bastion.SpellBook:GetSpell(1784)
local Vanish = Bastion.SpellBook:GetSpell(1856)
local ShadowDance = Bastion.SpellBook:GetSpell(185422)
local Subterfuge = Bastion.SpellBook:GetSpell(115192)
local Shadowmeld = Bastion.SpellBook:GetSpell(58984)
local Sepsis = Bastion.SpellBook:GetSpell(328305)
local Stealth = Bastion.Globals.SpellBook:GetSpell(1784)
local Vanish = Bastion.Globals.SpellBook:GetSpell(1856)
local ShadowDance = Bastion.Globals.SpellBook:GetSpell(185422)
local Subterfuge = Bastion.Globals.SpellBook:GetSpell(115192)
local Shadowmeld = Bastion.Globals.SpellBook:GetSpell(58984)
local Sepsis = Bastion.Globals.SpellBook:GetSpell(328305)
return self:GetAuras():FindAny(Stealth) or self:GetAuras():FindAny(ShadowDance)
end
@ -912,7 +959,7 @@ end
---@return nil
function Unit:WatchForSwings()
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
Bastion.Globals.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()
@ -973,4 +1020,186 @@ function Unit:IsDry()
return not ObjectIsSubmerged(self.unit)
end
-- The unit stagger amount
---@return number
function Unit:GetStagger()
return UnitStagger(self:GetOMToken())
end
-- The percent of health the unit is currently staggering
---@return number
function Unit:GetStaggerPercent()
local stagger = self:GetStagger()
local max_health = self:GetMaxHealth()
return (stagger / max_health) * 100
end
-- Get the units power regen rate
---@return number
function Unit:GetPowerRegen()
return GetPowerRegen(self:GetOMToken())
end
-- Get the units staggered health relation
---@return number
function Unit:GetStaggeredHealth()
local stagger = self:GetStagger()
local max_health = self:GetMaxHealth()
return (stagger / max_health) * 100
end
-- get the units combat reach
---@return number
function Unit:GetCombatReach()
return ObjectCombatReach(self:GetOMToken())
end
-- Get the units combat distance (distance - combat reach (realized distance))
---@return number
function Unit:GetCombatDistance(Target)
return self:GetDistance(Target) - Target:GetCombatReach()
end
-- Is the unit within distance of the target (combat reach + distance)
--- If the target is within 8 combat yards (8 + combat reach) of the unit
---@param Target Unit
---@param Distance number
---@return boolean
function Unit:IsWithinCombatDistance(Target, Distance)
if not Target:Exists() then
return false
end
return self:GetDistance(Target) <= Distance + Target:GetCombatReach()
end
-- Check if the unit is within X yards (consider combat reach)
---@param Target Unit
---@param Distance number
---@return boolean
function Unit:IsWithinDistance(Target, Distance)
return self:GetDistance(Target) <= Distance
end
-- Get the angle between the unit and the target in raidans
---@param Target Unit
---@return number
function Unit:GetAngle(Target)
if not Target:Exists() then
return 0
end
local sp = self:GetPosition()
local tp = Target:GetPosition()
local an = Tinkr.Common.GetAnglesBetweenPositions(sp.x, sp.y, sp.z, tp.x, tp.y, tp.z)
return an
end
function Unit:GetFacing()
return ObjectRotation(self:GetOMToken()) or 0
end
-- Check if target is within a arc around the unit (angle, distance) accounting for a rotation of self
---@param Target Unit
---@param Angle number
---@param Distance number
---@param rotation? number
---@return boolean
function Unit:IsWithinCone(Target, Angle, Distance, rotation)
if not Target:Exists() then
return false
end
local angle = self:GetAngle(Target)
rotation = rotation or self:GetFacing()
local diff = math.abs(angle - rotation)
if diff > math.pi then
diff = math.abs(diff - math.pi * 2)
end
return diff <= Angle and self:GetDistance(Target) <= Distance
end
function Unit:GetEmpoweredStage()
local stage = 0
local _, _, _, startTime, _, _, _, spellID, _, numStages = UnitChannelInfo(self:GetOMToken())
if numStages and numStages > 0 then
startTime = startTime / 1000
local currentTime = GetTime()
local stageDuration = 0
for i = 1, numStages do
stageDuration = stageDuration + GetUnitEmpowerStageDuration((self:GetOMToken()), i - 1) / 1000
if startTime + stageDuration > currentTime then
break
end
stage = i
end
end
return stage
end
-- local empowering = {}
-- Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_EMPOWER_START", function(...)
-- local unit, unk, id = ...
-- if not unit then return end
-- local guid = ObjectGUID(unit)
-- if not guid then return end
-- empowering[guid] = -1
-- end)
-- Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_EMPOWER_STOP", function(...)
-- local unit, unk, id = ...
-- if not unit then return end
-- local guid = ObjectGUID(unit)
-- if not guid then return end
-- empowering[guid] = -1
-- end)
-- function Unit:GetUnitEmpowerStage()
-- local name, text, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID, _, numStages =
-- UnitChannelInfo(self:GetOMToken());
-- if name and empowering[self:GetGUID()] == -1 then
-- empowering[self:GetGUID()] = numStages
-- end
-- if not name and empowering[self:GetGUID()] then
-- return empowering[self:GetGUID()]
-- end
-- if not name then
-- return empowering[self:GetGUID()]
-- end
-- local getStageDuration = function(stage)
-- if stage == numStages then
-- return GetUnitEmpowerHoldAtMaxTime(self:GetOMToken());
-- else
-- return GetUnitEmpowerStageDuration(self:GetOMToken(), stage - 1);
-- end
-- end
-- local time = GetTime() - (startTime / 1000);
-- local higheststage = 0
-- local sumdur = 0
-- for i = 1, numStages - 1, 1 do
-- local duration = getStageDuration(i) / 1000;
-- sumdur = sumdur + duration
-- if time > sumdur then
-- higheststage = i
-- end
-- end
-- return higheststage
-- end
return Unit

@ -4,49 +4,6 @@ local ObjectManager = Tinkr.Util.ObjectManager
local Unit = Bastion.Unit
local prefixes = {
'^player',
'^pet',
'^vehicle',
'^target',
'^focus',
'^mouseover',
'^none',
'^npc',
'^party[1-4]',
'^raid[1-4]?[0-9]',
'^boss[1-5]',
'^arena[1-5]'
}
-- Validate a unit is a valid token
local function Validate(token)
local start, index
local length, offset = string.len(token), 0
for i = 1, #prefixes do
start, index = string.find(token, prefixes[i])
if start then
offset = index + 1
if offset > length then
return true
else
while true do
start, index = string.find(token, 'target', offset, true)
if start then
offset = index + 1
if offset > length then
return true
end
else
return false
end
end
end
end
end
return false
end
-- Create a new UnitManager class
---@class UnitManager
local UnitManager = {
@ -108,9 +65,6 @@ function UnitManager:New()
return self
end
function UnitManager:Validate(token)
return Validate(token)
end
-- Get or create a unit
---@param token string
@ -153,7 +107,7 @@ end
-- Set a unit by guid
---@param unit Unit
---@return Unit
---@return nil
function UnitManager:SetObject(unit)
self.objects[unit:GetGUID()] = unit
end
@ -224,6 +178,7 @@ function UnitManager:GetNumFriendsWithBuff(spell)
if unit:GetAuras():FindMy(spell):IsUp() then
count = count + 1
end
return false
end)
return count
end
@ -236,6 +191,7 @@ function UnitManager:GetNumFriendsAlive()
if unit:IsAlive() then
count = count + 1
end
return false
end)
return count
end
@ -255,6 +211,7 @@ function UnitManager:GetFriendWithMostFriends(radius)
if other:IsAlive() and u:GetDistance(other) <= radius then
c = c + 1
end
return false
end)
if c > count then
unit = u
@ -264,13 +221,46 @@ function UnitManager:GetFriendWithMostFriends(radius)
if other:IsAlive() and u:GetDistance(other) <= radius then
table.insert(friends, other)
end
return false
end)
end
end
return false
end)
return unit, friends
end
-- Get the enemy with the most enemies within a given radius
function UnitManager:GetEnemiesWithMostEnemies(radius)
local unit = nil
local count = 0
local enemies = {}
self:EnumEnemies(function(u)
if u:IsAlive() then
local c = 0
self:EnumEnemies(function(other)
if other:IsAlive() and u:GetDistance(other) <= radius then
c = c + 1
end
return false
end)
if c > count then
unit = u
count = c
enemies = {}
self:EnumEnemies(function(other)
if other:IsAlive() and u:GetDistance(other) <= radius then
table.insert(enemies, other)
end
return false
end)
end
end
return false
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 radius number
---@param range number
@ -310,44 +300,12 @@ 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
-- Find the centroid of the most dense area of enemies of a given radius within a given range
---@param radius number
---@param range number
---@return Vector3 | nil
function UnitManager:FindEnemiesCentroid(distanceFromPlayer, radius)
local unit, enemies = self:GetEnemyWithMostEnemies(distanceFromPlayer, radius)
function UnitManager:FindEnemiesCentroid(radius, range)
local unit, enemies = self:GetEnemiesWithMostEnemies(radius)
if unit == nil then
return nil
end
@ -362,7 +320,7 @@ function UnitManager:FindEnemiesCentroid(distanceFromPlayer, radius)
centroid = centroid / #enemies
if unit:GetPosition():Distance(centroid) > distanceFromPlayer then
if unit:GetPosition():Distance(centroid) > range then
return unit:GetPosition()
end
@ -382,4 +340,3 @@ function UnitManager:FindEnemiesCentroid(distanceFromPlayer, radius)
end
return UnitManager

@ -1,7 +1,7 @@
-- Create a Vector3 class
---@class Vector3
local Vector3 = {}
local Vector3 = { }
Vector3.__index = Vector3
---@return string
@ -69,87 +69,129 @@ function Vector3:__index(k)
return Vector3[k]
end
---@class Vector3
---@field length number
if k == "length" then
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
---@class Vector3
---@field normalized Vector3
if k == "normalized" then
local length = math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
return Vector3:New(self.x / length, self.y / length, self.z / length)
end
---@class Vector3
---@field magnitude number
if k == "magnitude" then
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
---@class Vector3
---@field sqrMagnitude number
if k == "sqrMagnitude" then
return self.x * self.x + self.y * self.y + self.z * self.z
end
---@class Vector3
---@field zero Vector3
if k == "zero" then
return Vector3:New(0, 0, 0)
end
---@class Vector3
---@field one Vector3
if k == "one" then
return Vector3:New(1, 1, 1)
end
---@class Vector3
---@field up Vector3
if k == "up" then
return Vector3:New(0, 1, 0)
end
---@class Vector3
---@field down Vector3
if k == "down" then
return Vector3:New(0, -1, 0)
end
---@class Vector3
---@field left Vector3
if k == "left" then
return Vector3:New(-1, 0, 0)
end
---@class Vector3
---@field right Vector3
if k == "right" then
return Vector3:New(1, 0, 0)
end
---@class Vector3
---@field forward Vector3
if k == "forward" then
return Vector3:New(0, 0, 1)
end
---@class Vector3
---@field back Vector3
if k == "back" then
return Vector3:New(0, 0, -1)
end
---@class Vector3
---@field positiveInfinity Vector3
if k == "positiveInfinity" then
return Vector3:New(math.huge, math.huge, math.huge)
end
---@class Vector3
---@field negativeInfinity Vector3
if k == "negativeInfinity" then
return Vector3:New(-math.huge, -math.huge, -math.huge)
end
---@class Vector3
---@field nan Vector3
if k == "nan" then
return Vector3:New(0 / 0, 0 / 0, 0 / 0)
end
---@class Vector3
---@field epsilon number
if k == "epsilon" then
return 1.401298E-45
end
---@class Vector3
---@field maxValue number
if k == "maxValue" then
return 3.402823E+38
end
---@class Vector3
---@field minValue number
if k == "minValue" then
return -3.402823E+38
end
---@class Vector3
---@field x number
if k == "x" then
return self[1]
end
---@class Vector3
---@field y number
if k == "y" then
return self[2]
end
---@class Vector3
---@field z number
if k == "z" then
return self[3]
end
@ -272,7 +314,7 @@ function Vector3:ProjectOnPlane(planeNormal)
return self - self:Project(planeNormal)
end
---@param inDirection Vector3
---@param inNormal Vector3
---@return Vector3
function Vector3:Reflect(inNormal)
return -2 * inNormal:Dot(self) * inNormal + self

@ -1,262 +1,412 @@
local Tinkr = ...
local Evaulator = Tinkr.Evaluator
---@class Bastion
local Bastion = {
DebugMode = false
}
local Bastion = {DebugMode = false}
Bastion.__index = Bastion
function Bastion.require(class)
return Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
function Bastion:Require(file)
-- If require starts with an @ then we require from the scripts/bastion/scripts folder
if file:sub(1, 1) == '@' then
file = file:sub(2)
-- print('1')
return require('scripts/bastion/scripts/' .. file, Bastion)
elseif file:sub(1, 1) == "~" then
file = file:sub(2)
-- print("2")
return require('scripts/bastion/' .. file, Bastion)
else
-- print("Normal req")
return require(file, Bastion)
end
end
---@type ToggleManager
Bastion.ToggleManager = Bastion.require("ToggleManager")
---@type ClassMagic
Bastion.ClassMagic = Bastion.require("ClassMagic")
---@type List
Bastion.List = Bastion.require("List")
---@type NotificationsList, Notification
Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList")
---@type Vector3
Bastion.Vector3 = Bastion.require("Vector3")
---@type Sequencer
Bastion.Sequencer = Bastion.require("Sequencer")
---@type Command
Bastion.Command = Bastion.require("Command")
---@type Cache
Bastion.Cache = Bastion.require("Cache")
---@type Cacheable
Bastion.Cacheable = Bastion.require("Cacheable")
---@type Refreshable
Bastion.Refreshable = Bastion.require("Refreshable")
---@type Unit
Bastion.Unit = Bastion.require("Unit")
---@type Aura
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()
---@type ObjectManager
Bastion.ObjectManager = Bastion.require("ObjectManager"):New()
---@type EventManager
Bastion.EventManager = Bastion.require("EventManager"):New()
---@type Spell
Bastion.Spell = Bastion.require("Spell")
---@type SpellBook
Bastion.SpellBook = Bastion.require("SpellBook"):New()
---@type Item
Bastion.Item = Bastion.require("Item")
---@type ItemBook
Bastion.ItemBook = Bastion.require("ItemBook"):New()
---@type AuraTable
Bastion.AuraTable = Bastion.require("AuraTable")
---@type Class
Bastion.Class = Bastion.require("Class")
---@type Timer
Bastion.Timer = Bastion.require("Timer")
---@type Timer
Bastion.CombatTimer = Bastion.Timer:New('combat')
---@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)
local u = Bastion.UnitManager[unit]
if u then
u:GetAuras():OnUpdate(auras)
end
end)
local function Load(dir)
local dir = dir
Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
local unit, castGUID, spellID = ...
if dir:sub(1, 1) == '@' then
dir = dir:sub(2)
dir = 'scripts/bastion/scripts/' .. dir
end
local spell = Bastion.SpellBook:GetIfRegistered(spellID)
if dir:sub(1, 1) == '~' then
dir = dir:sub(2)
dir = 'scripts/bastion/' .. dir
end
if unit == "player" and spell then
spell.lastCastAt = GetTime()
local files = ListFiles(dir)
if spell:GetPostCastFunction() then
spell:GetPostCastFunction()(spell)
for i = 1, #files do
local file = files[i]
if file:sub(-4) == ".lua" or file:sub(-5) == '.luac' then
Bastion:Require(dir .. file:sub(1, -5))
end
end
end)
end
local pguid = UnitGUID("player")
function Bastion.require(class)
-- return require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
return Bastion:Require("~/src/" .. class .. "/" .. class)
end
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = { CombatLogGetCurrentEventInfo() }
-- fenv for all required files
function Bastion.Bootstrap()
Bastion.Globals = {}
---@type ClassMagic
Bastion.ClassMagic = Bastion.require("ClassMagic")
---@type List
Bastion.List = Bastion.require("List")
---@type Library
Bastion.Library = Bastion.require("Library")
---@type NotificationsList, Notification
Bastion.NotificationsList, Bastion.Notification = Bastion.require(
"NotificationsList")
---@type Vector3
Bastion.Vector3 = Bastion.require("Vector3")
---@type Sequencer
Bastion.Sequencer = Bastion.require("Sequencer")
---@type Command
Bastion.Command = Bastion.require("Command")
---@type Cache
Bastion.Cache = Bastion.require("Cache")
---@type Cacheable
Bastion.Cacheable = Bastion.require("Cacheable")
---@type Refreshable
Bastion.Refreshable = Bastion.require("Refreshable")
---@type Unit
Bastion.Unit = Bastion.require("Unit")
---@type Aura
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()
---@type ObjectManager
Bastion.ObjectManager = Bastion.require("ObjectManager"):New()
---@type EventManager
Bastion.EventManager = Bastion.require("EventManager")
Bastion.Globals.EventManager = Bastion.EventManager:New()
---@type Spell
Bastion.Spell = Bastion.require("Spell")
---@type SpellBook
Bastion.SpellBook = Bastion.require("SpellBook")
Bastion.Globals.SpellBook = Bastion.SpellBook:New()
---@type Item
Bastion.Item = Bastion.require("Item")
---@type ItemBook
Bastion.ItemBook = Bastion.require("ItemBook")
Bastion.Globals.ItemBook = Bastion.ItemBook:New()
---@type AuraTable
Bastion.AuraTable = Bastion.require("AuraTable")
---@type Class
Bastion.Class = Bastion.require("Class")
---@type Timer
Bastion.Timer = Bastion.require("Timer")
---@type Timer
Bastion.CombatTimer = Bastion.Timer:New('combat')
---@type MythicPlusUtils
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type NotificationsList
Bastion.Notifications = Bastion.NotificationsList:New()
local LIBRARIES = {}
local MODULES = {}
Bastion.Enabled = false
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA',
function(unit, auras)
local u = Bastion.UnitManager[unit]
if u then u:GetAuras():OnUpdate(auras) end
end)
local sourceGUID = args[4]
local destGUID = args[8]
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED",
function(...)
local unit, castGUID, spellID = ...
local u = Bastion.UnitManager[sourceGUID]
local u2 = Bastion.UnitManager[destGUID]
local spell = Bastion.Globals.SpellBook:GetIfRegistered(spellID)
local t = GetTime()
if unit == "player" and spell then
spell.lastCastAt = GetTime()
if u then
u:SetLastCombatTime(t)
end
if spell:GetPostCastFunction() then
spell:GetPostCastFunction()(spell)
end
end
end)
if u2 then
u2:SetLastCombatTime(t)
end
end)
local pguid = UnitGUID("player")
local missed = {}
Bastion.Ticker = C_Timer.NewTicker(0.1, function()
if not Bastion.CombatTimer:IsRunning() and UnitAffectingCombat("player") then
Bastion.CombatTimer:Start()
elseif Bastion.CombatTimer:IsRunning() and not UnitAffectingCombat("player") then
Bastion.CombatTimer:Reset()
end
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED",
function()
local args = {CombatLogGetCurrentEventInfo()}
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
local subEvent = args[2]
local sourceGUID = args[4]
local destGUID = args[8]
local spellID = args[12]
Bastion.ToggleManager:Refresh()
-- if sourceGUID == pguid then
-- local args = { CombatLogGetCurrentEventInfo() }
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()
-- for i = 1, #args do
-- Log(tostring(args[i]))
-- end
-- end
local u = Bastion.UnitManager[sourceGUID]
local u2 = Bastion.UnitManager[destGUID]
local t = GetTime()
if u then u:SetLastCombatTime(t) end
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)
function Bastion:Register(module)
table.insert(Bastion.modules, module)
Bastion:Print("Registered", module)
end
Bastion.Ticker = C_Timer.NewTicker(0.1, function()
if not Bastion.CombatTimer:IsRunning() and UnitAffectingCombat("player") then
Bastion.CombatTimer:Start()
elseif Bastion.CombatTimer:IsRunning() and
not UnitAffectingCombat("player") then
Bastion.CombatTimer:Reset()
end
-- Find a module by name
function Bastion:FindModule(name)
for i = 1, #Bastion.modules do
if Bastion.modules[i].name == name then
return Bastion.modules[i]
if Bastion.Enabled then
Bastion.ObjectManager:Refresh()
for i = 1, #MODULES do MODULES[i]:Tick() end
end
end)
function Bastion:Register(module)
table.insert(MODULES, module)
Bastion:Print("Registered", module)
end
return nil
end
-- Find a module by name
function Bastion:FindModule(name)
for i = 1, #MODULES do
if MODULES[i].name == name then return MODULES[i] end
end
function Bastion:Print(...)
local args = { ... }
local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
return nil
end
print(str)
end
function Bastion:Debug(...)
if not Bastion.DebugMode then
return
function Bastion:Print(...)
local args = {...}
local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF"
for i = 1, #args do str = str .. tostring(args[i]) .. " " end
print(str)
end
local args = { ... }
local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
function Bastion:Debug(...)
if not Bastion.DebugMode then return end
local args = {...}
local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF"
for i = 1, #args do str = str .. tostring(args[i]) .. " " end
print(str)
end
print(str)
end
local Command = Bastion.Command:New('bastion')
local Command = Bastion.Command:New('bastion')
Command:Register('toggle', 'Toggle bastion on/off', function()
Bastion.Enabled = not Bastion.Enabled
if Bastion.Enabled then
Bastion:Print("Enabled")
else
Bastion:Print("Disabled")
end
end)
Command:Register('toggle', 'Toggle bastion on/off', function()
Bastion.Enabled = not Bastion.Enabled
if Bastion.Enabled then
Bastion:Print("Enabled")
else
Bastion:Print("Disabled")
end
end)
Command:Register('debug', 'Toggle debug mode on/off', function()
Bastion.DebugMode = not Bastion.DebugMode
if Bastion.DebugMode then
Bastion:Print("Debug mode enabled")
else
Bastion:Print("Debug mode disabled")
end
end)
Command:Register('dumpspells', 'Dump spells to a file', function()
local i = 1
local rand = math.random(100000, 999999)
while true do
local spellName, spellSubName = GetSpellBookItemName(i, BOOKTYPE_SPELL)
if not spellName then
do break end
Command:Register('debug', 'Toggle debug mode on/off', function()
Bastion.DebugMode = not Bastion.DebugMode
if Bastion.DebugMode then
Bastion:Print("Debug mode enabled")
else
Bastion:Print("Debug mode disabled")
end
end)
Command:Register('dumpspells', 'Dump spells to a file', function()
local i = 1
local rand = math.random(100000, 999999)
local BOOKTYPE_SPELL = BOOKTYPE_SPELL or (Enum.SpellBookSpellBank.Player and Enum.SpellBookSpellBank.Player or 'spell')
while true do
local spellName, spellSubName
if C_SpellBook.GetSpellBookItemName then
spellName, spellSubName = C_SpellBook.GetSpellBookItemName(i, BOOKTYPE_SPELL)
else
spellName, spellSubName = GetSpellBookItemName(i, BOOKTYPE_SPELL)
end
if not spellName then do break end end
-- use spellName and spellSubName here
local spellID
-- use spellName and spellSubName here
local spellID = select(7, GetSpellInfo(spellName))
if C_Spell.GetSpellInfo then
local info = C_Spell.GetSpellInfo(spellName)
spellID = info.spellID
else
spellID = select(7, GetSpellInfo(spellName))
end
if spellID then
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand .. '.lua',
"local " .. spellName .. " = Bastion.SpellBook:GetSpell(" .. spellID .. ")", true)
if spellID then
spellName = spellName:gsub("[%W%s]", "")
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand ..
'.lua',
"local " .. spellName ..
" = Bastion.Globals.SpellBook:GetSpell(" ..
spellID .. ")\n", true)
end
i = i + 1
end
i = i + 1
end
end)
Command:Register('module', 'Toggle a module on/off', function(args)
local module = Bastion:FindModule(args[2])
if module then
module:Toggle()
if module.enabled then
Bastion:Print("Enabled", module.name)
end)
Command:Register('module', 'Toggle a module on/off', function(args)
local module = Bastion:FindModule(args[2])
if module then
module:Toggle()
if module.enabled then
Bastion:Print("Enabled", module.name)
else
Bastion:Print("Disabled", module.name)
end
else
Bastion:Print("Disabled", module.name)
Bastion:Print("Module not found")
end
else
Bastion:Print("Module not found")
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
Bastion:Print("[MythicPlusUtils] Unknown command")
Bastion:Print("Available commands:")
Bastion:Print("debuffs")
Bastion:Print("casts")
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)
---@param library Library
function Bastion:RegisterLibrary(library)
LIBRARIES[library.name] = library
end
end)
Command:Register('pause', 'Pause a module for X seconds', function (args)
if Bastion.Enabled then
local duration = args[3]
local moduleName = args[2]
local RequestedModule = Bastion:FindModule(moduleName)
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)
function Bastion:CheckLibraryDependencies()
for k, v in pairs(LIBRARIES) do
if v.dependencies then
for i = 1, #v.dependencies do
local dep = v.dependencies[i]
if LIBRARIES[dep] then
if LIBRARIES[dep].dependencies then
for j = 1, #LIBRARIES[dep].dependencies do
if LIBRARIES[dep].dependencies[j] == v.name then
Bastion:Print(
"Circular dependency detected between " ..
v.name .. " and " .. dep)
return false
end
end
end
else
Bastion:Print("Library " .. v.name .. " depends on " ..
dep .. " but it's not registered")
return false
end
end
end
end
return true
end
end)
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')
function Bastion:Import(library)
local lib = self:GetLibrary(library)
if not lib then error("Library " .. library .. " not found") end
return lib:Resolve()
end
function Bastion:GetLibrary(name)
if not LIBRARIES[name] then
error("Library " .. name .. " not found")
end
local library = LIBRARIES[name]
-- if library.dependencies then
-- for i = 1, #library.dependencies do
-- local dep = library.dependencies[i]
-- if LIBRARIES[dep] then
-- if LIBRARIES[dep].dependencies then
-- for j = 1, #LIBRARIES[dep].dependencies do
-- if LIBRARIES[dep].dependencies[j] == library.name then
-- Bastion:Print("Circular dependency detected between " .. library.name .. " and " .. dep)
-- return false
-- end
-- end
-- end
-- else
-- Bastion:Print("Library " .. v.name .. " depends on " .. dep .. " but it's not registered")
-- return false
-- end
-- end
-- end
return library
end
-- if not Bastion:CheckLibraryDependencies() then
-- return
-- end
Load("@Libraries/")
Load("@Modules/")
Load("@")
end
Bastion.Bootstrap()

Loading…
Cancel
Save