fix(map): completed/accepted/repeatable available-quest pins (#7 #9 #10)

#7: Completed quests kept showing as available '!'. The server completed-
quest list arrives async via QUEST_QUERY_COMPLETE, often after available
quests were first drawn. Recalculate available quests once char.complete is
populated by that event so completed quests are removed (also clears the
already-completed subset of #8).

#9: An accepted quest's available '!' could linger on the minimap until
/reload. UnloadQuestFramesByDataType unloaded the frame but left its name in
questIdFrames and _G; it now removes those references so the icon is torn
down immediately on both map and minimap.

#10: Added 'Hide repeatable quests below level 60' (Icons tab, off by
default) to hide repeatable available quests (e.g. the Ascension Callboard)
from map/minimap until level 60. Enforced in _DrawQuestIfAvailable and
ShouldBeHidden so existing minimap pins are removed too; reappear at 60.

Lint: selene 0 errors. Tests: 145 successes / same 7 pre-existing failures.
This commit is contained in:
Xurkon
2026-06-09 23:18:38 -05:00
parent 4827a3131e
commit ab560d5711
7 changed files with 42 additions and 1 deletions
+5
View File
@@ -22,6 +22,8 @@
### Features
- **[Map - Hide Repeatable/Callboard Quests Below Level 60]** (#10) Added "Hide repeatable quests below level 60" (Icons tab, off by default) which hides repeatable available `!` quests — such as the Ascension Callboard — from the map and minimap until the character reaches level 60, where they become relevant. Enforced both at draw time (`_DrawQuestIfAvailable`) and in `ShouldBeHidden`, so already-drawn pins are removed on the minimap too, and they reappear automatically on hitting 60. Only affects repeatable available quests and only when the option is enabled.
- **[Tooltip - ElvUI Style Without ElvUI]** Added "ElvUI tooltip style" (General tab, on by default) which skins Questie's tooltips — the NPC/item/object and world-map-pin tooltips plus the secondary learner tooltip — with ElvUI's transparent flat look (dark `0.06/0.06/0.06/0.8` background and a thin 1px border) even when ElvUI is not installed, so they match instead of falling back to the chunky default WoW border. `QuestieTooltips:SkinDefaultTooltips` applies it to `GameTooltip`, `WorldMapTooltip`, `ItemRefTooltip` and the shopping tooltips (re-asserted on show), and the secondary learner frame's fallback style was corrected to the same thin border. No-op when ElvUI is loaded, since ElvUI skins the frames itself.
- **[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.
@@ -30,6 +32,9 @@
### Bug Fixes
- **[Map - Completed Quests Still Shown As Available]** (#7) The server's completed-quest list is delivered asynchronously (the `QUEST_QUERY_COMPLETE` event), often after available quests were first drawn — so quests that were actually already complete kept showing as available `!` until something forced a redraw (e.g. `/reload`). Questie now recalculates available quests once `char.complete` is populated by that event, removing the completed ones. This also clears the "already completed" subset of the false-available pins reported in #8.
- **[Map - Available '!' Lingered On Minimap For Accepted Quests]** (#9) When a quest was accepted, its available `!` icon could remain on the minimap until a `/reload`. `QuestieMap:UnloadQuestFramesByDataType` unloaded the frame but left its name in the frame registry and `_G`, so the minimap icon was not fully torn down. It now removes the registry/global reference as well, so the available icon is cleared immediately on accept for both the map and minimap.
- **[Tooltip - Data Source In Secondary Tooltip Only]** The `Source:` attribution line now appears only inside the secondary learner tooltip (when "Use secondary learner tooltip" is enabled) and is never added to the main NPC/object/item tooltip; with the secondary tooltip disabled it does not appear at all. The map-pin source line is gated the same way. The "Show data source" option now depends on the secondary-tooltip option being enabled.
- **[Tooltip - Learner Pin Mislabelled As AscensionDB]** A spawn freshly learned via the learner was shown as `Source: AscensionDB` instead of `Learner`. `QuestieDB.GetPinDataSource` resolved AscensionDB-curated ownership before checking the learner, but in learner-only mode the displayed spawns are the learner's (curated coords are discarded by `GetNPC`/`GetObject`). It is now mode-aware: in learner mode, an entity with a learner record reports `Learner` even when AscensionDB also curates it.
- **[Learner - Learner-Only Mode No Longer Draws AscensionDB/Static Pins]** In learner-only mode the map/minimap drew curated AscensionDB (and static) spawns for every quest NPC/object the player had not personally recorded, instead of only the learner's own spawns. `QuestieDB:GetNPC`/`GetObject` fall back to the `npcDataOverrides`/`objectDataOverrides` entry for metadata when there is no learner record, and that entry's spawns (AscensionDB-curated for un-recorded entities) were being used as pins. Both functions now track whether a real learner record exists and, in learner mode, strip the spawns when it does not — so learner-only mode shows exclusively what the learner has recorded (the metadata fallback for names/tooltips is kept). Added a regression test. Recorded entities still show their learner spawns; auto/static/none modes are unchanged.
+8
View File
@@ -755,6 +755,14 @@ function QuestieCompat:QUEST_QUERY_COMPLETE(event)
QuestieCompat.Merge(Questie.db.char.complete, Questie.db.char.weekly)
end
end
-- The completed-quest list arrives asynchronously from the server (this event), often
-- AFTER available quests were first drawn — so quests that are actually already complete
-- kept showing as available '!' until something else forced a redraw (e.g. /reload).
-- Recalculate now that char.complete is populated so completed quests are removed. (#7)
if QuestieQuest and QuestieQuest.started and AvailableQuests and AvailableQuests.CalculateAndDrawAll then
AvailableQuests.CalculateAndDrawAll()
end
end
-- https://wowpedia.fandom.com/wiki/API_IsQuestFlaggedCompleted
+1
View File
@@ -542,6 +542,7 @@ function _Qframe:ShouldBeHidden()
and ((not DailyQuests:IsActiveDailyQuest(questId)) -- hide not-today-dailies
or ((not profile.enableAvailable) and normal)
or ((not profile.showRepeatableQuests) and repeatable)
or (profile.hideRepeatableBelowMaxLevel and repeatable and (UnitLevel("player") or 0) < 60) -- Hide Callboard/repeatable quests below 60 (#10)
or ((not profile.showEventQuests) and event)
or ((not profile.showDungeonQuests) and dungeon)
or ((not profile.showRaidQuests) and raid)
+6 -1
View File
@@ -129,9 +129,14 @@ end
function QuestieMap:UnloadQuestFramesByDataType(questId, dataType)
if QuestieMap.questIdFrames[questId] then
for _, frame in pairs(QuestieMap:GetFramesForQuest(questId)) do
for name, frame in pairs(QuestieMap:GetFramesForQuest(questId)) do
if frame and frame.data and frame.data.Type == dataType then
frame:Unload()
-- Also drop the registry/global reference. Without this the frame name
-- lingered in questIdFrames and _G, so the available '!' could stay on the
-- minimap after a quest was accepted until a full /reload rebuilt frames. (#9)
QuestieMap.questIdFrames[questId][name] = nil
_G[name] = nil
end
end
@@ -187,6 +187,23 @@ function QuestieOptions.tabs.icons:Initialize()
QuestieQuest:ToggleNotes(value)
end,
},
hideRepeatableBelowMaxLevel = {
type = "toggle",
order = 2.035,
name = function() return l10n('Hide repeatable quests below level 60'); end,
desc = function() return l10n('Hides repeatable available quests (such as the Ascension Callboard) from the map and minimap until the character reaches level 60, where they become relevant.'); end,
width = 1.595,
disabled = function() return (not Questie.db.profile.enabled) or (not Questie.db.profile.showRepeatableQuests); end,
get = function(info) return Questie.db.profile.hideRepeatableBelowMaxLevel end,
set = function(info, value)
Questie.db.profile.hideRepeatableBelowMaxLevel = value
QuestieQuest:ToggleNotes(true)
local AvailableQuests = QuestieLoader:ImportModule("AvailableQuests")
if AvailableQuests and AvailableQuests.CalculateAndDrawAll then
AvailableQuests.CalculateAndDrawAll()
end
end,
},
showPvPQuests = {
type = "toggle",
order = 2.04,
@@ -177,6 +177,7 @@ function QuestieOptionsDefaults:Load()
hideUnexploredMapIcons = false,
hideUntrackedQuestsMapIcons = false,
showRepeatableQuests = true,
hideRepeatableBelowMaxLevel = false,
showEventQuests = true,
showDungeonQuests = true,
showRaidQuests = true,
+4
View File
@@ -122,6 +122,9 @@ _CalculateAvailableQuests = function()
local completedQuests = Questie.db.char.complete
local showRepeatableQuests = Questie.db.profile.showRepeatableQuests
-- Hide repeatable available quests (e.g. the Ascension Callboard '!') below level 60,
-- where they aren't relevant yet. (#10)
local hideRepeatableBelowMax = Questie.db.profile.hideRepeatableBelowMaxLevel and playerLevel < 60
local showDungeonQuests = Questie.db.profile.showDungeonQuests
local showRaidQuests = Questie.db.profile.showRaidQuests
local showPvPQuests = Questie.db.profile.showPvPQuests
@@ -163,6 +166,7 @@ _CalculateAvailableQuests = function()
if (
((not showRepeatableQuests) and QuestieDB.IsRepeatable(questId)) or -- Don't show repeatable quests if option is disabled
(hideRepeatableBelowMax and QuestieDB.IsRepeatable(questId)) or -- Hide repeatable (Callboard) quests below level 60 (#10)
((not showPvPQuests) and QuestieDB.IsPvPQuest(questId)) or -- Don't show PvP quests if option is disabled
((not showDungeonQuests) and QuestieDB.IsDungeonQuest(questId)) or -- Don't show dungeon quests if option is disabled
((not showRaidQuests) and QuestieDB.IsRaidQuest(questId)) or -- Don't show raid quests if option is disabled