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. 7
      .gitignore
  2. 4
      README.md
  3. 0
      scripts/.gitkeep
  4. 24
      scripts/ExampleModule.lua
  5. 21
      scripts/Libraries/ExampleDependency.lua
  6. 15
      scripts/Libraries/ExampleDependencyError.lua
  7. 25
      scripts/Libraries/ExampleLibrary.lua
  8. 954
      scripts/outlaw.lua
  9. 758
      scripts/restodruid.lua
  10. 1379
      scripts/subtlety.lua
  11. 65
      src/APL/APL.lua
  12. 54
      src/Aura/Aura.lua
  13. 360
      src/AuraTable/AuraTable.lua
  14. 15
      src/Class/Class.lua
  15. 7
      src/Command/Command.lua
  16. 49
      src/Item/Item.lua
  17. 115
      src/Library/Library.lua
  18. 26
      src/List/List.lua
  19. 81
      src/MythicPlusUtils/MythicPlusUtils.lua
  20. 7
      src/ObjectManager/ObjectManager.lua
  21. 6
      src/Sequencer/Sequencer.lua
  22. 170
      src/Spell/Spell.lua
  23. 15
      src/SpellBook/SpellBook.lua
  24. 292
      src/Unit/Unit.lua
  25. 123
      src/UnitManager/UnitManager.lua
  26. 44
      src/Vector3/Vector3.lua
  27. 246
      src/_bastion.lua

7
.gitignore vendored

@ -7,6 +7,13 @@ DS_Store
## ignore all files in 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/*

@ -11,3 +11,7 @@ Feel free to browse around the [Wiki](https://git.tinkr.site/4n0n/bastion/wiki)
- [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.
## Developer Information
- [Issue Template](https://git.tinkr.site/Bastion/Bastion/wiki/Issues)
- [Pull Requests](https://git.tinkr.site/Bastion/Bastion/wiki/Pull-Requests)

@ -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,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,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

@ -1,5 +1,4 @@
-- Document with emmy lua: https://emmylua.github.io/
-- Create an APL trait for the APL class
---@class APLTrait
local APLTrait = {}
@ -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,23 +107,24 @@ 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)
end
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):Cast(self:GetActor().target)
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast(
self:GetActor().target)
end
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)
end
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
end
if self:GetActor().action then
-- print("Bastion: APL:Execute: Executing action " .. self:GetActor().action)
self:GetActor().cb(self)
@ -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()

@ -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

@ -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
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

@ -8,6 +8,7 @@ local MythicPlusUtils = {
loggedCasts = {},
loggedDebuffs = {},
kickList = {},
aoeBosses = {}
}
MythicPlusUtils.__index = MythicPlusUtils
@ -16,7 +17,52 @@ MythicPlusUtils.__index = 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
@ -377,9 +423,14 @@ function MythicPlusUtils:New()
false, true, false
}
},
[195696] = {
[387125] = {
true, false, false
}
}
}
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
if not self.debuffLogging then
return
end
@ -402,7 +453,7 @@ function MythicPlusUtils:New()
end
end)
Bastion.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_START', function(unitTarget, castGUID, spellID)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_START', function(unitTarget, castGUID, spellID)
if not self.castLogging then
return
end
@ -411,7 +462,14 @@ function MythicPlusUtils:New()
return
end
local name = GetSpellInfo(spellID)
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
@ -421,7 +479,7 @@ function MythicPlusUtils:New()
]], true)
end)
Bastion.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_CHANNEL_START', function(unitTarget, castGUID, spellID)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_CHANNEL_START', function(unitTarget, castGUID, spellID)
if not self.castLogging then
return
end
@ -430,7 +488,14 @@ function MythicPlusUtils:New()
return
end
local name = GetSpellInfo(spellID)
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
@ -511,4 +576,10 @@ function MythicPlusUtils:CastingCriticalStun(unit, percent)
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

@ -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
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,6 +525,10 @@ 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
@ -484,9 +621,7 @@ end
-- IsDiseaseDispel
---@return boolean
function Spell:IsDiseaseDispel()
return ({
})[self:GetID()]
return ({})[self:GetID()]
end
-- IsSpell
@ -496,4 +631,21 @@ function Spell:IsSpell(spell)
return self:GetID() == spell:GetID()
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
local cost = GetSpellPowerCost(self:GetID())
return cost and cost.cost or 0
end
-- IsFree
---@return boolean
function Spell:IsFree()
return self:GetCost() == 0
end
return Spell

@ -34,9 +34,24 @@ function SpellBook:GetSpells(...)
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

@ -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
@ -452,7 +505,7 @@ end
-- Check if unit is interruptible
---@param percent number
---@param ignoreInterruptible boolean
---@param ignoreInterruptible? boolean
---@return boolean
function Unit:IsInterruptibleAt(percent, ignoreInterruptible)
if not ignoreInterruptible and not self:IsInterruptible() then
@ -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()
@ -785,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
@ -873,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
@ -907,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()
@ -968,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,4 +300,43 @@ function UnitManager:FindFriendsCentroid(radius, range)
return centroid
end
-- 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(radius, range)
local unit, enemies = self:GetEnemiesWithMostEnemies(radius)
if unit == nil then
return nil
end
local centroid = Bastion.Vector3:New(0, 0, 0)
local zstart = -math.huge
for i = 1, #enemies do
local p = enemies[i]:GetPosition()
centroid = centroid + p
zstart = p.z > zstart and p.z or zstart
end
centroid = centroid / #enemies
if unit:GetPosition():Distance(centroid) > range then
return unit:GetPosition()
end
local _, _, z = TraceLine(
centroid.x,
centroid.y,
centroid.z + 5,
centroid.x,
centroid.y,
centroid.z - 5,
0x100151
)
centroid.z = z + 0.01
return centroid
end
return UnitManager

