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
|
||||
Reference in New Issue
Block a user