feat: release v1.3.0 - QuestieLearner Confidence & Tiered Pruning Engine
This commit is contained in:
@@ -11,7 +11,16 @@ local AceComm = LibStub("AceComm-3.0")
|
||||
|
||||
local addonPrefix = "QuestieLearner"
|
||||
local hiddenChannelName = "questiecomm"
|
||||
local ProtocolVersion = 1
|
||||
local ProtocolVersion = 2 -- Increment protocol version for enhanced/sanitized data
|
||||
|
||||
local time = time
|
||||
local GetTime = GetTime
|
||||
local math_min = math.min
|
||||
local math_floor = math.floor
|
||||
local math_random = math.random
|
||||
local table_insert = table.insert
|
||||
local table_remove = table.remove
|
||||
local table_getn = table.getn
|
||||
|
||||
-- Dev Logging Flags — defined first so all functions below can call DebugLog
|
||||
local LOG_CRITICAL = true
|
||||
@@ -123,10 +132,10 @@ function QuestieLearnerComms:Initialize()
|
||||
end
|
||||
|
||||
-- Process incoming/outgoing queues
|
||||
C_Timer.NewTicker(0.2, function() _QuestieLearnerComms:ProcessQueues() end)
|
||||
QuestieCompat.C_Timer.NewTicker(0.2, function() _QuestieLearnerComms:ProcessQueues() end)
|
||||
|
||||
-- Start Reinforcement Loop (every 60 seconds)
|
||||
C_Timer.NewTicker(60, function() _QuestieLearnerComms:ProcessReinforcement() end)
|
||||
QuestieCompat.C_Timer.NewTicker(60, function() _QuestieLearnerComms:ProcessReinforcement() end)
|
||||
end
|
||||
|
||||
function _QuestieLearnerComms:ProcessReinforcement()
|
||||
@@ -270,5 +279,5 @@ function _QuestieLearnerComms:ProcessRawMessage(encodedMsg, sender)
|
||||
|
||||
DebugLog("DEVELOP", "Received " .. tostring(op) .. " " .. tostring(typ) .. " " .. tostring(id) .. " from " .. tostring(sender))
|
||||
|
||||
QuestieLearner:HandleNetworkData(typ, id, d)
|
||||
QuestieLearner:HandleNetworkData(typ, id, d, op)
|
||||
end
|
||||
|
||||
@@ -421,19 +421,7 @@ function QuestieOptions.tabs.advanced:Initialize()
|
||||
order = 6,
|
||||
name = l10n('3.3.5 Compatibility Settings'),
|
||||
},
|
||||
plugin_header = {
|
||||
type = "header",
|
||||
order = 7,
|
||||
name = "|cFF5EBAF3Loaded Questie-X Plugins|r",
|
||||
},
|
||||
plugin_status_desc = {
|
||||
type = "description",
|
||||
order = 7.01,
|
||||
fontSize = "medium",
|
||||
name = function()
|
||||
return "|cFF888888Plugin stats have moved to the |r|cFFFFFFFFDatabase|r|cFF888888 tab.|r"
|
||||
end,
|
||||
},
|
||||
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
@@ -366,9 +366,34 @@ function QuestieOptions.tabs.database:Initialize()
|
||||
end,
|
||||
},
|
||||
|
||||
stale_threshold = {
|
||||
type = "range",
|
||||
order = 5.2,
|
||||
name = function() return l10n("Stale Data Threshold (Days)") end,
|
||||
desc = function() return l10n("Unconfirmed learned data (seen only once) will be pruned if it hasn't been seen in this many days. Verified data is permanent.") end,
|
||||
min = 1,
|
||||
max = 180,
|
||||
step = 1,
|
||||
get = function() return (Questie.db.global.learnedData.settings and Questie.db.global.learnedData.settings.staleThreshold) or 90 end,
|
||||
set = function(_, val)
|
||||
Questie.db.global.learnedData.settings.staleThreshold = val
|
||||
end,
|
||||
},
|
||||
|
||||
prune_verified = {
|
||||
type = "toggle",
|
||||
order = 5.3,
|
||||
name = function() return l10n("Include Verified Data in Pruning") end,
|
||||
desc = function() return l10n("If enabled, even high-confidence (Verified) data will be subject to redundancy pruning (e.g., if it's already in the official DB). Time-based pruning still only affects unconfirmed data.") end,
|
||||
get = function() return (Questie.db.global.learnedData.settings and Questie.db.global.learnedData.settings.pruneVerified) or false end,
|
||||
set = function(_, val)
|
||||
Questie.db.global.learnedData.settings.pruneVerified = val
|
||||
end,
|
||||
},
|
||||
|
||||
prune_dry_btn = {
|
||||
type = "execute",
|
||||
order = 5.2,
|
||||
order = 5.4,
|
||||
name = function() return l10n("Dry Run (Preview)") end,
|
||||
desc = function() return "Print a summary of entries that would be removed, without deleting anything." end,
|
||||
func = function()
|
||||
@@ -384,10 +409,10 @@ function QuestieOptions.tabs.database:Initialize()
|
||||
end
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
prune_btn = {
|
||||
type = "execute",
|
||||
order = 5.3,
|
||||
order = 5.5,
|
||||
name = function() return l10n("Prune Now") end,
|
||||
desc = function() return "|cFFFF8800Removes stale entries. Cannot be undone. Export first if you want a backup.|r" end,
|
||||
func = function()
|
||||
@@ -400,10 +425,10 @@ function QuestieOptions.tabs.database:Initialize()
|
||||
))
|
||||
end,
|
||||
},
|
||||
|
||||
|
||||
prune_all_btn = {
|
||||
type = "execute",
|
||||
order = 5.4,
|
||||
order = 5.6,
|
||||
name = function() return "|cFFFF4444" .. l10n("Reset All Learned Data") .. "|r" end,
|
||||
desc = function() return "|cFFFF0000DANGER: Wipes ALL learned data for ALL servers. Export first.|r" end,
|
||||
confirm = true,
|
||||
|
||||
@@ -985,22 +985,27 @@ function QuestieQuest:UpdateObjectiveNotes(quest)
|
||||
|
||||
Questie:Debug(Questie.DEBUG_INFO, "[QuestieQuest] UpdateObjectiveNotes:", quest.Id)
|
||||
for objectiveIndex, objective in pairs(quest.Objectives) do
|
||||
local result, err = xpcall(QuestieQuest.PopulateObjective, ERR_FUNCTION, QuestieQuest, quest, objectiveIndex,
|
||||
objective, false)
|
||||
if (not result) then
|
||||
Questie:Debug(Questie.DEBUG_ELEVATED, "[QuestieQuest] There was an error populating objectives for",
|
||||
quest.name, quest.Id, objectiveIndex, err)
|
||||
-- Skip tracker-only fallback objectives — they have no DB Id and can't be populated
|
||||
if objective.Type ~= "fallback" then
|
||||
local result, err = xpcall(QuestieQuest.PopulateObjective, ERR_FUNCTION, QuestieQuest, quest, objectiveIndex,
|
||||
objective, false)
|
||||
if (not result) then
|
||||
Questie:Debug(Questie.DEBUG_ELEVATED, "[QuestieQuest] There was an error populating objectives for",
|
||||
quest.name, quest.Id, objectiveIndex, err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if quest.SpecialObjectives and next(quest.SpecialObjectives) then
|
||||
for _, objective in pairs(quest.SpecialObjectives) do
|
||||
local result, err = xpcall(QuestieQuest.PopulateObjective, ERR_FUNCTION, QuestieQuest, quest, 0, objective,
|
||||
true)
|
||||
if not result then
|
||||
Questie:Error("[QuestieQuest]: [SpecialObjectives] " ..
|
||||
l10n("There was an error populating objectives for %s %s %s %s", quest.name or "No quest name",
|
||||
quest.Id or "No quest id", 0 or "No objective", err or "No error"));
|
||||
if objective.Type ~= "fallback" then
|
||||
local result, err = xpcall(QuestieQuest.PopulateObjective, ERR_FUNCTION, QuestieQuest, quest, 0, objective,
|
||||
true)
|
||||
if not result then
|
||||
Questie:Error("[QuestieQuest]: [SpecialObjectives] " ..
|
||||
l10n("There was an error populating objectives for %s %s %s %s", quest.name or "No quest name",
|
||||
quest.Id or "No quest id", 0 or "No objective", err or "No error"));
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1318,6 +1323,26 @@ function QuestieQuest:PopulateObjective(quest, objectiveIndex, objective, blockI
|
||||
objectiveCenter = { x = x, y = y }
|
||||
end
|
||||
|
||||
-- Filter static spawns if prioritizeMyData is enabled and we have high-confidence learned data
|
||||
if Questie.db.global.learnedData and Questie.db.global.learnedData.settings and Questie.db.global.learnedData.settings.prioritizeMyData then
|
||||
for zone in pairs(zones) do
|
||||
local suppressed = (objectiveData.Type == "monster" and QuestieDB.GetSuppressedNPCs(zone)) or (objectiveData.Type == "object" and QuestieDB.GetSuppressedObjects(zone))
|
||||
if suppressed then
|
||||
for id, spawnData in pairs(objective.spawnList) do
|
||||
if suppressed[id] and spawnData.Spawns and spawnData.Spawns[zone] then
|
||||
-- Only suppress if this isn't a learned spawn (learned spawns have .isLearned)
|
||||
if not spawnData.isLearned then
|
||||
spawnData.Spawns[zone] = nil
|
||||
if not next(spawnData.Spawns) then
|
||||
objective.spawnList[id] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local iconsToDraw, _ = _DetermineIconsToDraw(quest, objective, objectiveIndex, objectiveCenter)
|
||||
local icon, iconPerZone = _DrawObjectiveIcons(quest.Id, iconsToDraw, objective, maxPerType)
|
||||
_DrawObjectiveWaypoints(objective, icon, iconPerZone)
|
||||
@@ -1341,7 +1366,7 @@ _RegisterObjectiveTooltips = function(objective, questId, blockItemTooltips)
|
||||
-- No spawnList and no Id means there is nothing Questie can draw for this objective.
|
||||
-- This covers server-tracked trigger objectives (e.g. "complete N quests in zone" for
|
||||
-- quest 50150) which may have any objectiveType from the server, not just "event".
|
||||
if not objective.Id then
|
||||
if not objective.Id or objective.Id == 0 then
|
||||
objective.hasRegisteredTooltips = true
|
||||
return
|
||||
end
|
||||
|
||||
+1005
-80
File diff suppressed because it is too large
Load Diff
@@ -319,42 +319,73 @@ local QuestieDB -- lazily imported to avoid circular dep
|
||||
|
||||
function _Export:RunPrune(dryRun)
|
||||
if not QuestieDB then QuestieDB = QuestieLoader:ImportModule("QuestieDB") end
|
||||
|
||||
|
||||
local serverKey = GetServerKey()
|
||||
local bucket = GetServerBucket(serverKey)
|
||||
|
||||
|
||||
local result = { npcs = 0, quests = 0, items = 0, objects = 0, total = 0, reasons = {} }
|
||||
if not bucket then return result end
|
||||
|
||||
|
||||
local settings = (Questie.db.global.learnedData and Questie.db.global.learnedData.settings) or {}
|
||||
local thresholdDays = settings.staleThreshold or 90
|
||||
local thresholdSeconds = thresholdDays * 86400
|
||||
local minConfidence = settings.minConfidencePins or 2
|
||||
local pruneVerified = settings.pruneVerified
|
||||
local now = time()
|
||||
|
||||
local function ShouldPruneNPC(id, entry)
|
||||
if CountTable(entry) == 0 then return "empty entry" end
|
||||
if (entry.mc or 0) < 2 and not entry[7] then return "unverified with no coords" end
|
||||
local isVerified = (entry.mc or 0) >= minConfidence
|
||||
if (not isVerified) and (now - (entry.ls or 0)) > thresholdSeconds then
|
||||
return "unconfirmed and stale (> " .. thresholdDays .. " days)"
|
||||
end
|
||||
if pruneVerified or not isVerified then
|
||||
if (entry.mc or 0) < 2 and not entry[7] then return "unverified with no coords" end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function ShouldPruneQuest(id, entry)
|
||||
if CountTable(entry) == 0 then return "empty entry" end
|
||||
if QuestieDB and QuestieDB.GetQuest then
|
||||
local dbEntry = QuestieDB:GetQuest(id)
|
||||
if dbEntry and (entry.mc or 0) < 2 then
|
||||
return "fully covered by official DB, mc < 2"
|
||||
local isVerified = (entry.mc or 0) >= minConfidence
|
||||
if (not isVerified) and (now - (entry.ls or 0)) > thresholdSeconds then
|
||||
return "unconfirmed and stale (> " .. thresholdDays .. " days)"
|
||||
end
|
||||
if pruneVerified or not isVerified then
|
||||
if QuestieDB and QuestieDB.GetQuest then
|
||||
local dbEntry = QuestieDB.GetQuest(id)
|
||||
if dbEntry and (entry.mc or 0) < 2 then
|
||||
return "fully covered by official DB, mc < 2"
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function ShouldPruneItem(id, entry)
|
||||
if CountTable(entry) == 0 then return "empty entry" end
|
||||
if (entry.mc or 0) < 1 then return "zero match count" end
|
||||
local isVerified = (entry.mc or 0) >= minConfidence
|
||||
if (not isVerified) and (now - (entry.ls or 0)) > thresholdSeconds then
|
||||
return "unconfirmed and stale (> " .. thresholdDays .. " days)"
|
||||
end
|
||||
if pruneVerified or not isVerified then
|
||||
if (entry.mc or 0) < 1 then return "zero match count" end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function ShouldPruneObject(id, entry)
|
||||
if CountTable(entry) == 0 then return "empty entry" end
|
||||
if (entry.mc or 0) < 2 and not entry[4] then return "unverified with no coords" end
|
||||
local isVerified = (entry.mc or 0) >= minConfidence
|
||||
if (not isVerified) and (now - (entry.ls or 0)) > thresholdSeconds then
|
||||
return "unconfirmed and stale (> " .. thresholdDays .. " days)"
|
||||
end
|
||||
if pruneVerified or not isVerified then
|
||||
if (entry.mc or 0) < 2 and not entry[4] then return "unverified with no coords" end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function PruneStore(store, checkFn, typeName)
|
||||
if not store then return end
|
||||
for id, entry in pairs(store) do
|
||||
@@ -371,11 +402,11 @@ function _Export:RunPrune(dryRun)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
PruneStore(bucket.npcs, ShouldPruneNPC, "npcs")
|
||||
PruneStore(bucket.quests, ShouldPruneQuest, "quests")
|
||||
PruneStore(bucket.items, ShouldPruneItem, "items")
|
||||
PruneStore(bucket.objects, ShouldPruneObject, "objects")
|
||||
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -219,6 +219,64 @@ function QuestieTooltips:GetTooltip(key)
|
||||
local tooltipData = {}
|
||||
local tooltipLines = {}
|
||||
|
||||
if (not QuestieTooltips.lookupByKey[key]) then
|
||||
local QuestieLearner = QuestieLoader:ImportModule("QuestieLearner")
|
||||
local QuestLogCache = QuestieLoader:ImportModule("QuestLogCache")
|
||||
if QuestieLearner and QuestieLearner.data then
|
||||
-- Try to find in learned NPCs or objects
|
||||
local id = tonumber(key:sub(3))
|
||||
if id then
|
||||
if key:sub(1,2) == "m_" then
|
||||
local learnedNpc = QuestieLearner.data.npcs[id]
|
||||
if learnedNpc and learnedNpc[10] then -- check questObjectives
|
||||
for questId, objList in pairs(learnedNpc[10]) do
|
||||
for _, objText in ipairs(objList) do
|
||||
local needed, collected
|
||||
local objectives = QuestLogCache.GetQuestObjectives(questId)
|
||||
if objectives then
|
||||
for _, obj in pairs(objectives) do
|
||||
if obj.text and objText and (obj.text == objText or string.find(obj.text, objText, 1, true) or string.find(objText, obj.text, 1, true)) then
|
||||
needed = obj.numRequired
|
||||
collected = obj.numFulfilled
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
QuestieTooltips:RegisterObjectiveTooltip(questId, key, { Index = 0, Description = objText, Needed = needed, Collected = collected, Update = function() end })
|
||||
end
|
||||
end
|
||||
if learnedNpc.mc then
|
||||
tinsert(tooltipLines, "|cFF5EBAF3(Learned - Confidence: " .. tostring(learnedNpc.mc) .. ")|r")
|
||||
end
|
||||
end
|
||||
elseif key:sub(1,2) == "o_" then
|
||||
local learnedObj = QuestieLearner.data.objects[id]
|
||||
if learnedObj and learnedObj[10] then
|
||||
for questId, objList in pairs(learnedObj[10]) do
|
||||
for _, objText in ipairs(objList) do
|
||||
local needed, collected
|
||||
local objectives = QuestLogCache.GetQuestObjectives(questId)
|
||||
if objectives then
|
||||
for _, obj in pairs(objectives) do
|
||||
if obj.text and objText and (obj.text == objText or string.find(obj.text, objText, 1, true) or string.find(objText, obj.text, 1, true)) then
|
||||
needed = obj.numRequired
|
||||
collected = obj.numFulfilled
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
QuestieTooltips:RegisterObjectiveTooltip(questId, key, { Index = 0, Description = objText, Needed = needed, Collected = collected, Update = function() end })
|
||||
end
|
||||
end
|
||||
if learnedObj.mc then
|
||||
tinsert(tooltipLines, "|cFF5EBAF3(Learned - Confidence: " .. tostring(learnedObj.mc) .. ")|r")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if QuestieTooltips.lookupByKey[key] then
|
||||
local playerName = UnitName("player")
|
||||
for k, tooltip in pairs(QuestieTooltips.lookupByKey[key]) do
|
||||
@@ -229,7 +287,7 @@ function QuestieTooltips:GetTooltip(key)
|
||||
end
|
||||
else
|
||||
local objective = tooltip.objective
|
||||
if not (objective.IsSourceItem or objective.IsRequiredSourceItem) then
|
||||
if objective and not (objective.IsSourceItem or objective.IsRequiredSourceItem) and objective.Update then
|
||||
-- Tooltip was registered for a sourceItem or requiredSourceItem and not a real "objective"
|
||||
objective:Update()
|
||||
end
|
||||
|
||||
@@ -914,7 +914,17 @@ function QuestieTracker:Update()
|
||||
-- Set Quest Title - This handles the "Auto Minimize Completed Quests" option but we don't auto-minimize timed quests.
|
||||
local coloredQuestName
|
||||
|
||||
if timedQuest then
|
||||
if quest.isFallback or quest._isLogFallback then
|
||||
-- Quest not in DB: use the name stored on the fallback object
|
||||
local questName = quest.name or tostring(quest.Id)
|
||||
if Questie.db.profile.trackerShowQuestLevel and quest.level and quest.level > 0 then
|
||||
questName = "[" .. quest.level .. "] " .. questName
|
||||
end
|
||||
if Questie.db.profile.enableTooltipsQuestID then
|
||||
questName = questName .. " (" .. quest.Id .. ")"
|
||||
end
|
||||
coloredQuestName = "|cFFFFFF00" .. questName .. "|r"
|
||||
elseif timedQuest then
|
||||
coloredQuestName = QuestieLib:GetColoredQuestName(quest.Id,
|
||||
Questie.db.profile.trackerShowQuestLevel, false, false)
|
||||
else
|
||||
@@ -2290,10 +2300,15 @@ end
|
||||
|
||||
function QuestieTracker:UntrackQuestId(questId)
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieTracker:UntrackQuestId] - ", questId)
|
||||
-- Always remove from tracked quests when manually untracking
|
||||
Questie.db.char.TrackedQuests[questId] = nil
|
||||
-- Also remove from auto-untracked so it doesn't get re-tracked
|
||||
Questie.db.char.AutoUntrackedQuests[questId] = nil
|
||||
if Questie.db.profile.autoTrackQuests then
|
||||
-- In auto-track mode, mark the quest as explicitly hidden
|
||||
Questie.db.char.AutoUntrackedQuests[questId] = true
|
||||
Questie.db.char.TrackedQuests[questId] = nil
|
||||
else
|
||||
-- In manual-track mode, remove from tracked list
|
||||
Questie.db.char.TrackedQuests[questId] = nil
|
||||
Questie.db.char.AutoUntrackedQuests[questId] = nil
|
||||
end
|
||||
|
||||
if Questie.db.profile.hideUntrackedQuestsMapIcons then
|
||||
-- Hides objective icons for untracked quests.
|
||||
@@ -2346,11 +2361,8 @@ function QuestieTracker:AQW_Insert(index, expire)
|
||||
end
|
||||
else
|
||||
if Questie.db.char.AutoUntrackedQuests[questId] then
|
||||
-- Quest was manually hidden — shift-click re-tracks it
|
||||
Questie.db.char.AutoUntrackedQuests[questId] = nil
|
||||
|
||||
-- Add quest to the tracker
|
||||
elseif IsShiftKeyDown() and QuestLogFrame:IsShown() then
|
||||
Questie.db.char.AutoUntrackedQuests[questId] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -698,6 +698,41 @@ end
|
||||
-- Intentionally NOT stored in QuestiePlayer.currentQuestlog so arrow/map/other modules
|
||||
-- don't try to call DB-only methods on them.
|
||||
TrackerUtils._fallbackQuests = TrackerUtils._fallbackQuests or {}
|
||||
|
||||
-- Reverse-lookup: given a localized zone name string, find the area ID from l10n.zoneLookup.
|
||||
local function GetAreaIdByZoneName(zoneName)
|
||||
if not zoneName or zoneName == "" then return 0 end
|
||||
for _, zoneTable in pairs(l10n.zoneLookup) do
|
||||
for areaId, name in pairs(zoneTable) do
|
||||
if name == zoneName then return areaId end
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Walk the quest log to find the zone header for a given questId.
|
||||
-- In 3.3.5, zone names appear as isHeader=true entries above their quests.
|
||||
-- Returns the header title string, or nil if not found.
|
||||
local function GetQuestLogZoneName(questId)
|
||||
local targetIndex = nil
|
||||
local total = GetNumQuestLogEntries and GetNumQuestLogEntries() or 0
|
||||
for i = 1, total do
|
||||
local _, _, _, isHeader, _, _, _, logId = GetQuestLogTitle(i)
|
||||
if not isHeader and logId == questId then
|
||||
targetIndex = i
|
||||
break
|
||||
end
|
||||
end
|
||||
if not targetIndex then return nil end
|
||||
for i = targetIndex, 1, -1 do
|
||||
local title, _, _, isHeader = GetQuestLogTitle(i)
|
||||
if isHeader and title and title ~= "" then
|
||||
return title
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Returns nil if the quest is not currently in the quest log.
|
||||
function TrackerUtils:BuildFallbackQuest(questId)
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
@@ -723,13 +758,24 @@ function TrackerUtils:BuildFallbackQuest(questId)
|
||||
end
|
||||
end
|
||||
|
||||
local zoneId = GetCurrentMapAreaID and GetCurrentMapAreaID() or 0
|
||||
-- Walk backwards from i in the quest log to find the zone header.
|
||||
-- This is the canonical 3.3.5 method: zone headers sit above their quests.
|
||||
local zoneText = nil
|
||||
for h = i, 1, -1 do
|
||||
local hTitle, _, _, hIsHeader = GetQuestLogTitle(h)
|
||||
if hIsHeader and hTitle and hTitle ~= "" then
|
||||
zoneText = hTitle
|
||||
break
|
||||
end
|
||||
end
|
||||
local zoneId = (zoneText and GetAreaIdByZoneName(zoneText)) or 0
|
||||
|
||||
local quest = {
|
||||
Id = questId,
|
||||
name = title or ("Quest " .. questId),
|
||||
level = level or 0,
|
||||
zoneOrSort = zoneId,
|
||||
zoneName = zoneText,
|
||||
Objectives = objectives,
|
||||
SpecialObjectives = {},
|
||||
isFallback = true,
|
||||
@@ -774,7 +820,7 @@ function TrackerUtils:GetSortedQuestIds()
|
||||
local capturedId = qid
|
||||
quest.IsComplete = function(self)
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local _, _, _, _, isHeader, _, isCompleteFlag, _, logId = GetQuestLogTitle(i)
|
||||
local _, _, _, isHeader, _, isCompleteFlag, _, logId = GetQuestLogTitle(i)
|
||||
if not isHeader and logId == capturedId then
|
||||
return (isCompleteFlag == 1 or isCompleteFlag == true) and 1 or 0
|
||||
end
|
||||
@@ -784,10 +830,23 @@ function TrackerUtils:GetSortedQuestIds()
|
||||
if not quest.Objectives then quest.Objectives = {} end
|
||||
if not quest.SpecialObjectives then quest.SpecialObjectives = {} end
|
||||
if not quest.ExtraObjectives then quest.ExtraObjectives = {} end
|
||||
-- Use the quest log header walk (canonical 3.3.5 zone resolution)
|
||||
if not quest.zoneName or quest.zoneName == "" then
|
||||
local logZone = GetQuestLogZoneName(capturedId)
|
||||
if logZone then
|
||||
quest.zoneName = logZone
|
||||
quest.zoneOrSort = GetAreaIdByZoneName(logZone) or 0
|
||||
end
|
||||
end
|
||||
QuestiePlayer.currentQuestlog[qid] = quest
|
||||
else
|
||||
-- No object at all — build one from the log
|
||||
local fallback = TrackerUtils._fallbackQuests[qid]
|
||||
-- Re-build if cached without zone info (e.g. was built before log was ready)
|
||||
if fallback and not fallback.zoneName then
|
||||
TrackerUtils._fallbackQuests[qid] = nil
|
||||
fallback = nil
|
||||
end
|
||||
if not fallback then
|
||||
fallback = TrackerUtils:BuildFallbackQuest(qid)
|
||||
if fallback then
|
||||
@@ -807,7 +866,7 @@ function TrackerUtils:GetSortedQuestIds()
|
||||
-- Create questDetails table keys and insert values
|
||||
questDetails[qid] = {}
|
||||
questDetails[qid].quest = quest
|
||||
questDetails[qid].zoneName = _GetZoneName(quest.zoneOrSort, qid)
|
||||
questDetails[qid].zoneName = quest.zoneName or _GetZoneName(quest.zoneOrSort, qid)
|
||||
|
||||
if quest:IsComplete() == 1 or (not next(quest.Objectives)) then
|
||||
questDetails[qid].questCompletePercent = 1
|
||||
|
||||
Reference in New Issue
Block a user