refactory: redunant code

This commit is contained in:
Salikh Gurgenidze
2026-01-19 17:41:57 +04:00
parent 28307aa1a2
commit 60e2d99799
7 changed files with 959 additions and 723 deletions
+2 -24
View File
@@ -562,30 +562,8 @@ function CategoryManager:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar
return isBoE == ruleValue
elseif ruleType == "isQuestItem" then
local isQuestItem = false
local itemCategory = itemData.class or itemData.category or ""
local itemType = itemData.type or ""
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor")
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
if isQuestCategory then
isQuestItem = true
elseif not isOtherChar then
isQuestItem = addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)
if isQuestItem and isEquipment and not isQuestCategory then
isQuestItem = false
end
end
-- Also check QuestItemsDB
if not isQuestItem and itemData.link then
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
if itemID and addon.IsQuestItemByID then
local playerFaction = UnitFactionGroup("player")
isQuestItem = addon:IsQuestItemByID(itemID, playerFaction)
end
end
-- Use consolidated quest detection from Utils
local isQuestItem, _ = addon.Modules.Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, false)
return isQuestItem == ruleValue
elseif ruleType == "texturePattern" then
+166
View File
@@ -0,0 +1,166 @@
-- Guda Constants Module
-- Centralized location for all magic numbers and hardcoded values
local addon = Guda
-- Extend the existing Constants table from Init.lua
local C = addon.Constants
--=============================================================================
-- Bag IDs
--=============================================================================
C.BAG_BACKPACK = 0
C.BAG_FIRST = 1
C.BAG_LAST = 4
C.BANK_FIRST = 5
C.BANK_LAST = 11
C.KEYRING_BAG = -2
C.BANK_CONTAINER = -1
-- Bag ID ranges for iteration
C.BAG_IDS = {0, 1, 2, 3, 4}
C.BANK_BAG_IDS = {5, 6, 7, 8, 9, 10, 11}
C.ALL_BAG_IDS = {0, 1, 2, 3, 4, -2} -- Including keyring
--=============================================================================
-- Equipment Slots
--=============================================================================
C.EQUIPMENT_SLOT_FIRST = 1
C.EQUIPMENT_SLOT_LAST = 19
--=============================================================================
-- Item IDs
--=============================================================================
C.SOUL_SHARD_ID = 6265
C.HEARTHSTONE_ID = 6948
--=============================================================================
-- Colors (r, g, b tables for easy unpacking)
--=============================================================================
C.COLORS = {
-- Special item borders
KEYRING_CYAN = {r = 0.2, g = 0.8, b = 1.0},
QUEST_GOLD = {r = 1.0, g = 0.82, b = 0},
-- Text colors
GRAY_TEXT = {r = 0.5, g = 0.5, b = 0.5},
WHITE_TEXT = {r = 1.0, g = 1.0, b = 1.0},
GOLD_TITLE = {r = 1.0, g = 0.82, b = 0},
CYAN_LABEL = {r = 0, g = 1.0, b = 1.0},
-- Unusable item tint
UNUSABLE_RED = {r = 0.9, g = 0.2, b = 0.2, a = 0.45},
-- Lock/desaturate
LOCKED_GRAY = {r = 0.5, g = 0.5, b = 0.5},
}
--=============================================================================
-- UI Thresholds
--=============================================================================
C.ICON_SIZE_THRESHOLD = 44 -- Below this, use smaller insets/padding
C.ICON_INSET_SMALL = 10 -- Inset for small icons
C.ICON_INSET_LARGE = 15 -- Inset for large icons
C.ICON_TEXCOORD_CROP = 0.08 -- Texture coordinate crop amount
--=============================================================================
-- Database / Cleanup
--=============================================================================
C.CLEANUP_OLD_CHARS_DAYS = 90
--=============================================================================
-- Tooltip Scanning
--=============================================================================
C.QUEST_TOOLTIP_PATTERNS = {
"quest starter",
"this item begins a quest",
"starts a quest",
"quest item",
"manual",
}
C.BIND_ON_EQUIP_PATTERN = "binds when equipped"
--=============================================================================
-- Specialized Bag Types
--=============================================================================
C.BAG_TYPES = {
SOUL = "soul",
HERB = "herb",
ENCHANT = "enchant",
QUIVER = "quiver",
AMMO = "ammo",
}
-- Tooltip patterns for bag type detection
C.BAG_TYPE_PATTERNS = {
soul = {"soul bag", "soul pouch"},
herb = {"herb bag"},
enchant = {"enchanting bag"},
quiver = {"quiver"},
ammo = {"ammo pouch"},
}
--=============================================================================
-- Item Categories (for GetItemInfo)
--=============================================================================
C.ITEM_CATEGORIES = {
WEAPON = "Weapon",
ARMOR = "Armor",
CONSUMABLE = "Consumable",
CONTAINER = "Container",
TRADE_GOODS = "Trade Goods",
PROJECTILE = "Projectile",
QUIVER = "Quiver",
REAGENT = "Reagent",
RECIPE = "Recipe",
KEY = "Key",
MISCELLANEOUS = "Miscellaneous",
QUEST = "Quest",
}
--=============================================================================
-- Money Formatting
--=============================================================================
C.MONEY = {
COPPER_PER_SILVER = 100,
SILVER_PER_GOLD = 100,
COPPER_PER_GOLD = 10000,
}
-- Color codes for money display
C.MONEY_COLORS = {
GOLD = "|cFFFFD700",
SILVER = "|cFFC7C7CF",
COPPER = "|cFFEDA55F",
WHITE = "|cFFFFFFFF",
}
--=============================================================================
-- Frame Constants
--=============================================================================
C.FRAME = {
TITLE_HEIGHT = 40,
SEARCH_BAR_HEIGHT = 30,
FOOTER_HEIGHT = 45,
FOOTER_HEIGHT_HIDDEN = 10,
MIN_WIDTH = 200,
MIN_HEIGHT = 150,
MAX_WIDTH = 1250,
MAX_HEIGHT = 1000,
}
--=============================================================================
-- Bank Slots
--=============================================================================
C.BANK_MAIN_SLOTS = 24 -- Number of slots in main bank container
--=============================================================================
-- Tooltip Hook Settings
--=============================================================================
C.TOOLTIP = {
DEBOUNCE_TIME = 0.2, -- Seconds to wait before clearing cache
MAX_MONEY_FRAMES = 8, -- Maximum number of money frames to search
}
addon:Debug("Constants module loaded")
+200 -240
View File
@@ -4,261 +4,221 @@ local addon = Guda
local Tooltip = {}
addon.Modules.Tooltip = Tooltip
--=============================================================================
-- Item Counting Helper Functions (extracted for clarity and reuse)
--=============================================================================
-- Helper function to get item ID from link (Lua 5.0 compatible)
local function GetItemIDFromLink(link)
if not link then return nil end
if not link then return nil end
if type(link) == "number" then return link end
-- Try to find itemID in a standard link or a raw item:ID string
local _, _, itemID = string.find(link, "item:(%d+)")
return itemID and tonumber(itemID) or nil
local _, _, itemID = string.find(link, "item:(%d+)")
return itemID and tonumber(itemID) or nil
end
-- Count items in saved bag/bank data structure
-- Used for both bags and bank counting from saved character data
local function CountFromSavedContainers(containersData, itemID)
local count = 0
if not containersData or type(containersData) ~= "table" then
return count
end
for bagID, bagData in pairs(containersData) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
count = count + (itemData.count or 1)
end
end
end
end
end
return count
end
-- Count items in saved mailbox data structure
local function CountFromSavedMailbox(mailboxData, itemID)
local count = 0
if not mailboxData or type(mailboxData) ~= "table" then
return count
end
for _, mail in ipairs(mailboxData) do
local itemsToCheck = mail.items or (mail.item and {mail.item}) or {}
for _, item in ipairs(itemsToCheck) do
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
count = count + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
count = count + (item.count or 1)
end
end
end
end
return count
end
-- Count items in saved equipped data structure
local function CountFromSavedEquipped(equippedData, itemID)
local count = 0
if not equippedData or type(equippedData) ~= "table" then
return count
end
for slotName, itemData in pairs(equippedData) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
count = count + 1
end
end
end
return count
end
-- Count items in live container (bags or bank)
local function CountFromLiveContainer(bagIDs, itemID)
local count = 0
for _, bagID in ipairs(bagIDs) do
local numSlots = GetContainerNumSlots(bagID)
if numSlots and numSlots > 0 then
for slot = 1, numSlots do
local link = GetContainerItemLink(bagID, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, itemCount = GetContainerItemInfo(bagID, slot)
count = count + (itemCount or 1)
end
end
end
end
end
return count
end
-- Count items in live mailbox
local function CountFromLiveMailbox(itemID)
local count = 0
if not (addon.Modules.MailboxScanner and addon.Modules.MailboxScanner:IsMailboxOpen()) then
return count
end
local numInboxItems = GetInboxNumItems()
for i = 1, numInboxItems do
local _, _, _, _, _, _, _, hasItem = GetInboxHeaderInfo(i)
if hasItem then
local numAttachments = GetInboxNumAttachments and GetInboxNumAttachments(i) or 1
if numAttachments == 0 and hasItem then
numAttachments = 1
end
for j = 1, numAttachments do
local name, _, itemCount = GetInboxItem(i, j)
if name then
local itemLink = addon.Modules.Utils:GetInboxItemLink(i, j)
if itemLink then
local slotItemID = GetItemIDFromLink(itemLink)
if slotItemID == itemID then
count = count + (itemCount or 1)
end
end
end
end
end
end
return count
end
-- Count items in live equipment slots
local function CountFromLiveEquipped(itemID)
local count = 0
for slotID = 1, 19 do
local link = GetInventoryItemLink("player", slotID)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
count = count + 1
end
end
end
return count
end
--=============================================================================
-- Main Counting Functions
--=============================================================================
-- Count items for current character using live game data
local function CountCurrentCharacterItems(itemID)
local bagCount = 0
local bankCount = 0
local mailCount = 0
local equippedCount = 0
local bagCount = 0
local bankCount = 0
local mailCount = 0
local equippedCount = 0
-- Count current character's bags in real-time
local bagsToCount = {0, 1, 2, 3, 4, -2}
for _, bagID in ipairs(bagsToCount) do
local numSlots = GetContainerNumSlots(bagID)
for slot = 1, numSlots do
local link = GetContainerItemLink(bagID, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, count = GetContainerItemInfo(bagID, slot)
bagCount = bagCount + (count or 1)
end
end
end
end
-- Count bags in real-time
bagCount = CountFromLiveContainer({0, 1, 2, 3, 4, -2}, itemID)
-- Count current character's bank in real-time if bank is open
local bankFrame = getglobal("BankFrame")
if bankFrame and bankFrame:IsVisible() then
-- Main bank slots (-1)
local numMainSlots = GetContainerNumSlots(-1) or 24
for slot = 1, numMainSlots do
local link = GetContainerItemLink(-1, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, count = GetContainerItemInfo(-1, slot)
bankCount = bankCount + (count or 1)
end
end
end
-- Count bank: live if open, otherwise from saved data
local bankFrame = getglobal("BankFrame")
if bankFrame and bankFrame:IsVisible() then
-- Main bank + bank bags
bankCount = CountFromLiveContainer({-1, 5, 6, 7, 8, 9, 10, 11}, itemID)
else
-- Use saved data
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData then
bankCount = CountFromSavedContainers(charData.bank, itemID)
end
end
-- Bank bags (5-11)
for bagID = 5, 11 do
local numSlots = GetContainerNumSlots(bagID)
if numSlots and numSlots > 0 then
for slot = 1, numSlots do
local link = GetContainerItemLink(bagID, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, count = GetContainerItemInfo(bagID, slot)
bankCount = bankCount + (count or 1)
end
end
end
end
end
else
-- Bank not open - use saved data for bank counts
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData and charData.bank and type(charData.bank) == "table" then
for bagID, bagData in pairs(charData.bank) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
bankCount = bankCount + (itemData.count or 1)
end
end
end
end
end
end
end
-- Count mailbox: live if open, otherwise from saved data
if addon.Modules.MailboxScanner and addon.Modules.MailboxScanner:IsMailboxOpen() then
mailCount = CountFromLiveMailbox(itemID)
else
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData then
mailCount = CountFromSavedMailbox(charData.mailbox, itemID)
end
end
-- Count current character's mailbox in real-time if mailbox is open
if addon.Modules.MailboxScanner and addon.Modules.MailboxScanner:IsMailboxOpen() then
local numInboxItems = GetInboxNumItems()
for i = 1, numInboxItems do
local _, _, _, _, _, _, _, hasItem = GetInboxHeaderInfo(i)
if hasItem then
-- Turtle WoW supports up to 12 attachments per mail.
-- We use GetInboxNumAttachments if available to avoid over-scanning.
local numAttachments = 0
if GetInboxNumAttachments then
numAttachments = GetInboxNumAttachments(i) or 0
end
-- Count equipped items in real-time
equippedCount = CountFromLiveEquipped(itemID)
-- Fallback: if we don't have the count but header says there's an item, assume at least 1.
if numAttachments == 0 and hasItem then
numAttachments = 1
end
for j = 1, numAttachments do -- Turtle WoW supports up to 12 attachments
local name, _, count = GetInboxItem(i, j)
if name then
local itemLink = addon.Modules.Utils:GetInboxItemLink(i, j)
if itemLink then
local slotItemID = GetItemIDFromLink(itemLink)
if slotItemID == itemID then
mailCount = mailCount + (count or 1)
end
end
end
end
end
end
else
-- Mailbox not open - use saved data
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData and charData.mailbox and type(charData.mailbox) == "table" then
for _, mail in ipairs(charData.mailbox) do
if mail.items then
for _, item in ipairs(mail.items) do
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
elseif mail.item then -- Fallback for single item data structure
local item = mail.item
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
end
end
end
-- Count equipped items in real-time
for slotID = 1, 19 do -- All equipment slots
local link = GetInventoryItemLink("player", slotID)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
equippedCount = equippedCount + 1
end
end
end
return bagCount, bankCount, equippedCount, mailCount
return bagCount, bankCount, equippedCount, mailCount
end
-- Count items for a specific character with real-time data for current character
-- Count items for a specific character (current or other)
local function CountItemsForCharacter(itemID, characterData, isCurrentChar)
-- For current character, use real-time counting to avoid database sync issues
if isCurrentChar then
return CountCurrentCharacterItems(itemID)
end
-- For current character, use real-time counting
if isCurrentChar then
return CountCurrentCharacterItems(itemID)
end
-- For other characters, use saved data
local bagCount = 0
local bankCount = 0
local mailCount = 0
local equippedCount = 0
-- For other characters, use saved data
local bagCount = CountFromSavedContainers(characterData.bags, itemID)
local bankCount = CountFromSavedContainers(characterData.bank, itemID)
local mailCount = CountFromSavedMailbox(characterData.mailbox, itemID)
local equippedCount = CountFromSavedEquipped(characterData.equipped, itemID)
-- Count bags from saved data
if characterData.bags and type(characterData.bags) == "table" then
for bagID, bagData in pairs(characterData.bags) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
bagCount = bagCount + (itemData.count or 1)
end
end
end
end
end
end
-- Count bank from saved data
if characterData.bank and type(characterData.bank) == "table" then
for bagID, bagData in pairs(characterData.bank) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
bankCount = bankCount + (itemData.count or 1)
end
end
end
end
end
end
-- Count mailbox from saved data
if characterData.mailbox and type(characterData.mailbox) == "table" then
for _, mail in ipairs(characterData.mailbox) do
if mail.items then
for _, item in ipairs(mail.items) do
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
elseif mail.item then -- Fallback for single item data structure
local item = mail.item
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
end
end
-- Count equipped items from saved data
if characterData.equipped and type(characterData.equipped) == "table" then
for slotName, itemData in pairs(characterData.equipped) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
equippedCount = equippedCount + 1
end
end
end
end
return bagCount, bankCount, equippedCount, mailCount
return bagCount, bankCount, equippedCount, mailCount
end
+241 -210
View File
@@ -5,6 +5,64 @@ local addon = Guda
local Utils = {}
addon.Modules.Utils = Utils
--=============================================================================
-- SafeCall: Nil-safe module method invocation
-- Replaces verbose nil-checks like:
-- if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.Method then
-- return addon.Modules.Utils:Method(arg1, arg2)
-- end
-- With:
-- return Utils:SafeCall("Utils", "Method", arg1, arg2)
--=============================================================================
-- Call a method on a module safely, returns nil if module/method doesn't exist
-- Parameters:
-- moduleName: Name of the module in addon.Modules (e.g., "Utils", "DB", "BagFrame")
-- methodName: Name of the method to call (e.g., "GetQualityColor", "GetSetting")
-- ...: Arguments to pass to the method
-- Returns: The return value(s) of the method, or nil if not callable
function Utils:SafeCall(moduleName, methodName, ...)
if not addon or not addon.Modules then
return nil
end
local module = addon.Modules[moduleName]
if not module then
return nil
end
local method = module[methodName]
if not method or type(method) ~= "function" then
return nil
end
-- Call method with module as self (for : style calls)
return method(module, unpack(arg))
end
-- Check if a module method exists without calling it
function Utils:HasMethod(moduleName, methodName)
if not addon or not addon.Modules then
return false
end
local module = addon.Modules[moduleName]
if not module then
return false
end
local method = module[methodName]
return method ~= nil and type(method) == "function"
end
-- Get a module reference safely
function Utils:GetModule(moduleName)
if not addon or not addon.Modules then
return nil
end
return addon.Modules[moduleName]
end
-- Format money (copper to gold/silver/copper string) - WoW 1.12.1 version
function Utils:FormatMoney(copper, showZero, useColors)
if not copper or copper == 0 then
@@ -190,49 +248,123 @@ local function GetScanTooltip()
return scanTooltip
end
-- Check if an item is a quest item by scanning its tooltip
function Utils:IsQuestItemTooltip(bagID, slotID)
if not bagID or not slotID then return false end
-- Check if an item is a quest item by scanning its tooltip (internal helper)
-- Returns: isQuestItem, isQuestStarter
local function ScanTooltipForQuest(tooltip, tooltipName)
local isQuestItem = false
local isQuestStarter = false
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetBagItem(bagID, slotID)
-- Check tooltip lines for explicit quest-related phrases (case-insensitive)
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
local line = getglobal(tooltipName .. "TextLeft" .. i)
if line then
local text = line:GetText()
if text then
local tl = string.lower(text)
-- Only match explicit quest markers to avoid misclassifying consumables/recipes
-- Include "manual" which some quest items use (same as ItemButton detection)
-- Check for quest starter patterns first
if string.find(tl, "quest starter") or
string.find(tl, "this item begins a quest") or
string.find(tl, "starts a quest") or
string.find(tl, "quest item") or
string.find(tl, "manual") then
-- Double check category to avoid misidentifying equipment with "Use:"
local link = GetContainerItemLink(bagID, slotID)
if link and self.ExtractItemID and self.GetItemInfoSafe then
local itemID = self:ExtractItemID(link)
if itemID then
local _, _, _, _, itemCategory, itemType = self:GetItemInfoSafe(itemID)
-- If it's Weapon or Armor, and NOT categorized as Quest, then it's not a Quest Item
if (itemCategory == "Weapon" or itemCategory == "Armor" or itemType == "Weapon" or itemType == "Armor") and
(itemCategory ~= "Quest" and itemType ~= "Quest") then
return false
end
end
end
return true
string.find(tl, "starts a quest") then
isQuestItem = true
isQuestStarter = true
break
-- Check for regular quest item patterns
elseif string.find(tl, "quest item") or
string.find(tl, "manual") then
isQuestItem = true
-- Don't break, might still find a quest starter pattern
end
end
end
end
return false
return isQuestItem, isQuestStarter
end
-- Consolidated quest item detection function
-- Handles tooltip scanning, category checks, equipment filtering, and QuestItemsDB lookup
-- Parameters:
-- bagID, slotID: Required for tooltip scanning (can be nil for other char items)
-- itemData: Optional item data table with link, class/category, type fields
-- isOtherChar: Boolean, true if checking an item from another character's saved data
-- isBank: Boolean, true if the item is in the bank
-- Returns: isQuestItem (boolean), isQuestStarter (boolean)
function Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, isBank)
bagID = tonumber(bagID)
slotID = tonumber(slotID)
local isQuestItem = false
local isQuestStarter = false
-- Get item link and category info
local itemLink = itemData and itemData.link
local itemCategory = itemData and (itemData.class or itemData.category) or ""
local itemType = itemData and itemData.type or ""
local itemID
-- For live items, query the link directly
if not isOtherChar and bagID and slotID then
itemLink = GetContainerItemLink(bagID, slotID)
end
if itemLink then
itemID = self:ExtractItemID(itemLink)
if itemID then
local _, _, _, _, cat, typ = self:GetItemInfoSafe(itemID)
itemCategory = cat or itemCategory
itemType = typ or itemType
end
end
-- Check if item is equipment (should not be classified as quest unless explicitly Quest category)
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor" or
itemType == "Weapon" or itemType == "Armor")
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
-- Priority 1: If explicitly categorized as Quest, it's a quest item
if isQuestCategory then
return true, false
end
-- Priority 2: Tooltip scanning for current character items
if not isOtherChar and bagID and slotID then
local tooltip = GetScanTooltip()
tooltip:ClearLines()
-- Handle bank items differently
if isBank and bagID == -1 then
if tooltip.SetInventoryItem then
tooltip:SetInventoryItem("player", 39 + slotID)
else
tooltip:SetBagItem(bagID, slotID)
end
else
tooltip:SetBagItem(bagID, slotID)
end
isQuestItem, isQuestStarter = ScanTooltipForQuest(tooltip, "GudaBagScanTooltip")
-- Filter out equipment that has quest-like text but isn't categorized as Quest
if isQuestItem and isEquipment and not isQuestCategory then
isQuestItem = false
isQuestStarter = false
end
end
-- Priority 3: Check QuestItemsDB for known faction-specific quest items
if not isQuestItem and itemID and addon.IsQuestItemByID then
local playerFaction = UnitFactionGroup("player")
if addon:IsQuestItemByID(itemID, playerFaction) then
isQuestItem = true
end
end
return isQuestItem, isQuestStarter
end
-- Legacy compatibility wrapper - keep the old function name working
function Utils:IsQuestItemTooltip(bagID, slotID)
local isQuest, _ = self:IsQuestItem(bagID, slotID, nil, false, false)
return isQuest
end
-- Check if an item has a gray title in its tooltip or link
@@ -296,129 +428,8 @@ function Utils:TruncateText(text, maxLen)
return string.sub(text, 1, maxLen - 3) .. "..."
end
function Utils:IsAmmoQuiverBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
-- Get the bag item
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then
return false
end
local link = GetInventoryItemLink("player", invSlot)
if not link then
return false
end
-- Use tooltip scanning to get item class (more reliable in 1.12.1)
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
-- Scan tooltip lines for "Quiver" or "Ammo Pouch"
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
-- Check if the line contains "Quiver" or "Ammo Pouch"
if string.find(text, "Quiver") or string.find(text, "Ammo Pouch") then
return true
end
end
end
end
return false
end
-- Check if a bag is Herb Bag
function Utils:IsHerbBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then return false end
local link = GetInventoryItemLink("player", invSlot)
if not link then return false end
-- Prefer specialized type detection
local bagType = self:GetSpecializedBagType(bagID)
if bagType == "herb" then return true end
-- Fallback: tooltip scan for "Herb Bag"
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text and string.find(string.lower(text), "herb bag") then
return true
end
end
end
return false
end
-- Check if a bag is Soul Bag
function Utils:IsSoulBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
-- Get the bag item
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then
return false
end
local link = GetInventoryItemLink("player", invSlot)
if not link then
return false
end
-- First try GetSpecializedBagType
local bagType = self:GetSpecializedBagType(bagID)
if bagType == "soul" then
return true
end
-- Fallback: Use tooltip scanning (more reliable in 1.12.1)
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
-- Scan tooltip lines for "Soul Bag" or "Soul Pouch"
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
-- Check if the line contains "Soul Bag" or "Soul Pouch" or just "Soul" in bag name
local textLower = string.lower(text)
if string.find(textLower, "soul bag") or string.find(textLower, "soul pouch") or
(string.find(textLower, "soul") and (string.find(textLower, "bag") or string.find(textLower, "pouch"))) then
return true
end
end
end
end
return false
end
-- Returns: "soul", "herb", "enchant", "quiver", "ammo", or nil
-- This is the consolidated bag type detection function with tooltip fallback
function Utils:GetSpecializedBagType(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
@@ -437,44 +448,94 @@ function Utils:GetSpecializedBagType(bagID)
end
local itemID = self:ExtractItemID(link)
if not itemID then
return nil
-- Try GetItemInfo first (more reliable when available)
if itemID then
local _, _, _, _, _, itemType = self:GetItemInfoSafe(itemID)
if itemType then
local typeLower = string.lower(itemType)
if string.find(typeLower, "soul bag") or string.find(typeLower, "soul pouch") then
return "soul"
end
if string.find(typeLower, "herb bag") then
return "herb"
end
if string.find(typeLower, "enchanting bag") then
return "enchant"
end
if string.find(typeLower, "quiver") then
return "quiver"
end
if string.find(typeLower, "ammo pouch") then
return "ammo"
end
end
end
local itemName, _, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, itemSubType = self:GetItemInfoSafe(itemID)
if itemType then
-- Check for exact subtype matches
local typeLower = string.lower(itemType)
-- Fallback: tooltip scanning for all bag types
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
-- Soul Bag / Soul Pouch
if string.find(typeLower, "soul bag") or string.find(typeLower, "soul pouch") then
return "soul"
end
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
local textLower = string.lower(text)
-- Herb Bag
if string.find(typeLower, "herb bag") then
return "herb"
end
-- Soul Bag / Soul Pouch
if string.find(textLower, "soul bag") or string.find(textLower, "soul pouch") or
(string.find(textLower, "soul") and (string.find(textLower, "bag") or string.find(textLower, "pouch"))) then
return "soul"
end
-- Enchanting Bag
if string.find(typeLower, "enchanting bag") then
return "enchant"
end
-- Herb Bag
if string.find(textLower, "herb bag") then
return "herb"
end
-- Quiver
if string.find(typeLower, "quiver") then
return "quiver"
end
-- Enchanting Bag
if string.find(textLower, "enchanting bag") then
return "enchant"
end
-- Ammo Pouch
if string.find(typeLower, "ammo pouch") then
return "ammo"
-- Quiver
if string.find(textLower, "quiver") then
return "quiver"
end
-- Ammo Pouch
if string.find(textLower, "ammo pouch") then
return "ammo"
end
end
end
end
return nil
end
-- Simple helper to check if a bag is of a specific type
function Utils:IsBagType(bagID, bagType)
return self:GetSpecializedBagType(bagID) == bagType
end
-- Convenience wrappers for common bag type checks
function Utils:IsAmmoQuiverBag(bagID)
local bagType = self:GetSpecializedBagType(bagID)
return bagType == "quiver" or bagType == "ammo"
end
function Utils:IsHerbBag(bagID)
return self:IsBagType(bagID, "herb")
end
function Utils:IsSoulBag(bagID)
return self:IsBagType(bagID, "soul")
end
-- Get container priority for sorting (higher = more important)
function Utils:GetContainerPriority(bagID)
local bagType = self:GetSpecializedBagType(bagID)
@@ -727,37 +788,7 @@ function Utils:IsEnchantingItem(itemLink)
return false
end
-- Check if a bag is Enchanting Bag (parallel to IsHerbBag)
-- Check if a bag is Enchanting Bag
function Utils:IsEnchantBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then return false end
local link = GetInventoryItemLink("player", invSlot)
if not link then return false end
-- Prefer specialized type detection
local bagType = self:GetSpecializedBagType(bagID)
if bagType == "enchant" then return true end
-- Fallback: tooltip scan for "Enchanting Bag"
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text and string.find(string.lower(text), "enchanting bag") then
return true
end
end
end
return false
return self:IsBagType(bagID, "enchant")
end
+1
View File
@@ -9,6 +9,7 @@
Localization.lua
Core\Init.lua
Core\Constants.lua
Core\Database.lua
DB\QuestItems.lua
Core\Events.lua
+2 -22
View File
@@ -82,28 +82,8 @@ function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems,
return
end
-- Priority 3: Quest Items
local isQuestItem = false
local itemCategory = itemData.class or itemData.category or ""
local itemType = itemData.type or ""
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor" or itemType == "Weapon" or itemType == "Armor")
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
if isQuestCategory then
isQuestItem = true
elseif not isOtherChar then
isQuestItem = addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)
if isQuestItem and isEquipment and not isQuestCategory then
isQuestItem = false
end
end
if not isQuestItem and itemData.link then
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
if itemID and addon.IsQuestItemByID then
local playerFaction = UnitFactionGroup("player")
isQuestItem = addon:IsQuestItemByID(itemID, playerFaction)
end
end
-- Priority 3: Quest Items (use consolidated detection)
local isQuestItem, _ = addon.Modules.Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, false)
if isQuestItem then
table.insert(categories["Quest"], {bagID = bagID, slotID = slotID, itemData = itemData})
return
+347 -227
View File
@@ -8,90 +8,13 @@ local nextButtonID = 1
local scanTooltip = CreateFrame("GameTooltip", "Guda_QuestScanTooltip", nil, "GameTooltipTemplate")
scanTooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
-- Check if an item is a quest item by scanning its tooltip
-- Check if an item is a quest item by scanning its tooltip and determine type
-- Helper function to check if an item is a quest item
-- Delegates to consolidated Utils:IsQuestItem() function
local function IsQuestItem(bagID, slotID, isBank)
bagID = tonumber(bagID)
slotID = tonumber(slotID)
if not bagID or not slotID then return false end
scanTooltip:ClearLines()
if isBank and bagID == -1 then
if scanTooltip.SetInventoryItem then
scanTooltip:SetInventoryItem("player", 39 + slotID)
else
scanTooltip:SetBagItem(bagID, slotID)
end
else
scanTooltip:SetBagItem(bagID, slotID)
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
local isQuestItem = false
local isQuestStarter = false
-- Check all tooltip lines for quest-related text
for i = 1, scanTooltip:NumLines() do
local line = getglobal("Guda_QuestScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
-- Check for quest starter patterns
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
break
-- Check for regular quest item patterns
elseif string.find(text, "Quest Item") or
string.find(text, "Manual") then
isQuestItem = true
-- Don't break, might still find a quest starter pattern
end
end
end
end
-- Also check item category/type via GetItemInfo for "Quest"
-- Turtle WoW GetItemInfo returns: name, link, rarity, level, itemCategory, itemType, stack, subType, texture, equipLoc, sellPrice
local link = GetContainerItemLink(bagID, slotID)
local itemID
local itemCategory, itemType
if link and addon and addon.Modules 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
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
return false, false
end
--=====================================================
@@ -466,50 +389,31 @@ function Guda_ItemButton_UpdateCooldown(self)
end
end
-- Set item data
function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly)
-- Proactively convert to number to avoid comparisons with strings in downstream functions
bagID = tonumber(bagID)
slotID = tonumber(slotID)
-- Proactively clear any previous cooldown overlay state before reassigning this pooled button
do
local cd = getglobal(self:GetName().."Cooldown") or self.cooldown
if cd then
if CooldownFrame_SetTimer then
CooldownFrame_SetTimer(cd, 0, 0, 0)
elseif CooldownFrame_Set then
CooldownFrame_Set(cd, 0, 0, 0)
end
if cd.Hide then cd:Hide() end
end
end
--=====================================================
-- Helper functions for SetItem (extracted for clarity)
--=====================================================
-- Reset all visual state before reassigning pooled button
-- Reset all visual state on a button (for reuse from pool)
local function ResetButtonVisualState(self)
if self.questBorder then self.questBorder:Hide() end
if self.questIcon then self.questIcon:Hide() end
if self.qualityBorder then self.qualityBorder:Hide() end
if self.unusableOverlay then self.unusableOverlay:Hide() end
self.bagID = bagID
self.slotID = slotID
-- Also set the Blizzard slot ID for compatibility with ContainerFrameItemButtonTemplate behavior
-- ALWAYS set ID to something (0 if nil) to avoid leaking old IDs when button is reused
if self.SetID then
self:SetID(slotID or 0)
-- Clear cooldown overlay
local cd = getglobal(self:GetName().."Cooldown") or self.cooldown
if cd then
if CooldownFrame_SetTimer then
CooldownFrame_SetTimer(cd, 0, 0, 0)
elseif CooldownFrame_Set then
CooldownFrame_Set(cd, 0, 0, 0)
end
if cd.Hide then cd:Hide() end
end
-- Explicitly set bag index to avoid Blizzard's ContainerFrameItemButton_OnEnter logic
-- from picking up this button as part of a real bag.
self.bagIndex = bagID or -100 -- Use an invalid bag index for non-bag buttons
self.itemData = itemData
self.isBank = isBank or false
self.otherChar = otherCharName
self.isReadOnly = isReadOnly or false -- Track if this is read-only mode
self.isMail = false -- Clear mailbox flag by default
self.mailIndex = nil
self.mailItemIndex = nil
end
-- Re-register for drag/drop every time (crucial for button reuse in Classic/Vanilla)
-- Configure drag/drop registration based on read-only state
local function SetupDragDrop(self)
if not self.isReadOnly and not self.otherChar then
if self.RegisterForDrag then
self:RegisterForDrag("LeftButton")
@@ -529,137 +433,364 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
self:EnableMouse(true) -- Still enable mouse for tooltips
end
end
end
-- Default to true if not specified (for non-filtered displays)
-- Get display texture and count for an item slot
-- Returns: texture, count, hasItem (boolean)
local function GetItemDisplayInfo(bagID, slotID, itemData, isReadOnly)
local displayTexture, displayCount
local hasItem = false
if not isReadOnly then
-- LIVE MODE: Query game state directly
local liveTexture, liveCount = GetContainerItemInfo(bagID, slotID)
if liveTexture then
displayTexture = liveTexture
displayCount = liveCount
hasItem = true
elseif bagID == -2 then
-- Fallback for keyring in 1.12.1
local link = GetContainerItemLink(bagID, slotID)
if link then
hasItem = true
local _, _, _, _, _, _, _, _, itemTexture = GetItemInfo(link)
displayTexture = itemTexture
displayCount = 1
end
end
else
-- READ-ONLY MODE: Use cached itemData
if itemData and itemData.texture then
displayTexture = itemData.texture
displayCount = itemData.count
hasItem = true
end
end
return displayTexture, displayCount, hasItem
end
-- Update the tracking checkmark on an item button
local function UpdateTrackingCheckmark(self, Utils)
local check = getglobal(self:GetName().."_Check")
if not check then return end
local isTracked = false
if self.hasItem and self.itemData and self.itemData.link then
local itemID = Utils and Utils.ExtractItemID and Utils:ExtractItemID(self.itemData.link)
if itemID then
local trackedItems = Utils and Utils.SafeCall and Utils:SafeCall("DB", "GetSetting", "trackedItems") or {}
if trackedItems[itemID] then
isTracked = true
end
end
end
if isTracked then
check:Show()
else
check:Hide()
end
end
-- Resize empty slot background to match icon size
local function UpdateEmptySlotBackground(self, emptySlotBg, iconSize)
if not emptySlotBg then return end
emptySlotBg:ClearAllPoints()
-- Use smaller padding for small icons
local bgPadding = iconSize < 44 and 1 or 2
emptySlotBg:SetPoint("TOPLEFT", self, "TOPLEFT", -bgPadding, bgPadding)
emptySlotBg:SetPoint("BOTTOMRIGHT", self, "BOTTOMRIGHT", bgPadding, -bgPadding)
emptySlotBg:SetTexCoord(0.1, 0.9, 0.1, 0.9)
end
-- Resize texture elements to match button size
local function ResizeTextureElements(self, iconSize)
-- Pushed texture
local pushedTexture = getglobal(self:GetName().."PushedTexture")
if not pushedTexture and self.GetPushedTexture then
pushedTexture = self:GetPushedTexture()
end
if pushedTexture then
pushedTexture:ClearAllPoints()
pushedTexture:SetPoint("CENTER", self, "CENTER", 0, 0)
pushedTexture:SetWidth(iconSize)
pushedTexture:SetHeight(iconSize)
end
-- Highlight texture
local highlightTexture = getglobal(self:GetName().."HighlightTexture")
if not highlightTexture and self.GetHighlightTexture then
highlightTexture = self:GetHighlightTexture()
end
if highlightTexture then
highlightTexture:ClearAllPoints()
highlightTexture:SetPoint("CENTER", self, "CENTER", 0, 0)
highlightTexture:SetWidth(iconSize)
highlightTexture:SetHeight(iconSize)
end
-- Checked texture
local checkedTexture = getglobal(self:GetName().."CheckedTexture")
if not checkedTexture and self.GetCheckedTexture then
checkedTexture = self:GetCheckedTexture()
end
if checkedTexture then
checkedTexture:ClearAllPoints()
checkedTexture:SetPoint("CENTER", self, "CENTER", 0, 0)
checkedTexture:SetWidth(iconSize)
checkedTexture:SetHeight(iconSize)
end
end
-- Position icon and borders based on icon size
local function PositionIconAndBorders(self, iconSize)
local iconTexture = getglobal(self:GetName().."IconTexture")
if not iconTexture then
iconTexture = getglobal(self:GetName().."Icon") or self.icon or self.Icon
end
if not iconTexture or not self.hasItem then return end
-- Calculate icon inset based on size
local iconInset = iconSize < 44 and 10 or 15
local iconDisplaySize = iconSize - iconInset
iconTexture:ClearAllPoints()
iconTexture:SetPoint("CENTER", self, "CENTER", -0.5, 0.5)
iconTexture:SetWidth(iconDisplaySize)
iconTexture:SetHeight(iconDisplaySize)
iconTexture:SetTexCoord(0.08, 0.92, 0.08, 0.92)
iconTexture:Show()
-- Position quality border around the icon
if self.qualityBorder then
self.qualityBorder:ClearAllPoints()
self.qualityBorder:SetPoint("TOPLEFT", iconTexture, "TOPLEFT", -5, 5)
self.qualityBorder:SetPoint("BOTTOMRIGHT", iconTexture, "BOTTOMRIGHT", 5, -5)
end
-- Position quest border around the icon
if self.questBorder then
self.questBorder:ClearAllPoints()
self.questBorder:SetPoint("TOPLEFT", iconTexture, "TOPLEFT", -5, 5)
self.questBorder:SetPoint("BOTTOMRIGHT", iconTexture, "BOTTOMRIGHT", 5, -5)
end
-- Position quest icon in top-right corner
if self.questIcon then
local questIconSize = math.max(12, math.min(20, iconSize * 0.35))
self.questIcon:SetWidth(questIconSize)
self.questIcon:SetHeight(questIconSize)
self.questIcon:ClearAllPoints()
self.questIcon:SetPoint("TOPRIGHT", self, "TOPRIGHT", 1, 0)
end
end
-- Update quality border display
local function UpdateQualityBorder(self, itemQuality, itemLink, bagID, Utils)
if not self.qualityBorder then return end
if bagID == -2 then
-- Special border for keyring items (cyan/blue)
self.qualityBorder:SetBackdropBorderColor(0.2, 0.8, 1.0, 1)
self.qualityBorder:Show()
return
end
if not itemQuality then
self.qualityBorder:Hide()
return
end
-- Check settings
local showEquipmentBorder, showOtherBorder
if Utils and Utils.SafeCall then
showEquipmentBorder = Utils:SafeCall("DB", "GetSetting", "showQualityBorderEquipment")
showOtherBorder = Utils:SafeCall("DB", "GetSetting", "showQualityBorderOther")
end
if showEquipmentBorder == nil then showEquipmentBorder = true end
if showOtherBorder == nil then showOtherBorder = true end
-- Check if item is equipment
local isEquipment = false
if itemLink and Utils and Utils.IsEquipment then
isEquipment = Utils:IsEquipment(itemLink)
end
local shouldShowBorder = (isEquipment and showEquipmentBorder) or (not isEquipment and showOtherBorder)
if shouldShowBorder then
local r, g, b = 1, 1, 1
if Utils and Utils.GetQualityColor then
r, g, b = Utils:GetQualityColor(itemQuality)
end
self.qualityBorder:SetBackdropBorderColor(r, g, b, 1)
self.qualityBorder:Show()
else
self.qualityBorder:Hide()
end
end
-- Clear item button for empty slot
local function ClearItemButton(self, emptySlotBg, countText, bagID)
self.hasItem = false
if SetItemButtonTexture then SetItemButtonTexture(self, nil) end
if SetItemButtonCount then SetItemButtonCount(self, 0) end
if SetItemButtonDesaturated then SetItemButtonDesaturated(self, false) end
-- Clear cooldown overlay
local cooldown = getglobal(self:GetName().."Cooldown") or self.cooldown
if cooldown and cooldown.Hide then cooldown:Hide() end
-- Clear icon texture
local iconTexture = getglobal(self:GetName().."IconTexture")
if not iconTexture then
iconTexture = getglobal(self:GetName().."Icon") or self.icon or self.Icon
end
if iconTexture then
iconTexture:SetTexture(nil)
iconTexture:Hide()
end
-- Clear unusable tint
if SetItemButtonTextureVertexColor then
SetItemButtonTextureVertexColor(self, 1.0, 1.0, 1.0)
end
if self.unusableOverlay and self.unusableOverlay.Hide then
self.unusableOverlay:Hide()
end
-- Hide normal texture
self:SetNormalTexture("")
local normalBorder = getglobal(self:GetName().."NormalTexture")
if normalBorder then normalBorder:SetTexture("") end
-- Show/hide empty slot background
if emptySlotBg then
emptySlotBg:Show()
emptySlotBg:SetAlpha(0.5)
end
if countText then countText:Hide() end
-- Handle quality border for empty keyring slots
if self.qualityBorder then
if bagID == -2 then
self.qualityBorder:SetBackdropBorderColor(0.2, 0.8, 1.0, 0.5)
self.qualityBorder:Show()
else
self.qualityBorder:Hide()
end
end
-- Hide quest elements
if self.questBorder then self.questBorder:Hide() end
if self.questIcon then self.questIcon:Hide() end
end
--=====================================================
-- Main SetItem function (orchestrates helper functions)
--=====================================================
-- Set item data
function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly)
-- Proactively convert to number to avoid comparisons with strings in downstream functions
bagID = tonumber(bagID)
slotID = tonumber(slotID)
-- Reset all visual state before reassigning pooled button
ResetButtonVisualState(self)
-- Set button properties
self.bagID = bagID
self.slotID = slotID
if self.SetID then
self:SetID(slotID or 0)
end
self.bagIndex = bagID or -100
self.itemData = itemData
self.isBank = isBank or false
self.otherChar = otherCharName
self.isReadOnly = isReadOnly or false
self.isMail = false
self.mailIndex = nil
self.mailItemIndex = nil
self.mailData = nil
-- Configure drag/drop
SetupDragDrop(self)
-- Default to true if not specified
if matchesFilter == nil then
matchesFilter = true
end
self.mailData = nil -- Clear mail metadata by default
-- Use Blizzard's default count fontstring (ContainerFrameItemButtonTemplate creates $parentCount)
-- Get UI elements
local countText = getglobal(self:GetName().."Count")
local emptySlotBg = getglobal(self:GetName().."_EmptySlotBg")
local Utils = addon and addon.Modules and addon.Modules.Utils
-- Apply icon size setting (nil-safe)
-- Get icon size setting
local iconSize = 37
if addon and addon.Modules and addon.Modules.DB and addon.Modules.DB.GetSetting then
iconSize = addon.Modules.DB:GetSetting("iconSize") or iconSize
elseif Guda and Guda.Modules and Guda.Modules.DB and Guda.Modules.DB.GetSetting then
iconSize = Guda.Modules.DB:GetSetting("iconSize") or iconSize
if Utils and Utils.SafeCall then
iconSize = Utils:SafeCall("DB", "GetSetting", "iconSize") or iconSize
end
if addon and addon.Constants and addon.Constants.BUTTON_SIZE then
if addon and addon.Constants then
iconSize = iconSize or addon.Constants.BUTTON_SIZE
end
self:SetWidth(iconSize)
self:SetHeight(iconSize)
-- In live mode (readOnly=false), query real-time game state instead of cached DB
-- In read-only mode (readOnly=true), use cached itemData from DB
local displayTexture, displayCount
-- Get item display info (texture, count, hasItem)
local displayTexture, displayCount, hasItem = GetItemDisplayInfo(bagID, slotID, itemData, self.isReadOnly)
self.hasItem = hasItem
if not self.isReadOnly then
-- LIVE MODE: Always query game state directly, never use cached itemData
local liveTexture, liveCount = GetContainerItemInfo(bagID, slotID)
if liveTexture then
displayTexture = liveTexture
displayCount = liveCount
self.hasItem = true
elseif bagID == -2 then
-- Fallback for keyring in 1.12.1
local link = GetContainerItemLink(bagID, slotID)
if link then
self.hasItem = true
-- We might not have the texture from GetContainerItemInfo, try to get it from GetItemInfo
local _, _, _, _, _, _, _, _, itemTexture = GetItemInfo(link)
displayTexture = itemTexture
displayCount = 1 -- Keyring items are usually unique anyway
else
self.hasItem = false
end
else
-- No item in this slot (even if itemData has cached data)
self.hasItem = false
end
else
-- READ-ONLY MODE: Use cached itemData from DB (can't query other characters)
if itemData and itemData.texture then
displayTexture = itemData.texture
displayCount = itemData.count
self.hasItem = true
else
self.hasItem = false
end
end
-- Apply the determined texture and count
-- Apply display based on whether slot has item
if self.hasItem then
if SetItemButtonTexture then
SetItemButtonTexture(self, displayTexture)
-- Set texture
if SetItemButtonTexture then
SetItemButtonTexture(self, displayTexture)
end
-- Explicitly set and show icon texture as SetItemButtonTexture can be unreliable for custom paths in 1.12
local iconTexture = getglobal(self:GetName().."IconTexture") or getglobal(self:GetName().."Icon") or self.icon or self.Icon
if iconTexture and displayTexture then
iconTexture:SetTexture(displayTexture)
iconTexture:Show()
end
-- Set count
if SetItemButtonCount then SetItemButtonCount(self, displayCount or 1) end
if emptySlotBg then emptySlotBg:Hide() end
-- Update cooldown overlay for live items
if not self.isReadOnly and not self.otherChar and Guda_ItemButton_UpdateCooldown then
Guda_ItemButton_UpdateCooldown(self)
else
-- Ensure cooldown is hidden for read-only/other character views
local cd = getglobal(self:GetName().."Cooldown") or self.cooldown
if cd and cd.Hide then cd:Hide() end
end
-- Update unusable red overlay tint
if Guda_ItemButton_UpdateUsableTint then
Guda_ItemButton_UpdateUsableTint(self)
Guda_ItemButton_UpdateUsableTint(self)
end
else
-- Fully clear all item button state for empty slots
if SetItemButtonTexture then SetItemButtonTexture(self, nil) end
if SetItemButtonCount then SetItemButtonCount(self, 0) end
if SetItemButtonDesaturated then SetItemButtonDesaturated(self, false) end
-- Ensure cooldown overlay is hidden for empty slots
local cooldown = getglobal(self:GetName().."Cooldown") or self.cooldown
if cooldown and cooldown.Hide then cooldown:Hide() end
-- Also clear the icon texture directly
local iconTexture = getglobal(self:GetName().."IconTexture")
if not iconTexture then
iconTexture = getglobal(self:GetName().."Icon") or self.icon or self.Icon
end
if iconTexture then
iconTexture:SetTexture(nil)
iconTexture:Hide()
end
if emptySlotBg then emptySlotBg:Show() end
-- Ensure any unusable tint is cleared on empty
if SetItemButtonTextureVertexColor then
SetItemButtonTextureVertexColor(self, 1.0, 1.0, 1.0)
end
-- Clear unusable overlay for empty slots ✅ FIX: Clear red overlay when item is sold
if self.unusableOverlay and self.unusableOverlay.Hide then
self.unusableOverlay:Hide()
end
-- Clear empty slot
ClearItemButton(self, emptySlotBg, countText, bagID)
end
-- Update tracking checkmark
UpdateTrackingCheckmark(self, Utils)
local check = getglobal(self:GetName().."_Check")
if check then
local isTracked = false
if self.hasItem then
local itemID = addon.Modules.Utils:ExtractItemID(self.itemData and self.itemData.link)
if self.hasItem and self.itemData and self.itemData.link then
local itemID = Utils and Utils.ExtractItemID and Utils:ExtractItemID(self.itemData.link)
if itemID then
local trackedItems = addon.Modules.DB:GetSetting("trackedItems") or {}
local trackedItems = Utils and Utils.SafeCall and Utils:SafeCall("DB", "GetSetting", "trackedItems") or {}
if trackedItems[itemID] then
isTracked = true
end
@@ -735,10 +866,8 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
if countText and countText.GetFont then
local font, _, flags = countText:GetFont()
local fontSize = 12
if addon and addon.Modules and addon.Modules.DB and addon.Modules.DB.GetSetting then
fontSize = addon.Modules.DB:GetSetting("iconFontSize") or fontSize
elseif Guda and Guda.Modules and Guda.Modules.DB and Guda.Modules.DB.GetSetting then
fontSize = Guda.Modules.DB:GetSetting("iconFontSize") or fontSize
if Utils and Utils.SafeCall then
fontSize = Utils:SafeCall("DB", "GetSetting", "iconFontSize") or fontSize
end
countText:SetFont(font, fontSize, flags)
@@ -814,14 +943,11 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
self.qualityBorder:SetBackdropBorderColor(0.2, 0.8, 1.0, 1)
self.qualityBorder:Show()
elseif itemQuality then
-- Check settings to determine if we should show borders (nil-safe)
-- Check settings to determine if we should show borders
local showEquipmentBorder, showOtherBorder
if addon and addon.Modules and addon.Modules.DB and addon.Modules.DB.GetSetting then
showEquipmentBorder = addon.Modules.DB:GetSetting("showQualityBorderEquipment")
showOtherBorder = addon.Modules.DB:GetSetting("showQualityBorderOther")
elseif Guda and Guda.Modules and Guda.Modules.DB and Guda.Modules.DB.GetSetting then
showEquipmentBorder = Guda.Modules.DB:GetSetting("showQualityBorderEquipment")
showOtherBorder = Guda.Modules.DB:GetSetting("showQualityBorderOther")
if Utils and Utils.SafeCall then
showEquipmentBorder = Utils:SafeCall("DB", "GetSetting", "showQualityBorderEquipment")
showOtherBorder = Utils:SafeCall("DB", "GetSetting", "showQualityBorderOther")
end
-- Default to true if settings not found
@@ -832,14 +958,10 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
showOtherBorder = true
end
-- Check if item is equipment (nil-safe)
-- Check if item is equipment
local isEquipment = false
if itemLink then
if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.IsEquipment then
isEquipment = addon.Modules.Utils:IsEquipment(itemLink)
elseif Guda and Guda.Modules and Guda.Modules.Utils and Guda.Modules.Utils.IsEquipment then
isEquipment = Guda.Modules.Utils:IsEquipment(itemLink)
end
if itemLink and Utils and Utils.IsEquipment then
isEquipment = Utils:IsEquipment(itemLink)
end
-- Determine if we should show the border based on item type and settings
@@ -848,10 +970,8 @@ function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCha
if shouldShowBorder then
-- Show colored border for all items (Poor, Common, Uncommon, Rare, Epic, etc.)
local r, g, b = 1, 1, 1
if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.GetQualityColor then
r, g, b = addon.Modules.Utils:GetQualityColor(itemQuality)
elseif Guda and Guda.Modules and Guda.Modules.Utils and Guda.Modules.Utils.GetQualityColor then
r, g, b = Guda.Modules.Utils:GetQualityColor(itemQuality)
if Utils and Utils.GetQualityColor then
r, g, b = Utils:GetQualityColor(itemQuality)
end
self.qualityBorder:SetBackdropBorderColor(r, g, b, 1)
self.qualityBorder:Show()