From 6481cf512b44a3dbcd535e7c88268a7b98e2b8e9 Mon Sep 17 00:00:00 2001 From: Andrew Scott Date: Sat, 13 Feb 2021 11:12:08 -0800 Subject: [PATCH] Rework item cache to use links instead of IDs to better handle items with bonusIDs (like shadowlands legendaries) --- CHANGELOG.md | 1 + Components/Core/Common/Item.lua | 56 ++++++++- Components/Core/Spells/Equivalencies.lua | 2 + Components/Core/Spells/ItemCache.lua | 68 ++++++---- Components/Core/Suggester/Suggester.lua | 113 +++++++++++++++-- .../Config.lua | 5 +- Components/IconTypes/IconType_item/Config.lua | 117 +----------------- .../IconTypes/IconType_wpnenchant/Config.lua | 14 +-- Options/CHANGELOG.lua | 1 + TellMeWhen.lua | 2 +- 10 files changed, 209 insertions(+), 170 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb55d97c..d04a2a22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Bug Fixes * #1840 - Talents granted by Torghast powers are now correctly reflected by the Talent Learned condition. * #1844 - The Totem icon type has been updated for Monks to better support the wide range of "totems" that Monks have. +* #1842 - Fixed handling of Shadowlands legendaries in item suggestion lists ## v9.0.3 ### Bug Fixes diff --git a/Components/Core/Common/Item.lua b/Components/Core/Common/Item.lua index bd5c50b4..60566169 100644 --- a/Components/Core/Common/Item.lua +++ b/Components/Core/Common/Item.lua @@ -235,6 +235,10 @@ function ItemByID:GetName() return name end end +function ItemByID:GetLink() + local _, itemLink = GetItemInfo(self.itemID) + return itemLink +end @@ -271,7 +275,7 @@ function ItemByName:GetCooldown() end function ItemByName:GetID() - local _, itemLink = GetItemInfo(self.name) + local itemLink = self:GetLink() if itemLink then return tonumber(strmatch(itemLink, ":(%d+)")) end @@ -287,6 +291,10 @@ function ItemByName:GetName() return self.name end end +function ItemByName:GetLink() + local _, itemLink = GetItemInfo(self.name) + return itemLink +end @@ -297,6 +305,36 @@ end local ItemBySlot = TMW:NewClass("ItemBySlot", Item) + +local slotNames = {} +for _, slotName in pairs{ + "BackSlot", + "ChestSlot", + "FeetSlot", + "Finger0Slot", + "Finger1Slot", + "HandsSlot", + "HeadSlot", + "LegsSlot", + "MainHandSlot", + "NeckSlot", + "SecondaryHandSlot", + "ShirtSlot", + "ShoulderSlot", + "TabardSlot", + "Trinket0Slot", + "Trinket1Slot", + "WaistSlot", + "WristSlot", +} do + local slotID = GetInventorySlotInfo(slotName) + if slotID then + slotNames[slotID] = slotName + else + TMW:Debug("Invalid slot name %s", slotName) + end +end + function ItemBySlot:OnNewInstance(itemSlot) TMW:ValidateType("2 (itemSlot)", "ItemByID:New(itemSlot)", itemSlot, "number") @@ -323,8 +361,12 @@ function ItemBySlot:GetID() return GetInventoryItemID("player", self.slot) end function ItemBySlot:GetName() - local name = GetItemInfo(self:GetLink()) - return name + local link = self:GetLink() + if link then + local name = GetItemInfo(link) + if name then return name end + end + return slotNames[self.slot] and _G[slotNames[self.slot]:upper()] or nil end function ItemBySlot:GetLink() return GetInventoryItemLink("player", self.slot) @@ -343,7 +385,7 @@ function ItemByLink:OnNewInstance(itemLink) TMW:ValidateType("2 (itemLink)", "ItemByID:New(itemLink)", itemLink, "string") self.link = itemLink - self.itemID = tonumber(strmatch(itemLink, ":(%d+)")) + self.itemID = tonumber(strmatch(itemLink, "item:(%d+)")) self.name = GetItemInfo(itemLink) end @@ -367,9 +409,15 @@ end function ItemByLink:GetName() local name = GetItemInfo(self.link) + if not name then + name = self.link:match("%[(.-)%]") + end if name then self.name = name self.GetName = self.GetName_saved return name end +end +function ItemByLink:GetLink() + return self.link end \ No newline at end of file diff --git a/Components/Core/Spells/Equivalencies.lua b/Components/Core/Spells/Equivalencies.lua index 04c8a035..8fc10b5d 100644 --- a/Components/Core/Spells/Equivalencies.lua +++ b/Components/Core/Spells/Equivalencies.lua @@ -515,6 +515,8 @@ TMW.BE = { 288024, -- Diamond Barrier (Diamond-Laced Refracting Prism, Battle of Dazar'alor raid) 295271, -- Umbral Shell (Void Stone, Crucible of Storms raid) 295431, -- Ephemeral Vigor (Abyssal Speaker's Gauntlets, Crucible of Storms raid) + 311444, -- Indomitable Deck (Shadowlands tank darkmoon deck) + 322507, -- Celestial Brew (Monk, brewmaster) 324867, -- Fleshcraft (Necrolord) }, ImmuneToMagicCC = { diff --git a/Components/Core/Spells/ItemCache.lua b/Components/Core/Spells/ItemCache.lua index 841fc265..d2408039 100644 --- a/Components/Core/Spells/ItemCache.lua +++ b/Components/Core/Spells/ItemCache.lua @@ -41,7 +41,7 @@ TMW.IE:RegisterDatabaseDefaults{ }, } -TMW.IE:RegisterUpgrade(72310, { +TMW.IE:RegisterUpgrade(90403, { locale = function(self, locale) locale.ItemCache = nil locale.XPac_ItemCache = nil @@ -59,7 +59,7 @@ TMW.IE:RegisterUpgrade(62217, { --[[ Returns the main cache table. Structure: Cache = { - [itemID] = 1, + [link] = 1, } ]] function ItemCache:GetCache() @@ -74,8 +74,8 @@ end --[[ Returns a list of items that the player currently has. Structure: Cache = { - [itemID] = name, - [name] = itemID, + [link] = 1, + [name] = link, } ]] function ItemCache:GetCurrentItems() @@ -119,14 +119,14 @@ TMW:RegisterCallback("TMW_OPTIONS_LOADED", function() -- Compile all items from all timesegments into a cohesive table for -- fast lookups and easy iteration. for timestamp, items in pairs(Cache) do - for id, name in pairs(items) do - CompiledCache[id] = name + for link in pairs(items) do + CompiledCache[link] = 1 end end --Start requests so that we can validate itemIDs. - for id in pairs(CompiledCache) do - GetItemInfo(id) + for link in pairs(CompiledCache) do + GetItemInfo(link) end ItemCache:RegisterEvent("BAG_UPDATE") @@ -135,21 +135,36 @@ TMW:RegisterCallback("TMW_OPTIONS_LOADED", function() ItemCache:CacheItems() end) -local function cacheItem(itemID, name) +local function cacheItem(itemID, link) + -- Sanitize the link: + -- strip out all gems/enchants (not an important distinction for TMW) + -- LEAVE suffixID alone (makes sure that the name is correct) + -- strip out uniqueID (not an important distinction for TMW) + -- strip out linkLevel,specializationId + link = link:gsub("(item:%d+):%-?%d*:%-?%d*:%-?%d*:%-?%d*:%-?%d*:(%-?%d*):%-?%d*:%-?%d*:%-?%d*", "%1:::::%2::::") + + if link:find("|h%[%]|h") then + -- Links that don't have the item name loaded + -- will just have brackets in the position of the name and nothing else. + return + end + + CurrentItems[link] = 1 + -- The item is not cached at all. - if not CompiledCache[itemID] then - Cache[currentTimestamp][itemID] = name - CompiledCache[itemID] = name + if not CompiledCache[link] then + Cache[currentTimestamp][link] = 1 + CompiledCache[link] = 1 -- The item is in an old cache timesegment. - elseif not Cache[currentTimestamp][itemID] then + elseif not Cache[currentTimestamp][link] then -- Remove the item from the old cache. for timestamp, items in pairs(Cache) do - items[itemID] = nil + items[link] = nil end -- Add it to the current cache timesegment. - Cache[currentTimestamp][itemID] = name + Cache[currentTimestamp][link] = 1 end end @@ -169,12 +184,9 @@ function ItemCache:CacheItems(force) for container = 0, NUM_BAG_SLOTS do for slot = 1, GetContainerNumSlots(container) do local id = GetContainerItemID(container, slot) + local link = GetContainerItemLink(container, slot) if id then - local name = GetItemInfo(id) - name = name and strlower(name) - - CurrentItems[id] = name - cacheItem(id, name) + cacheItem(id, link) end end end @@ -182,17 +194,19 @@ function ItemCache:CacheItems(force) -- Cache equipped items for slot = 1, 19 do local id = GetInventoryItemID("player", slot) + local link = GetInventoryItemLink("player", slot) if id then - local name = GetItemInfo(id) - name = name and strlower(name) - - CurrentItems[id] = name - cacheItem(id, name) + cacheItem(id, link) end end - for id, name in pairs(CurrentItems) do - CurrentItems[name] = id + local reverseMap = {} + for link in pairs(CurrentItems) do + local name = link:match("%[(.-)%]") + reverseMap[strlower(name)] = link + end + for k, v in pairs(reverseMap) do + CurrentItems[k] = v end doUpdateCache = nil diff --git a/Components/Core/Suggester/Suggester.lua b/Components/Core/Suggester/Suggester.lua index 70c0c0c7..d20d612d 100644 --- a/Components/Core/Suggester/Suggester.lua +++ b/Components/Core/Suggester/Suggester.lua @@ -1056,6 +1056,7 @@ end local Module = SUG:NewModule("item", SUG:GetModule("default"), "AceEvent-3.0") +local Item = TMW.C.Item Module.showColorHelp = false Module.helpText = L["SUG_TOOLTIPTITLE_GENERIC"] function Module:GET_ITEM_INFO_RECEIVED() @@ -1065,30 +1066,122 @@ function Module:GET_ITEM_INFO_RECEIVED() end end Module:RegisterEvent("GET_ITEM_INFO_RECEIVED") +Module.Slots = {} function Module:Table_Get() - return TMW:GetModule("ItemCache"):GetCache() + return ItemCache:GetCache() +end +function Module:OnSuggest() + for i = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do + if i ~= INVSLOT_RANGED then -- why did you not get rid of this, blizz?.... + self.Slots[Item:GetRepresentation(i)] = 1 + end + end +end +function Module:Table_GetNormalSuggestions(suggestions, tbl) + local len = #suggestions + + local match = tonumber(SUG.lastName) + local inputLen = #SUG.lastName - 1 + + local sources = {tbl} + if self.IncludeSlots then + sources[2] = self.Slots + end + + for _, table in pairs(sources) do + for item in pairs(table) do + if type(item) ~= "table" then + item = Item:GetRepresentation(item) + end + + if SUG.inputType == "number" then + local id = item.slot or item:GetID() + if id and min(id, floor(id / 10^(floor(log10(id)) - inputLen))) == match then + len = len + 1 + suggestions[len] = item + end + else + local name = item:GetName() + if name and strfindsug(strlower(name)) then + len = len + 1 + suggestions[len] = item + end + end + end + end end function Module:Table_GetSpecialSuggestions_1(suggestions) local id = tonumber(SUG.lastName) + if not id then return end + local item = Item:GetRepresentation(id) - if id and GetItemInfo(id) and not TMW.tContains(suggestions, id) then - suggestions[#suggestions + 1] = id + if item:GetName() and not TMW.tContains(suggestions, item) then + suggestions[#suggestions + 1] = item end end -function Module:Entry_AddToList_1(f, id) - if id > INVSLOT_LAST_EQUIPPED then - local name, link = GetItemInfo(id) +function Module.Sorter_Item(a, b) + local haveA, haveB = a.slot, b.slot + if haveA or haveB then + if haveA and haveB then + return a.slot < b.slot + else + return haveA + end + end + + -- Sort by whether current posessed or not + haveA, haveB = a:GetCount() > 0, b:GetCount() > 0 + if (haveA ~= haveB) then + return haveA + end - f.Name:SetText(link and link:gsub("[%[%]]", "")) + local nameA, nameB = a:GetName(), b:GetName() + if nameA == nameB then + --sort identical names by ID + return a:GetID() < b:GetID() + else + --sort by name + return nameA < nameB + end +end +function Module:Table_GetSorter() + return self.Sorter_Item +end +function Module:Entry_AddToList_1(f, item) + local link = item:GetLink() + local id = item:GetID() + local name = item:GetName() + + f.Name:SetText(link and link:gsub("[%[%]]", "") or name) + + if item.slot then + id = item.slot + f.ID:SetText("(" .. item.slot .. ")") + f.overrideInsertID = L["SUG_INSERTITEMSLOT"] + if not link then + f.tooltiptitle = name + f.tooltiptext = TRANSMOGRIFY_INVALID_NO_ITEM + end + else f.ID:SetText(id) + end - f.insert = SUG.inputType == "number" and id or name - f.insert2 = SUG.inputType ~= "number" and id or name + f.insert = SUG.inputType == "number" and id or name + f.insert2 = SUG.inputType ~= "number" and id or name + if link then f.tooltipmethod = "SetHyperlink" f.tooltiparg = link + end + + f.Icon:SetTexture(item:GetIcon()) +end - f.Icon:SetTexture(GetItemIcon(id)) +local Module = SUG:NewModule("itemwithslots", SUG:GetModule("item")) +Module.IncludeSlots = true +function Module:Entry_Colorize_1(f, item) + if item.slot then + f.Background:SetVertexColor(.23, .20, .29, 1) -- color item slots purpleish end end diff --git a/Components/IconDataProcessorHooks/IconDataProcessorHook_Texture_CustomTex/Config.lua b/Components/IconDataProcessorHooks/IconDataProcessorHook_Texture_CustomTex/Config.lua index b5cbb7ce..14b2b2cc 100644 --- a/Components/IconDataProcessorHooks/IconDataProcessorHook_Texture_CustomTex/Config.lua +++ b/Components/IconDataProcessorHooks/IconDataProcessorHook_Texture_CustomTex/Config.lua @@ -20,9 +20,6 @@ local print = TMW.print local SUG = TMW.SUG -local ItemCache = TMW:GetModule("ItemCache") -local ItemCache_Cache - local Module = SUG:NewModule("texture_withVarTex", SUG:GetModule("texture")) Module.Slots = {} @@ -66,7 +63,7 @@ function Module:Entry_AddToList_2(f, id) f.overrideInsertName = L["SUG_INSERTITEMSLOT"] - local name = GetItemInfo(itemID) + local name = GetItemInfo(link) f.Name:SetText(link and link:gsub("[%[%]]", "")) f.ID:SetText(id) diff --git a/Components/IconTypes/IconType_item/Config.lua b/Components/IconTypes/IconType_item/Config.lua index 15be1191..32089329 100644 --- a/Components/IconTypes/IconType_item/Config.lua +++ b/Components/IconTypes/IconType_item/Config.lua @@ -28,6 +28,7 @@ local Type = rawget(TMW.Types, "item") if not Type then return end +local Item = TMW.C.Item local ItemCache = TMW:GetModule("ItemCache") local ItemCache_Cache @@ -53,119 +54,3 @@ function Type:DragReceived(icon, t, data, subType) end -local Module = SUG:NewModule("itemwithslots", SUG:GetModule("item")) -Module.Slots = {} - -local slotNames = {} -for _, slotName in pairs{ - "BackSlot", - "ChestSlot", - "FeetSlot", - "Finger0Slot", - "Finger1Slot", - "HandsSlot", - "HeadSlot", - "LegsSlot", - "MainHandSlot", - "NeckSlot", - "SecondaryHandSlot", - "ShirtSlot", - "ShoulderSlot", - "TabardSlot", - "Trinket0Slot", - "Trinket1Slot", - "WaistSlot", - "WristSlot", -} do - local slotID = GetInventorySlotInfo(slotName) - if slotID then - slotNames[slotID] = slotName - else - TMW:Debug("Invalid slot name %s", slotName) - end -end - -function Module:OnSuggest() - ItemCache_Cache = ItemCache:GetCache() -end -function Module:Entry_AddToList_2(f, id) - if id <= INVSLOT_LAST_EQUIPPED then - local itemID = GetInventoryItemID("player", id) -- get the itemID of the slot - local link = GetInventoryItemLink("player", id) - - f.overrideInsertID = L["SUG_INSERTITEMSLOT"] - - - local name = itemID and GetItemInfo(itemID) - local slotName = slotNames[id] and _G[slotNames[id]:upper()] - - f.Name:SetText(link and link:gsub("[%[%]]", "") or slotName) - f.ID:SetText("(" .. id .. ")") - - f.insert = SUG.inputType == "number" and id or name - f.insert2 = SUG.inputType ~= "number" and id or name - - if link then - f.tooltipmethod = "SetHyperlink" - f.tooltiparg = link - else - f.tooltiptitle = slotName - f.tooltiptext = TRANSMOGRIFY_INVALID_REASON1 - end - - f.Icon:SetTexture(GetItemIcon(itemID)) - end -end -function Module:Table_GetSpecialSuggestions_2(suggestions) - - local atBeginning = SUG.atBeginning - - for i = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED do - if i ~= INVSLOT_RANGED then -- why did you not get rid of this, blizz?.... - local itemID = GetInventoryItemID("player", i) -- get the itemID in the slot - self.Slots[i] = itemID and GetItemInfo(itemID) or _G[slotNames[i]:upper()] - end - end - - if SUG.inputType == "number" then - local len = #SUG.lastName - 1 - local match = tonumber(SUG.lastName) - - for id in pairs(self.Slots) do - if min(id, floor(id / 10^(floor(log10(id)) - len))) == match then -- this looks like shit, but is is approx 300% more efficient than the below commented line - -- if strfind(id, atBeginning) then - suggestions[#suggestions + 1] = id - end - end - else - for id, name in pairs(self.Slots) do - if name ~= "" and strfindsug(strlower(name)) then - suggestions[#suggestions + 1] = id - end - end - end -end -function Module:Entry_Colorize_1(f, id) - if id <= INVSLOT_LAST_EQUIPPED then - f.Background:SetVertexColor(.23, .20, .29, 1) -- color item slots purpleish - end -end -function Module.Sorter_ByName(a, b) - local haveA, haveB = Module.Slots[a], Module.Slots[b] - if haveA or haveB then - if haveA and haveB then - return a < b - else - return haveA - end - end - - local nameA, nameB = ItemCache_Cache[a], ItemCache_Cache[b] - if nameA == nameB then - --sort identical names by ID - return a < b - else - --sort by name - return nameA < nameB - end -end diff --git a/Components/IconTypes/IconType_wpnenchant/Config.lua b/Components/IconTypes/IconType_wpnenchant/Config.lua index 3a9d2f22..5f06eab7 100644 --- a/Components/IconTypes/IconType_wpnenchant/Config.lua +++ b/Components/IconTypes/IconType_wpnenchant/Config.lua @@ -187,9 +187,9 @@ function Module:GET_ITEM_INFO_RECEIVED(event, id) if gotItemInfo[id] then return end gotItemInfo[id] = true - local name = GetItemInfo(id) + local name, link = GetItemInfo(id) if name then - self.Items[name] = id + self.Items[name] = link self.Table[name] = id else print("wpnenchant SUG: WoW Server seems to think that item doesn't exist", id) @@ -200,9 +200,9 @@ function Module:Etc_DoItemLookups() self:UnregisterEvent("GET_ITEM_INFO_RECEIVED") for k, id in pairs(self.ItemIDs) do - local name = GetItemInfo(id) + local name, link = GetItemInfo(id) if name then - self.Items[name] = id + self.Items[name] = link else self:RegisterEvent("GET_ITEM_INFO_RECEIVED") end @@ -233,8 +233,8 @@ function Module:Entry_AddToList_1(f, name) f.insert = name elseif self.Items[name] then - local id = CurrentItems[strlowerCache[name]] or self.Items[name] - local name, link = GetItemInfo(id) + local link = CurrentItems[strlowerCache[name]] or self.Items[name] + local name, link = GetItemInfo(link) f.Name:SetText(link:gsub("[%[%]]", "")) f.ID:SetText(nil) @@ -278,8 +278,6 @@ end local PlayerSpells function Module:Table_GetSorter() - TMW:GetModule("ItemCache"):CacheItems(true) - PlayerSpells = TMW:GetModule("ClassSpellCache"):GetPlayerSpells() return self.Sorter diff --git a/Options/CHANGELOG.lua b/Options/CHANGELOG.lua index 91cb8e8e..09d55b53 100644 --- a/Options/CHANGELOG.lua +++ b/Options/CHANGELOG.lua @@ -10,6 +10,7 @@ TMW.CHANGELOG = [==[ ### Bug Fixes * #1840 - Talents granted by Torghast powers are now correctly reflected by the Talent Learned condition. * #1844 - The Totem icon type has been updated for Monks to better support the wide range of "totems" that Monks have. +* #1842 - Fixed handling of Shadowlands legendaries in item suggestion lists ## v9.0.3 ### Bug Fixes diff --git a/TellMeWhen.lua b/TellMeWhen.lua index 12c0a013..01eb7f14 100644 --- a/TellMeWhen.lua +++ b/TellMeWhen.lua @@ -44,7 +44,7 @@ elseif strmatch(projectVersion, "%-%d+%-") then end TELLMEWHEN_VERSION_FULL = TELLMEWHEN_VERSION .. " " .. TELLMEWHEN_VERSION_MINOR -TELLMEWHEN_VERSIONNUMBER = 90402 -- NEVER DECREASE THIS NUMBER (duh?). IT IS ALSO ONLY INTERNAL (for versioning of) +TELLMEWHEN_VERSIONNUMBER = 90403 -- NEVER DECREASE THIS NUMBER (duh?). IT IS ALSO ONLY INTERNAL (for versioning of) TELLMEWHEN_FORCECHANGELOG = 86005 -- if the user hasn't seen the changelog until at least this version, show it to them.