Migrate to Bastion/Bastion.lua

main
JeffyLikesApples 11 months ago
parent 96a1fafa41
commit b2e94e7d1b
  1. 24
      scripts/ExampleModule.lua
  2. 21
      scripts/Libraries/ExampleDependency.lua
  3. 15
      scripts/Libraries/ExampleDependencyError.lua
  4. 25
      scripts/Libraries/ExampleLibrary.lua
  5. 333
      src/Bastion/Bastion.lua
  6. 325
      src/_bastion.lua

@ -1,24 +0,0 @@
local Tinkr, Bastion = ...
local ExampleModule = Bastion.Module:New('ExampleModule')
local Player = Bastion.UnitManager:Get('player')
-- Create a local spellbook
local SpellBook = Bastion.SpellBook:New()
local FlashHeal = SpellBook:GetSpell(2061)
-- Get a global spell (this can collide with other modules, so be careful)
-- This is useful for caching common spells that you might not actually cast, and to avoid needless spell creation inline
local FlashHeal = Bastion.Globals.SpellBook:GetSpell(2061)
local AdvancedMath = Bastion:Import('AdvancedMath')
print(AdvancedMath:Add(1, 2))
ExampleModule:Sync(function()
if Player:GetHP() <= 50 then
FlashHeal:Cast(Player)
end
end)
Bastion:Register(ExampleModule)

@ -1,21 +0,0 @@
local Tinkr, Bastion = ...
local Player = Bastion.UnitManager:Get('player')
Bastion:RegisterLibrary(Bastion.Library:New({
name = 'Dependable',
exports = {
default = function()
local Dependable = {}
Dependable.__index = Dependable
function Dependable:Test(a)
print(a)
end
return Dependable
end,
Test = 5
}
}))

@ -1,15 +0,0 @@
local Tinkr, Bastion = ...
Bastion:RegisterLibrary(Bastion.Library:New({
name = 'Circular',
exports = {
default = function(self)
-- Return default first, and then the remaining exports
local Math, OtherExports = self:Import('AdvancedMath')
print(Math:Add(1, 2))
return 'Circular'
end
}
}))

@ -1,25 +0,0 @@
local Tinkr, Bastion = ...
Bastion:RegisterLibrary(Bastion.Library:New({
name = 'AdvancedMath',
exports = {
default = function(self) -- Function exports are called when the library is loaded
-- Return default first, and then the remaining exports
local Dependable, OtherExports = self:Import('Dependable')
local CircularDependency = self:Import('Circular') -- Causes a circular dependency error
Dependable:Test(OtherExports.Test)
local AdvancedMath = {}
AdvancedMath.__index = AdvancedMath
function AdvancedMath:Add(a, b)
return a + b
end
return AdvancedMath
end
}
}))

