refactory: centralized category view

This commit is contained in:
Salikh Gurgenidze
2026-01-04 03:43:53 +04:00
parent 42c4c0057a
commit 61eb0b6844
3 changed files with 176 additions and 257 deletions
+11 -147
View File
@@ -387,109 +387,18 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
local perRow = addon.Modules.DB:GetSetting("bagColumns") or 10 local perRow = addon.Modules.DB:GetSetting("bagColumns") or 10
local itemContainer = getglobal("Guda_BagFrame_ItemContainer") local itemContainer = getglobal("Guda_BagFrame_ItemContainer")
-- Group items by category -- Use centralized category initialization
local categories = {} local categories, specialItems = Guda_InitCategories()
local categoryList = { local categoryList = Guda_CategoryList
"Weapon", "Armor", "BoE", "Consumable", "Food", "Drink", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous", "Quest", "Junk", "Class Items", "Keyring"
}
for _, cat in ipairs(categoryList) do categories[cat] = {} end
local specialItems = {
Hearthstone = {},
Mount = {},
Tools = {}
}
-- Categorize all items using centralized function
for _, bagID in ipairs(addon.Constants.BAGS) do for _, bagID in ipairs(addon.Constants.BAGS) do
if not hiddenBags[bagID] then if not hiddenBags[bagID] then
local bag = bagData[bagID] local bag = bagData[bagID]
if bag and bag.slots then if bag and bag.slots then
for slotID, itemData in pairs(bag.slots) do for slotID, itemData in pairs(bag.slots) do
if itemData then if itemData then
local cat = "Miscellaneous" Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems, isOtherChar)
local itemName = itemData.name or ""
local itemType = itemData.type or ""
-- Detect consumable restore/eat/drink tag for current character only
if not isOtherChar and addon.Modules.Utils and addon.Modules.Utils.GetConsumableRestoreTag then
local tag = addon.Modules.Utils:GetConsumableRestoreTag(bagID, slotID)
if tag then
itemData.restoreTag = tag
end
end
-- Priority 1: Special items (Hearthstone, Mounts, Tools)
if string.find(itemName, "Hearthstone") then
table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData})
elseif addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then
table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData})
elseif string.find(itemName, "Runed .* Rod") or
itemType == "Fishing Pole" or
string.find(itemName, "Mining Pick") or
string.find(itemName, "Blacksmith Hammer") or
itemName == "Arclight Spanner" or
itemName == "Gyromatic Micro-Adjustor" or
itemName == "Philosopher's Stone" or
string.find(itemName, "Skinning Knife") or
itemName == "Blood Scythe" then
table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData})
-- Priority 2: Class Items (Soul Shards, Arrows, Bullets)
elseif addon.Modules.Utils:IsSoulShard(itemData.link) or
itemData.class == "Projectile" or
itemData.subclass == "Arrow" or
itemData.subclass == "Bullet" then
table.insert(categories["Class Items"], {bagID = bagID, slotID = slotID, itemData = itemData})
-- Priority 3: Quest Items
elseif (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then
table.insert(categories["Quest"], {bagID = bagID, slotID = slotID, itemData = itemData})
-- Priority 4: Junk (Gray items)
elseif itemData.quality == 0 or addon.Modules.Utils:IsItemGrayTooltip(bagID, slotID, itemData.link) then
table.insert(categories["Junk"], {bagID = bagID, slotID = slotID, itemData = itemData})
-- Priority 5: Food and Drink
elseif itemData.class == "Consumable" then
cat = "Consumable"
local sub = itemData.subclass or ""
if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then
if string.find(sub, "Drink") then
cat = "Drink"
else
cat = "Food"
end
end
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
-- Priority 6: BoE Equipment (Armor/Weapons that bind when equipped)
elseif (itemData.class == "Weapon" or itemData.class == "Armor") and
not isOtherChar then
-- Check for BoE
local isBoE = addon.Modules.Utils:IsBindOnEquip(bagID, slotID)
if isBoE then
table.insert(categories["BoE"], {bagID = bagID, slotID = slotID, itemData = itemData})
else
-- Not BoE, put in regular category
table.insert(categories[itemData.class], {bagID = bagID, slotID = slotID, itemData = itemData})
end
-- Priority 7: Equipment (other equippable items like trinkets, rings, or viewing other character's gear)
elseif itemData.equipSlot and itemData.equipSlot ~= "" then
-- For other characters, can't scan tooltip so just categorize normally
if itemData.class == "Weapon" or itemData.class == "Armor" then
cat = itemData.class
else
cat = "Armor"
end
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
-- Priority 8: Other Categories
else
cat = itemData.class or "Miscellaneous"
if not categories[cat] then cat = "Miscellaneous" end
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
end
end end
end end
end end
@@ -540,50 +449,14 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
local items = categories[catName] local items = categories[catName]
local numItems = items and table.getn(items) or 0 local numItems = items and table.getn(items) or 0
if numItems > 0 then if numItems > 0 then
-- Sort items in category: Subclass > Quality > Name -- Sort items using centralized sorter
table.sort(items, function(a, b) Guda_SortCategoryItems(items)
-- Rank Trade Goods: meat (name ends with 'meat') = 2, egg (contains 'egg') = 1, others = 0
local function tgRank(d)
if not d or not d.name then return 0 end
local t = d.type or d.class or ""
if t ~= "Trade Goods" then return 0 end
local n = string.lower(d.name)
if string.find(n, "meat$") then return 2 end
if string.find(n, "egg") then return 1 end
return 0
end
local ra = tgRank(a.itemData)
local rb = tgRank(b.itemData)
if ra ~= rb then
return ra > rb
end
-- Priority: consumable restore tags (eat > drink > restore > nil)
local pa = a.itemData and a.itemData.restoreTag or nil
local pb = b.itemData and b.itemData.restoreTag or nil
local function pr(t)
if t == "eat" then return 3 end
if t == "drink" then return 2 end
if t == "restore" then return 1 end
return 0
end
if pr(pa) ~= pr(pb) then
return pr(pa) > pr(pb)
end
-- Fallback: subclass, quality, name
if a.itemData.subclass ~= b.itemData.subclass then
return (a.itemData.subclass or "") < (b.itemData.subclass or "")
end
if a.itemData.quality ~= b.itemData.quality then
return a.itemData.quality > b.itemData.quality
end
return (a.itemData.name or "") < (b.itemData.name or "")
end)
local blockCols = numItems local blockCols = numItems
if blockCols > perRow then blockCols = perRow end if blockCols > perRow then blockCols = perRow end
local blockRows = math.ceil(numItems / perRow) local blockRows = math.ceil(numItems / perRow)
local blockWidth = blockCols * (buttonSize + spacing) local blockWidth = blockCols * (buttonSize + spacing)
local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + 5 -- 20 header, 5 padding local blockHeight = 20 + (blockRows * (buttonSize + spacing)) + 5
-- Check if it fits in current row -- Check if it fits in current row
if currentX > 0 and currentX + blockWidth + 20 > totalWidth + 5 then if currentX > 0 and currentX + blockWidth + 20 > totalWidth + 5 then
@@ -639,7 +512,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
end end
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
currentX = currentX + blockWidth + 20 -- 20px gap between categories currentX = currentX + blockWidth + 20
end end
end end
@@ -655,9 +528,8 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
} }
local x = startX local x = startX
y = startY - y -- convert to coordinate relative to TOPLEFT y = startY - y
-- Add spacing before special section
local hasAnyBottom = false local hasAnyBottom = false
for _, sec in ipairs(bottomSections) do for _, sec in ipairs(bottomSections) do
if table.getn(sec.items) > 0 then if table.getn(sec.items) > 0 then
@@ -678,7 +550,6 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
numItems = (totalFreeSlots > 0) and 1 or 0 numItems = (totalFreeSlots > 0) and 1 or 0
end end
if numItems > 0 then if numItems > 0 then
-- Sort Tools (Hearthstone/Mounts don't usually need it but good for consistency)
if sec.name == "Tools" then if sec.name == "Tools" then
table.sort(items, function(a, b) table.sort(items, function(a, b)
if a.itemData.quality ~= b.itemData.quality then if a.itemData.quality ~= b.itemData.quality then
@@ -694,14 +565,12 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
local blockWidth = blockCols * (buttonSize + spacing) local blockWidth = blockCols * (buttonSize + spacing)
local blockHeight = 20 + (blockRows * (buttonSize + spacing)) local blockHeight = 20 + (blockRows * (buttonSize + spacing))
-- Check if it fits in current row (Inline block for bottom sections too)
if currentBottomX > 0 and currentBottomX + blockWidth + 20 > totalWidth + 5 then if currentBottomX > 0 and currentBottomX + blockWidth + 20 > totalWidth + 5 then
currentBottomX = 0 currentBottomX = 0
y = y - sectionMaxHeight - 5 y = y - sectionMaxHeight - 5
sectionMaxHeight = 0 sectionMaxHeight = 0
end end
-- Add Header
local header = self:GetSectionHeader(headerIdx) local header = self:GetSectionHeader(headerIdx)
headerIdx = headerIdx + 1 headerIdx = headerIdx + 1
header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, y) header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, y)
@@ -714,7 +583,6 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
local sRow = 0 local sRow = 0
if sec.name == "Empty" then if sec.name == "Empty" then
-- Display one empty slot button with count
local bagID = firstFreeBag or 0 local bagID = firstFreeBag or 0
local slotID = firstFreeSlot or 1 local slotID = firstFreeSlot or 1
local bagParent = self:GetBagParent(bagID) local bagParent = self:GetBagParent(bagID)
@@ -731,9 +599,7 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
count = totalFreeSlots, count = totalFreeSlots,
name = "Empty Slots" name = "Empty Slots"
} }
-- Use fake data but real IDs for drop handling
Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, false, isOtherChar and charName or nil, true, true) Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, false, isOtherChar and charName or nil, true, true)
-- Ensure it's not actually read-only for drop behavior (it should still receive clicks/drops)
button.isReadOnly = false button.isReadOnly = false
button.inUse = true button.inUse = true
table.insert(itemButtons, button) table.insert(itemButtons, button)
@@ -760,9 +626,8 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
end end
if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end
currentBottomX = currentBottomX + blockWidth + 20 -- Match the 20px gap used in top categories currentBottomX = currentBottomX + blockWidth + 20
-- If we wrapped exactly at the end of a block
if currentBottomX >= totalWidth then if currentBottomX >= totalWidth then
currentBottomX = 0 currentBottomX = 0
y = y - sectionMaxHeight - 5 y = y - sectionMaxHeight - 5
@@ -776,7 +641,6 @@ function BagFrame:DisplayItemsByCategory(bagData, isOtherChar, charName)
end end
end end
-- Update container height
local finalHeight = math.abs(y) + 20 local finalHeight = math.abs(y) + 20
itemContainer:SetHeight(finalHeight) itemContainer:SetHeight(finalHeight)
self:ResizeFrame(nil, nil, perRow, finalHeight) self:ResizeFrame(nil, nil, perRow, finalHeight)
+10 -109
View File
@@ -234,100 +234,18 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
local perRow = addon.Modules.DB:GetSetting("bankColumns") or 10 local perRow = addon.Modules.DB:GetSetting("bankColumns") or 10
local itemContainer = getglobal("Guda_BankFrame_ItemContainer") local itemContainer = getglobal("Guda_BankFrame_ItemContainer")
-- Group items by category -- Use centralized category initialization
local categories = {} local categories, specialItems = Guda_InitCategories()
local categoryList = { local categoryList = Guda_CategoryList
"Weapon", "Armor", "Consumable", "Food", "Drink", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous", "Quest", "Junk", "Class Items"
}
for _, cat in ipairs(categoryList) do categories[cat] = {} end
local specialItems = { -- Categorize all items using centralized function
Hearthstone = {},
Mount = {},
Tools = {},
Empty = {}
}
-- Helper to assign category
local function CategorizeItem(itemData, bagID, slotID)
local itemName = itemData.name or ""
local itemType = itemData.type or ""
local cat = "Miscellaneous"
-- Priority 1: Special items (Hearthstone, Mounts, Tools)
if string.find(itemName, "Hearthstone") then
table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData})
return
elseif addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then
table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData})
return
elseif string.find(itemName, "Runed .* Rod") or
itemType == "Fishing Pole" or
string.find(itemName, "Mining Pick") or
string.find(itemName, "Blacksmith Hammer") or
itemName == "Arclight Spanner" or
itemName == "Gyromatic Micro-Adjustor" or
itemName == "Philosopher's Stone" or
string.find(itemName, "Skinning Knife") or
itemName == "Blood Scythe" then
table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 2: Class Items (Soul Shards, Arrows, Bullets)
if addon.Modules.Utils:IsSoulShard(itemData.link) or
itemData.class == "Projectile" or
itemData.subclass == "Arrow" or
itemData.subclass == "Bullet" then
table.insert(categories["Class Items"], {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 3: Quest Items
if (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then
cat = "Quest"
-- Priority 4: Junk (Gray items)
elseif itemData.quality == 0 or addon.Modules.Utils:IsItemGrayTooltip(bagID, slotID, itemData.link) then
cat = "Junk"
-- Priority 5: Food and Drink
elseif itemData.class == "Consumable" then
cat = "Consumable"
local sub = itemData.subclass or ""
if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then
if string.find(sub, "Drink") then
cat = "Drink"
else
cat = "Food"
end
end
-- Priority 4: Equipment (Weapon and Armor)
elseif itemData.equipSlot and itemData.equipSlot ~= "" then
if itemData.class == "Weapon" or itemData.class == "Armor" then
cat = itemData.class
else
cat = "Armor"
end
-- Priority 5: Other Categories
else
cat = itemData.class or "Miscellaneous"
end
if not categories[cat] then cat = "Miscellaneous" end
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
end
-- Bank slots
for _, bagID in ipairs(addon.Constants.BANK_BAGS) do for _, bagID in ipairs(addon.Constants.BANK_BAGS) do
if not hiddenBankBags[bagID] then if not hiddenBankBags[bagID] then
local bag = bankData[bagID] local bag = bankData[bagID]
if bag and bag.slots then if bag and bag.slots then
for slotID, itemData in pairs(bag.slots) do for slotID, itemData in pairs(bag.slots) do
if itemData then if itemData then
CategorizeItem(itemData, bagID, slotID) Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems, isOtherChar)
end end
end end
end end
@@ -366,16 +284,8 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
local items = categories[catName] local items = categories[catName]
local numItems = items and table.getn(items) or 0 local numItems = items and table.getn(items) or 0
if numItems > 0 then if numItems > 0 then
-- Sort items in category: Subclass > Quality > Name -- Sort items using centralized sorter
table.sort(items, function(a, b) Guda_SortCategoryItems(items)
if a.itemData.subclass ~= b.itemData.subclass then
return (a.itemData.subclass or "") < (b.itemData.subclass or "")
end
if a.itemData.quality ~= b.itemData.quality then
return a.itemData.quality > b.itemData.quality
end
return (a.itemData.name or "") < (b.itemData.name or "")
end)
local blockCols = numItems local blockCols = numItems
if blockCols > perRow then blockCols = perRow end if blockCols > perRow then blockCols = perRow end
@@ -436,7 +346,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
end end
if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end if blockHeight > rowMaxHeight then rowMaxHeight = blockHeight end
currentX = currentX + blockWidth + 20 -- 20px gap between categories currentX = currentX + blockWidth + 20
end end
end end
@@ -452,9 +362,8 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
} }
local x = startX local x = startX
y = startY - y -- convert to coordinate relative to TOPLEFT y = startY - y
-- Add spacing before special section
local hasAnyBottom = false local hasAnyBottom = false
for _, sec in ipairs(bottomSections) do for _, sec in ipairs(bottomSections) do
if table.getn(sec.items) > 0 then if table.getn(sec.items) > 0 then
@@ -475,7 +384,6 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
numItems = (totalFreeSlots > 0) and 1 or 0 numItems = (totalFreeSlots > 0) and 1 or 0
end end
if numItems > 0 then if numItems > 0 then
-- Sort Tools (Hearthstone/Mounts don't usually need it but good for consistency)
if sec.name == "Tools" then if sec.name == "Tools" then
table.sort(items, function(a, b) table.sort(items, function(a, b)
if a.itemData.quality ~= b.itemData.quality then if a.itemData.quality ~= b.itemData.quality then
@@ -491,14 +399,12 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
local blockWidth = blockCols * (buttonSize + spacing) local blockWidth = blockCols * (buttonSize + spacing)
local blockHeight = 20 + (blockRows * (buttonSize + spacing)) local blockHeight = 20 + (blockRows * (buttonSize + spacing))
-- Check if it fits in current row (Inline block for bottom sections too)
if currentBottomX > 0 and currentBottomX + blockWidth + 20 > totalWidth + 5 then if currentBottomX > 0 and currentBottomX + blockWidth + 20 > totalWidth + 5 then
currentBottomX = 0 currentBottomX = 0
y = y - sectionMaxHeight - 5 y = y - sectionMaxHeight - 5
sectionMaxHeight = 0 sectionMaxHeight = 0
end end
-- Add Header
local header = self:GetSectionHeader(headerIdx) local header = self:GetSectionHeader(headerIdx)
headerIdx = headerIdx + 1 headerIdx = headerIdx + 1
header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, y) header:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", x + currentBottomX, y)
@@ -511,7 +417,6 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
local sRow = 0 local sRow = 0
if sec.name == "Empty" then if sec.name == "Empty" then
-- Display one empty slot button with count
local bagID = firstFreeBag or -1 local bagID = firstFreeBag or -1
local slotID = firstFreeSlot or 1 local slotID = firstFreeSlot or 1
local bagParent = self:GetBagParent(bagID) local bagParent = self:GetBagParent(bagID)
@@ -528,9 +433,7 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
count = totalFreeSlots, count = totalFreeSlots,
name = "Empty Slots" name = "Empty Slots"
} }
-- Use fake data but real IDs for drop handling
Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, true, isOtherChar and charName or nil, true, true) Guda_ItemButton_SetItem(button, bagID, slotID, emptyItemData, true, isOtherChar and charName or nil, true, true)
-- Ensure it's not actually read-only for drop behavior
button.isReadOnly = false button.isReadOnly = false
button.inUse = true button.inUse = true
else else
@@ -555,9 +458,8 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
end end
if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end if blockHeight > sectionMaxHeight then sectionMaxHeight = blockHeight end
currentBottomX = currentBottomX + blockWidth + 20 -- Match the 20px gap used in top categories currentBottomX = currentBottomX + blockWidth + 20
-- If we wrapped exactly at the end of a block
if currentBottomX >= totalWidth then if currentBottomX >= totalWidth then
currentBottomX = 0 currentBottomX = 0
y = y - sectionMaxHeight - 5 y = y - sectionMaxHeight - 5
@@ -571,7 +473,6 @@ function BankFrame:DisplayItemsByCategory(bankData, isOtherChar, charName)
end end
end end
-- Update container height
local finalHeight = math.abs(y) + 20 local finalHeight = math.abs(y) + 20
itemContainer:SetHeight(finalHeight) itemContainer:SetHeight(finalHeight)
self:ResizeFrame(nil, nil, perRow, finalHeight) self:ResizeFrame(nil, nil, perRow, finalHeight)
+154
View File
@@ -4,6 +4,160 @@ local addon = Guda
local FrameHelpers = {} local FrameHelpers = {}
addon.Modules.FrameHelpers = FrameHelpers addon.Modules.FrameHelpers = FrameHelpers
-- Standard category list used by both Bag and Bank frames
Guda_CategoryList = {
"BoE", "Weapon", "Armor", "Consumable", "Food", "Drink", "Trade Goods", "Reagent", "Recipe", "Quiver", "Container", "Soul Bag", "Miscellaneous", "Quest", "Junk", "Class Items", "Keyring"
}
-- Categorize a single item into categories and specialItems tables
-- Returns nothing, modifies tables in place
function Guda_CategorizeItem(itemData, bagID, slotID, categories, specialItems, isOtherChar)
local itemName = itemData.name or ""
local itemType = itemData.type or ""
local cat = "Miscellaneous"
-- Detect consumable restore/eat/drink tag for current character only
if not isOtherChar and addon.Modules.Utils and addon.Modules.Utils.GetConsumableRestoreTag then
local tag = addon.Modules.Utils:GetConsumableRestoreTag(bagID, slotID)
if tag then
itemData.restoreTag = tag
end
end
-- Priority 1: Special items (Hearthstone, Mounts, Tools)
if string.find(itemName, "Hearthstone") then
table.insert(specialItems.Hearthstone, {bagID = bagID, slotID = slotID, itemData = itemData})
return
elseif addon.Modules.SortEngine and addon.Modules.SortEngine.IsMount and addon.Modules.SortEngine.IsMount(itemData.texture) then
table.insert(specialItems.Mount, {bagID = bagID, slotID = slotID, itemData = itemData})
return
elseif string.find(itemName, "Runed .* Rod") or
itemType == "Fishing Pole" or
string.find(itemName, "Mining Pick") or
string.find(itemName, "Blacksmith Hammer") or
itemName == "Arclight Spanner" or
itemName == "Gyromatic Micro-Adjustor" or
itemName == "Philosopher's Stone" or
string.find(itemName, "Skinning Knife") or
itemName == "Blood Scythe" then
table.insert(specialItems.Tools, {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 2: Class Items (Soul Shards, Arrows, Bullets)
if addon.Modules.Utils:IsSoulShard(itemData.link) or
itemData.class == "Projectile" or
itemData.subclass == "Arrow" or
itemData.subclass == "Bullet" then
table.insert(categories["Class Items"], {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 3: Quest Items
if (not isOtherChar and addon.Modules.Utils:IsQuestItemTooltip(bagID, slotID)) or itemData.class == "Quest" then
table.insert(categories["Quest"], {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 4: Junk (Gray items)
if itemData.quality == 0 or addon.Modules.Utils:IsItemGrayTooltip(bagID, slotID, itemData.link) then
table.insert(categories["Junk"], {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 5: Food and Drink
if itemData.class == "Consumable" then
cat = "Consumable"
local sub = itemData.subclass or ""
if sub == "Food & Drink" or string.find(sub, "Food") or string.find(sub, "Drink") then
if string.find(sub, "Drink") then
cat = "Drink"
else
cat = "Food"
end
end
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 6: BoE Equipment (Armor/Weapons that bind when equipped)
if (itemData.class == "Weapon" or itemData.class == "Armor") and not isOtherChar then
local isBoE = addon.Modules.Utils:IsBindOnEquip(bagID, slotID)
if isBoE then
table.insert(categories["BoE"], {bagID = bagID, slotID = slotID, itemData = itemData})
else
table.insert(categories[itemData.class], {bagID = bagID, slotID = slotID, itemData = itemData})
end
return
end
-- Priority 7: Equipment for other characters (can't scan tooltip)
if (itemData.class == "Weapon" or itemData.class == "Armor") and isOtherChar then
table.insert(categories[itemData.class], {bagID = bagID, slotID = slotID, itemData = itemData})
return
end
-- Priority 8: Other Categories
cat = itemData.class or "Miscellaneous"
if not categories[cat] then cat = "Miscellaneous" end
table.insert(categories[cat], {bagID = bagID, slotID = slotID, itemData = itemData})
end
-- Initialize empty category tables
function Guda_InitCategories()
local categories = {}
for _, cat in ipairs(Guda_CategoryList) do
categories[cat] = {}
end
local specialItems = {
Hearthstone = {},
Mount = {},
Tools = {}
}
return categories, specialItems
end
-- Sort items within a category
function Guda_SortCategoryItems(items)
table.sort(items, function(a, b)
-- Rank Trade Goods: meat (name ends with 'meat') = 2, egg (contains 'egg') = 1, others = 0
local function tgRank(d)
if not d or not d.name then return 0 end
local t = d.type or d.class or ""
if t ~= "Trade Goods" then return 0 end
local n = string.lower(d.name)
if string.find(n, "meat$") then return 2 end
if string.find(n, "egg") then return 1 end
return 0
end
local ra = tgRank(a.itemData)
local rb = tgRank(b.itemData)
if ra ~= rb then
return ra > rb
end
-- Priority: consumable restore tags (eat > drink > restore > nil)
local pa = a.itemData and a.itemData.restoreTag or nil
local pb = b.itemData and b.itemData.restoreTag or nil
local function pr(t)
if t == "eat" then return 3 end
if t == "drink" then return 2 end
if t == "restore" then return 1 end
return 0
end
if pr(pa) ~= pr(pb) then
return pr(pa) > pr(pb)
end
-- Fallback: subclass, quality, name
if a.itemData.subclass ~= b.itemData.subclass then
return (a.itemData.subclass or "") < (b.itemData.subclass or "")
end
if a.itemData.quality ~= b.itemData.quality then
return a.itemData.quality > b.itemData.quality
end
return (a.itemData.name or "") < (b.itemData.name or "")
end)
end
-- Create or return a section header for a given frame prefix and container -- Create or return a section header for a given frame prefix and container
function Guda_GetSectionHeader(framePrefix, containerName, index) function Guda_GetSectionHeader(framePrefix, containerName, index)
local name = framePrefix .. "_SectionHeader" .. index local name = framePrefix .. "_SectionHeader" .. index