dev-v.2.1.0 #10

Merged
Torsten merged 2 commits from dev-v.2.1.0 into dev 2026-04-25 16:21:08 +00:00
12 changed files with 401 additions and 681 deletions
Showing only changes of commit 02e062d66b - Show all commits

View File

@@ -1,486 +0,0 @@
local ADDON_NAME = "HailMaryGuildTools"
local HMGT = _G[ADDON_NAME]
if not HMGT then return end
local L = HMGT.L or LibStub("AceLocale-3.0"):GetLocale(ADDON_NAME)
local AceGUI = LibStub("AceGUI-3.0", true)
local function GetOrderedDebugLevels()
return { "error", "info", "verbose" }
end
local function GetOrderedDebugScopes()
local values = HMGT:GetDebugScopeOptions() or {}
local names = { "ALL" }
for scope in pairs(values) do
if scope ~= "ALL" then
names[#names + 1] = scope
end
end
table.sort(names, function(a, b)
if a == "ALL" then return true end
if b == "ALL" then return false end
return tostring(values[a] or a) < tostring(values[b] or b)
end)
return names, values
end
local function SetFilterButtonText(buttonWidget, prefix, valueLabel)
if not buttonWidget then
return
end
buttonWidget:SetText(string.format("%s: %s", tostring(prefix or ""), tostring(valueLabel or "")))
end
local function AdvanceDebugLevel(step)
local levels = GetOrderedDebugLevels()
local current = HMGT:GetConfiguredDebugLevel()
local nextIndex = 1
for index, value in ipairs(levels) do
if value == current then
nextIndex = index + (step or 1)
break
end
end
if nextIndex < 1 then
nextIndex = #levels
elseif nextIndex > #levels then
nextIndex = 1
end
HMGT.db.profile.debugLevel = levels[nextIndex]
HMGT:RefreshDebugWindow()
end
local function AdvanceDebugScope(step)
local scopes, labels = GetOrderedDebugScopes()
local current = (HMGT.db and HMGT.db.profile and HMGT.db.profile.debugScope) or "ALL"
local nextIndex = 1
for index, value in ipairs(scopes) do
if value == current then
nextIndex = index + (step or 1)
break
end
end
if nextIndex < 1 then
nextIndex = #scopes
elseif nextIndex > #scopes then
nextIndex = 1
end
HMGT.db.profile.debugScope = scopes[nextIndex]
HMGT:RefreshDebugWindow()
end
function HMGT:SetDebugWindowMinimized(minimized)
local frame = self.debugWindow
if not frame then
return
end
minimized = minimized and true or false
self.debugWindowStatus = self.debugWindowStatus or {
width = 860,
height = 340,
}
self.debugWindowStatus.minimized = minimized
local collapsedHeight = 64
if minimized then
self.debugWindowStatus.restoreHeight = self.debugWindowStatus.height or frame:GetHeight() or 340
end
local targetHeight = minimized
and collapsedHeight
or (self.debugWindowStatus.restoreHeight or self.debugWindowStatus.height or 340)
if frame.aceWidget then
frame.aceWidget:EnableResize(not minimized)
frame.aceWidget:SetHeight(targetHeight)
else
frame:SetHeight(targetHeight)
end
if frame.minimizeButton then
frame.minimizeButton:SetText(minimized and "+" or "-")
end
if frame.clearButton then
local buttonFrame = frame.clearButton.frame or frame.clearButton
buttonFrame:SetShown(not minimized)
end
if frame.selectButton then
local buttonFrame = frame.selectButton.frame or frame.selectButton
buttonFrame:SetShown(not minimized)
end
if frame.levelFilter then
local filterFrame = frame.levelFilter.frame or frame.levelFilter
filterFrame:SetShown(not minimized)
end
if frame.scopeFilter then
local filterFrame = frame.scopeFilter.frame or frame.scopeFilter
filterFrame:SetShown(not minimized)
end
if frame.logWidget then
frame.logWidget.frame:SetShown(not minimized)
end
if frame.scrollBG then
frame.scrollBG:SetShown(not minimized)
end
if not minimized then
self:RefreshDebugWindow()
end
end
function HMGT:ToggleDebugWindowMinimized()
self:SetDebugWindowMinimized(not (self.debugWindowStatus and self.debugWindowStatus.minimized))
end
function HMGT:EnsureDebugWindow()
if self.debugWindow then
return self.debugWindow
end
local frameWidget
if AceGUI then
frameWidget = AceGUI:Create("Frame")
self.debugWindowStatus = self.debugWindowStatus or {
width = 860,
height = 340,
}
frameWidget:SetTitle(L["DEBUG_WINDOW_TITLE"] or "HMGT Debug Console")
frameWidget:SetStatusText(L["DEBUG_WINDOW_HINT"] or "Mouse wheel scrolls, Ctrl+A selects all, Ctrl+C copies selected text")
frameWidget:SetStatusTable(self.debugWindowStatus)
frameWidget:SetWidth(self.debugWindowStatus.width or 860)
frameWidget:SetHeight(self.debugWindowStatus.height or 340)
frameWidget:EnableResize(true)
frameWidget.frame:SetClampedToScreen(true)
frameWidget.frame:SetToplevel(true)
frameWidget.frame:SetFrameStrata("FULLSCREEN_DIALOG")
frameWidget:Hide()
end
local frame = frameWidget and frameWidget.frame or CreateFrame("Frame", "HMGT_DebugWindow", UIParent, "BackdropTemplate")
if not frameWidget then
frame:SetSize(860, 340)
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 0)
frame:SetFrameStrata("DIALOG")
frame:SetClampedToScreen(true)
frame:SetMovable(true)
frame:EnableMouse(true)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", function(selfFrame) selfFrame:StartMoving() end)
frame:SetScript("OnDragStop", function(selfFrame) selfFrame:StopMovingOrSizing() end)
frame:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
edgeSize = 12,
insets = { left = 3, right = 3, top = 3, bottom = 3 },
})
frame:SetBackdropColor(0.05, 0.05, 0.06, 0.95)
frame:SetBackdropBorderColor(0.35, 0.55, 0.85, 1)
frame:Hide()
local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
title:SetPoint("TOPLEFT", frame, "TOPLEFT", 14, -12)
title:SetText(L["DEBUG_WINDOW_TITLE"] or "HMGT Debug Console")
frame.title = title
local closeButton = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
closeButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -5, -5)
frame.closeButton = closeButton
local minimizeButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
minimizeButton:SetSize(22, 20)
minimizeButton:SetPoint("TOPRIGHT", closeButton, "TOPLEFT", -2, 0)
minimizeButton:SetText((self.debugWindowStatus and self.debugWindowStatus.minimized) and "+" or "-")
minimizeButton:SetScript("OnClick", function()
HMGT:ToggleDebugWindowMinimized()
end)
frame.minimizeButton = minimizeButton
end
frame.aceWidget = frameWidget
if frameWidget and AceGUI then
local content = frameWidget.content
local minimizeButton = AceGUI:Create("Button")
minimizeButton:SetText((self.debugWindowStatus and self.debugWindowStatus.minimized) and "+" or "-")
minimizeButton:SetWidth(24)
minimizeButton:SetCallback("OnClick", function()
HMGT:ToggleDebugWindowMinimized()
end)
minimizeButton.frame:SetParent(frame)
minimizeButton.frame:ClearAllPoints()
minimizeButton.frame:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -34, -4)
minimizeButton.frame:SetHeight(20)
minimizeButton.frame:Show()
frame.minimizeButton = minimizeButton
local clearButton = AceGUI:Create("Button")
clearButton:SetText(L["OPT_DEBUG_CLEAR"] or "Clear log")
clearButton:SetWidth(120)
clearButton:SetCallback("OnClick", function()
HMGT:ClearDebugLog()
end)
clearButton.frame:SetParent(content)
clearButton.frame:ClearAllPoints()
clearButton.frame:SetPoint("TOPRIGHT", content, "TOPRIGHT", 0, -2)
clearButton.frame:Show()
frame.clearButton = clearButton
local selectButton = AceGUI:Create("Button")
selectButton:SetText(L["OPT_DEBUG_SELECT_ALL"] or "Select all")
selectButton:SetWidth(120)
selectButton:SetCallback("OnClick", function()
if frame.editBox then
frame.editBox:SetFocus()
frame.editBox:HighlightText(0)
end
end)
selectButton.frame:SetParent(content)
selectButton.frame:ClearAllPoints()
selectButton.frame:SetPoint("TOPRIGHT", clearButton.frame, "TOPLEFT", -6, 0)
selectButton.frame:Show()
frame.selectButton = selectButton
local levelFilter = AceGUI:Create("Button")
levelFilter:SetWidth(150)
levelFilter:SetCallback("OnClick", function()
AdvanceDebugLevel(1)
end)
levelFilter.frame:SetParent(content)
levelFilter.frame:ClearAllPoints()
levelFilter.frame:SetPoint("TOPLEFT", content, "TOPLEFT", 0, 0)
levelFilter.frame:Show()
frame.levelFilter = levelFilter
local scopeFilter = AceGUI:Create("Button")
scopeFilter:SetWidth(180)
scopeFilter:SetCallback("OnClick", function()
AdvanceDebugScope(1)
end)
scopeFilter.frame:SetParent(content)
scopeFilter.frame:ClearAllPoints()
scopeFilter.frame:SetPoint("TOPLEFT", levelFilter.frame, "TOPRIGHT", 8, 0)
scopeFilter.frame:Show()
frame.scopeFilter = scopeFilter
local logWidget = AceGUI:Create("MultiLineEditBox")
logWidget:SetLabel("")
logWidget:DisableButton(true)
logWidget:SetNumLines(18)
logWidget:SetText("")
logWidget.frame:SetParent(content)
logWidget.frame:ClearAllPoints()
logWidget.frame:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -54)
logWidget.frame:SetPoint("BOTTOMRIGHT", content, "BOTTOMRIGHT", 0, 0)
logWidget.frame:Show()
logWidget:SetCallback("OnTextChanged", function()
HMGT:RefreshDebugWindow()
end)
logWidget.editBox:SetScript("OnKeyDown", function(selfBox, key)
if IsControlKeyDown() and (key == "A" or key == "a") then
selfBox:HighlightText(0)
end
end)
frame.logWidget = logWidget
frame.editBox = logWidget.editBox
frame.scrollFrame = logWidget.scrollFrame
self.debugWindow = frame
self:SetDebugWindowMinimized(self.debugWindowStatus and self.debugWindowStatus.minimized)
return frame
end
local clearButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
clearButton:SetSize(90, 22)
clearButton:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -30, -6)
clearButton:SetText(L["OPT_DEBUG_CLEAR"] or "Clear log")
clearButton:SetScript("OnClick", function()
HMGT:ClearDebugLog()
end)
frame.clearButton = clearButton
local selectButton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
selectButton:SetSize(90, 22)
selectButton:SetPoint("TOPRIGHT", clearButton, "TOPLEFT", -6, 0)
selectButton:SetText(L["OPT_DEBUG_SELECT_ALL"] or "Select all")
selectButton:SetScript("OnClick", function()
if frame.editBox then
frame.editBox:SetFocus()
frame.editBox:HighlightText(0)
end
end)
frame.selectButton = selectButton
local scopeFilter = CreateFrame("Frame", nil, frame)
scopeFilter:SetSize(170, 22)
scopeFilter:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -8)
frame.scopeFilter = scopeFilter
local scrollBG = CreateFrame("Frame", nil, frame, "BackdropTemplate")
scrollBG:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
edgeSize = 16,
insets = { left = 4, right = 3, top = 4, bottom = 3 },
})
scrollBG:SetBackdropColor(0, 0, 0, 0.95)
scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4, 1)
scrollBG:SetPoint("TOPLEFT", frame, "TOPLEFT", 14, -36)
scrollBG:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -30, 14)
frame.scrollBG = scrollBG
local scrollFrame = CreateFrame("ScrollFrame", nil, scrollBG, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 6, -6)
scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -27, 4)
scrollFrame:EnableMouseWheel(true)
scrollFrame:SetScript("OnMouseWheel", function(selfMsg, delta)
if delta > 0 then
selfMsg:SetVerticalScroll(math.max(0, selfMsg:GetVerticalScroll() - 42))
else
selfMsg:SetVerticalScroll(selfMsg:GetVerticalScroll() + 42)
end
end)
frame.scrollFrame = scrollFrame
local editBox = CreateFrame("EditBox", nil, scrollFrame)
editBox:SetMultiLine(true)
editBox:SetAutoFocus(false)
editBox:SetFontObject(ChatFontNormal)
editBox:SetWidth(780)
editBox:SetTextInsets(6, 6, 6, 6)
editBox:EnableMouse(true)
editBox:SetScript("OnEscapePressed", function(selfBox)
selfBox:ClearFocus()
end)
editBox:SetScript("OnKeyDown", function(selfBox, key)
if IsControlKeyDown() and (key == "A" or key == "a") then
selfBox:HighlightText(0)
end
end)
editBox:SetScript("OnTextChanged", function(selfBox, userInput)
if userInput then
HMGT:RefreshDebugWindow()
else
selfBox:SetCursorPosition(selfBox:GetNumLetters())
selfBox:SetHeight(math.max(scrollFrame:GetHeight(), HMGT:GetDebugWindowTextHeight(frame, selfBox:GetText()) + 16))
scrollFrame:UpdateScrollChildRect()
end
end)
editBox:SetScript("OnMouseUp", function(selfBox)
selfBox:SetFocus()
end)
scrollFrame:SetScrollChild(editBox)
frame.editBox = editBox
local measureText = frame:CreateFontString(nil, "ARTWORK", "ChatFontNormal")
measureText:SetJustifyH("LEFT")
measureText:SetJustifyV("TOP")
if measureText.SetSpacing then
measureText:SetSpacing(2)
end
measureText:SetWidth(768)
frame.measureText = measureText
self.debugWindow = frame
self:SetDebugWindowMinimized(self.debugWindowStatus and self.debugWindowStatus.minimized)
return frame
end
function HMGT:GetDebugWindowTextHeight(frame, text)
if not frame or not frame.measureText then
return 0
end
local width = 768
if frame.editBox then
width = math.max(1, (frame.editBox:GetWidth() or width) - 12)
end
frame.measureText:SetWidth(width)
frame.measureText:SetText(text or "")
return frame.measureText:GetStringHeight()
end
function HMGT:RefreshDebugWindow()
local frame = self:EnsureDebugWindow()
if not frame then
return
end
local filtered = self:GetFilteredDebugBuffer() or self.debugBuffer or {}
local text = table.concat(filtered, "\n")
if frame.logWidget and frame.editBox then
if frame.levelFilter then
local levelOptions = self:GetDebugLevelOptions()
SetFilterButtonText(frame.levelFilter, L["OPT_DEBUG_LEVEL"] or "Level", levelOptions[self:GetConfiguredDebugLevel()])
end
if frame.scopeFilter then
local scopeOptions = self:GetDebugScopeOptions()
local currentScope = (self.db and self.db.profile and self.db.profile.debugScope) or "ALL"
SetFilterButtonText(frame.scopeFilter, L["OPT_DEBUG_SCOPE"] or "Module", scopeOptions[currentScope] or currentScope)
end
frame.logWidget:SetText(text)
frame.editBox:SetCursorPosition(frame.editBox:GetNumLetters())
return
end
if not frame.editBox then
return
end
frame.editBox:SetText(text)
frame.editBox:SetCursorPosition(#text)
frame.editBox:SetHeight(math.max(frame.scrollFrame:GetHeight(), self:GetDebugWindowTextHeight(frame, text) + 16))
frame.scrollFrame:SetVerticalScroll(math.max(0, frame.editBox:GetHeight() - frame.scrollFrame:GetHeight()))
end
function HMGT:UpdateDebugWindowVisibility()
if self.db and self.db.profile then
self.db.profile.debug = false
end
local frame = self.debugWindow
if not frame then
return
end
local widget = frame.aceWidget
if widget then
widget:Hide()
else
frame:Hide()
end
end
function HMGT:ClearDebugLog()
wipe(self.debugBuffer)
if self.debugWindow and self.debugWindow.logWidget then
self.debugWindow.logWidget:SetText("")
self.debugWindow.editBox:SetCursorPosition(0)
self.debugWindow.scrollFrame:SetVerticalScroll(0)
return
end
if self.debugWindow and self.debugWindow.editBox then
self.debugWindow.editBox:SetText("")
self.debugWindow.scrollFrame:SetVerticalScroll(0)
end
end
function HMGT:ToggleDebugWindowShortcut()
if self.db and self.db.profile then
self.db.profile.debug = false
end
local frame = self.debugWindow
if not frame then
return
end
local widget = frame.aceWidget
if widget then
widget:Hide()
else
frame:Hide()
end
end
function HMGT:DumpDebugLog(maxLines)
return
end

View File

@@ -68,12 +68,14 @@ local function GetPlayerVersionText(name)
return tostring(HMGT.ADDON_VERSION or "dev"), tonumber(HMGT.PROTOCOL_VERSION) or 0, true
end
local version = HMGT.peerVersions and HMGT.peerVersions[normalized] or nil
local protocol = HMGT.GetPeerProtocolVersion and HMGT:GetPeerProtocolVersion(normalized) or 0
if version and version ~= "" then
return tostring(version), tonumber(protocol) or 0, true
local addonStatus = HMGT.GetPlayerAddonStatus and HMGT:GetPlayerAddonStatus(normalized) or nil
if addonStatus and addonStatus.mode == "hmgt" and addonStatus.version and addonStatus.version ~= "" then
return tostring(addonStatus.version), tonumber(addonStatus.protocol) or 0, true
end
return nil, tonumber(protocol) or 0, false
if addonStatus and addonStatus.mode == "bridge" then
return L["VERSION_WINDOW_BRIDGE_MODE"] or "Bridge Mode", 0, true
end
return nil, tonumber(addonStatus and addonStatus.protocol) or 0, false
end
local function ApplyClassIcon(texture, classTag)
@@ -167,7 +169,11 @@ function HMGT:RefreshVersionNoticeWindow()
local versionText, protocol, hasAddon = GetPlayerVersionText(info.name)
if hasAddon then
row.versionText:SetText(versionText or "?")
if versionText == (L["VERSION_WINDOW_BRIDGE_MODE"] or "Bridge Mode") then
row.versionText:SetTextColor(0.55, 0.82, 1, 1)
else
row.versionText:SetTextColor(0.9, 0.9, 0.9, 1)
end
row.protocolText:SetText(protocol > 0 and tostring(protocol) or "-")
row.protocolText:SetTextColor(0.75, 0.75, 0.75, 1)
else

View File

@@ -288,16 +288,20 @@ HMGT.powerTracking = {
}
HMGT.pendingSpellPowerCosts = {}
HMGT.demoModeData = {}
HMGT.peerVersions = {}
HMGT.versionWarnings = {}
HMGT.versionWhisperWarnings = {}
HMGT.playerStatus = {}
HMGT.debugBuffer = {}
HMGT.debugBufferMax = 500
HMGT.enabledDebugScopes = {
General = true,
Debug = true,
Comm = true,
TrackedSpells = true,
TrackerCore = true,
TrackerSync = true,
TrackerUI = true,
TrackerBridge = true,
TrackerState = true,
PowerSpend = true,
}
HMGT.pendingReliableMessages = HMGT.pendingReliableMessages or {}
@@ -311,7 +315,11 @@ local DEBUG_SCOPE_LABELS = {
General = "General",
Debug = "Debug",
Comm = "Communication",
TrackedSpells = "Tracked Spells",
TrackerCore = "Tracker Core",
TrackerSync = "Tracker Sync",
TrackerUI = "Tracker UI",
TrackerBridge = "Tracker Bridge",
TrackerState = "Tracker State",
PowerSpend = "Power Spend",
RaidTimeline = "Raid Timeline",
Notes = "Notes",
@@ -340,8 +348,12 @@ end
function HMGT:GetTrackerDebugScope(tracker)
local trackerName = nil
local trackerId = nil
local trackerType = nil
if type(tracker) == "table" then
trackerName = tracker.name
trackerId = tonumber(tracker.id)
trackerType = tracker.trackerType
if (not trackerName or trackerName == "") and tracker.id then
trackerName = string.format("Tracker %s", tostring(tracker.id))
end
@@ -353,7 +365,106 @@ function HMGT:GetTrackerDebugScope(tracker)
if trackerName == "" then
trackerName = "Tracker"
end
return "Tracker: " .. trackerName
local prefix = "Tracker"
if trackerType == "group" then
prefix = "Tracker Group"
elseif trackerType == "normal" then
prefix = "Tracker Normal"
end
if trackerId then
return string.format("%s #%d: %s", prefix, trackerId, trackerName)
end
return prefix .. ": " .. trackerName
end
function HMGT:GetPlayerStatus(playerName, create)
local normalizedName = self:NormalizePlayerName(playerName)
if not normalizedName or normalizedName == "" then
return nil
end
self.playerStatus = self.playerStatus or {}
if create then
self.playerStatus[normalizedName] = self.playerStatus[normalizedName] or {}
end
return self.playerStatus[normalizedName]
end
function HMGT:SetPlayerVersionStatus(playerName, version, protocol, sourceTag)
local status = self:GetPlayerStatus(playerName, true)
if not status then
return nil
end
if version and version ~= "" then
status.version = tostring(version)
end
if tonumber(protocol) then
status.protocol = tonumber(protocol)
end
if sourceTag and sourceTag ~= "" then
status.versionSource = tostring(sourceTag)
end
status.mode = "hmgt"
return status
end
function HMGT:SetPlayerBridgeStatus(playerName, sourceName)
local source = tostring(sourceName or "")
if source == "" then
return nil
end
local status = self:GetPlayerStatus(playerName, true)
if not status then
return nil
end
status.bridgeSource = source
if not status.version or status.version == "" then
status.mode = "bridge"
end
return status
end
function HMGT:GetPlayerAddonStatus(playerName)
local status = self:GetPlayerStatus(playerName, false)
if not status then
return {
mode = "missing",
version = nil,
protocol = 0,
bridgeSource = nil,
}
end
local version = status.version
local protocol = tonumber(status.protocol) or 0
local bridgeSource = status.bridgeSource
local mode = status.mode
if version and version ~= "" then
mode = "hmgt"
elseif bridgeSource and bridgeSource ~= "" then
mode = "bridge"
else
mode = "missing"
end
return {
mode = mode,
version = version,
protocol = protocol,
bridgeSource = bridgeSource,
}
end
function HMGT:ClearPlayerStatus(playerName)
local normalizedName = self:NormalizePlayerName(playerName)
if not normalizedName or not self.playerStatus then
return false
end
if self.playerStatus[normalizedName] then
self.playerStatus[normalizedName] = nil
return true
end
return false
end
function HMGT:GetStaticDebugScopeOptions()
@@ -456,6 +567,10 @@ function HMGT:IsReliableCommType(msgType)
end
function HMGT:GetPeerProtocolVersion(playerName)
local status = self:GetPlayerStatus(playerName, false)
if status and tonumber(status.protocol) then
return tonumber(status.protocol) or 0
end
local normalizedName = self:NormalizePlayerName(playerName)
local peerProtocols = self.peerProtocols or {}
return tonumber(normalizedName and peerProtocols[normalizedName]) or 0
@@ -469,6 +584,7 @@ function HMGT:RememberPeerProtocolVersion(playerName, protocol)
end
self.peerProtocols = self.peerProtocols or {}
self.peerProtocols[normalizedName] = numeric
self:SetPlayerVersionStatus(normalizedName, nil, numeric, nil)
end
local function ParseVersionTokens(version)
@@ -747,7 +863,32 @@ function HMGT:SendDirectMessage(payload, target, prio)
end
function HMGT:DebugScoped(level, scope, fmt, ...)
return
local normalizedLevel = tostring(level or "info"):lower()
if not DEBUG_LEVELS[normalizedLevel] then
normalizedLevel = "info"
end
local normalizedScope = tostring(scope or "General"):match("^%s*(.-)%s*$")
if normalizedScope == "" then
normalizedScope = "General"
end
local ok, message = pcall(string.format, tostring(fmt or ""), ...)
if not ok then
message = tostring(fmt or "")
end
local line = string.format("%s [%s][%s] %s", date("%H:%M:%S"), string.upper(normalizedLevel), normalizedScope, tostring(message or ""))
self.debugBuffer = self.debugBuffer or {}
self.debugBuffer[#self.debugBuffer + 1] = line
local maxLines = tonumber(self.debugBufferMax) or 500
while #self.debugBuffer > maxLines do
table.remove(self.debugBuffer, 1)
end
if self.debugWindow and self.debugWindow.IsShown and self.debugWindow:IsShown() and self.RefreshDebugWindow then
self:RefreshDebugWindow()
end
end
function HMGT:Debug(fmt, ...)
@@ -764,7 +905,7 @@ end
function HMGT:RegisterPeerVersion(playerName, version, protocol, sourceTag)
if not playerName then return end
self.peerVersions[playerName] = version
self:SetPlayerVersionStatus(playerName, version, protocol, sourceTag)
self:RememberPeerProtocolVersion(playerName, protocol)
if self.versionNoticeWindow and self.versionNoticeWindow.IsShown and self.versionNoticeWindow:IsShown() and self.RefreshVersionNoticeWindow then
self:RefreshVersionNoticeWindow()
@@ -798,7 +939,7 @@ function HMGT:RegisterPeerVersion(playerName, version, protocol, sourceTag)
tostring(playerName), table.concat(details, " | "))
self:Print("|cffff5555HMGT|r " .. text)
self:ShowVersionMismatchPopup(playerName, table.concat(details, " | "), sourceTag)
self:Debug("info", "Version mismatch %s via=%s %s", tostring(playerName), tostring(sourceTag or "?"), table.concat(details, " | "))
self:DebugScoped("info", "TrackerCore", "Version mismatch %s via=%s %s", tostring(playerName), tostring(sourceTag or "?"), table.concat(details, " | "))
end
end
@@ -1091,7 +1232,7 @@ function HMGT:LogTrackedSpellCast(playerName, spellEntry, details)
self:DebugScoped(
"verbose",
"TrackedSpells",
"TrackerCore",
"%s -> %s von %s, %s",
GetTrackedSpellCategoryLabel(spellEntry),
GetSpellDebugLabel(spellEntry.spellId),
@@ -1699,22 +1840,9 @@ function HMGT:MigrateProfileSettings()
if #p.trackers == 0 and p.trackerModelVersion ~= TRACKER_MODEL_VERSION then
p.trackers = {
self:CreateTrackerConfig(1, CopyTrackerFields({
name = L["IT_NAME"] or "Interrupts",
trackerType = "normal",
categories = { "interrupt" },
}, p.interruptTracker or {})),
self:CreateTrackerConfig(2, CopyTrackerFields({
name = L["RCD_NAME"] or "Raid Cooldowns",
trackerType = "normal",
categories = { "raid" },
}, p.raidCooldownTracker or {})),
self:CreateTrackerConfig(3, CopyTrackerFields({
name = L["GCD_NAME"] or "Cooldowns",
trackerType = "group",
categories = { "defensive", "offensive", "tank", "healing", "utility", "cc", "lust" },
showChargesOnIcon = true,
}, p.groupCooldownTracker or {})),
self:BuildTrackerConfigFromPreset("interruptTracker", 1, CopyTrackerFields({}, p.interruptTracker or {})),
self:BuildTrackerConfigFromPreset("raidCooldownTracker", 2, CopyTrackerFields({}, p.raidCooldownTracker or {})),
self:BuildTrackerConfigFromPreset("groupCooldownTracker", 3, CopyTrackerFields({}, p.groupCooldownTracker or {})),
}
end
@@ -1732,11 +1860,7 @@ function HMGT:MigrateProfileSettings()
end
end
if #normalizedTrackers == 0 then
normalizedTrackers[1] = self:CreateTrackerConfig(1, {
name = L["IT_NAME"] or "Interrupts",
trackerType = "normal",
categories = { "interrupt" },
})
normalizedTrackers[1] = self:BuildTrackerConfigFromPreset("interruptTracker", 1)
end
p.trackers = normalizedTrackers
p.trackerModelVersion = TRACKER_MODEL_VERSION
@@ -2648,7 +2772,7 @@ function HMGT:OnGroupRosterUpdate()
if not validPlayers[name] then
self.playerData[name] = nil
self:ClearTrackerStateForPlayer(name)
self.peerVersions[name] = nil
self:ClearPlayerStatus(name)
self.versionWarnings[name] = nil
if self.peerProtocols then
self.peerProtocols[name] = nil
@@ -3040,127 +3164,6 @@ function HMGT:TestMode()
self:Print(L["TEST_MODE_ACTIVE"])
end
function HMGT:GetDemoEntries(trackerKey, database, settings)
local pool = {}
local poolByClass = {}
for _, entry in ipairs(database) do
if settings.enabledSpells[entry.spellId] ~= false then
pool[#pool + 1] = entry
for _, cls in ipairs(entry.classes or {}) do
poolByClass[cls] = poolByClass[cls] or {}
poolByClass[cls][#poolByClass[cls] + 1] = entry
end
end
end
if #pool == 0 then return {} end
local classKeys = {}
for cls in pairs(poolByClass) do
classKeys[#classKeys + 1] = cls
end
if #classKeys == 0 then classKeys[1] = "WARRIOR" end
local count = settings.showBar and math.min(8, #pool) or math.min(12, #pool)
local names = { "Alice", "Bob", "Clara", "Duke", "Elli", "Fynn", "Gina", "Hektor", "Ivo", "Jana", "Kira", "Lio" }
local spellIds = {}
for _, e in ipairs(pool) do spellIds[#spellIds + 1] = tostring(e.spellId) end
table.sort(spellIds)
local signature = table.concat(spellIds, ",") .. "|" .. tostring(settings.showBar and 1 or 0) .. "|" .. tostring(count)
local now = GetTime()
local cache = self.demoModeData[trackerKey]
if (not cache) or (cache.signature ~= signature) or (not cache.entries) or (#cache.entries ~= count) then
local entries = {}
for i = 1, count do
local cls = classKeys[math.random(1, #classKeys)]
local classPool = poolByClass[cls]
local spell = (classPool and classPool[math.random(1, #classPool)]) or pool[math.random(1, #pool)]
local duration = math.max(
1,
tonumber(HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(spell)) or tonumber(spell.cooldown) or 60
)
local playerName = names[((i - 1) % #names) + 1]
-- start offset so demo entries do not all tick in sync
local offset = math.random() * math.min(duration * 0.85, duration - 0.1)
entries[#entries + 1] = {
playerName = playerName,
class = cls or ((spell.classes and spell.classes[1]) or "WARRIOR"),
spellEntry = spell,
total = duration,
cycleStart = now - offset,
currentCharges = nil,
maxCharges = nil,
}
end
cache = { signature = signature, entries = entries }
self.demoModeData[trackerKey] = cache
end
local out = {}
for _, e in ipairs(cache.entries) do
local total = math.max(1, tonumber(e.total) or 1)
local elapsed = math.max(0, now - (e.cycleStart or now))
local phase = math.fmod(elapsed, total)
local rem = total - phase
-- show zero briefly at cycle boundary, then restart immediately
if elapsed > 0 and phase < 0.05 then rem = 0 end
out[#out + 1] = {
playerName = e.playerName,
class = e.class,
spellEntry = e.spellEntry,
remaining = rem,
total = total,
currentCharges = e.currentCharges,
maxCharges = e.maxCharges,
}
end
return out
end
function HMGT:GetOwnTestEntries(database, settings, cooldownInfoOpts)
local entries = {}
local enabledSpells = settings and settings.enabledSpells or {}
local playerName = self:NormalizePlayerName(UnitName("player")) or "Player"
local classToken = select(2, UnitClass("player"))
if not classToken then
return entries, playerName
end
local specIdx = GetSpecialization()
local lookupSpec = (specIdx and specIdx > 0) and specIdx or 0
local talents = (self.playerData[playerName] and self.playerData[playerName].talents) or {}
local spells = HMGT_SpellData.GetSpellsForSpec(classToken, lookupSpec, database or {})
for _, spellEntry in ipairs(spells) do
if enabledSpells[spellEntry.spellId] ~= false then
local remaining, total, curCharges, maxCharges = self:GetCooldownInfo(playerName, spellEntry.spellId, cooldownInfoOpts)
local effectiveCd = HMGT_SpellData.GetEffectiveCooldown(spellEntry, talents)
local isAvailabilitySpell = self:IsAvailabilitySpell(spellEntry)
local spellKnown = self:IsTrackedSpellKnownForPlayer(playerName, 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
local hasAvailabilityState = isAvailabilitySpell and self:HasAvailabilityState(playerName, spellEntry.spellId)
if spellKnown or hasActiveCd or hasAvailabilityState then
entries[#entries + 1] = {
playerName = playerName,
class = classToken,
spellEntry = spellEntry,
remaining = remaining,
total = total > 0 and total or effectiveCd,
currentCharges = curCharges,
maxCharges = maxCharges,
}
end
end
end
return entries, playerName
end
-- ═══════════════════════════════════════════════════════════════
-- HILFSFUNKTIONEN
-- ═══════════════════════════════════════════════════════════════

View File

@@ -44,7 +44,7 @@ Modules\Tracker\RaidCooldownTracker\RaidCooldownTracker.lua
Modules\Tracker\GroupCooldownTracker\GroupCooldownTracker.lua
Modules\Tracker\InterruptTracker\InterruptSpellDatabase.lua
Modules\Tracker\RaidcooldownTracker\RaidCooldownSpellDatabase.lua
Modules\Tracker\RaidCooldownTracker\RaidCooldownSpellDatabase.lua
Modules\Tracker\GroupCooldownTracker\GroupCooldownSpellDatabase.lua
Modules\Tracker\TrackerManager.lua
Modules\Tracker\NormalTrackerFrames.lua
@@ -65,5 +65,4 @@ Modules\MapOverlay\MapOverlay.xml
Modules\RaidTimeline\RaidTimelineBossAbilityData.lua
Modules\RaidTimeline\RaidTimeline.lua
Modules\RaidTimeline\RaidTimelineBigWigs.lua
Modules\RaidTimeline\RaidTimelineDBM.lua
Modules\RaidTimeline\RaidTimelineOptions.lua

View File

@@ -22,11 +22,12 @@ L["VERSION_WINDOW_MESSAGE"] = "Hail Mary Guild Tools Versionen in deiner aktuell
L["VERSION_WINDOW_DETAIL"] = "Erkannt ueber %s von %s.\n%s"
L["VERSION_WINDOW_NO_MISMATCH"] = "In deiner aktuellen Gruppe wurde keine neuere HMGT-Version erkannt."
L["VERSION_WINDOW_CURRENT"] = "Aktuelle Version: %s | Protokoll: %s"
L["VERSION_WINDOW_STATUS"] = "HMGT bei %d/%d Spielern erkannt"
L["VERSION_WINDOW_STATUS"] = "Addon oder Bridge bei %d/%d Spielern erkannt"
L["VERSION_WINDOW_REFRESH"] = "Aktualisieren"
L["VERSION_WINDOW_COLUMN_PLAYER"] = "Spieler"
L["VERSION_WINDOW_COLUMN_VERSION"] = "Version"
L["VERSION_WINDOW_COLUMN_PROTOCOL"] = "Protokoll"
L["VERSION_WINDOW_BRIDGE_MODE"] = "Bridge Mode"
L["VERSION_WINDOW_MISSING_ADDON"] = "Addon nicht vorhanden"
L["VERSION_WINDOW_LEADER_TAG"] = "(Leiter)"
L["VERSION_WINDOW_ASSISTANT_TAG"] = "(Assist)"

View File

@@ -22,11 +22,12 @@ L["VERSION_WINDOW_MESSAGE"] = "Hail Mary Guild Tools versions in your current gr
L["VERSION_WINDOW_DETAIL"] = "Detected via %s from %s.\n%s"
L["VERSION_WINDOW_NO_MISMATCH"] = "No newer HMGT version has been detected in your current group."
L["VERSION_WINDOW_CURRENT"] = "Current version: %s | Protocol: %s"
L["VERSION_WINDOW_STATUS"] = "Detected HMGT on %d/%d players"
L["VERSION_WINDOW_STATUS"] = "Detected addon or bridge on %d/%d players"
L["VERSION_WINDOW_REFRESH"] = "Refresh"
L["VERSION_WINDOW_COLUMN_PLAYER"] = "Player"
L["VERSION_WINDOW_COLUMN_VERSION"] = "Version"
L["VERSION_WINDOW_COLUMN_PROTOCOL"] = "Protocol"
L["VERSION_WINDOW_BRIDGE_MODE"] = "Bridge Mode"
L["VERSION_WINDOW_MISSING_ADDON"] = "Addon not installed"
L["VERSION_WINDOW_LEADER_TAG"] = "(Leader)"
L["VERSION_WINDOW_ASSISTANT_TAG"] = "(Assist)"

View File

@@ -1,8 +0,0 @@
local ADDON_NAME = "HailMaryGuildTools"
local HMGT = LibStub("AceAddon-3.0"):GetAddon(ADDON_NAME)
if not HMGT then return end
local RT = HMGT.RaidTimeline
if not RT then return end
-- Placeholder for later DBM-specific raid timeline integration.

View File

@@ -80,6 +80,8 @@ function HMGT:ApplyExternalKnownSpell(sourceName, playerName, spellId, class, co
knownSpells = knownSpells,
externalSource = source,
}
self:SetPlayerBridgeStatus(normalizedName, source)
self:DebugScoped("verbose", "TrackerBridge", "Bridge known spell source=%s player=%s spellId=%s", tostring(source), tostring(normalizedName), tostring(sid))
if tonumber(cooldown) and tonumber(cooldown) > 0 then
spellEntry._hmgtExternalBaseCd = tonumber(cooldown)
@@ -128,6 +130,8 @@ function HMGT:ApplyExternalSpecInfo(sourceName, playerName, class, specId, talen
knownSpells = knownSpells,
externalSource = source,
}
self:SetPlayerBridgeStatus(normalizedName, source)
self:DebugScoped("info", "TrackerBridge", "Bridge spec sync source=%s player=%s class=%s spec=%s", tostring(source), tostring(normalizedName), tostring(classToken), tostring(spec))
self:PruneAvailabilityStates(normalizedName, knownSpells)
self:TriggerTrackerUpdate("trackers")
@@ -154,6 +158,7 @@ function HMGT:ApplyExternalCooldown(sourceName, playerName, spellId, cooldown)
self:RegisterExternalAddonSource(source)
self:ApplyExternalKnownSpell(source, normalizedName, sid, nil, cd)
self:DebugScoped("info", "TrackerBridge", "Bridge cooldown source=%s player=%s spellId=%s cooldown=%.1f", tostring(source), tostring(normalizedName), tostring(sid), cd)
self:HandleRemoteSpellCast(normalizedName, sid, GetServerTime(), nil, nil, nil, cd)
return true
end

View File

@@ -4,6 +4,91 @@ if not HMGT then return end
HMGT.TrackerCore = HMGT.TrackerCore or {}
HMGT.TRACKER_PRESET_DEFINITIONS = HMGT.TRACKER_PRESET_DEFINITIONS or {
interruptTracker = {
moduleName = "InterruptTracker",
dbKey = "interruptTracker",
trackerType = "normal",
trackerKey = "interruptTracker",
categories = { "interrupt" },
defaultName = function(L)
return (L and L["IT_NAME"]) or "Interrupts"
end,
},
raidCooldownTracker = {
moduleName = "RaidCooldownTracker",
dbKey = "raidCooldownTracker",
trackerType = "normal",
trackerKey = "raidCooldownTracker",
categories = { "lust", "defensive", "healing", "tank", "utility", "offensive", "cc", "interrupt" },
defaultName = function(L)
return (L and L["RCD_NAME"]) or "Raid Cooldowns"
end,
},
groupCooldownTracker = {
moduleName = "GroupCooldownTracker",
dbKey = "groupCooldownTracker",
trackerType = "group",
trackerKey = "groupCooldownTracker",
categories = { "tank", "defensive", "healing", "cc", "utility", "offensive", "lust", "interrupt" },
includeSelfFrame = false,
showChargesOnIcon = true,
defaultName = function(L)
return (L and L["GCD_NAME"]) or "Cooldowns"
end,
},
}
function HMGT:GetTrackerPresetDefinitions()
return self.TRACKER_PRESET_DEFINITIONS or {}
end
function HMGT:GetTrackerPresetDefinition(key)
local definitions = self:GetTrackerPresetDefinitions()
return definitions and definitions[tostring(key or "")]
end
function HMGT:GetTrackerPresetDefinitionByModule(moduleName)
local target = tostring(moduleName or "")
for _, definition in pairs(self:GetTrackerPresetDefinitions()) do
if tostring(definition.moduleName or "") == target then
return definition
end
end
return nil
end
function HMGT:GetTrackerTypeOptions()
local L = self.L
return {
normal = (L and L["OPT_TRACKER_TYPE_NORMAL"]) or "Normal tracker",
group = (L and L["OPT_TRACKER_TYPE_GROUP"]) or "Group-based tracker",
}
end
function HMGT:BuildTrackerConfigFromPreset(presetKey, trackerId, overrides)
local definition = self:GetTrackerPresetDefinition(presetKey)
local config = overrides or {}
if not definition then
return self:CreateTrackerConfig(trackerId, config)
end
local base = {
name = type(definition.defaultName) == "function" and definition.defaultName(self.L) or tostring(definition.defaultName or ""),
trackerType = definition.trackerType,
trackerKey = definition.trackerKey,
categories = definition.categories,
includeSelfFrame = definition.includeSelfFrame,
showChargesOnIcon = definition.showChargesOnIcon,
}
for key, value in pairs(config) do
base[key] = value
end
return self:CreateTrackerConfig(trackerId, base)
end
local function EntryNeedsVisualTicker(entry)
if type(entry) ~= "table" then
return false
@@ -204,6 +289,93 @@ function HMGT:CollectTrackerEntries(tracker)
return entries
end
function HMGT:GetDemoEntries(trackerKey, database, settings)
local pool = {}
local poolByClass = {}
for _, entry in ipairs(database or {}) do
if settings.enabledSpells[entry.spellId] ~= false then
pool[#pool + 1] = entry
for _, classToken in ipairs(entry.classes or {}) do
poolByClass[classToken] = poolByClass[classToken] or {}
poolByClass[classToken][#poolByClass[classToken] + 1] = entry
end
end
end
if #pool == 0 then
return {}
end
local classKeys = {}
for classToken in pairs(poolByClass) do
classKeys[#classKeys + 1] = classToken
end
if #classKeys == 0 then
classKeys[1] = "WARRIOR"
end
local count = settings.showBar and math.min(8, #pool) or math.min(12, #pool)
local names = { "Alice", "Bob", "Clara", "Duke", "Elli", "Fynn", "Gina", "Hektor", "Ivo", "Jana", "Kira", "Lio" }
local spellIds = {}
for _, entry in ipairs(pool) do
spellIds[#spellIds + 1] = tostring(entry.spellId)
end
table.sort(spellIds)
local signature = table.concat(spellIds, ",") .. "|" .. tostring(settings.showBar and 1 or 0) .. "|" .. tostring(count)
local now = GetTime()
local cache = self.demoModeData[trackerKey]
if (not cache) or cache.signature ~= signature or (not cache.entries) or #cache.entries ~= count then
local cachedEntries = {}
for index = 1, count do
local classToken = classKeys[math.random(1, #classKeys)]
local classPool = poolByClass[classToken]
local spellEntry = (classPool and classPool[math.random(1, #classPool)]) or pool[math.random(1, #pool)]
local duration = math.max(
1,
tonumber(HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(spellEntry)) or tonumber(spellEntry.cooldown) or 60
)
local offset = math.random() * math.min(duration * 0.85, duration - 0.1)
cachedEntries[#cachedEntries + 1] = {
playerName = names[((index - 1) % #names) + 1],
class = classToken or ((spellEntry.classes and spellEntry.classes[1]) or "WARRIOR"),
spellEntry = spellEntry,
total = duration,
cycleStart = now - offset,
currentCharges = nil,
maxCharges = nil,
}
end
cache = {
signature = signature,
entries = cachedEntries,
}
self.demoModeData[trackerKey] = cache
end
local entries = {}
for _, entry in ipairs(cache.entries) do
local total = math.max(1, tonumber(entry.total) or 1)
local elapsed = math.max(0, now - (entry.cycleStart or now))
local phase = math.fmod(elapsed, total)
local remaining = total - phase
if elapsed > 0 and phase < 0.05 then
remaining = 0
end
entries[#entries + 1] = {
playerName = entry.playerName,
class = entry.class,
spellEntry = entry.spellEntry,
remaining = remaining,
total = total,
currentCharges = entry.currentCharges,
maxCharges = entry.maxCharges,
}
end
return entries
end
function HMGT:CollectTrackerTestEntries(tracker)
local playerName = self:NormalizePlayerName(UnitName("player")) or "Player"
local classToken = select(2, UnitClass("player"))
@@ -385,7 +557,7 @@ function HMGT:TriggerTrackerUpdate(reason)
if t0 and t1 then
local mod = HMGT[name]
local count = mod and mod.lastEntryCount or 0
self:Debug("verbose", "UIUpdate %s took %.2fms entries=%s", tostring(name), t1 - t0, tostring(count))
self:DebugScoped("verbose", "TrackerUI", "UIUpdate %s took %.2fms entries=%s", tostring(name), t1 - t0, tostring(count))
end
end

View File

@@ -247,7 +247,7 @@ function HMGT:RefreshOwnCooldownStateFromGame(spellId)
if isLikelyGlobalCooldown or isSuspiciousShortRefresh then
self:DebugScoped(
"verbose",
"TrackedSpells",
"TrackerState",
"Ignore suspicious refresh for %s: spellCD=%.3f gcd=%.3f existing=%.3f remaining=%.3f effective=%.3f",
GetSpellDebugLabel and GetSpellDebugLabel(sid) or tostring(sid),
cooldownDuration,

View File

@@ -142,13 +142,26 @@ local function IsPartyAttachMode(tracker)
end
local function IsGroupTracker(tracker)
return type(tracker) == "table" and tracker.trackerType == "group"
return HMGT.IsGroupTrackerConfig and HMGT:IsGroupTrackerConfig(tracker) or (type(tracker) == "table" and tracker.trackerType == "group")
end
local TRACKER_TYPE_VALUES = {
local function GetTrackerTypeValues()
return HMGT.GetTrackerTypeOptions and HMGT:GetTrackerTypeOptions() or {
normal = L["OPT_TRACKER_TYPE_NORMAL"] or "Normal tracker",
group = L["OPT_TRACKER_TYPE_GROUP"] or "Group-based tracker",
}
}
end
local function GetPresetLabel(presetKey)
local definition = HMGT.GetTrackerPresetDefinition and HMGT:GetTrackerPresetDefinition(presetKey) or nil
if not definition then
return tostring(presetKey or (L["OPT_TRACKER"] or "Tracker"))
end
if type(definition.defaultName) == "function" then
return tostring(definition.defaultName(L))
end
return tostring(definition.defaultName or definition.moduleName or presetKey)
end
local function GetTrackerVisibilitySummary(tracker)
local parts = {}
@@ -180,7 +193,7 @@ local function GetTrackerSummaryText(tracker)
local display = tracker.showBar and (L["OPT_DISPLAY_BAR"] or "Progress bars") or (L["OPT_DISPLAY_ICON"] or "Icons")
return table.concat({
string.format("|cffffd100%s|r: %s", L["OPT_TRACKER_TYPE"] or "Tracker type", TRACKER_TYPE_VALUES[tracker.trackerType or "normal"] or (L["OPT_TRACKER_TYPE_NORMAL"] or "Normal tracker")),
string.format("|cffffd100%s|r: %s", L["OPT_TRACKER_TYPE"] or "Tracker type", GetTrackerTypeValues()[tracker.trackerType or "normal"] or (L["OPT_TRACKER_TYPE_NORMAL"] or "Normal tracker")),
string.format("|cffffd100%s|r: %s", L["OPT_TRACKER_CATEGORIES"] or "Categories", GetTrackerCategoriesSummary(tracker)),
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_MODE"] or "Mode", modeLabel),
string.format("|cffffd100%s|r: %s", L["OPT_STATUS_DISPLAY"] or "Display", display),
@@ -814,7 +827,7 @@ local function BuildGlobalSpellBrowserArgs()
end
local function BuildTrackerOverviewArgs()
return {
local args = {
description = {
type = "description",
order = 1,
@@ -833,22 +846,36 @@ local function BuildTrackerOverviewArgs()
return string.format("%s\n\n%s (%d): %s", body, L["OPT_TRACKERS"] or "Tracker Bars", #trackers, table.concat(names, ", "))
end,
},
addTracker = {
}
local definitions = HMGT.GetTrackerPresetDefinitions and HMGT:GetTrackerPresetDefinitions() or {}
local presetKeys = {}
for presetKey in pairs(definitions) do
presetKeys[#presetKeys + 1] = presetKey
end
table.sort(presetKeys, function(a, b)
return GetPresetLabel(a) < GetPresetLabel(b)
end)
for index, presetKey in ipairs(presetKeys) do
args["addPreset_" .. presetKey] = {
type = "execute",
order = 2,
order = 2 + index,
width = "full",
name = L["OPT_ADD_TRACKER"] or "Add tracker",
name = function()
return string.format("%s: %s", L["OPT_ADD_TRACKER"] or "Add tracker", GetPresetLabel(presetKey))
end,
func = function()
local nextId = HMGT:GetNextTrackerId()
local tracker = HMGT:CreateTrackerConfig(nextId, {
name = string.format("%s %d", L["OPT_TRACKER"] or "Tracker", nextId),
})
local tracker = HMGT:BuildTrackerConfigFromPreset(presetKey, nextId)
HMGT.db.profile.trackers = HMGT.db.profile.trackers or {}
HMGT.db.profile.trackers[#HMGT.db.profile.trackers + 1] = tracker
TriggerTrackerUpdate(true)
end,
},
}
end
return args
end
local function BuildTrackerGroup(trackerId, order)
@@ -1008,7 +1035,7 @@ local function BuildTrackerGroup(trackerId, order)
width = "full",
name = L["OPT_TRACKER_TYPE"] or "Tracker type",
desc = L["OPT_TRACKER_TYPE_DESC"] or "Choose whether this tracker uses one shared frame or separate frames per group member.",
values = TRACKER_TYPE_VALUES,
values = GetTrackerTypeValues,
get = function()
local tracker = s()
return (tracker and tracker.trackerType) or "normal"

View File

@@ -203,7 +203,7 @@ function HMGT:SendSpellStateSnapshot(snapshot, target, revision)
self:DebugScoped(
"verbose",
"TrackedSpells",
"TrackerSync",
"SendSpellStateSnapshot target=%s spell=%s kind=%s rev=%d a=%.3f b=%.3f c=%.3f d=%.3f",
tostring(target and target ~= "" and target or "GROUP"),
GetSpellDebugLabel and GetSpellDebugLabel(sid) or tostring(sid),
@@ -307,7 +307,7 @@ function HMGT:BroadcastRepairSpellStates()
if not self:IsEnabled() then return end
local sent = self:SendOwnTrackedSpellStates()
if sent > 0 then
self:DebugScoped("verbose", "TrackedSpells", "RepairSpellStates sent=%d", sent)
self:DebugScoped("verbose", "TrackerSync", "RepairSpellStates sent=%d", sent)
end
end
@@ -407,7 +407,7 @@ function HMGT:BroadcastSpellCast(spellId, snapshot)
chargeRemaining = math.max(0, tonumber(remaining) or 0)
chargeDuration = math.max(0, tonumber(total) or 0)
end
self:DebugScoped("verbose", "TrackedSpells", "BroadcastSpellCast spell=%s serverTime=%s charges=%d/%d",
self:DebugScoped("verbose", "TrackerSync", "BroadcastSpellCast spell=%s serverTime=%s charges=%d/%d",
GetSpellDebugLabel and GetSpellDebugLabel(spellId) or tostring(spellId),
tostring(GetServerTime()),
cur,
@@ -563,7 +563,7 @@ function HMGT:StoreRemotePlayerInfo(playerName, class, specIndex, talentHash, kn
end
self:DebugScoped(
"info",
"TrackedSpells",
"TrackerSync",
"Spielerinfo von %s: class=%s spec=%s bekannteSpells=%d",
tostring(playerName),
tostring(class),
@@ -753,7 +753,7 @@ function HMGT:ApplyRemoteSpellState(playerName, spellId, kind, revision, a, b, c
if changed then
self:DebugScoped(
"info",
"TrackedSpells",
"TrackerSync",
"Sync von %s: %s -> %s (rev=%d)",
tostring(normalizedName),
GetSpellDebugLabel and GetSpellDebugLabel(sid) or tostring(sid),
@@ -814,7 +814,7 @@ function HMGT:OnCommReceived(prefix, message, distribution, sender)
if (tonumber(protocol) or 0) >= 5 then
return
end
self:DebugScoped("verbose", "TrackedSpells", "Legacy cast von %s: %s ts=%s",
self:DebugScoped("verbose", "TrackerSync", "Legacy cast von %s: %s ts=%s",
tostring(senderName),
GetSpellDebugLabel and GetSpellDebugLabel(spellId) or tostring(spellId),
tostring(timestamp))
@@ -892,7 +892,7 @@ function HMGT:OnCommReceived(prefix, message, distribution, sender)
self:RememberPeerProtocolVersion(senderName, protocol)
self:ClearRemoteSpellStateRevisions(senderName)
self:StoreRemotePlayerInfo(senderName, class, specIndex, talentHash, knownSpellList)
self:DebugScoped("info", "TrackedSpells", "Hello von %s: class=%s spec=%s spells=%s",
self:DebugScoped("info", "TrackerSync", "Hello von %s: class=%s spec=%s spells=%s",
tostring(senderName), tostring(class), tostring(specIndex), tostring(knownSpellList or ""))
self:SendSyncResponse(sender)
self:TriggerTrackerUpdate()
@@ -1003,7 +1003,7 @@ function HMGT:OnCommReceived(prefix, message, distribution, sender)
end
end
end
self:DebugScoped("info", "TrackedSpells", "SyncResponse von %s: cdsApplied=%d", tostring(senderName), applied)
self:DebugScoped("info", "TrackerSync", "SyncResponse von %s: cdsApplied=%d", tostring(senderName), applied)
end
self:TriggerTrackerUpdate()
end