fix: tooltip flicker, learner quest field mapping, secondary tooltip style
- QuestieLib.GetColoredQuestName now nil-guards QuestieDB.GetQuest() before reading .isComplete - TooltipHandler.AddItemDataToTooltip re-keyed on itemId only (was firing on every ElvUI refresh) - Removed unconditional ResizeTooltip call from item tooltip path (caused first-hover pop) - OnQuestDetail no longer writes objectives text to data[6] or quest body to data[17] or zoneId to data[8] - OnQuestAccepted documents why requiredLevel/requiredRaces/requiredClasses are intentionally not captured - Reused logIdx from data build pass instead of redeclaring it for the objective scan - Secondary learner tooltip now matches GameTooltip style with or without ElvUI (defers to ElvUI when loaded, replicates its Transparent template otherwise) - Combined-tooltip learner stats now framed by invisible spacer lines for visual separation
This commit is contained in:
+13
-1
@@ -6,7 +6,8 @@
|
||||
|
||||
- **[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, 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.
|
||||
- **[Questie Options - Faster Live Slider Refresh]** Reduced the redraw debounce on the icon, clustering, and learner spawn-dedup sliders so changes refresh pins much sooner after the user releases the slider instead of waiting half a second before `SmoothReset()` even begins.
|
||||
- **[Questie Options - Faster Live Slider Refresh]** Reduced the redraw debounce on the icon, clustering, and learner spawn-dedup sliders so changes refresh pins much sooner after the user releases the slider instead of waiting half a second before `SmoothReset()` even begins. Also made `ClusterRedraw()` pass `{ skipCacheTest = true }` so clustering-only slider changes skip the `QuestLogCache.TestGameCache()` wait entirely and redraw immediately without blocking on quest log state.
|
||||
- **[Questie Options - ClusterRedraw SkipCache]** `SmoothReset()` now accepts an optional `{ skipCacheTest = true }` table. `ClusterRedraw()` uses it so the Objective Icon Cluster Amount and Dense Pin Clustering Aggressiveness sliders redraw icons immediately without waiting on quest log readiness. The Quel'Danas phase, icon limit, and other callers continue to use the full `SmoothReset()` path unchanged.
|
||||
- **[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.
|
||||
@@ -20,6 +21,17 @@
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- **[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.
|
||||
- **[Tooltip - Item Flicker on ElvUI Refresh]** Simplified the `AddItemDataToTooltip` redraw check to key purely on `itemId`. The previous condition also re-keyed on tooltip name, line count, and frame name — all of which ElvUI's tooltip-frame recycling transiently resets, so every refresh cycle looked like a new item and caused Questie's lines to flicker repeatedly. The new condition only re-adds when the actual itemId changes, so the tooltip stays stable across ElvUI's refresh ticks.
|
||||
- **[Tooltip - First-Hover Resize Pop]** Removed the unconditional `QuestieTooltips:ResizeTooltip(self)` call from `AddItemDataToTooltip`. With the flicker fix in place, the resize pass still fired once per item hover and triggered ElvUI's smooth-resize animation, causing a visible frame pop on first hover. ElvUI already manages tooltip sizing correctly, so the Questie resize call is no longer needed for item tooltips.
|
||||
- **[QuestieLearner - Quest Detail Field Mapping]** Fixed three quest-field index bugs in `OnQuestDetail` / `OnQuestAccepted` that were silently corrupting learner `questDataOverrides`. The detail handler was writing the objectives text into `data[6]` (the `requiredRaces` bitmask field), the quest body into `data[17]` (the `zoneOrSort` field), and the current zone areaId into `data[8]` (the `objectivesText` table field). Each write permanently blocked the correct value for that field because `LearnQuest` only writes nil keys. The detail handler now only writes `name` and `zoneOrSort`, and the accepted handler is annotated to make the schema restrictions explicit so future fields are not added by guess.
|
||||
- **[QuestieLearner - Accepted Handler Reducer]** Collapsed a redundant second pass over the quest log in `OnQuestAccepted` that was about to introduce a `local logIdx = 0` redeclaration. The index found while building the data table is now reused for the subsequent objective-mapping scan instead of being re-derived from scratch.
|
||||
|
||||
### Tooltip
|
||||
|
||||
- **[QuestieLearner - Secondary Tooltip Style Match]** The optional secondary learner tooltip frame is now styled to match the standard GameTooltip whether or not ElvUI is installed. When ElvUI is loaded, the frame defers to `ElvUI:GetModule("Tooltip"):SetStyle(frame)` so the user's configured colors and fonts take effect. When ElvUI is absent, the frame applies a hard-coded replica of ElvUI's default "Transparent" template (backdrop color `0.06, 0.06, 0.06, 0.8`, black 1px border, `FRIZQT__.TTF` 12px, 3px insets, 12px edge) using the standard GameTooltip textures. The skin is re-applied on every `OnShow` so the frame stays consistent if ElvUI loads late or its settings change.
|
||||
- **[QuestieLearner - Combined Tooltip Spacing]** When the secondary learner tooltip is disabled and learner stats are appended directly to the active GameTooltip, the learner section is now framed by an invisible spacer line above and below. This keeps NPC/quest data above and any other-addon data below from being visually crammed against the learner counts, while adding no new texture or color assets.
|
||||
|
||||
- **[QuestieDB - Required Source Item Guard]** Fixed a fresh-load crash in `QuestieDB.GetQuest()` where quests that had `requiredSourceItems` but no `objectives` table would dereference `objectives[3]` while building `SpecialObjectives`. This was a code-side nil guard bug on our end, not a saved-variable problem, and it is now covered by a regression test.
|
||||
- **[QuestieLearner - Learner Sunstrider Clustering]** Relaxed the Sunstrider Isle clustering override in learner mode so the clustering/deduplication knobs can still consolidate newly learned pins there. Static/auto keeps the original Sunstrider visibility behavior, but learner mode now preserves clustering so nearby learner spawns do not render as an unbounded fan-out of separate icons.
|
||||
- **[QuestieLearner - Turn-In Arrow Spawn Promotion]** Allowed quest-related NPC and object spawns to keep their live learner coordinates even when the static database marks those spawn fields as protected. Learner mode now merges the learner's live override tables back into the getter path too, so quest giver and turn-in locations can point from learner-discovered hand-in targets without relaxing protection for unrelated world spawns.
|
||||
|
||||
@@ -200,7 +200,7 @@ function QuestieLib:GetColoredQuestName(questId, showLevel, showState, blizzLike
|
||||
name = name .. " " .. Questie:Colorize("(" .. l10n("Complete") .. ")", "green")
|
||||
|
||||
-- Quests treated as complete - zero objectives or synthetic objectives
|
||||
elseif isComplete == 0 and QuestieDB.GetQuest(questId).isComplete == true then
|
||||
elseif isComplete == 0 and QuestieDB.GetQuest(questId) and QuestieDB.GetQuest(questId).isComplete == true then
|
||||
name = name .. " " .. Questie:Colorize("(" .. l10n("Complete") .. ")", "green")
|
||||
end
|
||||
end
|
||||
|
||||
+115
-19
@@ -3307,18 +3307,16 @@ function QuestieLearner:OnQuestDetail()
|
||||
|
||||
local data = {}
|
||||
data[1] = GetTitleText and GetTitleText() or nil
|
||||
-- requiredLevel and questLevel are not always available on the detail screen;
|
||||
-- they will be filled in by OnQuestAccepted from the quest log.
|
||||
data[6] = GetObjectiveText and GetObjectiveText() or nil -- objectives text
|
||||
-- Details/description text (body)
|
||||
if GetQuestDescription then
|
||||
data[17] = GetQuestDescription()
|
||||
end
|
||||
-- requiredLevel [4], questLevel [5], requiredRaces [6], requiredClasses [7],
|
||||
-- and objectivesText [8] are all filled in by OnQuestAccepted from the quest log.
|
||||
-- OnQuestDetail should NOT write to those fields here — LearnQuest only writes
|
||||
-- nil values, so an early write would permanently block the correct value.
|
||||
|
||||
-- Record current zone as zoneOrSort if not already set
|
||||
-- [17] zoneOrSort: current zone areaId (OnQuestAccepted may overwrite with
|
||||
-- its own zoneId, which is fine — accepted is the more accurate context).
|
||||
local zoneId = GetZoneId()
|
||||
if zoneId and zoneId > 0 then
|
||||
data[8] = zoneId
|
||||
data[17] = zoneId
|
||||
end
|
||||
|
||||
self:LearnQuest(questId, data)
|
||||
@@ -3489,21 +3487,35 @@ function QuestieLearner:OnQuestAccepted(firstArg, secondArg)
|
||||
Questie:Debug(Questie.DEBUG_INFO, "[QuestieLearner] OnQuestAccepted id=" .. tostring(questId))
|
||||
if not questId or questId <= 0 then return end
|
||||
|
||||
-- Build data table from quest log (scan for matching entry)
|
||||
-- Only store fields that match the questKeys schema (name=1, questLevel=5).
|
||||
-- Build data table from quest log (scan for matching entry).
|
||||
-- Only store fields that match the questKeys schema.
|
||||
-- Do NOT store objectives (key 10) as raw text — the DB compiler expects structured
|
||||
-- {creatureId, text} tuples; plain strings crash pairs() in GetQuest.
|
||||
--
|
||||
-- DO NOT capture requiredLevel [4], requiredRaces [6], or requiredClasses [7]
|
||||
-- from the quest log. Those represent the quest's actual requirements, not
|
||||
-- the player's. A quest shown to a Human is automatically acceptable to a
|
||||
-- Human, but it may also be acceptable to Orcs — we cannot know from the
|
||||
-- log alone. Capturing the player's race/class bit as those fields would
|
||||
-- corrupt data for any other character on the same account that loads the
|
||||
-- same SavedVariables (IsDoable would blacklist the quest for them).
|
||||
-- Similarly, requiredLevel is not exposed by GetQuestLogLeaderBoard on 3.3.5.
|
||||
local data = {}
|
||||
local logIdx = 0
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local title, level, _, isHeader, _, _, _, id = QuestieCompat.GetQuestLogTitle(i)
|
||||
local title, level, suggestedGroup, isHeader, _, _, _, id = QuestieCompat.GetQuestLogTitle(i)
|
||||
if not isHeader and id == questId then
|
||||
data[1] = title
|
||||
-- [5] questLevel: level returned by GetQuestLogTitle IS the quest's own level
|
||||
data[5] = level and level > 0 and level or nil
|
||||
logIdx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
-- Zone: reverse-lookup from GetRealZoneText() which is always accurate on 3.3.5.
|
||||
-- [17] zoneOrSort: areaId from current zone name. This is the correct
|
||||
-- field for zone storage (was previously incorrectly written as [8] which
|
||||
-- is objectivesText, and as [17] with the quest description text).
|
||||
local zoneText = GetRealZoneText()
|
||||
if zoneText and zoneText ~= "" and l10n and l10n.GetAreaIdByLocalName then
|
||||
local areaId = l10n:GetAreaIdByLocalName(zoneText)
|
||||
@@ -3515,12 +3527,13 @@ function QuestieLearner:OnQuestAccepted(firstArg, secondArg)
|
||||
self:LearnQuest(questId, data)
|
||||
|
||||
-- Proactively map objectives based on quest log text
|
||||
local logIdx = 0
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local _, _, _, isHeader, _, _, _, id = QuestieCompat.GetQuestLogTitle(i)
|
||||
if not isHeader and id == questId then
|
||||
logIdx = i
|
||||
break
|
||||
if logIdx == 0 then
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local _, _, _, isHeader, _, _, _, id = QuestieCompat.GetQuestLogTitle(i)
|
||||
if not isHeader and id == questId then
|
||||
logIdx = i
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4443,8 +4456,64 @@ local function _CountLearnedNpcSpawns(entry)
|
||||
return total
|
||||
end
|
||||
|
||||
-- Replicates ElvUI's "Transparent" tooltip style so the secondary learner
|
||||
-- frame matches the look of the standard GameTooltip whether or not ElvUI
|
||||
-- is installed. If ElvUI IS installed, defer to its Tooltip:SetStyle (the
|
||||
-- user's configured colors/fonts override our defaults).
|
||||
local function _ApplyElvUIStyleTooltip(frame)
|
||||
if not frame or not frame.GetName then return end
|
||||
if ElvUI and ElvUI.GetModule then
|
||||
local ok, TT = pcall(ElvUI.GetModule, ElvUI, "Tooltip")
|
||||
if ok and TT and TT.SetStyle then
|
||||
pcall(TT.SetStyle, TT, frame)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- ElvUI default "Transparent" template — see ElvUI/Core/Toolkit.lua:82
|
||||
-- and ElvUI/Settings/Profile.lua:29-31. Hard-coded so the secondary
|
||||
-- frame looks the same with or without ElvUI.
|
||||
if not frame.SetBackdrop then return end
|
||||
frame:SetBackdrop({
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true,
|
||||
tileSize = 16,
|
||||
edgeSize = 12,
|
||||
insets = {left = 3, right = 3, top = 3, bottom = 3},
|
||||
})
|
||||
-- (0.06, 0.06, 0.06, 0.8) — ElvUI's default backdropfadecolor
|
||||
frame:SetBackdropColor(0.06, 0.06, 0.06, 0.8)
|
||||
-- Black border (ElvUI's default bordercolor is {0, 0, 0})
|
||||
frame:SetBackdropBorderColor(0, 0, 0, 1)
|
||||
|
||||
-- Apply the same font ElvUI uses by default. FontTemplate isn't available
|
||||
-- without ElvUI, so set font + shadow directly.
|
||||
local fontName, fontSize = "Fonts\\FRIZQT__.TTF", 12
|
||||
local tooltipName = frame:GetName()
|
||||
if tooltipName then
|
||||
for i = 1, frame:NumLines() or 10 do
|
||||
local left = _G[tooltipName .. "TextLeft" .. i]
|
||||
local right = _G[tooltipName .. "TextRight" .. i]
|
||||
for _, region in next, {left, right} do
|
||||
if region and region.SetFont then
|
||||
region:SetFont(fontName, fontSize, "")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function _GetLearnerTooltipFrame()
|
||||
if _learnerTooltipFrame then
|
||||
-- ElvUI may have loaded after this frame was first created; re-apply
|
||||
-- the skin in that case so the secondary frame stays consistent.
|
||||
if not _learnerTooltipFrame.__questieStyled then
|
||||
_ApplyElvUIStyleTooltip(_learnerTooltipFrame)
|
||||
_learnerTooltipFrame:HookScript("OnShow", function(self)
|
||||
_ApplyElvUIStyleTooltip(self)
|
||||
end)
|
||||
_learnerTooltipFrame.__questieStyled = true
|
||||
end
|
||||
return _learnerTooltipFrame
|
||||
end
|
||||
|
||||
@@ -4452,6 +4521,13 @@ local function _GetLearnerTooltipFrame()
|
||||
frame:SetFrameStrata("TOOLTIP")
|
||||
frame:SetClampedToScreen(true)
|
||||
frame:SetOwner(UIParent, "ANCHOR_NONE")
|
||||
-- Apply the ElvUI tooltip look now (works with or without ElvUI) and
|
||||
-- re-apply on every show so style changes / new addons don't drift.
|
||||
_ApplyElvUIStyleTooltip(frame)
|
||||
frame:HookScript("OnShow", function(self)
|
||||
_ApplyElvUIStyleTooltip(self)
|
||||
end)
|
||||
frame.__questieStyled = true
|
||||
_learnerTooltipFrame = frame
|
||||
return _learnerTooltipFrame
|
||||
end
|
||||
@@ -4462,6 +4538,16 @@ local function _HideLearnerTooltipFrame()
|
||||
end
|
||||
end
|
||||
|
||||
-- Adds a blank double-line spacer to a tooltip. Used as a lightweight
|
||||
-- visual separator between learner data and surrounding tooltip content
|
||||
-- when the secondary frame is disabled.
|
||||
local function _AddTooltipSeparator(tooltip)
|
||||
-- AddDoubleLine(leftText, rightText, leftR, leftG, leftB, rightR, rightG, rightB)
|
||||
-- Passing " " for both with 0 alpha makes the line invisible, creating a
|
||||
-- clean one-line vertical gap without needing any texture assets.
|
||||
tooltip:AddDoubleLine(" ", " ", 0, 0, 0, 0, 0, 0)
|
||||
end
|
||||
|
||||
local function _ShowLearnerTooltipFrame(sourceTooltip, lines)
|
||||
local tooltip = _GetLearnerTooltipFrame()
|
||||
tooltip:ClearLines()
|
||||
@@ -4552,14 +4638,24 @@ local function _AddLearnedSpawnTooltipLine(unitToken)
|
||||
|
||||
if Questie.db.profile.learnerTooltipUseSecondary == true then
|
||||
local rendered = {}
|
||||
-- Spacer before learner section
|
||||
rendered[#rendered + 1] = " "
|
||||
for _, pair in ipairs(lines) do
|
||||
rendered[#rendered + 1] = pair[1] .. ": " .. pair[2]
|
||||
end
|
||||
-- Spacer after learner section
|
||||
rendered[#rendered + 1] = " "
|
||||
_ShowLearnerTooltipFrame(GameTooltip, rendered)
|
||||
else
|
||||
-- Thin horizontal separator above learner section so it doesn't
|
||||
-- visually blend into the NPC data above.
|
||||
_AddTooltipSeparator(GameTooltip)
|
||||
for _, pair in ipairs(lines) do
|
||||
GameTooltip:AddDoubleLine(pair[1], pair[2])
|
||||
end
|
||||
-- Thin horizontal separator below learner section to separate
|
||||
-- from any addon data appended below (e.g. other tooltip mods).
|
||||
_AddTooltipSeparator(GameTooltip)
|
||||
local QuestieTooltips = QuestieLoader:ImportModule("QuestieTooltips")
|
||||
if QuestieTooltips and QuestieTooltips.ResizeTooltip then
|
||||
QuestieTooltips:ResizeTooltip(GameTooltip)
|
||||
|
||||
@@ -357,14 +357,7 @@ function _QuestieTooltips:AddItemDataToTooltip()
|
||||
string.match(link,
|
||||
"|?c?f?f?(%x*)|?H?([^:]*):?(%d+):?(%d*):?(%d*):?(%d*):?(%d*):?(%d*):?(%-?%d*):?(%-?%d*):?(%d*):?(%d*):?(%-?%d*)|?h?%[?([^%[%]]*)%]?|?h?|?r?"))
|
||||
end
|
||||
if name and itemId and (
|
||||
name ~= QuestieTooltips.lastGametooltipItem or
|
||||
(not QuestieTooltips.lastGametooltipCount) or
|
||||
_QuestieTooltips:CountTooltip() < QuestieTooltips.lastGametooltipCount or
|
||||
QuestieTooltips.lastGametooltipType ~= "item" or
|
||||
lastItemId ~= itemId or
|
||||
QuestieTooltips.lastFrameName ~= self:GetName()
|
||||
) then
|
||||
if name and itemId and (lastItemId ~= itemId) then
|
||||
QuestieTooltips.lastGametooltipItem = name
|
||||
local tooltipData = QuestieTooltips:GetTooltip("i_" .. (itemId or 0));
|
||||
if tooltipData then
|
||||
@@ -374,9 +367,6 @@ function _QuestieTooltips:AddItemDataToTooltip()
|
||||
for _, v in next, tooltipData do
|
||||
self:AddLine(v)
|
||||
end
|
||||
if QuestieTooltips.ResizeTooltip then
|
||||
QuestieTooltips:ResizeTooltip(self)
|
||||
end
|
||||
end
|
||||
QuestieTooltips.lastGametooltipCount = _QuestieTooltips:CountTooltip()
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user