644 lines
23 KiB
Lua
644 lines
23 KiB
Lua
-- Guda Item Detection
|
|
-- Centralized item property detection with caching
|
|
-- Used by: CategoryManager, SortEngine, QuestItemBar, ItemButton, BagFrame, BankFrame
|
|
|
|
local addon = Guda
|
|
|
|
local ItemDetection = {}
|
|
addon.Modules.ItemDetection = ItemDetection
|
|
|
|
--=====================================================
|
|
-- Detection Result Caching
|
|
-- 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
|
|
|
|
-- Clear the entire detection cache (use sparingly - only for major events)
|
|
-- For simple item moves, use InvalidateItem() or don't invalidate at all
|
|
function ItemDetection:ClearCache()
|
|
detectionCache = {}
|
|
chargesCache = {}
|
|
cacheHits = 0
|
|
cacheMisses = 0
|
|
end
|
|
|
|
-- Invalidate a specific item from cache (by itemLink)
|
|
-- Use this when a specific item's properties might have changed
|
|
function ItemDetection:InvalidateItem(itemLink)
|
|
if itemLink then
|
|
detectionCache[itemLink] = nil
|
|
end
|
|
end
|
|
|
|
-- Invalidate multiple items from cache
|
|
-- Use this for batch operations
|
|
function ItemDetection:InvalidateItems(itemLinks)
|
|
if itemLinks then
|
|
for _, link in ipairs(itemLinks) do
|
|
if link then
|
|
detectionCache[link] = nil
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check if we have cached data for an item (useful for debugging)
|
|
function ItemDetection:IsCached(itemLink)
|
|
return itemLink and detectionCache[itemLink] ~= nil
|
|
end
|
|
|
|
-- Get cache statistics
|
|
function ItemDetection:GetCacheStats()
|
|
local total = cacheHits + cacheMisses
|
|
local hitRate = total > 0 and (cacheHits / total * 100) or 0
|
|
local size = 0
|
|
for _ in pairs(detectionCache) do size = size + 1 end
|
|
return {
|
|
hits = cacheHits,
|
|
misses = cacheMisses,
|
|
total = total,
|
|
hitRate = hitRate,
|
|
size = size,
|
|
}
|
|
end
|
|
|
|
-- Generate cache key for an item
|
|
local function GetCacheKey(itemLink)
|
|
if not itemLink then return nil end
|
|
return itemLink
|
|
end
|
|
|
|
--=====================================================
|
|
-- Tooltip Scanning Helpers
|
|
-- Uses shared tooltip from Utils module
|
|
--=====================================================
|
|
|
|
-- Scan tooltip and return all text lines
|
|
local function ScanTooltipLines(bagID, slotID, itemLink)
|
|
-- Use shared tooltip from Utils module
|
|
local tooltip, tooltipName = addon.Modules.Utils:GetScanTooltip()
|
|
|
|
-- Ensure tooltip owner is set before each scan
|
|
tooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
|
tooltip:ClearLines()
|
|
|
|
-- Helper to safely call SetHyperlink (can fail with "Unknown link type")
|
|
local function SafeSetHyperlink(tip, link)
|
|
if not link then return false end
|
|
-- Extract just the item:XXXX portion for SetHyperlink
|
|
local _, _, itemString = string.find(link, "|H(item:[^|]+)|h")
|
|
if itemString then
|
|
local success = pcall(function() tip:SetHyperlink(itemString) end)
|
|
return success
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Set tooltip based on what we have
|
|
if bagID and slotID then
|
|
if bagID == -1 then
|
|
-- Bank main bag: SetInventoryItem doesn't work for bank slots
|
|
-- Use SetHyperlink with the item link instead
|
|
if not SafeSetHyperlink(tooltip, itemLink) then
|
|
return {}
|
|
end
|
|
elseif bagID >= 5 and bagID <= 10 then
|
|
-- Bank bags: Try SetBagItem first, it should work for bank bags
|
|
tooltip:SetBagItem(bagID, slotID)
|
|
else
|
|
-- Regular bags (0-4): SetBagItem works reliably
|
|
tooltip:SetBagItem(bagID, slotID)
|
|
end
|
|
elseif itemLink then
|
|
if not SafeSetHyperlink(tooltip, itemLink) then
|
|
return {}
|
|
end
|
|
else
|
|
return {}
|
|
end
|
|
|
|
local lines = {}
|
|
local numLines = tooltip:NumLines() or 0
|
|
|
|
for i = 1, numLines do
|
|
local leftLine = getglobal(tooltipName .. "TextLeft" .. i)
|
|
local rightLine = getglobal(tooltipName .. "TextRight" .. i)
|
|
|
|
local leftText = leftLine and leftLine:GetText() or ""
|
|
local rightText = rightLine and rightLine:GetText() or ""
|
|
|
|
-- Get left text colors (for detecting yellow/green/red text)
|
|
local lr, lg, lb, la = 1, 1, 1, 1
|
|
if leftLine and leftLine.GetTextColor then
|
|
lr, lg, lb, la = leftLine:GetTextColor()
|
|
end
|
|
|
|
-- Get right text colors (for detecting red requirements)
|
|
local rr, rg, rb, ra = 1, 1, 1, 1
|
|
if rightLine and rightLine.GetTextColor then
|
|
rr, rg, rb, ra = rightLine:GetTextColor()
|
|
end
|
|
|
|
table.insert(lines, {
|
|
left = leftText,
|
|
right = rightText,
|
|
leftLower = leftText and string.lower(leftText) or "",
|
|
rightLower = rightText and string.lower(rightText) or "",
|
|
r = lr, g = lg, b = lb,
|
|
rightR = rr, rightG = rg, rightB = rb,
|
|
})
|
|
end
|
|
|
|
return lines
|
|
end
|
|
|
|
--=====================================================
|
|
-- Core Detection Functions
|
|
--=====================================================
|
|
|
|
-- Check if item is a permanent enchant (enchanting scroll/vellum)
|
|
local function DetectPermanentEnchant(lines)
|
|
for _, line in ipairs(lines) do
|
|
if string.find(line.leftLower, "permanently") then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Check if item is a quest item and/or quest starter
|
|
local function DetectQuestItem(lines, itemData)
|
|
local isQuestItem = false
|
|
local isQuestStarter = false
|
|
|
|
-- Early exit: exclude known non-quest items misclassified by the API
|
|
if itemData and itemData.link and addon.Constants and addon.Constants.QUEST_CATEGORY_EXCLUSIONS then
|
|
local _, _, idStr = string.find(itemData.link, "item:(%d+)")
|
|
if idStr then
|
|
local id = tonumber(idStr)
|
|
if id and addon.Constants.QUEST_CATEGORY_EXCLUSIONS[id] then
|
|
return false, false
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Check item category first
|
|
if itemData and itemData.class == "Quest" then
|
|
isQuestItem = true
|
|
end
|
|
|
|
-- Scan tooltip for quest-related text
|
|
for _, line in ipairs(lines) do
|
|
local text = line.leftLower
|
|
|
|
-- Quest starter patterns (highest priority)
|
|
if string.find(text, "quest starter") or
|
|
string.find(text, "this item begins a quest") or
|
|
string.find(text, "begins a quest") or
|
|
string.find(text, "starts a quest") then
|
|
isQuestItem = true
|
|
isQuestStarter = true
|
|
end
|
|
|
|
-- Quest item patterns
|
|
if string.find(text, "quest item") then
|
|
isQuestItem = true
|
|
end
|
|
end
|
|
|
|
return isQuestItem, isQuestStarter
|
|
end
|
|
|
|
-- Check if item is junk (gray or white equippable without special properties)
|
|
local function DetectJunk(lines, itemData)
|
|
if not itemData then return false end
|
|
|
|
-- Ensure quality is a number
|
|
local quality = tonumber(itemData.quality)
|
|
local itemClass = itemData.class or ""
|
|
local itemSubclass = itemData.subclass or ""
|
|
local itemName = itemData.name or ""
|
|
local itemLink = itemData.link or ""
|
|
|
|
-- Gray items are always junk
|
|
-- Check quality value OR link color (gray = |cff9d9d9d)
|
|
if quality == 0 then
|
|
return true
|
|
end
|
|
|
|
-- Fallback: check link color for gray items
|
|
if itemLink and string.find(itemLink, "|cff9d9d9d") then
|
|
return true
|
|
end
|
|
|
|
-- If quality is still nil, try to determine from link color
|
|
if quality == nil then
|
|
if string.find(itemLink, "|cffffffff") then
|
|
quality = 1 -- White
|
|
else
|
|
return false -- Unknown quality, don't mark as junk
|
|
end
|
|
end
|
|
|
|
-- White equippable items might be junk (only if whiteItemsJunk setting is enabled)
|
|
local whiteItemsJunk = false
|
|
if addon and addon.Modules and addon.Modules.DB then
|
|
whiteItemsJunk = addon.Modules.DB:GetSetting("whiteItemsJunk")
|
|
end
|
|
if quality == 1 and whiteItemsJunk and (itemClass == "Weapon" or itemClass == "Armor") then
|
|
-- Exclusions: trinkets, rings, necklaces, tabards, shirts
|
|
local subLower = string.lower(itemSubclass)
|
|
if string.find(subLower, "trinket") or
|
|
string.find(subLower, "ring") or
|
|
string.find(subLower, "neck") or
|
|
string.find(subLower, "tabard") or
|
|
string.find(subLower, "shirt") then
|
|
return false
|
|
end
|
|
|
|
-- Check for profession tools by common names
|
|
local nameLower = string.lower(itemName)
|
|
if string.find(nameLower, "mining pick") or
|
|
string.find(nameLower, "skinning knife") or
|
|
string.find(nameLower, "blacksmith hammer") or
|
|
string.find(nameLower, "fishing pole") or
|
|
string.find(nameLower, "gnomish army knife") then
|
|
return false
|
|
end
|
|
|
|
-- Check for profession tool subtype
|
|
if string.find(subLower, "fishing pole") or
|
|
string.find(subLower, "mining pick") or
|
|
string.find(subLower, "skinning knife") then
|
|
return false
|
|
end
|
|
|
|
-- Check tooltip for special text (Use:, Equip:, green text)
|
|
for _, line in ipairs(lines) do
|
|
local text = line.leftLower
|
|
|
|
-- Yellow text (Use:, Equip:)
|
|
if line.r and line.g and line.b then
|
|
local isYellow = (line.r > 0.9 and line.g > 0.75 and line.b < 0.2)
|
|
local isGreen = (line.r < 0.2 and line.g > 0.9 and line.b < 0.2)
|
|
|
|
if isYellow and (string.find(text, "use:") or string.find(text, "equip:")) then
|
|
return false
|
|
end
|
|
|
|
if isGreen then
|
|
return false
|
|
end
|
|
end
|
|
end
|
|
|
|
-- White equippable without special properties = junk
|
|
return true
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
-- Check if item is usable for a quest (has yellow "Use:" text related to quest)
|
|
local function DetectQuestUsable(lines)
|
|
for _, line in ipairs(lines) do
|
|
-- Check for yellow "Use:" text
|
|
if line.r and line.g and line.b then
|
|
local isYellow = (line.r > 0.9 and line.g > 0.75 and line.b < 0.2)
|
|
if isYellow and line.leftLower and string.find(line.leftLower, "use:") then
|
|
return true
|
|
end
|
|
-- Also check for green "Use:" text (some quest items have green use text)
|
|
local isGreen = (line.r < 0.3 and line.g > 0.9 and line.b < 0.3)
|
|
if isGreen and line.leftLower and string.find(line.leftLower, "use:") then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
-- Debug function to dump tooltip lines for an item
|
|
-- Usage: /script Guda.Modules.ItemDetection:DebugTooltip(bagID, slotID)
|
|
function ItemDetection:DebugTooltip(bagID, slotID)
|
|
local itemLink = GetContainerItemLink(bagID, slotID)
|
|
if not itemLink then
|
|
addon:Print("No item at " .. bagID .. ":" .. slotID)
|
|
return
|
|
end
|
|
|
|
addon:Print("=== Tooltip Debug for " .. bagID .. ":" .. slotID .. " ===")
|
|
local lines = ScanTooltipLines(bagID, slotID, itemLink)
|
|
addon:Print("Total lines: " .. table.getn(lines))
|
|
|
|
for i, line in ipairs(lines) do
|
|
local text = line.left or "(no text)"
|
|
local r, g, b = line.r or 0, line.g or 0, line.b or 0
|
|
addon:Print(string.format("[%d] r=%.2f g=%.2f b=%.2f: %s", i, r, g, b, text))
|
|
end
|
|
addon:Print("=== End Tooltip Debug ===")
|
|
end
|
|
|
|
-- Durability pattern for filtering out broken item red text
|
|
local durabilityPattern = DURABILITY_TEMPLATE and string.gsub(DURABILITY_TEMPLATE, "%%d", "%%d+") or nil
|
|
|
|
-- Check if text color is red (unusable requirement)
|
|
local function IsRedColor(r, g, b)
|
|
if not r or not g or not b then return false end
|
|
-- RED_FONT_COLOR is typically (1.0, 0.1, 0.1)
|
|
local dr = math.abs(r - 1.0)
|
|
local dg = math.abs(g - 0.125)
|
|
local db = math.abs(b - 0.125)
|
|
return (dr < 0.15 and dg < 0.15 and db < 0.15)
|
|
end
|
|
|
|
-- Check if red text is a legitimate requirement (not just loading state or flavor text)
|
|
-- Returns true if text appears to be an actual unmet requirement
|
|
local function IsRequirementText(text)
|
|
if not text or text == "" then return false end
|
|
local lower = string.lower(text)
|
|
|
|
-- Known requirement patterns in vanilla WoW
|
|
if string.find(lower, "requires") then return true end -- "Requires Level 60", "Requires Class:", etc.
|
|
if string.find(lower, "require") then return true end -- Alternate forms
|
|
if string.find(lower, "classes:") then return true end -- "Classes: Warrior, Paladin"
|
|
if string.find(lower, "races:") then return true end -- "Races: Dwarf, Gnome"
|
|
if string.find(lower, "level %d") then return true end -- "Level 60" (without Requires)
|
|
if string.find(lower, "skill:") then return true end -- Profession requirements
|
|
if string.find(lower, "reputation") then return true end -- Reputation requirements
|
|
if string.find(lower, "riding") then return true end -- Riding skill
|
|
if string.find(lower, "already known") then return true end -- Recipe already known
|
|
|
|
-- Class names (when shown in red, indicates class restriction)
|
|
if string.find(lower, "warrior") then return true end
|
|
if string.find(lower, "paladin") then return true end
|
|
if string.find(lower, "hunter") then return true end
|
|
if string.find(lower, "rogue") then return true end
|
|
if string.find(lower, "priest") then return true end
|
|
if string.find(lower, "shaman") then return true end
|
|
if string.find(lower, "mage") then return true end
|
|
if string.find(lower, "warlock") then return true end
|
|
if string.find(lower, "druid") then return true end
|
|
|
|
-- Race names (when shown in red, indicates race restriction)
|
|
if string.find(lower, "human") then return true end
|
|
if string.find(lower, "dwarf") then return true end
|
|
if string.find(lower, "night elf") then return true end
|
|
if string.find(lower, "gnome") then return true end
|
|
if string.find(lower, "orc") then return true end
|
|
if string.find(lower, "undead") then return true end
|
|
if string.find(lower, "tauren") then return true end
|
|
if string.find(lower, "troll") then return true end
|
|
if string.find(lower, "goblin") then return true end -- Turtle WoW
|
|
if string.find(lower, "high elf") then return true end -- Turtle WoW
|
|
|
|
return false
|
|
end
|
|
|
|
-- Check if item is unusable (has red text indicating unmet requirements)
|
|
-- Excludes: durability lines, item name (line 1), non-requirement text
|
|
local function DetectUnusable(lines)
|
|
local numLines = table.getn(lines)
|
|
|
|
-- If tooltip has very few lines, data might not be loaded - be conservative
|
|
if numLines < 2 then
|
|
return false
|
|
end
|
|
|
|
-- Start from line 2 to skip the item name (line 1)
|
|
-- Item names can appear red when data isn't fully loaded
|
|
for i = 2, numLines do
|
|
local line = lines[i]
|
|
if line and line.r and line.g and line.b then
|
|
-- Check for red text (requirements not met)
|
|
local isRed = IsRedColor(line.r, line.g, line.b) or
|
|
(line.r > 0.85 and line.g < 0.3 and line.b < 0.3)
|
|
|
|
if isRed then
|
|
local text = line.left or ""
|
|
|
|
-- Ignore durability lines (broken items)
|
|
if durabilityPattern and string.find(text, durabilityPattern) then
|
|
-- skip durability
|
|
-- Only count as unusable if text looks like a requirement
|
|
elseif IsRequirementText(text) then
|
|
return true
|
|
end
|
|
-- If red but not a requirement pattern, ignore it (might be flavor text or loading state)
|
|
end
|
|
end
|
|
|
|
-- Also check right column (some requirements appear there, like "Warrior" class)
|
|
if line and line.rightR and line.rightG and line.rightB then
|
|
local isRed = IsRedColor(line.rightR, line.rightG, line.rightB) or
|
|
(line.rightR > 0.85 and line.rightG < 0.3 and line.rightB < 0.3)
|
|
|
|
if isRed then
|
|
local text = line.right or ""
|
|
if durabilityPattern and string.find(text, durabilityPattern) then
|
|
-- skip durability
|
|
elseif IsRequirementText(text) then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
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
|
|
--=====================================================
|
|
|
|
-- Get all item properties at once (cached)
|
|
-- Returns: { isQuestItem, isQuestStarter, isQuestUsable, isJunk, isPermanentEnchant, isUnusable }
|
|
function ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
|
if not itemData then
|
|
return {
|
|
isQuestItem = false,
|
|
isQuestStarter = false,
|
|
isQuestUsable = false,
|
|
isJunk = false,
|
|
isPermanentEnchant = false,
|
|
isUnusable = false,
|
|
}
|
|
end
|
|
|
|
local itemLink = itemData.link
|
|
local cacheKey = GetCacheKey(itemLink)
|
|
|
|
-- Check cache
|
|
if cacheKey and detectionCache[cacheKey] then
|
|
cacheHits = cacheHits + 1
|
|
return detectionCache[cacheKey]
|
|
end
|
|
cacheMisses = cacheMisses + 1
|
|
|
|
-- Scan tooltip once
|
|
local lines = ScanTooltipLines(bagID, slotID, itemLink)
|
|
local numLines = table.getn(lines)
|
|
|
|
-- Debug: log if tooltip scan failed
|
|
if numLines == 0 and addon.DEBUG then
|
|
addon:Debug("ItemDetection: No tooltip lines for %s (bag=%s, slot=%s)",
|
|
tostring(itemData.name or itemLink), tostring(bagID), tostring(slotID))
|
|
end
|
|
|
|
-- Check if tooltip data appears complete
|
|
-- A proper item tooltip should have at least 2 lines (name + something)
|
|
-- If tooltip is too short, data may not be fully loaded - don't cache
|
|
local tooltipLooksComplete = (numLines >= 2)
|
|
|
|
-- Detect all properties
|
|
local isPermanentEnchant = DetectPermanentEnchant(lines)
|
|
local isQuestItem, isQuestStarter = DetectQuestItem(lines, itemData)
|
|
local isQuestUsable = DetectQuestUsable(lines)
|
|
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)
|
|
local linkHasGray = itemLink and string.find(itemLink, "|cff9d9d9d")
|
|
if quality == 0 or linkHasGray then
|
|
addon:Debug("ItemDetection JUNK: %s quality=%s linkGray=%s isJunk=%s",
|
|
tostring(itemData.name), tostring(quality), tostring(linkHasGray), tostring(isJunk))
|
|
end
|
|
end
|
|
|
|
-- Permanent enchants are NOT quest items (even if categorized as Quest)
|
|
if isPermanentEnchant then
|
|
isQuestItem = false
|
|
isQuestStarter = false
|
|
isQuestUsable = false
|
|
end
|
|
|
|
local result = {
|
|
isQuestItem = isQuestItem,
|
|
isQuestStarter = isQuestStarter,
|
|
isQuestUsable = isQuestUsable,
|
|
isJunk = isJunk,
|
|
isPermanentEnchant = isPermanentEnchant,
|
|
isUnusable = isUnusable,
|
|
}
|
|
|
|
-- Only cache result if tooltip data appears complete
|
|
-- This prevents caching incorrect results from partially-loaded item data
|
|
if cacheKey and tooltipLooksComplete then
|
|
detectionCache[cacheKey] = result
|
|
end
|
|
|
|
return result
|
|
end
|
|
|
|
-- Convenience functions for single property checks
|
|
function ItemDetection:IsQuestItem(itemData, bagID, slotID)
|
|
local props = self:GetItemProperties(itemData, bagID, slotID)
|
|
return props.isQuestItem, props.isQuestStarter
|
|
end
|
|
|
|
function ItemDetection:IsQuestStarter(itemData, bagID, slotID)
|
|
local props = self:GetItemProperties(itemData, bagID, slotID)
|
|
return props.isQuestStarter
|
|
end
|
|
|
|
function ItemDetection:IsQuestUsable(itemData, bagID, slotID)
|
|
local props = self:GetItemProperties(itemData, bagID, slotID)
|
|
return props.isQuestUsable
|
|
end
|
|
|
|
function ItemDetection:IsJunk(itemData, bagID, slotID)
|
|
local props = self:GetItemProperties(itemData, bagID, slotID)
|
|
return props.isJunk
|
|
end
|
|
|
|
function ItemDetection:IsPermanentEnchant(itemData, bagID, slotID)
|
|
local props = self:GetItemProperties(itemData, bagID, slotID)
|
|
return props.isPermanentEnchant
|
|
end
|
|
|
|
function ItemDetection:IsUnusable(itemData, bagID, slotID)
|
|
local props = self:GetItemProperties(itemData, bagID, slotID)
|
|
return props.isUnusable
|
|
end
|
|
|
|
-- Cache-only lookup. Returns true/false when cached, nil when unknown
|
|
-- (no tooltip scan, no side effects). Lets the layout path skip tinting
|
|
-- on cold cache without blocking on ~80 tooltip scans.
|
|
function ItemDetection:IsUnusableCached(itemData)
|
|
if not itemData or not itemData.link then return nil end
|
|
local cacheKey = GetCacheKey(itemData.link)
|
|
if not cacheKey then return nil end
|
|
local props = detectionCache[cacheKey]
|
|
if not props then return nil end
|
|
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
|
|
--=====================================================
|
|
|
|
function ItemDetection:Initialize()
|
|
-- Clear cache when entering world (character switch)
|
|
addon.Modules.Events:Register("PLAYER_ENTERING_WORLD", function()
|
|
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
|