Hi!
I have an issue that I have been scratching my head about for a couple of days, and I was wondering if you good folks could assist me.
I'm making a bag addon that allows the user to separate slots from the main bag into separate bags. However, after a custom bag has been deleted, I need to reorder the slots, such that custom bags always use the "last" slots, I refer to this as "defragging".
I can rearrange the slots easily enough, but part of defragging involves moving items around in the inventory, such that to the user, they would appear to remain in place, and this is where my issue lies.
My approach so far has been to treat it as a problem of sorting. I impose a virtual order before I do any defragging. After I have reordered the slots, and thus shuffled the virtual order, I use a selection sort to swap the items around in the inventory.
The below image is before defragging. The numbers on the slots is bag number concatinated with a three digit slot number. Notice that the numbers in the bag called "Title - 1" (i.e. the Main bag) to the bottom is higher than those in the custom bag.
This is my desired result. Notice that the actual slots have shifted, but the items remain in place within the bags.
This is what actually happens. Notice that the slots are indeed in the correct place, but the items are misplaced.
Here are some relevant code sections:
Defragging
Lua Code:
local function DefragBags()
ClearCursor()
-- Reorder createdBags, so that we can get proper Ids
createdBags = ns.Util.Table.Arrange(createdBags)
-- Gather old bag data
local oldBagData = {}
local oldSlots = {}
local cnt = 1
for i = 1, #createdBags do
local gridView = createdBags[i].GridView
for k = 1, #gridView.items do
oldSlots[#oldSlots + 1] = {
phys = gridView.items[k]:GetPhysicalIdentifier(),
--virt = gridView.items[k]:GetVirtualIdentifier()
virt = cnt
}
cnt = cnt + 1
end
if not createdBags[i].IsMasterBag then
oldBagData[i] = {
numColumns = gridView:GetNumColumns(),
numSlots = gridView:ItemCount(),
}
end
end
-- Delete old bags
for i = #createdBags, 1, -1 do
if not createdBags[i].IsMasterBag then
createdBags[i]:DeleteBag()
end
end
-- Spawn new bags
for _,v in pairs(oldBagData) do
local bagConfig = ns.Util.Table.Copy(ns.BagFrame.DefaultConfigTable)
bagConfig.NumColumns = v.numColumns
bagConfig.Slots = v.numSlots
ns.BagFrame:New(bagConfig)
end
-- Create new list with shuffled virtual order
local newSlots = {}
for i = 1, #createdBags do
local gridView = createdBags[i].GridView
for n = 1, #gridView.items do
local nPhys = gridView.items[n]:GetPhysicalIdentifier()
local idx = ns.Util.Table.IndexWhere(oldSlots, function(k,v,...)
return v.phys == nPhys
end)
newSlots[#newSlots + 1] = oldSlots[idx]
end
end
-- This actually calls a selection sort for now, for simplicity. I was worried my cyclesort implementation was wrong
CycleSort.Sort(newSlots, cycleSortFuncs)
end
The utility functions passed to the sort function
Lua Code:
local cycleSortFuncs = {
Compare = function(arr, val1, val2)
if arr[val1].virt < arr[val2].virt then
return -1
elseif arr[val1].virt > arr[val2].virt then
return 1
else
return 0
end
end,
Swap = function(arr, val1, val2)
print("Swap: val1: " .. val1 .. "->" .. arr[val1].phys .. " val2: " .. val2 .. "->" .. arr[val2].phys)
local temp = arr[val1].virt
arr[val1].virt = arr[val2].virt
arr[val2].virt = temp
-- temp = arr[val1].phys
-- arr[val1].phys = arr[val2].phys
-- arr[val2].phys = temp
local bagId1, slotId1 = ns.BagSlot.DecodeSlotIdentifier(arr[val1].phys)
local bagId2, slotId2 = ns.BagSlot.DecodeSlotIdentifier(arr[val2].phys)
local attempts = 0
-- Wait for server...
repeat
local _, _, locked1 = GetContainerItemInfo(bagId1, slotId1)
local _, _, locked2 = GetContainerItemInfo(bagId2, slotId2)
if locked1 or locked2 then
coroutine.yield()
end
attempts = attempts + 1
until not (locked1 or locked2)
print("Swapping: attempts: " .. attempts)
PickupContainerItem(bagId1, slotId1)
PickupContainerItem(bagId2, slotId2)
end
}
The sorting function itself (It says cyclesort, but it is a selection sort. For now anyway
). The coroutine is resumed OnUpdate.
Lua Code:
function CycleSort.Process()
for i = 1, #CycleSort.array - 1 do
local jMin = i
for j = i + 1, #CycleSort.array do
if CycleSort.funcTable.Compare(CycleSort.array, j, jMin) == -1 then
jMin = j
end
end
if jMin ~= i then
CycleSort.funcTable.Swap(CycleSort.array, i, jMin)
coroutine.yield()
end
end
end
The entire thing is available here, if you wish to play around with it:
https://github.com/Malakahh/ObeliskBags/tree/WoWIBug
To recreate the issue:
1) Create a 10 slot bag
2) Create a 16 slot bag
3) Delete the 10 slot bag
4) Defrag
I don't know if it matters, but I have been using a 16slot backpack and 4x 10slot bags for testing.
Hopefully it's not so simple as me doing sorting wrong, but I can't really think of anything else it could be. Please help
Edit:
I seem to have circumvented my issue by taking a different approach. Instead of sorting based on a virtual slot number, I now record which items are supposed to be in which virtual slots. I scan my bags to find an item with matching Id (and quantity, if stackable) and place it in that virtual slot. I then "lock" the slot to make sure the item isn't moved again, and repeat for the remainder of the inventory.
I still don't understand why my original approach doesn't seem to do the trick, but I guess it's moot at this point