From 0c4631be6e0c8c566fdcb9a4f31b93cdb9b7c7dc Mon Sep 17 00:00:00 2001 From: Xurkon Date: Tue, 9 Jun 2026 17:57:58 -0500 Subject: [PATCH] feat(tooltip): accurate per-pin data source attribution Re-adds the 'Show data source' tooltip option (General tab, default off), but accurate this time. The previous version was removed because it guessed the source from the global data-source mode (showing Learner when data was AscensionDB, etc.). This tags each pin with its real provenance at creation and reads that tag. - QuestieDB.GetPinDataSource(entityType, id, spawnData): resolves Learner (per-spawn isLearned or learner record), AscensionDB (curated ascensionOverrideKeys override), or base Questie DB. Never guesses. - Objective pins tagged per-spawn in _DetermineIconsToDraw; available/ finisher pins defaulted by quest in DrawWorldIcon; manual notes tagged Townsfolk in DrawManualIcon. - World-map pins read the per-pin tag (MapIconTooltip); unit/object/item hovers derive per-id at the render layer (TooltipHandler via QuestieTooltips:GetDataSourceLine). Comms appended via KeyExists. - No line shown when source is genuinely unknown (never misleading). --- CHANGELOG.md | 2 ++ Database/QuestieDB.lua | 36 +++++++++++++++++++ Modules/Map/QuestieMap.lua | 13 +++++++ .../GeneralTab/QuestieOptionsGeneral.lua | 12 +++++++ Modules/Options/QuestieOptionsDefaults.lua | 1 + Modules/Quest/QuestieQuest.lua | 3 +- Modules/Tooltips/MapIconTooltip.lua | 21 +++++++++++ Modules/Tooltips/Tooltip.lua | 33 ++++++++++++++--- Modules/Tooltips/TooltipHandler.lua | 19 ++++++++++ 9 files changed, 134 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5dfaa..2870ff3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ - **[Map - Looted Object Pins Disappear On Loot]** When you open/loot a quest object node (e.g. Fell Wood piles), that specific node's map/minimap pin now disappears immediately even if the objective is not yet fully collected, instead of all node pins staying until turn-in. On `LOOT_OPENED`, `QuestieQuest:RemoveLootedObjectivePins` finds the active object-objective spawn nearest the player's world position (within ~12 yards, since you stand on the node to loot it), records it as consumed in `Questie.db.char.lootedObjectSpawns`, and redraws just that objective so clustering recomputes with the looted node skipped. `_DetermineIconsToDraw` skips consumed object spawns, so removal persists across redraws and `/reload`. The looted history is cleared on quest accept (`QuestieQuest:ClearLootedSpawns`), so abandoning and re-doing the quest — including after an Ascension prestige — shows every node again. Scoped to `object`-type objectives; monster/kill pins are unchanged. Works in every data source mode (auto / learner / static / none): the suppression is centralized in the single shared `_DetermineIconsToDraw` draw path that all redraw routes funnel through (core `UpdateQuest`, the learner's debounced `_DoFlushActiveQuestPins`, slider redraws), and it operates on the already mode-resolved `objective.spawnList`. Matching is radius-based (1.5 zone units) rather than exact-coordinate so that in learner/auto mode a spawn the learner re-adds at the player's position for the just-looted node is also suppressed instead of re-appearing. +- **[Tooltip - Data Source Attribution (Accurate)]** Re-added the "Show data source" tooltip option (General tab, off by default) that adds a `Source:` line to NPC, object, item and world-map-pin tooltips. Unlike the previous version (removed in an earlier build for guessing the source from the global data-source mode), this is accurate per-pin/per-entity and never guesses: each pin is tagged with its real provenance at creation via the new `QuestieDB.GetPinDataSource(entityType, id, spawnData)` helper — `Learner` (per-spawn `isLearned` flag or a learner record), `AscensionDB` (a curated `ascensionOverrideKeys` override for the id), `Townsfolk` (manual/menu notes), or the base `Questie DB` — with a `Comms` overlay appended when comms holds data for the entity. Objective pins are tagged per-spawn in `_DetermineIconsToDraw`; available/finisher pins are quest-scoped (defaulted in `DrawWorldIcon`); manual notes are tagged in `DrawManualIcon`. World-map pins read the per-pin tag (`MapIconTooltip`); unit/object/item hovers derive it per-id at the render layer (`TooltipHandler` via `QuestieTooltips:GetDataSourceLine`). When a pin's source genuinely can't be determined, no line is shown rather than a misleading one. + ### 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). diff --git a/Database/QuestieDB.lua b/Database/QuestieDB.lua index 8ad9b12..00edbed 100644 --- a/Database/QuestieDB.lua +++ b/Database/QuestieDB.lua @@ -2586,6 +2586,42 @@ local function _Asc_LoadIfString(data, label) return data end +--- Returns the data-source label for a pin/spawn, or nil if it cannot be determined. +--- Accuracy rule: never guess from global mode. Only returns a label backed by real +--- evidence — the per-spawn `isLearned` flag, an AscensionDB curated-override entry, or a +--- learner record for the entity — and otherwise reports the base "Questie DB". +---@param entityType string @"NPC" | "OBJECT" | "QUEST" | "ITEM" +---@param id number|nil +---@param spawnData table|nil @optional per-spawn entry from a spawnList (its .isLearned wins) +---@return string|nil +function QuestieDB.GetPinDataSource(entityType, id, spawnData) + -- Per-spawn truth is the most precise signal when available. + if spawnData and spawnData.isLearned then + return "Learner" + end + if not id then return nil end + + -- AscensionDB-curated entity (has at least one curated override field for this id). + local ascKeys = QuestieDB.ascensionOverrideKeys and QuestieDB.ascensionOverrideKeys[entityType] + if ascKeys and ascKeys[id] and next(ascKeys[id]) then + return "AscensionDB" + end + + -- Learner-known entity (learned data exists for this id). + local learner = Questie.dbLearner and Questie.dbLearner.global + if learner then + local bucket = (entityType == "OBJECT" and learner.objects) + or (entityType == "QUEST" and learner.quests) + or (entityType == "ITEM" and learner.items) + or learner.npcs + if bucket and bucket[id] then + return "Learner" + end + end + + return "Questie DB" +end + local function _Asc_ProtectField(dbType, id, key) QuestieDB.ascensionOverrideKeys = QuestieDB.ascensionOverrideKeys or {} QuestieDB.ascensionOverrideKeys[dbType] = QuestieDB.ascensionOverrideKeys[dbType] or {} diff --git a/Modules/Map/QuestieMap.lua b/Modules/Map/QuestieMap.lua index 66c0e12..fc99336 100644 --- a/Modules/Map/QuestieMap.lua +++ b/Modules/Map/QuestieMap.lua @@ -573,6 +573,12 @@ function QuestieMap:DrawManualIcon(data, areaID, x, y, typ) data.Id = data.id + -- Manual notes are placed by the Townsfolk / menu system. Tag them so the source + -- tooltip line can report it (only if a caller hasn't already set a more specific source). + if data.DataSource == nil then + data.DataSource = "Townsfolk" + end + local uiMapId = _ResolveMapUiMapId(ZoneDB:GetUiMapIdByAreaId(areaID), x, y) if (not uiMapId) then Questie:Debug(Questie.DEBUG_CRITICAL, "[QuestieMap:DrawManualIcon] No UiMapID for areaId:", areaID, tostring(data.Name)) @@ -714,6 +720,13 @@ function QuestieMap:DrawWorldIcon(data, areaID, x, y, showFlag) return nil, nil end + -- Tag data-source provenance for the source tooltip line. Objective pins set this + -- explicitly (per-spawn). Available/finisher pins are quest-scoped, so derive from the + -- quest. Leave nil for anything else so the tooltip shows no (potentially wrong) source. + if data.DataSource == nil and (data.Type == "available" or data.Type == "complete") then + data.DataSource = QuestieDB.GetPinDataSource("QUEST", data.Id) + end + local uiMapId = _ResolveMapUiMapId(ZoneDB:GetUiMapIdByAreaId(areaID), x, y) if (not uiMapId) then local parentMapId diff --git a/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua b/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua index 2ea0b0a..97e5c5a 100644 --- a/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua +++ b/Modules/Options/GeneralTab/QuestieOptionsGeneral.lua @@ -558,6 +558,18 @@ function QuestieOptions.tabs.general:Initialize() Questie.db.profile.learnerTooltipUseSecondary = value end }, + showDataSource = { + type = "toggle", + order = 8.605, + name = function() return l10n('Show data source'); end, + desc = function() return l10n('Adds a "Source:" line to NPC, object and map-pin tooltips showing where the data came from (Questie DB, AscensionDB, Learner, Townsfolk, Comms). Only shown when the source is actually known.'); end, + width = 1.5, + disabled = function() return not Questie.db.profile.enableTooltips; end, + get = function() return Questie.db.profile.enableTooltipsSource == true end, + set = function(_, value) + Questie.db.profile.enableTooltipsSource = value + end + }, partyOnlyToggle = { type = "toggle", order = 8.61, diff --git a/Modules/Options/QuestieOptionsDefaults.lua b/Modules/Options/QuestieOptionsDefaults.lua index e133757..e436ed3 100644 --- a/Modules/Options/QuestieOptionsDefaults.lua +++ b/Modules/Options/QuestieOptionsDefaults.lua @@ -87,6 +87,7 @@ function QuestieOptionsDefaults:Load() enableTooltipsObjectID = false, enableTooltipsQuestID = false, enableTooltipsQuestLevel = true, + enableTooltipsSource = false, showQuestXpAtMaxLevel = true, enableTooltipsNextInChain = true, learnerTooltips = true, diff --git a/Modules/Quest/QuestieQuest.lua b/Modules/Quest/QuestieQuest.lua index 4f2c42f..78b3193 100644 --- a/Modules/Quest/QuestieQuest.lua +++ b/Modules/Quest/QuestieQuest.lua @@ -1916,7 +1916,8 @@ _DetermineIconsToDraw = function(quest, objective, objectiveIndex, objectiveCent IconScale = spawnData.GetIconScale(), Name = spawnData.Name, Type = objective.Type, - ObjectiveTargetId = spawnData.Id + ObjectiveTargetId = spawnData.Id, + DataSource = QuestieDB.GetPinDataSource(objective.Type == "object" and "OBJECT" or "NPC", id, spawnData) } objective.AlreadySpawned[id] = { diff --git a/Modules/Tooltips/MapIconTooltip.lua b/Modules/Tooltips/MapIconTooltip.lua index 305da63..be8c7c7 100644 --- a/Modules/Tooltips/MapIconTooltip.lua +++ b/Modules/Tooltips/MapIconTooltip.lua @@ -102,6 +102,23 @@ local function _GetWorldMapTooltipSourceLine(pinData) return nil end +--- Per-pin data-source attribution line for the hovered world-map icon. +--- Reads the DataSource tag set at pin creation (objective/finisher/available/manual) so +--- the label is accurate, plus a Comms overlay when comms holds data for this id. +local function _GetWorldMapDataSourceLine(pinData) + if not Questie.db.profile.enableTooltipsSource then return nil end + local src = pinData and pinData.DataSource + local parts = {} + if src then tinsert(parts, src) end + local id = pinData and pinData.Id + if id and QuestieComms and QuestieComms.data and QuestieComms.data.KeyExists + and QuestieComms.data:KeyExists("m_" .. tostring(id)) then + tinsert(parts, "Comms") + end + if table.getn(parts) == 0 then return nil end + return "|cFF808080Source: " .. table.concat(parts, " + ") .. "|r" +end + function MapIconTooltip:Show() local _, _, _, alpha = self.texture:GetVertexColor(); if alpha == 0 then @@ -751,6 +768,10 @@ function MapIconTooltip:Show() end self:AddLine(_GetWorldMapTooltipSourceLine(self.data), 0.55, 0.55, 0.55) + local dataSourceLine = _GetWorldMapDataSourceLine(self.data) + if dataSourceLine then + self:AddLine(dataSourceLine, 0.55, 0.55, 0.55) + end end Tooltip:_Rebuild() -- we separate this so things like MODIFIER_STATE_CHANGED can redraw the tooltip Tooltip:SetFrameStrata("TOOLTIP"); diff --git a/Modules/Tooltips/Tooltip.lua b/Modules/Tooltips/Tooltip.lua index e7a8cd8..4997de2 100644 --- a/Modules/Tooltips/Tooltip.lua +++ b/Modules/Tooltips/Tooltip.lua @@ -115,12 +115,27 @@ local function _GetQuestObjectiveSummary(questId) return summary end -local function _BuildTooltipSourceLine(sourceFlags) - return nil -end - +--- Accurate data-source attribution for a unit/object/item hover tooltip. +--- Derives provenance from the entity id (never from the global mode): AscensionDB +--- curated override, learner record, or base "Questie DB", plus a Comms overlay when +--- comms holds data for this key. Returns nil when the source can't be determined or the +--- option is off, so a wrong/guessed label is never shown. +---@param key string @"m_" | "o_" | "i_" local function _GetTooltipSourceLine(key) - return nil + if not Questie.db.profile.enableTooltipsSource then return nil end + if not key then return nil end + local prefix = key:sub(1, 2) + local id = tonumber(key:sub(3)) + local entityType = (prefix == "o_" and "OBJECT") or (prefix == "i_" and "ITEM") or "NPC" + + local parts = {} + local src = id and QuestieDB.GetPinDataSource(entityType, id) or nil + if src then tinsert(parts, src) end + if QuestieComms and QuestieComms.data and QuestieComms.data.KeyExists and QuestieComms.data:KeyExists(key) then + tinsert(parts, "Comms") + end + if table.getn(parts) == 0 then return nil end + return "|cFF808080Source: " .. table.concat(parts, " + ") .. "|r" end ---@param questId number @@ -632,6 +647,14 @@ elseif key:sub(1,2) == "o_" then return tooltipLines end +--- Public accessor for the data-source attribution line. Called by the render layer +--- (TooltipHandler) rather than appended inside GetTooltip so it never interferes with +--- the quest-title de-duplication that consumes GetTooltip's result. +---@param key string @"m_" | "o_" | "i_" +function QuestieTooltips:GetDataSourceLine(key) + return _GetTooltipSourceLine(key) +end + _InitObjectiveTexts = function(objectivesText, objectiveIndex, playerName) if (not objectivesText) then objectivesText = {} diff --git a/Modules/Tooltips/TooltipHandler.lua b/Modules/Tooltips/TooltipHandler.lua index 1fb3c27..096cd8f 100644 --- a/Modules/Tooltips/TooltipHandler.lua +++ b/Modules/Tooltips/TooltipHandler.lua @@ -329,6 +329,12 @@ function _QuestieTooltips:AddUnitDataToTooltip() end end + -- Data-source attribution (opt-in; internally gated and nil when source unknown). + local npcSourceLine = QuestieTooltips:GetDataSourceLine("m_" .. npcId) + if npcSourceLine then + GameTooltip:AddLine(npcSourceLine) + end + local npcNum = tonumber(npcId) if npcNum then _AddQuestStarterDropsToTooltip(npcNum) @@ -394,6 +400,11 @@ function _QuestieTooltips:AddItemDataToTooltip() self:AddLine(v) end end + -- Data-source attribution (opt-in; internally gated and nil when source unknown). + local itemSourceLine = QuestieTooltips:GetDataSourceLine("i_" .. (itemId or 0)) + if itemSourceLine then + self:AddLine(itemSourceLine) + end QuestieTooltips.lastGametooltipCount = _QuestieTooltips:CountTooltip() end lastItemId = itemId; @@ -448,6 +459,14 @@ function _QuestieTooltips:AddObjectDataToTooltip(name) end end end + -- Data-source attribution for the hovered object (opt-in; nil when unknown). + local firstObjectId = lookup[1] + if firstObjectId then + local objSourceLine = QuestieTooltips:GetDataSourceLine("o_" .. firstObjectId) + if objSourceLine then + GameTooltip:AddLine(objSourceLine) + end + end if QuestieTooltips.ResizeTooltip then QuestieTooltips:ResizeTooltip(GameTooltip) end