God Where to start.....

main
ck 1 year ago
parent b43166be76
commit a1e75d0ed1
  1. 12
      .editorconfig
  2. 8
      .gitignore
  3. 168
      src/APL/APL.lua
  4. 123
      src/Aura/Aura.lua
  5. 45
      src/AuraTable/AuraTable.lua
  6. 9
      src/Cacheable/Cacheable.lua
  7. 8
      src/Class/Class.lua
  8. 13
      src/ClassMagic/ClassMagic.lua
  9. 75
      src/EventManager/EventManager.lua
  10. 63
      src/Item/Item.lua
  11. 26
      src/Library/Library.lua
  12. 7
      src/List/List.lua
  13. 1078
      src/MythicPlusUtils/MythicPlusUtils.lua
  14. 8
      src/ObjectManager/ObjectManager.lua
  15. 11
      src/Sequencer/Sequencer.lua
  16. 97
      src/Spell/Spell.lua
  17. 5
      src/SpellBook/SpellBook.lua
  18. 200
      src/Unit/Unit.lua
  19. 18
      src/UnitManager/UnitManager.lua
  20. 101
      src/_bastion.lua

@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org
# top-most EditorConfig file
root = true
[*.lua]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

8
.gitignore vendored

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

@ -1,11 +1,12 @@
-- Document with emmy lua: https://emmylua.github.io/
-- Create an APL trait for the APL class -- Create an APL trait for the APL class
---@class APLTrait ---@class APLTrait
---@field cb fun(actor?: APLActor):boolean
---@field lastcall number
local APLTrait = {} local APLTrait = {}
APLTrait.__index = APLTrait APLTrait.__index = APLTrait
-- Constructor -- Constructor
---@param cb fun():boolean ---@param cb fun(actor?: APLActor):boolean
---@return APLTrait ---@return APLTrait
function APLTrait:New(cb) function APLTrait:New(cb)
local self = setmetatable({}, APLTrait) local self = setmetatable({}, APLTrait)
@ -17,10 +18,11 @@ function APLTrait:New(cb)
end end
-- Evaulate the APL trait -- Evaulate the APL trait
---@param actor? APLActor
---@return boolean ---@return boolean
function APLTrait:Evaluate() function APLTrait:Evaluate(actor)
if GetTime() - self.lastcall > 0.1 then if GetTime() - self.lastcall > 0.1 then
self.lastresult = self.cb() self.lastresult = self.cb(actor)
self.lastcall = GetTime() self.lastcall = GetTime()
return self.lastresult return self.lastresult
end end
@ -34,13 +36,20 @@ function APLTrait:__tostring()
return "Bastion.__APLTrait" return "Bastion.__APLTrait"
end 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 -- Create an APL actor for the APL class
---@class APLActor ---@class APLActor
---@field actor APLActorTable
---@field traits APLTrait[]
local APLActor = {} local APLActor = {}
APLActor.__index = APLActor APLActor.__index = APLActor
-- Constructor -- Constructor
---@param actor table ---@param actor APLActorTable
function APLActor:New(actor) function APLActor:New(actor)
local self = setmetatable({}, APLActor) local self = setmetatable({}, APLActor)
@ -54,7 +63,7 @@ end
---@param ... APLTrait ---@param ... APLTrait
---@return APLActor ---@return APLActor
function APLActor:AddTraits(...) function APLActor:AddTraits(...)
for _, trait in ipairs({...}) do for _, trait in ipairs({ ... }) do
table.insert(self.traits, trait) table.insert(self.traits, trait)
end end
@ -62,7 +71,7 @@ function APLActor:AddTraits(...)
end end
-- Get the actor -- Get the actor
---@return table ---@return APLActorTable
function APLActor:GetActor() function APLActor:GetActor()
return self.actor return self.actor
end end
@ -71,7 +80,7 @@ end
---@return boolean ---@return boolean
function APLActor:Evaluate() function APLActor:Evaluate()
for _, trait in ipairs(self.traits) do for _, trait in ipairs(self.traits) do
if not trait:Evaluate() then if not trait:Evaluate(self) then
return false return false
end end
end end
@ -81,57 +90,71 @@ end
-- Execute -- Execute
function APLActor: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 the actor is a sequencer we don't want to continue executing the APL if the sequencer is not finished
if self:GetActor().sequencer then if actorTable.type == "sequencer" then
if self:GetActor().condition and self:GetActor().condition() and not self:GetActor().sequencer:Finished() then ---@cast actorTable APLActorSequencerTable
self:GetActor().sequencer:Execute() if actorTable.condition and actorTable.condition() and not actorTable.sequencer:Finished() then
actorTable.sequencer:Execute()
return true return true
end end
if not self:GetActor().condition and not self:GetActor().sequencer:Finished() then if not actorTable.condition and not actorTable.sequencer:Finished() then
self:GetActor().sequencer:Execute() actorTable.sequencer:Execute()
return true return true
end end
-- Check if the sequencer can be reset and reset it if it can -- Check if the sequencer can be reset and reset it if it can
if self:GetActor().sequencer:ShouldReset() then if actorTable.sequencer:ShouldReset() then
self:GetActor().sequencer:Reset() actorTable.sequencer:Reset()
end end
end end
if self:GetActor().apl then if actorTable.type == "apl" then
if self:GetActor().condition and self:GetActor().condition() then ---@cast actorTable APLActorAPLTable
-- print("Bastion: APL:Execute: Executing sub APL " .. self:GetActor().apl.name) if actorTable.condition and actorTable.condition() then
self:GetActor().apl:Execute() -- print("Bastion: APL:Execute: Executing sub APL " .. actorTable.apl.name)
if actorTable.apl:Execute() then
return true
end
end end
end end
if self:GetActor().spell then if actorTable.type == "spell" then
if self:GetActor().condition then ---@cast actorTable APLActorSpellTable
-- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName()) return actorTable.spell
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast( :CastableIf(actorTable.castableFunc)
self:GetActor().target, self:GetActor().condition) :OnCast(actorTable.onCastFunc)
:Cast(actorTable.target, actorTable.condition)
--[[ if actorTable.condition then
-- print("Bastion: APL:Execute: Condition for spell " .. actorTable.spell:GetName())
actorTable.spell
:CastableIf(actorTable.castableFunc)
:OnCast(actorTable.onCastFunc)
:Cast(actorTable.target, actorTable.condition)
else else
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName()) -- print("Bastion: APL:Execute: No condition for spell " .. actorTable.spell:GetName())
self:GetActor().spell:CastableIf(self:GetActor().castableFunc):OnCast(self:GetActor().onCastFunc):Cast( actorTable.spell:CastableIf(actorTable.castableFunc):OnCast(actorTable.onCastFunc):Cast(actorTable.target)
self:GetActor().target) end ]]
end
end end
if self:GetActor().item then if actorTable.type == "item" then
if self:GetActor().condition then ---@cast actorTable APLActorItemTable
-- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName()) return actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target, actorTable.condition)
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target, --[[ if actorTable.condition and type(actorTable.condition) == "string" then
self:GetActor().condition) -- print("Bastion: APL:Execute: Condition for spell " .. actorTable.spell:GetName())
actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target, actorTable.condition)
else else
-- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName()) -- print("Bastion: APL:Execute: No condition for spell " .. actorTable.spell:GetName())
self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target) actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target)
end end ]]
end end
if self:GetActor().action then if actorTable.type == "action" then
-- print("Bastion: APL:Execute: Executing action " .. self:GetActor().action) ---@cast actorTable APLActorActionTable
self:GetActor().cb(self) -- print("Bastion: APL:Execute: Executing action " .. actorTable.action)
actorTable.cb(self)
end end
if self:GetActor().variable then if actorTable.type == "variable" then
-- print("Bastion: APL:Execute: Setting variable " .. self:GetActor().variable) ---@cast actorTable APLActorVariableTable
self:GetActor()._apl.variables[self:GetActor().variable] = self:GetActor().cb(self:GetActor()._apl) -- print("Bastion: APL:Execute: Setting variable " .. actorTable.variable)
actorTable._apl.variables[actorTable.variable] = actorTable.cb(actorTable._apl)
end end
return false return false
end end
@ -150,6 +173,9 @@ end
-- APL (Attack priority list) class -- APL (Attack priority list) class
---@class APL ---@class APL
---@field apl APLActor[]
---@field variables table<string, any>
---@field name string
local APL = {} local APL = {}
APL.__index = APL APL.__index = APL
@ -180,36 +206,54 @@ function APL:GetVariable(name)
return self.variables[name] return self.variables[name]
end end
---@class APLActorVariableTable : APLActorTableBase
---@field variable string
---@field cb fun(...):any
---@field _apl APL
-- Add variable -- Add variable
---@param name string ---@param name string
---@param cb fun(...):any ---@param cb fun(...):any
---@return APLActor ---@return APLActor
function APL:AddVariable(name, cb) function APL:AddVariable(name, cb)
local actor = APLActor:New({ local actor = APLActor:New({
type = "variable",
variable = name, variable = name,
cb = cb, cb = cb,
_apl = self _apl = self,
}) })
table.insert(self.apl, actor) table.insert(self.apl, actor)
return actor return actor
end end
---@class APLActorActionTable : APLActorTableBase
---@field action string
---@field cb fun(...):any
-- Add a manual action to the APL -- Add a manual action to the APL
---@param action string ---@param action string
---@param cb fun(...):any ---@param cb fun(...):any
---@return APLActor ---@return APLActor
function APL:AddAction(action, cb) function APL:AddAction(action, cb)
local actor = APLActor:New({ local actor = APLActor:New({
type = "action",
action = action, action = action,
cb = cb cb = cb,
}) })
table.insert(self.apl, actor) table.insert(self.apl, actor)
return actor return actor
end 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
-- Add a spell to the APL -- Add a spell to the APL
---@param spell Spell ---@param spell Spell
---@param condition? string|fun(...):boolean ---@param condition? string|fun(self: Spell):boolean
---@return APLActor ---@return APLActor
function APL:AddSpell(spell, condition) function APL:AddSpell(spell, condition)
local castableFunc = spell.CastableIfFunc local castableFunc = spell.CastableIfFunc
@ -217,11 +261,12 @@ function APL:AddSpell(spell, condition)
local target = spell:GetTarget() local target = spell:GetTarget()
local actor = APLActor:New({ local actor = APLActor:New({
type = "spell",
spell = spell, spell = spell,
condition = condition, condition = condition,
castableFunc = castableFunc, castableFunc = castableFunc,
target = target, target = target,
onCastFunc = onCastFunc onCastFunc = onCastFunc,
}) })
table.insert(self.apl, actor) table.insert(self.apl, actor)
@ -229,19 +274,26 @@ function APL:AddSpell(spell, condition)
return actor return actor
end end
---@class APLActorItemTable : APLActorTableBase
---@field item Item
---@field condition? string | fun(self: Item): boolean
---@field usableFunc false | fun(...): boolean
---@field target Unit | nil
-- Add an item to the APL -- Add an item to the APL
---@param item Item ---@param item Item
---@param condition? fun(...):boolean ---@param condition? string
---@return APLActor ---@return APLActor
function APL:AddItem(item, condition) function APL:AddItem(item, condition)
local usableFunc = item.UsableIfFunc local usableFunc = item.UsableIfFunc
local target = item:GetTarget() local target = item:GetTarget()
local actor = APLActor:New({ local actor = APLActor:New({
type = "item",
item = item, item = item,
condition = condition, condition = condition,
usableFunc = usableFunc, usableFunc = usableFunc,
target = target target = target,
}) })
table.insert(self.apl, actor) table.insert(self.apl, actor)
@ -249,6 +301,10 @@ function APL:AddItem(item, condition)
return actor return actor
end end
---@class APLActorAPLTable : APLActorTableBase
---@field apl APL
---@field condition? fun(...):boolean
-- Add an APL to the APL (for sub APLs) -- Add an APL to the APL (for sub APLs)
---@param apl APL ---@param apl APL
---@param condition fun(...):boolean ---@param condition fun(...):boolean
@ -258,8 +314,9 @@ function APL:AddAPL(apl, condition)
error("Bastion: APL:AddAPL: No condition for APL " .. apl.name) error("Bastion: APL:AddAPL: No condition for APL " .. apl.name)
end end
local actor = APLActor:New({ local actor = APLActor:New({
type = "apl",
apl = apl, apl = apl,
condition = condition condition = condition,
}) })
table.insert(self.apl, actor) table.insert(self.apl, actor)
return actor return actor
@ -270,24 +327,31 @@ function APL:Execute()
for _, actor in ipairs(self.apl) do for _, actor in ipairs(self.apl) do
if actor:HasTraits() then if actor:HasTraits() then
if actor:Evaluate() and actor:Execute() then if actor:Evaluate() and actor:Execute() then
break return true
--break
end end
else else
if actor:Execute() then if actor:Execute() then
break return true
--break
end end
end end
end end
end end
---@class APLActorSequencerTable : APLActorTableBase
---@field sequencer Sequencer
---@field condition? fun(...):boolean
-- Add a Sequencer to the APL -- Add a Sequencer to the APL
---@param sequencer Sequencer ---@param sequencer Sequencer
---@param condition fun(...):boolean ---@param condition fun(...):boolean
---@return APLActor ---@return APLActor
function APL:AddSequence(sequencer, condition) function APL:AddSequence(sequencer, condition)
local actor = APLActor:New({ local actor = APLActor:New({
type = "sequencer",
sequencer = sequencer, sequencer = sequencer,
condition = condition condition = condition,
}) })
table.insert(self.apl, actor) table.insert(self.apl, actor)
return actor return actor

