693 lines
23 KiB
Lua
693 lines
23 KiB
Lua
-- Modules/GroupCooldownTracker.lua
|
|
-- Group-Cooldown-Tracker Modul (ein Frame pro Spieler in der Gruppe)
|
|
|
|
local ADDON_NAME = "HailMaryGuildTools"
|
|
local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME)
|
|
if not HMGT then return end
|
|
local L = LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME)
|
|
|
|
local GCT = HMGT:NewModule("GroupCooldownTracker")
|
|
HMGT.GroupCooldownTracker = GCT
|
|
|
|
GCT.frame = nil
|
|
GCT.frames = {}
|
|
|
|
local function SanitizeFrameToken(name)
|
|
if not name or name == "" then return "Unknown" end
|
|
return name:gsub("[^%w_]", "_")
|
|
end
|
|
|
|
local function ShortName(name)
|
|
if not name then return "" end
|
|
local short = name:match("^[^-]+")
|
|
return short or name
|
|
end
|
|
|
|
local function IsUsableAnchorFrame(frame)
|
|
return frame
|
|
and frame.IsObjectType
|
|
and (frame:IsObjectType("Frame") or frame:IsObjectType("Button"))
|
|
end
|
|
|
|
local function GetFrameUnit(frame)
|
|
if not frame then return nil end
|
|
local unit = frame.unit
|
|
if not unit and frame.GetAttribute then
|
|
unit = frame:GetAttribute("unit")
|
|
end
|
|
return unit
|
|
end
|
|
|
|
local function FrameMatchesUnit(frame, unitId)
|
|
if not IsUsableAnchorFrame(frame) then return false end
|
|
if not unitId then return true end
|
|
local unit = GetFrameUnit(frame)
|
|
return unit == unitId
|
|
end
|
|
|
|
local PLAYER_FRAME_CANDIDATES = {
|
|
"PlayerFrame",
|
|
"ElvUF_Player",
|
|
"NephUI_PlayerFrame",
|
|
"NephUIPlayerFrame",
|
|
"oUF_NephUI_Player",
|
|
"SUFUnitplayer",
|
|
}
|
|
|
|
local PARTY_FRAME_PATTERNS = {
|
|
"PartyMemberFrame%d", -- Blizzard alt
|
|
"CompactPartyFrameMember%d", -- Blizzard modern
|
|
"ElvUF_PartyGroup1UnitButton%d", -- ElvUI
|
|
"ElvUF_PartyUnitButton%d", -- ElvUI variant
|
|
"NephUI_PartyUnitButton%d", -- NephUI (common naming variants)
|
|
"NephUI_PartyFrame%d",
|
|
"NephUIPartyFrame%d",
|
|
"oUF_NephUI_PartyUnitButton%d",
|
|
"SUFUnitparty%d", -- Shadowed Unit Frames
|
|
}
|
|
|
|
local unitFrameCache = {}
|
|
|
|
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
|
|
|
|
local function BuildAnchorLayoutSignature(settings, ordered, unitByPlayer)
|
|
local parts = {
|
|
settings.attachToPartyFrame == true and "attach" or "stack",
|
|
tostring(settings.partyAttachSide or "RIGHT"),
|
|
tostring(tonumber(settings.partyAttachOffsetX) or 8),
|
|
tostring(tonumber(settings.partyAttachOffsetY) or 0),
|
|
tostring(settings.showBar and "bar" or "icon"),
|
|
tostring(settings.growDirection or "DOWN"),
|
|
tostring(settings.width or 250),
|
|
tostring(settings.barHeight or 20),
|
|
tostring(settings.iconSize or 32),
|
|
tostring(settings.iconCols or 6),
|
|
tostring(settings.barSpacing or 2),
|
|
tostring(settings.locked),
|
|
tostring(settings.anchorTo or "UIParent"),
|
|
tostring(settings.anchorPoint or "TOPLEFT"),
|
|
tostring(settings.anchorRelPoint or "TOPLEFT"),
|
|
tostring(settings.anchorX or settings.posX or 0),
|
|
tostring(settings.anchorY or settings.posY or 0),
|
|
}
|
|
|
|
for _, playerName in ipairs(ordered or {}) do
|
|
parts[#parts + 1] = tostring(playerName)
|
|
parts[#parts + 1] = tostring(unitByPlayer and unitByPlayer[playerName] or "")
|
|
end
|
|
|
|
return table.concat(parts, "|")
|
|
end
|
|
|
|
local function ResolveNamedUnitFrame(unitId)
|
|
if unitId == "player" then
|
|
for _, frameName in ipairs(PLAYER_FRAME_CANDIDATES) do
|
|
local frame = _G[frameName]
|
|
if FrameMatchesUnit(frame, unitId) or (frameName == "PlayerFrame" and IsUsableAnchorFrame(frame)) then
|
|
return frame
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local idx = type(unitId) == "string" and unitId:match("^party(%d+)$")
|
|
if not idx then
|
|
return nil
|
|
end
|
|
|
|
idx = tonumber(idx)
|
|
for _, pattern in ipairs(PARTY_FRAME_PATTERNS) do
|
|
local frame = _G[pattern:format(idx)]
|
|
if FrameMatchesUnit(frame, unitId) then
|
|
return frame
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function ScanUnitFrame(unitId)
|
|
local frame = EnumerateFrames()
|
|
local scanned = 0
|
|
while frame and scanned < 8000 do
|
|
if IsUsableAnchorFrame(frame) then
|
|
local unit = GetFrameUnit(frame)
|
|
if unit == unitId then
|
|
HMGT:DebugScoped("verbose", HMGT:GetTrackerDebugScope("Group Cooldowns"), "GroupAttach scan unit=%s scanned=%d found=true", tostring(unitId), scanned)
|
|
return frame
|
|
end
|
|
end
|
|
scanned = scanned + 1
|
|
frame = EnumerateFrames(frame)
|
|
end
|
|
HMGT:DebugScoped("verbose", HMGT:GetTrackerDebugScope("Group Cooldowns"), "GroupAttach scan unit=%s scanned=%d found=false", tostring(unitId), scanned)
|
|
return nil
|
|
end
|
|
|
|
local function ResolveUnitAnchorFrame(unitId)
|
|
if not unitId then return nil end
|
|
|
|
local now = GetTime()
|
|
local cached = unitFrameCache[unitId]
|
|
if cached and now < (cached.expires or 0) then
|
|
if cached.frame and cached.frame:IsShown() then
|
|
return cached.frame
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local frame = ResolveNamedUnitFrame(unitId)
|
|
if not frame then
|
|
frame = ScanUnitFrame(unitId)
|
|
end
|
|
|
|
local expiresIn = 1.0
|
|
if frame and frame:IsShown() then
|
|
expiresIn = 10.0
|
|
end
|
|
unitFrameCache[unitId] = {
|
|
frame = frame,
|
|
expires = now + expiresIn,
|
|
}
|
|
|
|
if frame and frame:IsShown() then
|
|
return frame
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function GCT:GetFrameIdForPlayer(playerName)
|
|
return "GroupCooldownTracker_" .. SanitizeFrameToken(playerName)
|
|
end
|
|
|
|
function GCT:GetPlayerFrame(playerName)
|
|
if not playerName then return nil end
|
|
return self.frames[playerName]
|
|
end
|
|
|
|
function GCT:GetAnchorableFrames()
|
|
return self.frames
|
|
end
|
|
|
|
function GCT:EnsurePlayerFrame(playerName)
|
|
local frame = self.frames[playerName]
|
|
local s = HMGT.db.profile.groupCooldownTracker
|
|
if frame then
|
|
return frame
|
|
end
|
|
|
|
frame = HMGT.TrackerFrame:CreateTrackerFrame(self:GetFrameIdForPlayer(playerName), s)
|
|
frame._hmgtPlayerName = playerName
|
|
self.frames[playerName] = frame
|
|
return frame
|
|
end
|
|
|
|
function GCT:HideAllFrames()
|
|
for _, frame in pairs(self.frames) do
|
|
frame:Hide()
|
|
end
|
|
self.activeOrder = nil
|
|
self.unitByPlayer = nil
|
|
self.frame = nil
|
|
self._lastAnchorLayoutSignature = nil
|
|
self._nextAnchorRetryAt = nil
|
|
end
|
|
|
|
function GCT:SetLockedAll(locked)
|
|
for _, frame in pairs(self.frames) do
|
|
HMGT.TrackerFrame:SetLocked(frame, locked)
|
|
end
|
|
end
|
|
|
|
function GCT:EnsureUpdateTicker()
|
|
if self.updateTicker then
|
|
return
|
|
end
|
|
self.updateTicker = C_Timer.NewTicker(0.1, function()
|
|
self:UpdateDisplay()
|
|
end)
|
|
end
|
|
|
|
function GCT:StopUpdateTicker()
|
|
if self.updateTicker then
|
|
self.updateTicker:Cancel()
|
|
self.updateTicker = nil
|
|
end
|
|
end
|
|
|
|
function GCT:SetUpdateTickerEnabled(enabled)
|
|
if enabled then
|
|
self:EnsureUpdateTicker()
|
|
else
|
|
self:StopUpdateTicker()
|
|
end
|
|
end
|
|
|
|
function GCT:InvalidateAnchorLayout()
|
|
self._lastAnchorLayoutSignature = nil
|
|
self._nextAnchorRetryAt = nil
|
|
end
|
|
|
|
function GCT:RefreshAnchors(force)
|
|
local s = HMGT.db.profile.groupCooldownTracker
|
|
if not s then return end
|
|
|
|
local ordered = {}
|
|
for _, playerName in ipairs(self.activeOrder or {}) do
|
|
local frame = self.frames[playerName]
|
|
if frame and frame:IsShown() then
|
|
table.insert(ordered, playerName)
|
|
end
|
|
end
|
|
|
|
if #ordered == 0 then
|
|
self.frame = nil
|
|
self._lastAnchorLayoutSignature = nil
|
|
self._nextAnchorRetryAt = nil
|
|
return
|
|
end
|
|
|
|
local now = GetTime()
|
|
local signature = BuildAnchorLayoutSignature(s, ordered, self.unitByPlayer)
|
|
if not force and self._lastAnchorLayoutSignature == signature then
|
|
local retryAt = tonumber(self._nextAnchorRetryAt) or 0
|
|
if retryAt <= 0 or now < retryAt then
|
|
return
|
|
end
|
|
end
|
|
|
|
-- Do not force anchor updates while user is dragging a tracker frame.
|
|
for _, playerName in ipairs(ordered) do
|
|
local frame = self.frames[playerName]
|
|
if frame and frame._hmgtDragging then
|
|
return
|
|
end
|
|
end
|
|
|
|
local primaryName = ordered[1]
|
|
local primary = self.frames[primaryName]
|
|
self.frame = primary
|
|
|
|
if s.attachToPartyFrame == true then
|
|
local side = s.partyAttachSide or "RIGHT"
|
|
local extraX = tonumber(s.partyAttachOffsetX) or 8
|
|
local extraY = tonumber(s.partyAttachOffsetY) or 0
|
|
local growsUp = s.showBar == true and s.growDirection == "UP"
|
|
local barHeight = tonumber(s.barHeight) or 20
|
|
local growUpAttachOffset = barHeight + 20
|
|
local prevPlaced = nil
|
|
local missingTargets = 0
|
|
|
|
for i = 1, #ordered do
|
|
local playerName = ordered[i]
|
|
local frame = self.frames[playerName]
|
|
local unitId = self.unitByPlayer and self.unitByPlayer[playerName]
|
|
local target = ResolveUnitAnchorFrame(unitId)
|
|
local contentTopInset = HMGT.TrackerFrame.GetContentTopInset and HMGT.TrackerFrame:GetContentTopInset(frame) or 0
|
|
|
|
frame:ClearAllPoints()
|
|
if target then
|
|
if side == "LEFT" then
|
|
if growsUp then
|
|
frame:SetPoint("BOTTOMRIGHT", target, "TOPLEFT", -extraX, extraY - growUpAttachOffset)
|
|
else
|
|
frame:SetPoint("TOPRIGHT", target, "TOPLEFT", -extraX, extraY + contentTopInset)
|
|
end
|
|
else
|
|
if growsUp then
|
|
frame:SetPoint("BOTTOMLEFT", target, "TOPRIGHT", extraX, extraY - growUpAttachOffset)
|
|
else
|
|
frame:SetPoint("TOPLEFT", target, "TOPRIGHT", extraX, extraY + contentTopInset)
|
|
end
|
|
end
|
|
elseif prevPlaced then
|
|
missingTargets = missingTargets + 1
|
|
HMGT:DebugScoped("verbose", HMGT:GetTrackerDebugScope("Group Cooldowns"), "GroupAttach fallback-stack player=%s unit=%s", tostring(playerName), tostring(unitId))
|
|
if growsUp then
|
|
frame:SetPoint("BOTTOMLEFT", prevPlaced, "TOPLEFT", 0, (s.barSpacing or 2) + 10)
|
|
else
|
|
frame:SetPoint("TOPLEFT", prevPlaced, "BOTTOMLEFT", 0, -((s.barSpacing or 2) + 10))
|
|
end
|
|
else
|
|
missingTargets = missingTargets + 1
|
|
HMGT:DebugScoped("info", HMGT:GetTrackerDebugScope("Group Cooldowns"), "GroupAttach fallback-anchor player=%s unit=%s (no party frame found)", tostring(playerName), tostring(unitId))
|
|
HMGT.TrackerFrame:ApplyAnchor(frame)
|
|
end
|
|
|
|
frame:EnableMouse(false)
|
|
prevPlaced = frame
|
|
end
|
|
if missingTargets > 0 then
|
|
self._lastAnchorLayoutSignature = nil
|
|
self._nextAnchorRetryAt = now + 1.0
|
|
else
|
|
self._lastAnchorLayoutSignature = signature
|
|
self._nextAnchorRetryAt = nil
|
|
end
|
|
return
|
|
end
|
|
|
|
HMGT.TrackerFrame:ApplyAnchor(primary)
|
|
primary:EnableMouse(not s.locked)
|
|
|
|
local gap = (s.barSpacing or 2) + 10
|
|
local growsUp = s.showBar == true and s.growDirection == "UP"
|
|
for i = 2, #ordered do
|
|
local prev = self.frames[ordered[i - 1]]
|
|
local frame = self.frames[ordered[i]]
|
|
frame:ClearAllPoints()
|
|
if growsUp then
|
|
frame:SetPoint("BOTTOMLEFT", prev, "TOPLEFT", 0, gap)
|
|
else
|
|
frame:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, -gap)
|
|
end
|
|
frame:EnableMouse(false)
|
|
end
|
|
self._lastAnchorLayoutSignature = signature
|
|
self._nextAnchorRetryAt = nil
|
|
end
|
|
|
|
-- ============================================================
|
|
-- ENABLE / DISABLE
|
|
-- ============================================================
|
|
|
|
function GCT:Enable()
|
|
local s = HMGT.db.profile.groupCooldownTracker
|
|
if not s.enabled and not s.demoMode and not s.testMode then return end
|
|
|
|
self:UpdateDisplay()
|
|
end
|
|
|
|
function GCT:Disable()
|
|
self:StopUpdateTicker()
|
|
self:HideAllFrames()
|
|
end
|
|
|
|
-- ============================================================
|
|
-- DISPLAY UPDATE
|
|
-- ============================================================
|
|
|
|
function GCT:UpdateDisplay()
|
|
local s = HMGT.db.profile.groupCooldownTracker
|
|
if not s then return end
|
|
|
|
if s.testMode then
|
|
local entries, playerName = HMGT:GetOwnTestEntries(HMGT_SpellData.GroupCooldowns, s, {
|
|
deferChargeCooldownUntilEmpty = false,
|
|
})
|
|
local byPlayer = { [playerName] = {} }
|
|
for _, entry in ipairs(entries) do
|
|
entry.playerName = playerName
|
|
table.insert(byPlayer[playerName], entry)
|
|
end
|
|
|
|
self.activeOrder = { playerName }
|
|
self.unitByPlayer = { [playerName] = "player" }
|
|
self.lastEntryCount = 0
|
|
local active = {}
|
|
local shownOrder = {}
|
|
local shouldTick = false
|
|
for _, pName in ipairs(self.activeOrder) do
|
|
local frame = self:EnsurePlayerFrame(pName)
|
|
HMGT.TrackerFrame:SetLocked(frame, s.locked)
|
|
HMGT.TrackerFrame:SetTitle(frame, string.format("%s - %s", L["GCD_TITLE"], ShortName(pName)))
|
|
local displayEntries = byPlayer[pName]
|
|
if HMGT.FilterDisplayEntries then
|
|
displayEntries = HMGT:FilterDisplayEntries(s, displayEntries) or displayEntries
|
|
end
|
|
if HMGT.SortDisplayEntries then
|
|
HMGT:SortDisplayEntries(displayEntries, "groupCooldownTracker")
|
|
end
|
|
if #displayEntries > 0 then
|
|
HMGT.TrackerFrame:UpdateFrame(frame, displayEntries, true)
|
|
self.lastEntryCount = self.lastEntryCount + #displayEntries
|
|
frame:Show()
|
|
active[pName] = true
|
|
shownOrder[#shownOrder + 1] = pName
|
|
for _, entry in ipairs(displayEntries) do
|
|
if EntryNeedsVisualTicker(entry) then
|
|
shouldTick = true
|
|
break
|
|
end
|
|
end
|
|
else
|
|
frame:Hide()
|
|
end
|
|
end
|
|
self.activeOrder = shownOrder
|
|
|
|
for pn, frame in pairs(self.frames) do
|
|
if not active[pn] then
|
|
frame:Hide()
|
|
end
|
|
end
|
|
|
|
self:RefreshAnchors()
|
|
self:SetUpdateTickerEnabled(shouldTick)
|
|
return
|
|
end
|
|
|
|
if s.demoMode then
|
|
local entries = HMGT:GetDemoEntries("groupCooldownTracker", HMGT_SpellData.GroupCooldowns, s)
|
|
local playerName = HMGT:NormalizePlayerName(UnitName("player")) or "DemoPlayer"
|
|
local byPlayer = { [playerName] = {} }
|
|
for _, entry in ipairs(entries) do
|
|
entry.playerName = playerName
|
|
table.insert(byPlayer[playerName], entry)
|
|
end
|
|
|
|
self.activeOrder = { playerName }
|
|
self.unitByPlayer = { [playerName] = "player" }
|
|
self.lastEntryCount = 0
|
|
local active = {}
|
|
local shownOrder = {}
|
|
local shouldTick = false
|
|
for _, playerName in ipairs(self.activeOrder) do
|
|
local frame = self:EnsurePlayerFrame(playerName)
|
|
HMGT.TrackerFrame:SetLocked(frame, s.locked)
|
|
HMGT.TrackerFrame:SetTitle(frame, string.format("%s - %s", L["GCD_TITLE"], ShortName(playerName)))
|
|
local displayEntries = byPlayer[playerName]
|
|
if HMGT.FilterDisplayEntries then
|
|
displayEntries = HMGT:FilterDisplayEntries(s, displayEntries) or displayEntries
|
|
end
|
|
if HMGT.SortDisplayEntries then
|
|
HMGT:SortDisplayEntries(displayEntries, "groupCooldownTracker")
|
|
end
|
|
if #displayEntries > 0 then
|
|
HMGT.TrackerFrame:UpdateFrame(frame, displayEntries, true)
|
|
self.lastEntryCount = self.lastEntryCount + #displayEntries
|
|
frame:Show()
|
|
active[playerName] = true
|
|
shownOrder[#shownOrder + 1] = playerName
|
|
shouldTick = true
|
|
else
|
|
frame:Hide()
|
|
end
|
|
end
|
|
self.activeOrder = shownOrder
|
|
|
|
for pn, frame in pairs(self.frames) do
|
|
if not active[pn] then
|
|
frame:Hide()
|
|
end
|
|
end
|
|
|
|
self:RefreshAnchors()
|
|
self:SetUpdateTickerEnabled(shouldTick)
|
|
return
|
|
end
|
|
|
|
if IsInRaid() or not IsInGroup() then
|
|
self.lastEntryCount = 0
|
|
self:StopUpdateTicker()
|
|
self:HideAllFrames()
|
|
return
|
|
end
|
|
if not s.enabled then
|
|
self.lastEntryCount = 0
|
|
self:StopUpdateTicker()
|
|
self:HideAllFrames()
|
|
return
|
|
end
|
|
if not HMGT:IsVisibleForCurrentGroup(s) then
|
|
self.lastEntryCount = 0
|
|
self:StopUpdateTicker()
|
|
self:HideAllFrames()
|
|
return
|
|
end
|
|
|
|
local entriesByPlayer, order, unitByPlayer = self:CollectEntriesByPlayer()
|
|
self.activeOrder = order
|
|
self.unitByPlayer = unitByPlayer
|
|
self.lastEntryCount = 0
|
|
local active = {}
|
|
local shownOrder = {}
|
|
local shouldTick = false
|
|
|
|
for _, playerName in ipairs(order) do
|
|
local frame = self:EnsurePlayerFrame(playerName)
|
|
HMGT.TrackerFrame:SetLocked(frame, s.locked)
|
|
HMGT.TrackerFrame:SetTitle(frame, string.format("%s - %s", L["GCD_TITLE"], ShortName(playerName)))
|
|
|
|
local entries = entriesByPlayer[playerName] or {}
|
|
if HMGT.FilterDisplayEntries then
|
|
entries = HMGT:FilterDisplayEntries(s, entries) or entries
|
|
end
|
|
if HMGT.SortDisplayEntries then
|
|
HMGT:SortDisplayEntries(entries, "groupCooldownTracker")
|
|
end
|
|
if #entries > 0 then
|
|
HMGT.TrackerFrame:UpdateFrame(frame, entries, true)
|
|
self.lastEntryCount = self.lastEntryCount + #entries
|
|
frame:Show()
|
|
active[playerName] = true
|
|
shownOrder[#shownOrder + 1] = playerName
|
|
for _, entry in ipairs(entries) do
|
|
if EntryNeedsVisualTicker(entry) then
|
|
shouldTick = true
|
|
break
|
|
end
|
|
end
|
|
else
|
|
frame:Hide()
|
|
end
|
|
end
|
|
self.activeOrder = shownOrder
|
|
|
|
for pn, frame in pairs(self.frames) do
|
|
if not active[pn] then
|
|
frame:Hide()
|
|
end
|
|
end
|
|
|
|
self:RefreshAnchors()
|
|
self:SetUpdateTickerEnabled(shouldTick)
|
|
end
|
|
|
|
function GCT:CollectEntriesByPlayer()
|
|
local s = HMGT.db.profile.groupCooldownTracker
|
|
local byPlayer = {}
|
|
local playerOrder = {}
|
|
local unitByPlayer = {}
|
|
local players = self:GetGroupPlayers()
|
|
|
|
for _, playerInfo in ipairs(players) do
|
|
repeat
|
|
local name = playerInfo.name
|
|
if not name then break end
|
|
|
|
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 knownCDs = HMGT_SpellData.GetSpellsForSpec(class, specIdx, HMGT_SpellData.GroupCooldowns)
|
|
local entries = {}
|
|
for _, spellEntry in ipairs(knownCDs) do
|
|
if s.enabledSpells[spellEntry.spellId] ~= false then
|
|
local remaining, total, curCharges, maxCharges = HMGT:GetCooldownInfo(name, spellEntry.spellId, {
|
|
deferChargeCooldownUntilEmpty = false,
|
|
})
|
|
local isAvailabilitySpell = HMGT.IsAvailabilitySpell and HMGT:IsAvailabilitySpell(spellEntry)
|
|
local effectiveCd = HMGT_SpellData.GetEffectiveCooldown(spellEntry, talents)
|
|
local hasChargeSpell = (tonumber(maxCharges) or 0) > 1
|
|
local hasPartialCharges = (tonumber(maxCharges) or 0) > 0 and (tonumber(curCharges) or tonumber(maxCharges) or 0) < (tonumber(maxCharges) or 0)
|
|
local include = HMGT:ShouldDisplayEntry(s, remaining, curCharges, maxCharges, spellEntry)
|
|
local spellKnown = HMGT:IsTrackedSpellKnownForPlayer(name, spellEntry.spellId)
|
|
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
|
|
table.insert(entries, {
|
|
playerName = name,
|
|
class = class,
|
|
spellEntry = spellEntry,
|
|
remaining = remaining,
|
|
total = total > 0 and total or effectiveCd,
|
|
currentCharges = curCharges,
|
|
maxCharges = maxCharges,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
|
|
if #entries > 0 then
|
|
byPlayer[name] = entries
|
|
table.insert(playerOrder, name)
|
|
unitByPlayer[name] = playerInfo.unitId
|
|
end
|
|
until true
|
|
end
|
|
|
|
table.sort(playerOrder, function(a, b)
|
|
local own = HMGT:NormalizePlayerName(UnitName("player"))
|
|
if a == own and b ~= own then return true end
|
|
if b == own and a ~= own then return false end
|
|
return a < b
|
|
end)
|
|
|
|
return byPlayer, playerOrder, unitByPlayer
|
|
end
|
|
|
|
function GCT:GetGroupPlayers()
|
|
local players = {}
|
|
local ownName = HMGT:NormalizePlayerName(UnitName("player"))
|
|
local settings = HMGT.db and HMGT.db.profile and HMGT.db.profile.groupCooldownTracker
|
|
|
|
if settings and settings.includeSelfFrame == true and ownName then
|
|
table.insert(players, {
|
|
name = ownName,
|
|
class = select(2, UnitClass("player")),
|
|
unitId = "player",
|
|
isOwn = true,
|
|
})
|
|
end
|
|
|
|
if IsInGroup() and not IsInRaid() 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
|
|
|