feat: display charges on item

This commit is contained in:
Vati
2026-04-06 00:08:00 +04:00
parent de072d3737
commit d48a4932db
7 changed files with 106 additions and 6 deletions
+54
View File
@@ -12,6 +12,7 @@ addon.Modules.ItemDetection = ItemDetection
-- Caches tooltip scan results to avoid repeated scans -- Caches tooltip scan results to avoid repeated scans
--===================================================== --=====================================================
local detectionCache = {} local detectionCache = {}
local chargesCache = {} -- Keyed by "bagID:slotID" (charges vary per-slot, not per-link)
local cacheHits = 0 local cacheHits = 0
local cacheMisses = 0 local cacheMisses = 0
@@ -19,6 +20,7 @@ local cacheMisses = 0
-- For simple item moves, use InvalidateItem() or don't invalidate at all -- For simple item moves, use InvalidateItem() or don't invalidate at all
function ItemDetection:ClearCache() function ItemDetection:ClearCache()
detectionCache = {} detectionCache = {}
chargesCache = {}
cacheHits = 0 cacheHits = 0
cacheMisses = 0 cacheMisses = 0
end end
@@ -447,6 +449,17 @@ local function DetectUnusable(lines)
return false return false
end 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 -- Public API - Cached Detection
--===================================================== --=====================================================
@@ -497,6 +510,11 @@ function ItemDetection:GetItemProperties(itemData, bagID, slotID)
local isJunk = DetectJunk(lines, itemData) local isJunk = DetectJunk(lines, itemData)
local isUnusable = DetectUnusable(lines) 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 -- Debug: log junk detection for gray items
if addon.DEBUG then if addon.DEBUG then
local quality = tonumber(itemData.quality) local quality = tonumber(itemData.quality)
@@ -563,6 +581,37 @@ function ItemDetection:IsUnusable(itemData, bagID, slotID)
return props.isUnusable return props.isUnusable
end 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 -- Initialization
--===================================================== --=====================================================
@@ -573,5 +622,10 @@ function ItemDetection:Initialize()
self:ClearCache() self:ClearCache()
end, "ItemDetection") 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") addon:Debug("ItemDetection module initialized")
end end
+1 -1
View File
@@ -2451,7 +2451,7 @@ function Guda_BagFrame_Sort()
end end
-- Restack and Clean (for category view) - merges stacks and refreshes view -- Restack and Clean (for category view) - merges stacks and refreshes view
-- Queue-based approach like BagShui -- Queue-based approach
function Guda_BagFrame_MergeStacks() function Guda_BagFrame_MergeStacks()
if currentViewChar then if currentViewChar then
addon:Print("Cannot restack for another character!") addon:Print("Cannot restack for another character!")
+1 -1
View File
@@ -123,7 +123,7 @@
<OnLoad> <OnLoad>
local icon = getglobal(this:GetName().."_Icon") local icon = getglobal(this:GetName().."_Icon")
if icon then 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:SetTexture("Interface\\AddOns\\Guda\\Assets\\Sorting")
icon:SetTexCoord(0, 1, 0, 1) icon:SetTexCoord(0, 1, 0, 1)
end end
+1 -1
View File
@@ -1686,7 +1686,7 @@ function Guda_BankFrame_Sort()
end end
-- Restack and Clean (for category view) - merges stacks and refreshes view -- Restack and Clean (for category view) - merges stacks and refreshes view
-- Queue-based approach like BagShui -- Queue-based approach
function Guda_BankFrame_MergeStacks() function Guda_BankFrame_MergeStacks()
if isReadOnlyMode or currentViewChar then if isReadOnlyMode or currentViewChar then
addon:Print("Cannot restack in read-only mode!") addon:Print("Cannot restack in read-only mode!")
+1 -1
View File
@@ -76,7 +76,7 @@
<OnLoad> <OnLoad>
local icon = getglobal(this:GetName().."_Icon") local icon = getglobal(this:GetName().."_Icon")
if icon then 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:SetTexture("Interface\\AddOns\\Guda\\Assets\\Sorting")
icon:SetTexCoord(0, 1, 0, 1) icon:SetTexCoord(0, 1, 0, 1)
end end
+38 -2
View File
@@ -1334,6 +1334,8 @@ local function ResetButtonVisualState(self)
if self.unusableOverlay then self.unusableOverlay:Hide() end if self.unusableOverlay then self.unusableOverlay:Hide() end
HideJunkIcon(self) HideJunkIcon(self)
if self.categoryMarkIcon then self.categoryMarkIcon:Hide() end if self.categoryMarkIcon then self.categoryMarkIcon:Hide() end
local chargesText = getglobal(self:GetName().."_Charges")
if chargesText then chargesText:Hide() end
-- Clear cooldown overlay -- Clear cooldown overlay
local cd = getglobal(self:GetName().."Cooldown") or self.cooldown local cd = getglobal(self:GetName().."Cooldown") or self.cooldown
@@ -1634,6 +1636,8 @@ local function ClearItemButton(self, emptySlotBg, countText, bagID)
end end
if countText then countText:Hide() end if countText then countText:Hide() end
local chargesText = getglobal(self:GetName().."_Charges")
if chargesText then chargesText:Hide() end
ResetSlotBorder(self) ResetSlotBorder(self)
HideInnerShadow(self.innerShadow) HideInnerShadow(self.innerShadow)
@@ -1681,6 +1685,7 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
-- Get UI elements -- Get UI elements
local countText = getglobal(self:GetName().."Count") local countText = getglobal(self:GetName().."Count")
local chargesText = getglobal(self:GetName().."_Charges")
local emptySlotBg = getglobal(self:GetName().."_EmptySlotBg") local emptySlotBg = getglobal(self:GetName().."_EmptySlotBg")
local Utils = addon and addon.Modules and addon.Modules.Utils 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
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) -- Get live metadata if in live mode (quality, link, lock status)
local itemQuality, itemLink, isLocked local itemQuality, itemLink, isLocked
if not self.isReadOnly and bagID and slotID and self.hasItem then 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 if self.hasItem then
-- Icon already set above based on mode (live vs cached) -- 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 -- Don't desaturate items from other characters since they're read-only anyway
if not self.otherChar and not self.isReadOnly then if not self.otherChar and not self.isReadOnly then
SetItemButtonDesaturated(self, isLocked, 0.5, 0.5, 0.5) 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
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 -- Handle tracking toggle on click
-- Note: Tracking toggle is now handled in the main OnClick script above to avoid conflicts -- Note: Tracking toggle is now handled in the main OnClick script above to avoid conflicts
-- and unified with QuestItemBar pinning logic. -- and unified with QuestItemBar pinning logic.
@@ -2369,7 +2405,7 @@ function Guda_ItemButton_OnEnter(self)
end end
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 if MerchantFrame:IsShown() and not self.isBank and not self.otherChar and self.hasItem then
ShowContainerSellCursor(self.bagID, self.slotID) ShowContainerSellCursor(self.bagID, self.slotID)
else else
+10
View File
@@ -37,6 +37,16 @@
</Anchor> </Anchor>
</Anchors> </Anchors>
</FontString> </FontString>
<FontString name="$parent_Charges" inherits="NumberFontNormal" justifyH="RIGHT" hidden="true">
<Color r="1" g="0.82" b="0"/>
<Anchors>
<Anchor point="BOTTOMRIGHT">
<Offset>
<AbsDimension x="-8" y="20"/>
</Offset>
</Anchor>
</Anchors>
</FontString>
<Texture name="$parent_Check" file="Interface\Buttons\UI-CheckBox-Check" hidden="true"> <Texture name="$parent_Check" file="Interface\Buttons\UI-CheckBox-Check" hidden="true">
<Size> <Size>
<AbsDimension x="24" y="24"/> <AbsDimension x="24" y="24"/>