@ -20,7 +20,7 @@ function Aura:__index(k)
end end
-- Equals -- Equals
---@param other Aura|Spell ---@param other Aura | Spell
---@return boolean ---@return boolean
function Aura:__eq(other) function Aura:__eq(other)
if getmetatable(other) == Aura then if getmetatable(other) == Aura then
@ -41,11 +41,12 @@ function Aura:__tostring()
end end
-- Constructor -- Constructor
---@param unit Unit ---@param unit? Unit
---@param index number ---@param index? number
---@param type string ---@param type? string
function Aura:New(unit, index, type) function Aura:New(unit, index, type)
if unit == nil then if unit == nil or index == nil or type == nil then
---@class Aura
local self = setmetatable({}, Aura) local self = setmetatable({}, Aura)
self.aura = { self.aura = {
name = nil, name = nil,
@ -63,10 +64,10 @@ function Aura:New(unit, index, type)
castByPlayer = false, castByPlayer = false,
nameplateShowAll = false, nameplateShowAll = false,
timeMod = 0, timeMod = 0,
points = {},
index = nil, index = nil,
type = nil type = nil,
} }
if self.aura.spellId then if self.aura.spellId then
@ -75,9 +76,9 @@ function Aura:New(unit, index, type)
return self return self
end end
local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal, spellId, local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod, v1, v2, v3, v4 =
canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod = UnitAura(unit:GetOMToken(), index, type) UnitAura(unit:GetOMToken(), index, type)
---@class Aura
local self = setmetatable({}, Aura) local self = setmetatable({}, Aura)
self.aura = { self.aura = {
name = name, name = name,
@ -96,9 +97,14 @@ function Aura:New(unit, index, type)
nameplateShowAll = nameplateShowAll, nameplateShowAll = nameplateShowAll,
timeMod = timeMod, timeMod = timeMod,
auraInstanceID = nil, auraInstanceID = nil,
points = {
[1] = v1,
[2] = v2,
[3] = v3,
[4] = v4,
},
index = index, index = index,
type = type type = type,
} }
if self.aura.spellId then if self.aura.spellId then
Bastion.Globals.SpellBook:GetSpell(self.aura.spellId) Bastion.Globals.SpellBook:GetSpell(self.aura.spellId)
@ -106,31 +112,75 @@ function Aura:New(unit, index, type)
return self return self
end end
local foodAndDrinkStrings = {
[5] = "Refreshment",
[1] = MINIMAP_TRACKING_VENDOR_FOOD, -- Food & Drink
[2] = POWER_TYPE_FOOD, -- Food
[3] = EMOTE36_TOKEN, -- DRINK
[4] = TUTORIAL_TITLE12, -- Drink
}
---@type { [number]: boolean }
local cachedFoodAndDrinkIDs = {}
function Aura:IsFoodOrDrink()
if self.aura.spellId and self.aura.spellId > 0 and self:IsValid() and self:IsUp() then
if cachedFoodAndDrinkIDs[self.aura.spellId] then
return cachedFoodAndDrinkIDs[self.aura.spellId]
else
local auraName = self.aura.name
if auraName then
print("Aura Name", auraName)
for i = 1, #foodAndDrinkStrings do
if auraName:upper():find(foodAndDrinkStrings[i]:upper()) then
cachedFoodAndDrinkIDs[self.aura.spellId] = true
return true
end
end
else
print("No Aura Name", self.aura.spellId)
end
end
end
return false
end
-- Constructor -- Constructor
---@param unitAuraInfo UnitAuraInfo ---@param unitAuraInfo AuraData
---@return Aura ---@return Aura
function Aura:CreateFromUnitAuraInfo(unitAuraInfo) function Aura:CreateFromUnitAuraInfo(unitAuraInfo)
---@class Aura
local self = setmetatable({}, Aura) local self = setmetatable({}, Aura)
--[[
applications
charges
isNameplateOnly
isRaid
maxCharges
]]
--
self.aura = { self.aura = {
name = unitAuraInfo.name, auraInstanceID = unitAuraInfo.auraInstanceID,
icon = unitAuraInfo.icon, canApplyAura = unitAuraInfo.canApplyAura,
castByPlayer = unitAuraInfo.isFromPlayerOrPlayerPet,
count = unitAuraInfo.applications, count = unitAuraInfo.applications,
dispelType = unitAuraInfo.dispelName, dispelType = unitAuraInfo.dispelName or "",
duration = unitAuraInfo.duration, duration = unitAuraInfo.duration,
expirationTime = unitAuraInfo.expirationTime, expirationTime = unitAuraInfo.expirationTime,
source = unitAuraInfo.sourceUnit, icon = unitAuraInfo.icon,
isBossDebuff = unitAuraInfo.isBossAura,
isStealable = unitAuraInfo.isStealable, isStealable = unitAuraInfo.isStealable,
name = unitAuraInfo.name,
nameplateShowAll = unitAuraInfo.nameplateShowAll,
nameplateShowPersonal = unitAuraInfo.nameplateShowPersonal, nameplateShowPersonal = unitAuraInfo.nameplateShowPersonal,
points = unitAuraInfo.points,
source = unitAuraInfo.sourceUnit,
spellId = unitAuraInfo.spellId, spellId = unitAuraInfo.spellId,
canApplyAura = unitAuraInfo.canApplyAura,
isBossDebuff = unitAuraInfo.isBossAura,
castByPlayer = unitAuraInfo.isFromPlayerOrPlayerPet,
nameplateShowAll = unitAuraInfo.nameplateShowAll,
timeMod = unitAuraInfo.timeMod, timeMod = unitAuraInfo.timeMod,
auraInstanceID = unitAuraInfo.auraInstanceID,
index = nil, index = nil,
type = unitAuraInfo.isHarmful and "HARMFUL" or "HELPFUL" type = unitAuraInfo.isHarmful and "HARMFUL" or "HELPFUL",
} }
-- Register spell in spellbook -- Register spell in spellbook
@ -175,7 +225,7 @@ function Aura:GetName()
end end
-- Get the auras icon -- Get the auras icon
---@return string ---@return number
function Aura:GetIcon() function Aura:GetIcon()
return self.aura.icon return self.aura.icon
end end
@ -198,6 +248,15 @@ function Aura:GetDuration()
return self.aura.duration return self.aura.duration
end end
-- Get the auras refresh status
---@return boolean
function Aura:Refreshable()
if not self:IsUp() then
return true
end
return self:GetRemainingTime() < self:GetDuration() * 0.3
end
-- Get the auras remaining time -- Get the auras remaining time
---@return number ---@return number
function Aura:GetRemainingTime() function Aura:GetRemainingTime()
@ -291,23 +350,31 @@ end
-- Check if the aura is dispelable by a spell -- Check if the aura is dispelable by a spell
---@param spell Spell ---@param spell Spell
function Aura:IsDispelableBySpell(spell) function Aura:IsDispelableBySpell(spell)
if
(self:GetDispelType() == "" or self:GetDispelType() == nil)
and self:GetIsStealable()
and spell:IsEnrageDispel()
then
return true
end
if self:GetDispelType() == nil then if self:GetDispelType() == nil then
return false return false
end end
if self:GetDispelType() == 'Magic' and spell:IsMagicDispel() then if self:GetDispelType() == "Magic" and spell:IsMagicDispel() then
return true return true
end end
if self:GetDispelType() == 'Curse' and spell:IsCurseDispel() then if self:GetDispelType() == "Curse" and spell:IsCurseDispel() then
return true return true
end end
if self:GetDispelType() == 'Poison' and spell:IsPoisonDispel() then if self:GetDispelType() == "Poison" and spell:IsPoisonDispel() then
return true return true
end end
if self:GetDispelType() == 'Disease' and spell:IsDiseaseDispel() then if self:GetDispelType() == "Disease" and spell:IsDiseaseDispel() then
return true return true
end end

