Files
HailMaryGuildTools/Modules/Tracker/SingleFrameTrackerBase.lua
Torsten Brendgen fc5a8aa361 initial commit
2026-04-10 21:30:31 +02:00

306 lines
9.9 KiB
Lua

-- Modules/Tracker/SingleFrameTrackerBase.lua
-- Shared implementation for single-frame tracker modules.
local ADDON_NAME = "HailMaryGuildTools"
local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME)
if not HMGT then return end
HMGT.SingleFrameTrackerBase = HMGT.SingleFrameTrackerBase or {}
local Base = HMGT.SingleFrameTrackerBase
local function GetDefaultGroupPlayers()
local players = {}
local ownName = HMGT:NormalizePlayerName(UnitName("player"))
local ownClass = select(2, UnitClass("player"))
table.insert(players, { name = ownName, class = ownClass, isOwn = true, unitId = "player" })
if IsInRaid() then
for i = 1, GetNumGroupMembers() do
local unitId = "raid" .. i
local name = HMGT:NormalizePlayerName(UnitName(unitId))
local class = select(2, UnitClass(unitId))
if name and name ~= ownName then
table.insert(players, { name = name, class = class, unitId = unitId })
end
end
elseif IsInGroup() then
for i = 1, GetNumGroupMembers() - 1 do
local unitId = "party" .. i
local name = HMGT:NormalizePlayerName(UnitName(unitId))
local class = select(2, UnitClass(unitId))
if name and name ~= ownName then
table.insert(players, { name = name, class = class, unitId = unitId })
end
end
end
return players
end
local function ResolveConfigValue(configValue, self)
if type(configValue) == "function" then
return configValue(self)
end
return configValue
end
local function EntryNeedsVisualTicker(entry)
if type(entry) ~= "table" then
return false
end
local remaining = tonumber(entry.remaining) or 0
if remaining > 0 then
return true
end
local maxCharges = tonumber(entry.maxCharges) or 0
local currentCharges = tonumber(entry.currentCharges)
if maxCharges > 0 and currentCharges ~= nil and currentCharges < maxCharges then
return true
end
return false
end
function Base:Create(config)
local tracker = {
frame = nil,
updateTicker = nil,
lastEntryCount = 0,
}
function tracker:GetSettings()
return HMGT.db.profile[config.profileKey]
end
function tracker:GetDatabase()
return ResolveConfigValue(config.database, self) or {}
end
function tracker:GetTitle()
return ResolveConfigValue(config.title, self) or config.frameName
end
function tracker:GetDemoKey()
return ResolveConfigValue(config.demoKey, self) or config.profileKey
end
function tracker:GetCooldownInfoOpts()
return ResolveConfigValue(config.cooldownInfoOpts, self)
end
function tracker:GetGroupPlayers()
local custom = ResolveConfigValue(config.groupPlayersProvider, self)
if type(custom) == "table" then
return custom
end
return GetDefaultGroupPlayers()
end
function tracker:EnsureUpdateTicker()
if self.updateTicker then
return
end
self.updateTicker = C_Timer.NewTicker(0.1, function()
self:UpdateDisplay()
end)
end
function tracker:StopUpdateTicker()
if self.updateTicker then
self.updateTicker:Cancel()
self.updateTicker = nil
end
end
function tracker:SetUpdateTickerEnabled(enabled)
if enabled then
self:EnsureUpdateTicker()
else
self:StopUpdateTicker()
end
end
function tracker:Enable()
local s = self:GetSettings()
if not s.enabled and not s.demoMode and not s.testMode then return end
if not self.frame then
self.frame = HMGT.TrackerFrame:CreateTrackerFrame(config.frameName, s)
HMGT.TrackerFrame:SetTitle(self.frame, self:GetTitle())
end
if HMGT:IsVisibleForCurrentGroup(s) then
self.frame:Show()
else
self.frame:Hide()
end
self:UpdateDisplay()
end
function tracker:Disable()
self:StopUpdateTicker()
if self.frame then
self.frame:Hide()
end
end
function tracker:UpdateDisplay()
if not self.frame then
self:StopUpdateTicker()
return
end
local s = self:GetSettings()
local database = self:GetDatabase()
local cooldownInfoOpts = self:GetCooldownInfoOpts()
if s.testMode then
HMGT.TrackerFrame:SetLocked(self.frame, s.locked)
local entries = HMGT:GetOwnTestEntries(database, s, cooldownInfoOpts)
self.lastEntryCount = #entries
HMGT.TrackerFrame:UpdateFrame(self.frame, entries)
self.frame:Show()
local shouldTick = false
for _, entry in ipairs(entries) do
if EntryNeedsVisualTicker(entry) then
shouldTick = true
break
end
end
self:SetUpdateTickerEnabled(shouldTick)
return
end
if s.demoMode then
HMGT.TrackerFrame:SetLocked(self.frame, s.locked)
local entries = HMGT:GetDemoEntries(self:GetDemoKey(), database, s)
self.lastEntryCount = #entries
HMGT.TrackerFrame:UpdateFrame(self.frame, entries)
self.frame:Show()
self:SetUpdateTickerEnabled(#entries > 0)
return
end
if not s.enabled then
self.lastEntryCount = 0
self.frame:Hide()
self:StopUpdateTicker()
return
end
if not HMGT:IsVisibleForCurrentGroup(s) then
self.lastEntryCount = 0
self.frame:Hide()
self:StopUpdateTicker()
return
end
HMGT.TrackerFrame:SetLocked(self.frame, s.locked)
local entries = self:CollectEntries()
self.lastEntryCount = #entries
if HMGT.SortDisplayEntries then
HMGT:SortDisplayEntries(entries, config.profileKey)
end
HMGT.TrackerFrame:UpdateFrame(self.frame, entries)
self.frame:Show()
local shouldTick = false
for _, entry in ipairs(entries) do
if EntryNeedsVisualTicker(entry) then
shouldTick = true
break
end
end
self:SetUpdateTickerEnabled(shouldTick)
end
function tracker:CollectEntries()
local entries = {}
local s = self:GetSettings()
local database = self:GetDatabase()
local cooldownInfoOpts = self:GetCooldownInfoOpts()
local players = self:GetGroupPlayers()
for _, playerInfo in ipairs(players) do
repeat
local name = playerInfo.name
local pData = HMGT.playerData[name]
local class = pData and pData.class or playerInfo.class
local specIdx
if playerInfo.isOwn then
specIdx = GetSpecialization()
if not specIdx or specIdx == 0 then break end
else
specIdx = pData and pData.specIndex or nil
if not specIdx or tonumber(specIdx) <= 0 then break end
end
local talents = pData and pData.talents or {}
if not class then break end
local knownSpells = HMGT_SpellData.GetSpellsForSpec(class, specIdx, database)
for _, spellEntry in ipairs(knownSpells) do
if s.enabledSpells[spellEntry.spellId] ~= false then
local remaining, total, curCharges, maxCharges = HMGT:GetCooldownInfo(name, spellEntry.spellId, cooldownInfoOpts)
local isAvailabilitySpell = HMGT.IsAvailabilitySpell and HMGT:IsAvailabilitySpell(spellEntry)
local effectiveCd = HMGT_SpellData.GetEffectiveCooldown(spellEntry, talents)
local include = HMGT:ShouldDisplayEntry(s, remaining, curCharges, maxCharges, spellEntry)
local spellKnown = HMGT:IsTrackedSpellKnownForPlayer(name, spellEntry.spellId)
local hasPartialCharges = (tonumber(maxCharges) or 0) > 0
and (tonumber(curCharges) or tonumber(maxCharges) or 0) < (tonumber(maxCharges) or 0)
local hasActiveCd = ((remaining or 0) > 0) or hasPartialCharges
if not spellKnown and not hasActiveCd then
include = false
end
if isAvailabilitySpell and not spellKnown then
include = false
end
if not playerInfo.isOwn then
if isAvailabilitySpell and not HMGT:HasAvailabilityState(name, spellEntry.spellId) then
include = false
end
end
if include then
entries[#entries + 1] = {
playerName = name,
class = class,
spellEntry = spellEntry,
remaining = remaining,
total = total > 0 and total or effectiveCd,
currentCharges = curCharges,
maxCharges = maxCharges,
}
end
end
end
until true
end
return entries
end
return tracker
end
function Base:CreateModule(moduleName, config, ...)
if type(moduleName) ~= "string" or moduleName == "" then
return self:Create(config)
end
local module = HMGT:NewModule(moduleName, ...)
local tracker = self:Create(config)
for key, value in pairs(tracker) do
module[key] = value
end
HMGT[moduleName] = module
return module
end