diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..13ee2b0
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "nuxt.isNuxtApp": false
+}
\ No newline at end of file
diff --git a/Sorting/SortEngine.lua b/Sorting/SortEngine.lua
index 54cc653..28dabc2 100644
--- a/Sorting/SortEngine.lua
+++ b/Sorting/SortEngine.lua
@@ -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
diff --git a/UI/BagFrame.lua b/UI/BagFrame.lua
index c73fcab..b2086b7 100644
--- a/UI/BagFrame.lua
+++ b/UI/BagFrame.lua
@@ -201,13 +201,13 @@ function BagFrame:ResizeFrame(currentRow, currentCol, columns)
-- Calculate required dimensions based on columns
local containerWidth = (columns * (buttonSize + spacing)) + 20
local containerHeight = (totalRows * (buttonSize + spacing)) + 20
- local frameWidth = containerWidth + 30
+ local frameWidth = containerWidth + 20
local frameHeight = containerHeight + 100 -- Title (40) + search (30) + footer (30)
-- Minimum sizes
if containerWidth < 200 then
containerWidth = 200
- frameWidth = 230
+ frameWidth = 220
end
if containerHeight < 150 then
containerHeight = 150
@@ -219,7 +219,7 @@ function BagFrame:ResizeFrame(currentRow, currentCol, columns)
-- Maximum sizes
if containerWidth > 800 then
containerWidth = 800
- frameWidth = 830
+ frameWidth = 820
end
if containerHeight > 600 then
containerHeight = 600
diff --git a/UI/BagFrame.xml b/UI/BagFrame.xml
index 8656b64..190b830 100644
--- a/UI/BagFrame.xml
+++ b/UI/BagFrame.xml
@@ -67,7 +67,7 @@
-
+
@@ -295,7 +295,7 @@
-
+
@@ -373,7 +373,7 @@
-
+