diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b1d459..cc5c6f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ ### Bug Fixes +- **[Tooltip - Collapse Stacked Ascension Quest-Progress Lines]** (#9) Tooltips could pile up a stack of progress lines for the same objective — `0/8`, `1/8`, `2/8`, `3/8` — because the Ascension server appends a new progress line on every objective update instead of replacing the old one. A new always-on pass (`DedupeAscensionProgressLines`) collapses each stack to its single most-progressed line. It only ever hides a line when the same tooltip holds another progress line with the same objective text and denominator, so it never touches other addons' tooltip lines or Questie's own single-line objectives (unit-tested). This is separate from the opt-in "Hide Ascension quest progress lines" option (#16), which removes such lines entirely. - **[Map - Completed Quest Pins Stuck On The World Map Until Reload]** (#9) A completed quest/objective's pin could stay on the world map (while the minimap correctly cleared it) until a `/reload`. Cause: a draw-queue race. The world-map and minimap draw queues are processed independently in `ProcessQueue`, so when a frame was unloaded while still queued (e.g. an objective that completes the same tick its pins were queued), the frame's minimap-queue entry could trigger the deferred `Unload` first — removing both icons and clearing the unload flag — and then a later map-queue entry would re-add the world-map icon. Because `Unload` had already nil'd the `questIdFrames`/`_G` reference, that re-added pin was orphaned and survived until `/reload`. `Unload` now drops the frame's pending draw-queue entries (new `QuestieMap:DequeueFrameDrawCalls`), so an unloaded frame can never be re-added to a map — keeping both maps in sync. - **[Map - Quest-Type Filters Now Truly Apply To The Minimap]** (#11) Filtered quest types (e.g. dungeon quests) could still show on the minimap while correctly hidden on the world map. The previous fix made the minimap `FadeLogic` re-check `ShouldBeHidden` only when deciding whether to *re-show* an already-hidden icon — so an icon that was already visible (or that HBD's pin renderer showed on coming into range) was never hidden. `FadeLogic` now proactively calls `ShouldBeHidden` for every in-range minimap icon and `FakeHide`s it when filtered, in both minimap fade paths (quest icons and townsfolk/manual icons). The same path also picked up the #17 minimap-radius cutoff gating it was missing. - **[Learner - Kills Never Recorded Spawn Coordinates]** The combat-log kill handler computed the player's spawn position inside `if credited then ... end`, but `credited` was read **before** it was assigned (the `local credited = ...` came several lines later), so it was always `nil` — the position-capture block never ran. Every killed NPC was saved with `spawnSource="fallback"` and no `[7]` spawns regardless of kill count, so in learner-only mode their pins never appeared (and re-killing didn't help). Moved the `credited` computation above the position capture, and guard against a `0,0` position being stored. The learner's `GetPlayerCoords` (used by quest-giver/object learning) also now uses the robust `QuestieCompat.GetCurrentPlayerPosition()` (handles `SetMapToCurrentZone` and the Sunstrider parent/child coordinate correction) instead of the raw `GetPlayerMapPosition`, returning `nil` when no valid position exists so no `0,0` pins are recorded. Newly killed mobs now record real coordinates and their learner pins persist. **Hardening:** the kill spawn is now keyed under the same map space the coordinates were captured in (`GetCurrentPlayerPosition`'s mapId) rather than a separately-derived `GetZoneId()` that can disagree with the coordinate space on subzones; a throttled debug warning surfaces credited kills that yield no usable position; and a regression test (`Tests/QuestieLearnerKillCapture_spec.lua`) drives the real kill handler end-to-end and asserts capture, the `0,0` guard, and the per-mode display contract (learner shows the learned spawn, static never exposes learner spawns). diff --git a/Modules/Tooltips/TooltipHandler.lua b/Modules/Tooltips/TooltipHandler.lua index 1365b8e..ff20b24 100644 --- a/Modules/Tooltips/TooltipHandler.lua +++ b/Modules/Tooltips/TooltipHandler.lua @@ -252,17 +252,77 @@ local function _AddQuestStarterDropsToTooltip(npcId) end end +-- Collapses STACKED Ascension quest-progress lines for the same objective. +-- The Ascension server appends a new progress line on every objective update instead of +-- replacing the previous one, so a tooltip can pile up "0/8", "1/8", "2/8", "3/8" for one +-- objective (#9). This keeps only the most-progressed line of each stack. +-- +-- It is SAFE to run unconditionally (unlike the full strip below): it only hides a line +-- when the SAME tooltip holds another progress line with the same objective text AND the +-- same denominator. Other addons — and Questie's own single-line objectives — never produce +-- two such duplicates, so their tooltip lines are never touched. +function _QuestieTooltips:DedupeAscensionProgressLines(tooltip, numLines) + local frameName = tooltip:GetName() + if not frameName then return end + + local groups = {} + for i = 2, numLines do + local fontString = _G[frameName .. "TextLeft" .. i] + local text = fontString and fontString:GetText() + if text then + local clean = string.gsub(text, "|[cC]%x%x%x%x%x%x%x%x", "") + clean = string.gsub(clean, "|[rR]", "") + clean = string.match(clean, "^%s*(.-)%s*$") or clean + -- Strip an optional leading bullet/dash so "- 2/8 ..." matches "2/8 ...". + local body = string.match(clean, "^%-%s*(.+)$") or clean + local current, total, label = string.match(body, "^(%d+)%s*/%s*(%d+)%s*(.-)$") + if current and total then + local key = (label or "") .. "@@" .. total + local group = groups[key] + if not group then + groups[key] = { indices = { i }, bestIndex = i, bestCurrent = tonumber(current) } + else + table.insert(group.indices, i) + if tonumber(current) >= group.bestCurrent then + group.bestCurrent = tonumber(current) + group.bestIndex = i + end + end + end + end + end + + for _, group in pairs(groups) do + if table.getn(group.indices) > 1 then + for _, idx in ipairs(group.indices) do + if idx ~= group.bestIndex then + local fontString = _G[frameName .. "TextLeft" .. idx] + if fontString then + fontString:SetText("") + fontString:Hide() + end + end + end + end + end +end + function _QuestieTooltips:HideAscensionQuestLines(tooltip) if not Questie.db.profile.enableTooltips then return end - -- Opt-in only. This strips lines matching quest-objective patterns ("N/M", "[N] ...") - -- from tooltips to hide Ascension's server-injected quest progress. It runs on every - -- tooltip, so by default it must NOT touch them — otherwise it clobbers other tooltip - -- addons' lines that happen to look like "N/M" (durability, stack counts, etc.). Users - -- who want the Ascension quest-spam hidden can enable it explicitly. (#16) - if not Questie.db.profile.hideAscensionTooltipQuestLines then return end local numLines = tooltip:NumLines() if not numLines or numLines < 1 then return end + -- Always collapse stacked duplicate progress lines for the same objective. This is the + -- safe, targeted fix for the "0/8 1/8 2/8 ..." pile-up and never touches other addons. + _QuestieTooltips:DedupeAscensionProgressLines(tooltip, numLines) + + -- Opt-in only. The full strip removes EVERY line matching a quest-objective pattern + -- ("N/M", "[N] ...") to hide all of Ascension's server-injected quest progress. It runs + -- on every tooltip, so by default it must NOT touch them — otherwise it clobbers other + -- tooltip addons' lines that happen to look like "N/M" (durability, stack counts, etc.). + -- Users who want all Ascension quest-spam gone can enable it explicitly. (#16) + if not Questie.db.profile.hideAscensionTooltipQuestLines then return end + for i = 2, numLines do local fontString = _G[tooltip:GetName() .. "TextLeft" .. i] if fontString then