fix(tracker): name and group quests built from partial records
Quests assembled from a Learner entry or a questDataOverrides entry that never captured every field reach the tracker with nil fields, since GetQuest copies rawdata key by key. Two of those show: a nil name printed as the quest id, and a nil zoneOrSort that sent _GetZoneName down its very first line, `if not zoneOrSort then return "Unknown Zone" end`, before it could consult the quest log header the earlier fix added. Those objects are cached for the session, so neither repaired itself. GetQuest now fills a missing name from the quest log and defaults zoneOrSort to 0, the value every caller already reads as "no zone on file" -- and which some of them require, `quest.zoneOrSort > 0` erroring outright on nil. _GetZoneName treats nil the same way rather than short-circuiting, which also stops a nil quest from labelling its group Unknown Zone under the sort modes that do not group by zone at all. The tracker asks the quest log for a title before printing an id, so quests already cached without a name come out right too, and the live-fallback builder stops discarding the override data it just looked up.
This commit is contained in:
+43
-5
@@ -1785,16 +1785,36 @@ function QuestieDB.GetQuest(questId, ...) -- /dump QuestieDB.GetQuest(867)
|
|||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
-- Build a minimal live-fallback quest from the quest log so the tracker still works
|
-- Build a minimal live-fallback quest from the quest log so the tracker still works.
|
||||||
|
-- The override that got us here is partial by nature -- a Learner record of a single
|
||||||
|
-- field, a correction -- but whatever it does carry beats guessing, and the quest object
|
||||||
|
-- built here is cached for the session, so anything left blank stays blank.
|
||||||
local logEntry = QuestLogCache.GetQuest(questId)
|
local logEntry = QuestLogCache.GetQuest(questId)
|
||||||
if not logEntry then return nil end
|
if not logEntry then return nil end
|
||||||
|
local function NonEmpty(text)
|
||||||
|
if text and text ~= "" then return text end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
local overrideName = NonEmpty(overrideData[QuestieDB.questKeys.name])
|
||||||
|
local overrideLevel = overrideData[QuestieDB.questKeys.questLevel]
|
||||||
|
local overrideZone = overrideData[QuestieDB.questKeys.zoneOrSort]
|
||||||
|
local cachedTitle = NonEmpty(logEntry.title)
|
||||||
|
local liveTitle
|
||||||
|
if (not overrideName) and (not cachedTitle) and GetQuestLogIndexByID and GetQuestLogTitle then
|
||||||
|
-- QuestLogCache is a snapshot and can be missing the title of a quest the client
|
||||||
|
-- has since filled in, which is what leaves a quest showing as its own id.
|
||||||
|
local questLogIndex = GetQuestLogIndexByID(questId)
|
||||||
|
if questLogIndex and questLogIndex > 0 then
|
||||||
|
liveTitle = NonEmpty(GetQuestLogTitle(questLogIndex))
|
||||||
|
end
|
||||||
|
end
|
||||||
local fallback = {
|
local fallback = {
|
||||||
Id = questId,
|
Id = questId,
|
||||||
name = logEntry.title or tostring(questId),
|
name = overrideName or cachedTitle or liveTitle or tostring(questId),
|
||||||
level = logEntry.level or 0,
|
level = overrideLevel or logEntry.level or 0,
|
||||||
questLevel = logEntry.level or 0,
|
questLevel = overrideLevel or logEntry.level or 0,
|
||||||
requiredLevel = 0,
|
requiredLevel = 0,
|
||||||
zoneOrSort = 0,
|
zoneOrSort = overrideZone or 0,
|
||||||
questFlags = 0,
|
questFlags = 0,
|
||||||
specialFlags = 0,
|
specialFlags = 0,
|
||||||
Starts = { CreatureStarts = {}, ObjectStarts = {}, ItemStarts = {} },
|
Starts = { CreatureStarts = {}, ObjectStarts = {}, ItemStarts = {} },
|
||||||
@@ -1916,6 +1936,24 @@ function QuestieDB.GetQuest(questId, ...) -- /dump QuestieDB.GetQuest(867)
|
|||||||
if learnerRecord.objIndex and not QO.objIndex then QO.objIndex = learnerRecord.objIndex end
|
if learnerRecord.objIndex and not QO.objIndex then QO.objIndex = learnerRecord.objIndex end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- A record that never captured a name -- a Learner entry written from an objective update, an
|
||||||
|
-- override carrying a single key -- leaves QO.name nil, and this object is cached for the rest
|
||||||
|
-- of the session, so everything downstream ends up printing the quest id. Ask the client.
|
||||||
|
if ((not QO.name) or QO.name == "") and GetQuestLogIndexByID and GetQuestLogTitle then
|
||||||
|
local questLogIndex = GetQuestLogIndexByID(questId)
|
||||||
|
if questLogIndex and questLogIndex > 0 then
|
||||||
|
local logTitle = GetQuestLogTitle(questLogIndex)
|
||||||
|
if logTitle and logTitle ~= "" then
|
||||||
|
QO.name = logTitle
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Same story for the zone: partial records leave it nil, and callers all treat it as a number
|
||||||
|
-- (`quest.zoneOrSort > 0` errors outright on nil). 0 is the value everything already reads as
|
||||||
|
-- "no zone on file", which sends the tracker to the quest log for one.
|
||||||
|
QO.zoneOrSort = QO.zoneOrSort or 0
|
||||||
|
|
||||||
local questLevel, requiredLevel = QuestieLib.GetTbcLevel(questId)
|
local questLevel, requiredLevel = QuestieLib.GetTbcLevel(questId)
|
||||||
QO.level = questLevel
|
QO.level = questLevel
|
||||||
QO.requiredLevel = requiredLevel
|
QO.requiredLevel = requiredLevel
|
||||||
|
|||||||
@@ -63,6 +63,22 @@ local function GetWrappedWidth(label)
|
|||||||
return label:GetWidth()
|
return label:GetWidth()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- A quest built from partial data can reach the tracker with no name, or with the id standing in
|
||||||
|
-- for one, and it is cached that way for the session. Ask the quest log before printing a number.
|
||||||
|
local function GetDisplayableQuestName(quest)
|
||||||
|
local questName = quest.name
|
||||||
|
|
||||||
|
if (not questName) or questName == "" or questName == tostring(quest.Id) then
|
||||||
|
questName = TrackerUtils:GetQuestLogTitleById(quest.Id) or questName
|
||||||
|
end
|
||||||
|
|
||||||
|
if (not questName) or questName == "" then
|
||||||
|
questName = tostring(quest.Id)
|
||||||
|
end
|
||||||
|
|
||||||
|
return questName
|
||||||
|
end
|
||||||
|
|
||||||
local LSM30 = LibStub and LibStub("LibSharedMedia-3.0", true)
|
local LSM30 = LibStub and LibStub("LibSharedMedia-3.0", true)
|
||||||
|
|
||||||
-- Local Vars
|
-- Local Vars
|
||||||
@@ -1031,7 +1047,7 @@ function QuestieTracker:Update()
|
|||||||
|
|
||||||
if quest.isFallback or quest._isLogFallback then
|
if quest.isFallback or quest._isLogFallback then
|
||||||
-- Quest not in DB: use the name stored on the fallback object
|
-- Quest not in DB: use the name stored on the fallback object
|
||||||
local questName = quest.name or tostring(quest.Id)
|
local questName = GetDisplayableQuestName(quest)
|
||||||
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
||||||
questName = "[" .. quest.level .. "] " .. questName
|
questName = "[" .. quest.level .. "] " .. questName
|
||||||
end
|
end
|
||||||
@@ -1065,7 +1081,7 @@ function QuestieTracker:Update()
|
|||||||
-- which queries the static DB only: for a quest with no DB entry it
|
-- which queries the static DB only: for a quest with no DB entry it
|
||||||
-- silently defaults to level 1, and Ascension's scaling then scales
|
-- silently defaults to level 1, and Ascension's scaling then scales
|
||||||
-- that wrong base instead of the quest's real level.
|
-- that wrong base instead of the quest's real level.
|
||||||
local fallbackName = quest.name or tostring(quest.Id)
|
local fallbackName = GetDisplayableQuestName(quest)
|
||||||
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
||||||
fallbackName = QuestieLib:GetQuestString(quest.Id, fallbackName, quest.level, false)
|
fallbackName = QuestieLib:GetQuestString(quest.Id, fallbackName, quest.level, false)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -809,7 +809,13 @@ local function _GetZoneName(zoneOrSort, questId, zoneNameOverride)
|
|||||||
if zoneNameOverride and zoneNameOverride ~= "" then
|
if zoneNameOverride and zoneNameOverride ~= "" then
|
||||||
return zoneNameOverride
|
return zoneNameOverride
|
||||||
end
|
end
|
||||||
if not zoneOrSort then return "Unknown Zone" end
|
|
||||||
|
-- A quest assembled from a partial record -- a Learner entry that never captured the field,
|
||||||
|
-- an override carrying a single key -- reaches here with no zoneOrSort at all. That is the
|
||||||
|
-- same "nothing to look up" case as 0, and the quest log below still knows where the client
|
||||||
|
-- files the quest, so it must not short-circuit to Unknown Zone ahead of that.
|
||||||
|
zoneOrSort = zoneOrSort or 0
|
||||||
|
|
||||||
local zoneName
|
local zoneName
|
||||||
local sortObj = Questie.db.profile.trackerSortObjectives
|
local sortObj = Questie.db.profile.trackerSortObjectives
|
||||||
if sortObj == "byZone" or sortObj == "byZoneComplete" or sortObj == "byZoneCompleteReversed" or sortObj == "byZonePlayerProximity" or sortObj == "byZonePlayerProximityReversed" then
|
if sortObj == "byZone" or sortObj == "byZoneComplete" or sortObj == "byZoneCompleteReversed" or sortObj == "byZonePlayerProximity" or sortObj == "byZonePlayerProximityReversed" then
|
||||||
@@ -865,6 +871,22 @@ local function _GetZoneName(zoneOrSort, questId, zoneNameOverride)
|
|||||||
return zoneName
|
return zoneName
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- The client's own title for a quest, or nil if the player does not have it. Quest objects can
|
||||||
|
-- reach the tracker without a usable name -- a questDataOverrides entry that carries no name
|
||||||
|
-- field, a QuestLogCache row read before the client had filled the title in -- and since those
|
||||||
|
-- objects are cached for the session, the name never repairs itself. The log always knows.
|
||||||
|
function TrackerUtils:GetQuestLogTitleById(questId)
|
||||||
|
local questLogIndex = GetQuestLogIndexForQuest(questId)
|
||||||
|
if not questLogIndex then return nil end
|
||||||
|
|
||||||
|
local title = GetQuestLogTitle(questLogIndex)
|
||||||
|
if title and title ~= "" then
|
||||||
|
return title
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
-- IsComplete must be a method (called as quest:IsComplete()), and it reads the state the last
|
-- IsComplete must be a method (called as quest:IsComplete()), and it reads the state the last
|
||||||
-- refresh stored rather than closing over the state at build time -- these quests outlive many
|
-- refresh stored rather than closing over the state at build time -- these quests outlive many
|
||||||
-- redraws, and a captured value would still claim the quest is unfinished after it is turned in.
|
-- redraws, and a captured value would still claim the quest is unfinished after it is turned in.
|
||||||
|
|||||||
Reference in New Issue
Block a user