Fix tracker crash and killcredit NPC linking
This commit is contained in:
@@ -65,13 +65,46 @@ _QuestieQuest.objectiveSpawnListCallTable = {}
|
||||
killcredit = function(npcId, objective, objectiveData)
|
||||
---@type SpawnListNPC[]
|
||||
local ret = {}
|
||||
local foundValid = false
|
||||
|
||||
-- First pass: try all IDs in IdList
|
||||
for npcIdIndex = 1, #objectiveData.IdList do
|
||||
local killCreditNpcId = objectiveData.IdList[npcIdIndex]
|
||||
local monsterResult = monster(killCreditNpcId, objective)
|
||||
if monsterResult then
|
||||
ret[killCreditNpcId] = monsterResult[killCreditNpcId]
|
||||
if killCreditNpcId and killCreditNpcId > 0 then
|
||||
local monsterResult = monster(killCreditNpcId, objective)
|
||||
if monsterResult then
|
||||
ret[killCreditNpcId] = monsterResult[killCreditNpcId]
|
||||
foundValid = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Second pass: if no valid NPCs found, try name-based lookup from objective description
|
||||
-- This helps custom server quests where IdList may have 0 or wrong IDs
|
||||
if not foundValid and objective and (objective.Description or objective.text) then
|
||||
local desc = objective.Description or objective.text
|
||||
local targetName = desc:match("^%d+/%d+%s+(.+)$") or desc:match("^(.-):%s*%d+/%d+$")
|
||||
if not targetName then
|
||||
targetName = desc:gsub("%d+/%d+", ""):gsub("%d+", ""):gsub("[:!?,.%(%)]", ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
|
||||
if targetName and targetName ~= "" then
|
||||
-- Search for NPC by name using the npcData table
|
||||
local npcData = QuestieDB.npcData or {}
|
||||
for searchId, npcRecord in pairs(npcData) do
|
||||
if npcRecord and npcRecord[1] and string.lower(npcRecord[1]) == string.lower(targetName) then
|
||||
local monsterResult = monster(searchId, objective)
|
||||
if monsterResult then
|
||||
ret[searchId] = monsterResult[searchId]
|
||||
foundValid = true
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[killcredit] Found NPC by name fallback:", searchId, targetName)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
@@ -79,12 +112,8 @@ end
|
||||
---@param objective any
|
||||
---@return table<NpcId, SpawnListNPC>?
|
||||
monster = function(npcId, objective)
|
||||
if (not npcId) then
|
||||
Questie:Error(
|
||||
"Corrupted objective data handed to objectiveSpawnListCallTable['monster']:",
|
||||
"'" .. objective.Description .. "' -",
|
||||
"Please report this error on Discord or GitHub."
|
||||
)
|
||||
if (not npcId) or npcId <= 0 then
|
||||
Questie:Debug(Questie.DEBUG_CRITICAL, "Invalid NPC ID passed to monster function:", npcId)
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
+122
-4
@@ -1546,16 +1546,48 @@ function QuestieLearner:OnQuestAccepted(firstArg, secondArg)
|
||||
local numObj = GetNumQuestLeaderBoards and GetNumQuestLeaderBoards(logIdx) or 0
|
||||
for j = 1, numObj do
|
||||
local objText, objType, finished = GetQuestLogLeaderBoard(j, logIdx)
|
||||
if objText and not finished and objType == "monster" then
|
||||
-- Try to extract "Boar" from "0/10 Boar Slain"
|
||||
if objText and not finished and (objType == "monster" or objType == "killcredit") then
|
||||
local targetName = objText:match("^%d+/%d+%s+(.+)%s*") or objText:match("^(.+):%s*%d+/%d+")
|
||||
if not targetName then
|
||||
-- Fallback: strip everything that looks like a count or punctuation
|
||||
targetName = objText:gsub("%d+/%d+", ""):gsub("%d+", ""):gsub("[:!?,.%(%)]", ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
|
||||
if targetName and targetName ~= "" then
|
||||
local npcId = self:GetNPCIdByName(targetName)
|
||||
local npcId = nil
|
||||
-- For killcredit, try ID-based lookup first using quest objectives data
|
||||
if objType == "killcredit" then
|
||||
local quest = QuestieDB and QuestieDB.GetQuest and QuestieDB.GetQuest(questId)
|
||||
if quest and quest.ObjectiveData and quest.ObjectiveData[j] then
|
||||
local objData = quest.ObjectiveData[j]
|
||||
if objData.IdList then
|
||||
for _, possibleId in ipairs(objData.IdList) do
|
||||
if possibleId and possibleId > 0 then
|
||||
local npc = QuestieDB:GetNPC(possibleId)
|
||||
if npc and npc.name and string.lower(npc.name) == string.lower(targetName) then
|
||||
npcId = possibleId
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Fallback: try first valid ID in the list even if name doesn't match
|
||||
if not npcId then
|
||||
for _, possibleId in ipairs(objData.IdList) do
|
||||
if possibleId and possibleId > 0 then
|
||||
local npc = QuestieDB:GetNPC(possibleId)
|
||||
if npc then
|
||||
npcId = possibleId
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Fallback to name-based lookup
|
||||
if not npcId then
|
||||
npcId = self:GetNPCIdByName(targetName)
|
||||
end
|
||||
if npcId then
|
||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] Proactively mapped objective", j, "to NPC", npcId, "(" .. targetName .. ")")
|
||||
self:LearnQuestObjectiveNPC(questId, npcId, objText, j)
|
||||
@@ -1953,9 +1985,95 @@ function QuestieLearner:Initialize()
|
||||
self:PruneGuidNpcCache()
|
||||
end)
|
||||
|
||||
-- Scan existing quests in log after initialization (deferred to ensure DB is ready)
|
||||
QuestieCompat.C_Timer.After(1, function()
|
||||
self:ScanExistingQuestLog()
|
||||
end)
|
||||
|
||||
Questie:Debug(Questie.DEBUG_INFO, "[QuestieLearner] Initialized")
|
||||
end
|
||||
|
||||
function QuestieLearner:ScanExistingQuestLog()
|
||||
if not self:IsEnabled() then return end
|
||||
if not Questie.dbLearner.global.settings.learnQuests then return end
|
||||
if not GetNumQuestLogEntries then return end
|
||||
|
||||
Questie:Debug(Questie.DEBUG_DEVELOP, "[QuestieLearner] Scanning existing quest log...")
|
||||
local count = 0
|
||||
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local title, level, _, isHeader, _, _, _, questId = QuestieCompat.GetQuestLogTitle(i)
|
||||
if not isHeader and questId and questId > 0 then
|
||||
-- Check if this quest needs objective mapping
|
||||
local existingData = Questie.dbLearner.global.quests[questId]
|
||||
local needsMapping = not existingData or not existingData.objIndex or not next(existingData.objIndex)
|
||||
|
||||
if needsMapping then
|
||||
-- Use the existing OnQuestAccepted logic by manually triggering objective mapping
|
||||
local logIdx = i
|
||||
local numObj = GetNumQuestLeaderBoards and GetNumQuestLeaderBoards(logIdx) or 0
|
||||
for j = 1, numObj do
|
||||
local objText, objType, finished = GetQuestLogLeaderBoard(j, logIdx)
|
||||
if objText and not finished and (objType == "monster" or objType == "killcredit") then
|
||||
local targetName = objText:match("^%d+/%d+%s+(.+)%s*") or objText:match("^(.+):%s*%d+/%d+")
|
||||
if not targetName then
|
||||
targetName = objText:gsub("%d+/%d+", ""):gsub("%d+", ""):gsub("[:!?,.%(%)]", ""):gsub("^%s+", ""):gsub("%s+$", "")
|
||||
end
|
||||
|
||||
if targetName and targetName ~= "" then
|
||||
local npcId = nil
|
||||
-- For killcredit, try ID-based lookup first
|
||||
if objType == "killcredit" then
|
||||
local quest = QuestieDB and QuestieDB.GetQuest and QuestieDB.GetQuest(questId)
|
||||
if quest and quest.ObjectiveData and quest.ObjectiveData[j] then
|
||||
local objData = quest.ObjectiveData[j]
|
||||
if objData.IdList then
|
||||
for _, possibleId in ipairs(objData.IdList) do
|
||||
if possibleId and possibleId > 0 then
|
||||
local npc = QuestieDB:GetNPC(possibleId)
|
||||
if npc and npc.name and string.lower(npc.name) == string.lower(targetName) then
|
||||
npcId = possibleId
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Fallback: try first valid ID in the list
|
||||
if not npcId then
|
||||
for _, possibleId in ipairs(objData.IdList) do
|
||||
if possibleId and possibleId > 0 then
|
||||
local npc = QuestieDB:GetNPC(possibleId)
|
||||
if npc then
|
||||
npcId = possibleId
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Fallback to name-based lookup
|
||||
if not npcId then
|
||||
npcId = self:GetNPCIdByName(targetName)
|
||||
end
|
||||
if npcId then
|
||||
Questie:Debug(Questie.DEBUG_LEARNER, "[QuestieLearner] Scanned existing quest", questId, "objective", j, "to NPC", npcId, "(" .. targetName .. ")")
|
||||
self:LearnQuestObjectiveNPC(questId, npcId, objText, j)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if count > 0 then
|
||||
self:InjectLearnedData()
|
||||
Questie:Debug(Questie.DEBUG_INFO, "[QuestieLearner] Scanned existing quest log, mapped", count, "objectives")
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------------------------------------
|
||||
-- Network bridge
|
||||
------------------------------------------------------------------------
|
||||
|
||||
@@ -1230,7 +1230,7 @@ function QuestieTracker:Update()
|
||||
-- Add incomplete Quest Objectives
|
||||
if complete == 0 and quest.isComplete ~= true then
|
||||
for _, objective in pairs(quest.Objectives) do
|
||||
if (not Questie.db.profile.hideCompletedQuestObjectives or (Questie.db.profile.hideCompletedQuestObjectives and objective.Needed ~= objective.Collected)) then
|
||||
if objective and (not Questie.db.profile.hideCompletedQuestObjectives or (Questie.db.profile.hideCompletedQuestObjectives and objective.Needed ~= objective.Collected)) then
|
||||
-- Get next line in linePool
|
||||
line = TrackerLinePool.GetNextLine()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user