main
ck 12 months ago
parent a572688294
commit 031c68009c
  1. 297
      src/APL/APL.lua
  2. 141
      src/APLActor/APLActor.lua
  3. 42
      src/APLTrait/APLTrait.lua
  4. 24
      src/Aura/Aura.lua
  5. 136
      src/AuraTable/AuraTable.lua
  6. 5
      src/Cache/Cache.lua
  7. 23
      src/Cacheable/Cacheable.lua
  8. 8
      src/Class/Class.lua
  9. 2
      src/ClassMagic/ClassMagic.lua
  10. 7
      src/Command/Command.lua
  11. 2
      src/Config/Config.lua
  12. 13
      src/EventManager/EventManager.lua
  13. 48
      src/Item/Item.lua
  14. 8
      src/ItemBook/ItemBook.lua
  15. 10
      src/Library/Library.lua
  16. 45
      src/List/List.lua
  17. 11
      src/Module/Module.lua
  18. 27
      src/MythicPlusUtils/MythicPlusUtils.lua
  19. 69
      src/Notification/Notification.lua
  20. 92
      src/NotificationList/NotificationList.lua
  21. 147
      src/NotificationsList/NotificationsList.lua
  22. 69
      src/ObjectManager/ObjectManager.lua
  23. 4
      src/Refreshable/Refreshable.lua
  24. 6
      src/Sequencer/Sequencer.lua
  25. 165
      src/Spell/Spell.lua
  26. 20
      src/SpellBook/SpellBook.lua
  27. 46
      src/TimeToDie/TimeToDie.lua
  28. 5
      src/Timer/Timer.lua
  29. 75
      src/Unit/Unit.lua
  30. 57
      src/UnitManager/UnitManager.lua
  31. 164
      src/Vector3/Vector3.lua
  32. 183
      src/_bastion.lua

