fix: empty override tables no longer wipe base DB spawns; real-time pin refresh on NPC learn
Two fixes for QuestieLearner real-time pin rendering:
1. _MergeOverride now skips empty tables (IsEmptyTable guard).
QuestieLearner stores spawns={} before any coords are captured.
Previously _MergeOverride treated {} as valid data and replaced the
base DB's real spawn coordinates with an empty table, breaking pins
for all NPCs that had been seen but not killed (mouseovers, etc.).
2. _InvalidateSpawnListsForNPC triggers after LearnNPC adds new spawn data.
The quest objective system caches spawnList once per objective.
When a kill adds new coordinates, the cached list is stale.
The new helper:
- Scans all active quest objectives for references to the NPC
- Clears objective.spawnList and objective.AlreadySpawned
- Calls QuestieQuest:UpdateQuest to rebuild pins with fresh data
This makes learned pins appear in real-time without /reload.
This commit is contained in:
@@ -15,12 +15,19 @@ local _QuestieDB = QuestieDB.private
|
|||||||
-- 1. override[stringKey] (string-keyed, e.g. from wotlkNPCFixes)
|
-- 1. override[stringKey] (string-keyed, e.g. from wotlkNPCFixes)
|
||||||
-- 2. override[intKey] (numeric-keyed, e.g. from QuestieLearner / AscensionDB)
|
-- 2. override[intKey] (numeric-keyed, e.g. from QuestieLearner / AscensionDB)
|
||||||
-- 3. rawdata[intKey] (fallback to compiled DB)
|
-- 3. rawdata[intKey] (fallback to compiled DB)
|
||||||
|
--
|
||||||
|
-- Empty tables from QuestieLearner (e.g. spawns={}) are treated as "not present"
|
||||||
|
-- so the base DB's real spawn data is preserved until actual coordinates are learned.
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
|
local function IsEmptyTable(val)
|
||||||
|
return type(val) == "table" and next(val) == nil
|
||||||
|
end
|
||||||
|
|
||||||
local function _MergeOverride(result, override, rawdata, keyMap)
|
local function _MergeOverride(result, override, rawdata, keyMap)
|
||||||
for stringKey, intKey in pairs(keyMap) do
|
for stringKey, intKey in pairs(keyMap) do
|
||||||
if override[stringKey] ~= nil then
|
if override[stringKey] ~= nil then
|
||||||
result[stringKey] = override[stringKey]
|
result[stringKey] = override[stringKey]
|
||||||
elseif override[intKey] ~= nil then
|
elseif override[intKey] ~= nil and not IsEmptyTable(override[intKey]) then
|
||||||
result[stringKey] = override[intKey]
|
result[stringKey] = override[intKey]
|
||||||
elseif rawdata then
|
elseif rawdata then
|
||||||
result[stringKey] = rawdata[intKey]
|
result[stringKey] = rawdata[intKey]
|
||||||
|
|||||||
@@ -308,6 +308,46 @@ local function _RefreshActiveQuestPins(questIdSet)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Invalidates cached objective.spawnList for any active quest whose objectives
|
||||||
|
-- reference the given npcId. This forces the map system to rebuild spawn lists
|
||||||
|
-- from QuestieDB on the next update, picking up newly learned coordinates.
|
||||||
|
local function _InvalidateSpawnListsForNPC(npcId)
|
||||||
|
if not QuestieQuest or not QuestiePlayer or not QuestiePlayer.currentQuestlog then return end
|
||||||
|
local timer = (C_Timer) or (QuestieCompat and QuestieCompat.C_Timer)
|
||||||
|
local questsToRefresh = {}
|
||||||
|
for questId, _ in pairs(QuestiePlayer.currentQuestlog) do
|
||||||
|
local quest = QuestieDB.GetQuest and QuestieDB.GetQuest(questId)
|
||||||
|
if quest and quest.Objectives then
|
||||||
|
for _, objective in pairs(quest.Objectives) do
|
||||||
|
local shouldInvalidate = false
|
||||||
|
-- Monster objectives reference NPCs directly in spawnList keys
|
||||||
|
if objective.spawnList then
|
||||||
|
if objective.spawnList[npcId] then
|
||||||
|
shouldInvalidate = true
|
||||||
|
end
|
||||||
|
-- Also check killcredit IdList
|
||||||
|
if not shouldInvalidate and objective.IdList then
|
||||||
|
for _, id in ipairs(objective.IdList) do
|
||||||
|
if id == npcId then shouldInvalidate = true; break end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Fallback: if objective Id matches the NPC (some objectives use NPC as their primary Id)
|
||||||
|
if not shouldInvalidate and objective.Id == npcId then
|
||||||
|
shouldInvalidate = true
|
||||||
|
end
|
||||||
|
if shouldInvalidate then
|
||||||
|
objective.spawnList = nil
|
||||||
|
objective.AlreadySpawned = nil
|
||||||
|
questsToRefresh[questId] = true
|
||||||
|
break -- one invalidation per quest is enough
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
_RefreshActiveQuestPins(questsToRefresh)
|
||||||
|
end
|
||||||
|
|
||||||
------------------------------------------------------------------------
|
------------------------------------------------------------------------
|
||||||
-- CrossLinkAfterNPC: called when a new NPC is first learned.
|
-- CrossLinkAfterNPC: called when a new NPC is first learned.
|
||||||
-- Scans all learned quests for any reference to this npcId and stitches
|
-- Scans all learned quests for any reference to this npcId and stitches
|
||||||
@@ -645,6 +685,11 @@ function QuestieLearner:LearnNPC(npcId, name, level, subName, npcFlags, factionS
|
|||||||
if isNew then
|
if isNew then
|
||||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] New NPC learned:", npcId, name or "?")
|
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] New NPC learned:", npcId, name or "?")
|
||||||
CrossLinkAfterNPC(npcId)
|
CrossLinkAfterNPC(npcId)
|
||||||
|
else
|
||||||
|
-- Existing NPC got new spawn data: invalidate cached spawnLists
|
||||||
|
-- for any active quest objective that references this NPC so the
|
||||||
|
-- map system rebuilds them with fresh data on next update.
|
||||||
|
_InvalidateSpawnListsForNPC(npcId)
|
||||||
end
|
end
|
||||||
_Learner:BroadcastIfCommsAvailable("NPC", npcId, existing)
|
_Learner:BroadcastIfCommsAvailable("NPC", npcId, existing)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user