From d207feb3dba0ba065c1abc9798a46e4512454273 Mon Sep 17 00:00:00 2001 From: Xurkon Date: Tue, 9 Jun 2026 17:17:12 -0500 Subject: [PATCH] fix(tooltip): skip objective lookup for quests not in the log Hovering an NPC right after turning in one of its associated quests spammed a debugstack trace in DEVELOP mode: the learner objective- correlation block in GetTooltip called QuestLogCache.GetQuestObjectives for a quest no longer in QuestLogCache (which gracefully returns {} but logs the stack at line 306). Guard the NPC and object correlation loops on QuestiePlayer.currentQuestlog[questId] so live objective progress is only looked up for quests the player is currently on. Not a crash and no functional change for active quests. --- CHANGELOG.md | 1 + Modules/Tooltips/Tooltip.lua | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00f4d6a..6e5dfaa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ ### Bug Fixes +- **[Tooltip - Stale Quest Objective Lookup On Turn-In]** Hovering an NPC right after turning in (or abandoning) one of its associated quests spammed a `debugstack` trace in DEVELOP mode: the learner objective-correlation block in `QuestieTooltips:GetTooltip` called `QuestLogCache.GetQuestObjectives` for a quest no longer in `QuestLogCache` (gracefully returns `{}`, but logs a stack). The NPC and object correlation loops now skip quests not in `QuestiePlayer.currentQuestlog`, so live objective progress is only looked up for quests the player is currently on. No functional change for active quests (a completed quest had no progress to show anyway). - **[Tooltip - Secondary Learner Tooltip Leak]** With the "Use secondary learner tooltip" option enabled, learner spawn/kill lines still appeared in the main NPC tooltip instead of going solely to the secondary frame. Two parallel learner-tooltip systems were both rendering on unit hover: QuestieLearner's `OnTooltipSetUnit` hook (toggle-aware, secondary-capable) and `Tooltip.lua`'s `_GetLearnerTooltipLines` (always injected "Learned spawn / Total spawns learned / Total kills recorded" into the `m_` tooltip data). `QuestieTooltips:GetTooltip` now takes a `suppressLearnerLines` flag, and the NPC unit-hover call in `TooltipHandler.lua` passes it so the inline learner lines are omitted there — the `OnTooltipSetUnit` hook owns that display and routes it to the main tooltip (secondary off) or the separate secondary frame (secondary on). Map-pin and object tooltips are unaffected and keep their learner lines. Also removes the latent duplicate learner lines that appeared in the main tooltip even with the secondary frame disabled. - **[Map - Completed Objective Pins Linger]** Added `QuestieMap:UnloadQuestFramesForObjective(questId, objectiveIndex)` and call it from every completion path in `QuestieQuest:PopulateObjective` (objective completed, hidden by `ShouldHideObjective`, and static no-`Update` objectives). Previously, removing a completed objective's map/minimap pins relied solely on `_UnloadAlreadySpawnedIcons`, which walks `objective.AlreadySpawned`. That table desyncs from the frames actually on screen after learner spawn-list invalidation or complete→abandon→reaccept cycles, so when it was reset to `{}` the live frames leaked and pins for fully-completed objectives (e.g. 6/6 kills, a collected item objective) stayed on the map even though tooltips updated correctly. The new function unloads frames deterministically off the map's own `questIdFrames` registry by matching `frame.data.ObjectiveIndex`, so completed-objective pins are removed regardless of `AlreadySpawned` state. Restricted to positive standard-objective indices; SpecialObjectives (sentinel index 0) keep using `AlreadySpawned` to avoid cross-unloading siblings. - **[QuestieLib - GetColoredQuestName Nil Guard]** Added a nil guard in `QuestieLib:GetColoredQuestName` so a synthetic `questId` (e.g. one used in a chat link on servers that do not have a real quest entry) no longer crashes with `attempt to index a nil value`. The function now checks `QuestieDB.GetQuest(questId)` before reading its `isComplete` flag. diff --git a/Modules/Tooltips/Tooltip.lua b/Modules/Tooltips/Tooltip.lua index 3fda0d6..e7a8cd8 100644 --- a/Modules/Tooltips/Tooltip.lua +++ b/Modules/Tooltips/Tooltip.lua @@ -379,7 +379,11 @@ if key:sub(1,2) == "m_" then if learnedNpc and learnedNpc[10] then for _, questId in ipairs(learnedNpc[10]) do local qData = QuestieLearner.data.quests[questId] - if qData and qData[10] then + -- Only correlate live objective progress for quests the player + -- is currently on. A just-turned-in/abandoned quest is no longer in + -- QuestLogCache, so calling GetQuestObjectives for it would log a + -- debugstack and return {} (harmless but noisy in DEVELOP mode). + if qData and qData[10] and QuestiePlayer.currentQuestlog and QuestiePlayer.currentQuestlog[questId] then for slotIdx = 1, #qData[10] do local objSlot = qData[10][slotIdx] if objSlot then @@ -432,7 +436,11 @@ elseif key:sub(1,2) == "o_" then if learnedObj and learnedObj[2] then for _, questId in ipairs(learnedObj[2]) do local qData = QuestieLearner.data.quests[questId] - if qData and qData[10] then + -- Only correlate live objective progress for quests the player + -- is currently on. A just-turned-in/abandoned quest is no longer in + -- QuestLogCache, so calling GetQuestObjectives for it would log a + -- debugstack and return {} (harmless but noisy in DEVELOP mode). + if qData and qData[10] and QuestiePlayer.currentQuestlog and QuestiePlayer.currentQuestlog[questId] then for slotIdx = 1, #qData[10] do local objSlot = qData[10][slotIdx] if objSlot then