Compare commits

..

No commits in common. 'main' and '4n0n-patch-1' have entirely different histories.

  1. 6
      .gitignore
  2. 6
      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. 60
      src/APL/APL.lua
  8. 20
      src/Aura/Aura.lua
  9. 357
      src/AuraTable/AuraTable.lua
  10. 15
      src/Class/Class.lua
  11. 7
      src/Command/Command.lua
  12. 8
      src/Item/Item.lua
  13. 115
      src/Library/Library.lua
  14. 10
      src/List/List.lua
  15. 11
      src/MythicPlusUtils/MythicPlusUtils.lua
  16. 7
      src/ObjectManager/ObjectManager.lua
  17. 3
      src/Sequencer/Sequencer.lua
  18. 30
      src/Spell/Spell.lua
  19. 13
      src/SpellBook/SpellBook.lua
  20. 40
      src/Unit/Unit.lua
  21. 46
      src/UnitManager/UnitManager.lua
  22. 173
      src/_bastion.lua

6
.gitignore vendored

@ -7,13 +7,7 @@ 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/*

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

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

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

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

@ -1,25 +0,0 @@
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,4 +1,5 @@
-- Document with emmy lua: https://emmylua.github.io/
-- Create an APL trait for the APL class
---@class APLTrait
local APLTrait = {}
@ -54,7 +55,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
@ -84,11 +85,13 @@ 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
@ -108,22 +111,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)
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().target,
self:GetActor().condition)
end
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast(self
:GetActor().target)
end
if self:GetActor().item then
if self:GetActor().condition then
-- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target,
self:GetActor().condition)
else
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target)
end
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target)
end
if self:GetActor().action then
-- print("Bastion: APL:Execute: Executing action " .. self:GetActor().action)
@ -185,11 +189,7 @@ 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,17 +199,14 @@ 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? string|fun(...):boolean
---@param condition? fun(...):boolean
---@return APLActor
function APL:AddSpell(spell, condition)
local castableFunc = spell.CastableIfFunc
@ -231,18 +228,13 @@ 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)
@ -257,10 +249,7 @@ 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
@ -268,12 +257,14 @@ end
-- Execute the APL
function APL:Execute()
for _, actor in ipairs(self.apl) do
if actor:HasTraits() then
if actor:Evaluate() and actor:Execute() then
if actor:HasTraits() and actor:Evaluate() then
if actor:Execute() then
print("BREAQK", actor)
break
end
else
if actor:Execute() then
print("BREAQK", actor)
break
end
end
@ -285,10 +276,7 @@ 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,4 +1,5 @@
-- Document with emmy lua: https://emmylua.github.io/
local Tinkr, Bastion = ...
-- Create a new Aura class
@ -65,18 +66,19 @@ function Aura:New(unit, index, type)
timeMod = 0,
index = nil,
type = nil
type = nil,
}
if self.aura.spellId then
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
Bastion.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)
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 = {
@ -98,10 +100,10 @@ function Aura:New(unit, index, type)
auraInstanceID = nil,
index = index,
type = type
type = type,
}
if self.aura.spellId then
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
Bastion.SpellBook:GetSpell(self.aura.spellId)
end
return self
end
@ -130,11 +132,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.Globals.SpellBook:GetSpell(self.aura.spellId)
Bastion.SpellBook:GetSpell(self.aura.spellId)
return self
end
@ -237,7 +239,7 @@ end
-- Get the auras spell id
---@return Spell
function Aura:GetSpell()
return Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
return Bastion.SpellBook:GetSpell(self.aura.spellId)
end
-- Get the auras can apply aura status

@ -118,7 +118,7 @@ end
-- Get a units buffs
---@return nil
function AuraTable:GetUnitBuffs()
if Tinkr.classic or Tinkr.era then
if Tinkr.classic 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 or Tinkr.era then
if Tinkr.classic 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 or Tinkr.era then
if not Tinkr.classic 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 or Tinkr.era then
if not Tinkr.classic then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -334,7 +334,7 @@ function AuraTable:FindFrom(spell, source)
return a
end
else
if not Tinkr.classic or Tinkr.era then
if not Tinkr.classic then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -362,7 +362,7 @@ function AuraTable:FindTheirs(spell)
return a
end
else
if not Tinkr.classic or Tinkr.era then
if not Tinkr.classic then
self:RemoveInstanceID(a:GetAuraInstanceID())
end
end
@ -384,351 +384,6 @@ 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,21 +18,12 @@ 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,
@ -59,16 +50,10 @@ 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,13 +1,6 @@
-- 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

@ -54,7 +54,7 @@ function Item:New(id)
local name, spellID = GetItemSpell(self:GetID())
if spellID then
self.spellID = spellID
Bastion.Globals.SpellBook:GetSpell(spellID)
Bastion.SpellBook:GetSpell(spellID)
end
return self
@ -239,7 +239,7 @@ function Item:Click(x, y, z)
if type(x) == 'table' then
x, y, z = x.x, x.y, x.z
end
if IsSpellPending() == 64 then
if IsItemPending() == 64 then
MouselookStop()
Click(x, y, z)
if self:GetWasLooking() then
@ -302,7 +302,7 @@ end
-- Get the last use time
---@return number
function Item:GetLastUseTime()
return Bastion.Globals.SpellBook:GetSpell(self:GetID()):GetLastCastTime()
return Bastion.SpellBook:GetSpell(self:GetID()):GetLastCastTime()
end
-- Get time since last use
@ -430,7 +430,7 @@ end
---@return Spell | nil
function Item:GetSpell()
if self.spellID then
return Bastion.Globals.SpellBook:GetSpell(self.spellID)
return Bastion.SpellBook:GetSpell(self.spellID)
end
return nil

@ -1,115 +0,0 @@
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,3 +1,5 @@
local Tinkr, Bastion = ...
---@class List
local List = {
-- Add overload
@ -114,12 +116,8 @@ end
---@return boolean
function List:reduce(callback, initialValue)
local result = initialValue
local done = false
for _, v in ipairs(self._list) do
result, done = callback(result, v)
if done then
break
end
result = callback(result, v)
end
return result
end
@ -146,7 +144,7 @@ function List:findIndex(callback)
return nil
end
---@param callback fun(...): boolean
---@param callback fun(value: any): boolean
---@return nil
function List:sort(callback)
table.sort(self._list, callback)

@ -17,7 +17,6 @@ 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 = {
@ -430,7 +429,7 @@ function MythicPlusUtils:New()
}
}
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
if not self.debuffLogging then
return
end
@ -453,7 +452,7 @@ function MythicPlusUtils:New()
end
end)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_START', function(unitTarget, castGUID, spellID)
Bastion.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_START', function(unitTarget, castGUID, spellID)
if not self.castLogging then
return
end
@ -472,7 +471,7 @@ function MythicPlusUtils:New()
]], true)
end)
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_CHANNEL_START', function(unitTarget, castGUID, spellID)
Bastion.EventManager:RegisterWoWEvent('UNIT_SPELLCAST_CHANNEL_START', function(unitTarget, castGUID, spellID)
if not self.castLogging then
return
end
@ -568,4 +567,8 @@ function MythicPlusUtils:IsAOEBoss(unit)
return self.aoeBosses[unit:GetID()]
end
function MythicPlusUtils:IsTankBuster(spell)
return self.tankBusters[spell:GetID()]
end
return MythicPlusUtils

@ -1,11 +1,6 @@
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
@ -25,7 +20,7 @@ end
-- Register a custom list with a callback
---@param name string
---@param cb function
---@return List | false
---@return List
function ObjectManager:RegisterList(name, cb)
if self._lists[name] then
return false

@ -1,4 +1,5 @@
-- 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
@ -44,10 +45,12 @@ 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,11 +12,11 @@ local Spell = {
lastCastAt = false,
conditions = {},
target = false,
release_at = false
release_at = false,
}
local usableExcludes = {
[18562] = true
[18562] = true,
}
function Spell:__index(k)
@ -57,12 +57,6 @@ 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()
@ -161,15 +155,11 @@ end
-- Cast the spell
---@param unit Unit
---@param condition? string|function
---@param condition string
---@return boolean
function Spell:Cast(unit, condition)
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
if condition and not self:EvaluateCondition(condition) then
return false
end
if not self:Castable() then
@ -317,9 +307,9 @@ function Spell:GetWasLooking()
end
-- Click the spell
---@param x number|Vector3
---@param y? number
---@param z? number
---@param x number
---@param y number
---@param z number
---@return boolean
function Spell:Click(x, y, z)
if type(x) == 'table' then
@ -533,7 +523,9 @@ end
-- IsDiseaseDispel
---@return boolean
function Spell:IsDiseaseDispel()
return ({})[self:GetID()]
return ({
})[self:GetID()]
end
-- IsSpell

@ -27,24 +27,13 @@ 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)

@ -13,7 +13,7 @@ local Unit = {
last_off_attack = 0,
last_main_attack = 0,
last_combat_time = 0,
ttd_ticker = false,
ttd_ticker = 0,
ttd = 0,
id = false,
}
@ -422,7 +422,7 @@ function Unit:GetCastingOrChannelingSpell()
end
if name then
return Bastion.Globals.SpellBook:GetSpell(spellId)
return Bastion.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 or Tinkr.era then
if Tinkr.classic then
if not unit then
return 0
end
@ -607,7 +607,7 @@ end
---@return number
function Unit:GetComboPointsMax()
if Tinkr.classic or Tinkr.era then
if Tinkr.classic 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 or Tinkr.era then
if Tinkr.classic then
return self:GetComboPointsMax() - self:GetComboPoints(unit)
end
return self:GetComboPointsMax() - self:GetComboPoints()
@ -784,8 +784,7 @@ function Unit:PredictHealth(time)
table.remove(self.regression_history, 1)
end
local currentTime = GetTime()
table.insert(self.regression_history, { time = currentTime, percent = self:GetHP() })
table.insert(self.regression_history, { time = GetTime(), percent = self:GetHP() })
for i = 1, #self.regression_history do
local entry = self.regression_history[i]
@ -794,12 +793,9 @@ function Unit:PredictHealth(time)
end
local slope, intercept = self:LinearRegression(x, y)
return slope * (currentTime + time) + intercept
return slope * time + intercept
end
-- Use linear regression to guess the time until a given health percent
---@param percent number
---@return number
@ -842,7 +838,7 @@ function Unit:TimeToDie()
self.regression_history = {}
if self.ttd_ticker then
self.ttd_ticker:Cancel()
self.ttd_ticker = false
self.ttd_ticker = nil
end
return 0
end
@ -930,12 +926,13 @@ end
-- IsStealthed
---@return boolean
function Unit:IsStealthed()
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)
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)
return self:GetAuras():FindAny(Stealth) or self:GetAuras():FindAny(ShadowDance)
end
@ -963,7 +960,7 @@ end
---@return nil
function Unit:WatchForSwings()
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName, _, amount, interrupt, a, b, c, d, offhand, multistrike =
CombatLogGetCurrentEventInfo()
@ -1110,7 +1107,6 @@ 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
@ -1118,7 +1114,7 @@ function Unit:IsWithinCone(Target, Angle, Distance, rotation)
end
local angle = self:GetAngle(Target)
rotation = rotation or self:GetFacing()
local rotation = rotation or self:GetFacing()
local diff = math.abs(angle - rotation)

@ -4,6 +4,49 @@ 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 = {
@ -65,6 +108,9 @@ function UnitManager:New()
return self
end
function UnitManager:Validate(token)
return Validate(token)
end
-- Get or create a unit
---@param token string

@ -6,58 +6,14 @@ local Bastion = {
}
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 require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
return Bastion:Require("~/src/" .. class .. "/" .. class)
return Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
end
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
@ -85,18 +41,15 @@ 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()
Bastion.EventManager = Bastion.require("EventManager"):New()
---@type Spell
Bastion.Spell = Bastion.require("Spell")
---@type SpellBook
Bastion.SpellBook = Bastion.require("SpellBook")
Bastion.Globals.SpellBook = Bastion.SpellBook:New()
Bastion.SpellBook = Bastion.require("SpellBook"):New()
---@type Item
Bastion.Item = Bastion.require("Item")
---@type ItemBook
Bastion.ItemBook = Bastion.require("ItemBook")
Bastion.Globals.ItemBook = Bastion.ItemBook:New()
Bastion.ItemBook = Bastion.require("ItemBook"):New()
---@type AuraTable
Bastion.AuraTable = Bastion.require("AuraTable")
---@type Class
@ -109,13 +62,10 @@ Bastion.CombatTimer = Bastion.Timer:New('combat')
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type NotificationsList
Bastion.Notifications = Bastion.NotificationsList:New()
local LIBRARIES = {}
local MODULES = {}
Bastion.modules = {}
Bastion.Enabled = false
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
Bastion.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
local u = Bastion.UnitManager[unit]
if u then
@ -123,10 +73,10 @@ Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras)
end
end)
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
local unit, castGUID, spellID = ...
local spell = Bastion.Globals.SpellBook:GetIfRegistered(spellID)
local spell = Bastion.SpellBook:GetIfRegistered(spellID)
if unit == "player" and spell then
spell.lastCastAt = GetTime()
@ -139,9 +89,8 @@ end)
local pguid = UnitGUID("player")
local missed = {}
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = {CombatLogGetCurrentEventInfo()}
Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = { CombatLogGetCurrentEventInfo() }
local subEvent = args[2]
local sourceGUID = args[4]
@ -183,7 +132,6 @@ Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", fun
end
end
end)
Bastion.Ticker = C_Timer.NewTicker(0.1, function()
if not Bastion.CombatTimer:IsRunning() and UnitAffectingCombat("player") then
Bastion.CombatTimer:Start()
@ -193,22 +141,22 @@ Bastion.Ticker = C_Timer.NewTicker(0.1, function()
if Bastion.Enabled then
Bastion.ObjectManager:Refresh()
for i = 1, #MODULES do
MODULES[i]:Tick()
for i = 1, #Bastion.modules do
Bastion.modules[i]:Tick()
end
end
end)
function Bastion:Register(module)
table.insert(MODULES, module)
table.insert(Bastion.modules, module)
Bastion:Print("Registered", module)
end
-- Find a module by name
function Bastion:FindModule(name)
for i = 1, #MODULES do
if MODULES[i].name == name then
return MODULES[i]
for i = 1, #Bastion.modules do
if Bastion.modules[i].name == name then
return Bastion.modules[i]
end
end
@ -216,7 +164,7 @@ function Bastion:FindModule(name)
end
function Bastion:Print(...)
local args = {...}
local args = { ... }
local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
@ -228,7 +176,7 @@ function Bastion:Debug(...)
if not Bastion.DebugMode then
return
end
local args = {...}
local args = { ... }
local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF"
for i = 1, #args do
str = str .. tostring(args[i]) .. " "
@ -255,16 +203,13 @@ Command:Register('debug', 'Toggle debug mode on/off', function()
Bastion:Print("Debug mode disabled")
end
end)
Command:Register('dumpspells', 'Dump spells to a file', function()
local i = 1
local rand = math.random(100000, 999999)
while true do
local spellName, spellSubName = GetSpellBookItemName(i, BOOKTYPE_SPELL)
if not spellName then
do
break
end
do break end
end
-- use spellName and spellSubName here
@ -273,7 +218,7 @@ Command:Register('dumpspells', 'Dump spells to a file', function()
if spellID then
spellName = spellName:gsub("[%W%s]", "")
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand .. '.lua',
"local " .. spellName .. " = Bastion.Globals.SpellBook:GetSpell(" .. spellID .. ")\n", true)
"local " .. spellName .. " = Bastion.SpellBook:GetSpell(" .. spellID .. ")", true)
end
i = i + 1
end
@ -319,79 +264,11 @@ Command:Register('missed', 'Dump the list of immune kidney shot spells', functio
end
end)
---@param library Library
function Bastion:RegisterLibrary(library)
LIBRARIES[library.name] = library
end
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)
local files = ListFiles("scripts/bastion/scripts")
if not lib then
error("Library " .. library .. " not found")
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)
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("@")

Loading…
Cancel
Save