From 05d74cc4327e9380e67572da9c12f1a5b18774fd Mon Sep 17 00:00:00 2001 From: Xurkon Date: Mon, 1 Jun 2026 18:23:55 -0500 Subject: [PATCH] feat(slash): add /radiusdebug and /terraindebug diagnostics - Add /radiusdebug slash command. Dumps zoom, shape, size, scale, Minimap:GetViewRadius() (3.3.5a), C_Minimap.GetViewRadius() (Retail/WoTLK), CVars, fallback radius (outdoor/indoor), active pin count, and 8 sample pin metrics (screen offset, normalized radius, edge error). Includes an interpretation block. - Add /terraindebug slash command. Dumps Minimap, MinimapCluster, zoom buttons, border, backdrop, north tag, compass texture, mask state, MMHolder, and 5 levels of parent chain. Detects ElvUI presence and prints interpretation hints. - All API calls are defensive with pcall/conditional guards and tostring() fallbacks. Slash commands lazy-load _G.Questie*Debug so they work even if the function table is updated post-load. These commands were used during the minimap drift investigation to verify that radius, scale, and frame hierarchy were not the drift source. They are invaluable for future regression investigation and have zero runtime cost when not invoked. --- Modules/QuestieSlash.lua | 212 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 212 insertions(+) diff --git a/Modules/QuestieSlash.lua b/Modules/QuestieSlash.lua index 35b84a2..09bf55d 100644 --- a/Modules/QuestieSlash.lua +++ b/Modules/QuestieSlash.lua @@ -22,10 +22,222 @@ local l10n = QuestieLoader:ImportModule("l10n") ---@type QuestieCombatQueue local QuestieCombatQueue = QuestieLoader:ImportModule("QuestieCombatQueue") +local function _fmt(v) + if v == nil then return "nil" end + if type(v) == "number" then return string.format("%.4f", v) end + return tostring(v) +end + +local function _dumpFrame(name, frame) + if not frame then + print(string.format("%s: nil", name)) + return + end + + local p1, r1, p2, x, y = frame:GetPoint() + print(string.format( + "%s: %s size=%.1fx%.1f scale=%.4f eff=%.4f visible=%s strata=%s level=%s point=(%s,%s,%s,%.1f,%.1f)", + name, + frame:GetName() or tostring(frame), + frame:GetWidth() or 0, + frame:GetHeight() or 0, + frame.GetScale and frame:GetScale() or 0, + frame.GetEffectiveScale and frame:GetEffectiveScale() or 0, + tostring(frame:IsShown()), + tostring(frame.GetFrameStrata and frame:GetFrameStrata() or "?"), + tostring(frame.GetFrameLevel and frame:GetFrameLevel() or "?"), + tostring(p1), tostring(r1 and r1.GetName and r1:GetName() or r1), tostring(p2), x or 0, y or 0 + )) +end + +local function _dumpMaskState(frame) + if not frame then + return + end + + local mask = nil + if frame.GetMaskTexture then + pcall(function() + mask = frame:GetMaskTexture() + end) + end + + local tex = nil + if frame.GetTexture then + pcall(function() + tex = frame:GetTexture() + end) + end + + local shape = GetMinimapShape and GetMinimapShape() or "UNKNOWN" + local isSquare = tostring(shape) == "SQUARE" + print(string.format( + "Minimap mask/texture: mask=%s texture=%s shape=%s square=%s", + tostring(mask), + tostring(tex), + tostring(shape), + tostring(isSquare) + )) +end + +local function QuestieTerrainDebug() + print("=== MINIMAP TERRAIN DEBUG ===") + _dumpFrame("Minimap", Minimap) + _dumpFrame("MinimapCluster", MinimapCluster) + _dumpFrame("MinimapZoomIn", MinimapZoomIn) + _dumpFrame("MinimapZoomOut", MinimapZoomOut) + _dumpFrame("MinimapBorderTop", MinimapBorderTop) + _dumpFrame("MinimapBackdrop", MinimapBackdrop) + _dumpFrame("MinimapNorthTag", MinimapNorthTag) + _dumpFrame("MinimapCompassTexture", MinimapCompassTexture) + _dumpMaskState(Minimap) + + local mmHolder = _G.MMHolder + _dumpFrame("MMHolder", mmHolder) + + local parent = Minimap and Minimap:GetParent() + local depth = 0 + while parent and depth < 5 do + _dumpFrame(string.format("Parent[%d]", depth + 1), parent) + parent = parent:GetParent() + depth = depth + 1 + end + + local elvUIEnabled = _G.ElvUI ~= nil or _G.ElvDB ~= nil + print("") + print("Interpretation:") + print(string.format("- ElvUI detected: %s", tostring(elvUIEnabled))) + print("- If Minimap uses MMHolder plus a square mask/shape, ElvUI's minimap module is active.") + print("- If anchors are stock but the terrain still misaligns, the likely bug is ElvUI's mask/texture path, not Questie pin math.") +end + +local function QuestieRadiusDebug() + local m = Minimap + if not m then + print("=== MINIMAP RADIUS DEBUG ===") + print("Minimap frame not available") + return + end + + local function fmt(v) + if v == nil then return "nil" end + if type(v) == "number" then return string.format("%.4f", v) end + return tostring(v) + end + + local zoom = m:GetZoom() or -1 + local width = m:GetWidth() or 0 + local height = m:GetHeight() or 0 + local scale = m:GetScale() or 0 + local effScale = m.GetEffectiveScale and m:GetEffectiveScale() or 0 + local shape = GetMinimapShape and GetMinimapShape() or "UNKNOWN" + + print("=== MINIMAP RADIUS DEBUG ===") + print(string.format("Zoom level: %d", zoom)) + print(string.format("Shape: %s", tostring(shape))) + print(string.format("Minimap size: %.1f x %.1f", width, height)) + print(string.format("Minimap scale: %.4f (effective %.4f)", scale, effScale)) + print(string.format("Visible: %s", tostring(m:IsVisible()))) + + local function getFallbackRadius(isOutdoor) + local outdoor = {[0]=466 + 2/3, [1]=400, [2]=333 + 1/3, [3]=266 + 2/3, [4]=200, [5]=133 + 1/3} + local indoor = {[0]=300, [1]=240, [2]=180, [3]=120, [4]=80, [5]=50} + local tableRef = isOutdoor and outdoor or indoor + return (tableRef[zoom] or 0) / 2 + end + + if m.GetViewRadius then + print(string.format("Minimap:GetViewRadius() = %s", fmt(m:GetViewRadius()))) + else + print("Minimap:GetViewRadius() = NOT AVAILABLE") + end + + if C_Minimap and C_Minimap.GetViewRadius then + print(string.format("C_Minimap.GetViewRadius() = %s", fmt(C_Minimap.GetViewRadius()))) + else + print("C_Minimap.GetViewRadius() = NOT AVAILABLE") + end + + local zoomCVar = tonumber(GetCVar("minimapZoom") or "0") or 0 + local insideZoomCVar = tonumber(GetCVar("minimapInsideZoom") or "0") or 0 + local indoors = zoomCVar == zoom and "outdoor" or "indoor" + print(string.format("CVar minimapZoom=%d minimapInsideZoom=%d => addon fallback treats current state as %s", zoomCVar, insideZoomCVar, indoors)) + print(string.format("Fallback outdoor radius[%d] = %.2f", zoom, getFallbackRadius(true))) + print(string.format("Fallback indoor radius[%d] = %.2f", zoom, getFallbackRadius(false))) + + local HBDPins = QuestieCompat and QuestieCompat.HBDPins + if HBDPins then + local activeCount = 0 + for _ in pairs(HBDPins.activeMinimapPins or {}) do + activeCount = activeCount + 1 + end + print(string.format("Questie HBDPins activeMinimapPins: %d", activeCount)) + + local printed = 0 + print("--- SAMPLE PIN METRICS ---") + for pin, data in pairs(HBDPins.activeMinimapPins or {}) do + printed = printed + 1 + if printed > 8 then break end + + local _, _, _, xOff, yOff = pin:GetPoint() + local px = xOff or 0 + local py = yOff or 0 + local screenDist = math.sqrt(px * px + py * py) + local radiusPx = math.min(width, height) * 0.5 + local radiusNorm = radiusPx > 0 and (screenDist / radiusPx) or 0 + local edgePx = radiusPx * 0.9 + local edgeErr = screenDist - edgePx + local onEdge = data.onEdge and true or false + local shown = pin.IsShown and pin:IsShown() or false + local iconData = pin.data or {} + local label = iconData.Name or iconData.name or iconData.Title or "unknown" + local questId = iconData.Id or iconData.id or iconData.questId or "?" + + print(string.format( + "[%d] %s q=%s shown=%s edge=%s world=%.2f,%.2f dist=%.3f screen=%.1f,%.1f |px|=%.1f norm=%.3f edgePx=%.1f edgeErr=%.1f", + printed, label, questId, tostring(shown), tostring(onEdge), + data.x or 0, data.y or 0, data.distanceFromMinimapCenter or 0, + px, py, screenDist, radiusNorm, edgePx, edgeErr + )) + end + else + print("QuestieCompat.HBDPins = NOT AVAILABLE") + end + + local parent = m:GetParent() + print(string.format("Minimap parent: %s (size %.0fx%.0f scale %.2f)", + parent and parent:GetName() or "nil", + parent and parent:GetWidth() or 0, + parent and parent:GetHeight() or 0, + parent and parent:GetScale() or 0)) + + print("") + print("Interpretation:") + print("- If Minimap:GetViewRadius() and C_Minimap.GetViewRadius() match the fallback, radius is not the drift source.") + print("- If the sample pin screen error stays near 0.0, Questie's projection math is internally consistent.") + print("- If radius is correct but the terrain still looks offset, the remaining issue is texture/layout alignment, not pin math.") +end + +_G.QuestieRadiusDebug = QuestieRadiusDebug +_G.QuestieTerrainDebug = QuestieTerrainDebug function QuestieSlash.RegisterSlashCommands() Questie:RegisterChatCommand("questieclassic", QuestieSlash.HandleCommands) Questie:RegisterChatCommand("questie", QuestieSlash.HandleCommands) + Questie:RegisterChatCommand("radiusdebug", function() + if _G.QuestieRadiusDebug then + _G.QuestieRadiusDebug() + else + print("[Questie] radius debug unavailable") + end + end) + Questie:RegisterChatCommand("terraindebug", function() + if _G.QuestieTerrainDebug then + _G.QuestieTerrainDebug() + else + print("[Questie] terrain debug unavailable") + end + end) end function QuestieSlash.HandleCommands(input)