refactory: redunant code
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -9,6 +9,7 @@
|
||||
Localization.lua
|
||||
|
||||
Core\Init.lua
|
||||
Core\Constants.lua
|
||||
Core\Database.lua
|
||||
DB\QuestItems.lua
|
||||
Core\Events.lua
|
||||
|
||||
+2
-22
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user