feat: Restack
This commit is contained in:
+182
-2
@@ -342,10 +342,19 @@ function BagFrame:Update()
|
||||
|
||||
if viewType == "category" then
|
||||
self:DisplayItemsByCategory(bagData, isOtherChar, charName)
|
||||
if getglobal("Guda_BagFrame_SortButton") then getglobal("Guda_BagFrame_SortButton"):Hide() end
|
||||
-- Show sort button with merge icon/tooltip for category view
|
||||
local sortBtn = getglobal("Guda_BagFrame_SortButton")
|
||||
if sortBtn then
|
||||
sortBtn:Show()
|
||||
sortBtn.isCategoryView = true
|
||||
end
|
||||
else
|
||||
self:DisplayItems(bagData, isOtherChar, charName)
|
||||
if getglobal("Guda_BagFrame_SortButton") then getglobal("Guda_BagFrame_SortButton"):Show() end
|
||||
local sortBtn = getglobal("Guda_BagFrame_SortButton")
|
||||
if sortBtn then
|
||||
sortBtn:Show()
|
||||
sortBtn.isCategoryView = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Update money
|
||||
@@ -1304,6 +1313,13 @@ function Guda_BagFrame_Sort()
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if we're in category view - only merge stacks
|
||||
local sortBtn = getglobal("Guda_BagFrame_SortButton")
|
||||
if sortBtn and sortBtn.isCategoryView then
|
||||
Guda_BagFrame_MergeStacks()
|
||||
return
|
||||
end
|
||||
|
||||
local success, message = addon.Modules.SortEngine:ExecuteSort(
|
||||
function() return addon.Modules.SortEngine:SortBagsPass() end,
|
||||
function() return addon.Modules.SortEngine:AnalyzeBags() end,
|
||||
@@ -1316,6 +1332,170 @@ function Guda_BagFrame_Sort()
|
||||
end
|
||||
end
|
||||
|
||||
-- Merge stacks only (for category view) - queue-based approach like BagShui
|
||||
function Guda_BagFrame_MergeStacks()
|
||||
if currentViewChar then
|
||||
addon:Print("Cannot merge stacks for another character!")
|
||||
return
|
||||
end
|
||||
|
||||
local bagIDs = addon.Constants.BAGS
|
||||
local moveQueue = {}
|
||||
|
||||
-- Collect all partial stacks grouped by item
|
||||
local partialStacks = {}
|
||||
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 = GetContainerItemInfo(bagID, slot)
|
||||
local _, _, itemID = string.find(link, "item:(%d+)")
|
||||
itemID = tonumber(itemID)
|
||||
|
||||
if itemID then
|
||||
local _, _, _, _, _, _, itemStackCount = GetItemInfo(itemID)
|
||||
local maxStack = tonumber(itemStackCount) or 1
|
||||
|
||||
-- Only track items that can stack and aren't full
|
||||
if maxStack > 1 and count < maxStack then
|
||||
local groupKey = tostring(itemID)
|
||||
if not partialStacks[groupKey] then
|
||||
partialStacks[groupKey] = {
|
||||
maxStack = maxStack,
|
||||
stacks = {}
|
||||
}
|
||||
end
|
||||
table.insert(partialStacks[groupKey].stacks, {
|
||||
bagID = bagID,
|
||||
slot = slot,
|
||||
count = count
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Build move queue for each item group
|
||||
for _, group in pairs(partialStacks) do
|
||||
if table.getn(group.stacks) > 1 then
|
||||
-- Sort stacks: larger stacks first (targets), smaller stacks last (sources)
|
||||
table.sort(group.stacks, function(a, b)
|
||||
return a.count > b.count
|
||||
end)
|
||||
|
||||
local sourceLoopStart = table.getn(group.stacks)
|
||||
|
||||
-- Process targets from start, sources from end
|
||||
for targetIdx = 1, table.getn(group.stacks) - 1 do
|
||||
local target = group.stacks[targetIdx]
|
||||
|
||||
if target.count < group.maxStack and target.count > 0 then
|
||||
for sourceIdx = sourceLoopStart, targetIdx + 1, -1 do
|
||||
local source = group.stacks[sourceIdx]
|
||||
|
||||
if source.count > 0 and target.count < group.maxStack then
|
||||
-- Queue this move
|
||||
table.insert(moveQueue, {
|
||||
source = source,
|
||||
target = target,
|
||||
maxStack = group.maxStack
|
||||
})
|
||||
|
||||
-- Calculate changes (for queue planning)
|
||||
local oldTargetCount = target.count
|
||||
target.count = math.min(target.count + source.count, group.maxStack)
|
||||
source.count = source.count - (target.count - oldTargetCount)
|
||||
|
||||
-- Move source pointer if depleted
|
||||
if source.count == 0 then
|
||||
sourceLoopStart = sourceLoopStart - 1
|
||||
end
|
||||
|
||||
-- Stop if target is full
|
||||
if target.count >= group.maxStack then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if table.getn(moveQueue) == 0 then
|
||||
addon:Print("No stacks to merge")
|
||||
return
|
||||
end
|
||||
|
||||
-- Process queue with delays
|
||||
local queueIndex = 1
|
||||
local retryCount = 0
|
||||
local totalMoves = table.getn(moveQueue)
|
||||
|
||||
local function ProcessNextMove()
|
||||
if queueIndex > table.getn(moveQueue) then
|
||||
addon:Print("Merged " .. totalMoves .. " stack(s)")
|
||||
BagFrame:Update()
|
||||
return
|
||||
end
|
||||
|
||||
local move = moveQueue[queueIndex]
|
||||
local source = move.source
|
||||
local target = move.target
|
||||
|
||||
-- Check if items are locked
|
||||
local _, _, sourceLocked = GetContainerItemInfo(source.bagID, source.slot)
|
||||
local _, _, targetLocked = GetContainerItemInfo(target.bagID, target.slot)
|
||||
|
||||
if sourceLocked or targetLocked then
|
||||
retryCount = retryCount + 1
|
||||
if retryCount < 10 then
|
||||
-- Retry after delay
|
||||
Guda_ScheduleTimer(0.3, ProcessNextMove)
|
||||
return
|
||||
else
|
||||
-- Give up on this move
|
||||
retryCount = 0
|
||||
queueIndex = queueIndex + 1
|
||||
Guda_ScheduleTimer(0.1, ProcessNextMove)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Perform the move
|
||||
ClearCursor()
|
||||
PickupContainerItem(source.bagID, source.slot)
|
||||
PickupContainerItem(target.bagID, target.slot)
|
||||
ClearCursor()
|
||||
|
||||
-- Move to next
|
||||
retryCount = 0
|
||||
queueIndex = queueIndex + 1
|
||||
Guda_ScheduleTimer(0.15, ProcessNextMove)
|
||||
end
|
||||
|
||||
ProcessNextMove()
|
||||
end
|
||||
|
||||
-- Simple timer helper
|
||||
function Guda_ScheduleTimer(delay, callback)
|
||||
local frame = CreateFrame("Frame")
|
||||
frame.elapsed = 0
|
||||
frame.delay = delay
|
||||
frame.callback = callback
|
||||
frame:SetScript("OnUpdate", function()
|
||||
this.elapsed = this.elapsed + arg1
|
||||
if this.elapsed >= this.delay then
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this.callback()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- Hook bag container buttons to open Guda Bag View
|
||||
local function HookBagContainers()
|
||||
-- Hook the main bag container buttons (bags 1-4)
|
||||
|
||||
+6
-1
@@ -145,7 +145,12 @@
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
GameTooltip:SetOwner(this, "ANCHOR_TOP")
|
||||
GameTooltip:SetText("Sort Bags")
|
||||
if this.isCategoryView then
|
||||
GameTooltip:SetText("Merge Stacks")
|
||||
GameTooltip:AddLine("Combines split stacks of the same item", 0.7, 0.7, 0.7)
|
||||
else
|
||||
GameTooltip:SetText("Sort Bags")
|
||||
end
|
||||
GameTooltip:Show()
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
|
||||
+172
-2
@@ -192,10 +192,19 @@ function BankFrame:Update()
|
||||
|
||||
if viewType == "category" then
|
||||
self:DisplayItemsByCategory(bankData, isOtherChar, charName)
|
||||
if getglobal("Guda_BankFrame_SortButton") then getglobal("Guda_BankFrame_SortButton"):Hide() end
|
||||
-- Show sort button with merge icon/tooltip for category view
|
||||
local sortBtn = getglobal("Guda_BankFrame_SortButton")
|
||||
if sortBtn then
|
||||
sortBtn:Show()
|
||||
sortBtn.isCategoryView = true
|
||||
end
|
||||
else
|
||||
self:DisplayItems(bankData, isOtherChar, charName)
|
||||
if getglobal("Guda_BankFrame_SortButton") then getglobal("Guda_BankFrame_SortButton"):Show() end
|
||||
local sortBtn = getglobal("Guda_BankFrame_SortButton")
|
||||
if sortBtn then
|
||||
sortBtn:Show()
|
||||
sortBtn.isCategoryView = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Update money
|
||||
@@ -939,6 +948,13 @@ function Guda_BankFrame_Sort()
|
||||
return
|
||||
end
|
||||
|
||||
-- Check if we're in category view - only merge stacks
|
||||
local sortBtn = getglobal("Guda_BankFrame_SortButton")
|
||||
if sortBtn and sortBtn.isCategoryView then
|
||||
Guda_BankFrame_MergeStacks()
|
||||
return
|
||||
end
|
||||
|
||||
local success, message = addon.Modules.SortEngine:ExecuteSort(
|
||||
function() return addon.Modules.SortEngine:SortBankPass() end,
|
||||
function() return addon.Modules.SortEngine:AnalyzeBank() end,
|
||||
@@ -951,6 +967,160 @@ function Guda_BankFrame_Sort()
|
||||
end
|
||||
end
|
||||
|
||||
-- Merge stacks only (for category view) - queue-based approach like BagShui
|
||||
function Guda_BankFrame_MergeStacks()
|
||||
if isReadOnlyMode or currentViewChar then
|
||||
addon:Print("Cannot merge stacks in read-only mode!")
|
||||
return
|
||||
end
|
||||
|
||||
if not addon.Modules.BankScanner:IsBankOpen() then
|
||||
addon:Print("Bank must be open to merge stacks!")
|
||||
return
|
||||
end
|
||||
|
||||
local bagIDs = addon.Constants.BANK_BAGS
|
||||
local moveQueue = {}
|
||||
|
||||
-- Collect all partial stacks grouped by item
|
||||
local partialStacks = {}
|
||||
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 = GetContainerItemInfo(bagID, slot)
|
||||
local _, _, itemID = string.find(link, "item:(%d+)")
|
||||
itemID = tonumber(itemID)
|
||||
|
||||
if itemID then
|
||||
local _, _, _, _, _, _, itemStackCount = GetItemInfo(itemID)
|
||||
local maxStack = tonumber(itemStackCount) or 1
|
||||
|
||||
-- Only track items that can stack and aren't full
|
||||
if maxStack > 1 and count < maxStack then
|
||||
local groupKey = tostring(itemID)
|
||||
if not partialStacks[groupKey] then
|
||||
partialStacks[groupKey] = {
|
||||
maxStack = maxStack,
|
||||
stacks = {}
|
||||
}
|
||||
end
|
||||
table.insert(partialStacks[groupKey].stacks, {
|
||||
bagID = bagID,
|
||||
slot = slot,
|
||||
count = count
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Build move queue for each item group
|
||||
for _, group in pairs(partialStacks) do
|
||||
if table.getn(group.stacks) > 1 then
|
||||
-- Sort stacks: larger stacks first (targets), smaller stacks last (sources)
|
||||
table.sort(group.stacks, function(a, b)
|
||||
return a.count > b.count
|
||||
end)
|
||||
|
||||
local sourceLoopStart = table.getn(group.stacks)
|
||||
|
||||
-- Process targets from start, sources from end
|
||||
for targetIdx = 1, table.getn(group.stacks) - 1 do
|
||||
local target = group.stacks[targetIdx]
|
||||
|
||||
if target.count < group.maxStack and target.count > 0 then
|
||||
for sourceIdx = sourceLoopStart, targetIdx + 1, -1 do
|
||||
local source = group.stacks[sourceIdx]
|
||||
|
||||
if source.count > 0 and target.count < group.maxStack then
|
||||
-- Queue this move
|
||||
table.insert(moveQueue, {
|
||||
source = source,
|
||||
target = target,
|
||||
maxStack = group.maxStack
|
||||
})
|
||||
|
||||
-- Calculate changes (for queue planning)
|
||||
local oldTargetCount = target.count
|
||||
target.count = math.min(target.count + source.count, group.maxStack)
|
||||
source.count = source.count - (target.count - oldTargetCount)
|
||||
|
||||
-- Move source pointer if depleted
|
||||
if source.count == 0 then
|
||||
sourceLoopStart = sourceLoopStart - 1
|
||||
end
|
||||
|
||||
-- Stop if target is full
|
||||
if target.count >= group.maxStack then
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if table.getn(moveQueue) == 0 then
|
||||
addon:Print("No stacks to merge")
|
||||
return
|
||||
end
|
||||
|
||||
-- Process queue with delays
|
||||
local queueIndex = 1
|
||||
local retryCount = 0
|
||||
local totalMoves = table.getn(moveQueue)
|
||||
|
||||
local function ProcessNextMove()
|
||||
if queueIndex > table.getn(moveQueue) then
|
||||
addon:Print("Merged " .. totalMoves .. " stack(s)")
|
||||
BankFrame:Update()
|
||||
return
|
||||
end
|
||||
|
||||
local move = moveQueue[queueIndex]
|
||||
local source = move.source
|
||||
local target = move.target
|
||||
|
||||
-- Check if items are locked
|
||||
local _, _, sourceLocked = GetContainerItemInfo(source.bagID, source.slot)
|
||||
local _, _, targetLocked = GetContainerItemInfo(target.bagID, target.slot)
|
||||
|
||||
if sourceLocked or targetLocked then
|
||||
retryCount = retryCount + 1
|
||||
if retryCount < 10 then
|
||||
-- Retry after delay
|
||||
Guda_ScheduleTimer(0.3, ProcessNextMove)
|
||||
return
|
||||
else
|
||||
-- Give up on this move
|
||||
retryCount = 0
|
||||
queueIndex = queueIndex + 1
|
||||
Guda_ScheduleTimer(0.1, ProcessNextMove)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Perform the move
|
||||
ClearCursor()
|
||||
PickupContainerItem(source.bagID, source.slot)
|
||||
PickupContainerItem(target.bagID, target.slot)
|
||||
ClearCursor()
|
||||
|
||||
-- Move to next
|
||||
retryCount = 0
|
||||
queueIndex = queueIndex + 1
|
||||
Guda_ScheduleTimer(0.15, ProcessNextMove)
|
||||
end
|
||||
|
||||
ProcessNextMove()
|
||||
end
|
||||
|
||||
-- Switch to Blizzard bank UI
|
||||
function Guda_BankFrame_SwitchToBlizzardUI()
|
||||
-- Hide custom bank frame
|
||||
|
||||
+6
-1
@@ -98,7 +98,12 @@
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
GameTooltip:SetOwner(this, "ANCHOR_TOP")
|
||||
GameTooltip:SetText("Sort Bank")
|
||||
if this.isCategoryView then
|
||||
GameTooltip:SetText("Merge Stacks")
|
||||
GameTooltip:AddLine("Combines split stacks of the same item", 0.7, 0.7, 0.7)
|
||||
else
|
||||
GameTooltip:SetText("Sort Bank")
|
||||
end
|
||||
GameTooltip:Show()
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
|
||||
Reference in New Issue
Block a user