@ -1,7 +1,9 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ... local Tinkr, Bastion = ...
-- Create a new AuraTable class -- Create a new AuraTable class
---@class AuraTable ---@class AuraTable
---@field unit Unit
local AuraTable = {} local AuraTable = {}
AuraTable.__index = AuraTable AuraTable.__index = AuraTable
@ -53,7 +55,7 @@ function AuraTable:OnUpdate(auras)
if updatedAuras and #updatedAuras > 0 then if updatedAuras and #updatedAuras > 0 then
for i = 1, #updatedAuras do for i = 1, #updatedAuras do
local id = updatedAuras[i] local id = updatedAuras[i]
local newAura = C_UnitAuras_GetAuraDataByAuraInstanceID(self.unit:GetOMToken(), id); local newAura = C_UnitAuras_GetAuraDataByAuraInstanceID(self.unit:GetOMToken(), id)
if newAura then if newAura then
local aura = Bastion.Aura:CreateFromUnitAuraInfo(newAura) local aura = Bastion.Aura:CreateFromUnitAuraInfo(newAura)
self:AddOrUpdateAuraInstanceID(aura:GetAuraInstanceID(), aura) self:AddOrUpdateAuraInstanceID(aura:GetAuraInstanceID(), aura)
@ -100,7 +102,7 @@ function AuraTable:AddOrUpdateAuraInstanceID(instanceID, aura)
self.instanceIDLookup[instanceID] = spellId self.instanceIDLookup[instanceID] = spellId
if Bastion.UnitManager['player']:IsUnit(aura:GetSource()) then if Bastion.UnitManager["player"]:IsUnit(aura:GetSource()) then
if not self.playerAuras[spellId] then if not self.playerAuras[spellId] then
self.playerAuras[spellId] = {} self.playerAuras[spellId] = {}
end end
@ -120,7 +122,7 @@ end
function AuraTable:GetUnitBuffs() function AuraTable:GetUnitBuffs()
if Tinkr.classic or Tinkr.era then if Tinkr.classic or Tinkr.era then
for i = 1, 40 do for i = 1, 40 do
local aura = Bastion.Aura:New(self.unit, i, 'HELPFUL') local aura = Bastion.Aura:New(self.unit, i, "HELPFUL")
if not aura:IsValid() then if not aura:IsValid() then
break break
@ -128,7 +130,7 @@ function AuraTable:GetUnitBuffs()
local spellId = aura:GetSpell():GetID() local spellId = aura:GetSpell():GetID()
if Bastion.UnitManager['player']:IsUnit(aura:GetSource()) then if Bastion.UnitManager["player"]:IsUnit(aura:GetSource()) then
if not self.playerAuras[spellId] then if not self.playerAuras[spellId] then
self.playerAuras[spellId] = {} self.playerAuras[spellId] = {}
end end
@ -145,7 +147,7 @@ function AuraTable:GetUnitBuffs()
return return
end end
AuraUtil_ForEachAura(self.unit:GetOMToken(), 'HELPFUL', nil, function(a) AuraUtil_ForEachAura(self.unit:GetOMToken(), "HELPFUL", nil, function(a)
local aura = Bastion.Aura:CreateFromUnitAuraInfo(a) local aura = Bastion.Aura:CreateFromUnitAuraInfo(a)
if aura:IsValid() then if aura:IsValid() then
@ -159,7 +161,7 @@ end
function AuraTable:GetUnitDebuffs() function AuraTable:GetUnitDebuffs()
if Tinkr.classic or Tinkr.era then if Tinkr.classic or Tinkr.era then
for i = 1, 40 do for i = 1, 40 do
local aura = Bastion.Aura:New(self.unit, i, 'HARMFUL') local aura = Bastion.Aura:New(self.unit, i, "HARMFUL")
if not aura:IsValid() then if not aura:IsValid() then
break break
@ -167,7 +169,7 @@ function AuraTable:GetUnitDebuffs()
local spellId = aura:GetSpell():GetID() local spellId = aura:GetSpell():GetID()
if Bastion.UnitManager['player']:IsUnit(aura:GetSource()) then if Bastion.UnitManager["player"]:IsUnit(aura:GetSource()) then
if not self.playerAuras[spellId] then if not self.playerAuras[spellId] then
self.playerAuras[spellId] = {} self.playerAuras[spellId] = {}
end end
@ -184,7 +186,7 @@ function AuraTable:GetUnitDebuffs()
return return
end end
AuraUtil_ForEachAura(self.unit:GetOMToken(), 'HARMFUL', nil, function(a) AuraUtil_ForEachAura(self.unit:GetOMToken(), "HARMFUL", nil, function(a)
local aura = Bastion.Aura:CreateFromUnitAuraInfo(a) local aura = Bastion.Aura:CreateFromUnitAuraInfo(a)
if aura:IsValid() then if aura:IsValid() then
@ -208,7 +210,7 @@ function AuraTable:Update()
end end
-- Get a units auras -- Get a units auras
---@return table ---@return table<number, table<number, Aura>>
function AuraTable:GetUnitAuras() function AuraTable:GetUnitAuras()
if not self.did then if not self.did then
self.did = true self.did = true
@ -730,13 +732,18 @@ function AuraTable:FindLeastOfFrom(spells, source)
end end
-- Has any stealable aura -- Has any stealable aura
---@param spell? Spell
---@return boolean ---@return boolean
function AuraTable:HasAnyStealableAura() function AuraTable:HasAnyStealableAura(spell)
for _, auras in pairs(self:GetUnitAuras()) do for _, auras in pairs(self:GetUnitAuras()) do
for _, aura in pairs(auras) do for _, aura in pairs(auras) do
if aura:IsUp() then -- Handle expired and non refreshed dropoffs not coming in UNIT_AURA if aura:IsUp() then -- Handle expired and non refreshed dropoffs not coming in UNIT_AURA
if aura:GetIsStealable() then if aura:GetIsStealable() then
return true if spell ~= nil then
return aura:IsDispelableBySpell(spell)
else
return true
end
end end
else else
self:RemoveInstanceID(aura:GetAuraInstanceID()) self:RemoveInstanceID(aura:GetAuraInstanceID())
@ -766,4 +773,20 @@ function AuraTable:HasAnyDispelableAura(spell)
return false return false
end end
function AuraTable:HasAnyFoodOrDrinkAura()
for _, auras in pairs(self:GetUnitAuras()) do
for _, aura in pairs(auras) do
if aura:IsUp() then -- Handle expired and non refreshed dropoffs not coming in UNIT_AURA
if aura:IsFoodOrDrink() then
return true
end
else
self:RemoveInstanceID(aura:GetAuraInstanceID())
end
end
end
return false
end
return AuraTable return AuraTable

@ -2,6 +2,9 @@ local Tinkr, Bastion = ...
-- Define a Cacheable class -- Define a Cacheable class
---@class Cacheable ---@class Cacheable
---@field cache? Cache
---@field callback? fun():any
---@field value any
local Cacheable = { local Cacheable = {
cache = nil, cache = nil,
callback = nil, callback = nil,
@ -21,9 +24,9 @@ function Cacheable:__index(k)
error("Cacheable:__index: " .. k .. " does not exist") error("Cacheable:__index: " .. k .. " does not exist")
end end
if not self.cache:IsCached('self') then if not self.cache:IsCached("self") then
self.value = self.callback() self.value = self.callback()
self.cache:Set('self', self.value, 0.5) self.cache:Set("self", self.value, 0.5)
end end
return self.value[k] return self.value[k]
@ -45,7 +48,7 @@ function Cacheable:New(value, cb)
self.value = value self.value = value
self.callback = cb self.callback = cb
self.cache:Set('self', self.value, 0.5) self.cache:Set("self", self.value, 0.5)
return self return self
end end

@ -36,7 +36,7 @@ function Class:New(locale, name, id)
self.class = { self.class = {
locale = locale, locale = locale,
name = name, name = name,
id = id id = id,
} }
return self return self
end end
@ -59,16 +59,10 @@ function Class:GetID()
return self.class.id return self.class.id
end end
---@class ColorMixin
---@field r number
---@field g number
---@field b number
-- Return the classes color -- Return the classes color
---@return ColorMixin classColor ---@return ColorMixin classColor
function Class:GetColor() function Class:GetColor()
return C_ClassColor.GetClassColor(self.class.name) return C_ClassColor.GetClassColor(self.class.name)
end end
return Class return Class

@ -10,8 +10,8 @@ function ClassMagic:Resolve(Class, key)
return Class[key] return Class[key]
end end
if Class['Get' .. key:sub(1, 1):upper() .. key:sub(2)] then if Class["Get" .. key:sub(1, 1):upper() .. key:sub(2)] then
local func = Class['Get' .. key:sub(1, 1):upper() .. key:sub(2)] local func = Class["Get" .. key:sub(1, 1):upper() .. key:sub(2)]
-- Call the function and return the result if there's more than one return value return it as a table -- Call the function and return the result if there's more than one return value return it as a table
local result = { func(self) } local result = { func(self) }
@ -22,9 +22,8 @@ function ClassMagic:Resolve(Class, key)
return result[1] return result[1]
end end
if Class["Get" .. key:upper()] then
if Class['Get' .. key:upper()] then local func = Class["Get" .. key:upper()]
local func = Class['Get' .. key:upper()]
-- Call the function and return the result if there's more than one return value return it as a table -- Call the function and return the result if there's more than one return value return it as a table
local result = { func(self) } local result = { func(self) }
@ -35,8 +34,8 @@ function ClassMagic:Resolve(Class, key)
return result[1] return result[1]
end end
if Class['Is' .. key:upper()] then if Class["Is" .. key:upper()] then
local func = Class['Is' .. key:upper()] local func = Class["Is" .. key:upper()]
-- Call the function and return the result if there's more than one return value return it as a table -- Call the function and return the result if there's more than one return value return it as a table
local result = { func(self) } local result = { func(self) }

@ -1,12 +1,19 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
-- Create an EventManager class -- Create an EventManager class
---@class EventManager ---@class EventManager
---@field frame Frame
---@field events table<string, { [number]: fun(...) }>
---@field eventHandlers table<string, { [number]: fun(...) }>
---@field wowEventHandlers table<string, { [number]: fun(...) }>
---@field selfCLEUHandlers table<string, { [number]: fun(...) }>
local EventManager = { local EventManager = {
events = {}, events = {},
eventHandlers = {}, eventHandlers = {},
wowEventHandlers = {}, wowEventHandlers = {},
frame = nil frame = nil,
} }
EventManager.__index = EventManager EventManager.__index = EventManager
-- Constructor -- Constructor
@ -16,11 +23,12 @@ function EventManager:New()
self.events = {} self.events = {}
self.eventHandlers = {} self.eventHandlers = {}
self.wowEventHandlers = {} self.wowEventHandlers = {}
self.selfCLEUHandlers = {}
-- Frame for wow events -- Frame for wow events
self.frame = CreateFrame("Frame") self.frame = CreateFrame("Frame")
self.frame:SetScript('OnEvent', function(f, event, ...) self.frame:SetScript("OnEvent", function(f, event, ...)
if self.wowEventHandlers[event] then if self.wowEventHandlers[event] then
for _, callback in ipairs(self.wowEventHandlers[event]) do for _, callback in ipairs(self.wowEventHandlers[event]) do
callback(...) callback(...)
@ -28,6 +36,10 @@ function EventManager:New()
end end
end) end)
self:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function(event)
self:CLEUHandler(event, CombatLogGetCurrentEventInfo())
end)
return self return self
end end
@ -44,16 +56,27 @@ function EventManager:RegisterEvent(event, handler)
end end
-- Register a wow event -- Register a wow event
---@param event string ---@param event string | string[]
---@param handler fun(...) ---@param handler fun(...)
---@return nil ---@return nil
function EventManager:RegisterWoWEvent(event, handler) function EventManager:RegisterWoWEvent(event, handler)
if not self.wowEventHandlers[event] then if type(event) == "table" then
self.wowEventHandlers[event] = {} for _, e in ipairs(event) do
self.frame:RegisterEvent(event) if not self.wowEventHandlers[e] then
end self.wowEventHandlers[e] = {}
self.frame:RegisterEvent(e)
end
table.insert(self.wowEventHandlers[event], handler) table.insert(self.wowEventHandlers[e], handler)
end
else
if not self.wowEventHandlers[event] then
self.wowEventHandlers[event] = {}
self.frame:RegisterEvent(event)
end
table.insert(self.wowEventHandlers[event], handler)
end
end end
-- Trigger an event -- Trigger an event
@ -68,4 +91,38 @@ function EventManager:TriggerEvent(event, ...)
end end
end end
---@param subevent string | string[]
---@param handler fun(...)
function EventManager:RegisterSelfCLEUEvent(subevent, handler)
if type(subevent) == "table" then
for _, e in ipairs(subevent) do
if not self.selfCLEUHandlers[e] then
self.selfCLEUHandlers[e] = {}
end
table.insert(self.selfCLEUHandlers[e], handler)
end
else
if not self.selfCLEUHandlers[subevent] then
self.selfCLEUHandlers[subevent] = {}
end
table.insert(self.selfCLEUHandlers[subevent], handler)
end
end
---@param event "COMBAT_LOG_EVENT_UNFILTERED"
---@param timestamp number
---@param subevent string
---@param ... any
function EventManager:CLEUHandler(event, timestamp, subevent, ...)
if self.selfCLEUHandlers[subevent] then
if select(2, ...) == UnitGUID("player") then
for _, handler in pairs(self.selfCLEUHandlers[subevent]) do
handler(timestamp, subevent, ...)
end
end
end
end
return EventManager return EventManager

@ -1,7 +1,15 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ... local Tinkr, Bastion = ...
-- Create a new Item class -- Create a new Item class
---@class Item ---@class 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 spellID number | nil
local Item = { local Item = {
UsableIfFunc = false, UsableIfFunc = false,
PreUseFunc = false, PreUseFunc = false,
@ -16,6 +24,14 @@ local usableExcludes = {
[18562] = true, [18562] = true,
} }
---@param itemId number | string
---@return number charges, number maxCharges, number start, number duration
local GetItemCharges = function(itemId)
local spellId = select(2, GetItemSpell(itemId))
local charges, maxCharges, start, duration, chargeModRate = GetSpellCharges(spellId)
return charges, maxCharges, start, duration
end
function Item:__index(k) function Item:__index(k)
local response = Bastion.ClassMagic:Resolve(Item, k) local response = Bastion.ClassMagic:Resolve(Item, k)
@ -69,7 +85,7 @@ end
-- Get the Items name -- Get the Items name
---@return string ---@return string
function Item:GetName() function Item:GetName()
return GetItemInfo(self:GetID()) return select(1, GetItemInfo(self:GetID()))
end end
-- Get the Items icon -- Get the Items icon
@ -111,11 +127,15 @@ end
-- Use the Item -- Use the Item
---@param unit Unit ---@param unit Unit
---@param condition string ---@param condition? string | fun(self:Item):boolean
---@return boolean ---@return boolean
function Item:Use(unit, condition) function Item:Use(unit, condition)
if condition and not self:EvaluateCondition(condition) then if condition then
return false if type(condition) == "string" and not self:EvaluateCondition(condition) then
return false
elseif type(condition) == "function" and not condition(self) then
return false
end
end end
if not self:Usable() then if not self:Usable() then
@ -159,7 +179,6 @@ function Item:GetTimeSinceLastUseAttempt()
end end
-- Check if the Item is known -- Check if the Item is known
---@return boolean
function Item:IsEquipped() function Item:IsEquipped()
return IsEquippedItem(self:GetID()) return IsEquippedItem(self:GetID())
end end
@ -180,12 +199,11 @@ end
-- Check if the Item is Usable -- Check if the Item is Usable
---@return boolean ---@return boolean
function Item:IsEquippedAndUsable() function Item:IsEquippedAndUsable()
return ((self:IsEquippable() and self:IsEquipped()) or return ((self:IsEquippable() and self:IsEquipped()) or (not self:IsEquippable() and self:IsUsable()))
(not self:IsEquippable() and self:IsUsable())) and not self:IsOnCooldown() and not self:IsOnCooldown()
end end
-- Is equippable -- Is equippable
---@return boolean
function Item:IsEquippable() function Item:IsEquippable()
return IsEquippableItem(self:GetID()) return IsEquippableItem(self:GetID())
end end
@ -236,7 +254,7 @@ end
---@param z number ---@param z number
---@return boolean ---@return boolean
function Item:Click(x, y, z) function Item:Click(x, y, z)
if type(x) == 'table' then if type(x) == "table" then
x, y, z = x.x, x.y, x.z x, y, z = x.x, x.y, x.z
end end
if IsSpellPending() == 64 then if IsSpellPending() == 64 then
@ -270,7 +288,7 @@ function Item:IsInRange(unit)
local them = Object(unit:GetOMToken()) local them = Object(unit:GetOMToken())
local tx, ty, tz = ObjectPosition(unit:GetOMToken()) local tx, ty, tz = ObjectPosition(unit:GetOMToken())
local px, py, pz = ObjectPosition('player') local px, py, pz = ObjectPosition("player")
if not them then if not them then
return false return false
@ -283,16 +301,13 @@ function Item:IsInRange(unit)
local combatReach = ObjectCombatReach("player") local combatReach = ObjectCombatReach("player")
local themCombatReach = ObjectCombatReach(unit:GetOMToken()) local themCombatReach = ObjectCombatReach(unit:GetOMToken())
if Bastion.UnitManager['player']:InMelee(unit) and Itemmin == 0 then if Bastion.UnitManager["player"]:InMelee(unit) and Itemmin == 0 then
return true return true
end end
local distance = FastDistance(px, py, pz, tx, ty, tz) local distance = FastDistance(px, py, pz, tx, ty, tz)
if Itemmax if Itemmax and distance >= Itemmin and distance <= combatReach + themCombatReach + Itemmax then
and distance >= Itemmin
and distance <= combatReach + themCombatReach + Itemmax
then
return true return true
end end
@ -300,7 +315,6 @@ function Item:IsInRange(unit)
end end
-- Get the last use time -- Get the last use time
---@return number
function Item:GetLastUseTime() function Item:GetLastUseTime()
return Bastion.Globals.SpellBook:GetSpell(self:GetID()):GetLastCastTime() return Bastion.Globals.SpellBook:GetSpell(self:GetID()):GetLastCastTime()
end end
@ -317,7 +331,7 @@ end
-- Get the Items charges -- Get the Items charges
---@return number ---@return number
function Item:GetCharges() function Item:GetCharges()
return GetItemCharges(self:GetID()) return select(1, GetItemCharges(self:GetID()))
end end
-- Get the Items charges remaining -- Get the Items charges remaining
@ -333,14 +347,13 @@ end
---@return Item ---@return Item
function Item:Condition(name, func) function Item:Condition(name, func)
self.conditions[name] = { self.conditions[name] = {
func = func func = func,
} }
return self return self
end end
-- Get a condition for the Item -- Get a condition for the Item
---@param name string ---@param name string
---@return function | nil
function Item:GetCondition(name) function Item:GetCondition(name)
local condition = self.conditions[name] local condition = self.conditions[name]
if condition then if condition then
@ -383,7 +396,7 @@ function Item:SetTarget(unit)
end end
-- Get the Items target -- Get the Items target
---@return Unit | nil ---@return Unit | boolean
function Item:GetTarget() function Item:GetTarget()
return self.target return self.target
end end
@ -392,7 +405,7 @@ end
---@return boolean ---@return boolean
function Item:IsMagicDispel() function Item:IsMagicDispel()
return ({ return ({
[88423] = true [88423] = true,
})[self:GetID()] })[self:GetID()]
end end
@ -400,7 +413,7 @@ end
---@return boolean ---@return boolean
function Item:IsCurseDispel() function Item:IsCurseDispel()
return ({ return ({
[88423] = true [88423] = true,
})[self:GetID()] })[self:GetID()]
end end
@ -408,16 +421,14 @@ end
---@return boolean ---@return boolean
function Item:IsPoisonDispel() function Item:IsPoisonDispel()
return ({ return ({
[88423] = true [88423] = true,
})[self:GetID()] })[self:GetID()]
end end
-- IsDiseaseDispel -- IsDiseaseDispel
---@return boolean ---@return boolean
function Item:IsDiseaseDispel() function Item:IsDiseaseDispel()
return ({ return ({})[self:GetID()]
})[self:GetID()]
end end
---@param item Item ---@param item Item

@ -1,25 +1,28 @@
local Tinkr, Bastion = ... local Tinkr, Bastion = ...
---@class Library ---@class Library
---@field name string ---@field name string | nil
---@field dependencies table ---@field dependencies table
---@field exports table ---@field exports table
---@field resolved table ---@field resolved table | nil
local Library = { local Library = {
name = nil, name = nil,
dependencies = {}, dependencies = {},
exports = { exports = {
default = function() default = function()
return nil return nil
end end,
}, },
resolved = nil resolved = nil,
} }
Library.__index = Library Library.__index = Library
---@param name string ---@class NewLibrary
---@param library table ---@field name string
---@field exports? { default: fun(self: Library):any }
---@param library NewLibrary
---@return Library ---@return Library
function Library:New(library) function Library:New(library)
local self = { local self = {
@ -28,9 +31,9 @@ function Library:New(library)
exports = library.exports or { exports = library.exports or {
default = function() default = function()
return nil return nil
end end,
}, },
resolved = nil resolved = nil,
} }
self = setmetatable(self, Library) self = setmetatable(self, Library)
@ -39,7 +42,7 @@ function Library:New(library)
end end
function Library:ResolveExport(export) function Library:ResolveExport(export)
if type(export) == 'function' then if type(export) == "function" then
return export(self) return export(self)
end end
@ -64,12 +67,12 @@ function Library:Resolve()
local default = self.exports.default local default = self.exports.default
local remaining = {} local remaining = {}
for k, v in pairs(self.exports) do for k, v in pairs(self.exports) do
if k ~= 'default' then if k ~= "default" then
remaining[k] = self:ResolveExport(v) remaining[k] = self:ResolveExport(v)
end end
end end
self.resolved = {self:ResolveExport(default), remaining} self.resolved = { self:ResolveExport(default), remaining }
return self.resolved[1], self.resolved[2] return self.resolved[1], self.resolved[2]
end end
@ -101,6 +104,7 @@ function Library:Import(library)
error("Library " .. library .. " does not exist") error("Library " .. library .. " does not exist")
end end
---@diagnostic disable-next-line: undefined-field
if not table.contains(self.dependencies, library) then if not table.contains(self.dependencies, library) then
table.insert(self.dependencies, library) table.insert(self.dependencies, library)
end end

@ -109,9 +109,10 @@ function List:filter(callback)
return newList return newList
end end
---@param callback fun(result: any, value: any): boolean ---@generic I
---@param initialValue any ---@param callback fun(result: I, value: I): I, boolean?
---@return boolean ---@param initialValue I
---@return I
function List:reduce(callback, initialValue) function List:reduce(callback, initialValue)
local result = initialValue local result = initialValue
local done = false local done = false

File diff suppressed because it is too large Load Diff

@ -1,3 +1,4 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ... local Tinkr, Bastion = ...
---@class ObjectManager ---@class ObjectManager
@ -33,7 +34,7 @@ function ObjectManager:RegisterList(name, cb)
self._lists[name] = { self._lists[name] = {
list = Bastion.List:New(), list = Bastion.List:New(),
cb = cb cb = cb,
} }
return self._lists[name].list return self._lists[name].list
@ -80,7 +81,7 @@ function ObjectManager:Refresh()
for _, object in pairs(objects) do for _, object in pairs(objects) do
self:EnumLists(object) self:EnumLists(object)
if ({ [5] = true,[6] = true,[7] = true })[ObjectType(object)] then if ({ [5] = true, [6] = true, [7] = true })[ObjectType(object)] then
local unit = Bastion.UnitManager:GetObject(ObjectGUID(object)) local unit = Bastion.UnitManager:GetObject(ObjectGUID(object))
if not unit then if not unit then
unit = Bastion.Unit:New(object) unit = Bastion.Unit:New(object)
@ -89,7 +90,7 @@ function ObjectManager:Refresh()
if unit:GetID() == 120651 then if unit:GetID() == 120651 then
self.explosives:push(unit) self.explosives:push(unit)
elseif unit:IsPlayer() and (unit:IsInParty() or unit == Bastion.UnitManager['player']) then elseif unit:IsPlayer() and (unit:IsInParty() or unit == Bastion.UnitManager["player"]) then
self.friends:push(unit) self.friends:push(unit)
elseif unit:IsEnemy() then elseif unit:IsEnemy() then
self.enemies:push(unit) self.enemies:push(unit)
@ -104,7 +105,6 @@ end
return ObjectManager return ObjectManager
-- -- Register a list of objects that are training dummies -- -- Register a list of objects that are training dummies
-- local dummies = Bastion.ObjectManager:RegisterList('dummies', function(object) -- local dummies = Bastion.ObjectManager:RegisterList('dummies', function(object)
-- if ObjectType(object) == 5 or ObjectType(object) == 6 then -- if ObjectType(object) == 5 or ObjectType(object) == 6 then

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

@ -1,7 +1,17 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ... local Tinkr, Bastion = ...
-- Create a new Spell class -- Create a new Spell class
---@class Spell ---@class Spell
---@field spellID number
---@field PostCastFunc fun(self:Spell) | false
---@field OnCastFunc fun(self:Spell) | false
---@field PreCastFunc fun(self:Spell) | false
---@field CastableIfFunc false | fun(self:Spell):boolean
---@field lastCastAt number | false
---@field lastCastAttempt number | false
---@field target Unit | false
---@field damageFormula false | fun(self:Spell):number
local Spell = { local Spell = {
CastableIfFunc = false, CastableIfFunc = false,
PreCastFunc = false, PreCastFunc = false,
@ -11,12 +21,16 @@ local Spell = {
wasLooking = false, wasLooking = false,
lastCastAt = false, lastCastAt = false,
conditions = {}, conditions = {},
buffs = {},
debuffs = {},
target = false, target = false,
release_at = false release_at = false,
damageFormula = false,
damage = 0,
} }
local usableExcludes = { local usableExcludes = {
[18562] = true [18562] = true,
} }
function Spell:__index(k) function Spell:__index(k)
@ -66,7 +80,7 @@ end
-- Get the spells id -- Get the spells id
---@return number ---@return number
function Spell:GetID() function Spell:GetID()
return self.spellID return self:IsOverridden() and self:OverrideSpellID() or self.spellID
end end
-- Add post cast func -- Add post cast func
@ -80,7 +94,7 @@ end
-- Get the spells name -- Get the spells name
---@return string ---@return string
function Spell:GetName() function Spell:GetName()
return GetSpellInfo(self:GetID()) return select(1, GetSpellInfo(self:GetID()))
end end
-- Get the spells icon -- Get the spells icon
@ -116,19 +130,16 @@ function Spell:GetFullRechargeTime()
end end
-- Return the castable function -- Return the castable function
---@return fun(self:Spell):boolean
function Spell:GetCastableFunction() function Spell:GetCastableFunction()
return self.CastableIfFunc return self.CastableIfFunc
end end
-- Return the precast function -- Return the precast function
---@return fun(self:Spell)
function Spell:GetPreCastFunction() function Spell:GetPreCastFunction()
return self.PreCastFunc return self.PreCastFunc
end end
-- Get the on cast func -- Get the on cast func
---@return fun(self:Spell)
function Spell:GetOnCastFunction() function Spell:GetOnCastFunction()
return self.OnCastFunc return self.OnCastFunc
end end
@ -161,11 +172,11 @@ end
-- Cast the spell -- Cast the spell
---@param unit Unit ---@param unit Unit
---@param condition? string|function ---@param condition? string|fun(self:Spell):boolean
---@return boolean ---@return boolean
function Spell:Cast(unit, condition) function Spell:Cast(unit, condition)
if condition then if condition then
if type(condition) == "string" and not self:EvaluateCondition(condition) then if type(condition) == "string" and not self:EvaluateCondition(condition) then
return false return false
elseif type(condition) == "function" and not condition(self) then elseif type(condition) == "function" and not condition(self) then
return false return false
@ -186,7 +197,7 @@ function Spell:Cast(unit, condition)
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast -- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast
local u = unit:GetOMToken() local u = unit:GetOMToken()
if type(u) == "string" and string.find(u, 'nameplate') then if type(u) == "string" and string.find(u, "nameplate") then
u = Object(u) u = Object(u)
end end
@ -203,13 +214,11 @@ function Spell:Cast(unit, condition)
if self:GetOnCastFunction() then if self:GetOnCastFunction() then
self:GetOnCastFunction()(self) self:GetOnCastFunction()(self)
end end
return true return true
end end
-- ForceCast the spell -- ForceCast the spell
---@param unit Unit ---@param unit Unit
---@param condition string
---@return boolean ---@return boolean
function Spell:ForceCast(unit) function Spell:ForceCast(unit)
-- Call pre cast function -- Call pre cast function
@ -222,7 +231,7 @@ function Spell:ForceCast(unit)
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast -- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast
local u = unit:GetOMToken() local u = unit:GetOMToken()
if type(u) == "string" and string.find(u, 'nameplate') then if type(u) == "string" and string.find(u, "nameplate") then
u = Object(u) u = Object(u)
end end
@ -244,15 +253,22 @@ function Spell:ForceCast(unit)
end end
-- Get post cast func -- Get post cast func
---@return fun(self:Spell)
function Spell:GetPostCastFunction() function Spell:GetPostCastFunction()
return self.PostCastFunc return self.PostCastFunc
end end
function Spell:OverrideSpellID()
return FindSpellOverrideByID(self.spellID)
end
function Spell:IsOverridden()
return self:OverrideSpellID() ~= self.spellID
end
-- Check if the spell is known -- Check if the spell is known
---@return boolean ---@return boolean
function Spell:IsKnown() function Spell:IsKnown()
local isKnown = IsSpellKnown(self:GetID()) local isKnown = IsSpellKnownOrOverridesKnown(self:GetID())
local isPlayerSpell = IsPlayerSpell(self:GetID()) local isPlayerSpell = IsPlayerSpell(self:GetID())
return isKnown or isPlayerSpell return isKnown or isPlayerSpell
end end
@ -260,7 +276,8 @@ end
-- Check if the spell is on cooldown -- Check if the spell is on cooldown
---@return boolean ---@return boolean
function Spell:IsOnCooldown() function Spell:IsOnCooldown()
return select(2, GetSpellCooldown(self:GetID())) > 0 local spellIDName = self:IsOverridden() and self:GetName() or self:GetID()
return select(2, GetSpellCooldown(spellIDName)) > 0
end end
-- Check if the spell is usable -- Check if the spell is usable
@ -322,9 +339,12 @@ end
---@param z? number ---@param z? number
---@return boolean ---@return boolean
function Spell:Click(x, y, z) function Spell:Click(x, y, z)
if type(x) == 'table' then if type(x) == "table" then
x, y, z = x.x, x.y, x.z z, y, x = x.z, x.y, x.x
end end
---@cast x number
---@cast y number
---@cast z number
if IsSpellPending() == 64 then if IsSpellPending() == 64 then
MouselookStop() MouselookStop()
Click(x, y, z) Click(x, y, z)
@ -376,11 +396,11 @@ function Spell:IsInRange(unit)
return true return true
end end
return Bastion.UnitManager['player']:InMelee(unit) return Bastion.UnitManager["player"]:InMelee(unit)
end end
-- Get the last cast time -- Get the last cast time
---@return number ---@return number | false
function Spell:GetLastCastTime() function Spell:GetLastCastTime()
return self.lastCastAt return self.lastCastAt
end end
@ -406,7 +426,7 @@ end
-- Get the spells charges -- Get the spells charges
---@return number ---@return number
function Spell:GetCharges() function Spell:GetCharges()
return GetSpellCharges(self:GetID()) return select(1, GetSpellCharges(self:GetID()))
end end
function Spell:GetMaxCharges() function Spell:GetMaxCharges()
@ -451,14 +471,14 @@ end
---@return Spell ---@return Spell
function Spell:Condition(name, func) function Spell:Condition(name, func)
self.conditions[name] = { self.conditions[name] = {
func = func func = func,
} }
return self return self
end end
-- Get a condition for the spell -- Get a condition for the spell
---@param name string ---@param name string
---@return function | nil ---@return { func: fun(self: Spell): boolean } | nil
function Spell:GetCondition(name) function Spell:GetCondition(name)
local condition = self.conditions[name] local condition = self.conditions[name]
if condition then if condition then
@ -501,16 +521,23 @@ function Spell:SetTarget(unit)
end end
-- Get the spells target -- Get the spells target
---@return Unit
function Spell:GetTarget() function Spell:GetTarget()
return self.target return self.target
end end
-- IsEnrageDispel
---@return boolean
function Spell:IsEnrageDispel()
return ({
[2908] = true,
})[self:GetID()]
end
-- IsMagicDispel -- IsMagicDispel
---@return boolean ---@return boolean
function Spell:IsMagicDispel() function Spell:IsMagicDispel()
return ({ return ({
[88423] = true [88423] = true,
})[self:GetID()] })[self:GetID()]
end end
@ -518,7 +545,7 @@ end
---@return boolean ---@return boolean
function Spell:IsCurseDispel() function Spell:IsCurseDispel()
return ({ return ({
[88423] = true [88423] = true,
})[self:GetID()] })[self:GetID()]
end end
@ -526,7 +553,7 @@ end
---@return boolean ---@return boolean
function Spell:IsPoisonDispel() function Spell:IsPoisonDispel()
return ({ return ({
[88423] = true [88423] = true,
})[self:GetID()] })[self:GetID()]
end end
@ -547,7 +574,7 @@ end
---@return number ---@return number
function Spell:GetCost() function Spell:GetCost()
local cost = GetSpellPowerCost(self:GetID()) local cost = GetSpellPowerCost(self:GetID())
return cost and cost.cost or 0 return cost and cost[1] and cost[1].cost or 0
end end
-- IsFree -- IsFree
@ -556,4 +583,18 @@ function Spell:IsFree()
return self:GetCost() == 0 return self:GetCost() == 0
end end
---@param damageFormula fun(self:Spell): number
function Spell:RegisterDamageFormula(damageFormula)
self.damageFormula = damageFormula
end
---@return number
function Spell:Damage()
if self.damageFormula then
return self:damageFormula()
else
return self.damage
end
end
return Spell return Spell

@ -2,6 +2,7 @@ local Tinkr, Bastion = ...
-- Create a new SpellBook class -- Create a new SpellBook class
---@class SpellBook ---@class SpellBook
---@field spells table<number, Spell>
local SpellBook = {} local SpellBook = {}
SpellBook.__index = SpellBook SpellBook.__index = SpellBook
@ -27,7 +28,7 @@ end
---@return Spell, ... Spell ---@return Spell, ... Spell
function SpellBook:GetSpells(...) function SpellBook:GetSpells(...)
local spells = {} local spells = {}
for _, id in ipairs({...}) do for _, id in ipairs({ ... }) do
table.insert(spells, self:GetSpell(id)) table.insert(spells, self:GetSpell(id))
end end
@ -38,7 +39,7 @@ end
---@return List ---@return List
function SpellBook:GetList(...) function SpellBook:GetList(...)
local spells = {} local spells = {}
for _, id in ipairs({...}) do for _, id in ipairs({ ... }) do
table.insert(spells, self:GetSpell(id)) table.insert(spells, self:GetSpell(id))
end end

@ -1,12 +1,17 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ... local Tinkr, Bastion = ...
-- Create a new Unit class -- Create a new Unit class
---@class Unit ---@class Unit
---@field id boolean | number
---@field ttd_ticker false | cbObject
---@field unit TinkrObjectReference
local Unit = { local Unit = {
---@type Cache
cache = nil, cache = nil,
---@type AuraTable ---@type AuraTable
aura_table = nil, aura_table = nil,
---@type Unit ---@type UnitId | WowGameObject
unit = nil, unit = nil,
last_shadow_techniques = 0, last_shadow_techniques = 0,
swings_since_sht = 0, swings_since_sht = 0,
@ -15,13 +20,13 @@ local Unit = {
last_combat_time = 0, last_combat_time = 0,
ttd_ticker = false, ttd_ticker = false,
ttd = 0, ttd = 0,
id = false, id = false, --[[ @asnumber ]]
} }
function Unit:__index(k) function Unit:__index(k)
local response = Bastion.ClassMagic:Resolve(Unit, k) local response = Bastion.ClassMagic:Resolve(Unit, k)
if k == 'unit' then if k == "unit" then
return rawget(self, k) return rawget(self, k)
end end
@ -40,7 +45,7 @@ end
---@param other Unit ---@param other Unit
---@return boolean ---@return boolean
function Unit:__eq(other) function Unit:__eq(other)
return UnitIsUnit(self:GetOMToken(), other.unit) return UnitIsUnit(self:GetOMToken(), other:GetOMToken())
end end
-- tostring -- tostring
@ -51,17 +56,17 @@ end
---``` ---```
---@return string ---@return string
function Unit:__tostring() function Unit:__tostring()
return "Bastion.__Unit(" .. tostring(self:GetOMToken()) .. ")" .. " - " .. (self:GetName() or '') return "Bastion.__Unit(" .. tostring(self:GetOMToken()) .. ")" .. " - " .. (self:GetName() or "")
end end
-- Constructor -- Constructor
---@param unit string ---@param unit TinkrObjectReference
---@return Unit ---@return Unit
function Unit:New(unit) function Unit:New(unit)
local self = setmetatable({}, Unit) local self = setmetatable({}, Unit)
self.unit = unit self.unit = unit
self.cache = Bastion.Cache:New() self.cache = Bastion.Cache:New()
self.aura_table = Bastion.AuraTable:New(self) self.aura_table = Bastion.AuraTable:New(self)
self.regression_history = {} self.regression_history = {}
return self return self
end end
@ -73,9 +78,8 @@ function Unit:IsValid()
end end
-- Check if the unit exists -- Check if the unit exists
---@return boolean
function Unit:Exists() function Unit:Exists()
return Object(self:GetOMToken()) return Object(self:GetOMToken()) ~= false
end end
-- Get the units token -- Get the units token
@ -87,11 +91,10 @@ end
-- Get the units name -- Get the units name
---@return string ---@return string
function Unit:GetName() function Unit:GetName()
return UnitName(self:GetOMToken()) return select(1, UnitName(self:GetOMToken()))
end end
-- Get the units GUID -- Get the units GUID
---@return string
function Unit:GetGUID() function Unit:GetGUID()
return ObjectGUID(self:GetOMToken()) return ObjectGUID(self:GetOMToken())
end end
@ -139,9 +142,9 @@ function Unit:GetHealthPercent()
end end
-- Get the units power type -- Get the units power type
---@return number ---@return Enum.PowerType
function Unit:GetPowerType() function Unit:GetPowerType()
return UnitPowerType(self:GetOMToken()) return select(1, UnitPowerType(self:GetOMToken()))
end end
-- Get the units power -- Get the units power
@ -226,7 +229,7 @@ end
-- Is the unit a hostile unit -- Is the unit a hostile unit
---@return boolean ---@return boolean
function Unit:IsHostile() function Unit:IsHostile()
return UnitCanAttack(self:GetOMToken(), 'player') return UnitCanAttack(self:GetOMToken(), "player")
end end
-- Is the unit a boss -- Is the unit a boss
@ -247,7 +250,7 @@ function Unit:IsBoss()
return false return false
end end
---@return string ---@return UnitId
function Unit:GetOMToken() function Unit:GetOMToken()
if not self.unit then if not self.unit then
return "none" return "none"
@ -341,24 +344,17 @@ local losFlag = bit.bor(0x1, 0x10, 0x100000)
---@param unit Unit ---@param unit Unit
---@return boolean ---@return boolean
function Unit:CanSee(unit) function Unit:CanSee(unit)
-- mechagon smoke cloud
-- local mechagonID = 2097
-- local smokecloud = 298602
-- local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceID, instanceGroupSize, LfgDungeonID =
-- GetInstanceInfo()
-- otherUnit = otherUnit and otherUnit or "player"
-- if instanceID == 2097 then
-- if (self:debuff(smokecloud, unit) and not self:debuff(smokecloud, otherUnit))
-- or (self:debuff(smokecloud, otherUnit) and not self:debuff(smokecloud, unit))
-- then
-- return false
-- end
-- end
local ax, ay, az = ObjectPosition(self:GetOMToken()) local ax, ay, az = ObjectPosition(self:GetOMToken())
local ah = ObjectHeight(self:GetOMToken()) local ah = ObjectHeight(self:GetOMToken())
local attx, atty, attz = GetUnitAttachmentPosition(unit:GetOMToken(), 34) local attx, atty, attz = GetUnitAttachmentPosition(unit:GetOMToken(), 18)
local alwaysLos = {
[189727] = true, -- Khajin the Unyielding
}
--if alwaysLos[unit:GetID()] then
--return true
--end
if not attx or not ax then if not attx or not ax then
return false return false
@ -391,12 +387,12 @@ function Unit:IsCasting()
end end
function Unit:GetTimeCastIsAt(percent) function Unit:GetTimeCastIsAt(percent)
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo( local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId =
self:GetOMToken()) UnitCastingInfo(self:GetOMToken())
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId =
:GetOMToken()) UnitChannelInfo(self:GetOMToken())
end end
if name and startTimeMS and endTimeMS then if name and startTimeMS and endTimeMS then
@ -413,12 +409,12 @@ end
-- Get Casting or channeling spell -- Get Casting or channeling spell
---@return Spell | nil ---@return Spell | nil
function Unit:GetCastingOrChannelingSpell() function Unit:GetCastingOrChannelingSpell()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo( local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId =
self:GetOMToken()) UnitCastingInfo(self:GetOMToken())
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId =
:GetOMToken()) UnitChannelInfo(self:GetOMToken())
end end
if name then if name then
@ -431,12 +427,12 @@ end
-- Get the end time of the cast or channel -- Get the end time of the cast or channel
---@return number ---@return number
function Unit:GetCastingOrChannelingEndTime() function Unit:GetCastingOrChannelingEndTime()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo( local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId =
self:GetOMToken()) UnitCastingInfo(self:GetOMToken())
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId =
:GetOMToken()) UnitChannelInfo(self:GetOMToken())
end end
if name then if name then
@ -458,6 +454,10 @@ function Unit:IsCastingOrChanneling()
return self:IsCasting() or self:IsChanneling() return self:IsCasting() or self:IsChanneling()
end end
function Unit:IsImmobilized()
return bit.band(self:GetMovementFlag(), 0x400) > 0
end
-- Check if the unit can attack the target -- Check if the unit can attack the target
---@param unit Unit ---@param unit Unit
---@return boolean ---@return boolean
@ -467,12 +467,12 @@ end
---@return number ---@return number
function Unit:GetChannelOrCastPercentComplete() function Unit:GetChannelOrCastPercentComplete()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo( local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId =
self:GetOMToken()) UnitCastingInfo(self:GetOMToken())
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId =
:GetOMToken()) UnitChannelInfo(self:GetOMToken())
end end
if name and startTimeMS and endTimeMS then if name and startTimeMS and endTimeMS then
@ -488,12 +488,12 @@ end
-- Check if unit is interruptible -- Check if unit is interruptible
---@return boolean ---@return boolean
function Unit:IsInterruptible() function Unit:IsInterruptible()
local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo( local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId =
self:GetOMToken()) UnitCastingInfo(self:GetOMToken())
if not name then if not name then
name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId =
:GetOMToken()) UnitChannelInfo(self:GetOMToken())
end end
if name then if name then
@ -534,13 +534,19 @@ function Unit:GetEnemies(range)
local count = 0 local count = 0
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if not self:IsUnit(unit) and self:IsWithinCombatDistance(unit, range) and unit:IsAlive() and self:CanSee(unit) and if
unit:IsEnemy() then not self:IsUnit(unit)
and self:IsWithinCombatDistance(unit, range)
and unit:IsAlive()
and self:CanSee(unit)
and unit:IsEnemy()
then
count = count + 1 count = count + 1
end end
return false
end) end)
self.cache:Set("enemies_" .. range, count, .5) self.cache:Set("enemies_" .. range, count, 0.5)
return count return count
end end
@ -555,13 +561,13 @@ function Unit:GetMeleeAttackers()
local count = 0 local count = 0
Bastion.UnitManager:EnumEnemies(function(unit) Bastion.UnitManager:EnumEnemies(function(unit)
if not self:IsUnit(unit) and unit:IsAlive() and self:CanSee(unit) and if not self:IsUnit(unit) and unit:IsAlive() and self:CanSee(unit) and self:InMelee(unit) and unit:IsEnemy() then
self:InMelee(unit) and unit:IsEnemy() then
count = count + 1 count = count + 1
end end
return false
end) end)
self.cache:Set("melee_attackers", count, .5) self.cache:Set("melee_attackers", count, 0.5)
return count return count
end end
@ -572,10 +578,16 @@ function Unit:GetPartyHPAround(distance, percent)
local count = 0 local count = 0
Bastion.UnitManager:EnumFriends(function(unit) Bastion.UnitManager:EnumFriends(function(unit)
if not self:IsUnit(unit) and unit:GetDistance(self) <= distance and unit:IsAlive() and self:CanSee(unit) and if
unit:GetHP() <= percent then not self:IsUnit(unit)
and unit:GetDistance(self) <= distance
and unit:IsAlive()
and self:CanSee(unit)
and unit:GetHP() <= percent
then
count = count + 1 count = count + 1
end end
return false
end) end)
return count return count
@ -587,6 +599,10 @@ function Unit:IsMoving()
return GetUnitSpeed(self:GetOMToken()) > 0 return GetUnitSpeed(self:GetOMToken()) > 0
end end
function Unit:GetMovementFlag()
return ObjectMovementFlag(self:GetOMToken())
end
-- Is moving at all -- Is moving at all
---@return boolean ---@return boolean
function Unit:IsMovingAtAll() function Unit:IsMovingAtAll()
@ -627,15 +643,15 @@ end
---@param unit Unit ---@param unit Unit
---@return boolean ---@return boolean
function Unit:IsUnit(unit) function Unit:IsUnit(unit)
return UnitIsUnit(self:GetOMToken(), unit and unit:GetOMToken() or 'none') return UnitIsUnit(self:GetOMToken(), unit and unit:GetOMToken() or "none")
end end
-- IsTanking -- IsTanking
---@param unit Unit ---@param unit Unit
---@return boolean ---@return boolean
function Unit:IsTanking(unit) function Unit:IsTanking(unit)
local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(), local isTanking, status, threatpct, rawthreatpct, threatvalue =
unit:GetOMToken()) UnitDetailedThreatSituation(self:GetOMToken(), unit:GetOMToken())
return isTanking return isTanking
end end
@ -651,6 +667,7 @@ function Unit:IsFacing(unit)
return false return false
end end
---@diagnostic disable-next-line: deprecated
local angle = math.atan2(y2 - y, x2 - x) - rot local angle = math.atan2(y2 - y, x2 - x) - rot
angle = math.deg(angle) angle = math.deg(angle)
angle = angle % 360 angle = angle % 360
@ -672,7 +689,7 @@ function Unit:IsBehind(unit)
if not x or not x2 then if not x or not x2 then
return false return false
end end
---@diagnostic disable-next-line: deprecated
local angle = math.atan2(y2 - y, x2 - x) - rot local angle = math.atan2(y2 - y, x2 - x) - rot
angle = math.deg(angle) angle = math.deg(angle)
angle = angle % 360 angle = angle % 360
@ -692,7 +709,16 @@ end
---@return number ---@return number
function Unit:GetMeleeBoost() function Unit:GetMeleeBoost()
if IsPlayerSpell(196924) then if IsPlayerSpell(197524) then
local astralNode = C_Traits.GetNodeInfo(C_ClassTalents.GetActiveConfigID() or 0, 82210)
local currentSpec = select(1, GetSpecializationInfo(GetSpecialization()))
if astralNode then
local currentRank = astralNode.activeRank
if currentRank > 0 then
return ((currentSpec == 103 or currentSpec == 104) and 1 or 3) + (currentRank == 2 and 2 or 0)
end
end
elseif IsPlayerSpell(196924) then
return 3 return 3
end end
return 0 return 0
@ -733,9 +759,11 @@ end
-- Get object id -- Get object id
---@return number ---@return number
function Unit:GetID() function Unit:GetID()
if self.id then return self.id end if self.id then
return self.id --[[ @as number ]]
end
self.id = ObjectID(self:GetOMToken()) self.id = ObjectID(self:GetOMToken())
return self.id return self.id --[[ @as number ]]
end end
-- In party -- In party
@ -836,7 +864,7 @@ end
function Unit:TimeToDie() function Unit:TimeToDie()
if self:IsDead() then if self:IsDead() then
self.regression_history = {} self.regression_history = {}
if self.ttd_ticker then if type(self.ttd_ticker) == "table" then
self.ttd_ticker:Cancel() self.ttd_ticker:Cancel()
self.ttd_ticker = false self.ttd_ticker = false
end end
@ -854,8 +882,7 @@ function Unit:TimeToDie()
-- if the unit has more than 5 million health but there's not enough data to make a prediction we can assume there's roughly 250000 damage per second and estimate the time to die -- if the unit has more than 5 million health but there's not enough data to make a prediction we can assume there's roughly 250000 damage per second and estimate the time to die
if #self.regression_history < 5 and self:GetMaxHealth() > 5000000 then if #self.regression_history < 5 and self:GetMaxHealth() > 5000000 then
return self:GetMaxHealth() / return self:GetMaxHealth() / 250000 -- 250000 is an estimate of the average damage per second a well geared group will average
250000 -- 250000 is an estimate of the average damage per second a well geared group will average
end end
if self.ttd ~= self.ttd or self.ttd < 0 or self.ttd == math.huge then if self.ttd ~= self.ttd or self.ttd < 0 or self.ttd == math.huge then
@ -933,7 +960,7 @@ function Unit:IsStealthed()
local Shadowmeld = Bastion.Globals.SpellBook:GetSpell(58984) local Shadowmeld = Bastion.Globals.SpellBook:GetSpell(58984)
local Sepsis = Bastion.Globals.SpellBook:GetSpell(328305) local Sepsis = Bastion.Globals.SpellBook:GetSpell(328305)
return self:GetAuras():FindAny(Stealth) or self:GetAuras():FindAny(ShadowDance) return self:GetAuras():FindAny(Stealth):IsUp() or self:GetAuras():FindAny(ShadowDance):IsUp()
end end
-- Get unit swing timers -- Get unit swing timers
@ -963,7 +990,7 @@ function Unit:WatchForSwings()
local _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName, _, amount, interrupt, a, b, c, d, offhand, multistrike = local _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName, _, amount, interrupt, a, b, c, d, offhand, multistrike =
CombatLogGetCurrentEventInfo() CombatLogGetCurrentEventInfo()
if sourceGUID == self:GetGUID() then if sourceGUID == self:GetGUID() and subtype then
if subtype == "SPELL_ENERGIZE" and spellID == 196911 then if subtype == "SPELL_ENERGIZE" and spellID == 196911 then
self.last_shadow_techniques = GetTime() self.last_shadow_techniques = GetTime()
self.swings_since_sht = 0 self.swings_since_sht = 0
@ -1036,8 +1063,8 @@ function Unit:GetStaggerPercent()
end end
-- Get the units power regen rate -- Get the units power regen rate
---@return number
function Unit:GetPowerRegen() function Unit:GetPowerRegen()
---@diagnostic disable-next-line: redundant-parameter
return GetPowerRegen(self:GetOMToken()) return GetPowerRegen(self:GetOMToken())
end end
@ -1051,7 +1078,7 @@ function Unit:GetStaggeredHealth()
end end
-- get the units combat reach -- get the units combat reach
---@return number ---@return number | false
function Unit:GetCombatReach() function Unit:GetCombatReach()
return ObjectCombatReach(self:GetOMToken()) return ObjectCombatReach(self:GetOMToken())
end end
@ -1144,6 +1171,27 @@ function Unit:GetEmpoweredStage()
return stage return stage
end end
function Unit:IsConnected()
return UnitIsConnected(self:GetOMToken())
end
function Unit:HasIncomingRessurection()
return self:IsDead() and UnitHasIncomingResurrection(self:GetOMToken())
end
function Unit:LootTarget()
return ObjectLootTarget(self:GetOMToken())
end
function Unit:CanLoot()
return ObjectLootable(self:GetOMToken())
end
function Unit:HasTarget()
return ObjectTarget(self:GetOMToken()) ~= false
end
function Unit:Target()
return self:HasTarget() and Bastion.UnitManager:Get(ObjectTarget(self:GetOMToken()):unit())
or Bastion.UnitManager:Get("none")
end
-- local empowering = {} -- local empowering = {}
-- Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_EMPOWER_START", function(...) -- Bastion.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_EMPOWER_START", function(...)

@ -169,6 +169,24 @@ function UnitManager:EnumUnits(cb)
end) end)
end end
-- Get the number of enemies with a debuff
---@param spell Spell
---@param range number
---@return number
function UnitManager:GetNumEnemiesWithDebuff(spell, range)
local count = 0
if range == nil then
range = spell:GetRange()
end
self:EnumEnemies(function(unit)
if unit:GetAuras():FindMy(spell):IsUp() then
count = count + 1
end
return false
end)
return count
end
-- Get the number of friends with a buff (party/raid members) -- Get the number of friends with a buff (party/raid members)
---@param spell Spell ---@param spell Spell
---@return number ---@return number