@ -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,21 +1,69 @@
local Tinkr = ...
local Evaulator = Tinkr.Evaluator
---@class Bastion
local Bastion = {
DebugMode = false
}
local Bastion = {DebugMode = false}
Bastion.__index = 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
local function Load(dir)
local dir = dir
if dir:sub(1, 1) == '@' then
dir = dir:sub(2)
dir = 'scripts/bastion/scripts/' .. dir
end
if dir:sub(1, 1) == '~' then
dir = dir:sub(2)
dir = 'scripts/bastion/' .. dir
end
local files = ListFiles(dir)
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
function Bastion.require(class)
return Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
-- return require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
return Bastion:Require("~/src/" .. class .. "/" .. class)
end
-- 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")
Bastion.NotificationsList, Bastion.Notification = Bastion.require(
"NotificationsList")
---@type Vector3
Bastion.Vector3 = Bastion.require("Vector3")
---@type Sequencer
@ -34,21 +82,25 @@ Bastion.Unit = Bastion.require("Unit")
Bastion.Aura = Bastion.require("Aura")
---@type APL, APLActor, APLTrait
Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL")
---@type Module
Bastion.Module = Bastion.require("Module")
---@type UnitManager
Bastion.UnitManager = Bastion.require("UnitManager"):New()
---@type ObjectManager
Bastion.ObjectManager = Bastion.require("ObjectManager"):New()
---@type EventManager
Bastion.EventManager = Bastion.require("EventManager"):New()
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"):New()
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"):New()
Bastion.ItemBook = Bastion.require("ItemBook")
Bastion.Globals.ItemBook = Bastion.ItemBook:New()
---@type AuraTable
Bastion.AuraTable = Bastion.require("AuraTable")
---@type Class
@ -62,21 +114,23 @@ Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type NotificationsList
Bastion.Notifications = Bastion.NotificationsList:New()
Bastion.modules = {}
local LIBRARIES = {}
local MODULES = {}
Bastion.Enabled = false
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA',
function(unit, auras)
local u = Bastion.UnitManager[unit]
if u then
u:GetAuras():OnUpdate(auras)
end
if u then u:GetAuras():OnUpdate(auras) end
end)
Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED",
function(...)
local unit, castGUID, spellID = ...
local spell = Bastion.SpellBook:GetIfRegistered(spellID)
local spell = Bastion.Globals.SpellBook:GetIfRegistered(spellID)
if unit == "player" and spell then
spell.lastCastAt = GetTime()
@ -90,7 +144,8 @@ end)
local pguid = UnitGUID("player")
local missed = {}
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED",
function()
local args = {CombatLogGetCurrentEventInfo()}
local subEvent = args[2]
@ -111,14 +166,13 @@ Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local t = GetTime()
if u then
u:SetLastCombatTime(t)
end
if u then u:SetLastCombatTime(t) end
if u2 then
u2:SetLastCombatTime(t)
if subEvent == "SPELL_MISSED" and sourceGUID == pguid and spellID == 408 then
if subEvent == "SPELL_MISSED" and sourceGUID == pguid and spellID ==
408 then
local missType = args[15]
if missType == "IMMUNE" then
@ -137,29 +191,26 @@ 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
elseif Bastion.CombatTimer:IsRunning() and
not UnitAffectingCombat("player") then
Bastion.CombatTimer:Reset()
end
if Bastion.Enabled then
Bastion.ObjectManager:Refresh()
for i = 1, #Bastion.modules do
Bastion.modules[i]:Tick()
end
for i = 1, #MODULES do MODULES[i]:Tick() end
end
end)
function Bastion:Register(module)
table.insert(Bastion.modules, module)
table.insert(MODULES, module)
Bastion:Print("Registered", module)
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]
end
for i = 1, #MODULES do
if MODULES[i].name == name then return MODULES[i] end
end
return nil
@ -168,21 +219,15 @@ end
function Bastion:Print(...)
local args = {...}
local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
end
for i = 1, #args do str = str .. tostring(args[i]) .. " " end
print(str)
end
function Bastion:Debug(...)
if not Bastion.DebugMode then
return
end
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
for i = 1, #args do str = str .. tostring(args[i]) .. " " end
print(str)
end
@ -209,18 +254,35 @@ 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 = GetSpellBookItemName(i, BOOKTYPE_SPELL)
if not spellName then
do break end
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 = select(7, GetSpellInfo(spellName))
local spellID
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)
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
@ -244,13 +306,16 @@ 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")
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")
Bastion:Print("Cast logging",
Bastion.MythicPlusUtils.castLogging and "enabled" or
"disabled")
return
end
@ -260,17 +325,88 @@ Command:Register('mplus', 'Toggle m+ module on/off', function(args)
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
Command:Register('missed', 'Dump the list of immune kidney shot spells',
function()
for k, v in pairs(missed) do Bastion:Print(k) end
end)
local files = ListFiles("scripts/bastion/scripts")
---@param library Library
function Bastion:RegisterLibrary(library)
LIBRARIES[library.name] = library
end
for i = 1, #files do
local file = files[i]
if file:sub( -4) == ".lua" or file:sub( -5) == '.luac' then
Tinkr:require("scripts/bastion/scripts/" .. file:sub(1, -5), Bastion)
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
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