From d48a4932db37b9e669d82a7dbd3feb80eaa50e69 Mon Sep 17 00:00:00 2001 From: Vati Date: Mon, 6 Apr 2026 00:08:00 +0400 Subject: [PATCH] feat: display charges on item --- Core/ItemDetection.lua | 54 ++++++++++++++++++++++++++++++++++++++++++ UI/BagFrame.lua | 2 +- UI/BagFrame.xml | 2 +- UI/BankFrame.lua | 2 +- UI/BankFrame.xml | 2 +- UI/ItemButton.lua | 40 +++++++++++++++++++++++++++++-- UI/ItemButton.xml | 10 ++++++++ 7 files changed, 106 insertions(+), 6 deletions(-) diff --git a/Core/ItemDetection.lua b/Core/ItemDetection.lua index e37f453..7f88eec 100644 --- a/Core/ItemDetection.lua +++ b/Core/ItemDetection.lua @@ -12,6 +12,7 @@ addon.Modules.ItemDetection = ItemDetection -- Caches tooltip scan results to avoid repeated scans --===================================================== local detectionCache = {} +local chargesCache = {} -- Keyed by "bagID:slotID" (charges vary per-slot, not per-link) local cacheHits = 0 local cacheMisses = 0 @@ -19,6 +20,7 @@ local cacheMisses = 0 -- For simple item moves, use InvalidateItem() or don't invalidate at all function ItemDetection:ClearCache() detectionCache = {} + chargesCache = {} cacheHits = 0 cacheMisses = 0 end @@ -447,6 +449,17 @@ local function DetectUnusable(lines) return false end +-- Detect item charges (e.g. "5 Charges" on Wizard Oil, Mana Oil, etc.) +local function DetectCharges(lines) + for _, line in ipairs(lines) do + local _, _, num = string.find(line.leftLower, "^(%d+) charges?$") + if num then + return tonumber(num) + end + end + return nil +end + --===================================================== -- Public API - Cached Detection --===================================================== @@ -497,6 +510,11 @@ function ItemDetection:GetItemProperties(itemData, bagID, slotID) local isJunk = DetectJunk(lines, itemData) local isUnusable = DetectUnusable(lines) + -- Store charges in per-slot cache during property scan (avoids double tooltip scan) + if bagID and slotID and tooltipLooksComplete then + chargesCache[bagID .. ":" .. slotID] = DetectCharges(lines) + end + -- Debug: log junk detection for gray items if addon.DEBUG then local quality = tonumber(itemData.quality) @@ -563,6 +581,37 @@ function ItemDetection:IsUnusable(itemData, bagID, slotID) return props.isUnusable end +function ItemDetection:GetCharges(itemData, bagID, slotID) + if not bagID or not slotID then return nil end + local slotKey = bagID .. ":" .. slotID + -- Use per-slot cache if available + if chargesCache[slotKey] ~= nil then + return chargesCache[slotKey] + end + -- Otherwise scan tooltip fresh + local itemLink = itemData and itemData.link + local lines = ScanTooltipLines(bagID, slotID, itemLink) + local charges = DetectCharges(lines) + if table.getn(lines) >= 2 then + chargesCache[slotKey] = charges + end + return charges +end + +-- Invalidate charges cache for a specific bag (called on BAG_UPDATE) +function ItemDetection:InvalidateCharges(bagID) + if bagID then + local prefix = bagID .. ":" + for key in pairs(chargesCache) do + if string.find(key, "^" .. prefix) then + chargesCache[key] = nil + end + end + else + chargesCache = {} + end +end + --===================================================== -- Initialization --===================================================== @@ -573,5 +622,10 @@ function ItemDetection:Initialize() self:ClearCache() end, "ItemDetection") + -- Invalidate charges cache on bag updates (charges change per-slot) + addon.Modules.Events:Register("BAG_UPDATE", function() + self:InvalidateCharges(arg1) + end, "ItemDetection_Charges") + addon:Debug("ItemDetection module initialized") end diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua index 4a5a3a3..793d030 100644 --- a/UI/BagFrame.lua +++ b/UI/BagFrame.lua @@ -2451,7 +2451,7 @@ function Guda_BagFrame_Sort() end -- Restack and Clean (for category view) - merges stacks and refreshes view --- Queue-based approach like BagShui +-- Queue-based approach function Guda_BagFrame_MergeStacks() if currentViewChar then addon:Print("Cannot restack for another character!") diff --git a/UI/BagFrame.xml b/UI/BagFrame.xml index d55521b..829cd45 100644 --- a/UI/BagFrame.xml +++ b/UI/BagFrame.xml @@ -123,7 +123,7 @@ local icon = getglobal(this:GetName().."_Icon") if icon then - -- BagShui approach: no file extension, WoW finds .blp automatically + -- No file extension, WoW finds .blp automatically icon:SetTexture("Interface\\AddOns\\Guda\\Assets\\Sorting") icon:SetTexCoord(0, 1, 0, 1) end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index b376b81..06c7c7c 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -1686,7 +1686,7 @@ function Guda_BankFrame_Sort() end -- Restack and Clean (for category view) - merges stacks and refreshes view --- Queue-based approach like BagShui +-- Queue-based approach function Guda_BankFrame_MergeStacks() if isReadOnlyMode or currentViewChar then addon:Print("Cannot restack in read-only mode!") diff --git a/UI/BankFrame.xml b/UI/BankFrame.xml index 5a52ec3..5d3f06a 100644 --- a/UI/BankFrame.xml +++ b/UI/BankFrame.xml @@ -76,7 +76,7 @@ local icon = getglobal(this:GetName().."_Icon") if icon then - -- BagShui approach: no file extension, WoW finds .blp automatically + -- No file extension, WoW finds .blp automatically icon:SetTexture("Interface\\AddOns\\Guda\\Assets\\Sorting") icon:SetTexCoord(0, 1, 0, 1) end diff --git a/UI/ItemButton.lua b/UI/ItemButton.lua index e85e769..ef4baf7 100644 --- a/UI/ItemButton.lua +++ b/UI/ItemButton.lua @@ -1334,6 +1334,8 @@ local function ResetButtonVisualState(self) if self.unusableOverlay then self.unusableOverlay:Hide() end HideJunkIcon(self) if self.categoryMarkIcon then self.categoryMarkIcon:Hide() end + local chargesText = getglobal(self:GetName().."_Charges") + if chargesText then chargesText:Hide() end -- Clear cooldown overlay local cd = getglobal(self:GetName().."Cooldown") or self.cooldown @@ -1634,6 +1636,8 @@ local function ClearItemButton(self, emptySlotBg, countText, bagID) end if countText then countText:Hide() end + local chargesText = getglobal(self:GetName().."_Charges") + if chargesText then chargesText:Hide() end ResetSlotBorder(self) HideInnerShadow(self.innerShadow) @@ -1681,6 +1685,7 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha -- Get UI elements local countText = getglobal(self:GetName().."Count") + local chargesText = getglobal(self:GetName().."_Charges") local emptySlotBg = getglobal(self:GetName().."_EmptySlotBg") local Utils = addon and addon.Modules and addon.Modules.Utils @@ -1827,6 +1832,23 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha end end + -- Apply icon font size setting to charges text (positioned above stack count) + if chargesText and chargesText.GetFont then + local font, _, flags = chargesText:GetFont() + local fontSize = 12 + if Utils and Utils.SafeCall then + fontSize = Utils:SafeCall("DB", "GetSetting", "iconFontSize") or fontSize + end + chargesText:SetFont(font, fontSize, flags) + chargesText:SetTextColor(1, 0.82, 0) + chargesText:ClearAllPoints() + if iconSize < 44 then + chargesText:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -1, 1) + else + chargesText:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", -3, 3) + end + end + -- Get live metadata if in live mode (quality, link, lock status) local itemQuality, itemLink, isLocked if not self.isReadOnly and bagID and slotID and self.hasItem then @@ -1896,7 +1918,7 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha if self.hasItem then -- Icon already set above based on mode (live vs cached) - -- Gray out locked items (being traded, mailed, or auctioned) - BagShui style + -- Gray out locked items (being traded, mailed, or auctioned) -- Don't desaturate items from other characters since they're read-only anyway if not self.otherChar and not self.isReadOnly then SetItemButtonDesaturated(self, isLocked, 0.5, 0.5, 0.5) @@ -2060,6 +2082,20 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha end end + -- Show/hide charges text (e.g. "x5" for Wizard Oil) + if chargesText then + local charges = nil + if itemData and addon.Modules.ItemDetection then + charges = addon.Modules.ItemDetection:GetCharges(itemData, bagID, slotID) + end + if charges and charges > 0 then + chargesText:SetText("x" .. charges) + chargesText:Show() + else + chargesText:Hide() + end + end + -- Handle tracking toggle on click -- Note: Tracking toggle is now handled in the main OnClick script above to avoid conflicts -- and unified with QuestItemBar pinning logic. @@ -2369,7 +2405,7 @@ function Guda_ItemButton_OnEnter(self) end end - -- Handle merchant sell cursor (same approach as BagShui) + -- Handle merchant sell cursor if MerchantFrame:IsShown() and not self.isBank and not self.otherChar and self.hasItem then ShowContainerSellCursor(self.bagID, self.slotID) else diff --git a/UI/ItemButton.xml b/UI/ItemButton.xml index 11d2a1c..4a7cdb5 100644 --- a/UI/ItemButton.xml +++ b/UI/ItemButton.xml @@ -37,6 +37,16 @@ +