local ADDON_NAME = "HailMaryGuildTools" local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME) if not HMGT or not HMGT.TrackerManager then return end local Manager = HMGT.TrackerManager local S = Manager._shared or {} function Manager:EnsurePlayerFrame(tracker, playerName) local frameKey = S.GetTrackerFrameKey(tracker.id) self.perPlayerFrames[frameKey] = self.perPlayerFrames[frameKey] or {} local frame = self.perPlayerFrames[frameKey][playerName] if not frame then frame = HMGT.TrackerFrame:CreateTrackerFrame(S.GetTrackerPlayerFrameName(tracker.id, playerName), tracker) frame._hmgtTrackerId = tonumber(tracker.id) or 0 frame._hmgtPlayerName = playerName self.perPlayerFrames[frameKey][playerName] = frame end frame._settings = tracker HMGT.TrackerFrame:SetTitle(frame, string.format("%s - %s", S.GetTrackerLabel(tracker), S.ShortName(playerName))) HMGT.TrackerFrame:SetLocked(frame, tracker.locked) return frame end function Manager:HidePlayerFrames(frameKey) local frames = self.perPlayerFrames[frameKey] if type(frames) ~= "table" then self.activeOrders[frameKey] = nil self.unitByPlayer[frameKey] = nil self.anchorLayoutSignatures[frameKey] = nil self.nextAnchorRetryAt[frameKey] = nil self._displaySignatures[frameKey] = "0" return end for _, frame in pairs(frames) do frame:Hide() end self.activeOrders[frameKey] = nil self.unitByPlayer[frameKey] = nil self.anchorLayoutSignatures[frameKey] = nil self.nextAnchorRetryAt[frameKey] = nil self._displaySignatures[frameKey] = "0" end function Manager:BuildEntriesByPlayerForTracker(tracker) local frameKey = S.GetTrackerFrameKey(tracker.id) local ownName = HMGT:NormalizePlayerName(UnitName("player")) or "Player" if tracker.testMode then local entries = self:CollectTestEntries(tracker) if S.IsGroupTracker(tracker) and tracker.attachToPartyFrame == true then return S.BuildPartyPreviewEntries(entries) end local byPlayer, order, unitByPlayer = {}, {}, {} if #entries > 0 then byPlayer[ownName] = entries order[1] = ownName unitByPlayer[ownName] = "player" end return byPlayer, order, unitByPlayer, true end if tracker.demoMode then local entries = HMGT:GetDemoEntries(frameKey, S.GetTrackerSpellPool(tracker.categories), tracker) if S.IsGroupTracker(tracker) and tracker.attachToPartyFrame == true then return S.BuildPartyPreviewEntries(entries) end for _, entry in ipairs(entries) do entry.playerName = ownName end local byPlayer, order, unitByPlayer = {}, {}, {} if #entries > 0 then byPlayer[ownName] = entries order[1] = ownName unitByPlayer[ownName] = "player" end return byPlayer, order, unitByPlayer, true end if not tracker.enabled or not HMGT:IsVisibleForCurrentGroup(tracker) then return {}, {}, {}, false end if IsInRaid() or not IsInGroup() then return {}, {}, {}, false end local byPlayer, order, unitByPlayer = {}, {}, {} for _, playerInfo in ipairs(S.GetGroupPlayers(tracker)) do local entries = S.CollectEntriesForPlayer(tracker, playerInfo) if #entries > 0 then local playerName = playerInfo.name byPlayer[playerName] = entries order[#order + 1] = playerName unitByPlayer[playerName] = playerInfo.unitId end end return byPlayer, order, unitByPlayer, true end function Manager:RefreshPerGroupAnchors(tracker, force) local frameKey = S.GetTrackerFrameKey(tracker.id) local frames = self.perPlayerFrames[frameKey] or {} local ordered = {} for _, playerName in ipairs(self.activeOrders[frameKey] or {}) do local frame = frames[playerName] if frame and frame:IsShown() then ordered[#ordered + 1] = playerName end end if #ordered == 0 then self.anchorLayoutSignatures[frameKey] = nil self.nextAnchorRetryAt[frameKey] = nil return end local now = GetTime() local signature = S.BuildAnchorLayoutSignature(tracker, ordered, self.unitByPlayer[frameKey]) if not force and self.anchorLayoutSignatures[frameKey] == signature then local retryAt = tonumber(self.nextAnchorRetryAt[frameKey]) or 0 if retryAt <= 0 or now < retryAt then return end end for _, playerName in ipairs(ordered) do local frame = frames[playerName] if frame and frame._hmgtDragging then return end end if tracker.attachToPartyFrame == true then local side = tracker.partyAttachSide or "RIGHT" local extraX = tonumber(tracker.partyAttachOffsetX) or 8 local extraY = tonumber(tracker.partyAttachOffsetY) or 0 local growsUp = tracker.showBar == true and tracker.growDirection == "UP" local barHeight = tonumber(tracker.barHeight) or 20 local growUpAttachOffset = barHeight + 20 local prevPlaced = nil local missingTargets = 0 for _, playerName in ipairs(ordered) do local frame = frames[playerName] local unitId = self.unitByPlayer[frameKey] and self.unitByPlayer[frameKey][playerName] local target = S.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(tracker), "TrackerAttach fallback-stack tracker=%s player=%s unit=%s", tostring(tracker.id), tostring(playerName), tostring(unitId)) if growsUp then frame:SetPoint("BOTTOMLEFT", prevPlaced, "TOPLEFT", 0, (tracker.barSpacing or 2) + 10) else frame:SetPoint("TOPLEFT", prevPlaced, "BOTTOMLEFT", 0, -((tracker.barSpacing or 2) + 10)) end else missingTargets = missingTargets + 1 HMGT:DebugScoped("info", HMGT:GetTrackerDebugScope(tracker), "TrackerAttach fallback-anchor tracker=%s player=%s unit=%s", tostring(tracker.id), tostring(playerName), tostring(unitId)) HMGT.TrackerFrame:ApplyAnchor(frame) end frame:EnableMouse(false) prevPlaced = frame end if missingTargets > 0 then self.anchorLayoutSignatures[frameKey] = nil self.nextAnchorRetryAt[frameKey] = now + 1.0 else self.anchorLayoutSignatures[frameKey] = signature self.nextAnchorRetryAt[frameKey] = nil end return end local primary = frames[ordered[1]] HMGT.TrackerFrame:ApplyAnchor(primary) primary:EnableMouse(not tracker.locked) local gap = (tracker.barSpacing or 2) + 10 local growsUp = tracker.showBar == true and tracker.growDirection == "UP" for index = 2, #ordered do local prev = frames[ordered[index - 1]] local frame = frames[ordered[index]] 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.anchorLayoutSignatures[frameKey] = signature self.nextAnchorRetryAt[frameKey] = nil end function Manager:UpdatePerGroupMemberTracker(tracker) local frameKey = S.GetTrackerFrameKey(tracker.id) local byPlayer, order, unitByPlayer, shouldShow = self:BuildEntriesByPlayerForTracker(tracker) self.activeOrders[frameKey] = order self.unitByPlayer[frameKey] = unitByPlayer local active, shownOrder = {}, {} local shownByPlayer = {} local entryCount, shouldTick = 0, false for _, playerName in ipairs(order) do local frame = self:EnsurePlayerFrame(tracker, playerName) local entries = byPlayer[playerName] or {} if HMGT.FilterDisplayEntries then entries = HMGT:FilterDisplayEntries(tracker, entries) or entries end if HMGT.SortDisplayEntries then HMGT:SortDisplayEntries(entries) end if #entries > 0 then HMGT.TrackerFrame:UpdateFrame(frame, entries, true) frame:Show() active[playerName] = true shownOrder[#shownOrder + 1] = playerName shownByPlayer[playerName] = entries entryCount = entryCount + #entries for _, entry in ipairs(entries) do if S.EntryNeedsVisualTicker(entry) then shouldTick = true break end end else frame:Hide() end end for playerName, frame in pairs(self.perPlayerFrames[frameKey] or {}) do if not active[playerName] then frame:Hide() end end self.activeOrders[frameKey] = shownOrder self.unitByPlayer[frameKey] = unitByPlayer self._displaySignatures[frameKey] = S.BuildGroupDisplaySignature and S.BuildGroupDisplaySignature(shownOrder, shownByPlayer) or nil if shouldShow and #shownOrder > 0 then self:RefreshPerGroupAnchors(tracker, false) return true, entryCount, shouldTick end self:HidePlayerFrames(frameKey) self._displaySignatures[frameKey] = "0" return false, 0, false end