@ -0,0 +1,333 @@
---@type Tinkr
local Tinkr = ...
---@class Bastion.Globals.SpellName : { [spellId]: string }
---@class Bastion
local Bastion = {
Enabled = false,
Globals = {
---@type Bastion.Globals.SpellName
SpellName = {}
},
---@class Bastion.LoadedFiles.Table
---@field [number] { originalPath: string, loadedPath: string, reloadable: boolean, order: number, newPath: string }
LoadedFiles = {},
LoadThird = true,
Paths = {
Scripts = "scripts",
BastionBase = "scripts/bastion",
BastionScripts = "scripts/bastion/scripts",
ThirdParty = "scripts/BastionScripts",
},
Tick = 0,
}
function Bastion:__index(key)
if Bastion[key] then
return Bastion[key]
end
return rawget(self, key)
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/EventManager/EventManager",
"~/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/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",
}
---@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 = filePath.filePath or filePath,
newPath = filePath.filePath or filePath,
loadedPath = filePath.filePath or filePath,
reloadable = not filePath.reloadable and false or false,
order = #Bastion.LoadedFiles + 1,
}
local pathResolutionShortcut = loadedFile.originalPath:sub(1, 1) == "@" and Bastion.Paths.BastionScripts or
loadedFile.originalPath:sub(1, 1) == "~" and Bastion.Paths.BastionBase or
loadedFile.originalPath:sub(1, 1) == "!" and Bastion.Paths.Scripts
if pathResolutionShortcut then
loadedFile.newPath = string.format("%s%s", pathResolutionShortcut, loadedFile.originalPath:sub(2))
loadedFile.loadedPath = loadedFile.newPath
end
if DirectoryExists(loadedFile.newPath) then
local fileList = ListFiles(loadedFile.newPath)
if #fileList > 0 then
local loadResults = {}
for i = 1, #fileList do
local file = loadedFile.newPath .. fileList[i]
local status, returns = Bastion:Require(file, ...)
table.insert(loadResults, { file = file, status = status, returns = returns })
end
return 1, loadResults
end
Log(string.format("Bastion:Require - No files found in directory: %s", loadedFile.newPath))
return 0, SafePack(nil)
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
---@param load? boolean
---@param folder? string
local function LoadThird(load, folder)
load = type(load) == "nil" and Bastion.LoadThird or load
folder = folder or Bastion.Paths.ThirdParty
if not load then
return
end
local thirdPartyModulesFolders = ListFolders(folder)
for i = 1, #thirdPartyModulesFolders do
local moduleFolder = thirdPartyModulesFolders[i]
local currentFolderDir = string.format("%s/%s", folder, moduleFolder)
local loaderFilePath = string.format("%s/%s", currentFolderDir, "loader")
if FileExists(loaderFilePath .. ".lua") or FileExists(loaderFilePath .. ".luac") then
Bastion:Require(loaderFilePath, currentFolderDir)
end
end
end
---@param toggle? boolean
function Bastion:Toggle(toggle)
Bastion.Enabled = type(toggle) ~= "nil" and toggle or not Bastion.Enabled
end
local loaded = false
function Bastion:Load()
if loaded then
return self
end
for i = 1, #bastionFiles do
self:Require(bastionFiles[i])
end
self.Globals.Command:Register('toggle', 'Toggle bastion on/off', function()
self:Toggle()
if self.Enabled then
self.Util:Print("Enabled")
else
self.Util:Print("Disabled")
end
end)
self.Globals.CombatTimer = self.Timer:New("combat", function()
return UnitAffectingCombat("player")
end)
---@param unitTarget UnitId
self.Globals.EventManager:RegisterWoWEvent("UNIT_HEALTH", function(unitTarget)
--Bastion.UnitManager:Get(unitTarget):UpdateHealth()
end)
---@param unit UnitToken
---@param auras UnitAuraUpdateInfo
self.Globals.EventManager:RegisterWoWEvent("UNIT_AURA", function(unit, auras)
---@type Bastion.Unit | nil
local u = self.UnitManager:Get(unit)
if u then
u:GetAuras():OnUpdate(auras)
end
end)
self.Globals.EventManager:RegisterWoWEvent("UNIT_SPELLCAST_SUCCEEDED", function(...)
---@type UnitIds, string, spellId
local unit, castGUID, spellID = ...
local spell = self.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 = {}
self.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = { CombatLogGetCurrentEventInfo() }
local subEvent = args[2]
local sourceGUID = args[4]
local destGUID = args[8]
local spellID = args[12]
local spellName = args[13]
if subEvent == "SPELL_CAST_SUCCESS" then
if (not self.Globals.SpellName[spellID] or self.Globals.SpellName[spellID] ~= spellName) then
self.Globals.SpellName[spellID] = spellName
end
end
local u = self.UnitManager[sourceGUID]
local u2 = self.UnitManager[destGUID]
local t = GetTime()
if u then
u:SetLastCombatTime(t)
end
if u2 then
u2:SetLastCombatTime(t)
if subEvent == "SPELL_MISSED" and sourceGUID == playerGuid and spellID == 408 then
local missType = args[15]
if 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)
self.Ticker = C_Timer.NewTicker(0.1, function()
Bastion.Tick = GetGameTick()
self.Globals.CombatTimer:Check()
if Bastion.Enabled then
self.ObjectManager:Refresh()
self.TimeToDie:Refresh()
self:TickModules()
end
end)
self.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)
self.Globals.Command:Register("missed", "Dump the list of immune kidney shot spells", function()
for k, v in pairs(missed) do
self.Util:Print(k)
end
end)
self:Require("@/Libraries/")
self:Require("@/Modules/")
self:Require("@/")
LoadThird()
loaded = true
return self
end
return Bastion

@ -1,327 +1,4 @@
---@type Tinkr
local Tinkr = ...
---@class Bastion.Globals.SpellName : { [spellId]: string }
---@class Bastion
local Bastion = {
Enabled = false,
Globals = {
---@type Bastion.Globals.SpellName
SpellName = {}
},
Tick = GetGameTick()
}
Bastion.__index = Bastion
local BastionBase = string.format("%s/%s", "scripts", "bastion")
local BastionScriptsBase = string.format("%s/%s", BastionBase, "scripts")
local ThirdPartyModulesBase = string.format("%s/%s", "scripts", "BastionScripts")
---@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/EventManager/EventManager",
"~/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/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
---@param toggle? boolean
function Bastion:Toggle(toggle)
Bastion.Enabled = type(toggle) ~= "nil" and toggle or not Bastion.Enabled
end
Bastion.Globals.Command:Register('toggle', 'Toggle bastion on/off', function()
Bastion:Toggle()
if Bastion.Enabled then
Bastion.Util:Print("Enabled")
else
Bastion.Util:Print("Disabled")
end
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() }
local subEvent = args[2]
local sourceGUID = args[4]
local destGUID = args[8]
local spellID = args[12]
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
local u = Bastion.UnitManager[sourceGUID]
local u2 = Bastion.UnitManager[destGUID]
local t = GetTime()
if u then
u:SetLastCombatTime(t)
end
if u2 then
u2:SetLastCombatTime(t)
if subEvent == "SPELL_MISSED" and sourceGUID == playerGuid and spellID == 408 then
local missType = args[15]
if 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()
require("scripts/bastion/src/Bastion/Bastion"):Load()

Loading…
Cancel
Save