@ -1,50 +1,88 @@
---@type Tinkr
local Tinkr = ... local Tinkr = ...
---@class Bastion ---@class Bastion
local Bastion = { local Bastion = {
DebugMode = false DebugMode = false,
} }
local TinkrScriptsBase = "scripts"
local BastionBase = string.format("%s/%s", TinkrScriptsBase, "bastion")
local BastionScriptsBase = string.format("%s/%s", BastionBase, "scripts")
local ThirdPartyModulesBase = string.format("%s/%s", TinkrScriptsBase, "BastionScripts")
Bastion.__index = Bastion Bastion.__index = Bastion
function Bastion:Require(file) ---@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 require starts with an @ then we require from the scripts/bastion/scripts folder
if file:sub(1, 1) == '@' then if file:sub(1, 1) == "@" then
file = file:sub(2) file = file:sub(2)
-- print('1') -- print('1')
return require('scripts/bastion/scripts/' .. file, Bastion) return require(string.format("%s%s", BastionScriptsBase, file), Bastion, ...)
elseif file:sub(1, 1) == "~" then elseif file:sub(1, 1) == "~" then
file = file:sub(2) file = file:sub(2)
-- print("2") -- print("2")
return require('scripts/bastion/' .. file, Bastion) return require(string.format("%s%s", BastionBase, file), Bastion, ...)
else else
-- print("Normal req") -- print("Normal req")
return require(file, Bastion) return require(file, Bastion, ...)
end end
end end
local loadExamples = false
local exampleNames = {
"ExampleDependency.lua",
"ExampleDependencyError.lua",
"ExampleLibrary.lua",
"ExampleModule.lua",
}
local function Load(dir) local function Load(dir)
local dir = dir local dir = dir
if dir:sub(1, 1) == '@' then if dir:sub(1, 1) == "@" then
dir = dir:sub(2) dir = dir:sub(2)
dir = 'scripts/bastion/scripts/' .. dir dir = string.format("%s/%s", BastionScriptsBase, dir)
end end
if dir:sub(1, 1) == '~' then if dir:sub(1, 1) == "~" then
dir = dir:sub(2) dir = dir:sub(2)
dir = 'scripts/bastion/' .. dir dir = string.format("%s/%s", BastionBase, dir)
end end
local files = ListFiles(dir) local files = ListFiles(dir)
for i = 1, #files do for i = 1, #files do
local file = files[i] local file = files[i]
if file:sub(-4) == ".lua" or file:sub(-5) == '.luac' then local loadFile = true
return Bastion:Require(dir .. file:sub(1, -5)) if not loadExamples then
for j = 1, #exampleNames do
if file:find(exampleNames[j]) then
loadFile = false
break
end
end
end
if loadFile and (file:sub(-4) == ".lua" or file:sub(-5) == ".luac") then
Bastion:Require(dir .. file:sub(1, -5))
end
end
end
local function LoadThird()
local thirdPartyModulesFolders = ListFolders(ThirdPartyModulesBase)
for i = 1, #thirdPartyModulesFolders do
local currentFolderDir = string.format("%s/%s", ThirdPartyModulesBase, thirdPartyModulesFolders[i])
local loaderFilePath = string.format("%s/%s", currentFolderDir, "loader")
if FileExists(loaderFilePath .. ".lua") or FileExists(loaderFilePath .. ".luac") then
Bastion:Require(loaderFilePath, currentFolderDir)
end end
end end
end end
---@return any ...
function Bastion.require(class) function Bastion.require(class)
-- return require("scripts/bastion/src/" .. class .. "/" .. class, Bastion) -- return require("scripts/bastion/src/" .. class .. "/" .. class, Bastion)
return Bastion:Require("~/src/" .. class .. "/" .. class) return Bastion:Require("~/src/" .. class .. "/" .. class)
@ -104,7 +142,7 @@ Bastion.Class = Bastion.require("Class")
---@type Timer ---@type Timer
Bastion.Timer = Bastion.require("Timer") Bastion.Timer = Bastion.require("Timer")
---@type Timer ---@type Timer
Bastion.CombatTimer = Bastion.Timer:New('combat') Bastion.CombatTimer = Bastion.Timer:New("combat")
---@type MythicPlusUtils ---@type MythicPlusUtils
Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New() Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New()
---@type NotificationsList ---@type NotificationsList
@ -115,7 +153,7 @@ local MODULES = {}
Bastion.Enabled = false Bastion.Enabled = false
Bastion.Globals.EventManager:RegisterWoWEvent('UNIT_AURA', function(unit, auras) Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_AURA", function(unit, auras)
local u = Bastion.UnitManager[unit] local u = Bastion.UnitManager[unit]
if u then if u then
@ -125,7 +163,6 @@ end)
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...) Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
local unit, castGUID, spellID = ... local unit, castGUID, spellID = ...
local spell = Bastion.Globals.SpellBook:GetIfRegistered(spellID) local spell = Bastion.Globals.SpellBook:GetIfRegistered(spellID)
if unit == "player" and spell then if unit == "player" and spell then
@ -141,7 +178,7 @@ local pguid = UnitGUID("player")
local missed = {} local missed = {}
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function() Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = {CombatLogGetCurrentEventInfo()} local args = { CombatLogGetCurrentEventInfo() }
local subEvent = args[2] local subEvent = args[2]
local sourceGUID = args[4] local sourceGUID = args[4]
@ -216,7 +253,7 @@ function Bastion:FindModule(name)
end end
function Bastion:Print(...) function Bastion:Print(...)
local args = {...} local args = { ... }
local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF" local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF"
for i = 1, #args do for i = 1, #args do
str = str .. tostring(args[i]) .. " " str = str .. tostring(args[i]) .. " "
@ -228,7 +265,7 @@ function Bastion:Debug(...)
if not Bastion.DebugMode then if not Bastion.DebugMode then
return return
end end
local args = {...} local args = { ... }
local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF" local str = "|cFFDF6520[Bastion]|r |cFFFFFFFF"
for i = 1, #args do for i = 1, #args do
str = str .. tostring(args[i]) .. " " str = str .. tostring(args[i]) .. " "
@ -236,9 +273,9 @@ function Bastion:Debug(...)
print(str) print(str)
end end
local Command = Bastion.Command:New('bastion') local Command = Bastion.Command:New("bastion")
Command:Register('toggle', 'Toggle bastion on/off', function() Command:Register("toggle", "Toggle bastion on/off", function()
Bastion.Enabled = not Bastion.Enabled Bastion.Enabled = not Bastion.Enabled
if Bastion.Enabled then if Bastion.Enabled then
Bastion:Print("Enabled") Bastion:Print("Enabled")
@ -247,7 +284,7 @@ Command:Register('toggle', 'Toggle bastion on/off', function()
end end
end) end)
Command:Register('debug', 'Toggle debug mode on/off', function() Command:Register("debug", "Toggle debug mode on/off", function()
Bastion.DebugMode = not Bastion.DebugMode Bastion.DebugMode = not Bastion.DebugMode
if Bastion.DebugMode then if Bastion.DebugMode then
Bastion:Print("Debug mode enabled") Bastion:Print("Debug mode enabled")
@ -256,7 +293,7 @@ Command:Register('debug', 'Toggle debug mode on/off', function()
end end
end) end)
Command:Register('dumpspells', 'Dump spells to a file', function() Command:Register("dumpspells", "Dump spells to a file", function()
local i = 1 local i = 1
local rand = math.random(100000, 999999) local rand = math.random(100000, 999999)
while true do while true do
@ -272,14 +309,17 @@ Command:Register('dumpspells', 'Dump spells to a file', function()
if spellID then if spellID then
spellName = spellName:gsub("[%W%s]", "") spellName = spellName:gsub("[%W%s]", "")
WriteFile('bastion-' .. UnitClass('player') .. '-' .. rand .. '.lua', WriteFile(
"local " .. spellName .. " = Bastion.Globals.SpellBook:GetSpell(" .. spellID .. ")\n", true) "bastion-" .. UnitClass("player") .. "-" .. rand .. ".lua",
"local " .. spellName .. " = Bastion.Globals.SpellBook:GetSpell(" .. spellID .. ")\n",
true
)
end end
i = i + 1 i = i + 1
end end
end) end)
Command:Register('module', 'Toggle a module on/off', function(args) Command:Register("module", "Toggle a module on/off", function(args)
local module = Bastion:FindModule(args[2]) local module = Bastion:FindModule(args[2])
if module then if module then
module:Toggle() module:Toggle()
@ -293,15 +333,15 @@ Command:Register('module', 'Toggle a module on/off', function(args)
end end
end) end)
Command:Register('mplus', 'Toggle m+ module on/off', function(args) Command:Register("mplus", "Toggle m+ module on/off", function(args)
local cmd = args[2] local cmd = args[2]
if cmd == 'debuffs' then if cmd == "debuffs" then
Bastion.MythicPlusUtils:ToggleDebuffLogging() Bastion.MythicPlusUtils:ToggleDebuffLogging()
Bastion:Print("Debuff logging", Bastion.MythicPlusUtils.debuffLogging and "enabled" or "disabled") Bastion:Print("Debuff logging", Bastion.MythicPlusUtils.debuffLogging and "enabled" or "disabled")
return return
end end
if cmd == 'casts' then if cmd == "casts" then
Bastion.MythicPlusUtils:ToggleCastLogging() Bastion.MythicPlusUtils:ToggleCastLogging()
Bastion:Print("Cast logging", Bastion.MythicPlusUtils.castLogging and "enabled" or "disabled") Bastion:Print("Cast logging", Bastion.MythicPlusUtils.castLogging and "enabled" or "disabled")
return return
@ -313,7 +353,7 @@ Command:Register('mplus', 'Toggle m+ module on/off', function(args)
Bastion:Print("casts") Bastion:Print("casts")
end) end)
Command:Register('missed', 'Dump the list of immune kidney shot spells', function() Command:Register("missed", "Dump the list of immune kidney shot spells", function()
for k, v in pairs(missed) do for k, v in pairs(missed) do
Bastion:Print(k) Bastion:Print(k)
end end
@ -395,3 +435,4 @@ end
Load("@Libraries/") Load("@Libraries/")
Load("@Modules/") Load("@Modules/")
Load("@") Load("@")
LoadThird()

Loading…
Cancel
Save