Item detection issues
This commit is contained in:
+12
-80
@@ -600,7 +600,12 @@ function CategoryManager:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar
|
||||
return isBoE == ruleValue
|
||||
|
||||
elseif ruleType == "isQuestItem" then
|
||||
-- Use consolidated quest detection from Utils
|
||||
-- Use centralized ItemDetection for quest item detection
|
||||
if addon.Modules.ItemDetection then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isQuestItem == ruleValue
|
||||
end
|
||||
-- Fallback to Utils if ItemDetection not available
|
||||
local isQuestItem, _ = addon.Modules.Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, false)
|
||||
return isQuestItem == ruleValue
|
||||
|
||||
@@ -662,86 +667,13 @@ function CategoryManager:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar
|
||||
return isProfessionTool == ruleValue
|
||||
|
||||
elseif ruleType == "isJunk" then
|
||||
-- Junk items: gray items (quality 0) OR white equippable items (quality 1 + Weapon/Armor)
|
||||
-- EXCLUDES:
|
||||
-- 1. Trinkets, Rings, Necklaces (these typically have special effects)
|
||||
-- 2. Profession tools (skinning knife, mining pick, fishing poles, etc.)
|
||||
-- 3. Items with yellow description text (Use:, Equip:, Chance on hit: effects)
|
||||
-- 4. Items with green description text (set bonuses, special properties)
|
||||
local quality = itemData.quality
|
||||
local isGray = false
|
||||
local isWhiteEquip = false
|
||||
|
||||
-- Check for gray items (quality 0)
|
||||
if quality == 0 then
|
||||
isGray = true
|
||||
elseif not isOtherChar and addon.Modules.Utils and addon.Modules.Utils.IsItemGrayTooltip then
|
||||
-- Tooltip fallback for gray detection
|
||||
isGray = addon.Modules.Utils:IsItemGrayTooltip(bagID, slotID, itemData.link)
|
||||
-- Use centralized ItemDetection for junk detection
|
||||
if addon.Modules.ItemDetection then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isJunk == ruleValue
|
||||
end
|
||||
|
||||
-- Check for white equippable items (quality 1 + Weapon/Armor)
|
||||
if quality == 1 then
|
||||
local itemClass = itemData.class or ""
|
||||
local itemSubclass = itemData.subclass or ""
|
||||
|
||||
if itemClass == "Weapon" or itemClass == "Armor" then
|
||||
-- In Turtle WoW, equip slot info is in itemSubType, not itemEquipLoc
|
||||
local itemSubclass = itemData.subclass or ""
|
||||
addon:Debug("isJunk check: name='%s', subclass='%s', class='%s'",
|
||||
tostring(itemData.name), tostring(itemSubclass), tostring(itemClass))
|
||||
|
||||
-- EXCLUDE: Trinkets, Rings, Necklaces, Tabards, Shirts - these typically have special effects or are cosmetic
|
||||
-- Check by itemSubType which contains INVTYPE_* values in Turtle WoW
|
||||
local isSpecialSlot = (itemSubclass == "INVTYPE_TRINKET" or
|
||||
itemSubclass == "INVTYPE_FINGER" or
|
||||
itemSubclass == "INVTYPE_NECK" or
|
||||
itemSubclass == "INVTYPE_TABARD" or
|
||||
itemSubclass == "INVTYPE_BODY")
|
||||
if isSpecialSlot then
|
||||
addon:Debug("isJunk: EXCLUDED (trinket/ring/neck/tabard/shirt) - %s (subclass=%s)", tostring(itemData.name), tostring(itemSubclass))
|
||||
return false == ruleValue
|
||||
end
|
||||
|
||||
-- Check if this is a profession tool (should NOT be junk)
|
||||
local isProfessionTool = false
|
||||
|
||||
-- Check by item ID
|
||||
if itemData.link then
|
||||
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
|
||||
if itemID and addon.Constants.PROFESSION_TOOL_IDS and addon.Constants.PROFESSION_TOOL_IDS[itemID] then
|
||||
isProfessionTool = true
|
||||
addon:Debug("isJunk: PROFESSION TOOL (by ID) - %s", tostring(itemData.name))
|
||||
end
|
||||
end
|
||||
|
||||
-- Check by subtype (e.g., Fishing Pole)
|
||||
if not isProfessionTool and addon.Constants.PROFESSION_TOOL_SUBTYPES and addon.Constants.PROFESSION_TOOL_SUBTYPES[itemSubclass] then
|
||||
isProfessionTool = true
|
||||
addon:Debug("isJunk: PROFESSION TOOL (by subtype) - %s", tostring(itemData.name))
|
||||
end
|
||||
|
||||
-- Check for special tooltip text (yellow/green descriptions)
|
||||
-- These items have Use:, Equip:, or special effects and should NOT be junk
|
||||
local hasSpecialText = false
|
||||
if not isProfessionTool and not isOtherChar then
|
||||
if addon.Modules.Utils and addon.Modules.Utils.HasSpecialTooltipText then
|
||||
hasSpecialText = addon.Modules.Utils:HasSpecialTooltipText(bagID, slotID, itemData.link)
|
||||
if hasSpecialText then
|
||||
addon:Debug("isJunk: HAS SPECIAL TEXT (Use:/Equip:/green) - %s", tostring(itemData.name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not isProfessionTool and not hasSpecialText then
|
||||
isWhiteEquip = true
|
||||
addon:Debug("isJunk: WHITE EQUIP DETECTED - %s", tostring(itemData.name))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local isJunk = isGray or isWhiteEquip
|
||||
return isJunk == ruleValue
|
||||
-- Fallback: gray items are always junk
|
||||
return (itemData.quality == 0) == ruleValue
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
-- 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 cacheHits = 0
|
||||
local cacheMisses = 0
|
||||
|
||||
-- Clear the detection cache
|
||||
function ItemDetection:ClearCache()
|
||||
detectionCache = {}
|
||||
cacheHits = 0
|
||||
cacheMisses = 0
|
||||
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
|
||||
--=====================================================
|
||||
|
||||
-- Get or create the scanning tooltip
|
||||
local scanTooltip = nil
|
||||
local function GetScanTooltip()
|
||||
if not scanTooltip then
|
||||
scanTooltip = CreateFrame("GameTooltip", "GudaItemDetectionTooltip", nil, "GameTooltipTemplate")
|
||||
scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
end
|
||||
return scanTooltip
|
||||
end
|
||||
|
||||
-- Scan tooltip and return all text lines
|
||||
local function ScanTooltipLines(bagID, slotID, itemLink)
|
||||
local tooltip = GetScanTooltip()
|
||||
local tooltipName = "GudaItemDetectionTooltip"
|
||||
|
||||
-- Ensure tooltip owner is set before each scan
|
||||
tooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
tooltip:ClearLines()
|
||||
|
||||
-- Set tooltip based on what we have
|
||||
if bagID and slotID then
|
||||
if bagID == -1 then
|
||||
-- Bank main bag: use SetInventoryItem (slot 39 + slotID)
|
||||
if tooltip.SetInventoryItem then
|
||||
tooltip:SetInventoryItem("player", 39 + slotID)
|
||||
else
|
||||
-- Fallback to hyperlink if SetInventoryItem not available
|
||||
if itemLink then
|
||||
tooltip:SetHyperlink(itemLink)
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
else
|
||||
-- Regular bags and bank bags (5-11)
|
||||
tooltip:SetBagItem(bagID, slotID)
|
||||
end
|
||||
elseif itemLink then
|
||||
tooltip:SetHyperlink(itemLink)
|
||||
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 text colors (for detecting yellow/green text)
|
||||
local lr, lg, lb, la = 1, 1, 1, 1
|
||||
if leftLine and leftLine.GetTextColor then
|
||||
lr, lg, lb, la = leftLine: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,
|
||||
})
|
||||
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
|
||||
|
||||
-- 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
|
||||
if quality == 1 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 string.find(line.leftLower, "use:") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--=====================================================
|
||||
-- Public API - Cached Detection
|
||||
--=====================================================
|
||||
|
||||
-- Get all item properties at once (cached)
|
||||
-- Returns: { isQuestItem, isQuestStarter, isQuestUsable, isJunk, isPermanentEnchant }
|
||||
function ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
if not itemData then
|
||||
return {
|
||||
isQuestItem = false,
|
||||
isQuestStarter = false,
|
||||
isQuestUsable = false,
|
||||
isJunk = false,
|
||||
isPermanentEnchant = 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)
|
||||
|
||||
-- Debug: log if tooltip scan failed
|
||||
if table.getn(lines) == 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
|
||||
|
||||
-- Detect all properties
|
||||
local isPermanentEnchant = DetectPermanentEnchant(lines)
|
||||
local isQuestItem, isQuestStarter = DetectQuestItem(lines, itemData)
|
||||
local isQuestUsable = DetectQuestUsable(lines)
|
||||
local isJunk = DetectJunk(lines, itemData)
|
||||
|
||||
-- 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,
|
||||
}
|
||||
|
||||
-- Cache result
|
||||
if cacheKey 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
|
||||
|
||||
--=====================================================
|
||||
-- Initialization
|
||||
--=====================================================
|
||||
|
||||
function ItemDetection:Initialize()
|
||||
-- Clear cache when entering world (character switch)
|
||||
addon.Modules.Events:Register("PLAYER_ENTERING_WORLD", function()
|
||||
self:ClearCache()
|
||||
end, "ItemDetection")
|
||||
|
||||
addon:Debug("ItemDetection module initialized")
|
||||
end
|
||||
@@ -15,6 +15,11 @@ function Main:Initialize()
|
||||
-- Initialize database
|
||||
addon.Modules.DB:Initialize()
|
||||
|
||||
-- Initialize item detection (before scanners, as they may use it)
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:Initialize()
|
||||
end
|
||||
|
||||
-- Initialize scanners
|
||||
addon.Modules.BagScanner:Initialize()
|
||||
addon.Modules.BankScanner:Initialize()
|
||||
@@ -178,6 +183,12 @@ function Main:SetupSlashCommands()
|
||||
addon:Print("Tooltip Cache: %d hits, %d misses (%.1f%% hit rate)",
|
||||
stats.hits, stats.misses, stats.hitRate)
|
||||
end
|
||||
-- Show item detection cache stats
|
||||
if addon.Modules.ItemDetection and addon.Modules.ItemDetection.GetCacheStats then
|
||||
local stats = addon.Modules.ItemDetection:GetCacheStats()
|
||||
addon:Print("ItemDetection Cache: %d hits, %d misses (%.1f%% hit rate, %d items)",
|
||||
stats.hits, stats.misses, stats.hitRate, stats.size)
|
||||
end
|
||||
|
||||
elseif msg == "perfreset" then
|
||||
-- Reset performance statistics
|
||||
@@ -193,6 +204,10 @@ function Main:SetupSlashCommands()
|
||||
if addon.Modules.Utils and addon.Modules.Utils.ClearTooltipCache then
|
||||
addon.Modules.Utils:ClearTooltipCache()
|
||||
end
|
||||
-- Clear item detection cache
|
||||
if addon.Modules.ItemDetection and addon.Modules.ItemDetection.ClearCache then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
|
||||
elseif msg == "help" then
|
||||
-- Show help
|
||||
|
||||
@@ -78,6 +78,14 @@ function BankScanner:GetBankData()
|
||||
end
|
||||
end
|
||||
dirtySlots = {}
|
||||
|
||||
-- Check for invalidated bags (nil entries) and rescan them
|
||||
for _, bagID in ipairs(addon.Constants.BANK_BAGS) do
|
||||
if bankCache[bagID] == nil then
|
||||
bankCache[bagID] = self:ScanBankBag(bagID)
|
||||
end
|
||||
end
|
||||
|
||||
return bankCache
|
||||
end
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ Core\Database.lua
|
||||
DB\QuestItems.lua
|
||||
Core\Events.lua
|
||||
Core\Utils.lua
|
||||
Core\ItemDetection.lua
|
||||
Core\CategoryManager.lua
|
||||
Core\Tooltip.lua
|
||||
|
||||
|
||||
+130
-90
@@ -236,24 +236,45 @@ local function GetItemProperties(bagID, slotID, itemLink)
|
||||
end
|
||||
|
||||
-- Check if an item is a quest item by scanning its tooltip
|
||||
local function IsQuestItemTooltip(bagID, slotID)
|
||||
-- Uses centralized ItemDetection when available
|
||||
local function IsQuestItemTooltip(bagID, slotID, itemData)
|
||||
if not bagID or not slotID then return false end
|
||||
-- Use ItemDetection if available
|
||||
if addon.Modules.ItemDetection and itemData then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isQuestItem
|
||||
end
|
||||
-- Fallback to local GetItemProperties
|
||||
local link = GetContainerItemLink(bagID, slotID)
|
||||
local props = GetItemProperties(bagID, slotID, link)
|
||||
return props and props.isQuest or false
|
||||
end
|
||||
|
||||
-- Check if a quest item is usable (has 'Use:' or click to text)
|
||||
local function IsQuestItemUsable(bagID, slotID)
|
||||
-- Uses centralized ItemDetection when available
|
||||
local function IsQuestItemUsable(bagID, slotID, itemData)
|
||||
if not bagID or not slotID then return false end
|
||||
-- Use ItemDetection if available
|
||||
if addon.Modules.ItemDetection and itemData then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isQuestUsable
|
||||
end
|
||||
-- Fallback to local GetItemProperties
|
||||
local link = GetContainerItemLink(bagID, slotID)
|
||||
local props = GetItemProperties(bagID, slotID, link)
|
||||
return props and props.isQuestUsable or false
|
||||
end
|
||||
|
||||
-- Check if item is a quest starter (explicit 'Starts a Quest' or 'This Item Begins a Quest')
|
||||
local function IsQuestItemStarter(bagID, slotID)
|
||||
-- Uses centralized ItemDetection when available
|
||||
local function IsQuestItemStarter(bagID, slotID, itemData)
|
||||
if not bagID or not slotID then return false end
|
||||
-- Use ItemDetection if available
|
||||
if addon.Modules.ItemDetection and itemData then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isQuestStarter
|
||||
end
|
||||
-- Fallback to local GetItemProperties
|
||||
local link = GetContainerItemLink(bagID, slotID)
|
||||
local props = GetItemProperties(bagID, slotID, link)
|
||||
return props and props.isQuestStarter or false
|
||||
@@ -635,52 +656,42 @@ local function AddSortKeys(items)
|
||||
item.isMount = isMount
|
||||
|
||||
-- Class and slot ordering
|
||||
-- Check for items that should be treated as junk:
|
||||
-- 1. Gray items (quality 0)
|
||||
-- 2. Items with gray tooltip
|
||||
-- 3. White equippable items (quality 1 Weapon/Armor) - vendor trash
|
||||
-- EXCLUDES from junk:
|
||||
-- - Trinkets, Rings, Necklaces (these typically have special effects)
|
||||
-- - Profession tools (skinning knife, mining pick, fishing poles, etc.)
|
||||
-- - Items with yellow description text (Use:, Equip:, Chance on hit: effects)
|
||||
-- - Items with green description text (set bonuses, special properties)
|
||||
local isGrayItem = itemRarity == 0 or IsItemGrayTooltip(item.bagID, item.slot, item.data.link)
|
||||
local isWhiteEquip = false
|
||||
-- Check for items that should be treated as junk
|
||||
-- Use centralized ItemDetection when available
|
||||
local isJunkItem = false
|
||||
if addon.Modules.ItemDetection then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(item.data, item.bagID, item.slot)
|
||||
isJunkItem = props.isJunk
|
||||
else
|
||||
-- Fallback: manual junk detection
|
||||
local isGrayItem = itemRarity == 0 or IsItemGrayTooltip(item.bagID, item.slot, item.data.link)
|
||||
local isWhiteEquip = false
|
||||
|
||||
if itemRarity == 1 and (itemCategory == "Weapon" or itemCategory == "Armor") then
|
||||
-- In Turtle WoW, equip slot info is in itemSubType, not itemEquipLoc
|
||||
addon:Debug("SortEngine isJunk: name='%s', subclass='%s', class='%s'",
|
||||
tostring(itemName), tostring(itemSubType), tostring(itemCategory))
|
||||
if itemRarity == 1 and (itemCategory == "Weapon" or itemCategory == "Armor") then
|
||||
addon:Debug("SortEngine isJunk: name='%s', subclass='%s', class='%s'",
|
||||
tostring(itemName), tostring(itemSubType), tostring(itemCategory))
|
||||
|
||||
-- EXCLUDE: Trinkets, Rings, Necklaces, Tabards, Shirts - these typically have special effects or are cosmetic
|
||||
-- Check by itemSubType which contains INVTYPE_* values in Turtle WoW
|
||||
local isSpecialSlot = (itemSubType == "INVTYPE_TRINKET" or
|
||||
itemSubType == "INVTYPE_FINGER" or
|
||||
itemSubType == "INVTYPE_NECK" or
|
||||
itemSubType == "INVTYPE_TABARD" or
|
||||
itemSubType == "INVTYPE_BODY")
|
||||
local isSpecialSlot = (itemSubType == "INVTYPE_TRINKET" or
|
||||
itemSubType == "INVTYPE_FINGER" or
|
||||
itemSubType == "INVTYPE_NECK" or
|
||||
itemSubType == "INVTYPE_TABARD" or
|
||||
itemSubType == "INVTYPE_BODY")
|
||||
|
||||
if isSpecialSlot then
|
||||
addon:Debug("SortEngine: EXCLUDED (special slot) - %s (subclass=%s)", tostring(itemName), tostring(itemSubType))
|
||||
end
|
||||
|
||||
if not isSpecialSlot then
|
||||
-- Check exclusions for white equippable items
|
||||
local isProfTool = IsProfessionTool(item.data.link, itemSubType)
|
||||
local hasSpecialText = false
|
||||
|
||||
if not isProfTool then
|
||||
hasSpecialText = HasSpecialTooltipText(item.bagID, item.slot, item.data.link)
|
||||
end
|
||||
|
||||
-- Only mark as junk if NOT a profession tool AND NOT has special text
|
||||
if not isProfTool and not hasSpecialText then
|
||||
isWhiteEquip = true
|
||||
if not isSpecialSlot then
|
||||
local isProfTool = IsProfessionTool(item.data.link, itemSubType)
|
||||
local hasSpecialText = false
|
||||
if not isProfTool then
|
||||
hasSpecialText = HasSpecialTooltipText(item.bagID, item.slot, item.data.link)
|
||||
end
|
||||
if not isProfTool and not hasSpecialText then
|
||||
isWhiteEquip = true
|
||||
end
|
||||
end
|
||||
end
|
||||
isJunkItem = isGrayItem or isWhiteEquip
|
||||
end
|
||||
|
||||
if isGrayItem or isWhiteEquip then
|
||||
if isJunkItem then
|
||||
item.sortedClass = CATEGORY_ORDER["Junk"] or 99
|
||||
item.equipSlotOrder = 999
|
||||
item.isEquippable = false -- Treat junk gear as junk, not gear
|
||||
@@ -704,7 +715,7 @@ local function AddSortKeys(items)
|
||||
local nameLower = string.lower(item.itemName)
|
||||
if string.find(nameLower, "manual") or string.find(nameLower, "quest") then
|
||||
item.sortedClass = CATEGORY_ORDER["Quest"] or 7
|
||||
elseif IsQuestItemTooltip(item.bagID, item.slot) then
|
||||
elseif IsQuestItemTooltip(item.bagID, item.slot, item.data) then
|
||||
item.sortedClass = CATEGORY_ORDER["Quest"] or 7
|
||||
elseif addon.IsQuestItemByID then
|
||||
-- Check QuestItemsDB for faction-specific quest items
|
||||
@@ -739,10 +750,10 @@ local function AddSortKeys(items)
|
||||
item.isPermanentEnchant = isPermanentEnchant -- Store for potential use in sorting
|
||||
local nameLower = string.lower(item.itemName)
|
||||
if not isPermanentEnchant then
|
||||
if itemCategory == "Quest" or string.find(nameLower, "quest") or item.data.class == "Quest" or IsQuestItemTooltip(item.bagID, item.slot) then
|
||||
if itemCategory == "Quest" or string.find(nameLower, "quest") or item.data.class == "Quest" or IsQuestItemTooltip(item.bagID, item.slot, item.data) then
|
||||
item.isQuest = true
|
||||
if IsQuestItemStarter(item.bagID, item.slot) then item.isQuestStarter = true end
|
||||
if IsQuestItemUsable(item.bagID, item.slot) then item.isQuestUsable = true end
|
||||
if IsQuestItemStarter(item.bagID, item.slot, item.data) then item.isQuestStarter = true end
|
||||
if IsQuestItemUsable(item.bagID, item.slot, item.data) then item.isQuestUsable = true end
|
||||
elseif addon.IsQuestItemByID then
|
||||
-- Check QuestItemsDB for faction-specific quest items
|
||||
local playerFaction = UnitFactionGroup("player")
|
||||
@@ -1167,57 +1178,47 @@ end
|
||||
|
||||
-- Split a list of collected items into non-junk and junk items
|
||||
-- Junk includes: gray items (quality 0), gray tooltip items, white equippable items (quality 1 Weapon/Armor)
|
||||
-- EXCLUDES from junk:
|
||||
-- 1. Trinkets, Rings, Necklaces (these typically have special effects)
|
||||
-- 2. Profession tools (skinning knife, mining pick, fishing poles, etc.)
|
||||
-- 3. Items with yellow description text (Use:, Equip:, Chance on hit: effects)
|
||||
-- 4. Items with green description text (set bonuses, special properties)
|
||||
-- Uses centralized ItemDetection when available
|
||||
local function SplitGreyItems(items)
|
||||
local nonGreys, greys = {}, {}
|
||||
for _, item in ipairs(items) do
|
||||
-- Use same logic as AddSortKeys for determining Junk status (stability)
|
||||
local quality = tonumber(item.quality or 0)
|
||||
local isGray = quality == 0 or IsItemGrayTooltip(item.bagID, item.slot, item.data.link)
|
||||
-- White equippable items (Weapon/Armor) are also treated as junk
|
||||
local itemClass = item.class or ""
|
||||
local itemSubclass = item.data and item.data.subclass or ""
|
||||
local itemLink = item.data and item.data.link
|
||||
local isWhiteEquip = false
|
||||
local isJunk = false
|
||||
|
||||
if quality == 1 and (itemClass == "Weapon" or itemClass == "Armor") then
|
||||
-- In Turtle WoW, equip slot info is in itemSubType (stored as subclass), not equipLoc
|
||||
addon:Debug("SplitGreyItems isJunk: name='%s', subclass='%s', class='%s'",
|
||||
tostring(item.data and item.data.name), tostring(itemSubclass), tostring(itemClass))
|
||||
-- Use centralized ItemDetection when available
|
||||
if addon.Modules.ItemDetection and item.data then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(item.data, item.bagID, item.slot)
|
||||
isJunk = props.isJunk
|
||||
else
|
||||
-- Fallback: manual junk detection
|
||||
local quality = tonumber(item.quality or 0)
|
||||
local isGray = quality == 0 or IsItemGrayTooltip(item.bagID, item.slot, item.data and item.data.link)
|
||||
local itemClass = item.class or ""
|
||||
local itemSubclass = item.data and item.data.subclass or ""
|
||||
local itemLink = item.data and item.data.link
|
||||
local isWhiteEquip = false
|
||||
|
||||
-- EXCLUDE: Trinkets, Rings, Necklaces, Tabards, Shirts - these typically have special effects or are cosmetic
|
||||
-- Check by subclass which contains INVTYPE_* values in Turtle WoW
|
||||
local isSpecialSlot = (itemSubclass == "INVTYPE_TRINKET" or
|
||||
itemSubclass == "INVTYPE_FINGER" or
|
||||
itemSubclass == "INVTYPE_NECK" or
|
||||
itemSubclass == "INVTYPE_TABARD" or
|
||||
itemSubclass == "INVTYPE_BODY")
|
||||
if quality == 1 and (itemClass == "Weapon" or itemClass == "Armor") then
|
||||
local isSpecialSlot = (itemSubclass == "INVTYPE_TRINKET" or
|
||||
itemSubclass == "INVTYPE_FINGER" or
|
||||
itemSubclass == "INVTYPE_NECK" or
|
||||
itemSubclass == "INVTYPE_TABARD" or
|
||||
itemSubclass == "INVTYPE_BODY")
|
||||
|
||||
if isSpecialSlot then
|
||||
addon:Debug("SplitGreyItems: EXCLUDED (special slot) - %s (subclass='%s')", tostring(item.data and item.data.name), tostring(itemSubclass))
|
||||
end
|
||||
|
||||
if not isSpecialSlot then
|
||||
-- Check exclusions for white equippable items
|
||||
local isProfTool = IsProfessionTool(itemLink, itemSubclass)
|
||||
local hasSpecialText = false
|
||||
|
||||
if not isProfTool then
|
||||
hasSpecialText = HasSpecialTooltipText(item.bagID, item.slot, itemLink)
|
||||
end
|
||||
|
||||
-- Only mark as junk if NOT a profession tool AND NOT has special text
|
||||
if not isProfTool and not hasSpecialText then
|
||||
isWhiteEquip = true
|
||||
if not isSpecialSlot then
|
||||
local isProfTool = IsProfessionTool(itemLink, itemSubclass)
|
||||
local hasSpecialText = false
|
||||
if not isProfTool then
|
||||
hasSpecialText = HasSpecialTooltipText(item.bagID, item.slot, itemLink)
|
||||
end
|
||||
if not isProfTool and not hasSpecialText then
|
||||
isWhiteEquip = true
|
||||
end
|
||||
end
|
||||
end
|
||||
isJunk = isGray or isWhiteEquip
|
||||
end
|
||||
|
||||
if isGray or isWhiteEquip then
|
||||
if isJunk then
|
||||
table.insert(greys, item)
|
||||
else
|
||||
table.insert(nonGreys, item)
|
||||
@@ -1464,6 +1465,12 @@ function SortEngine:SortBags()
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear caches after sorting to ensure fresh detection on next UI update
|
||||
addon.Modules.BagScanner:ClearCache()
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
|
||||
-- Return total moves made
|
||||
return routeCount + consolidateCount + specializedMoves + regularMoves
|
||||
end
|
||||
@@ -1595,6 +1602,12 @@ function SortEngine:SortBank()
|
||||
end
|
||||
end
|
||||
|
||||
-- Clear caches after sorting to ensure fresh detection on next UI update
|
||||
addon.Modules.BankScanner:ClearCache()
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
|
||||
-- Return total moves made
|
||||
return routeCount + consolidateCount + specializedMoves + regularMoves
|
||||
end
|
||||
@@ -1651,7 +1664,7 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
-- Sorting is complete!
|
||||
addon:DebugSort("%s sort complete! (%d passes, %d total moves)", sortType, passCount, totalMoves)
|
||||
|
||||
-- Final update
|
||||
-- Final update (cache clearing happens after delay)
|
||||
local frame = CreateFrame("Frame")
|
||||
local startTime = GetTime()
|
||||
frame:SetScript("OnUpdate", function()
|
||||
@@ -1659,6 +1672,15 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
frame:SetScript("OnUpdate", nil)
|
||||
SortEngine.sortingInProgress = false
|
||||
SortEngine:UpdateSortButtonState(false)
|
||||
-- Clear caches after sorting to ensure fresh detection
|
||||
if sortType == "bank" then
|
||||
addon.Modules.BankScanner:ClearCache()
|
||||
else
|
||||
addon.Modules.BagScanner:ClearCache()
|
||||
end
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
updateFrame()
|
||||
end
|
||||
end)
|
||||
@@ -1667,7 +1689,7 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
addon:DebugSort("%s sort stopped at safety limit! (%d/%d items still need sorting after %d passes)",
|
||||
sortType, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems, passCount)
|
||||
|
||||
-- Final update
|
||||
-- Final update (cache clearing happens after delay)
|
||||
local frame = CreateFrame("Frame")
|
||||
local startTime = GetTime()
|
||||
frame:SetScript("OnUpdate", function()
|
||||
@@ -1675,6 +1697,15 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
frame:SetScript("OnUpdate", nil)
|
||||
SortEngine.sortingInProgress = false
|
||||
SortEngine:UpdateSortButtonState(false)
|
||||
-- Clear caches after sorting to ensure fresh detection
|
||||
if sortType == "bank" then
|
||||
addon.Modules.BankScanner:ClearCache()
|
||||
else
|
||||
addon.Modules.BagScanner:ClearCache()
|
||||
end
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
updateFrame()
|
||||
end
|
||||
end)
|
||||
@@ -1690,7 +1721,7 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
addon:DebugSort("%s sort stopped due to no progress after %d passes (items remaining: %d/%d)",
|
||||
sortType, passCount, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems)
|
||||
|
||||
-- Final update
|
||||
-- Final update (cache clearing happens after delay)
|
||||
local frame = CreateFrame("Frame")
|
||||
local startTime = GetTime()
|
||||
frame:SetScript("OnUpdate", function()
|
||||
@@ -1698,6 +1729,15 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
frame:SetScript("OnUpdate", nil)
|
||||
SortEngine.sortingInProgress = false
|
||||
SortEngine:UpdateSortButtonState(false)
|
||||
-- Clear caches after sorting to ensure fresh detection
|
||||
if sortType == "bank" then
|
||||
addon.Modules.BankScanner:ClearCache()
|
||||
else
|
||||
addon.Modules.BagScanner:ClearCache()
|
||||
end
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
updateFrame()
|
||||
end
|
||||
end)
|
||||
|
||||
+17
-4
@@ -1630,13 +1630,22 @@ function BankFrame:Initialize()
|
||||
if currentViewChar then return end
|
||||
|
||||
if event == "PLAYERBANKSLOTS_CHANGED" and arg1 then
|
||||
-- Mark specific slot as dirty in main bank (bagID = -1)
|
||||
addon.Modules.BankScanner:MarkSlotDirty(-1, arg1)
|
||||
-- Invalidate the entire main bank bag to ensure fresh data
|
||||
-- (MarkSlotDirty was causing timing issues with item data)
|
||||
addon.Modules.BankScanner:InvalidateBag(-1)
|
||||
-- Clear ItemDetection cache to ensure fresh detection after item swap
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
elseif event == "BAG_UPDATE" and arg1 then
|
||||
-- Check if this is a bank bag (5-10)
|
||||
if arg1 >= 5 and arg1 <= 10 then
|
||||
-- Invalidate the specific bank bag (we don't know which slot)
|
||||
-- Invalidate the specific bank bag
|
||||
addon.Modules.BankScanner:InvalidateBag(arg1)
|
||||
-- Clear ItemDetection cache to ensure fresh detection after item swap
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
else
|
||||
-- Not a bank bag, ignore for bank frame
|
||||
return
|
||||
@@ -1645,9 +1654,13 @@ function BankFrame:Initialize()
|
||||
-- Bank container slot changed (bag added/removed)
|
||||
-- Must clear entire cache since bag structure changed
|
||||
addon.Modules.BankScanner:ClearCache()
|
||||
if addon.Modules.ItemDetection then
|
||||
addon.Modules.ItemDetection:ClearCache()
|
||||
end
|
||||
end
|
||||
|
||||
ScheduleBankFrameUpdate(0.1)
|
||||
-- Slightly longer delay to ensure WoW API has updated
|
||||
ScheduleBankFrameUpdate(0.15)
|
||||
end)
|
||||
|
||||
end
|
||||
|
||||
+37
-4
@@ -9,8 +9,14 @@ local scanTooltip = CreateFrame("GameTooltip", "Guda_QuestScanTooltip", nil, "Ga
|
||||
scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
|
||||
-- Helper function to check if an item is a quest item
|
||||
-- Delegates to consolidated Utils:IsQuestItem() function
|
||||
local function IsQuestItem(bagID, slotID, isBank)
|
||||
-- Uses centralized ItemDetection module
|
||||
local function IsQuestItem(bagID, slotID, isBank, itemData)
|
||||
-- Use ItemDetection if available
|
||||
if addon and addon.Modules and addon.Modules.ItemDetection then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isQuestItem, props.isQuestStarter
|
||||
end
|
||||
-- Fallback to Utils
|
||||
if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.IsQuestItem then
|
||||
return addon.Modules.Utils:IsQuestItem(bagID, slotID, nil, false, isBank)
|
||||
end
|
||||
@@ -363,7 +369,7 @@ function Guda_ItemButton_OnLoad(self)
|
||||
if link and addon and addon.Modules and addon.Modules.Utils then
|
||||
local itemID = addon.Modules.Utils:ExtractItemID(link)
|
||||
if itemID then
|
||||
local isQuest = IsQuestItem(this.bagID, this.slotID, this.isBank)
|
||||
local isQuest = IsQuestItem(this.bagID, this.slotID, this.isBank, this.itemData)
|
||||
local isUnique = addon.Modules.Utils:IsUniqueItem(this.bagID, this.slotID, link)
|
||||
|
||||
-- Only pin to QuestItemBar if it's a unique quest item
|
||||
@@ -958,6 +964,33 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
|
||||
itemLink = GetContainerItemLink(bagID, slotID)
|
||||
itemQuality = quality
|
||||
isLocked = locked
|
||||
|
||||
-- Ensure itemData is populated for live items (needed for ItemDetection)
|
||||
if itemLink then
|
||||
local itemName, _, itemRarity, itemLevel, itemMinLevel, itemType, itemSubType, itemStackCount, itemEquipLoc, itemTexture = GetItemInfo(itemLink)
|
||||
if not itemData then
|
||||
-- Create new itemData
|
||||
if itemName then
|
||||
itemData = {
|
||||
link = itemLink,
|
||||
name = itemName,
|
||||
quality = itemRarity or quality or 0,
|
||||
class = itemType,
|
||||
subclass = itemSubType,
|
||||
texture = itemTexture,
|
||||
count = 1,
|
||||
}
|
||||
self.itemData = itemData
|
||||
end
|
||||
else
|
||||
-- Update existing itemData with missing fields
|
||||
if not itemData.link then itemData.link = itemLink end
|
||||
if itemData.quality == nil then itemData.quality = itemRarity or quality or 0 end
|
||||
if not itemData.class and itemType then itemData.class = itemType end
|
||||
if not itemData.subclass and itemSubType then itemData.subclass = itemSubType end
|
||||
if not itemData.name and itemName then itemData.name = itemName end
|
||||
end
|
||||
end
|
||||
elseif itemData then
|
||||
-- Use cached metadata from database
|
||||
itemQuality = itemData.quality
|
||||
@@ -1071,7 +1104,7 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
|
||||
end
|
||||
|
||||
if not self.otherChar and not self.isReadOnly then
|
||||
local isQuest, isQuestStarter = IsQuestItem(bagID, slotID)
|
||||
local isQuest, isQuestStarter = IsQuestItem(bagID, slotID, self.isBank, itemData)
|
||||
-- Update quest icon with the appropriate texture
|
||||
Guda_ItemButton_UpdateQuestIcon(self, isQuest, isQuestStarter)
|
||||
if self.questBorder then
|
||||
|
||||
+19
-87
@@ -14,103 +14,35 @@ local questItems = {}
|
||||
local flyoutButtons = {}
|
||||
local flyoutFrame
|
||||
|
||||
-- Create a hidden tooltip for scanning
|
||||
local scanTooltip
|
||||
local function GetScanTooltip()
|
||||
if not scanTooltip then
|
||||
scanTooltip = CreateFrame("GameTooltip", "Guda_QuestBarScanTooltip", nil, "GameTooltipTemplate")
|
||||
scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||
end
|
||||
return scanTooltip
|
||||
end
|
||||
--=====================================================
|
||||
-- Quest Item Detection (using centralized ItemDetection)
|
||||
--=====================================================
|
||||
|
||||
-- Combined function to check if an item is a quest item AND usable in ONE tooltip scan
|
||||
-- This avoids the expensive double-scan that was causing lag
|
||||
-- Combined function to check if an item is a quest item AND usable
|
||||
-- Uses centralized ItemDetection module for consistent detection
|
||||
function QuestItemBar:CheckQuestItemUsable(bagID, slotID)
|
||||
if not bagID or not slotID then return false, false, false end
|
||||
|
||||
local tooltip = GetScanTooltip()
|
||||
tooltip:ClearLines()
|
||||
tooltip:SetBagItem(bagID, slotID)
|
||||
|
||||
local isQuestItem = false
|
||||
local isQuestStarter = false
|
||||
local isUsable = false
|
||||
local isPermanentEnchant = false -- Track enchanting scrolls/vellums
|
||||
|
||||
for i = 1, tooltip:NumLines() do
|
||||
local line = getglobal("Guda_QuestBarScanTooltipTextLeft" .. i)
|
||||
if line then
|
||||
local text = line:GetText()
|
||||
if text then
|
||||
local tl = string.lower(text)
|
||||
-- Check for quest item indicators
|
||||
if string.find(text, "Quest Starter") or
|
||||
string.find(text, "This Item Begins a Quest") or
|
||||
string.find(text, "Use: Starts a Quest") then
|
||||
isQuestItem = true
|
||||
isQuestStarter = true
|
||||
isUsable = true -- Quest starters are always usable
|
||||
elseif string.find(text, "Quest Item") then
|
||||
isQuestItem = true
|
||||
end
|
||||
-- Check for usability (case-insensitive)
|
||||
if string.find(tl, "use:") or string.find(tl, "begins a quest") or string.find(tl, "starts a quest") then
|
||||
isUsable = true
|
||||
end
|
||||
-- Check for permanent enchant items (should NOT be quest items)
|
||||
-- Just check for "permanently" anywhere (green text doesn't have "Use:" prefix)
|
||||
if string.find(tl, "permanently") then
|
||||
isPermanentEnchant = true
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Get item data for ItemDetection
|
||||
local itemData = nil
|
||||
if addon.Modules.BagScanner then
|
||||
itemData = addon.Modules.BagScanner:ScanSlot(bagID, slotID)
|
||||
end
|
||||
|
||||
-- Permanent enchant items are NOT quest items, even if categorized as Quest
|
||||
if isPermanentEnchant then
|
||||
return false, false, false
|
||||
-- Use centralized ItemDetection
|
||||
if addon.Modules.ItemDetection and itemData then
|
||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||
return props.isQuestItem, props.isQuestStarter, props.isQuestUsable
|
||||
end
|
||||
|
||||
-- Fallback check for quest category if not detected from tooltip
|
||||
local link = GetContainerItemLink(bagID, slotID)
|
||||
local itemID
|
||||
local itemCategory, itemType
|
||||
|
||||
if link and addon.Modules.Utils and addon.Modules.Utils.ExtractItemID and addon.Modules.Utils.GetItemInfoSafe then
|
||||
itemID = addon.Modules.Utils:ExtractItemID(link)
|
||||
if itemID then
|
||||
_, _, _, _, itemCategory, itemType = addon.Modules.Utils:GetItemInfoSafe(itemID)
|
||||
end
|
||||
-- Fallback to Utils if ItemDetection not available
|
||||
if addon.Modules.Utils and addon.Modules.Utils.IsQuestItem then
|
||||
local isQuestItem, isQuestStarter = addon.Modules.Utils:IsQuestItem(bagID, slotID, nil, false, false)
|
||||
-- For usability fallback, check if it's a quest item (assume usable)
|
||||
return isQuestItem, isQuestStarter, isQuestItem
|
||||
end
|
||||
|
||||
-- If it's a Weapon or Armor, it shouldn't be a QuestItem unless it's specifically categorized as Quest
|
||||
-- This avoids "Use:" equipment showing up in the quest bar
|
||||
if itemCategory == "Weapon" or itemCategory == "Armor" or itemType == "Weapon" or itemType == "Armor" then
|
||||
if itemCategory ~= "Quest" and itemType ~= "Quest" then
|
||||
isQuestItem = false
|
||||
isQuestStarter = false
|
||||
end
|
||||
end
|
||||
|
||||
if not isQuestItem then
|
||||
if itemCategory == "Quest" or itemType == "Quest" then
|
||||
isQuestItem = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Check the QuestItemsDB for known faction-specific quest items
|
||||
if not isQuestItem then
|
||||
if itemID and addon.IsQuestItemByID then
|
||||
local playerFaction = UnitFactionGroup("player")
|
||||
local isDBQuestItem = addon:IsQuestItemByID(itemID, playerFaction)
|
||||
if isDBQuestItem then
|
||||
isQuestItem = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return isQuestItem, isQuestStarter, isUsable
|
||||
return false, false, false
|
||||
end
|
||||
|
||||
-- Scan bags for quest items (optimized: single tooltip scan per item)
|
||||
|
||||
Reference in New Issue
Block a user