Compare commits

..

55 Commits

Author SHA1 Message Date
João Fidalgo 073a9e03b7 feat: add spell traits 6 months ago
4n0n c0c8ab8362 Fix? 11 months ago
4n0n fad3d76d8a 11.0 ItemName 11 months ago
4n0n e6b2b8f42f Merge pull request '11.0' (#27) from 4n0n/Bastion:main into main 11 months ago
4n0n 86e7aeccec 11.0 11 months ago
4n0n f9583a85d8 11.0 11 months ago
4n0n d0cad5746d 11.0 11 months ago
4n0n 010d2b4111 11.0 11 months ago
4n0n 7442498326 11.0 11 months ago
4n0n 83086a9b57 11.0 11 months ago
4n0n 878d5db7ae Merge pull request 'fix: C_UnitAuras.GetAuraDataByAuraInstanceID' (#21) from vibe/Bastion:fix_getauradatabyaurainstanceid into main 11 months ago
João Fidalgo 4d08a04958 fix: C_UnitAuras.GetAuraDataByAuraInstanceID 1 year ago
4n0n a22aa67d2a Merge pull request 'main' (#19) from CiscOH/Bastion:main into main 2 years ago
Ciscoh Bellic 3877c58b0c Removed return in load(dir) function. it was only loading the first file and no more 2 years ago
CiscOH 8a6308c584 Merge pull request 'main' (#7) from Bastion/Bastion:main into main 2 years ago
4n0n b43166be76 Era 2 years ago
4n0n ede085dd13 Merge pull request 'Bug Fix on APL traits' (#18) from LyLoLoq/Bastion:main into main 2 years ago
LyLoLoq 15920cecb3 Typing fix 2 years ago
LyLoLoq 8e5268229a Bug Fix on APL traits 2 years ago
CiscOH b1d34f113d Merge pull request 'main' (#6) from Bastion/Bastion:main into main 2 years ago
4n0n cadb7df64e Merge pull request 'Fix APL + Change to Spell:Cast' (#17) from LyLoLoq/Bastion:main into main 2 years ago
LyLoLoq a84930cef2 Change Spell:Cast 2 years ago
LyLoLoq 1bdf96e5ca Fix APL 2 years ago
CiscOH 4680344e6f Merge pull request 'main' (#5) from Bastion/Bastion:main into main 2 years ago
4n0n f722d17306 Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 2 years ago
4n0n 5db2f059d8 Add Spell:Fresh() 2 years ago
4n0n 910387c775 Merge pull request 'main' (#16) from CiscOH/Bastion:main into main 2 years ago
CiscOH 2994e8d1dd remove UnitManager:Validate(token) method 2 years ago
CiscOH eab3bb3a8a Merge pull request 'main' (#4) from Bastion/Bastion:main into main 2 years ago
4n0n e4d4270b60 add bastion requires 2 years ago
4n0n 025c0c5c88 remove 2 years ago
CiscOH 5dc90ce113 Merge pull request 'main' (#3) from Bastion/Bastion:main into main 2 years ago
4n0n bfdd70359d Fix typo 2 years ago
4n0n 5472aa1998 Merge pull request 'main' (#15) from CiscOH/Bastion:main into main 2 years ago
CiscOH ba9de7753f added line break to dump spells 2 years ago
CiscOH 94b584a99b Merge pull request 'Update 'README.md'' (#1) from Bastion/Bastion:main into main 2 years ago
4n0n 6924780785 Update 'README.md' 2 years ago
4n0n febee39182 Add highest/leastof functions to auratable, remove print 2 years ago
4n0n a99e71dd8e Aura table updates 2 years ago
4n0n 36ab641a13 Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 2 years ago
4n0n e8913d3ab8 Spellbook get list 2 years ago
4n0n 9d594366dd Merge pull request 'Change Unit.ttd_ticker initialization state' (#13) from abunai/Bastion:abunai-ttd-fix into main 2 years ago
abunai 2200a36bdf Change Unit.ttd_ticker initialization state 2 years ago
4n0n 559c4339a4 Fix aura list finding 2 years ago
4n0n 7b3b714d39 Add findanyof 2 years ago
4n0n 1cc8b541f8 Cleanup 2 years ago
4n0n 677229738d Move spellbook and itembook out of globals, remove prints 2 years ago
4n0n 9d47758f8c Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 2 years ago
4n0n 06916f6bc6 Spellbook and Itembook no longer shared through the engine 2 years ago
4n0n 4c138f1c9f Merge pull request 'More types' (#11) from lyloloq/Bastion:main into main 2 years ago
4n0n f5367327e6 Updated library handling 2 years ago
LyLoLoq 630fcfc3d9 More types 2 years ago
4n0n d5b0ccf42b Merge branch 'main' of https://git.tinkr.site/Bastion/Bastion 2 years ago
4n0n 5f0affd907 Add Bastion Libraries 2 years ago
4n0n 356cc78dd1 Merge pull request 'Remove rogue print' (#9) from 4n0n-patch-1 into main 2 years ago
  1. 6
      .gitignore
  2. 4
      README.md
  3. 24
      scripts/ExampleModule.lua
  4. 21
      scripts/Libraries/ExampleDependency.lua
  5. 15
      scripts/Libraries/ExampleDependencyError.lua
  6. 25
      scripts/Libraries/ExampleLibrary.lua
  7. 58
      src/APL/APL.lua
  8. 54
      src/Aura/Aura.lua
  9. 359
      src/AuraTable/AuraTable.lua
  10. 15
      src/Class/Class.lua
  11. 7
      src/Command/Command.lua
  12. 49
      src/Item/Item.lua
  13. 115
      src/Library/Library.lua
  14. 10
      src/List/List.lua
  15. 29
      src/MythicPlusUtils/MythicPlusUtils.lua
  16. 7
      src/ObjectManager/ObjectManager.lua
  17. 3
      src/Sequencer/Sequencer.lua
  18. 175
      src/Spell/Spell.lua
  19. 23
      src/SpellBook/SpellBook.lua
  20. 32
      src/Unit/Unit.lua
  21. 46
      src/UnitManager/UnitManager.lua
  22. 408
      src/_bastion.lua

6
.gitignore vendored

@ -7,7 +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,5 +1,4 @@
-- Document with emmy lua: https://emmylua.github.io/
-- Create an APL trait for the APL class
---@class APLTrait
local APLTrait = {}
@ -55,7 +54,7 @@ end
---@param ... APLTrait
---@return APLActor
function APLActor:AddTraits(...)
for _, trait in ipairs({ ... }) do
for _, trait in ipairs({...}) do
table.insert(self.traits, trait)
end
@ -85,13 +84,11 @@ function APLActor:Execute()
-- If the actor is a sequencer we don't want to continue executing the APL if the sequencer is not finished
if self:GetActor().sequencer then
if self:GetActor().condition and self:GetActor().condition() and not self:GetActor().sequencer:Finished() then
print("Execute?")
self:GetActor().sequencer:Execute()
return true
end
if not self:GetActor().condition and not self:GetActor().sequencer:Finished() then
print("Execute?")
self:GetActor().sequencer:Execute()
return true
end
@ -111,24 +108,23 @@ function APLActor:Execute()
if self:GetActor().condition then
-- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast(
self:GetActor().target,
self:GetActor().condition)
end
self:GetActor().target, self:GetActor().condition)
else
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast(self
:GetActor().target)
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)
@ -189,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
@ -199,14 +199,17 @@ 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
@ -228,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)
@ -249,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
@ -257,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
@ -276,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')
@ -279,7 +279,7 @@ function AuraTable:Find(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
@ -305,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
@ -334,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
@ -362,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
@ -384,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
@ -116,8 +114,12 @@ end
---@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)

@ -17,6 +17,7 @@ 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 = {
@ -429,7 +430,7 @@ function MythicPlusUtils:New()
}
}
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
if not self.debuffLogging then
return
end
@ -452,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
@ -461,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
@ -471,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
@ -480,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
@ -567,8 +582,4 @@ function MythicPlusUtils:IsAOEBoss(unit)
return self.aoeBosses[unit:GetID()]
end
function MythicPlusUtils:IsTankBuster(spell)
return self.tankBusters[spell: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,5 +1,4 @@
-- 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
@ -45,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

@ -13,10 +13,18 @@ local Spell = {
conditions = {},
target = false,
release_at = false,
traits = {
ignoreChanneling = false,
ignoreFacing = false,
ignoreLoS = false,
ignoreGCD = false,
ignoreMoving = false,
targeted = false
}
}
local usableExcludes = {
[18562] = true,
[18562] = true
}
function Spell:__index(k)
@ -48,15 +56,29 @@ end
-- Constructor
---@param id number
---@param traits? table
---@return Spell
function Spell:New(id)
function Spell:New(id, traits)
local self = setmetatable({}, Spell)
self.spellID = id
if traits == nil then traits = {} end
for k in pairs(traits) do
if self.traits[k] ~= nil and traits[k] ~= nil then
self.traits[k] = traits[k]
end
end
return self
end
-- Duplicator
---@return Spell
function Spell:Fresh()
return Spell:New(self:GetID())
end
-- Get the spells id
---@return number
function Spell:GetID()
@ -74,24 +96,53 @@ 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
@ -130,6 +181,10 @@ 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
@ -137,6 +192,9 @@ 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
@ -155,11 +213,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
@ -242,6 +304,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
@ -250,12 +314,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
@ -273,7 +345,44 @@ function Spell:Castable()
return self:GetCastableFunction()(self)
end
return self:IsKnownAndUsable()
if not self:IsKnownAndUsable() then return false end
local player = Bastion.UnitManager:Get("player")
if self.traits.targeted then
local target = self:GetTarget()
if not target or
not target:Exists() then return false end
if not self:IsInRange(target) then return false end
if not self.traits.ignoreFacing then
if not player:IsFacing(target) then return false end
end
if not self.traits.ignoreLoS then
if not player:CanSee(target) then return false end
end
end
if not self.traits.ignoreCasting then
if player:IsCasting() then return false end
end
if not self.traits.ignoreChanneling then
if player:IsChanneling() then return false end
end
if not self.traits.ignoreGCD then
if player:GetGCD() > C_Spell.GetSpellQueueWindow() then return false end
end
if not self.traits.ignoreMoving then
if self:GetCastLength() > 0 and player:IsMoving() then return false end
end
return true
end
-- Set a script to check if the spell is castable
@ -307,9 +416,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
@ -340,6 +449,9 @@ 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
@ -347,6 +459,10 @@ end
---@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
@ -355,6 +471,7 @@ end
---@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())
@ -396,20 +513,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
@ -431,6 +578,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
@ -523,9 +674,7 @@ end
-- IsDiseaseDispel
---@return boolean
function Spell:IsDiseaseDispel()
return ({
})[self:GetID()]
return ({})[self:GetID()]
end
-- IsSpell
@ -538,6 +687,10 @@ 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

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

@ -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,
}
@ -422,7 +422,7 @@ function Unit:GetCastingOrChannelingSpell()
end
if name then
return Bastion.SpellBook:GetSpell(spellId)
return Bastion.Globals.SpellBook:GetSpell(spellId)
end
return nil
@ -505,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
@ -596,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
@ -607,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)
@ -617,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()
@ -838,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
@ -926,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
@ -960,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()
@ -1107,6 +1106,7 @@ end
---@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
@ -1114,7 +1114,7 @@ function Unit:IsWithinCone(Target, Angle, Distance, rotation)
end
local angle = self:GetAngle(Target)
local rotation = rotation or self:GetFacing()
rotation = rotation or self:GetFacing()
local diff = math.abs(angle - rotation)

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

@ -1,82 +1,136 @@
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
---@type ClassMagic
Bastion.ClassMagic = Bastion.require("ClassMagic")
---@type List
Bastion.List = Bastion.require("List")
---@type NotificationsList, Notification
Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList")
---@type Vector3
Bastion.Vector3 = Bastion.require("Vector3")
---@type Sequencer
Bastion.Sequencer = Bastion.require("Sequencer")
---@type Command
Bastion.Command = Bastion.require("Command")
---@type Cache
Bastion.Cache = Bastion.require("Cache")
---@type Cacheable
Bastion.Cacheable = Bastion.require("Cacheable")
---@type Refreshable
Bastion.Refreshable = Bastion.require("Refreshable")
---@type Unit
Bastion.Unit = Bastion.require("Unit")
---@type Aura
Bastion.Aura = Bastion.require("Aura")
---@type APL, APLActor, APLTrait
Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL")
---@type Module
Bastion.Module = Bastion.require("Module")
---@type UnitManager
Bastion.UnitManager = Bastion.require("UnitManager"):New()
---@type ObjectManager
Bastion.ObjectManager = Bastion.require("ObjectManager"):New()
---@type EventManager
Bastion.EventManager = Bastion.require("EventManager"):New()
---@type Spell
Bastion.Spell = Bastion.require("Spell")
---@type SpellBook
Bastion.SpellBook = Bastion.require("SpellBook"):New()
---@type Item
Bastion.Item = Bastion.require("Item")
---@type ItemBook
Bastion.ItemBook = Bastion.require("ItemBook"):New()
---@type AuraTable
Bastion.AuraTable = Bastion.require("AuraTable")
---@type Class
Bastion.Class = Bastion.require("Class")
---@type Timer
Bastion.Timer = Bastion.require("Timer")
---@type Timer
Bastion.CombatTimer = Bastion.Timer:New('combat')
---@type MythicPlusUtils
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type NotificationsList
Bastion.Notifications = Bastion.NotificationsList:New()
Bastion.modules = {}
Bastion.Enabled = false
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
-- fenv for all required files
function Bastion.Bootstrap()
Bastion.Globals = {}
---@type ClassMagic
Bastion.ClassMagic = Bastion.require("ClassMagic")
---@type List
Bastion.List = Bastion.require("List")
---@type Library
Bastion.Library = Bastion.require("Library")
---@type NotificationsList, Notification
Bastion.NotificationsList, Bastion.Notification = Bastion.require(
"NotificationsList")
---@type Vector3
Bastion.Vector3 = Bastion.require("Vector3")
---@type Sequencer
Bastion.Sequencer = Bastion.require("Sequencer")
---@type Command
Bastion.Command = Bastion.require("Command")
---@type Cache
Bastion.Cache = Bastion.require("Cache")
---@type Cacheable
Bastion.Cacheable = Bastion.require("Cacheable")
---@type Refreshable
Bastion.Refreshable = Bastion.require("Refreshable")
---@type Unit
Bastion.Unit = Bastion.require("Unit")
---@type Aura
Bastion.Aura = Bastion.require("Aura")
---@type APL, APLActor, APLTrait
Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL")
---@type Module
Bastion.Module = Bastion.require("Module")
---@type UnitManager
Bastion.UnitManager = Bastion.require("UnitManager"):New()
---@type ObjectManager
Bastion.ObjectManager = Bastion.require("ObjectManager"):New()
---@type EventManager
Bastion.EventManager = Bastion.require("EventManager")
Bastion.Globals.EventManager = Bastion.EventManager:New()
---@type Spell
Bastion.Spell = Bastion.require("Spell")
---@type SpellBook
Bastion.SpellBook = Bastion.require("SpellBook")
Bastion.Globals.SpellBook = Bastion.SpellBook:New()
---@type Item
Bastion.Item = Bastion.require("Item")
---@type ItemBook
Bastion.ItemBook = Bastion.require("ItemBook")
Bastion.Globals.ItemBook = Bastion.ItemBook:New()
---@type AuraTable
Bastion.AuraTable = Bastion.require("AuraTable")
---@type Class
Bastion.Class = Bastion.require("Class")
---@type Timer
Bastion.Timer = Bastion.require("Timer")
---@type Timer
Bastion.CombatTimer = Bastion.Timer:New('combat')
---@type MythicPlusUtils
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type NotificationsList
Bastion.Notifications = Bastion.NotificationsList:New()
local LIBRARIES = {}
local MODULES = {}
Bastion.Enabled = false
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA',
function(unit, auras)
local u = Bastion.UnitManager[unit]
if u then
u:GetAuras():OnUpdate(auras)
end
end)
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()
@ -85,12 +139,14 @@ Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
spell:GetPostCastFunction()(spell)
end
end
end)
end)
local pguid = UnitGUID("player")
local missed = {}
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = { CombatLogGetCurrentEventInfo() }
local pguid = UnitGUID("player")
local missed = {}
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED",
function()
local args = {CombatLogGetCurrentEventInfo()}
local subEvent = args[2]
local sourceGUID = args[4]
@ -110,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
@ -131,100 +186,109 @@ Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
end
end
end
end)
Bastion.Ticker = C_Timer.NewTicker(0.1, function()
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()
for i = 1, #MODULES do MODULES[i]:Tick() end
end
end
end)
end)
function Bastion:Register(module)
table.insert(Bastion.modules, module)
function Bastion:Register(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
-- Find a module by name
function Bastion:FindModule(name)
for i = 1, #MODULES do
if MODULES[i].name == name then return MODULES[i] end
end
return nil
end
end
function Bastion:Print(...)
local args = { ... }
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
local args = { ... }
function Bastion:Debug(...)
if not Bastion.DebugMode then return end
local args = {...}
local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
end
for i = 1, #args do str = str .. tostring(args[i]) .. " " end
print(str)
end
end
local Command = Bastion.Command:New('bastion')
local Command = Bastion.Command:New('bastion')
Command:Register('toggle', 'Toggle bastion on/off', function()
Command:Register('toggle', 'Toggle bastion on/off', function()
Bastion.Enabled = not Bastion.Enabled
if Bastion.Enabled then
Bastion:Print("Enabled")
else
Bastion:Print("Disabled")
end
end)
end)
Command:Register('debug', 'Toggle debug mode on/off', function()
Command:Register('debug', 'Toggle debug mode on/off', function()
Bastion.DebugMode = not Bastion.DebugMode
if Bastion.DebugMode then
Bastion:Print("Debug mode enabled")
else
Bastion:Print("Debug mode disabled")
end
end)
Command:Register('dumpspells', 'Dump spells to a file', function()
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
spellName = spellName:gsub("[%W%s]", "")
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand .. '.lua',
"local " .. spellName .. " = Bastion.SpellBook:GetSpell(" .. spellID .. ")", true)
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand ..
'.lua',
"local " .. spellName ..
" = Bastion.Globals.SpellBook:GetSpell(" ..
spellID .. ")\n", true)
end
i = i + 1
end
end)
end)
Command:Register('module', 'Toggle a module on/off', function(args)
Command:Register('module', 'Toggle a module on/off', function(args)
local module = Bastion:FindModule(args[2])
if module then
module:Toggle()
@ -236,19 +300,22 @@ Command:Register('module', 'Toggle a module on/off', function(args)
else
Bastion:Print("Module not found")
end
end)
end)
Command:Register('mplus', 'Toggle m+ module on/off', function(args)
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
@ -256,19 +323,90 @@ Command:Register('mplus', 'Toggle m+ module on/off', function(args)
Bastion:Print("Available commands:")
Bastion:Print("debuffs")
Bastion:Print("casts")
end)
end)
Command:Register('missed', 'Dump the list of immune kidney shot spells',
function()
for k, v in pairs(missed) do Bastion:Print(k) end
end)
Command:Register('missed', 'Dump the list of immune kidney shot spells', function()
for k, v in pairs(missed) do
Bastion:Print(k)
---@param library Library
function Bastion:RegisterLibrary(library)
LIBRARIES[library.name] = library
end
end)
local files = ListFiles("scripts/bastion/scripts")
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
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)
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