sorting fixed
This commit is contained in:
+146
@@ -207,3 +207,149 @@ function Utils:IsAmmoQuiverBag(bagID)
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Get specialized bag type (for sorting priority)
|
||||||
|
-- Returns: "soul", "quiver", "ammo", or nil
|
||||||
|
function Utils:GetSpecializedBagType(bagID)
|
||||||
|
-- Skip backpack, bank, and keyring
|
||||||
|
if bagID == 0 or bagID == -1 or bagID == -2 then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the bag item
|
||||||
|
local invSlot = ContainerIDToInventoryID(bagID)
|
||||||
|
if not invSlot then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
local link = GetInventoryItemLink("player", invSlot)
|
||||||
|
if not link then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Use tooltip scanning
|
||||||
|
local tooltip = GetScanTooltip()
|
||||||
|
tooltip:ClearLines()
|
||||||
|
tooltip:SetInventoryItem("player", invSlot)
|
||||||
|
|
||||||
|
-- Scan tooltip lines
|
||||||
|
for i = 1, tooltip:NumLines() do
|
||||||
|
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
|
||||||
|
if line then
|
||||||
|
local text = line:GetText()
|
||||||
|
if text then
|
||||||
|
if string.find(text, "Soul") then
|
||||||
|
return "soul"
|
||||||
|
elseif string.find(text, "Quiver") then
|
||||||
|
return "quiver"
|
||||||
|
elseif string.find(text, "Ammo Pouch") then
|
||||||
|
return "ammo"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get container priority for sorting (higher = more important)
|
||||||
|
function Utils:GetContainerPriority(bagID)
|
||||||
|
local bagType = self:GetSpecializedBagType(bagID)
|
||||||
|
if bagType == "soul" then
|
||||||
|
return 40
|
||||||
|
elseif bagType == "quiver" then
|
||||||
|
return 30
|
||||||
|
elseif bagType == "ammo" then
|
||||||
|
return 20
|
||||||
|
else
|
||||||
|
return 10 -- Regular bag
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Soul Shard item ID
|
||||||
|
local SOUL_SHARD_ID = 6265
|
||||||
|
|
||||||
|
-- Check if item is a Soul Shard
|
||||||
|
function Utils:IsSoulShard(itemLink)
|
||||||
|
if not itemLink then return false end
|
||||||
|
local _, _, itemID = string.find(itemLink, "item:(%d+)")
|
||||||
|
return tonumber(itemID) == SOUL_SHARD_ID
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Extract hyperlink from item link for tooltip scanning
|
||||||
|
local function ExtractHyperlink(itemLink)
|
||||||
|
if not itemLink then return nil end
|
||||||
|
local _, _, hyperlink = string.find(itemLink, "|H(.+)|h")
|
||||||
|
return hyperlink
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if item is Arrow or Bullet (for Quiver routing)
|
||||||
|
function Utils:IsArrowOrBullet(itemLink)
|
||||||
|
if not itemLink then return false end
|
||||||
|
|
||||||
|
local hyperlink = ExtractHyperlink(itemLink)
|
||||||
|
if not hyperlink then return false end
|
||||||
|
|
||||||
|
-- Use tooltip scanning
|
||||||
|
local tooltip = GetScanTooltip()
|
||||||
|
tooltip:ClearLines()
|
||||||
|
tooltip:SetHyperlink(hyperlink)
|
||||||
|
|
||||||
|
for i = 1, tooltip:NumLines() do
|
||||||
|
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
|
||||||
|
if line then
|
||||||
|
local text = line:GetText()
|
||||||
|
if text then
|
||||||
|
if string.find(text, "Arrow") or string.find(text, "Bullet") then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if item is Ammo (any type)
|
||||||
|
function Utils:IsAmmo(itemLink)
|
||||||
|
if not itemLink then return false end
|
||||||
|
|
||||||
|
local hyperlink = ExtractHyperlink(itemLink)
|
||||||
|
if not hyperlink then return false end
|
||||||
|
|
||||||
|
-- Use tooltip scanning
|
||||||
|
local tooltip = GetScanTooltip()
|
||||||
|
tooltip:ClearLines()
|
||||||
|
tooltip:SetHyperlink(hyperlink)
|
||||||
|
|
||||||
|
for i = 1, tooltip:NumLines() do
|
||||||
|
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
|
||||||
|
if line then
|
||||||
|
local text = line:GetText()
|
||||||
|
if text then
|
||||||
|
-- Check for ammo keywords
|
||||||
|
if string.find(text, "Arrow") or
|
||||||
|
string.find(text, "Bullet") or
|
||||||
|
string.find(text, "Ammo") or
|
||||||
|
string.find(text, "Projectile") then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get preferred container type for an item
|
||||||
|
-- Returns: "soul", "quiver", "ammo", or nil
|
||||||
|
function Utils:GetItemPreferredContainer(itemLink)
|
||||||
|
if self:IsSoulShard(itemLink) then
|
||||||
|
return "soul"
|
||||||
|
elseif self:IsArrowOrBullet(itemLink) then
|
||||||
|
return "quiver"
|
||||||
|
elseif self:IsAmmo(itemLink) then
|
||||||
|
return "ammo"
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|||||||
+377
-49
@@ -1,5 +1,5 @@
|
|||||||
-- Guda Sort Engine
|
-- Guda Sort Engine
|
||||||
-- Adapted from Baganator sorting logic for WoW 1.12.1
|
-- 6-Phase Advanced Sorting Algorithm for WoW 1.12.1
|
||||||
|
|
||||||
local addon = Guda
|
local addon = Guda
|
||||||
|
|
||||||
@@ -11,7 +11,7 @@ local PriorityItems = {
|
|||||||
[6948] = true, -- Hearthstone
|
[6948] = true, -- Hearthstone
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Custom ordering for item classes (matches Baganator's logical grouping)
|
-- Custom ordering for item classes
|
||||||
local classOrder = {
|
local classOrder = {
|
||||||
[0] = 1, -- Consumable
|
[0] = 1, -- Consumable
|
||||||
[6] = 2, -- Projectile (ammo/arrows)
|
[6] = 2, -- Projectile (ammo/arrows)
|
||||||
@@ -38,11 +38,8 @@ end
|
|||||||
local function GetItemClassID(link)
|
local function GetItemClassID(link)
|
||||||
if not link then return 99 end
|
if not link then return 99 end
|
||||||
local _, _, _, _, _, _, class = GetItemInfo(link)
|
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
|
if not class then return 99 end
|
||||||
|
|
||||||
-- Map common class names to IDs (1.12.1 compatible)
|
|
||||||
local classMap = {
|
local classMap = {
|
||||||
["Consumable"] = 0,
|
["Consumable"] = 0,
|
||||||
["Container"] = 1,
|
["Container"] = 1,
|
||||||
@@ -61,65 +58,289 @@ local function GetItemClassID(link)
|
|||||||
return classMap[class] or 99
|
return classMap[class] or 99
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add sort keys to items
|
--===========================================================================
|
||||||
|
-- PHASE 1: Special Container Detection
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
|
local function DetectSpecializedBags(bagIDs)
|
||||||
|
local containers = {
|
||||||
|
soul = {},
|
||||||
|
quiver = {},
|
||||||
|
ammo = {},
|
||||||
|
regular = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, bagID in ipairs(bagIDs) do
|
||||||
|
local bagType = addon.Modules.Utils:GetSpecializedBagType(bagID)
|
||||||
|
if bagType == "soul" then
|
||||||
|
table.insert(containers.soul, bagID)
|
||||||
|
elseif bagType == "quiver" then
|
||||||
|
table.insert(containers.quiver, bagID)
|
||||||
|
elseif bagType == "ammo" then
|
||||||
|
table.insert(containers.ammo, bagID)
|
||||||
|
else
|
||||||
|
table.insert(containers.regular, bagID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return containers
|
||||||
|
end
|
||||||
|
|
||||||
|
--===========================================================================
|
||||||
|
-- PHASE 2: Specialized Item Routing
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
|
local function RouteSpecializedItems(bagIDs, containers)
|
||||||
|
-- This phase moves specialized items to their preferred containers
|
||||||
|
-- We'll do this by marking items for routing and then executing moves
|
||||||
|
|
||||||
|
local routingPlan = {}
|
||||||
|
|
||||||
|
-- Scan all items
|
||||||
|
for _, bagID in ipairs(bagIDs) do
|
||||||
|
local numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
|
||||||
|
if numSlots and numSlots > 0 then
|
||||||
|
for slot = 1, numSlots do
|
||||||
|
local link = GetContainerItemLink(bagID, slot)
|
||||||
|
if link then
|
||||||
|
local preferredType = addon.Modules.Utils:GetItemPreferredContainer(link)
|
||||||
|
|
||||||
|
-- If item has a preferred container type and isn't already in one
|
||||||
|
if preferredType then
|
||||||
|
local currentBagType = addon.Modules.Utils:GetSpecializedBagType(bagID)
|
||||||
|
|
||||||
|
-- If not already in preferred container
|
||||||
|
if currentBagType ~= preferredType then
|
||||||
|
local targetBags = containers[preferredType]
|
||||||
|
if targetBags and table.getn(targetBags) > 0 then
|
||||||
|
-- Find first available slot in preferred containers
|
||||||
|
local foundSlot = false
|
||||||
|
for _, targetBagID in ipairs(targetBags) do
|
||||||
|
if not foundSlot then
|
||||||
|
local targetSlots = addon.Modules.Utils:GetBagSlotCount(targetBagID)
|
||||||
|
for targetSlot = 1, targetSlots do
|
||||||
|
local targetLink = GetContainerItemLink(targetBagID, targetSlot)
|
||||||
|
if not targetLink then
|
||||||
|
-- Found empty slot - plan the move
|
||||||
|
table.insert(routingPlan, {
|
||||||
|
fromBag = bagID,
|
||||||
|
fromSlot = slot,
|
||||||
|
toBag = targetBagID,
|
||||||
|
toSlot = targetSlot
|
||||||
|
})
|
||||||
|
foundSlot = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Execute routing plan
|
||||||
|
for _, move in ipairs(routingPlan) do
|
||||||
|
PickupContainerItem(move.fromBag, move.fromSlot)
|
||||||
|
PickupContainerItem(move.toBag, move.toSlot)
|
||||||
|
ClearCursor()
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.getn(routingPlan)
|
||||||
|
end
|
||||||
|
|
||||||
|
--===========================================================================
|
||||||
|
-- PHASE 3: Stack Consolidation
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
|
local function ConsolidateStacks(bagIDs)
|
||||||
|
-- Group items by (itemID, quality, soulbound)
|
||||||
|
local itemGroups = {}
|
||||||
|
|
||||||
|
-- Collect all items with their locations
|
||||||
|
for _, bagID in ipairs(bagIDs) do
|
||||||
|
local numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
|
||||||
|
if numSlots and numSlots > 0 then
|
||||||
|
for slot = 1, numSlots do
|
||||||
|
local link = GetContainerItemLink(bagID, slot)
|
||||||
|
if link then
|
||||||
|
local texture, count, locked = GetContainerItemInfo(bagID, slot)
|
||||||
|
local itemID = GetItemID(link)
|
||||||
|
local _, _, quality = GetItemInfo(link)
|
||||||
|
|
||||||
|
-- Create group key
|
||||||
|
local groupKey = itemID .. "_" .. (quality or 0)
|
||||||
|
|
||||||
|
if not itemGroups[groupKey] then
|
||||||
|
itemGroups[groupKey] = {
|
||||||
|
itemID = itemID,
|
||||||
|
link = link,
|
||||||
|
stacks = {}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(itemGroups[groupKey].stacks, {
|
||||||
|
bagID = bagID,
|
||||||
|
slot = slot,
|
||||||
|
count = count or 1,
|
||||||
|
priority = addon.Modules.Utils:GetContainerPriority(bagID)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- For each group, consolidate stacks
|
||||||
|
local consolidationMoves = 0
|
||||||
|
for _, group in pairs(itemGroups) do
|
||||||
|
if table.getn(group.stacks) > 1 then
|
||||||
|
-- Get max stack size
|
||||||
|
local _, _, _, _, _, _, _, maxStack = GetItemInfo(group.link)
|
||||||
|
maxStack = maxStack or 1
|
||||||
|
|
||||||
|
if maxStack > 1 then
|
||||||
|
-- Sort stacks by priority DESC, then count DESC
|
||||||
|
table.sort(group.stacks, function(a, b)
|
||||||
|
if a.priority ~= b.priority then
|
||||||
|
return a.priority > b.priority
|
||||||
|
end
|
||||||
|
return a.count > b.count
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Greedy consolidation
|
||||||
|
for i = 1, table.getn(group.stacks) do
|
||||||
|
local source = group.stacks[i]
|
||||||
|
if source.count < maxStack then
|
||||||
|
for j = i + 1, table.getn(group.stacks) do
|
||||||
|
local target = group.stacks[j]
|
||||||
|
if target.count > 0 then
|
||||||
|
local spaceAvailable = maxStack - source.count
|
||||||
|
local amountToMove = math.min(spaceAvailable, target.count)
|
||||||
|
|
||||||
|
if amountToMove > 0 then
|
||||||
|
-- Split from target and add to source
|
||||||
|
if amountToMove < target.count then
|
||||||
|
SplitContainerItem(target.bagID, target.slot, amountToMove)
|
||||||
|
PickupContainerItem(source.bagID, source.slot)
|
||||||
|
ClearCursor()
|
||||||
|
else
|
||||||
|
-- Move entire stack
|
||||||
|
PickupContainerItem(target.bagID, target.slot)
|
||||||
|
PickupContainerItem(source.bagID, source.slot)
|
||||||
|
ClearCursor()
|
||||||
|
end
|
||||||
|
|
||||||
|
source.count = source.count + amountToMove
|
||||||
|
target.count = target.count - amountToMove
|
||||||
|
consolidationMoves = consolidationMoves + 1
|
||||||
|
|
||||||
|
if source.count >= maxStack then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return consolidationMoves
|
||||||
|
end
|
||||||
|
|
||||||
|
--===========================================================================
|
||||||
|
-- PHASE 4: Categorical Sorting
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
local function AddSortKeys(items)
|
local function AddSortKeys(items)
|
||||||
for _, item in ipairs(items) do
|
for _, item in ipairs(items) do
|
||||||
if item.data and item.data.link then
|
if item.data and item.data.link then
|
||||||
local itemID = GetItemID(item.data.link)
|
local itemID = GetItemID(item.data.link)
|
||||||
local classID = GetItemClassID(item.data.link)
|
local classID = GetItemClassID(item.data.link)
|
||||||
|
local _, _, quality, iLevel = GetItemInfo(item.data.link)
|
||||||
|
|
||||||
-- Priority: Hearthstone = 1, everything else = 1000
|
-- Priority: Hearthstone = 1, everything else = 1000
|
||||||
item.priority = PriorityItems[itemID] and 1 or 1000
|
item.priority = PriorityItems[itemID] and 1 or 1000
|
||||||
|
|
||||||
-- Get sorted class order (lower = earlier in bags)
|
-- Container priority
|
||||||
|
item.containerPriority = addon.Modules.Utils:GetContainerPriority(item.bagID)
|
||||||
|
|
||||||
|
-- Get sorted class order
|
||||||
item.sortedClass = classOrder[classID] or 99
|
item.sortedClass = classOrder[classID] or 99
|
||||||
|
|
||||||
-- Inverted quality (higher quality = earlier)
|
-- Inverted quality (higher quality = earlier)
|
||||||
item.invertedQuality = -(item.quality or 0)
|
item.invertedQuality = -(quality or 0)
|
||||||
|
|
||||||
|
-- Inverted item level
|
||||||
|
item.invertedItemLevel = -(iLevel or 0)
|
||||||
|
|
||||||
-- Item name for alphabetical sorting
|
-- Item name for alphabetical sorting
|
||||||
item.itemName = item.name or ""
|
item.itemName = item.name or ""
|
||||||
|
|
||||||
|
-- Inverted count (larger stacks first)
|
||||||
|
item.invertedCount = -(item.data.count or 1)
|
||||||
|
|
||||||
-- Inverted item ID for consistent sorting
|
-- Inverted item ID for consistent sorting
|
||||||
item.invertedItemID = -(itemID)
|
item.invertedItemID = -(itemID)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sort items using multi-criteria comparison
|
|
||||||
local function SortItems(items)
|
local function SortItems(items)
|
||||||
AddSortKeys(items)
|
AddSortKeys(items)
|
||||||
|
|
||||||
table.sort(items, function(a, b)
|
table.sort(items, function(a, b)
|
||||||
-- Sort by priority first (Hearthstone)
|
-- Priority items first (Hearthstone)
|
||||||
if a.priority ~= b.priority then
|
if a.priority ~= b.priority then
|
||||||
return a.priority < b.priority
|
return a.priority < b.priority
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Then by class (Consumables, Ammo, Weapons, Armor, etc.)
|
-- Container type (Soul > Quiver > Ammo > Regular)
|
||||||
|
if a.containerPriority ~= b.containerPriority then
|
||||||
|
return a.containerPriority > b.containerPriority
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Item category
|
||||||
if a.sortedClass ~= b.sortedClass then
|
if a.sortedClass ~= b.sortedClass then
|
||||||
return a.sortedClass < b.sortedClass
|
return a.sortedClass < b.sortedClass
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Within same class, sort by quality (Epic > Rare > Uncommon > Common > Poor)
|
-- Quality (Epic > Rare > Uncommon > Common > Poor)
|
||||||
if a.invertedQuality ~= b.invertedQuality then
|
if a.invertedQuality ~= b.invertedQuality then
|
||||||
return a.invertedQuality < b.invertedQuality
|
return a.invertedQuality < b.invertedQuality
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Then alphabetically by name
|
-- Item level (descending)
|
||||||
|
if a.invertedItemLevel ~= b.invertedItemLevel then
|
||||||
|
return a.invertedItemLevel < b.invertedItemLevel
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Alphabetically by name
|
||||||
if a.itemName ~= b.itemName then
|
if a.itemName ~= b.itemName then
|
||||||
return a.itemName < b.itemName
|
return a.itemName < b.itemName
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Finally by item ID for consistency
|
-- Item ID (group same items together)
|
||||||
return a.invertedItemID < b.invertedItemID
|
if a.invertedItemID ~= b.invertedItemID then
|
||||||
|
return a.invertedItemID < b.invertedItemID
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Stack count (larger stacks first within same item)
|
||||||
|
return a.invertedCount < b.invertedCount
|
||||||
end)
|
end)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Collect all items from bags
|
--===========================================================================
|
||||||
function SortEngine:CollectItems(bagIDs)
|
-- PHASE 5: Empty Slot Management & Apply Sort
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
|
local function CollectItems(bagIDs)
|
||||||
local items = {}
|
local items = {}
|
||||||
|
|
||||||
for _, bagID in ipairs(bagIDs) do
|
for _, bagID in ipairs(bagIDs) do
|
||||||
@@ -146,12 +367,27 @@ function SortEngine:CollectItems(bagIDs)
|
|||||||
return items
|
return items
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build target slot positions (sequential, no gaps)
|
|
||||||
local function BuildTargetPositions(bagIDs, itemCount)
|
local function BuildTargetPositions(bagIDs, itemCount)
|
||||||
local positions = {}
|
local positions = {}
|
||||||
local index = 1
|
local index = 1
|
||||||
|
|
||||||
|
-- Sort bags by priority: Soul (40) > Quiver (30) > Ammo (20) > Regular (10)
|
||||||
|
local sortedBags = {}
|
||||||
for _, bagID in ipairs(bagIDs) do
|
for _, bagID in ipairs(bagIDs) do
|
||||||
|
table.insert(sortedBags, {
|
||||||
|
bagID = bagID,
|
||||||
|
priority = addon.Modules.Utils:GetContainerPriority(bagID)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Sort bags by priority descending (highest first)
|
||||||
|
table.sort(sortedBags, function(a, b)
|
||||||
|
return a.priority > b.priority
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- Build positions in priority order
|
||||||
|
for _, bagInfo in ipairs(sortedBags) do
|
||||||
|
local bagID = bagInfo.bagID
|
||||||
local numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
|
local numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
|
||||||
|
|
||||||
if addon.Modules.Utils:IsBagValid(bagID) then
|
if addon.Modules.Utils:IsBagValid(bagID) then
|
||||||
@@ -173,30 +409,9 @@ local function BuildTargetPositions(bagIDs, itemCount)
|
|||||||
return positions
|
return positions
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply sorted items back to bags (two-phase move system from Baganator)
|
local function ApplySort(bagIDs, items, targetPositions)
|
||||||
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()
|
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 moveQueue0 = {} -- Moves to empty slots
|
||||||
local moveQueue1 = {} -- Swaps with occupied slots
|
local moveQueue1 = {} -- Swaps with occupied slots
|
||||||
|
|
||||||
@@ -259,24 +474,137 @@ function SortEngine:ApplySort(bagIDs, method)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if moveCount > 0 then
|
return moveCount
|
||||||
addon:Print("Sort complete! (%d items moved)", moveCount)
|
|
||||||
else
|
|
||||||
addon:Print("Items are already sorted!")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sort current bags
|
--===========================================================================
|
||||||
|
-- PHASE 6: Final Validation (Informational)
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
|
local function ValidateSort(bagIDs)
|
||||||
|
-- This is mostly informational - in practice, the sort should be correct
|
||||||
|
-- Could add checks here if needed for debugging
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
--===========================================================================
|
||||||
|
-- Main Sort Functions
|
||||||
|
--===========================================================================
|
||||||
|
|
||||||
function SortEngine:SortBags()
|
function SortEngine:SortBags()
|
||||||
self:ApplySort(addon.Constants.BAGS, "type")
|
addon:Print("Starting 6-phase bag sort...")
|
||||||
|
|
||||||
|
local bagIDs = addon.Constants.BAGS
|
||||||
|
|
||||||
|
-- Phase 1: Detect specialized bags
|
||||||
|
local containers = DetectSpecializedBags(bagIDs)
|
||||||
|
addon:Print("Phase 1: Detected %d soul, %d quiver, %d ammo, %d regular bags",
|
||||||
|
table.getn(containers.soul), table.getn(containers.quiver),
|
||||||
|
table.getn(containers.ammo), table.getn(containers.regular))
|
||||||
|
|
||||||
|
-- Phase 2: Route specialized items to their bags
|
||||||
|
local routeCount = RouteSpecializedItems(bagIDs, containers)
|
||||||
|
if routeCount > 0 then
|
||||||
|
addon:Print("Phase 2: Routed %d specialized items", routeCount)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 3: Consolidate stacks in ALL bags (including specialized)
|
||||||
|
local consolidateCount = ConsolidateStacks(bagIDs)
|
||||||
|
if consolidateCount > 0 then
|
||||||
|
addon:Print("Phase 3: Consolidated %d stacks", consolidateCount)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 4: Sort items WITHIN each specialized bag (soul, quiver, ammo)
|
||||||
|
local specializedMoves = 0
|
||||||
|
for _, bagType in ipairs({"soul", "quiver", "ammo"}) do
|
||||||
|
local specialBags = containers[bagType]
|
||||||
|
for _, bagID in ipairs(specialBags) do
|
||||||
|
-- Sort items within this single specialized bag
|
||||||
|
local items = CollectItems({bagID})
|
||||||
|
if table.getn(items) > 0 then
|
||||||
|
items = SortItems(items)
|
||||||
|
local targetPositions = BuildTargetPositions({bagID}, table.getn(items))
|
||||||
|
local moveCount = ApplySort({bagID}, items, targetPositions)
|
||||||
|
specializedMoves = specializedMoves + moveCount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if specializedMoves > 0 then
|
||||||
|
addon:Print("Phase 4a: Sorted %d items within specialized bags", specializedMoves)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 5: Categorical sort regular bags
|
||||||
|
local regularBagIDs = containers.regular
|
||||||
|
if table.getn(regularBagIDs) > 0 then
|
||||||
|
local items = CollectItems(regularBagIDs)
|
||||||
|
if table.getn(items) > 0 then
|
||||||
|
items = SortItems(items)
|
||||||
|
local targetPositions = BuildTargetPositions(regularBagIDs, table.getn(items))
|
||||||
|
local moveCount = ApplySort(regularBagIDs, items, targetPositions)
|
||||||
|
|
||||||
|
if moveCount > 0 then
|
||||||
|
addon:Print("Phase 4b: Sorted and compressed %d items in regular bags", moveCount)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 6: Validate
|
||||||
|
ValidateSort(bagIDs)
|
||||||
|
|
||||||
|
addon:Print("Sort complete!")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Sort bank
|
|
||||||
function SortEngine:SortBank()
|
function SortEngine:SortBank()
|
||||||
if not addon.Modules.BankScanner:IsBankOpen() then
|
if not addon.Modules.BankScanner:IsBankOpen() then
|
||||||
addon:Print("Bank must be open to sort!")
|
addon:Print("Bank must be open to sort!")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
self:ApplySort(addon.Constants.BANK_BAGS, "type")
|
addon:Print("Starting 6-phase bank sort...")
|
||||||
|
|
||||||
|
local bagIDs = addon.Constants.BANK_BAGS
|
||||||
|
|
||||||
|
-- Phase 1: Detect specialized bags
|
||||||
|
local containers = DetectSpecializedBags(bagIDs)
|
||||||
|
|
||||||
|
-- Phase 2: Route specialized items
|
||||||
|
local routeCount = RouteSpecializedItems(bagIDs, containers)
|
||||||
|
if routeCount > 0 then
|
||||||
|
addon:Print("Routed %d specialized items", routeCount)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 3: Consolidate stacks
|
||||||
|
local consolidateCount = ConsolidateStacks(bagIDs)
|
||||||
|
if consolidateCount > 0 then
|
||||||
|
addon:Print("Consolidated %d stacks", consolidateCount)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 4: Sort items WITHIN each specialized bag
|
||||||
|
local specializedMoves = 0
|
||||||
|
for _, bagType in ipairs({"soul", "quiver", "ammo"}) do
|
||||||
|
local specialBags = containers[bagType]
|
||||||
|
for _, bagID in ipairs(specialBags) do
|
||||||
|
local items = CollectItems({bagID})
|
||||||
|
if table.getn(items) > 0 then
|
||||||
|
items = SortItems(items)
|
||||||
|
local targetPositions = BuildTargetPositions({bagID}, table.getn(items))
|
||||||
|
local moveCount = ApplySort({bagID}, items, targetPositions)
|
||||||
|
specializedMoves = specializedMoves + moveCount
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Phase 5: Sort regular bags
|
||||||
|
local regularBagIDs = containers.regular
|
||||||
|
local regularMoves = 0
|
||||||
|
if table.getn(regularBagIDs) > 0 then
|
||||||
|
local items = CollectItems(regularBagIDs)
|
||||||
|
if table.getn(items) > 0 then
|
||||||
|
items = SortItems(items)
|
||||||
|
local targetPositions = BuildTargetPositions(regularBagIDs, table.getn(items))
|
||||||
|
regularMoves = ApplySort(regularBagIDs, items, targetPositions)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Print("Bank sort complete! Moved %d items", specializedMoves + regularMoves)
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user