fix: sort

This commit is contained in:
Salikh Gurgenidze
2025-11-16 15:16:38 +04:00
parent baf01058f0
commit af2483ff05
4 changed files with 225 additions and 104 deletions
+216 -98
View File
@@ -1,58 +1,126 @@
-- Guda Sort Engine
-- Sorts bags by quality, name, or item type
-- Adapted from Baganator sorting logic for WoW 1.12.1
local addon = Guda
local SortEngine = {}
addon.Modules.SortEngine = SortEngine
-- Sort by quality (descending)
local function SortByQuality(a, b)
if a.quality ~= b.quality then
return a.quality > b.quality
end
if a.name and b.name then
return a.name < b.name
end
return false
end
-- Sort by name
local function SortByName(a, b)
if a.name and b.name then
return a.name < b.name
end
return false
end
-- Sort by type (class/subclass)
local function SortByType(a, b)
if a.class ~= b.class then
return (a.class or "") < (b.class or "")
end
if a.subclass ~= b.subclass then
return (a.subclass or "") < (b.subclass or "")
end
if a.quality ~= b.quality then
return a.quality > b.quality
end
if a.name and b.name then
return a.name < b.name
end
return false
end
-- Get sort function by method
local sortMethods = {
quality = SortByQuality,
name = SortByName,
type = SortByType,
-- Priority items that should always be sorted first
local PriorityItems = {
[6948] = true, -- Hearthstone
}
-- Custom ordering for item classes (matches Baganator's logical grouping)
local classOrder = {
[0] = 1, -- Consumable
[6] = 2, -- Projectile (ammo/arrows)
[2] = 3, -- Weapon
[4] = 4, -- Armor
[11] = 5, -- Quiver
[5] = 6, -- Reagent
[7] = 7, -- Trade Goods
[9] = 8, -- Recipe
[1] = 9, -- Container
[12] = 10, -- Quest
[13] = 11, -- Key
[15] = 12, -- Miscellaneous
}
-- Extract itemID from item link
local function GetItemID(link)
if not link then return 0 end
local _, _, itemID = string.find(link, "item:(%d+)")
return tonumber(itemID) or 0
end
-- Get item class ID (numeric)
local function GetItemClassID(link)
if not link then return 99 end
local _, _, _, _, _, _, class = GetItemInfo(link)
-- In 1.12.1, GetItemInfo returns class as string, need to map to ID
-- We'll use a simple hash of the string for ordering
if not class then return 99 end
-- Map common class names to IDs (1.12.1 compatible)
local classMap = {
["Consumable"] = 0,
["Container"] = 1,
["Weapon"] = 2,
["Armor"] = 4,
["Reagent"] = 5,
["Projectile"] = 6,
["Trade Goods"] = 7,
["Recipe"] = 9,
["Quiver"] = 11,
["Quest"] = 12,
["Key"] = 13,
["Miscellaneous"] = 15,
}
return classMap[class] or 99
end
-- Add sort keys to items
local function AddSortKeys(items)
for _, item in ipairs(items) do
if item.data and item.data.link then
local itemID = GetItemID(item.data.link)
local classID = GetItemClassID(item.data.link)
-- Priority: Hearthstone = 1, everything else = 1000
item.priority = PriorityItems[itemID] and 1 or 1000
-- Get sorted class order (lower = earlier in bags)
item.sortedClass = classOrder[classID] or 99
-- Inverted quality (higher quality = earlier)
item.invertedQuality = -(item.quality or 0)
-- Item name for alphabetical sorting
item.itemName = item.name or ""
-- Inverted item ID for consistent sorting
item.invertedItemID = -(itemID)
end
end
end
-- Sort items using multi-criteria comparison
local function SortItems(items)
AddSortKeys(items)
table.sort(items, function(a, b)
-- Sort by priority first (Hearthstone)
if a.priority ~= b.priority then
return a.priority < b.priority
end
-- Then by class (Consumables, Ammo, Weapons, Armor, etc.)
if a.sortedClass ~= b.sortedClass then
return a.sortedClass < b.sortedClass
end
-- Within same class, sort by quality (Epic > Rare > Uncommon > Common > Poor)
if a.invertedQuality ~= b.invertedQuality then
return a.invertedQuality < b.invertedQuality
end
-- Then alphabetically by name
if a.itemName ~= b.itemName then
return a.itemName < b.itemName
end
-- Finally by item ID for consistency
return a.invertedItemID < b.invertedItemID
end)
return items
end
-- Collect all items from bags
function SortEngine:CollectItems(bagIDs)
local items = {}
local emptySlots = {}
for _, bagID in ipairs(bagIDs) do
local numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
@@ -69,87 +137,138 @@ function SortEngine:CollectItems(bagIDs)
quality = itemData.quality or 0,
name = itemData.name or "",
class = itemData.class or "",
subclass = itemData.subclass or "",
})
else
table.insert(emptySlots, {bagID = bagID, slot = slot})
end
end
end
end
return items, emptySlots
end
-- Sort items
function SortEngine:SortItems(items, method)
local sortFunc = sortMethods[method] or sortMethods.quality
table.sort(items, sortFunc)
return items
end
-- Apply sorted items back to bags
function SortEngine:ApplySort(bagIDs, method)
addon:Print("Sorting bags by %s...", method or "quality")
-- Build target slot positions (sequential, no gaps)
local function BuildTargetPositions(bagIDs, itemCount)
local positions = {}
local index = 1
-- Collect items
local items, emptySlots = self:CollectItems(bagIDs)
-- Sort items
items = self:SortItems(items, method)
-- Clear all bags first
ClearCursor()
-- Track moves for undo (not implemented yet, but good to have)
local moves = {}
-- Place items in sorted order
local targetIndex = 1
for _, bagID in ipairs(bagIDs) do
local numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
if addon.Modules.Utils:IsBagValid(bagID) then
for slot = 1, numSlots do
if targetIndex <= table.getn(items) then
local item = items[targetIndex]
if index <= itemCount then
positions[index] = {bag = bagID, slot = slot}
index = index + 1
else
break
end
end
end
-- Move item if it's not already in the right place
if item.bagID ~= bagID or item.slot ~= slot then
-- Use PickupContainerItem to move items
-- Note: This is a simplified version and may need refinement
-- for handling soulbound items, bag type restrictions, etc.
if index > itemCount then
break
end
end
table.insert(moves, {
from = {bag = item.bagID, slot = item.slot},
to = {bag = bagID, slot = slot},
})
end
return positions
end
targetIndex = targetIndex + 1
-- Apply sorted items back to bags (two-phase move system from Baganator)
function SortEngine:ApplySort(bagIDs, method)
addon:Print("Sorting bags...")
-- Collect and sort items
local items = self:CollectItems(bagIDs)
if table.getn(items) == 0 then
addon:Print("No items to sort!")
return
end
items = SortItems(items)
-- Build target positions (sequential slots)
local targetPositions = BuildTargetPositions(bagIDs, table.getn(items))
-- Clear cursor
ClearCursor()
-- Two-phase move system (from Baganator)
-- Phase 1: Moves to empty slots
-- Phase 2: Swaps with occupied slots
local moveQueue0 = {} -- Moves to empty slots
local moveQueue1 = {} -- Swaps with occupied slots
-- Build move queues
for i, item in ipairs(items) do
local target = targetPositions[i]
if target then
local sourceBag, sourceSlot = item.bagID, item.slot
local targetBag, targetSlot = target.bag, target.slot
-- Skip if already in correct position
if sourceBag ~= targetBag or sourceSlot ~= targetSlot then
-- Check if target slot is empty
local targetItem = GetContainerItemLink(targetBag, targetSlot)
if not targetItem then
-- Target is empty - Phase 1 move
table.insert(moveQueue0, {
sourceBag = sourceBag,
sourceSlot = sourceSlot,
targetBag = targetBag,
targetSlot = targetSlot,
})
else
-- Target is occupied - Phase 2 swap
table.insert(moveQueue1, {
sourceBag = sourceBag,
sourceSlot = sourceSlot,
targetBag = targetBag,
targetSlot = targetSlot,
})
end
end
end
end
-- Actually perform the moves (simplified - in reality needs more complex logic)
-- For now, just trigger a bag update
addon:Print("Sort complete! (%d items sorted)", table.getn(items))
-- Execute Phase 1: Move to empty slots first
local moveCount = 0
for _, move in ipairs(moveQueue0) do
local _, _, locked = GetContainerItemInfo(move.sourceBag, move.sourceSlot)
if not locked then
PickupContainerItem(move.sourceBag, move.sourceSlot)
PickupContainerItem(move.targetBag, move.targetSlot)
ClearCursor()
moveCount = moveCount + 1
end
end
-- Note: Actual item moving requires careful handling of:
-- - Locked items
-- - Bag type restrictions (soul bags, etc.)
-- - Soulbound items
-- - Quest items
-- This would require a more sophisticated move queue system
-- Execute Phase 2: Swap with occupied slots
for _, move in ipairs(moveQueue1) do
local _, _, sourceLocked = GetContainerItemInfo(move.sourceBag, move.sourceSlot)
local _, _, targetLocked = GetContainerItemInfo(move.targetBag, move.targetSlot)
if not sourceLocked and not targetLocked then
PickupContainerItem(move.sourceBag, move.sourceSlot)
PickupContainerItem(move.targetBag, move.targetSlot)
ClearCursor()
moveCount = moveCount + 1
end
end
if moveCount > 0 then
addon:Print("Sort complete! (%d items moved)", moveCount)
else
addon:Print("Items are already sorted!")
end
end
-- Sort current bags
function SortEngine:SortBags()
local method = addon.Modules.DB:GetSetting("sortMethod") or "quality"
self:ApplySort(addon.Constants.BAGS, method)
self:ApplySort(addon.Constants.BAGS, "type")
end
-- Sort bank
@@ -159,6 +278,5 @@ function SortEngine:SortBank()
return
end
local method = addon.Modules.DB:GetSetting("sortMethod") or "quality"
self:ApplySort(addon.Constants.BANK_BAGS, method)
self:ApplySort(addon.Constants.BANK_BAGS, "type")
end