fix: gate learner item learning to quest items

This commit is contained in:
Xurkon
2026-06-06 10:21:36 -05:00
parent ada40add8b
commit ccdfa525cf
3 changed files with 142 additions and 20 deletions
+97 -7
View File
@@ -1195,12 +1195,21 @@ local function CrossLinkAfterQuest(questId)
if qData[11] and qData[11] > 0 then
local iData = learned.items[qData[11]]
if iData then
iData.questRelevant = true
if not iData[5] then
iData[5] = questId
if liveEnabled and QuestieDB and QuestieDB.itemDataOverrides then
local ovr = QuestieDB.itemDataOverrides[qData[11]] or {}
QuestieDB.itemDataOverrides[qData[11]] = ovr
if ovr[1] == nil and iData[1] ~= nil and not IsAscensionProtected("ITEM", qData[11], 1) then
ovr[1] = iData[1]
end
if not ovr[5] then ovr[5] = questId end
for k, v in pairs(iData) do
if k ~= 1 and k ~= 5 and k ~= "mc" and ovr[k] == nil and not IsAscensionProtected("ITEM", qData[11], k) then
ovr[k] = v
end
end
end
end
end
@@ -1212,6 +1221,7 @@ local function CrossLinkAfterQuest(questId)
local itemId = entry[1]
local iData = learned.items[itemId]
if iData and iData[2] then
iData.questRelevant = true
for _, dropNpcId in ipairs(iData[2]) do
_AddToQuestObjective(qData, 1, dropNpcId, nil, qOvr, questId)
end
@@ -2097,11 +2107,81 @@ end
-- Item learning
------------------------------------------------------------------------
local function IsQuestRelevantItem(itemId, itemClass)
local learned = Questie and Questie.dbLearner and Questie.dbLearner.global
if not learned or not learned.quests then
return false
end
if itemClass == 12 then
return true
end
for _, qData in pairs(learned.quests) do
if qData[11] == itemId then
return true
end
if qData[10] and qData[10][3] then
for _, entry in ipairs(qData[10][3]) do
if entry[1] == itemId then
return true
end
end
end
if qData[2] and qData[2][3] then
for _, entry in ipairs(qData[2][3]) do
if entry[1] == itemId then
return true
end
end
end
end
return false
end
local function HasQuestReferences(itemId)
local learned = Questie and Questie.dbLearner and Questie.dbLearner.global
if not learned or not learned.quests then
return false
end
local itemData = learned.items and learned.items[itemId]
if itemData and itemData.questRelevant then
return true
end
for _, qData in pairs(learned.quests) do
if qData[11] == itemId then
return true
end
if qData[10] and qData[10][3] then
for _, entry in ipairs(qData[10][3]) do
if entry[1] == itemId then
return true
end
end
end
if qData[2] and qData[2][3] then
for _, entry in ipairs(qData[2][3]) do
if entry[1] == itemId then
return true
end
end
end
end
return false
end
function QuestieLearner:LearnItem(itemId, name, itemLevel, requiredLevel, itemClass, itemSubClass)
if not self:IsEnabled() then return end
if not Questie.dbLearner.global.settings.learnItems then return end
itemId = tonumber(itemId)
if not itemId or itemId <= 0 then return end
if not IsQuestRelevantItem(itemId, itemClass) then
return false
end
local existing = Questie.dbLearner.global.items[itemId]
local isNew = existing == nil
@@ -2110,6 +2190,10 @@ function QuestieLearner:LearnItem(itemId, name, itemLevel, requiredLevel, itemCl
Questie.dbLearner.global.items[itemId] = existing
end
if itemClass == 12 or HasQuestReferences(itemId) then
existing.questRelevant = true
end
if name and not existing[1] then existing[1] = name end
if itemLevel and itemLevel > 0 and not existing[9] then existing[9] = itemLevel end
if requiredLevel and requiredLevel > 0 and not existing[10] then existing[10] = requiredLevel end
@@ -2138,6 +2222,7 @@ function QuestieLearner:LearnItem(itemId, name, itemLevel, requiredLevel, itemCl
CrossLinkAfterItem(itemId)
end
_Learner:BroadcastIfCommsAvailable("ITEM", itemId, existing)
return true
end
function QuestieLearner:LearnItemDrop(itemId, npcId)
@@ -2713,9 +2798,11 @@ function QuestieLearner:InjectLearnedData()
if type(itemId) == "string" and iid then
itemIdsToFix[itemId] = iid
end
if not QuestieDB.itemDataOverrides[iid or itemId] then
QuestieDB.itemDataOverrides[iid or itemId] = data
itemCount = itemCount + 1
if HasQuestReferences(iid or itemId) then
if not QuestieDB.itemDataOverrides[iid or itemId] then
QuestieDB.itemDataOverrides[iid or itemId] = data
itemCount = itemCount + 1
end
end
end
for old, new in pairs(itemIdsToFix) do
@@ -3474,8 +3561,8 @@ function QuestieLearner:OnLootOpened()
if itemId and itemId > 0 then
local itemName, _, _, itemLevel, requiredLevel, _, _, _, _, _, _, itemClassId, itemSubClassId = GetItemInfo(link)
if itemName then
self:LearnItem(itemId, itemName, itemLevel, requiredLevel, itemClassId, itemSubClassId)
if npcId then self:LearnItemDrop(itemId, npcId) end
local learnedItem = self:LearnItem(itemId, itemName, itemLevel, requiredLevel, itemClassId, itemSubClassId)
if learnedItem and npcId then self:LearnItemDrop(itemId, npcId) end
else
-- GetItemInfo returned nil; queue for retry (class check happens on retry)
table.insert(_Learner.pendingItemLinks, { link = link, itemId = itemId, npcId = npcId })
@@ -3571,8 +3658,8 @@ function QuestieLearner:OnGetItemInfoReceived(itemId)
if entry.itemId == itemId then
local itemName, _, _, itemLevel, requiredLevel, _, _, _, _, _, _, itemClassId, itemSubClassId = GetItemInfo(entry.link)
if itemName then
self:LearnItem(itemId, itemName, itemLevel, requiredLevel, itemClassId, itemSubClassId)
if entry.npcId then self:LearnItemDrop(itemId, entry.npcId) end
local learnedItem = self:LearnItem(itemId, itemName, itemLevel, requiredLevel, itemClassId, itemSubClassId)
if learnedItem and entry.npcId then self:LearnItemDrop(itemId, entry.npcId) end
else
table.insert(remaining, entry) -- still not cached, keep
end
@@ -4540,6 +4627,9 @@ function QuestieLearner:_ApplyIncomingNetworkMerge(typ, id, d, op)
store = Questie.dbLearner.global.quests
elseif typ == "ITEM" then
if not Questie.dbLearner.global.settings.learnItems then return false end
if not ((type(d) == "table" and d.questRelevant) or HasQuestReferences(id)) then
return false
end
store = Questie.dbLearner.global.items
elseif typ == "OBJECT" then
if not Questie.dbLearner.global.settings.learnObjects then return false end
+19 -5
View File
@@ -289,24 +289,38 @@ describe("QuestieLearner GUID and loot learning", function()
assert.equals("Creature", unitType)
end)
it("learns loot item drop sources even when class metadata is not quest-item shaped", function()
it("ignores non-quest loot items and only learns quest-item drops", function()
local learnedItemId = nil
local dropItemId = nil
local dropNpcId = nil
QuestieLearner.LearnItem = function(self, itemId, name, itemLevel, requiredLevel, itemClassId, itemSubClassId)
learnedItemId = itemId
if itemClassId == 12 then
learnedItemId = itemId
return true
end
return false
end
QuestieLearner.LearnItemDrop = function(self, itemId, npcId)
dropItemId = itemId
dropNpcId = npcId
end
local originalGetItemInfo = _G.GetItemInfo
_G.GetItemInfo = function(link)
if link == "item:20470" then
return "Quest Token", nil, nil, 1, 1, nil, nil, nil, nil, nil, nil, 1, 0
end
return originalGetItemInfo(link)
end
QuestieLearner:OnLootOpened()
assert.equals(20470, learnedItemId)
assert.equals(20470, dropItemId)
assert.equals(15297, dropNpcId)
assert.is_nil(learnedItemId)
assert.is_nil(dropItemId)
assert.is_nil(dropNpcId)
_G.GetItemInfo = originalGetItemInfo
end)
end)
+26 -8
View File
@@ -77,6 +77,7 @@ describe("QuestieLearner kill-path batching", function()
npcCache = {
[1001] = { name = "Cached Boar" },
},
itemCache = {},
}
QuestiePlayer.currentQuestlog = {}
@@ -141,20 +142,37 @@ describe("QuestieLearner kill-path batching", function()
assert.equals(1, table.getn(Questie.dbLearner.global.npcs[4001][7][44]))
end)
it("records item drop sources even when the item class is not available on first pass", function()
it("ignores non-quest loot items so they do not pollute learner state", function()
Questie.dbLearner.global.settings.dataSourceMode = "learner"
QuestieDB.private.itemCache = {
[2301] = { cached = true },
}
QuestieDB.private.itemCache = {}
QuestieLearner:LearnItem(2301, "Quest Shard", 1, 1, 1, 0)
QuestieLearner:LearnItemDrop(2301, 7301)
local learned = QuestieLearner:LearnItem(2301, "Quest Shard", 1, 1, 1, 0)
assert.is_table(Questie.dbLearner.global.items[2301][2])
assert.equals(7301, Questie.dbLearner.global.items[2301][2][1])
assert.is_false(learned)
assert.is_nil(Questie.dbLearner.global.items[2301])
assert.is_nil(QuestieDB.private.itemCache[2301])
end)
it("keeps quest item drop sources when the item is a quest item", function()
Questie.dbLearner.global.settings.dataSourceMode = "learner"
local learned = QuestieLearner:LearnItem(2302, "Quest Shard", 1, 1, 12, 0)
assert.is_true(learned)
assert.is_true(Questie.dbLearner.global.items[2302].questRelevant)
QuestieLearner:LearnItemDrop(2302, 7301)
assert.is_table(Questie.dbLearner.global.items[2302][2])
assert.equals(7301, Questie.dbLearner.global.items[2302][2][1])
end)
it("rejects non-quest item network merges without quest references", function()
Questie.dbLearner.global.settings.dataSourceMode = "learner"
local changed = QuestieLearner:_ApplyIncomingNetworkMerge("ITEM", 2401, { [1] = "Arcane Sliver" }, "NEW")
assert.is_false(changed)
assert.is_nil(Questie.dbLearner.global.items[2401])
end)
it("clears cached quest data when learner adds questgiver links", function()
Questie.dbLearner.global.settings.dataSourceMode = "learner"
QuestieDB.private.questCache = {