@ -1,174 +1,9 @@
-- Create an APL trait for the APL class
---@class APLTrait
---@field cb fun(actor?: APLActorTable):boolean
---@field lastcall number
local APLTrait = {}
APLTrait.__index = APLTrait
-- Constructor
---@param cb fun(actor?: APLActorTable):boolean
---@return APLTrait
function APLTrait:New(cb)
local self = setmetatable({}, APLTrait)
self.cb = cb
self.lastcall = 0
return self
end
-- Evaulate the APL trait
---@param actor? APLActorTable
---@return boolean
function APLTrait:Evaluate(actor)
if GetTime() - self.lastcall > 0.1 then
self.lastresult = self.cb(actor)
self.lastcall = GetTime()
return self.lastresult
end
return self.lastresult
end
-- tostring
---@return string
function APLTrait:__tostring()
return "Bastion.__APLTrait"
end
---@class APLActorTableBase
---@field type "spell" | "item" | "apl" | "sequencer" | "variable" | "action"
---@alias APLActorTable APLActorSpellTable | APLActorItemTable | APLActorAPLTable | APLActorSequencerTable | APLActorVariableTable | APLActorActionTable
-- Create an APL actor for the APL class
---@class APLActor
---@field actor APLActorTable
---@field traits APLTrait[]
---@field name string
local APLActor = {}
APLActor.__index = APLActor
-- Constructor
---@param actor APLActorTable
function APLActor:New(actor)
local self = setmetatable({}, APLActor)
if actor.type == "spell" then
self.name = string.format("[%s] `%s`<%s>", "spell", actor.spell:GetName(), actor.spell:GetID())
elseif actor.type == "item" then
self.name = string.format("[%s] `%s`<%s>", "item", actor.item:GetName(), actor.item:GetID())
elseif actor.type == "apl" then
self.name = string.format("[%s] `%s`", "apl", actor.apl.name)
elseif actor.type == "sequencer" then
self.name = string.format("[%s]", "sequencer")
elseif actor.type == "variable" then
self.name = string.format("[%s] `%s`", "variable", actor.variable)
elseif actor.type == "action" then
self.name = string.format("[%s] `%s`", "action", actor.action)
else
self.name = string.format("[%s] Unknown", actor.type)
end
self.actor = actor
self.traits = {}
return self
end
-- Add a trait to the APL actor
---@param ... APLTrait
---@return APLActor
function APLActor:AddTraits(...)
for _, trait in ipairs({ ... }) do
table.insert(self.traits, trait)
end
return self
end
-- Get the actor
---@return APLActorTable
function APLActor:GetActor()
return self.actor
end
-- Evaulate the APL actor
---@return boolean
function APLActor:Evaluate()
for _, trait in ipairs(self.traits) do
if not trait:Evaluate(self:GetActor()) then
return false
end
end
return true
end
-- Execute
function APLActor:Execute()
local actorTable = self:GetActor()
-- If the actor is a sequencer we don't want to continue executing the APL if the sequencer is not finished
if actorTable.type == "sequencer" then
---@cast actorTable APLActorSequencerTable
if actorTable.condition and actorTable.condition() and not actorTable.sequencer:Finished() then
actorTable.sequencer:Execute()
return true
end
if not actorTable.condition and not actorTable.sequencer:Finished() then
actorTable.sequencer:Execute()
return true
end
-- Check if the sequencer can be reset and reset it if it can
if actorTable.sequencer:ShouldReset() then
actorTable.sequencer:Reset()
end
end
if actorTable.type == "apl" then
---@cast actorTable APLActorAPLTable
if actorTable.condition and actorTable.condition() then
-- print("Bastion: APL:Execute: Executing sub APL " .. actorTable.apl.name)
if actorTable.apl:Execute() then
return true
end
end
end
if actorTable.type == "spell" then
---@cast actorTable APLActorSpellTable
return actorTable.spell:CastableIf(actorTable.castableFunc):OnCast(actorTable.onCastFunc):Cast(actorTable.target, actorTable.condition)
end
if actorTable.type == "item" then
---@cast actorTable APLActorItemTable
return actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target, actorTable.condition)
end
if self:GetActor().type == "action" then
---@cast actorTable APLActorActionTable
-- print("Bastion: APL:Execute: Executing action " .. actorTable.action)
self:GetActor().cb()
end
if actorTable.type == "variable" then
---@cast actorTable APLActorVariableTable
-- print("Bastion: APL:Execute: Setting variable " .. actorTable.variable)
actorTable._apl.variables[actorTable.variable] = actorTable.cb(actorTable._apl)
end
return false
end
-- has traits
---@return boolean
function APLActor:HasTraits()
return #self.traits > 0
end
-- tostring
---@return string
function APLActor:__tostring()
return string.format("Bastion.__APLActor(%s)", self.name)
end
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
-- APL (Attack priority list) class
---@class APL
---@field apl APLActor[]
---@class Bastion.APL
---@field apl Bastion.APLActor[]
---@field variables table<string, any>
---@field name string
---@field last { successful: { name: string, time: number, index: number }, attempted: { name: string, time: number, index: number } }
@ -177,14 +12,25 @@ APL.__index = APL
-- Constructor
---@param name string
---@return APL
---@return Bastion.APL
function APL:New(name)
local self = setmetatable({}, APL)
self.apl = {}
self.variables = {}
self.name = name
self.last = {}
self.last = {
successful = {
name = "",
time = -1,
index = -1,
},
attempted = {
name = "",
time = -1,
index = -1,
},
}
return self
end
@ -203,17 +49,17 @@ function APL:GetVariable(name)
return self.variables[name]
end
---@class APLActorVariableTable : APLActorTableBase
---@class Bastion.APL.Actor.Variable.Table : Bastion.APLActor.Table.Base
---@field variable string
---@field cb fun(...):any
---@field _apl APL
---@field _apl Bastion.APL
-- Add variable
---@param name string
---@param cb fun(...):any
---@return APLActor
---@return Bastion.APLActor
function APL:AddVariable(name, cb)
local actor = APLActor:New({
local actor = Bastion.APLActor:New({
type = "variable",
variable = name,
cb = cb,
@ -223,16 +69,16 @@ function APL:AddVariable(name, cb)
return actor
end
---@class APLActorActionTable : APLActorTableBase
---@class Bastion.APL.Actor.Action.Table : Bastion.APLActor.Table.Base
---@field action string
---@field cb fun(...):any
-- Add a manual action to the APL
---@param action string
---@param cb fun(...):any
---@return APLActor
---@return Bastion.APLActor
function APL:AddAction(action, cb)
local actor = APLActor:New({
local actor = Bastion.APLActor:New({
type = "action",
action = action,
cb = cb,
@ -241,23 +87,23 @@ function APL:AddAction(action, cb)
return actor
end
---@class APLActorSpellTable : APLActorTableBase
---@field spell Spell
---@field condition? string|fun(self: Spell): boolean
---@field castableFunc false | fun(self: Spell): boolean
---@field target Unit | false
---@field onCastFunc false | fun(self: Spell):any
---@class Bastion.APL.Actor.Spell.Table : Bastion.APLActor.Table.Base
---@field spell Bastion.Spell
---@field condition? string|fun(self: Bastion.Spell): boolean
---@field castableFunc false | fun(self: Bastion.Spell): boolean
---@field target Bastion.Unit | false
---@field onCastFunc false | fun(self: Bastion.Spell):any
-- Add a spell to the APL
---@param spell Spell
---@param condition? string|fun(self: Spell):boolean
---@return APLActor
---@param spell Bastion.Spell
---@param condition? string|fun(self: Bastion.Spell):boolean
---@return Bastion.APLActor
function APL:AddSpell(spell, condition)
local castableFunc = spell.CastableIfFunc
local onCastFunc = spell.OnCastFunc
local target = spell:GetTarget()
local actor = APLActor:New({
local actor = Bastion.APLActor:New({
type = "spell",
spell = spell,
condition = condition,
@ -271,21 +117,21 @@ function APL:AddSpell(spell, condition)
return actor
end
---@class APLActorItemTable : APLActorTableBase
---@field item Item
---@field condition? string | fun(self: Item): boolean
---@class Bastion.APL.Actor.Item.Table : Bastion.APLActor.Table.Base
---@field item Bastion.Item
---@field condition? string | fun(self: Bastion.Item): boolean
---@field usableFunc false | fun(...): boolean
---@field target Unit | nil
---@field target Bastion.Unit | nil
-- Add an item to the APL
---@param item Item
---@param condition? string | fun(self: Item): boolean
---@return APLActor
---@param item Bastion.Item
---@param condition? string | fun(self: Bastion.Item): boolean
---@return Bastion.APLActor
function APL:AddItem(item, condition)
local usableFunc = item.UsableIfFunc
local target = item:GetTarget()
local actor = APLActor:New({
local actor = Bastion.APLActor:New({
type = "item",
item = item,
condition = condition,
@ -298,19 +144,19 @@ function APL:AddItem(item, condition)
return actor
end
---@class APLActorAPLTable : APLActorTableBase
---@field apl APL
---@class Bastion.APL.Actor.APL.Table : Bastion.APLActor.Table.Base
---@field apl Bastion.APL
---@field condition? fun(...):boolean
-- Add an APL to the APL (for sub APLs)
---@param apl APL
---@param apl Bastion.APL
---@param condition fun(...):boolean
---@return APLActor
---@return Bastion.APLActor
function APL:AddAPL(apl, condition)
if not condition then
error("Bastion: APL:AddAPL: No condition for APL " .. apl.name)
end
local actor = APLActor:New({
local actor = Bastion.APLActor:New({
type = "apl",
apl = apl,
condition = condition,
@ -319,35 +165,50 @@ function APL:AddAPL(apl, condition)
return actor
end
---@param name string
---@param enabled? boolean
function APL:ToggleAPL(name, enabled)
for _, actor in ipairs(self.apl) do
if actor.actor.type == "apl" and actor.name == name then
actor.enabled = type(enabled) == "boolean" and enabled or not actor.enabled
break
end
end
end
function APL:UpdateLastAttempted(name, index)
self.last.attempted.name = name
self.last.attempted.time = GetTime()
self.last.attempted.index = index
end
function APL:UpdateLastSuccessful(name, index)
self.last.successful.name = name
self.last.successful.time = GetTime()
self.last.successful.index = index
end
-- Execute the APL
function APL:Execute()
for i, actor in ipairs(self.apl) do
self.last.attempted = {
name = actor.name,
time = GetTime(),
index = i,
}
if (not actor:HasTraits() or actor:Evaluate()) and actor:Execute() then
self.last.successful = {
name = actor.name,
time = GetTime(),
index = i,
}
self:UpdateLastAttempted(actor.name, i)
if actor.enabled and (not actor:HasTraits() or actor:Evaluate()) and actor:Execute() then
self:UpdateLastSuccessful(actor.name, i)
return true
end
end
end
---@class APLActorSequencerTable : APLActorTableBase
---@field sequencer Sequencer
---@class Bastion.APL.Actor.Sequencer.Table : Bastion.APLActor.Table.Base
---@field sequencer Bastion.Sequencer
---@field condition? fun(...):boolean
-- Add a Sequencer to the APL
---@param sequencer Sequencer
---@param sequencer Bastion.Sequencer
---@param condition fun(...):boolean
---@return APLActor
---@return Bastion.APLActor
function APL:AddSequence(sequencer, condition)
local actor = APLActor:New({
local actor = Bastion.APLActor:New({
type = "sequencer",
sequencer = sequencer,
condition = condition,
@ -362,4 +223,4 @@ function APL:__tostring()
return "Bastion.__APL(" .. self.name .. ")"
end
return APL, APLActor, APLTrait
return APL

@ -0,0 +1,141 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class Bastion.APLActor.Table.Base
---@field type "spell" | "item" | "apl" | "sequencer" | "variable" | "action"
---@alias Bastion.APLActor.Table Bastion.APL.Actor.Spell.Table | Bastion.APL.Actor.Item.Table | Bastion.APL.Actor.APL.Table | Bastion.APL.Actor.Sequencer.Table | Bastion.APL.Actor.Variable.Table | Bastion.APL.Actor.Action.Table
-- Create an APL actor for the APL class
---@class Bastion.APLActor
---@field actor Bastion.APLActor.Table
---@field enabled boolean
---@field traits Bastion.APLTrait[]
---@field name string
local APLActor = {}
APLActor.__index = APLActor
-- Constructor
---@param actor Bastion.APLActor.Table
function APLActor:New(actor)
local self = setmetatable({}, APLActor)
if actor.type == "spell" then
local name = actor.spell:GetName() or "Unknown"
local id = actor.spell:GetID() or 0
self.name = string.format("[%s] `%s`<%s>", "spell", name, id)
elseif actor.type == "item" then
local name = actor.item:GetName() or "Unknown"
local id = actor.item:GetID() or 0
self.name = string.format("[%s] `%s`<%s>", "item", name, id)
elseif actor.type == "apl" then
self.name = string.format("[%s] `%s`", "apl", actor.apl.name)
elseif actor.type == "sequencer" then
self.name = string.format("[%s]", "sequencer")
elseif actor.type == "variable" then
self.name = string.format("[%s] `%s`", "variable", actor.variable)
elseif actor.type == "action" then
self.name = string.format("[%s] `%s`", "action", actor.action)
else
self.name = string.format("[%s] Unknown", actor.type)
end
self.actor = actor
self.enabled = true
self.traits = {}
return self
end
-- Add a trait to the APL actor
---@param ... Bastion.APLTrait
---@return Bastion.APLActor
function APLActor:AddTraits(...)
for _, trait in ipairs({ ... }) do
table.insert(self.traits, trait)
end
return self
end
-- Get the actor
---@return Bastion.APLActor.Table
function APLActor:GetActor()
return self.actor
end
-- Evaulate the APL actor
---@return boolean
function APLActor:Evaluate()
for _, trait in ipairs(self.traits) do
if not trait:Evaluate(self:GetActor()) then
return false
end
end
return true
end
-- Execute
function APLActor:Execute()
local actorTable = self:GetActor()
-- If the actor is a sequencer we don't want to continue executing the APL if the sequencer is not finished
if actorTable.type == "sequencer" then
---@cast actorTable Bastion.APL.Actor.Sequencer.Table
if actorTable.condition and actorTable.condition() and not actorTable.sequencer:Finished() then
actorTable.sequencer:Execute()
return true
end
if not actorTable.condition and not actorTable.sequencer:Finished() then
actorTable.sequencer:Execute()
return true
end
-- Check if the sequencer can be reset and reset it if it can
if actorTable.sequencer:ShouldReset() then
actorTable.sequencer:Reset()
end
end
if actorTable.type == "apl" then
---@cast actorTable Bastion.APL.Actor.APL.Table
if actorTable.condition and actorTable.condition() then
-- print("Bastion: APL:Execute: Executing sub APL " .. actorTable.apl.name)
if actorTable.apl:Execute() then
return true
end
end
end
if actorTable.type == "spell" then
---@cast actorTable Bastion.APL.Actor.Spell.Table
return actorTable.spell:CastableIf(actorTable.castableFunc):OnCast(actorTable.onCastFunc):Cast(actorTable.target,
actorTable.condition)
end
if actorTable.type == "item" then
---@cast actorTable Bastion.APL.Actor.Item.Table
return actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target, actorTable.condition)
end
if self:GetActor().type == "action" then
---@cast actorTable Bastion.APL.Actor.Action.Table
-- print("Bastion: APL:Execute: Executing action " .. actorTable.action)
self:GetActor().cb()
end
if actorTable.type == "variable" then
---@cast actorTable Bastion.APL.Actor.Variable.Table
-- print("Bastion: APL:Execute: Setting variable " .. actorTable.variable)
actorTable._apl.variables[actorTable.variable] = actorTable.cb(actorTable._apl)
end
return false
end
-- has traits
---@return boolean
function APLActor:HasTraits()
return #self.traits > 0
end
-- tostring
---@return string
function APLActor:__tostring()
return string.format("Bastion.__APLActor(%s)", self.name)
end
return APLActor

@ -0,0 +1,42 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
-- Create an APL trait for the APL class
---@class Bastion.APLTrait
---@field cb fun(actor?: Bastion.APLActor.Table):boolean
---@field lastcall number
local APLTrait = {}
APLTrait.__index = APLTrait
-- Constructor
---@param cb fun(actor?: Bastion.APLActor.Table):boolean
---@return Bastion.APLTrait
function APLTrait:New(cb)
local self = setmetatable({}, APLTrait)
self.cb = cb
self.lastcall = 0
return self
end
-- Evaulate the APL trait
---@param actor? Bastion.APLActor.Table
---@return boolean
function APLTrait:Evaluate(actor)
if GetTime() - self.lastcall > 0.1 then
self.lastresult = self.cb(actor)
self.lastcall = GetTime()
return self.lastresult
end
return self.lastresult
end
-- tostring
---@return string
function APLTrait:__tostring()
return "Bastion.__APLTrait"
end
return APLTrait

@ -2,7 +2,7 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class AuraAuraData
---@class Bastion.Aura.Aura.Data
---@field auraInstanceID? number
---@field name? string
---@field icon? number
@ -24,8 +24,8 @@ local Tinkr, Bastion = ...
---@field type? string
-- Create a new Aura class
---@class Aura
---@field aura AuraAuraData
---@class Bastion.Aura
---@field aura Bastion.Aura.Aura.Data
local Aura = {}
function Aura:__index(k)
@ -43,7 +43,7 @@ function Aura:__index(k)
end
-- Equals
---@param other Aura | Spell
---@param other Bastion.Aura | Bastion.Spell
---@return boolean
function Aura:__eq(other)
if getmetatable(other) == Aura then
@ -64,12 +64,12 @@ function Aura:__tostring()
end
-- Constructor
---@param unit? Unit
---@param unit? Bastion.Unit
---@param index? number
---@param type? string
function Aura:New(unit, index, type)
if unit == nil or index == nil or type == nil then
---@class Aura
---@class Bastion.Aura
local self = setmetatable({}, Aura)
self.aura = {
name = nil,
@ -101,7 +101,7 @@ function Aura:New(unit, index, type)
local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod, v1, v2, v3, v4 =
UnitAura(unit:GetOMToken(), index, type)
---@class Aura
---@class Bastion.Aura
local self = setmetatable({}, Aura)
self.aura = {
name = name,
@ -165,9 +165,9 @@ end
-- Constructor
---@param unitAuraInfo AuraData
---@return Aura
---@return Bastion.Aura
function Aura:CreateFromUnitAuraInfo(unitAuraInfo)
---@class Aura
---@class Bastion.Aura
local self = setmetatable({}, Aura)
--[[
applications
@ -298,7 +298,7 @@ function Aura:GetExpirationTime()
end
-- Get the auras source
---@return Unit
---@return Bastion.Unit
function Aura:GetSource()
return Bastion.UnitManager[self.aura.source]
end
@ -316,7 +316,7 @@ function Aura:GetNameplateShowPersonal()
end
-- Get the auras spell id
---@return Spell
---@return Bastion.Spell
function Aura:GetSpell()
return Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
end
@ -370,7 +370,7 @@ function Aura:GetAuraInstanceID()
end
-- Check if the aura is dispelable by a spell
---@param spell Spell
---@param spell Bastion.Spell
function Aura:IsDispelableBySpell(spell)
if (self:GetDispelType() == "" or self:GetDispelType() == nil) and self:GetIsStealable() and spell:IsEnrageDispel() then
return true

@ -3,20 +3,20 @@ local Tinkr, Bastion = ...
---@alias auraInstanceId integer
---@class AuraInstanceTable
---@field [spellId] { [auraInstanceId]: Aura }
---@class Bastion.Aura.Instance.Table
---@field [spellId] { [auraInstanceId]: Bastion.Aura }
-- Create a new AuraTable class
---@class AuraTable
---@field unit Unit
---@field playerAuras AuraInstanceTable
---@field auras AuraInstanceTable
---@class Bastion.AuraTable
---@field unit Bastion.Unit
---@field playerAuras Bastion.Aura.Instance.Table
---@field auras Bastion.Aura.Instance.Table
local AuraTable = {}
AuraTable.__index = AuraTable
-- Constructor
---@param unit Unit
---@return AuraTable
---@param unit Bastion.Unit
---@return Bastion.AuraTable
function AuraTable:New(unit)
local self = setmetatable({}, AuraTable)
@ -103,7 +103,7 @@ end
-- Update the aura table
---@param instanceID number
---@param aura Aura
---@param aura Bastion.Aura
---@return nil
function AuraTable:AddOrUpdateAuraInstanceID(instanceID, aura)
local spellId = aura:GetSpell():GetID()
@ -219,7 +219,7 @@ function AuraTable:Update()
end
-- Get a units auras
---@return table<number, table<number, Aura>>
---@return table<number, table<number, Bastion.Aura>>
function AuraTable:GetUnitAuras()
if not self.did then
self.did = true
@ -274,8 +274,8 @@ function AuraTable:Clear()
end
-- Check if the unit has a specific aura
---@param spell Spell
---@return Aura
---@param spell Bastion.Spell
---@return Bastion.Aura
function AuraTable:Find(spell)
local auras = self:GetUnitAuras()
local aurasub = auras[spell:GetID()]
@ -300,8 +300,8 @@ function AuraTable:Find(spell)
end
-- Check if the unit has a specific aura
---@param spell Spell
---@return Aura
---@param spell Bastion.Spell
---@return Bastion.Aura
function AuraTable:FindMy(spell)
local auras = self:GetMyUnitAuras()
local aurasub = auras[spell:GetID()]
@ -326,9 +326,9 @@ function AuraTable:FindMy(spell)
end
-- Check if the unit has a specific aura
---@param spell Spell
---@param source "any" | Unit
---@return Aura
---@param spell Bastion.Spell
---@param source "any" | Bastion.Unit
---@return Bastion.Aura
function AuraTable:FindFrom(spell, source)
if type(source) == "string" and source == "any" then
return self:FindAny(spell)
@ -359,8 +359,8 @@ function AuraTable:FindFrom(spell, source)
end
-- Find the aura from the current unit
---@param spell Spell
---@return Aura
---@param spell Bastion.Spell
---@return Bastion.Aura
function AuraTable:FindTheirs(spell)
local auras = self:GetUnitAuras()
local aurasub = auras[spell:GetID()]
@ -387,8 +387,8 @@ function AuraTable:FindTheirs(spell)
end
-- Find any
---@param spell Spell
---@return Aura
---@param spell Bastion.Spell
---@return Bastion.Aura
function AuraTable:FindAny(spell)
local a = self:Find(spell)
if a:IsValid() then
@ -399,8 +399,8 @@ function AuraTable:FindAny(spell)
end
-- FindAnyOf
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindAnyOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindAny(cur)
@ -412,8 +412,8 @@ function AuraTable:FindAnyOf(spells)
end
-- FindAnyOfMy
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindAnyOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
@ -425,8 +425,8 @@ function AuraTable:FindAnyOfMy(spells)
end
-- FindAnyOfTheirs
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindAnyOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
@ -438,9 +438,9 @@ function AuraTable:FindAnyOfTheirs(spells)
end
-- FindAnyFrom
---@param spells List
---@param source Unit
---@return Aura
---@param spells Bastion.List
---@param source Bastion.Unit
---@return Bastion.Aura
function AuraTable:FindAnyFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
@ -452,8 +452,8 @@ function AuraTable:FindAnyFrom(spells, source)
end
-- FindLongestOf
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindLongestOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
@ -470,8 +470,8 @@ function AuraTable:FindLongestOf(spells)
end
-- FindLongestOfMy
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindLongestOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
@ -488,8 +488,8 @@ function AuraTable:FindLongestOfMy(spells)
end
-- FindLongestOfTheirs
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindLongestOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
@ -506,9 +506,9 @@ function AuraTable:FindLongestOfTheirs(spells)
end
-- FindLongestOfFrom
---@param spells List
---@param source Unit
---@return Aura
---@param spells Bastion.List
---@param source Bastion.Unit
---@return Bastion.Aura
function AuraTable:FindLongestOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
@ -525,8 +525,8 @@ function AuraTable:FindLongestOfFrom(spells, source)
end
-- FindShortestOf
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindShortestOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
@ -543,8 +543,8 @@ function AuraTable:FindShortestOf(spells)
end
-- FindShortestOfMy
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindShortestOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
@ -561,8 +561,8 @@ function AuraTable:FindShortestOfMy(spells)
end
-- FindShortestOfTheirs
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindShortestOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
@ -579,9 +579,9 @@ function AuraTable:FindShortestOfTheirs(spells)
end
-- FindShortestOfFrom
---@param spells List
---@param source Unit
---@return Aura
---@param spells Bastion.List
---@param source Bastion.Unit
---@return Bastion.Aura
function AuraTable:FindShortestOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
@ -598,8 +598,8 @@ function AuraTable:FindShortestOfFrom(spells, source)
end
-- FindMostOf
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindMostOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
@ -616,8 +616,8 @@ function AuraTable:FindMostOf(spells)
end
-- FindMostOfMy
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindMostOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
@ -634,8 +634,8 @@ function AuraTable:FindMostOfMy(spells)
end
-- FindMostOfTheirs
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindMostOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
@ -652,9 +652,9 @@ function AuraTable:FindMostOfTheirs(spells)
end
-- FindMostOfFrom
---@param spells List
---@param source Unit
---@return Aura
---@param spells Bastion.List
---@param source Bastion.Unit
---@return Bastion.Aura
function AuraTable:FindMostOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
@ -671,8 +671,8 @@ function AuraTable:FindMostOfFrom(spells, source)
end
-- FindLeastOf
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindLeastOf(spells)
return spells:reduce(function(acc, cur)
local aura = self:Find(cur)
@ -689,8 +689,8 @@ function AuraTable:FindLeastOf(spells)
end
-- FindLeastOfMy
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindLeastOfMy(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindMy(cur)
@ -707,8 +707,8 @@ function AuraTable:FindLeastOfMy(spells)
end
-- FindLeastOfTheirs
---@param spells List
---@return Aura
---@param spells Bastion.List
---@return Bastion.Aura
function AuraTable:FindLeastOfTheirs(spells)
return spells:reduce(function(acc, cur)
local aura = self:FindTheirs(cur)
@ -725,9 +725,9 @@ function AuraTable:FindLeastOfTheirs(spells)
end
-- FindLeastOfFrom
---@param spells List
---@param source Unit
---@return Aura
---@param spells Bastion.List
---@param source Bastion.Unit
---@return Bastion.Aura
function AuraTable:FindLeastOfFrom(spells, source)
return spells:reduce(function(acc, cur)
local aura = self:FindFrom(cur, source)
@ -744,7 +744,7 @@ function AuraTable:FindLeastOfFrom(spells, source)
end
-- Has any stealable aura
---@param spell? Spell
---@param spell? Bastion.Spell
---@return boolean
function AuraTable:HasAnyStealableAura(spell)
for _, auras in pairs(self:GetUnitAuras()) do
@ -781,7 +781,7 @@ function AuraTable:GetStealableAura()
end
-- Has any dispelable aura
---@param spell Spell
---@param spell Bastion.Spell
---@return boolean
function AuraTable:HasAnyDispelableAura(spell)
for _, auras in pairs(self:GetUnitAuras()) do

@ -1,9 +1,9 @@
---@class Cache
---@class Bastion.Cache
local Cache = {}
Cache.__index = Cache
-- Constructor
---@return Cache
---@return Bastion.Cache
function Cache:New()
local self = setmetatable({}, Cache)
self.cache = {}
@ -13,7 +13,6 @@ end
---@param key any
---@param value any
---@param duration number
---@return nil
function Cache:Set(key, value, duration)
self.cache = self.cache or {}
self.cache[key] = {

@ -2,12 +2,13 @@
local Tinkr, Bastion = ...
---@class Cacheable
---@field cache? Cache
---@class Bastion.Cacheable
---@field cache? Bastion.Cache
local Cacheable = {
cache = nil,
callback = nil,
value = nil,
args = nil,
-- __eq = function(self, other)
-- return self.value.__eq(self.value, other)
-- end
@ -24,7 +25,7 @@ function Cacheable:__index(k)
end
if not self.cache:IsCached("self") then
self.value = self.callback()
self.value = self.callback(SafeUnpack(self.args))
self.cache:Set("self", self.value, 0.5)
end
@ -38,14 +39,17 @@ end
-- Create
---@generic V
---@generic P
---@param value V
---@param cb fun(): V
---@param cb fun(...:P): V
---@param ... P
---@return V
function Cacheable:New(value, cb)
function Cacheable:New(value, cb, ...)
local self = setmetatable({}, Cacheable)
self.cache = Bastion.Cache:New()
self.value = value
self.args = SafePack(...)
self.callback = cb
self.cache:Set("self", self.value, 0.5)
@ -62,7 +66,7 @@ end
-- Update the value
function Cacheable:Update()
self.value = self.callback()
self.value = self.callback(SafeUnpack(self.args))
end
-- Set a new value
@ -72,9 +76,12 @@ function Cacheable:Set(value)
end
-- Set a new callback
---@param cb fun():any
function Cacheable:SetCallback(cb)
---@generic P
---@param cb fun(...: P):any
---@param ... P
function Cacheable:SetCallback(cb, ...)
self.callback = cb
self.args = SafePack(...)
end
return Cacheable

@ -2,7 +2,8 @@
local Tinkr, Bastion = ...
-- Create a new Class class
---@class Class
---@class Bastion.Class
---@field class Class.Class
local Class = {}
function Class:__index(k)
@ -19,10 +20,7 @@ function Class:__index(k)
return response
end
---@class Class
---@field class Class.class
---@class Class.class
---@class Class.Class
---@field locale string
---@field name string
---@field id number

@ -1,4 +1,4 @@
---@class ClassMagic
---@class Bastion.ClassMagic
local ClassMagic = {}
ClassMagic.__index = ClassMagic

@ -1,10 +1,11 @@
-- Create a wow command handler class
---@class Command
---@class Bastion.Command
---@field command string
---@field commands Command.commands[]
---@field commands Bastion.Command.Commands[]
---@field prefix string
local Command = {}
---@class Command.commands
---@class Bastion.Command.Commands
---@field helpmsg string
---@field cb fun(args: table)

@ -1,7 +1,7 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class Config
---@class Bastion.Config
---@field instantiated boolean
---@field config Tinkr.Util.Config.Instance
---@field defaults nil | table

@ -2,25 +2,18 @@
local Tinkr, Bastion = ...
-- Create an EventManager class
---@class EventManager
---@class Bastion.EventManager
---@field frame Frame
---@field events table<string, { [number]: fun(...) }>
---@field eventHandlers table<string, { [number]: fun(...) }>
---@field wowEventHandlers table<string, { [number]: fun(...) }>
---@field selfCombatEventHandlers table<string, { [number]: fun(...) }>
---@field CombatEventHandlers table<string, { [number]: fun(...) }>
local EventManager = {
events = {},
eventHandlers = {},
wowEventHandlers = {},
selfCombatEventHandlers = {},
CombatEventHandlers = {},
frame = nil,
}
local EventManager = {}
EventManager.__index = EventManager
-- Constructor
---@return EventManager
---@return Bastion.EventManager
function EventManager:New()
local self = setmetatable({}, EventManager)
self.events = {}

@ -2,13 +2,13 @@
local Tinkr, Bastion = ...
-- Create a new Item class
---@class Item
---@class Bastion.Item
---@field ItemID number
---@field UsableIfFunc boolean | fun(self:Item):boolean
---@field PreUseFunc boolean | fun(self:Item)
---@field target boolean | Unit
---@field conditions table<string, { func: fun(self:Item):boolean }>
---@field OnUseFunc boolean | fun(self:Item)
---@field UsableIfFunc boolean | fun(self:Bastion.Item):boolean
---@field PreUseFunc boolean | fun(self:Bastion.Item)
---@field target boolean | Bastion.Unit
---@field conditions table<string, { func: fun(self:Bastion.Item):boolean }>
---@field OnUseFunc boolean | fun(self:Bastion.Item)
---@field spellID number | nil
local Item = {
UsableIfFunc = false,
@ -47,7 +47,7 @@ function Item:__index(k)
end
-- Equals
---@param other Item
---@param other Bastion.Item
---@return boolean
function Item:__eq(other)
return self:GetID() == other:GetID()
@ -126,8 +126,8 @@ function Item:GetCooldownRemaining()
end
-- Use the Item
---@param unit Unit
---@param condition? string | fun(self:Item):boolean
---@param unit Bastion.Unit
---@param condition? string | fun(self:Bastion.Item):boolean
---@return boolean
function Item:Use(unit, condition)
if condition then
@ -219,24 +219,24 @@ function Item:Usable()
end
-- Set a script to check if the Item is Usable
---@param func fun(self:Item):boolean
---@return Item
---@param func fun(self:Bastion.Item):boolean
---@return Bastion.Item
function Item:UsableIf(func)
self.UsableIfFunc = func
return self
end
-- Set a script to run before the Item has been Use
---@param func fun(self:Item)
---@return Item
---@param func fun(self:Bastion.Item)
---@return Bastion.Item
function Item:PreUse(func)
self.PreUseFunc = func
return self
end
-- Set a script to run after the Item has been Use
---@param func fun(self:Item)
---@return Item
---@param func fun(self:Bastion.Item)
---@return Bastion.Item
function Item:OnUse(func)
self.OnUseFunc = func
return self
@ -269,7 +269,7 @@ function Item:Click(x, y, z)
end
-- Check if the Item is Usable and Use it
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Item:Call(unit)
if self:Usable() then
@ -280,7 +280,7 @@ function Item:Call(unit)
end
-- Check if the Item is in range of the unit
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Item:IsInRange(unit)
local name, rank, icon, UseTime, Itemmin, Itemmax, ItemID = GetItemInfo(self:GetID())
@ -343,8 +343,8 @@ end
-- Create a condition for the Item
---@param name string
---@param func fun(self:Item)
---@return Item
---@param func fun(self:Bastion.Item)
---@return Bastion.Item
function Item:Condition(name, func)
self.conditions[name] = {
func = func,
@ -388,15 +388,15 @@ function Item:HasCondition(name)
end
-- Set the Items target
---@param unit Unit
---@return Item
---@param unit Bastion.Unit
---@return Bastion.Item
function Item:SetTarget(unit)
self.target = unit
return self
end
-- Get the Items target
---@return Unit | boolean
---@return Bastion.Unit | boolean
function Item:GetTarget()
return self.target
end
@ -431,14 +431,14 @@ function Item:IsDiseaseDispel()
return ({})[self:GetID()]
end
---@param item Item
---@param item Bastion.Item
---@return boolean
function Item:IsItem(item)
return self:GetID() == item:GetID()
end
-- Get the Items spell
---@return Spell | nil
---@return Bastion.Spell | nil
function Item:GetSpell()
if self.spellID then
return Bastion.Globals.SpellBook:GetSpell(self.spellID)

@ -4,13 +4,13 @@ local Tinkr, Bastion = ...
---@alias itemId integer
-- Create a new ItemBook class
---@class ItemBook
---@field items table<itemId, Item>
---@class Bastion.ItemBook
---@field items table<itemId, Bastion.Item>
local ItemBook = {}
ItemBook.__index = ItemBook
-- Constructor
---@return ItemBook
---@return Bastion.ItemBook
function ItemBook:New()
local self = setmetatable({}, ItemBook)
self.items = {}
@ -19,7 +19,7 @@ end
-- Get a spell from the ItemBook
---@param id number
---@return Item
---@return Bastion.Item
function ItemBook:GetItem(id)
if self.items[id] == nil then
self.items[id] = Bastion.Item:New(id)

@ -1,7 +1,7 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class Library
---@class Bastion.Library
---@field name string | nil
---@field dependencies table
---@field exports table
@ -19,12 +19,12 @@ local Library = {
Library.__index = Library
---@class NewLibrary
---@class Bastion.Library.New
---@field name string
---@field exports? { default: fun(self: Library):any }
---@field exports? { default: fun(self: Bastion.Library):any }
---@param library NewLibrary
---@return Library
---@param library Bastion.Library.New
---@return Bastion.Library
function Library:New(library)
local self = {
name = library.name or nil,

@ -1,20 +1,22 @@
---@class List
---@operator add(any): List
---@operator sub(any): List
---@class Bastion.List<I>: { _list: I[] }
---@operator add(any): Bastion.List
---@operator sub(any): Bastion.List
local List = {
-- Add overload
---@param self List
---@param value any
---@return List
---@param self Bastion.List
---@generic I
---@param value I
---@return Bastion.List<I>
__add = function(self, value)
self:push(value)
return self
end,
-- Subtract overload
---@param self List
---@param value any
---@return List
---@param self Bastion.List
---@generic I
---@param value I
---@return Bastion.List<I>
__sub = function(self, value)
self:remove(value)
return self
@ -22,8 +24,9 @@ local List = {
}
List.__index = List
---@param from table | nil
---@return List
---@generic I : table
---@param from? I[]
---@return Bastion.List<I> | Bastion.List
function List:New(from)
local self = setmetatable({}, List)
self._list = from or {}
@ -31,7 +34,6 @@ function List:New(from)
end
---@param value any
---@return nil
function List:push(value)
table.insert(self._list, value)
end
@ -92,8 +94,8 @@ function List:each(callback)
end
---@generic I : any
---@param callback fun(value: I): boolean
---@return List
---@param callback fun(value: I): I
---@return Bastion.List<I> | Bastion.List
function List:map(callback)
local newList = List:New()
for _, v in ipairs(self._list) do
@ -104,7 +106,7 @@ end
---@generic I : any
---@param callback fun(value: I): boolean
---@return List
---@return Bastion.List<I> | Bastion.List
function List:filter(callback)
local newList = List:New()
for _, v in ipairs(self._list) do
@ -134,7 +136,7 @@ end
---@generic I
---@param callback fun(value: I): boolean
---@return boolean | nil
---@return I
function List:find(callback)
for _, v in ipairs(self._list) do
if callback(v) then
@ -144,7 +146,8 @@ function List:find(callback)
return nil
end
---@param callback fun(value: any): boolean
---@generic I
---@param callback fun(value: I): boolean
---@return number | nil
function List:findIndex(callback)
for i, v in ipairs(self._list) do
@ -155,13 +158,13 @@ function List:findIndex(callback)
return nil
end
---@param callback fun(...): boolean
---@generic I : any
---@param callback fun(a: I, b: I): boolean
---@return nil
function List:sort(callback)
table.sort(self._list, callback)
end
---@return List
function List:reverse()
local newList = List:New()
for i = #self._list, 1, -1 do
@ -170,7 +173,6 @@ function List:reverse()
return newList
end
---@return List
function List:clone()
local newList = List:New()
for _, v in ipairs(self._list) do
@ -179,8 +181,7 @@ function List:clone()
return newList
end
---@param list List
---@return List
---@param list Bastion.List
function List:concat(list)
local newList = List:New()
for _, v in ipairs(self._list) do

@ -1,6 +1,6 @@
-- Create a module class for a bastion module
---@class Module
---@class Bastion.Module
---@field name string
---@field enabled boolean
---@field synced function[]
@ -15,7 +15,7 @@ end
-- Constructor
---@param name string
---@return Module
---@return Bastion.Module
function Module:New(name)
local module = {}
setmetatable(module, Module)
@ -28,13 +28,11 @@ function Module:New(name)
end
-- Enable the module
---@return nil
function Module:Enable()
self.enabled = true
end
-- Disable the module
---@return nil
function Module:Disable()
self.enabled = false
end
@ -51,25 +49,22 @@ end
-- Add a function to the sync list
---@param func function
---@return nil
function Module:Sync(func)
table.insert(self.synced, func)
end
-- Remove a function from the sync list
---@param func function
---@return nil
function Module:Unsync(func)
for i = 1, #self.synced do
if self.synced[i] == func then
table.remove(self.synced, i)
return
break
end
end
end
-- Sync
---@return nil
function Module:Tick()
if self.enabled then
for i = 1, #self.synced do

@ -1,20 +1,31 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class MythicPlusUtils
---@class Bastion.MythicPlusUtils.KickEntry
---@field isKick boolean
---@field isStun boolean
---@field isDisorient boolean
---@class Bastion.MythicPlusUtils
---@field random number
---@field aoeBosses table<number, boolean>
---@field tankBusters table<number, boolean>
---@field debuffLogging boolean
---@field castLogging boolean
---@field loggedCasts table<number, boolean>
---@field loggedDebuffs table<number, boolean>
---@field kickList table<spellId, table<number, boolean[]>>
local MythicPlusUtils = {
debuffLogging = false,
castLogging = false,
random = "",
loggedCasts = {},
loggedDebuffs = {},
kickList = {},
aoeBosses = {},
}
MythicPlusUtils.__index = MythicPlusUtils
---@return MythicPlusUtils
---@return Bastion.MythicPlusUtils
function MythicPlusUtils:New()
local self = setmetatable({}, MythicPlusUtils)
@ -227,7 +238,7 @@ function MythicPlusUtils:New()
false,
false,
},
}, -- Thundering Squall
}, -- Thundering Squall
[257899] = { -- Painful Motivation
[0] = {
true,
@ -1259,7 +1270,7 @@ function MythicPlusUtils:ToggleCastLogging()
self.castLogging = not self.castLogging
end
---@param unit Unit
---@param unit Bastion.Unit
---@param percent? number
---@return boolean
function MythicPlusUtils:CastingCriticalKick(unit, percent)
@ -1288,7 +1299,7 @@ function MythicPlusUtils:CastingCriticalKick(unit, percent)
return false
end
---@param unit Unit
---@param unit Bastion.Unit
---@param percent number
---@return boolean
function MythicPlusUtils:CastingCriticalStun(unit, percent)
@ -1317,7 +1328,7 @@ function MythicPlusUtils:CastingCriticalStun(unit, percent)
return false
end
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function MythicPlusUtils:IsAOEBoss(unit)
return self.aoeBosses[unit:GetID()]

@ -0,0 +1,69 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
-- Create a notification class for the notifications list (takes icon and text)
---@class Bastion.Notification
---@field icon Texture
---@field text FontString
---@field frame Frame
---@field addedAt number
---@field duration number
---@field list Bastion.NotificationList
local Notification = {
}
Notification.__index = Notification
-- Constructor
---@param list Bastion.NotificationList
---@param icon string
---@param text string
---@param duration number
---@return Bastion.Notification
function Notification:New(list, icon, text, duration)
local self = setmetatable({}, Notification)
if not duration then duration = 2 end
-- Create a frame for the notification
self.frame = CreateFrame("Frame", nil, list.frame)
self.frame:SetSize(5, 5)
self.frame:SetPoint("CENTER", list.frame, "CENTER", 0, 0)
self.frame:SetFrameStrata("HIGH")
-- Create a texture for the icon
self.icon = self.frame:CreateTexture(nil, "ARTWORK")
self.icon:SetSize(32, 32)
self.icon:SetPoint("LEFT", self.frame, "LEFT", 0, 0)
self.icon:SetTexture(icon)
-- Create a fontstring for the text
self.text = self.frame:CreateFontString(nil, "BACKGROUND", "NumberFontNormal")
self.text:SetPoint("LEFT", self.frame, "LEFT", 32 + 16, 0)
self.text:SetText(text)
self.text:SetFont("Fonts\\OpenSans-Bold.ttf", 18, "")
-- set the frame size to the size of the text + icon
self.frame:SetSize(self.text:GetStringWidth() + 32 + 16, 32)
self.addedAt = GetTime()
self.duration = duration
self.list = list
return self
end
-- Remove notification
---@return nil
function Notification:Remove()
-- Fade out the notification frame and remove it after the fade
UIFrameFadeOut(self.frame, 0.2, 1, 0)
C_Timer.After(0.5, function()
self.frame:Hide()
self.frame:ClearAllPoints()
self.frame:SetParent(nil)
self.frame = nil
self.list:Update()
end)
end
return Notification

@ -0,0 +1,92 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
-- Create a NotificationList class
---@class Bastion.NotificationList
---@field frame Frame
local NotificationList = {
notifications = {}
}
NotificationList.__index = NotificationList
-- Constructor
---@return Bastion.NotificationList
function NotificationList:New()
local self = setmetatable({}, NotificationList)
-- Create a frame for the notifications
self.frame = CreateFrame("Frame", "BastionNotificationList", UIParent)
self.frame:SetSize(600, 60)
self.frame:SetPoint("TOP", UIParent, "TOP", 0, -100)
self.frame:SetFrameStrata("HIGH")
-- Remove notifications after 5 seconds
C_Timer.NewTicker(0.1, function()
for i, notification in ipairs(self.notifications) do
if GetTime() - notification.addedAt > notification.duration then
notification:Remove()
table.remove(self.notifications, i)
end
end
end)
return self
end
-- Add a notification to the list
---@param icon string
---@param text string
---@param duration number
---@return nil
function NotificationList:AddNotification(icon, text, duration)
-- Create a new notification
local notification = Bastion.Notification:New(self, icon, text, duration)
-- Add the notification to the list
table.insert(self.notifications, notification)
UIFrameFadeIn(notification.frame, 0.2, 0, 1)
-- Update the notifications
self:Update()
end
-- Update the notifications
---@return nil
function NotificationList:Update()
-- Loop through the notifications
for i, notification in ipairs(self.notifications) do
-- Set the position of the notification
notification.frame:SetPoint("CENTER", self.frame, "CENTER", 0, -42 * (i - 1))
end
end
-- Remove a notification from the list
---@param notification Bastion.Notification
---@return nil
function NotificationList:RemoveNotification(notification)
-- Loop through the notifications
for i, v in ipairs(self.notifications) do
-- Check if the notification is the one we want to remove
if v == notification then
-- Remove the notification from the list
table.remove(self.notifications, i)
notification:Remove()
break
end
end
end
-- Remove all notifications from the list
---@return nil
function NotificationList:RemoveAllNotifications()
-- Loop through the notifications
for i, v in ipairs(self.notifications) do
-- Remove the notification from the list
table.remove(self.notifications, i)
self.notifications[i]:Remove()
end
end
-- Remove all notifications
return NotificationList

@ -1,147 +0,0 @@
-- Create a NotificationsList class
---@class NotificationsList
local NotificationsList = {
notifications = {}
}
NotificationsList.__index = NotificationsList
-- Constructor
---@return NotificationsList
function NotificationsList:New()
local self = setmetatable({}, NotificationsList)
-- Create a frame for the notifications
self.frame = CreateFrame("Frame", "BastionNotificationsList", UIParent)
self.frame:SetSize(600, 60)
self.frame:SetPoint("TOP", UIParent, "TOP", 0, -100)
self.frame:SetFrameStrata("HIGH")
-- Remove notifications after 5 seconds
C_Timer.NewTicker(0.1, function()
for i, notification in ipairs(self.notifications) do
if GetTime() - notification.addedAt > notification.duration then
notification:Remove()
table.remove(self.notifications, i)
end
end
end)
return self
end
-- Create a notification class for the notifications list (takes icon and text)
---@class Notification
local Notification = {
}
Notification.__index = Notification
-- Constructor
---@param list NotificationsList
---@param icon string
---@param text string
---@param duration number
---@return Notification
function Notification:New(list, icon, text, duration)
local self = setmetatable({}, Notification)
if not duration then duration = 2 end
-- Create a frame for the notification
self.frame = CreateFrame("Frame", nil, list.frame)
self.frame:SetSize(5, 5)
self.frame:SetPoint("CENTER", list.frame, "CENTER", 0, 0)
self.frame:SetFrameStrata("HIGH")
-- Create a texture for the icon
self.icon = self.frame:CreateTexture(nil, "ARTWORK")
self.icon:SetSize(32, 32)
self.icon:SetPoint("LEFT", self.frame, "LEFT", 0, 0)
self.icon:SetTexture(icon)
-- Create a fontstring for the text
self.text = self.frame:CreateFontString(nil, "BACKGROUND", "NumberFontNormal")
self.text:SetPoint("LEFT", self.frame, "LEFT", 32 + 16, 0)
self.text:SetText(text)
self.text:SetFont("Fonts\\OpenSans-Bold.ttf", 18)
-- set the frame size to the size of the text + icon
self.frame:SetSize(self.text:GetStringWidth() + 32 + 16, 32)
self.addedAt = GetTime()
self.duration = duration
self.list = list
return self
end
-- Remove notification
---@return nil
function Notification:Remove()
-- Fade out the notification frame and remove it after the fade
UIFrameFadeOut(self.frame, 0.2, 1, 0)
C_Timer.After(0.5, function()
self.frame:Hide()
self.frame:ClearAllPoints()
self.frame:SetParent(nil)
self.frame = nil
self.list:Update()
end)
end
-- Add a notification to the list
---@param icon string
---@param text string
---@param duration number
---@return nil
function NotificationsList:AddNotification(icon, text, duration)
-- Create a new notification
local notification = Notification:New(self, icon, text, duration)
-- Add the notification to the list
table.insert(self.notifications, notification)
UIFrameFadeIn(notification.frame, 0.2, 0, 1)
-- Update the notifications
self:Update()
end
-- Update the notifications
---@return nil
function NotificationsList:Update()
-- Loop through the notifications
for i, notification in ipairs(self.notifications) do
-- Set the position of the notification
notification.frame:SetPoint("CENTER", self.frame, "CENTER", 0, -42 * (i - 1))
end
end
-- Remove a notification from the list
---@param notification Notification
---@return nil
function NotificationsList:RemoveNotification(notification)
-- Loop through the notifications
for i, v in ipairs(self.notifications) do
-- Check if the notification is the one we want to remove
if v == notification then
-- Remove the notification from the list
table.remove(self.notifications, i)
notification:Remove()
break
end
end
end
-- Remove all notifications from the list
---@return nil
function NotificationsList:RemoveAllNotifications()
-- Loop through the notifications
for i, v in ipairs(self.notifications) do
-- Remove the notification from the list
table.remove(self.notifications, i)
self.notifications[i]:Remove()
end
end
-- Remove all notifications
return NotificationsList, Notification

@ -1,14 +1,16 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class ObjectManager
---@field _lists table<string, { list: List, cb: fun(object: TinkrObjectReference): false | Unit }>
---@field enemies List
---@field friends List
---@field activeEnemies List
---@field explosives List
---@field incorporeal List
---@field others List
---@class Bastion.ObjectManager
---@field _lists table<string, { list: Bastion.List, cb: fun(object: TinkrObjectReference): false | Bastion.Unit }>
---@field afflicted Bastion.List
---@field enemies Bastion.List
---@field critters Bastion.List
---@field friends Bastion.List
---@field activeEnemies Bastion.List
---@field explosives Bastion.List
---@field incorporeal Bastion.List
---@field others Bastion.List
local ObjectManager = {}
ObjectManager.__index = ObjectManager
@ -18,10 +20,12 @@ function ObjectManager:New()
self._lists = {}
self.enemies = Bastion.List:New()
self.critters = Bastion.List:New()
self.friends = Bastion.List:New()
self.activeEnemies = Bastion.List:New()
self.explosives = Bastion.List:New()
self.incorporeal = Bastion.List:New()
self.afflicted = Bastion.List:New()
self.others = Bastion.List:New()
return self
@ -29,8 +33,8 @@ end
-- Register a custom list with a callback
---@param name string
---@param cb fun(object: TinkrObjectReference): false | Unit
---@return List | false
---@param cb fun(object: TinkrObjectReference): false | Bastion.Unit
---@return Bastion.List | false
function ObjectManager:RegisterList(name, cb)
if self._lists[name] then
return false
@ -66,7 +70,7 @@ end
-- Get a list
---@param name string
---@return List
---@return Bastion.List
function ObjectManager:GetList(name)
return self._lists[name].list
end
@ -74,7 +78,9 @@ end
-- Refresh all lists
---@return nil
function ObjectManager:Refresh()
self.afflicted:clear()
self.enemies:clear()
self.critters:clear()
self.friends:clear()
self.activeEnemies:clear()
self.explosives:clear()
@ -88,25 +94,32 @@ function ObjectManager:Refresh()
self:EnumLists(object)
if ({ [5] = true, [6] = true, [7] = true })[ObjectType(object)] then
local unit = Bastion.UnitManager:GetObject(ObjectGUID(object))
if not unit then
unit = Bastion.Unit:New(object)
Bastion.UnitManager:SetObject(unit)
end
local objectGUID = ObjectGUID(object)
if objectGUID then
local unit = Bastion.UnitManager:GetObject(objectGUID)
if not unit then
unit = Bastion.Unit:New(object)
Bastion.UnitManager:SetObject(unit)
end
if unit:GetID() == 204560 then
self.incorporeal:push(unit)
elseif unit:GetID() == 120651 then
self.explosives:push(unit)
elseif unit:IsPlayer() and (unit:IsInParty() or unit == Bastion.UnitManager["player"]) then
self.friends:push(unit)
elseif unit:IsEnemy() then
self.enemies:push(unit)
if unit:InCombatOdds() > 80 then
self.activeEnemies:push(unit)
if ObjectType(object) == 5 and ObjectCreatureType(object) == 8 then
self.critters:push(unit)
elseif unit:GetID() == 204560 then
self.incorporeal:push(unit)
elseif unit:GetID() == 204773 then
self.afflicted:push(unit)
elseif unit:GetID() == 120651 then
self.explosives:push(unit)
elseif unit:IsPlayer() and (unit:IsInParty() or unit == Bastion.UnitManager["player"]) then
self.friends:push(unit)
elseif unit:IsEnemy() then
self.enemies:push(unit)
if unit:InCombatOdds() > 80 or unit:IsAffectingCombat() then
self.activeEnemies:push(unit)
end
else
self.others:push(unit)
end
else
self.others:push(unit)
end
end
end

@ -2,9 +2,9 @@
local Tinkr, Bastion = ...
-- Define a Refreshable class
---@class Refreshable
---@class Bastion.Refreshable
---@field cache Bastion.Cache
local Refreshable = {
cache = nil,
callback = nil,
value = nil,
__eq = function(self, other)

@ -1,17 +1,17 @@
-- Create a sequencer class that takes a table of actions and executes them in order
---@class Sequencer
---@class Bastion.Sequencer
---@field resetCondition? fun(): boolean
---@field abortCondition? fun(): boolean
---@field actions SequencerAction[]
local Sequencer = {}
Sequencer.__index = Sequencer
---@alias SequencerAction fun(sequence: Sequencer):boolean
---@alias SequencerAction fun(sequence: Bastion.Sequencer):boolean
-- Constructor
---@param actions table<number, SequencerAction>
---@param resetCondition? fun(): boolean
---@return Sequencer
---@return Bastion.Sequencer
function Sequencer:New(actions, resetCondition)
local self = setmetatable({}, Sequencer)

@ -1,66 +1,54 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@class Spell.Traits.Cast
---@class Bastion.Spell.Traits.Cast
---@field moving boolean
---@field dead boolean
---@field global boolean
---@field casting boolean
---@field channeling boolean
---@field override boolean
---@field talent boolean | spellId
---@class Spell.Traits.Target
---@class Bastion.Spell.Traits.Target
---@field exists boolean
---@field player boolean
---@class Spell.Traits
---@field cast Spell.Traits.Cast
---@field target Spell.Traits.Target
---@class Bastion.Spell.Traits
---@field cast Bastion.Spell.Traits.Cast
---@field target Bastion.Spell.Traits.Target
---@class Spell.Traits.Cast.Params : Spell.Traits.Cast, { [string]?: boolean }
---@class Spell.Traits.Target.Params : Spell.Traits.Target, { [string]?: boolean }
---@class Bastion.Spell.Traits.Cast.Params : Bastion.Spell.Traits.Cast, { [string]?: boolean }
---@class Spell.Traits.Target.Params : Bastion.Spell.Traits.Target, { [string]?: boolean }
---@class Spell.Traits.Params
---@field cast? Spell.Traits.Cast.Params
---@class Bastion.Spell.Traits.Params
---@field cast? Bastion.Spell.Traits.Cast.Params
---@field target? Spell.Traits.Target.Params
---@class Spell.Aura
---@field spell Spell
---@field source? Unit
---@field target? Unit
---@class Bastion.Spell.Aura
---@field spell Bastion.Spell
---@field source? Bastion.Unit
---@field target? Bastion.Unit
---@field lastApplied number
-- Create a new Spell class
---@class Spell
---@field auras table<spellId, Spell.Aura>
---@field CastableIfFunc false | fun(self:Spell):boolean
---@class Bastion.Spell
---@field auras table<spellId, Bastion.Spell.Aura>
---@field CastableIfFunc false | fun(self:Bastion.Spell):boolean
---@field damage number
---@field damageFormula false | fun(self:Spell):number
---@field damageFormula false | fun(self:Bastion.Spell):number
---@field lastCastAt number | false
---@field lastCastAttempt number | false
---@field OnCastFunc fun(self:Spell) | false
---@field overrides { [spellId]: fun(self: Spell): Spell }
---@field PostCastFunc fun(self:Spell) | false
---@field PreCastFunc fun(self:Spell) | false
---@field OnCastFunc fun(self:Bastion.Spell) | false
---@field overrides { [spellId]: fun(self: Bastion.Spell): Bastion.Spell }
---@field PostCastFunc fun(self:Bastion.Spell) | false
---@field PreCastFunc fun(self:Bastion.Spell) | false
---@field release_at false
---@field spellID number
---@field target Unit | false
---@field traits Spell.Traits
---@field target Bastion.Unit | false
---@field traits Bastion.Spell.Traits
---@field wasLooking boolean
local Spell = {
auras = {},
CastableIfFunc = false,
conditions = {},
damage = 0,
damageFormula = false,
lastCastAt = false,
lastCastAttempt = false,
OnCastFunc = false,
overrides = {},
PostCastFunc = false,
PreCastFunc = false,
release_at = false,
}
local Spell = {}
local usableExcludes = {
[18562] = true,
@ -81,7 +69,7 @@ function Spell:__index(k)
end
-- Equals
---@param other Spell
---@param other Bastion.Spell
function Spell:__eq(other)
return self:GetID() == other:GetID()
end
@ -94,9 +82,17 @@ end
-- Constructor
---@param id number
function Spell:New(id)
---@class Spell
---@class Bastion.Spell
local self = setmetatable({}, Spell)
self.auras = {}
self.CastableIfFunc = false
self.damage = 0
self.damageFormula = false
self.lastCastAt = false
self.lastCastAttempt = false
self.OnCastFunc = false
self.PostCastFunc = false
self.release_at = false
self.conditions = {}
self.overrides = {}
self.spellID = id
@ -108,6 +104,7 @@ function Spell:New(id)
casting = false,
channeling = false,
override = false,
talent = false,
},
target = {
exists = true,
@ -137,7 +134,7 @@ function Spell:GetID(ignoreOverride)
end
-- Add post cast func
---@param func fun(self:Spell)
---@param func fun(self:Bastion.Spell)
function Spell:PostCast(func)
self.PostCastFunc = func
return self
@ -202,21 +199,21 @@ function Spell:OnCooldown()
end
-- Clear castable function
---@return Spell
---@return Bastion.Spell
function Spell:ClearCastableFunction()
self.CastableIfFunc = false
return self
end
---@param spell Spell
---@param func fun(self: Spell): boolean
---@param spell Bastion.Spell
---@param func fun(self: Bastion.Spell): boolean
function Spell:AddOverrideSpell(spell, func)
self.overrides[spell:GetID()] = func
end
-- Cast the spell
---@param unit Unit
---@param condition? string | fun(self:Spell):boolean
---@param unit Bastion.Unit
---@param condition? string | fun(self:Bastion.Spell):boolean
---@return boolean
function Spell:Cast(unit, condition)
if condition then
@ -237,12 +234,14 @@ function Spell:Cast(unit, condition)
end
-- Check if the mouse was looking
---@type boolean
self.wasLooking = IsMouselooking()
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast
local u = unit and unit:GetOMToken() or self.traits.target.player and "none" or "none"
if type(u) == "string" and string.find(u, "nameplate") then
---@diagnostic disable-next-line: cast-local-type
u = Object(u)
end
@ -264,7 +263,7 @@ function Spell:Cast(unit, condition)
end
-- ForceCast the spell
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Spell:ForceCast(unit)
-- Check if the mouse was looking
@ -273,6 +272,7 @@ function Spell:ForceCast(unit)
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast
local u = unit and unit:GetOMToken() or self.traits.target.player and "none" or "none"
if type(u) == "string" and string.find(u, "nameplate") then
---@diagnostic disable-next-line: cast-local-type
u = Object(u)
end
@ -338,7 +338,7 @@ function Spell:IsKnownAndUsable(override)
return self:IsKnown(override) and not self:IsOnCooldown() and self:IsUsable(override ~= nil and false or true)
end
---@param traits Spell.Traits.Params
---@param traits Bastion.Spell.Traits.Params
function Spell:SetTraits(traits)
for _, trait in pairs({ "cast", "target" }) do
if type(traits[trait]) == "table" then
@ -353,7 +353,7 @@ function Spell:SetTraits(traits)
end
function Spell:EvaluateTraits()
local player = Bastion.UnitManager["player"]
local player = Bastion.UnitManager:Get("player")
if not self.traits.cast.global and player:GetGCD() > 0 then
return false
@ -379,6 +379,10 @@ function Spell:EvaluateTraits()
return false
end
if self.traits.cast.talent and not IsPlayerSpell(type(self.traits.cast.talent) == "number" and tonumber(self.traits.cast.talent) or self:GetID()) then
return false
end
return true
end
@ -388,28 +392,29 @@ function Spell:Castable()
return false
end
if self:GetCastableFunction() then
return self:GetCastableFunction()(self) and self:IsKnownAndUsable(self.traits.cast.override or nil)
return self:GetCastableFunction()(self) and
self:IsKnownAndUsable(type(self.traits.cast.override) ~= nil and self.traits.cast.override or nil)
end
return self:IsKnownAndUsable(self.traits.cast.override or nil)
return self:IsKnownAndUsable(type(self.traits.cast.override) ~= nil and self.traits.cast.override or nil)
end
-- Set a script to check if the spell is castable
---@param func fun(spell:Spell):boolean
---@param func fun(spell:Bastion.Spell):boolean
function Spell:CastableIf(func)
self.CastableIfFunc = func
return self
end
-- Set a script to run before the spell has been cast
---@param func fun(spell:Spell)
---@param func fun(spell:Bastion.Spell)
function Spell:PreCast(func)
self.PreCastFunc = func
return self
end
-- Set a script to run after the spell has been cast
---@param func fun(spell:Spell)
---@param func fun(spell:Bastion.Spell)
function Spell:OnCast(func)
self.OnCastFunc = func
return self
@ -422,7 +427,7 @@ function Spell:GetWasLooking()
end
-- Click the spell
---@param x number|Vector3
---@param x number|Bastion.Vector3
---@param y? number
---@param z? number
---@return boolean
@ -445,18 +450,12 @@ function Spell:Click(x, y, z)
end
-- Check if the spell is castable and cast it
---@param unit Unit
---@return boolean
---@param unit Bastion.Unit
function Spell:Call(unit)
if self:Castable() then
self:Cast(unit)
return true
end
return false
return self:Cast(unit)
end
-- Check if the spell is castable and cast it
---@return boolean
function Spell:HasRange()
return SpellHasRange(self:GetName())
end
@ -470,7 +469,7 @@ function Spell:GetRange()
end
-- Check if the spell is in range of the unit
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Spell:IsInRange(unit)
local hasRange = self:HasRange()
@ -484,7 +483,7 @@ function Spell:IsInRange(unit)
return true
end
return Bastion.UnitManager["player"]:InMelee(unit)
return Bastion.UnitManager:Get("player"):InMelee(unit)
end
-- Get the last cast time
@ -570,8 +569,8 @@ end
-- Create a condition for the spell
---@param name string
---@param func fun(self:Spell):boolean
---@return Spell
---@param func fun(self:Bastion.Spell):boolean
---@return Bastion.Spell
function Spell:Condition(name, func)
self.conditions[name] = {
func = func,
@ -581,7 +580,7 @@ end
-- Get a condition for the spell
---@param name string
---@return { func: fun(self: Spell): boolean } | nil
---@return { func: fun(self: Bastion.Spell): boolean } | nil
function Spell:GetCondition(name)
local condition = self.conditions[name]
if condition then
@ -616,8 +615,8 @@ function Spell:HasCondition(name)
end
-- Set the spells target
---@param unit Unit
---@return Spell
---@param unit Bastion.Unit
---@return Bastion.Spell
function Spell:SetTarget(unit)
self.target = unit
return self
@ -671,7 +670,7 @@ function Spell:IsDiseaseDispel()
end
-- IsSpell
---@param spell Spell
---@param spell Bastion.Spell
---@return boolean
function Spell:IsSpell(spell)
return self:GetID() == spell:GetID()
@ -690,7 +689,7 @@ function Spell:IsFree()
return self:GetCost() == 0
end
---@param damageFormula fun(self:Spell): number
---@param damageFormula fun(self:Bastion.Spell): number
function Spell:RegisterDamageFormula(damageFormula)
self.damageFormula = damageFormula
end
@ -704,19 +703,19 @@ function Spell:Damage()
end
end
---@param target Unit
---@param source? "any" | Unit
---@param target Bastion.Unit
---@param source? "any" | Bastion.Unit
function Spell:GetAura(target, source)
if type(source) == "nil" then
source = Bastion.UnitManager["player"]
source = Bastion.UnitManager:Get("player")
end
return target:GetAuras():FindFrom(self, source)
end
---@param spell Spell
---@param source? Unit
---@param target? Unit
---@param spell Bastion.Spell
---@param source? Bastion.Unit
---@param target? Bastion.Unit
function Spell:TrackAura(spell, source, target)
self.auras[spell:GetID()] = {
spell = spell,
@ -726,9 +725,9 @@ function Spell:TrackAura(spell, source, target)
}
end
---@param aura Spell
---@param source? Unit
---@param target? Unit
---@param aura Bastion.Spell
---@param source? Bastion.Unit
---@param target? Bastion.Unit
function Spell:CheckAuraStatus(aura, source, target)
for id, trackedAura in pairs(self.auras) do
if aura:GetID() == id then
@ -738,9 +737,9 @@ function Spell:CheckAuraStatus(aura, source, target)
return false
end
---@param spell Spell
---@param source? Unit
---@param target? Unit
---@param spell Bastion.Spell
---@param source? Bastion.Unit
---@param target? Bastion.Unit
function Spell:UpdateAura(spell, source, target)
if not self.auras[spell:GetID()] then
self:TrackAura(spell, source, target)

@ -2,14 +2,14 @@
local Tinkr, Bastion = ...
-- Create a new SpellBook class
---@class SpellBook
---@field spells table<number, Spell>
---@field auras { [spellId]: { [spellId]: { spell: Spell, source?: Unit, target?: Unit, lastApplied: number } } }
---@class Bastion.SpellBook
---@field spells table<number, Bastion.Spell>
---@field auras { [spellId]: { [spellId]: { spell: Bastion.Spell, source?: Bastion.Unit, target?: Bastion.Unit, lastApplied: number } } }
local SpellBook = {}
SpellBook.__index = SpellBook
-- Constructor
---@return SpellBook
---@return Bastion.SpellBook
function SpellBook:New()
local self = setmetatable({}, SpellBook)
self.spells = {}
@ -28,7 +28,7 @@ end ]]
-- Get a spell from the spellbook
---@param id integer
---@return Spell
---@return Bastion.Spell
function SpellBook:GetSpell(id)
local override = FindSpellOverrideByID(id)
if self.spells[id] == nil then
@ -45,7 +45,7 @@ function SpellBook:GetSpell(id)
end
---@param ... integer
---@return Spell, ... Spell
---@return Bastion.Spell, ... Spell
function SpellBook:GetSpells(...)
local spells = {}
for _, id in ipairs({ ... }) do
@ -55,8 +55,8 @@ function SpellBook:GetSpells(...)
return unpack(spells)
end
---@param aura Spell
---@return Spell[]
---@param aura Bastion.Spell
---@return Bastion.Spell[]
function SpellBook:GetAuraSpells(aura)
local spells = {}
for _, spell in pairs(self.spells) do
@ -69,7 +69,7 @@ function SpellBook:GetAuraSpells(aura)
end
---@param ... integer
---@return List
---@return Bastion.List
function SpellBook:GetList(...)
local spells = {}
for _, id in ipairs({ ... }) do
@ -80,7 +80,7 @@ function SpellBook:GetList(...)
end
---@param name string
---@return Spell
---@return Bastion.Spell
function SpellBook:GetSpellByName(name)
local _, rank, icon, castTime, minRange, maxRange, spellID, originalIcon = GetSpellInfo(name)
return self:GetSpell(spellID)

@ -10,7 +10,7 @@ end
local Cache = Bastion.Globals.UnitInfo
--- An attempt to integrate HeroLib TTD timers.
---@class TimeToDie
---@class Bastion.TimeToDie
local TimeToDie = {
Settings = {
-- Refresh time (seconds) : min=0.1, max=2, default = 0.1
@ -29,8 +29,7 @@ local TimeToDie = {
}
function TimeToDie:IterableUnits()
return Bastion.List:New():concat(Bastion.ObjectManager.enemies):concat(Bastion.ObjectManager.explosives):concat(
Bastion.ObjectManager.incorporeal)
return Bastion.ObjectManager.enemies
end
function TimeToDie:Refresh()
@ -38,24 +37,22 @@ function TimeToDie:Refresh()
local historyCount = TimeToDie.Settings.HistoryCount
local historyTime = TimeToDie.Settings.HistoryTime
local ttdCache = TimeToDie.Cache
local iterableUnits = TimeToDie:IterableUnits()
local iterableUnits = Bastion.ObjectManager.enemies
local units = TimeToDie.Units
local existingUnits = TimeToDie.ExistingUnits
wipe(existingUnits)
local thisUnit
---@param unit Unit
---@param unit Bastion.Unit
iterableUnits:each(function(unit)
thisUnit = unit
if thisUnit:Exists() then
local unitGUID = thisUnit:GetGUID()
if unit:Exists() then
local unitGUID = unit:GetGUID()
-- Check if we didn't already scanned this unit.
if unitGUID and not existingUnits[unitGUID] then
existingUnits[unitGUID] = true
local healthPercentage = thisUnit:GetHealthPercent()
local healthPercentage = unit:GetHealthPercent()
-- Check if it's a valid unit
if Player:CanAttack(thisUnit) and healthPercentage < 100 then
if healthPercentage < 100 then
local unitTable = units[unitGUID]
-- Check if we have seen one time this unit, if we don't then initialize it.
if not unitTable or healthPercentage > unitTable[1][1][2] then
@ -123,33 +120,33 @@ TimeToDie.specialTTDPercentageData = {
----- Castle Nathria -----
--- Stone Legion Generals
-- General Kaal leaves the fight at 50% if General Grashaal has not fight yet. We take 49% as check value since it get -95% dmg reduction at 50% until intermission is over.
---@param self Unit
---@param self Bastion.Unit
[168112] = function(self) return (not self:CheckHPFromBossList(168113, 99) and 49) or 0 end,
--- Sun King's Salvation
-- Shade of Kael'thas fight is 60% -> 45% and then 10% -> 0%.
---@param self Unit
---@param self Bastion.Unit
[165805] = function(self) return (self:GetHealthPercent() > 20 and 45) or 0 end,
----- Sanctum of Domination -----
--- Eye of the Jailer leaves at 66% and 33%
---@param self Unit
---@param self Bastion.Unit
[180018] = function(self)
return (self:GetHealthPercent() > 66 and 66) or
(self:GetHealthPercent() <= 66 and self:GetHealthPercent() > 33 and 33) or 0
end,
--- Painsmith Raznal leaves at 70% and 40%
---@param self Unit
---@param self Bastion.Unit
[176523] = function(self)
return (self:GetHealthPercent() > 70 and 70) or
(self:GetHealthPercent() <= 70 and self:GetHealthPercent() > 40 and 40) or 0
end,
--- Fatescribe Roh-Kalo phases at 70% and 40%
---@param self Unit
---@param self Bastion.Unit
[179390] = function(self)
return (self:GetHealthPercent() > 70 and 70) or
(self:GetHealthPercent() <= 70 and self:GetHealthPercent() > 40 and 40) or 0
end,
--- Sylvanas Windrunner intermission at 83% and "dies" at 50% (45% in MM)
---@param self Unit
---@param self Bastion.Unit
[180828] = function(self)
local _, _, difficultyId = GetInstanceInfo()
return (self:GetHealthPercent() > 83 and 83) or
@ -166,7 +163,7 @@ TimeToDie.specialTTDPercentageData = {
-- Hymdall leaves the fight at 10%.
[94960] = 10,
-- Fenryr leaves the fight at 60%. We take 50% as check value since it doesn't get immune at 60%.
---@param self Unit
---@param self Bastion.Unit
[95674] = function(self) return (self:GetHealthPercent() > 50 and 60) or 0 end,
-- Odyn leaves the fight at 80%.
[95676] = 80,
@ -176,14 +173,15 @@ TimeToDie.specialTTDPercentageData = {
----- Trial of Valor -----
--- Odyn
-- Hyrja & Hymdall leaves the fight at 25% during first stage and 85%/90% during second stage (HM/MM).
---@param self Unit
---@param self Bastion.Unit
[114360] = function(self)
local _, _, difficultyId = GetInstanceInfo()
return (not self:CheckHPFromBossList(114263, 99) and 25) or
(difficultyId == 16 and 85) or 90
end,
---@param self Unit
---@param self Bastion.Unit
[114361] = function(self)
local _, _, difficultyId = GetInstanceInfo()
return (not self:CheckHPFromBossList(114263, 99) and 25) or
(difficultyId == 16 and 85) or 90
end,
@ -191,7 +189,7 @@ TimeToDie.specialTTDPercentageData = {
[114263] = 10,
----- Nighthold -----
--- Elisande leaves the fight two times at 10% then normally dies. She looses 50% power per stage (100 -> 50 -> 0).
---@param self Unit
---@param self Bastion.Unit
[106643] = function(self) return (self:GetPower() > 0 and 10) or 0 end,
--- Warlord of Draenor (WoD)
@ -215,7 +213,7 @@ TimeToDie.specialTTDPercentageData = {
}
-- Returns the max fight length of boss units, or the current selected target if no boss units
---@param enemies? List
---@param enemies? Bastion.List
---@param bossOnly? boolean
function TimeToDie.FightRemains(enemies, bossOnly)
local bossExists, maxTimeToDie
@ -236,7 +234,7 @@ function TimeToDie.FightRemains(enemies, bossOnly)
-- If we specify an AoE range, iterate through all the targets in the specified range
if enemies then
---@param enemy Unit
---@param enemy Bastion.Unit
enemies:each(function(enemy)
if (enemy:InCombatOdds() > 80 or enemy:IsDummy()) and enemy:TimeToDieIsNotValid() then
maxTimeToDie = math.max(maxTimeToDie or 0, enemy:TimeToDie2())
@ -262,7 +260,7 @@ function TimeToDie.BossFightRemainsIsNotValid()
end
-- Returns if the current fight length meets the requirements.
---@param enemies? List
---@param enemies? Bastion.List
---@param operator CompareThisTable
---@param value number
---@param checkIfValid boolean

@ -2,7 +2,8 @@
local Tinkr, Bastion = ...
-- Create a new Timer class
---@class Timer
---@class Bastion.Timer
---@field type string
local Timer = {
startTime = nil,
resetAfterCombat = false,
@ -11,7 +12,7 @@ Timer.__index = Timer
-- Constructor
---@param type string
---@return Timer
---@return Bastion.Timer
function Timer:New(type)
local self = setmetatable({}, Timer)
self.startTime = nil

@ -2,22 +2,17 @@
local Tinkr, Bastion = ...
-- Create a new Unit class
---@class Unit
---@class Bastion.Unit
---@field id boolean | number
---@field ttd_ticker false | cbObject
---@field unit TinkrObjectReference
---@field aura_table AuraTable | nil
---@field unit? TinkrObjectReference
---@field aura_table Bastion.AuraTable | nil
local Unit = {
---@type Cache
---@type Bastion.Cache
cache = nil,
aura_table = nil,
---@type UnitId | WowGameObject
unit = nil,
last_shadow_techniques = 0,
swings_since_sht = 0,
last_off_attack = 0,
last_main_attack = 0,
last_combat_time = 0,
ttd_ticker = false,
ttd = 0,
id = false, --[[ @asnumber ]]
@ -42,7 +37,7 @@ function Unit:__index(k)
end
-- Equals
---@param other Unit
---@param other Bastion.Unit
---@return boolean
function Unit:__eq(other)
return UnitIsUnit(self:GetOMToken(), other:GetOMToken())
@ -61,10 +56,15 @@ end
-- Constructor
---@param unit? TinkrObjectReference
---@return Unit
function Unit:New(unit)
---@class Bastion.Unit
local self = setmetatable({}, Unit)
self.unit = unit
self.last_shadow_techniques = 0
self.swings_since_sht = 0
self.last_off_attack = 0
self.last_main_attack = 0
self.last_combat_time = 0
self.cache = Bastion.Cache:New()
self.aura_table = Bastion.AuraTable:New(self)
self.regression_history = {}
@ -89,7 +89,6 @@ function Unit:Token()
end
-- Get the units name
---@return string?
function Unit:GetName()
local unitName, realm = UnitName(self:GetOMToken())
return unitName
@ -181,14 +180,14 @@ function Unit:GetPowerDeficit(powerType)
end
-- Get the units position
---@return Vector3
---@return Bastion.Vector3
function Unit:GetPosition()
local x, y, z = ObjectPosition(self:GetOMToken())
return Bastion.Vector3:New(x, y, z)
end
-- Get the units distance from another unit
---@param unit Unit
---@param unit Bastion.Unit
---@return number
function Unit:GetDistance(unit)
local pself = self:GetPosition()
@ -244,8 +243,7 @@ function Unit:IsHostile()
return UnitCanAttack(self:GetOMToken(), "player")
end
---@param unit Unit
---@return number
---@param unit Bastion.Unit
function Unit:GetReaction(unit)
return UnitReaction(unit:GetOMToken(), self:GetOMToken())
end
@ -354,14 +352,14 @@ function Unit:IsAffectingCombat()
end
-- Get the units class id
---@return Class
---@return Bastion.Class
function Unit:GetClass()
local locale, class, classID = UnitClass(self:GetOMToken())
return Bastion.Class:New(locale, class, classID)
end
-- Get the units auras
---@return AuraTable
---@return Bastion.AuraTable
function Unit:GetAuras()
return self.aura_table
end
@ -382,7 +380,7 @@ local isClassicWow = select(4, GetBuildInfo()) < 40000
local losFlag = bit.bor(0x1, 0x10)
-- Check if the unit can see another unit
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:CanSee(unit)
-- mechagon smoke cloud
@ -455,7 +453,7 @@ function Unit:GetTimeCastIsAt(percent)
end
-- Get Casting or channeling spell
---@return Spell | nil
---@return Bastion.Spell | nil
function Unit:GetCastingOrChannelingSpell()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(
self:GetOMToken())
@ -503,10 +501,12 @@ function Unit:IsCastingOrChanneling()
end
function Unit:CastTarget()
return self:IsCastingOrChanneling() and Bastion.UnitManager:Get(ObjectCastingTarget(self:GetOMToken()))
---@diagnostic disable-next-line: param-type-mismatch
return self:IsCastingOrChanneling() and Bastion.UnitManager:Get(ObjectCastingTarget(self:GetOMToken())) or
Bastion.UnitManager:Get("none")
end
---@param unit Unit
---@param unit Bastion.Unit
function Unit:CastTargetIsUnit(unit)
return self:IsCastingOrChanneling() and self:CastTarget():IsUnit(unit)
end
@ -516,7 +516,7 @@ function Unit:IsImmobilized()
end
-- Check if the unit can attack the target
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:CanAttack(unit)
return UnitCanAttack(self:GetOMToken(), unit:GetOMToken())
@ -655,7 +655,7 @@ function Unit:IsMovingAtAll()
return ObjectMovementFlag(self:GetOMToken()) ~= 0
end
---@param unit? Unit
---@param unit? Bastion.Unit
---@return number
function Unit:GetComboPoints(unit)
if Tinkr.classic or Tinkr.era then
@ -676,7 +676,7 @@ function Unit:GetComboPointsMax()
end
-- Get combopoints deficit
---@param unit Unit | nil
---@param unit Bastion.Unit | nil
---@return number
function Unit:GetComboPointsDeficit(unit)
if Tinkr.classic or Tinkr.era then
@ -686,14 +686,14 @@ function Unit:GetComboPointsDeficit(unit)
end
-- IsUnit
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:IsUnit(unit)
return UnitIsUnit(self:GetOMToken(), unit and unit:GetOMToken() or "none")
end
-- IsTanking
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:IsTanking(unit)
local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(),
@ -702,7 +702,7 @@ function Unit:IsTanking(unit)
end
-- IsFacing
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:IsFacing(unit)
local rot = ObjectRotation(self:GetOMToken())
@ -725,7 +725,7 @@ function Unit:IsFacing(unit)
end
-- IsBehind
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:IsBehind(unit)
local rot = ObjectRotation(unit:GetOMToken())
@ -747,7 +747,7 @@ function Unit:IsBehind(unit)
end
-- IsInfront
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:IsInfront(unit)
return not self:IsBehind(unit)
@ -783,7 +783,7 @@ end
-- return ((myPos.x - targetPos.x) * (myPos.x - targetPos.x)) + ((myPos.y - targetPos.y) * (myPos.y - targetPos.y)) + ((myPos.z - targetPos.z) * (myPos.z - targetPos.z)) <= (float)(fMaxDist * fMaxDist);
-- InMelee
---@param unit Unit
---@param unit Bastion.Unit
---@return boolean
function Unit:InMelee(unit)
local x, y, z = ObjectPosition(self.unit)
@ -809,10 +809,12 @@ end
-- Get object id
function Unit:GetID()
if self.id then
if self.id ~= false then
---@type number
return self.id
end
self.id = ObjectID(self:GetOMToken())
---@type number
return self.id or 0
end
@ -970,6 +972,7 @@ end
---@return number
function Unit:InCombatOdds()
local time = self:GetCombatTime()
local percent = 1 - (time / 60)
return percent * 100
@ -1162,7 +1165,7 @@ end
-- Is the unit within distance of the target (combat reach + distance)
--- If the target is within 8 combat yards (8 + combat reach) of the unit
---@param Target Unit
---@param Target Bastion.Unit
---@param Distance number
---@return boolean
function Unit:IsWithinCombatDistance(Target, Distance)
@ -1173,7 +1176,7 @@ function Unit:IsWithinCombatDistance(Target, Distance)
end
-- Check if the unit is within X yards (consider combat reach)
---@param Target Unit
---@param Target Bastion.Unit
---@param Distance number
---@return boolean
function Unit:IsWithinDistance(Target, Distance)
@ -1181,7 +1184,7 @@ function Unit:IsWithinDistance(Target, Distance)
end
-- Get the angle between the unit and the target in raidans
---@param Target Unit
---@param Target Bastion.Unit
---@return number
function Unit:GetAngle(Target)
if not Target:Exists() then
@ -1201,7 +1204,7 @@ function Unit:GetFacing()
end
-- Check if target is within a arc around the unit (angle, distance) accounting for a rotation of self
---@param Target Unit
---@param Target Bastion.Unit
---@param Angle number
---@param Distance number
---@param rotation? number

@ -1,20 +1,18 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
local ObjectManager = Tinkr.Util.ObjectManager
local Unit = Bastion.Unit
---@class UnitManager.CustomUnit
---@field unit Cacheable | Unit
---@field cb fun(): Unit
---@class Bastion.UnitManager.CustomUnit
---@field unit Bastion.Cacheable | Bastion.Unit
---@field cb fun(): Bastion.Unit
-- Create a new UnitManager class
---@class UnitManager : { [UnitId]: Unit }
---@field units table<string, Unit>
---@field customUnits table<string, UnitManager.CustomUnit>
---@field objects table<string, Unit>
---@field cache Cache
---@class Bastion.UnitManager
---@field units table<string, Bastion.Unit>
---@field customUnits table<string, Bastion.UnitManager.CustomUnit>
---@field objects table<string, Bastion.Unit>
---@field cache Bastion.Cache
local UnitManager = {
units = {},
customUnits = {},
@ -27,7 +25,6 @@ function UnitManager:__index(k)
if k == "none" then
return self:Get("none")
end
if UnitManager[k] then
return UnitManager[k]
end
@ -66,9 +63,9 @@ function UnitManager:__index(k)
end
-- Constructor
---@return UnitManager
---@return Bastion.UnitManager
function UnitManager:New()
---@class UnitManager
---@class Bastion.UnitManager
local self = setmetatable({}, UnitManager)
self.units = {}
self.customUnits = {}
@ -78,7 +75,7 @@ end
-- Get or create a unit
---@param token UnitId
---@return Unit
---@return Bastion.Unit
function UnitManager:Get(token)
-- if not Validate(token) then
-- error("UnitManager:Get - Invalid token: " .. token)
@ -90,6 +87,7 @@ function UnitManager:Get(token)
if token == "none" then
self.objects["none"] = Unit:New()
else
---@diagnostic disable-next-line: param-type-mismatch
self.objects[tguid] = Unit:New(Object(tguid))
end
end
@ -101,6 +99,7 @@ function UnitManager:Get(token)
if token == "none" then
self.objects["none"] = Unit:New()
else
---@diagnostic disable-next-line: param-type-mismatch
self.objects[tguid] = Unit:New(Object(tguid))
end
end
@ -110,21 +109,21 @@ end
-- Get a unit by guid
---@param guid string | WowGameObject
---@return Unit
---@return Bastion.Unit
function UnitManager:GetObject(guid)
return self.objects[guid]
end
-- Set a unit by guid
---@param unit Unit
---@param unit Bastion.Unit
function UnitManager:SetObject(unit)
self.objects[unit:GetGUID()] = unit
end
-- Create a custom unit and cache it for .5 seconds
---@generic V :Unit
---@generic V :Bastion.Unit
---@param token string
---@param cb fun(): Unit
---@param cb fun(): Bastion.Unit
function UnitManager:CreateCustomUnit(token, cb)
local unit = cb()
local cachedUnit = Bastion.Cacheable:New(unit, cb)
@ -143,7 +142,7 @@ function UnitManager:CreateCustomUnit(token, cb)
end
---@description Enumerates all friendly units in the battlefield
---@param cb fun(unit: Unit):boolean
---@param cb fun(unit: Bastion.Unit):boolean
---@return nil
function UnitManager:EnumFriends(cb)
Bastion.ObjectManager.friends:each(function(unit)
@ -155,7 +154,7 @@ function UnitManager:EnumFriends(cb)
end
-- Enum Enemies (object manager)
---@param cb fun(unit: Unit):boolean
---@param cb fun(unit: Bastion.Unit):boolean
---@return nil
function UnitManager:EnumEnemies(cb)
Bastion.ObjectManager.activeEnemies:each(function(unit)
@ -167,7 +166,7 @@ function UnitManager:EnumEnemies(cb)
end
-- Enum Units (object manager)
---@param cb fun(unit: Unit):boolean
---@param cb fun(unit: Bastion.Unit):boolean
---@return nil
function UnitManager:EnumUnits(cb)
Bastion.ObjectManager.enemies:each(function(unit)
@ -179,7 +178,7 @@ function UnitManager:EnumUnits(cb)
end
-- Enum Incorporeal (object manager)
---@param cb fun(unit: Unit):boolean
---@param cb fun(unit: Bastion.Unit):boolean
---@return nil
function UnitManager:EnumIncorporeal(cb)
Bastion.ObjectManager.incorporeal:each(function(unit)
@ -191,7 +190,7 @@ function UnitManager:EnumIncorporeal(cb)
end
-- Get the number of enemies with a debuff
---@param spell Spell
---@param spell Bastion.Spell
---@param range? number
---@return number
function UnitManager:GetNumEnemiesWithDebuff(spell, range)
@ -209,7 +208,7 @@ function UnitManager:GetNumEnemiesWithDebuff(spell, range)
end
-- Get the number of friends with a buff (party/raid members)
---@param spell Spell
---@param spell Bastion.Spell
---@return number
function UnitManager:GetNumFriendsWithBuff(spell)
local count = 0
@ -237,8 +236,8 @@ end
-- Get the friend with the most friends within a given radius (party/raid members)
---@param radius number
---@return Unit | nil
---@return Unit[]
---@return Bastion.Unit | nil
---@return Bastion.Unit[]
function UnitManager:GetFriendWithMostFriends(radius)
local unit = nil
local count = 0
@ -270,7 +269,7 @@ function UnitManager:GetFriendWithMostFriends(radius)
end
-- Get the enemy with the most enemies within a given radius
---@return Unit|nil, Unit[]
---@return Bastion.Unit|nil, Bastion.Unit[]
function UnitManager:GetEnemiesWithMostEnemies(radius)
local unit = nil
local count = 0
@ -304,7 +303,7 @@ end
-- Find the centroid of the most dense area of friends (party/raid members) of a given radius within a given range
---@param radius number
---@param range number
---@return Vector3 | nil
---@return Bastion.Vector3 | nil
function UnitManager:FindFriendsCentroid(radius, range)
local unit, friends = self:GetFriendWithMostFriends(radius)
if unit == nil then
@ -335,7 +334,7 @@ end
-- Find the centroid of the most dense area of enemies of a given radius within a given range
---@param radius number
---@param range number
---@return Vector3 | nil
---@return Bastion.Vector3 | nil
function UnitManager:FindEnemiesCentroid(radius, range)
local unit, enemies = self:GetEnemiesWithMostEnemies(radius)
if unit == nil then

@ -1,11 +1,32 @@
-- Create a Vector3 class
---@class Vector3
---@operator add(Vector3): Vector3
---@operator sub(Vector3|number): Vector3
---@operator mul(number): Vector3
---@operator div(number): Vector3
---@operator unm(): Vector3
---@class Bastion.Vector3
---@field length number
---@field normalized Bastion.Vector3
---@field magnitude number
---@field sqrMagnitude number
---@field zero Bastion.Vector3
---@field one Bastion.Vector3
---@field up Bastion.Vector3
---@field down Bastion.Vector3
---@field left Bastion.Vector3
---@field right Bastion.Vector3
---@field forward Bastion.Vector3
---@field back Bastion.Vector3
---@field positiveInfinity Bastion.Vector3
---@field negativeInfinity Bastion.Vector3
---@field nan Bastion.Vector3
---@field epsilon number
---@field maxValue number
---@field minValue number
---@field x number
---@field y number
---@field z number
---@operator add(Bastion.Vector3): Bastion.Vector3
---@operator sub(Bastion.Vector3|number): Bastion.Vector3
---@operator mul(number): Bastion.Vector3
---@operator div(number): Bastion.Vector3
---@operator unm(): Bastion.Vector3
---@operator len(): number
local Vector3 = {}
Vector3.__index = Vector3
@ -15,14 +36,14 @@ function Vector3:__tostring()
return "Vector3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")"
end
---@param other Vector3
---@return Vector3
---@param other Bastion.Vector3
---@return Bastion.Vector3
function Vector3:__add(other)
return Vector3:New(self.x + other.x, self.y + other.y, self.z + other.z)
end
---@param other Vector3 | number
---@return Vector3
---@param other Bastion.Vector3 | number
---@return Bastion.Vector3
function Vector3:__sub(other)
if type(other) == "number" then
return Vector3:New(self.x - other, self.y - other, self.z - other)
@ -31,36 +52,36 @@ function Vector3:__sub(other)
end
---@param other number
---@return Vector3
---@return Bastion.Vector3
function Vector3:__mul(other)
return Vector3:New(self.x * other, self.y * other, self.z * other)
end
---@param other number
---@return Vector3
---@return Bastion.Vector3
function Vector3:__div(other)
return Vector3:New(self.x / other, self.y / other, self.z / other)
end
---@param other Vector3
---@param other Bastion.Vector3
---@return boolean
function Vector3:__eq(other)
return self.x == other.x and self.y == other.y and self.z == other.z
end
---@param other Vector3
---@param other Bastion.Vector3
---@return boolean
function Vector3:__lt(other)
return self.x < other.x and self.y < other.y and self.z < other.z
end
---@param other Vector3
---@param other Bastion.Vector3
---@return boolean
function Vector3:__le(other)
return self.x <= other.x and self.y <= other.y and self.z <= other.z
end
---@return Vector3
---@return Bastion.Vector3
function Vector3:__unm()
return Vector3:New(-self.x, -self.y, -self.z)
end
@ -70,134 +91,114 @@ function Vector3:__len()
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
---@param k string
function Vector3:__index(k)
if Vector3[k] then
return Vector3[k]
end
---@class Vector3
---@field length number
if k == "length" then
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
---@class Vector3
---@field normalized Vector3
if k == "normalized" then
local length = math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
return Vector3:New(self.x / length, self.y / length, self.z / length)
end
---@class Vector3
---@field magnitude number
if k == "magnitude" then
return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z)
end
---@class Vector3
---@field sqrMagnitude number
if k == "sqrMagnitude" then
return self.x * self.x + self.y * self.y + self.z * self.z
end
---@class Vector3
---@field zero Vector3
if k == "zero" then
return Vector3:New(0, 0, 0)
end
---@class Vector3
---@field one Vector3
if k == "one" then
return Vector3:New(1, 1, 1)
end
---@class Vector3
---@field up Vector3
if k == "up" then
return Vector3:New(0, 1, 0)
end
---@class Vector3
---@field down Vector3
if k == "down" then
return Vector3:New(0, -1, 0)
end
---@class Vector3
---@field left Vector3
if k == "left" then
return Vector3:New(-1, 0, 0)
end
---@class Vector3
---@field right Vector3
if k == "right" then
return Vector3:New(1, 0, 0)
end
---@class Vector3
---@field forward Vector3
if k == "forward" then
return Vector3:New(0, 0, 1)
end
---@class Vector3
---@field back Vector3
if k == "back" then
return Vector3:New(0, 0, -1)
end
---@class Vector3
---@field positiveInfinity Vector3
if k == "positiveInfinity" then
return Vector3:New(math.huge, math.huge, math.huge)
end
---@class Vector3
---@field negativeInfinity Vector3
if k == "negativeInfinity" then
return Vector3:New(-math.huge, -math.huge, -math.huge)
end
---@class Vector3
---@field nan Vector3
if k == "nan" then
return Vector3:New(0 / 0, 0 / 0, 0 / 0)
end
---@class Vector3
---@field epsilon number
if k == "epsilon" then
return 1.401298E-45
end
---@class Vector3
---@field maxValue number
if k == "maxValue" then
return 3.402823E+38
end
---@class Vector3
---@field minValue number
if k == "minValue" then
return -3.402823E+38
end
---@class Vector3
---@field x number
if k == "x" then
return self[1]
end
---@class Vector3
---@field y number
if k == "y" then
return self[2]
end
---@class Vector3
---@field z number
if k == "z" then
return self[3]
end
@ -220,7 +221,7 @@ end
---@param x number
---@param y number
---@param z number
---@return Vector3
---@return Bastion.Vector3
function Vector3:New(x, y, z)
if x == false then
return Vector3:New(0, 0, 0)
@ -230,37 +231,38 @@ function Vector3:New(x, y, z)
return self
end
---@param rhs Vector3
---@param rhs Bastion.Vector3
---@return number
function Vector3:Dot(rhs)
return self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
end
---@param rhs Vector3
---@return Vector3
---@param rhs Bastion.Vector3
---@return Bastion.Vector3
function Vector3:Cross(rhs)
return Vector3:New(self.y * rhs.z - self.z * rhs.y, self.z * rhs.x - self.x * rhs.z, self.x * rhs.y - self.y * rhs.x)
end
---@param b Vector3
---@param b Bastion.Vector3
---@return number
function Vector3:Distance(b)
return FastDistance(self.x, self.y, self.z, b.x, b.y, b.z)
end
---@param to Vector3
---@param to Bastion.Vector3
function Vector3:GetAbsoluteAngle(to)
return self:NormalizeOrientation(math.atan2(to.y - self.y, to.x - self.x))
end
---@param to Vector3
---@param to Bastion.Vector3
---@return number
function Vector3:Angle(to)
return math.acos(self:Dot(to) / (math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) * math.sqrt(to.x * to.x + to.y * to.y + to.z * to.z)))
return math.acos(self:Dot(to) /
(math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) * math.sqrt(to.x * to.x + to.y * to.y + to.z * to.z)))
end
---@param maxLength number
---@return Vector3
---@return Bastion.Vector3
function Vector3:ClampMagnitude(maxLength)
if self:Dot(self) > maxLength * maxLength then
return self.normalized * maxLength
@ -269,7 +271,7 @@ function Vector3:ClampMagnitude(maxLength)
return self
end
---@return Vector3
---@return Bastion.Vector3
function Vector3:directionOrZero()
local mag = self.magnitude
if mag < 0.0000001 then
@ -290,17 +292,17 @@ local function clamp(x, min, max)
return x < min and min or (x > max and max or x)
end
---@param b Vector3
---@param b Bastion.Vector3
---@param t number
---@return Vector3
---@return Bastion.Vector3
function Vector3:Lerp(b, t)
t = clamp(t, 0, 1)
return Vector3:New(self.x + (b.x - self.x) * t, self.y + (b.y - self.y) * t, self.z + (b.z - self.z) * t)
end
---@param target Vector3
---@param target Bastion.Vector3
---@param maxDistanceDelta number
---@return Vector3
---@return Bastion.Vector3
function Vector3:MoveTowards(target, maxDistanceDelta)
local toVector = target - self
local distance = toVector.magnitude
@ -311,14 +313,14 @@ function Vector3:MoveTowards(target, maxDistanceDelta)
return self + toVector / distance * maxDistanceDelta
end
---@param b Vector3
---@return Vector3
---@param b Bastion.Vector3
---@return Bastion.Vector3
function Vector3:Scale(b)
return Vector3:New(self.x * b.x, self.y * b.y, self.z * b.z)
end
---@param onNormal Vector3
---@return Vector3
---@param onNormal Bastion.Vector3
---@return Bastion.Vector3
function Vector3:Project(onNormal)
local num = onNormal:Dot(onNormal)
if num < 1.401298E-45 then
@ -328,19 +330,19 @@ function Vector3:Project(onNormal)
return onNormal * self:Dot(onNormal) / num
end
---@param planeNormal Vector3
---@return Vector3
---@param planeNormal Bastion.Vector3
---@return Bastion.Vector3
function Vector3:ProjectOnPlane(planeNormal)
return self - self:Project(planeNormal)
end
---@param inNormal Vector3
---@return Vector3
---@param inNormal Bastion.Vector3
---@return Bastion.Vector3
function Vector3:Reflect(inNormal)
return -2 * inNormal:Dot(self) * inNormal + self
end
---@return Vector3
---@return Bastion.Vector3
function Vector3:Normalize()
local num = self:Dot(self)
if num > 1E-05 then

@ -13,23 +13,94 @@ local ThirdPartyModulesBase = string.format("%s/%s", TinkrScriptsBase, "BastionS
Bastion.__index = Bastion
---@param file string
---@param ... any
---@return any ...
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(string.format("%s%s", BastionScriptsBase, file), Bastion, ...)
elseif file:sub(1, 1) == "~" then
file = file:sub(2)
-- print("2")
return require(string.format("%s%s", BastionBase, file), Bastion, ...)
---@class Bastion.LoadedFiles.Table
---@field [number] { originalPath: string, loadedPath: string, reloadable: boolean, order: number, newPath: string }
Bastion.LoadedFiles = {}
---@param filePath string
function Bastion:CheckIfLoaded(filePath)
for i, file in ipairs(Bastion.LoadedFiles) do
if file.loadedPath == filePath or file.originalPath == filePath or file.newPath == filePath then
return true
end
end
return false
end
---@param path string
---@param extension string
---@return string
local function AppendExtension(path, extension)
return string.format("%s.%s", path, extension)
end
---@param path string
---@param extensions string|string[]
local function CheckFileExtensions(path, extensions)
local exts = {}
if type(extensions) == "string" then
exts = { extensions }
else
-- print("Normal req")
return require(file, Bastion, ...)
exts = extensions
end
for i, extension in ipairs(exts) do
local newPath = path
if newPath:sub(extension:len() * -1) ~= extension then
newPath = AppendExtension(newPath, extension)
end
if FileExists(newPath) then
return newPath:sub(1, (extension:len() + 2) * -1), true
end
end
return path, false
end
--- 0 = Failed, 1 = Success, 2 = Already Loaded
---@param filePath string | { filePath: string, reloadable: boolean }
---@param ... any
---@return 0|1|2, ...
function Bastion:Require(filePath, ...)
local loadedFile = {
originalPath = type(filePath) == "table" and filePath.filePath or tostring(filePath),
newPath = type(filePath) == "table" and filePath.filePath or tostring(filePath),
loadedPath = type(filePath) == "table" and filePath.filePath or tostring(filePath),
reloadable = type(filePath) == "table" and filePath.reloadable or false,
order = #Bastion.LoadedFiles + 1,
}
local filePathModifier = loadedFile.originalPath:sub(1, 1)
if filePathModifier == "@" then
-- If require starts with an @ then we require from the scripts/bastion/scripts folder
loadedFile.newPath = string.format("%s%s", BastionScriptsBase, loadedFile.originalPath:sub(2))
loadedFile.loadedPath = loadedFile.newPath
elseif filePathModifier == "~" then
-- If file path starts with a ~ then we require from the scripts/bastion folder
loadedFile.newPath = string.format("%s%s", BastionBase, loadedFile.originalPath:sub(2))
loadedFile.loadedPath = loadedFile.newPath
end
loadedFile.loadedPath = loadedFile.newPath
local found = false
-- Check if file path has a .lua or .luac extension. If not, try to add one and check if the file exists
loadedFile.loadedPath, found = CheckFileExtensions(loadedFile.newPath, { "lua", "luac" })
if not found then
Log(string.format("Bastion:Require - Not Found: %s (%s)", loadedFile.newPath, loadedFile.originalPath))
return 0, SafePack(nil)
end
if not loadedFile.reloadable then
if Bastion:CheckIfLoaded(loadedFile.loadedPath) then
--Log(string.format("Bastion:Require - Already loaded: %s (%s)", loadedFile.newPath, loadedFile.originalPath))
return 2, SafePack(nil)
end
end
table.insert(Bastion.LoadedFiles, loadedFile)
return 1, SafePack(require(loadedFile.loadedPath, Bastion, ...))
end
local loadExamples = false
@ -82,50 +153,57 @@ local function LoadThird()
end
end
---@generic V
---@generic V : string
---@param class `V`
---@return V ...
function Bastion.require(class)
---@cast class string
local newClass = class:gsub("Bastion%.", "")
-- return require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
return Bastion:Require("~/src/" .. class .. "/" .. class)
local success, result = Bastion:Require("~/src/" .. newClass .. "/" .. newClass)
if success == 0 then
Log("Bastion.require - Failed to load " .. class .. ": " .. result)
end
return SafeUnpack(result)
end
Bastion.Globals = {}
Bastion.ClassMagic = Bastion.require("ClassMagic")
Bastion.List = Bastion.require("List")
Bastion.Library = Bastion.require("Library")
---@type NotificationsList, Notification
Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList")
Bastion.Vector3 = Bastion.require("Vector3")
Bastion.Sequencer = Bastion.require("Sequencer")
Bastion.Command = Bastion.require("Command")
Bastion.Cache = Bastion.require("Cache")
Bastion.Cacheable = Bastion.require("Cacheable")
Bastion.Refreshable = Bastion.require("Refreshable")
Bastion.Unit = Bastion.require("Unit")
Bastion.Aura = Bastion.require("Aura")
---@type APL, APLActor, APLTrait
Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL")
Bastion.Module = Bastion.require("Module")
Bastion.UnitManager = Bastion.require("UnitManager"):New()
Bastion.ObjectManager = Bastion.require("ObjectManager"):New()
Bastion.EventManager = Bastion.require("EventManager")
Bastion.ClassMagic = Bastion.require("Bastion.ClassMagic")
Bastion.List = Bastion.require("Bastion.List")
Bastion.Library = Bastion.require("Bastion.Library")
Bastion.Notification = Bastion.require("Bastion.Notification")
Bastion.NotificationList = Bastion.require("Bastion.NotificationList")
Bastion.Vector3 = Bastion.require("Bastion.Vector3")
Bastion.Sequencer = Bastion.require("Bastion.Sequencer")
Bastion.Command = Bastion.require("Bastion.Command")
Bastion.Cache = Bastion.require("Bastion.Cache")
Bastion.Cacheable = Bastion.require("Bastion.Cacheable")
Bastion.Refreshable = Bastion.require("Bastion.Refreshable")
Bastion.Unit = Bastion.require("Bastion.Unit")
Bastion.Aura = Bastion.require("Bastion.Aura")
Bastion.APLTrait = Bastion.require("Bastion.APLTrait")
Bastion.APLActor = Bastion.require("Bastion.APLActor")
Bastion.APL = Bastion.require("Bastion.APL")
Bastion.Module = Bastion.require("Bastion.Module")
Bastion.UnitManager = Bastion.require("Bastion.UnitManager"):New()
Bastion.ObjectManager = Bastion.require("Bastion.ObjectManager"):New()
Bastion.EventManager = Bastion.require("Bastion.EventManager")
Bastion.Globals.EventManager = Bastion.EventManager:New()
Bastion.Spell = Bastion.require("Spell")
Bastion.SpellBook = Bastion.require("SpellBook")
Bastion.Spell = Bastion.require("Bastion.Spell")
Bastion.SpellBook = Bastion.require("Bastion.SpellBook")
Bastion.Globals.SpellBook = Bastion.SpellBook:New()
Bastion.Item = Bastion.require("Item")
Bastion.ItemBook = Bastion.require("ItemBook")
Bastion.Item = Bastion.require("Bastion.Item")
Bastion.ItemBook = Bastion.require("Bastion.ItemBook")
Bastion.Globals.ItemBook = Bastion.ItemBook:New()
Bastion.AuraTable = Bastion.require("AuraTable")
Bastion.Class = Bastion.require("Class")
Bastion.Timer = Bastion.require("Timer")
Bastion.AuraTable = Bastion.require("Bastion.AuraTable")
Bastion.Class = Bastion.require("Bastion.Class")
Bastion.Timer = Bastion.require("Bastion.Timer")
Bastion.CombatTimer = Bastion.Timer:New("combat")
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
Bastion.Notifications = Bastion.NotificationsList:New()
Bastion.Config = Bastion.require("Config")
Bastion.TimeToDie = Bastion.require("TimeToDie")
Bastion.MythicPlusUtils = Bastion.require("Bastion.MythicPlusUtils"):New()
Bastion.Notifications = Bastion.NotificationList:New()
Bastion.Config = Bastion.require("Bastion.Config")
Bastion.TimeToDie = Bastion.require("Bastion.TimeToDie")
---@enum (key) CompareThisTable
@ -150,14 +228,14 @@ Bastion.Utils = {
}
local LIBRARIES = {}
---@type Module[]
---@type Bastion.Module[]
local MODULES = {}
Bastion.Enabled = false
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_AURA", function(unit, auras)
---@type Unit | nil
local u = Bastion.UnitManager[unit]
---@type Bastion.Unit | nil
local u = Bastion.UnitManager:Get(unit)
if u then
u:GetAuras():OnUpdate(auras)
@ -211,6 +289,8 @@ Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", fun
-- Log(tostring(args[i]))
-- end
-- end
--Bastion.UnitManager:SetCombatTime(sourceGUID)
--Bastion.UnitManager:SetCombatTime(destGUID)
local u = Bastion.UnitManager[sourceGUID]
local u2 = Bastion.UnitManager[destGUID]
@ -223,7 +303,6 @@ Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", fun
if u2 then
u2:SetLastCombatTime(t)
if subEvent == "SPELL_MISSED" and sourceGUID == pguid and spellID == 408 then
local missType = args[15]
@ -385,7 +464,7 @@ Command:Register("missed", "Dump the list of immune kidney shot spells", functio
end
end)
---@param library Library
---@param library Bastion.Library
function Bastion:RegisterLibrary(library)
LIBRARIES[library.name] = library
end

Loading…
Cancel
Save