feat: Restack

This commit is contained in:
Salikh Gurgenidze
2026-01-04 04:02:15 +04:00
parent 61eb0b6844
commit b0c35d83af
4 changed files with 366 additions and 6 deletions
+182 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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>