refactory: redunant code

This commit is contained in:
Salikh Gurgenidze
2026-01-19 17:41:57 +04:00
parent 28307aa1a2
commit 60e2d99799
7 changed files with 959 additions and 723 deletions
+2 -24
View File
@@ -562,30 +562,8 @@ function CategoryManager:EvaluateRule(rule, itemData, bagID, slotID, isOtherChar
return isBoE == ruleValue
elseif ruleType == "isQuestItem" then
local isQuestItem = false
local itemCategory = itemData.class or itemData.category or ""
local itemType = itemData.type or ""
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor")
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
if isQuestCategory then
isQuestItem = true
elseif not isOtherChar then
isQuestItem = addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)
if isQuestItem and isEquipment and not isQuestCategory then
isQuestItem = false
end
end
-- Also check QuestItemsDB
if not isQuestItem and itemData.link then
local itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
if itemID and addon.IsQuestItemByID then
local playerFaction = UnitFactionGroup("player")
isQuestItem = addon:IsQuestItemByID(itemID, playerFaction)
end
end
-- Use consolidated quest detection from Utils
local isQuestItem, _ = addon.Modules.Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, false)
return isQuestItem == ruleValue
elseif ruleType == "texturePattern" then
+166
View File
@@ -0,0 +1,166 @@
-- Guda Constants Module
-- Centralized location for all magic numbers and hardcoded values
local addon = Guda
-- Extend the existing Constants table from Init.lua
local C = addon.Constants
--=============================================================================
-- Bag IDs
--=============================================================================
C.BAG_BACKPACK = 0
C.BAG_FIRST = 1
C.BAG_LAST = 4
C.BANK_FIRST = 5
C.BANK_LAST = 11
C.KEYRING_BAG = -2
C.BANK_CONTAINER = -1
-- Bag ID ranges for iteration
C.BAG_IDS = {0, 1, 2, 3, 4}
C.BANK_BAG_IDS = {5, 6, 7, 8, 9, 10, 11}
C.ALL_BAG_IDS = {0, 1, 2, 3, 4, -2} -- Including keyring
--=============================================================================
-- Equipment Slots
--=============================================================================
C.EQUIPMENT_SLOT_FIRST = 1
C.EQUIPMENT_SLOT_LAST = 19
--=============================================================================
-- Item IDs
--=============================================================================
C.SOUL_SHARD_ID = 6265
C.HEARTHSTONE_ID = 6948
--=============================================================================
-- Colors (r, g, b tables for easy unpacking)
--=============================================================================
C.COLORS = {
-- Special item borders
KEYRING_CYAN = {r = 0.2, g = 0.8, b = 1.0},
QUEST_GOLD = {r = 1.0, g = 0.82, b = 0},
-- Text colors
GRAY_TEXT = {r = 0.5, g = 0.5, b = 0.5},
WHITE_TEXT = {r = 1.0, g = 1.0, b = 1.0},
GOLD_TITLE = {r = 1.0, g = 0.82, b = 0},
CYAN_LABEL = {r = 0, g = 1.0, b = 1.0},
-- Unusable item tint
UNUSABLE_RED = {r = 0.9, g = 0.2, b = 0.2, a = 0.45},
-- Lock/desaturate
LOCKED_GRAY = {r = 0.5, g = 0.5, b = 0.5},
}
--=============================================================================
-- UI Thresholds
--=============================================================================
C.ICON_SIZE_THRESHOLD = 44 -- Below this, use smaller insets/padding
C.ICON_INSET_SMALL = 10 -- Inset for small icons
C.ICON_INSET_LARGE = 15 -- Inset for large icons
C.ICON_TEXCOORD_CROP = 0.08 -- Texture coordinate crop amount
--=============================================================================
-- Database / Cleanup
--=============================================================================
C.CLEANUP_OLD_CHARS_DAYS = 90
--=============================================================================
-- Tooltip Scanning
--=============================================================================
C.QUEST_TOOLTIP_PATTERNS = {
"quest starter",
"this item begins a quest",
"starts a quest",
"quest item",
"manual",
}
C.BIND_ON_EQUIP_PATTERN = "binds when equipped"
--=============================================================================
-- Specialized Bag Types
--=============================================================================
C.BAG_TYPES = {
SOUL = "soul",
HERB = "herb",
ENCHANT = "enchant",
QUIVER = "quiver",
AMMO = "ammo",
}
-- Tooltip patterns for bag type detection
C.BAG_TYPE_PATTERNS = {
soul = {"soul bag", "soul pouch"},
herb = {"herb bag"},
enchant = {"enchanting bag"},
quiver = {"quiver"},
ammo = {"ammo pouch"},
}
--=============================================================================
-- Item Categories (for GetItemInfo)
--=============================================================================
C.ITEM_CATEGORIES = {
WEAPON = "Weapon",
ARMOR = "Armor",
CONSUMABLE = "Consumable",
CONTAINER = "Container",
TRADE_GOODS = "Trade Goods",
PROJECTILE = "Projectile",
QUIVER = "Quiver",
REAGENT = "Reagent",
RECIPE = "Recipe",
KEY = "Key",
MISCELLANEOUS = "Miscellaneous",
QUEST = "Quest",
}
--=============================================================================
-- Money Formatting
--=============================================================================
C.MONEY = {
COPPER_PER_SILVER = 100,
SILVER_PER_GOLD = 100,
COPPER_PER_GOLD = 10000,
}
-- Color codes for money display
C.MONEY_COLORS = {
GOLD = "|cFFFFD700",
SILVER = "|cFFC7C7CF",
COPPER = "|cFFEDA55F",
WHITE = "|cFFFFFFFF",
}
--=============================================================================
-- Frame Constants
--=============================================================================
C.FRAME = {
TITLE_HEIGHT = 40,
SEARCH_BAR_HEIGHT = 30,
FOOTER_HEIGHT = 45,
FOOTER_HEIGHT_HIDDEN = 10,
MIN_WIDTH = 200,
MIN_HEIGHT = 150,
MAX_WIDTH = 1250,
MAX_HEIGHT = 1000,
}
--=============================================================================
-- Bank Slots
--=============================================================================
C.BANK_MAIN_SLOTS = 24 -- Number of slots in main bank container
--=============================================================================
-- Tooltip Hook Settings
--=============================================================================
C.TOOLTIP = {
DEBOUNCE_TIME = 0.2, -- Seconds to wait before clearing cache
MAX_MONEY_FRAMES = 8, -- Maximum number of money frames to search
}
addon:Debug("Constants module loaded")
+200 -240
View File
@@ -4,261 +4,221 @@ local addon = Guda
local Tooltip = {}
addon.Modules.Tooltip = Tooltip
--=============================================================================
-- Item Counting Helper Functions (extracted for clarity and reuse)
--=============================================================================
-- Helper function to get item ID from link (Lua 5.0 compatible)
local function GetItemIDFromLink(link)
if not link then return nil end
if not link then return nil end
if type(link) == "number" then return link end
-- Try to find itemID in a standard link or a raw item:ID string
local _, _, itemID = string.find(link, "item:(%d+)")
return itemID and tonumber(itemID) or nil
local _, _, itemID = string.find(link, "item:(%d+)")
return itemID and tonumber(itemID) or nil
end
-- Count items in saved bag/bank data structure
-- Used for both bags and bank counting from saved character data
local function CountFromSavedContainers(containersData, itemID)
local count = 0
if not containersData or type(containersData) ~= "table" then
return count
end
for bagID, bagData in pairs(containersData) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
count = count + (itemData.count or 1)
end
end
end
end
end
return count
end
-- Count items in saved mailbox data structure
local function CountFromSavedMailbox(mailboxData, itemID)
local count = 0
if not mailboxData or type(mailboxData) ~= "table" then
return count
end
for _, mail in ipairs(mailboxData) do
local itemsToCheck = mail.items or (mail.item and {mail.item}) or {}
for _, item in ipairs(itemsToCheck) do
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
count = count + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
count = count + (item.count or 1)
end
end
end
end
return count
end
-- Count items in saved equipped data structure
local function CountFromSavedEquipped(equippedData, itemID)
local count = 0
if not equippedData or type(equippedData) ~= "table" then
return count
end
for slotName, itemData in pairs(equippedData) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
count = count + 1
end
end
end
return count
end
-- Count items in live container (bags or bank)
local function CountFromLiveContainer(bagIDs, itemID)
local count = 0
for _, bagID in ipairs(bagIDs) do
local numSlots = GetContainerNumSlots(bagID)
if numSlots and numSlots > 0 then
for slot = 1, numSlots do
local link = GetContainerItemLink(bagID, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, itemCount = GetContainerItemInfo(bagID, slot)
count = count + (itemCount or 1)
end
end
end
end
end
return count
end
-- Count items in live mailbox
local function CountFromLiveMailbox(itemID)
local count = 0
if not (addon.Modules.MailboxScanner and addon.Modules.MailboxScanner:IsMailboxOpen()) then
return count
end
local numInboxItems = GetInboxNumItems()
for i = 1, numInboxItems do
local _, _, _, _, _, _, _, hasItem = GetInboxHeaderInfo(i)
if hasItem then
local numAttachments = GetInboxNumAttachments and GetInboxNumAttachments(i) or 1
if numAttachments == 0 and hasItem then
numAttachments = 1
end
for j = 1, numAttachments do
local name, _, itemCount = GetInboxItem(i, j)
if name then
local itemLink = addon.Modules.Utils:GetInboxItemLink(i, j)
if itemLink then
local slotItemID = GetItemIDFromLink(itemLink)
if slotItemID == itemID then
count = count + (itemCount or 1)
end
end
end
end
end
end
return count
end
-- Count items in live equipment slots
local function CountFromLiveEquipped(itemID)
local count = 0
for slotID = 1, 19 do
local link = GetInventoryItemLink("player", slotID)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
count = count + 1
end
end
end
return count
end
--=============================================================================
-- Main Counting Functions
--=============================================================================
-- Count items for current character using live game data
local function CountCurrentCharacterItems(itemID)
local bagCount = 0
local bankCount = 0
local mailCount = 0
local equippedCount = 0
local bagCount = 0
local bankCount = 0
local mailCount = 0
local equippedCount = 0
-- Count current character's bags in real-time
local bagsToCount = {0, 1, 2, 3, 4, -2}
for _, bagID in ipairs(bagsToCount) do
local numSlots = GetContainerNumSlots(bagID)
for slot = 1, numSlots do
local link = GetContainerItemLink(bagID, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, count = GetContainerItemInfo(bagID, slot)
bagCount = bagCount + (count or 1)
end
end
end
end
-- Count bags in real-time
bagCount = CountFromLiveContainer({0, 1, 2, 3, 4, -2}, itemID)
-- Count current character's bank in real-time if bank is open
local bankFrame = getglobal("BankFrame")
if bankFrame and bankFrame:IsVisible() then
-- Main bank slots (-1)
local numMainSlots = GetContainerNumSlots(-1) or 24
for slot = 1, numMainSlots do
local link = GetContainerItemLink(-1, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, count = GetContainerItemInfo(-1, slot)
bankCount = bankCount + (count or 1)
end
end
end
-- Count bank: live if open, otherwise from saved data
local bankFrame = getglobal("BankFrame")
if bankFrame and bankFrame:IsVisible() then
-- Main bank + bank bags
bankCount = CountFromLiveContainer({-1, 5, 6, 7, 8, 9, 10, 11}, itemID)
else
-- Use saved data
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData then
bankCount = CountFromSavedContainers(charData.bank, itemID)
end
end
-- Bank bags (5-11)
for bagID = 5, 11 do
local numSlots = GetContainerNumSlots(bagID)
if numSlots and numSlots > 0 then
for slot = 1, numSlots do
local link = GetContainerItemLink(bagID, slot)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
local _, count = GetContainerItemInfo(bagID, slot)
bankCount = bankCount + (count or 1)
end
end
end
end
end
else
-- Bank not open - use saved data for bank counts
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData and charData.bank and type(charData.bank) == "table" then
for bagID, bagData in pairs(charData.bank) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
bankCount = bankCount + (itemData.count or 1)
end
end
end
end
end
end
end
-- Count mailbox: live if open, otherwise from saved data
if addon.Modules.MailboxScanner and addon.Modules.MailboxScanner:IsMailboxOpen() then
mailCount = CountFromLiveMailbox(itemID)
else
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData then
mailCount = CountFromSavedMailbox(charData.mailbox, itemID)
end
end
-- Count current character's mailbox in real-time if mailbox is open
if addon.Modules.MailboxScanner and addon.Modules.MailboxScanner:IsMailboxOpen() then
local numInboxItems = GetInboxNumItems()
for i = 1, numInboxItems do
local _, _, _, _, _, _, _, hasItem = GetInboxHeaderInfo(i)
if hasItem then
-- Turtle WoW supports up to 12 attachments per mail.
-- We use GetInboxNumAttachments if available to avoid over-scanning.
local numAttachments = 0
if GetInboxNumAttachments then
numAttachments = GetInboxNumAttachments(i) or 0
end
-- Count equipped items in real-time
equippedCount = CountFromLiveEquipped(itemID)
-- Fallback: if we don't have the count but header says there's an item, assume at least 1.
if numAttachments == 0 and hasItem then
numAttachments = 1
end
for j = 1, numAttachments do -- Turtle WoW supports up to 12 attachments
local name, _, count = GetInboxItem(i, j)
if name then
local itemLink = addon.Modules.Utils:GetInboxItemLink(i, j)
if itemLink then
local slotItemID = GetItemIDFromLink(itemLink)
if slotItemID == itemID then
mailCount = mailCount + (count or 1)
end
end
end
end
end
end
else
-- Mailbox not open - use saved data
local playerName = addon.Modules.DB:GetPlayerFullName()
local charData = Guda_DB and Guda_DB.characters and Guda_DB.characters[playerName]
if charData and charData.mailbox and type(charData.mailbox) == "table" then
for _, mail in ipairs(charData.mailbox) do
if mail.items then
for _, item in ipairs(mail.items) do
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
elseif mail.item then -- Fallback for single item data structure
local item = mail.item
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
end
end
end
-- Count equipped items in real-time
for slotID = 1, 19 do -- All equipment slots
local link = GetInventoryItemLink("player", slotID)
if link then
local slotItemID = GetItemIDFromLink(link)
if slotItemID == itemID then
equippedCount = equippedCount + 1
end
end
end
return bagCount, bankCount, equippedCount, mailCount
return bagCount, bankCount, equippedCount, mailCount
end
-- Count items for a specific character with real-time data for current character
-- Count items for a specific character (current or other)
local function CountItemsForCharacter(itemID, characterData, isCurrentChar)
-- For current character, use real-time counting to avoid database sync issues
if isCurrentChar then
return CountCurrentCharacterItems(itemID)
end
-- For current character, use real-time counting
if isCurrentChar then
return CountCurrentCharacterItems(itemID)
end
-- For other characters, use saved data
local bagCount = 0
local bankCount = 0
local mailCount = 0
local equippedCount = 0
-- For other characters, use saved data
local bagCount = CountFromSavedContainers(characterData.bags, itemID)
local bankCount = CountFromSavedContainers(characterData.bank, itemID)
local mailCount = CountFromSavedMailbox(characterData.mailbox, itemID)
local equippedCount = CountFromSavedEquipped(characterData.equipped, itemID)
-- Count bags from saved data
if characterData.bags and type(characterData.bags) == "table" then
for bagID, bagData in pairs(characterData.bags) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
bagCount = bagCount + (itemData.count or 1)
end
end
end
end
end
end
-- Count bank from saved data
if characterData.bank and type(characterData.bank) == "table" then
for bagID, bagData in pairs(characterData.bank) do
if bagData and type(bagData) == "table" and bagData.slots and type(bagData.slots) == "table" then
for slotID, itemData in pairs(bagData.slots) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
bankCount = bankCount + (itemData.count or 1)
end
end
end
end
end
end
-- Count mailbox from saved data
if characterData.mailbox and type(characterData.mailbox) == "table" then
for _, mail in ipairs(characterData.mailbox) do
if mail.items then
for _, item in ipairs(mail.items) do
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
elseif mail.item then -- Fallback for single item data structure
local item = mail.item
local slotItemID = item.link and GetItemIDFromLink(item.link)
if slotItemID == itemID then
mailCount = mailCount + (item.count or 1)
elseif not slotItemID and item.name then
-- Fallback to name matching if link is missing
local targetName = GetItemInfo(itemID)
if targetName == item.name then
mailCount = mailCount + (item.count or 1)
end
end
end
end
end
-- Count equipped items from saved data
if characterData.equipped and type(characterData.equipped) == "table" then
for slotName, itemData in pairs(characterData.equipped) do
if itemData and type(itemData) == "table" and itemData.link then
local slotItemID = GetItemIDFromLink(itemData.link)
if slotItemID == itemID then
equippedCount = equippedCount + 1
end
end
end
end
return bagCount, bankCount, equippedCount, mailCount
return bagCount, bankCount, equippedCount, mailCount
end
+241 -210
View File
@@ -5,6 +5,64 @@ local addon = Guda
local Utils = {}
addon.Modules.Utils = Utils
--=============================================================================
-- SafeCall: Nil-safe module method invocation
-- Replaces verbose nil-checks like:
-- if addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.Method then
-- return addon.Modules.Utils:Method(arg1, arg2)
-- end
-- With:
-- return Utils:SafeCall("Utils", "Method", arg1, arg2)
--=============================================================================
-- Call a method on a module safely, returns nil if module/method doesn't exist
-- Parameters:
-- moduleName: Name of the module in addon.Modules (e.g., "Utils", "DB", "BagFrame")
-- methodName: Name of the method to call (e.g., "GetQualityColor", "GetSetting")
-- ...: Arguments to pass to the method
-- Returns: The return value(s) of the method, or nil if not callable
function Utils:SafeCall(moduleName, methodName, ...)
if not addon or not addon.Modules then
return nil
end
local module = addon.Modules[moduleName]
if not module then
return nil
end
local method = module[methodName]
if not method or type(method) ~= "function" then
return nil
end
-- Call method with module as self (for : style calls)
return method(module, unpack(arg))
end
-- Check if a module method exists without calling it
function Utils:HasMethod(moduleName, methodName)
if not addon or not addon.Modules then
return false
end
local module = addon.Modules[moduleName]
if not module then
return false
end
local method = module[methodName]
return method ~= nil and type(method) == "function"
end
-- Get a module reference safely
function Utils:GetModule(moduleName)
if not addon or not addon.Modules then
return nil
end
return addon.Modules[moduleName]
end
-- Format money (copper to gold/silver/copper string) - WoW 1.12.1 version
function Utils:FormatMoney(copper, showZero, useColors)
if not copper or copper == 0 then
@@ -190,49 +248,123 @@ local function GetScanTooltip()
return scanTooltip
end
-- Check if an item is a quest item by scanning its tooltip
function Utils:IsQuestItemTooltip(bagID, slotID)
if not bagID or not slotID then return false end
-- Check if an item is a quest item by scanning its tooltip (internal helper)
-- Returns: isQuestItem, isQuestStarter
local function ScanTooltipForQuest(tooltip, tooltipName)
local isQuestItem = false
local isQuestStarter = false
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetBagItem(bagID, slotID)
-- Check tooltip lines for explicit quest-related phrases (case-insensitive)
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
local line = getglobal(tooltipName .. "TextLeft" .. i)
if line then
local text = line:GetText()
if text then
local tl = string.lower(text)
-- Only match explicit quest markers to avoid misclassifying consumables/recipes
-- Include "manual" which some quest items use (same as ItemButton detection)
-- Check for quest starter patterns first
if string.find(tl, "quest starter") or
string.find(tl, "this item begins a quest") or
string.find(tl, "starts a quest") or
string.find(tl, "quest item") or
string.find(tl, "manual") then
-- Double check category to avoid misidentifying equipment with "Use:"
local link = GetContainerItemLink(bagID, slotID)
if link and self.ExtractItemID and self.GetItemInfoSafe then
local itemID = self:ExtractItemID(link)
if itemID then
local _, _, _, _, itemCategory, itemType = self:GetItemInfoSafe(itemID)
-- If it's Weapon or Armor, and NOT categorized as Quest, then it's not a Quest Item
if (itemCategory == "Weapon" or itemCategory == "Armor" or itemType == "Weapon" or itemType == "Armor") and
(itemCategory ~= "Quest" and itemType ~= "Quest") then
return false
end
end
end
return true
string.find(tl, "starts a quest") then
isQuestItem = true
isQuestStarter = true
break
-- Check for regular quest item patterns
elseif string.find(tl, "quest item") or
string.find(tl, "manual") then
isQuestItem = true
-- Don't break, might still find a quest starter pattern
end
end
end
end
return false
return isQuestItem, isQuestStarter
end
-- Consolidated quest item detection function
-- Handles tooltip scanning, category checks, equipment filtering, and QuestItemsDB lookup
-- Parameters:
-- bagID, slotID: Required for tooltip scanning (can be nil for other char items)
-- itemData: Optional item data table with link, class/category, type fields
-- isOtherChar: Boolean, true if checking an item from another character's saved data
-- isBank: Boolean, true if the item is in the bank
-- Returns: isQuestItem (boolean), isQuestStarter (boolean)
function Utils:IsQuestItem(bagID, slotID, itemData, isOtherChar, isBank)
bagID = tonumber(bagID)
slotID = tonumber(slotID)
local isQuestItem = false
local isQuestStarter = false
-- Get item link and category info
local itemLink = itemData and itemData.link
local itemCategory = itemData and (itemData.class or itemData.category) or ""
local itemType = itemData and itemData.type or ""
local itemID
-- For live items, query the link directly
if not isOtherChar and bagID and slotID then
itemLink = GetContainerItemLink(bagID, slotID)
end
if itemLink then
itemID = self:ExtractItemID(itemLink)
if itemID then
local _, _, _, _, cat, typ = self:GetItemInfoSafe(itemID)
itemCategory = cat or itemCategory
itemType = typ or itemType
end
end
-- Check if item is equipment (should not be classified as quest unless explicitly Quest category)
local isEquipment = (itemCategory == "Weapon" or itemCategory == "Armor" or
itemType == "Weapon" or itemType == "Armor")
local isQuestCategory = (itemCategory == "Quest" or itemType == "Quest")
-- Priority 1: If explicitly categorized as Quest, it's a quest item
if isQuestCategory then
return true, false
end
-- Priority 2: Tooltip scanning for current character items
if not isOtherChar and bagID and slotID then
local tooltip = GetScanTooltip()
tooltip:ClearLines()
-- Handle bank items differently
if isBank and bagID == -1 then
if tooltip.SetInventoryItem then
tooltip:SetInventoryItem("player", 39 + slotID)
else
tooltip:SetBagItem(bagID, slotID)
end
else
tooltip:SetBagItem(bagID, slotID)
end
isQuestItem, isQuestStarter = ScanTooltipForQuest(tooltip, "GudaBagScanTooltip")
-- Filter out equipment that has quest-like text but isn't categorized as Quest
if isQuestItem and isEquipment and not isQuestCategory then
isQuestItem = false
isQuestStarter = false
end
end
-- Priority 3: Check QuestItemsDB for known faction-specific quest items
if not isQuestItem and itemID and addon.IsQuestItemByID then
local playerFaction = UnitFactionGroup("player")
if addon:IsQuestItemByID(itemID, playerFaction) then
isQuestItem = true
end
end
return isQuestItem, isQuestStarter
end
-- Legacy compatibility wrapper - keep the old function name working
function Utils:IsQuestItemTooltip(bagID, slotID)
local isQuest, _ = self:IsQuestItem(bagID, slotID, nil, false, false)
return isQuest
end
-- Check if an item has a gray title in its tooltip or link
@@ -296,129 +428,8 @@ function Utils:TruncateText(text, maxLen)
return string.sub(text, 1, maxLen - 3) .. "..."
end
function Utils:IsAmmoQuiverBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
-- Get the bag item
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then
return false
end
local link = GetInventoryItemLink("player", invSlot)
if not link then
return false
end
-- Use tooltip scanning to get item class (more reliable in 1.12.1)
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
-- Scan tooltip lines for "Quiver" or "Ammo Pouch"
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
-- Check if the line contains "Quiver" or "Ammo Pouch"
if string.find(text, "Quiver") or string.find(text, "Ammo Pouch") then
return true
end
end
end
end
return false
end
-- Check if a bag is Herb Bag
function Utils:IsHerbBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then return false end
local link = GetInventoryItemLink("player", invSlot)
if not link then return false end
-- Prefer specialized type detection
local bagType = self:GetSpecializedBagType(bagID)
if bagType == "herb" then return true end
-- Fallback: tooltip scan for "Herb Bag"
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text and string.find(string.lower(text), "herb bag") then
return true
end
end
end
return false
end
-- Check if a bag is Soul Bag
function Utils:IsSoulBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
-- Get the bag item
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then
return false
end
local link = GetInventoryItemLink("player", invSlot)
if not link then
return false
end
-- First try GetSpecializedBagType
local bagType = self:GetSpecializedBagType(bagID)
if bagType == "soul" then
return true
end
-- Fallback: Use tooltip scanning (more reliable in 1.12.1)
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
-- Scan tooltip lines for "Soul Bag" or "Soul Pouch"
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
-- Check if the line contains "Soul Bag" or "Soul Pouch" or just "Soul" in bag name
local textLower = string.lower(text)
if string.find(textLower, "soul bag") or string.find(textLower, "soul pouch") or
(string.find(textLower, "soul") and (string.find(textLower, "bag") or string.find(textLower, "pouch"))) then
return true
end
end
end
end
return false
end
-- Returns: "soul", "herb", "enchant", "quiver", "ammo", or nil
-- This is the consolidated bag type detection function with tooltip fallback
function Utils:GetSpecializedBagType(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
@@ -437,44 +448,94 @@ function Utils:GetSpecializedBagType(bagID)
end
local itemID = self:ExtractItemID(link)
if not itemID then
return nil
-- Try GetItemInfo first (more reliable when available)
if itemID then
local _, _, _, _, _, itemType = self:GetItemInfoSafe(itemID)
if itemType then
local typeLower = string.lower(itemType)
if string.find(typeLower, "soul bag") or string.find(typeLower, "soul pouch") then
return "soul"
end
if string.find(typeLower, "herb bag") then
return "herb"
end
if string.find(typeLower, "enchanting bag") then
return "enchant"
end
if string.find(typeLower, "quiver") then
return "quiver"
end
if string.find(typeLower, "ammo pouch") then
return "ammo"
end
end
end
local itemName, _, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, itemSubType = self:GetItemInfoSafe(itemID)
if itemType then
-- Check for exact subtype matches
local typeLower = string.lower(itemType)
-- Fallback: tooltip scanning for all bag types
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
-- Soul Bag / Soul Pouch
if string.find(typeLower, "soul bag") or string.find(typeLower, "soul pouch") then
return "soul"
end
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text then
local textLower = string.lower(text)
-- Herb Bag
if string.find(typeLower, "herb bag") then
return "herb"
end
-- Soul Bag / Soul Pouch
if string.find(textLower, "soul bag") or string.find(textLower, "soul pouch") or
(string.find(textLower, "soul") and (string.find(textLower, "bag") or string.find(textLower, "pouch"))) then
return "soul"
end
-- Enchanting Bag
if string.find(typeLower, "enchanting bag") then
return "enchant"
end
-- Herb Bag
if string.find(textLower, "herb bag") then
return "herb"
end
-- Quiver
if string.find(typeLower, "quiver") then
return "quiver"
end
-- Enchanting Bag
if string.find(textLower, "enchanting bag") then
return "enchant"
end
-- Ammo Pouch
if string.find(typeLower, "ammo pouch") then
return "ammo"
-- Quiver
if string.find(textLower, "quiver") then
return "quiver"
end
-- Ammo Pouch
if string.find(textLower, "ammo pouch") then
return "ammo"
end
end
end
end
return nil
end
-- Simple helper to check if a bag is of a specific type
function Utils:IsBagType(bagID, bagType)
return self:GetSpecializedBagType(bagID) == bagType
end
-- Convenience wrappers for common bag type checks
function Utils:IsAmmoQuiverBag(bagID)
local bagType = self:GetSpecializedBagType(bagID)
return bagType == "quiver" or bagType == "ammo"
end
function Utils:IsHerbBag(bagID)
return self:IsBagType(bagID, "herb")
end
function Utils:IsSoulBag(bagID)
return self:IsBagType(bagID, "soul")
end
-- Get container priority for sorting (higher = more important)
function Utils:GetContainerPriority(bagID)
local bagType = self:GetSpecializedBagType(bagID)
@@ -727,37 +788,7 @@ function Utils:IsEnchantingItem(itemLink)
return false
end
-- Check if a bag is Enchanting Bag (parallel to IsHerbBag)
-- Check if a bag is Enchanting Bag
function Utils:IsEnchantBag(bagID)
-- Skip backpack, bank, and keyring
if bagID == 0 or bagID == -1 or bagID == -2 then
return false
end
local invSlot = ContainerIDToInventoryID(bagID)
if not invSlot then return false end
local link = GetInventoryItemLink("player", invSlot)
if not link then return false end
-- Prefer specialized type detection
local bagType = self:GetSpecializedBagType(bagID)
if bagType == "enchant" then return true end
-- Fallback: tooltip scan for "Enchanting Bag"
local tooltip = GetScanTooltip()
tooltip:ClearLines()
tooltip:SetInventoryItem("player", invSlot)
for i = 1, tooltip:NumLines() do
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
if line then
local text = line:GetText()
if text and string.find(string.lower(text), "enchanting bag") then
return true
end
end
end
return false
return self:IsBagType(bagID, "enchant")
end