From 1ca426ee9feb5f7b68a560581fd2adc1a80a8c2f Mon Sep 17 00:00:00 2001 From: Xurkon Date: Sat, 6 Jun 2026 14:30:07 -0500 Subject: [PATCH] feat: expand learner tooltip controls --- CHANGELOG.md | 2 +- .../GeneralTab/QuestieOptionsGeneral.lua | 26 +++- Modules/Options/QuestieOptionsDefaults.lua | 2 + Modules/QuestieLearner.lua | 127 +++++++++++++++--- Modules/Tooltips/Tooltip.lua | 2 +- Tests/QuestieLearnerDataSourceMode_spec.lua | 6 + docs/changelog.html | 2 +- 7 files changed, 148 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 211d73f..93f063d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ ### Performance - **[Questie Debug - Message Throttle]** Added a live debug-message throttle so non-fatal debug output cannot spam chat faster than it can be read. The throttle is configurable in the Advanced tab and keeps fatal output separate. -- **[Questie Learner - Tooltip Controls]** Added learner-specific tooltip controls so players can toggle the learned spawn line, learner confidence lines, and automatic tooltip resizing from the Tooltip options. The learner tooltip paths now respect those toggles before adding extra lines. +- **[Questie Learner - Tooltip Controls]** Added learner-specific tooltip controls so players can toggle the learned spawn line, total learned spawn count, learner confidence lines, automatic tooltip resizing, and a secondary learner tooltip from the Tooltip options. The learner tooltip paths now respect those toggles before adding extra lines. - **[QuestieLearner - Kill/Pin Refresh Throttling]** Debounced learner-triggered map-pin refreshes so heavy kill streaks do not redraw pins on every event. Added a maximum wait cap so batched updates still flush predictably instead of being pushed out forever by constant activity. - **[QuestieLearner - Bystander Kill Suppression]** Changed visible nearby `UNIT_DIED` handling so kills from other players can update short-lived correlation evidence without immediately running full learner injection or pin refresh work. - **[QuestieLearner - PARTY_KILL Event-Order Fix]** Fixed an edge case where a `UNIT_DIED` debounce entry could suppress a later authoritative `PARTY_KILL` for the same GUID. The debounce now tracks event type and allows the player's/group's kill event through while still suppressing true duplicates. diff --git a/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua b/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua index 048bfe3..9851be7 100644 --- a/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua +++ b/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua @@ -522,6 +522,18 @@ function QuestieOptions.tabs.general:Initialize() Questie.db.profile.learnerTooltipShowConfidence = value end }, + learnerTooltipShowTotalSpawns = { + type = "toggle", + order = 8.585, + name = function() return l10n('Show total spawns learned'); end, + desc = function() return l10n('Show the total number of learned spawn points for NPC tooltips.'); end, + width = 1.5, + disabled = function() return not (Questie.db.profile.enableTooltips and (Questie.db.profile.learnerTooltips ~= false)); end, + get = function() return Questie.db.profile.learnerTooltipShowTotalSpawns ~= false end, + set = function(_, value) + Questie.db.profile.learnerTooltipShowTotalSpawns = value + end + }, learnerTooltipAutoResize = { type = "toggle", order = 8.59, @@ -534,9 +546,21 @@ function QuestieOptions.tabs.general:Initialize() Questie.db.profile.learnerTooltipAutoResize = value end }, - partyOnlyToggle = { + learnerTooltipUseSecondary = { type = "toggle", order = 8.6, + name = function() return l10n('Use secondary learner tooltip'); end, + desc = function() return l10n('Show learner-specific spawn details in a separate tooltip instead of adding them to the existing one.'); end, + width = 1.5, + disabled = function() return not (Questie.db.profile.enableTooltips and (Questie.db.profile.learnerTooltips ~= false)); end, + get = function() return Questie.db.profile.learnerTooltipUseSecondary == true end, + set = function(_, value) + Questie.db.profile.learnerTooltipUseSecondary = value + end + }, + partyOnlyToggle = { + type = "toggle", + order = 8.61, name = function() return l10n('Only show party members'); end, desc = function() return l10n('When this is enabled, shared quest info will only show players in your party.'); end, width = 1.5, diff --git a/Modules/Options/QuestieOptionsDefaults.lua b/Modules/Options/QuestieOptionsDefaults.lua index eae5556..e133757 100644 --- a/Modules/Options/QuestieOptionsDefaults.lua +++ b/Modules/Options/QuestieOptionsDefaults.lua @@ -92,7 +92,9 @@ function QuestieOptionsDefaults:Load() learnerTooltips = true, learnerTooltipShowSpawn = true, learnerTooltipShowConfidence = true, + learnerTooltipShowTotalSpawns = true, learnerTooltipAutoResize = true, + learnerTooltipUseSecondary = false, learnerBroadcast = true, enableMapIcons = true, enableMiniMapIcons = true, diff --git a/Modules/QuestieLearner.lua b/Modules/QuestieLearner.lua index b67c919..24a7bda 100644 --- a/Modules/QuestieLearner.lua +++ b/Modules/QuestieLearner.lua @@ -4427,33 +4427,103 @@ end ------------------------------------------------------------------------ local _tooltipHookRegistered = false +local _learnerTooltipFrame = nil + +local function _CountLearnedNpcSpawns(entry) + if not entry or not entry[7] then + return 0 + end + + local total = 0 + for _, zoneSpawns in next, entry[7] do + if type(zoneSpawns) == "table" then + total = total + table.getn(zoneSpawns) + end + end + return total +end + +local function _GetLearnerTooltipFrame() + if _learnerTooltipFrame then + return _learnerTooltipFrame + end + + local frame = CreateFrame("GameTooltip", "QuestieLearnerTooltip", UIParent, "GameTooltipTemplate") + frame:SetFrameStrata("TOOLTIP") + frame:SetClampedToScreen(true) + frame:SetOwner(UIParent, "ANCHOR_NONE") + _learnerTooltipFrame = frame + return _learnerTooltipFrame +end + +local function _HideLearnerTooltipFrame() + if _learnerTooltipFrame then + _learnerTooltipFrame:Hide() + end +end + +local function _ShowLearnerTooltipFrame(sourceTooltip, lines) + local tooltip = _GetLearnerTooltipFrame() + tooltip:ClearLines() + tooltip:SetOwner(sourceTooltip or GameTooltip, "ANCHOR_RIGHT") + for _, line in next, lines do + tooltip:AddLine(line) + end + local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips") + if QuestieTooltips and QuestieTooltips.ResizeTooltip then + QuestieTooltips:ResizeTooltip(tooltip) + end + tooltip:Show() +end --- Adds a "Learned spawn: (x, y) from N kills" line to the tooltip --- for the NPC represented by the given unit token. --- Called by the GameTooltip OnTooltipSetUnit hook. ---@param unitToken string WoW unit token (e.g. "mouseover") local function _AddLearnedSpawnTooltipLine(unitToken) - if not Questie or not Questie.dbLearner then return end - if not Questie.db.profile or Questie.db.profile.learnerTooltips == false or Questie.db.profile.learnerTooltipShowSpawn == false then + if not Questie or not Questie.dbLearner then + _HideLearnerTooltipFrame() + return + end + if not Questie.db.profile or Questie.db.profile.learnerTooltips == false then + _HideLearnerTooltipFrame() return end local guid = UnitGUID(unitToken) - if not guid then return end + if not guid then + _HideLearnerTooltipFrame() + return + end local npcId, guidType = GetIdAndTypeFromGUID(guid) - if guidType ~= "Creature" and guidType ~= "Vehicle" then return end - if not npcId then return end + if guidType ~= "Creature" and guidType ~= "Vehicle" then + _HideLearnerTooltipFrame() + return + end + if not npcId then + _HideLearnerTooltipFrame() + return + end local entry = Questie.dbLearner.global.npcs[npcId] - if not entry or not entry[7] then return end + if not entry or not entry[7] then + _HideLearnerTooltipFrame() + return + end -- Find the first zone with spawn data local spawnsByZone = entry[7] local zoneId = next(spawnsByZone) - if not zoneId then return end + if not zoneId then + _HideLearnerTooltipFrame() + return + end local zoneSpawns = spawnsByZone[zoneId] - if not zoneSpawns or #zoneSpawns == 0 then return end + if not zoneSpawns or #zoneSpawns == 0 then + _HideLearnerTooltipFrame() + return + end -- Use the first recorded coordinate local x = zoneSpawns[1][1] @@ -4462,14 +4532,38 @@ local function _AddLearnedSpawnTooltipLine(unitToken) local formattedX = ("%.1f"):format(x) local formattedY = ("%.1f"):format(y) - local text = ("(%s, %s)"):format(formattedX, formattedY) - if Questie.db.profile.learnerTooltipShowConfidence ~= false then - text = text .. (" from %d kill%s"):format(kills, kills == 1 and "" or "s") + local lines = {} + if Questie.db.profile.learnerTooltipShowSpawn ~= false then + local text = ("(%s, %s)"):format(formattedX, formattedY) + if Questie.db.profile.learnerTooltipShowConfidence ~= false then + text = text .. (" from %d kill%s"):format(kills, kills == 1 and "" or "s") + end + lines[#lines + 1] = {"Learned spawn", text} end - GameTooltip:AddDoubleLine("Learned spawn", text) - local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips") - if QuestieTooltips and QuestieTooltips.ResizeTooltip then - QuestieTooltips:ResizeTooltip(GameTooltip) + + if Questie.db.profile.learnerTooltipShowTotalSpawns ~= false then + lines[#lines + 1] = {"Total spawns learned", tostring(_CountLearnedNpcSpawns(entry))} + end + + if #lines == 0 then + _HideLearnerTooltipFrame() + return + end + + if Questie.db.profile.learnerTooltipUseSecondary == true then + local rendered = {} + for _, pair in ipairs(lines) do + rendered[#rendered + 1] = pair[1] .. ": " .. pair[2] + end + _ShowLearnerTooltipFrame(GameTooltip, rendered) + else + for _, pair in ipairs(lines) do + GameTooltip:AddDoubleLine(pair[1], pair[2]) + end + local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips") + if QuestieTooltips and QuestieTooltips.ResizeTooltip then + QuestieTooltips:ResizeTooltip(GameTooltip) + end end end @@ -4485,6 +4579,9 @@ local function _RegisterLearnedSpawnTooltipHook() _AddLearnedSpawnTooltipLine(unitToken) end end) + GameTooltip:HookScript("OnHide", function() + _HideLearnerTooltipFrame() + end) end ------------------------------------------------------------------------ diff --git a/Modules/Tooltips/Tooltip.lua b/Modules/Tooltips/Tooltip.lua index 6fb1423..df736eb 100644 --- a/Modules/Tooltips/Tooltip.lua +++ b/Modules/Tooltips/Tooltip.lua @@ -84,7 +84,7 @@ local function _ResizeTooltipToFit(tooltip) end function QuestieTooltips:ResizeTooltip(tooltip) - if Questie.db.profile.learnerTooltipAutoResize == false then + if not (Questie.db and Questie.db.profile) or Questie.db.profile.learnerTooltipAutoResize == false then return end _ResizeTooltipToFit(tooltip) diff --git a/Tests/QuestieLearnerDataSourceMode_spec.lua b/Tests/QuestieLearnerDataSourceMode_spec.lua index 3bafc3a..e8a36f9 100644 --- a/Tests/QuestieLearnerDataSourceMode_spec.lua +++ b/Tests/QuestieLearnerDataSourceMode_spec.lua @@ -53,13 +53,19 @@ describe("QuestieLearner data source mode", function() assert.is_true(has(defaults, "learnerTooltips = true")) assert.is_true(has(defaults, "learnerTooltipShowSpawn = true")) assert.is_true(has(defaults, "learnerTooltipShowConfidence = true")) + assert.is_true(has(defaults, "learnerTooltipShowTotalSpawns = true")) assert.is_true(has(defaults, "learnerTooltipAutoResize = true")) + assert.is_true(has(defaults, "learnerTooltipUseSecondary = false")) assert.is_true(has(general, "Learner Tooltips")) assert.is_true(has(general, "Enable learner tooltips")) + assert.is_true(has(general, "Show total spawns learned")) + assert.is_true(has(general, "Use secondary learner tooltip")) assert.is_true(has(tip, "function QuestieTooltips:ResizeTooltip(tooltip)")) assert.is_true(has(handler, "QuestieTooltips:ResizeTooltip(GameTooltip)")) assert.is_true(has(handler, "QuestieTooltips:ResizeTooltip(self)")) assert.is_true(has(learner, "learnerTooltipShowSpawn")) + assert.is_true(has(learner, "_CountLearnedNpcSpawns(entry)")) + assert.is_true(has(learner, "_ShowLearnerTooltipFrame(GameTooltip, rendered)")) end) it("allows learner mode to draw pins from a single learned spawn when needed", function() diff --git a/docs/changelog.html b/docs/changelog.html index f2d95bc..5734160 100644 --- a/docs/changelog.html +++ b/docs/changelog.html @@ -180,7 +180,7 @@