Bastion aims to serve as a highly performant, simplisitic, and expandable World of Warcraft data visualization framework.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Bastion/src/_bastion.lua

321 lines
9.6 KiB

---@type Tinkr
local Tinkr = ...
---@class Bastion.Globals.SpellName : { [spellId]: string }
---@class Bastion
local Bastion = {
Enabled = false,
Globals = {
---@type Bastion.Globals.SpellName
SpellName = {}
},
Tick = GetGameTick()
}
local BastionBase = string.format("%s/%s", "scripts", "bastion")
local BastionScriptsBase = string.format("%s/%s", BastionBase, "scripts")
local ThirdPartyModulesBase = string.format("%s/%s", "scripts", "BastionScripts")
Bastion.__index = 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
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, table
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)
local base = filePathModifier == "@" and BastionScriptsBase or filePathModifier == "~" and BastionBase
if base then
loadedFile.newPath = string.format("%s%s", base, loadedFile.originalPath:sub(2))
loadedFile.loadedPath = loadedFile.newPath
end
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
local exampleNames = {
"ExampleDependency.lua",
"ExampleDependencyError.lua",
"ExampleLibrary.lua",
"ExampleModule.lua",
}
---@param dir string
local function Load(dir)
if dir:sub(1, 1) == "@" then
dir = dir:sub(2)
dir = string.format("%s/%s", BastionScriptsBase, dir)
end
if dir:sub(1, 1) == "~" then
dir = dir:sub(2)
dir = string.format("%s/%s", BastionBase, dir)
end
local files = ListFiles(dir)
for i = 1, #files do
local file = files[i]
local loadFile = true
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
local bastionFiles = {
"~/src/ClassMagic/ClassMagic",
"~/src/List/List",
"~/src/Command/Command",
"~/src/Util/Util",
"~/src/Library/Library",
"~/src/Notification/Notification",
"~/src/NotificationList/NotificationList",
"~/src/Vector3/Vector3",
"~/src/Sequencer/Sequencer",
"~/src/Cache/Cache",
"~/src/Cacheable/Cacheable",
"~/src/Refreshable/Refreshable",
"~/src/Unit/Unit",
"~/src/Aura/Aura",
"~/src/APLTrait/APLTrait",
"~/src/APLActor/APLActor",
"~/src/APL/APL",
"~/src/Module/Module",
"~/src/UnitManager/UnitManager",
"~/src/ObjectManager/ObjectManager",
"~/src/EventManager/EventManager",
"~/src/Spell/Spell",
"~/src/SpellBook/SpellBook",
"~/src/Item/Item",
"~/src/ItemBook/ItemBook",
"~/src/AuraTable/AuraTable",
"~/src/Class/Class",
"~/src/Timer/Timer",
"~/src/MythicPlusUtils/MythicPlusUtils",
"~/src/Config/Config",
"~/src/TimeToDie/TimeToDie",
}
for i = 1, #bastionFiles do
Bastion:Require(bastionFiles[i])
end
Bastion.Globals.CombatTimer = Bastion.Timer:New("combat", function()
return UnitAffectingCombat("player")
end)
---@param unitTarget UnitId
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_HEALTH", function(unitTarget)
--Bastion.UnitManager:Get(unitTarget):UpdateHealth()
end)
---@param unit UnitToken
---@param auras UnitAuraUpdateInfo
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_AURA", function(unit, auras)
---@type Bastion.Unit | nil
local u = Bastion.UnitManager:Get(unit)
if u then
u:GetAuras():OnUpdate(auras)
end
end)
Bastion.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
---@type UnitIds, string, spellId
local unit, castGUID, spellID = ...
local spell = Bastion.Globals.SpellBook:GetIfRegistered(spellID)
if unit == "player" and spell then
spell.lastCastAt = GetTime()
if spell:GetPostCastFunction() then
spell:GetPostCastFunction()(spell)
end
end
end)
local playerGuid = UnitGUID("player")
local missed = {}
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = { CombatLogGetCurrentEventInfo() }
---@type string
local subEvent = args[2]
---@type string
local sourceGUID = args[4]
---@type string
local destGUID = args[8]
---@type number
local spellID = args[12]
---@type string
local spellName = args[13]
if subEvent == "SPELL_CAST_SUCCESS" then
if (not Bastion.Globals.SpellName[spellID] or Bastion.Globals.SpellName[spellID] ~= spellName) then
Bastion.Globals.SpellName[spellID] = spellName
end
end
---@type Bastion.Unit?
local u = Bastion.UnitManager[sourceGUID]
---@type Bastion.Unit
local u2 = Bastion.UnitManager[destGUID]
local t = GetTime()
if type(u) ~= "nil" then
u:SetLastCombatTime(t)
end
if type(u2) ~= "nil" then
u2:SetLastCombatTime(t)
if subEvent == "SPELL_MISSED" and sourceGUID == playerGuid and spellID == 408 then
local missType = args[15]
if type(u) ~= "nil" and missType == "IMMUNE" then
local castingSpell = u:GetCastingOrChannelingSpell()
if castingSpell and type(castingSpell) == "table" then
if not missed[castingSpell:GetID()] then
missed[castingSpell:GetID()] = true
end
end
end
end
end
end)
Bastion.Ticker = C_Timer.NewTicker(0.1, function()
Bastion.Tick = GetGameTick()
Bastion.Globals.CombatTimer:Check()
if Bastion.Enabled then
Bastion.ObjectManager:Refresh()
Bastion.TimeToDie:Refresh()
Bastion:TickModules()
end
end)
Bastion.Globals.Command:Register("dumpspells", "Dump spells to a file", function()
local i = 1
local rand = math.random(100000, 999999)
while true do
local spellName, spellSubName = GetSpellBookItemName(i, BOOKTYPE_SPELL)
if not spellName then
do
break
end
end
-- use spellName and spellSubName here
local spellID = select(7, GetSpellInfo(spellName))
if spellID then
spellName = spellName:gsub("[%W%s]", "")
WriteFile("bastion-" .. UnitClass("player") .. "-" .. rand .. ".lua",
"local " .. spellName .. " = Bastion.Globals.SpellBook:GetSpell(" .. spellID .. ")\n", true)
end
i = i + 1
end
end)
Bastion.Globals.Command:Register("missed", "Dump the list of immune kidney shot spells", function()
for k, v in pairs(missed) do
Bastion.Util:Print(k)
end
end)
Load("@Libraries/")
Load("@Modules/")
Load("@")
LoadThird()