fix: herb bags
This commit is contained in:
+100
-22
@@ -240,6 +240,41 @@ function Utils:IsAmmoQuiverBag(bagID)
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check if a bag is Herb Bag
|
||||
function Utils:IsHerbBag(bagID)
|
||||
-- Skip backpack, bank, and keyring
|
||||
if bagID == 0 or bagID == -1 or bagID == -2 then
|
||||
return false
|
||||
end
|
||||
|
||||
local invSlot = ContainerIDToInventoryID(bagID)
|
||||
if not invSlot then return false end
|
||||
|
||||
local link = GetInventoryItemLink("player", invSlot)
|
||||
if not link then return false end
|
||||
|
||||
-- Prefer specialized type detection
|
||||
local bagType = self:GetSpecializedBagType(bagID)
|
||||
if bagType == "herb" then return true end
|
||||
|
||||
-- Fallback: tooltip scan for "Herb Bag"
|
||||
local tooltip = GetScanTooltip()
|
||||
tooltip:ClearLines()
|
||||
tooltip:SetInventoryItem("player", invSlot)
|
||||
|
||||
for i = 1, tooltip:NumLines() do
|
||||
local line = getglobal("GudaBagScanTooltipTextLeft" .. i)
|
||||
if line then
|
||||
local text = line:GetText()
|
||||
if text and string.find(string.lower(text), "herb bag") then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check if a bag is Soul Bag
|
||||
function Utils:IsSoulBag(bagID)
|
||||
-- Skip backpack, bank, and keyring
|
||||
@@ -289,7 +324,7 @@ function Utils:IsSoulBag(bagID)
|
||||
end
|
||||
|
||||
-- Get specialized bag type (for sorting priority)
|
||||
-- Returns: "soul", "quiver", "ammo", or nil
|
||||
-- Returns: "soul", "herb", "quiver", "ammo", or nil
|
||||
function Utils:GetSpecializedBagType(bagID)
|
||||
-- Skip backpack, bank, and keyring
|
||||
if bagID == 0 or bagID == -1 or bagID == -2 then
|
||||
@@ -322,6 +357,11 @@ function Utils:GetSpecializedBagType(bagID)
|
||||
return "soul"
|
||||
end
|
||||
|
||||
-- Herb Bag
|
||||
if string.find(typeLower, "herb bag") then
|
||||
return "herb"
|
||||
end
|
||||
|
||||
-- Quiver
|
||||
if string.find(typeLower, "quiver") then
|
||||
return "quiver"
|
||||
@@ -341,6 +381,8 @@ function Utils:GetContainerPriority(bagID)
|
||||
local bagType = self:GetSpecializedBagType(bagID)
|
||||
if bagType == "soul" then
|
||||
return 40
|
||||
elseif bagType == "herb" then
|
||||
return 35
|
||||
elseif bagType == "quiver" then
|
||||
return 30
|
||||
elseif bagType == "ammo" then
|
||||
@@ -381,32 +423,37 @@ function Utils:IsAmmo(itemType)
|
||||
end
|
||||
|
||||
-- Get preferred container type for an item
|
||||
-- Returns: "soul", "quiver", "ammo", or nil
|
||||
-- Returns: "soul", "herb", "quiver", "ammo", or nil
|
||||
function Utils:GetItemPreferredContainer(itemLink)
|
||||
if not itemLink then return nil end
|
||||
if not itemLink then return nil end
|
||||
|
||||
-- Check for soul shards first
|
||||
if self:IsSoulShard(itemLink) then
|
||||
return "soul"
|
||||
end
|
||||
-- Check for soul shards first
|
||||
if self:IsSoulShard(itemLink) then
|
||||
return "soul"
|
||||
end
|
||||
|
||||
-- Get item info using utility function
|
||||
local itemID = self:ExtractItemID(itemLink)
|
||||
if not itemID then return nil end
|
||||
-- Get item info using utility function
|
||||
local itemID = self:ExtractItemID(itemLink)
|
||||
if not itemID then return nil end
|
||||
|
||||
local itemName, _, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, itemSubType = self:GetItemInfoSafe(itemID)
|
||||
if not itemType then return nil end
|
||||
local itemName, _, itemRarity, itemLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture = self:GetItemInfoSafe(itemID)
|
||||
if not itemType then return nil end
|
||||
|
||||
-- Only route PROJECTILE category items that are specifically arrows or bullets
|
||||
if itemCategory == "Projectile" then
|
||||
if itemType == "Arrow" then
|
||||
return "quiver"
|
||||
elseif itemType == "Bullet" then
|
||||
return "ammo"
|
||||
end
|
||||
end
|
||||
-- Only route PROJECTILE category items that are specifically arrows or bullets
|
||||
if itemCategory == "Projectile" then
|
||||
if itemType == "Arrow" then
|
||||
return "quiver"
|
||||
elseif itemType == "Bullet" then
|
||||
return "ammo"
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
-- Route herbs to herb bags (robust: category/subtype OR texture pattern fallback)
|
||||
if self:IsHerbItem(itemLink) then
|
||||
return "herb"
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Check if an item is equipment (armor, weapon, or other equippable)
|
||||
@@ -432,5 +479,36 @@ function Utils:IsEquipment(itemLink)
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
return false
|
||||
end
|
||||
|
||||
-- Determine if an item is a herb (for routing to herb bags)
|
||||
-- NEW rule (per request): require BOTH
|
||||
-- 1) itemCategory == "Trade Goods"
|
||||
-- 2) texture contains "INV_Misc_Herb" (case-insensitive; prefix tolerated)
|
||||
function Utils:IsHerbItem(itemLink)
|
||||
if not itemLink then return false end
|
||||
|
||||
local itemID = self:ExtractItemID(itemLink)
|
||||
if not itemID then return false end
|
||||
|
||||
local name, _, quality, iLevel, itemCategory, itemType, itemStackCount, itemSubType, itemTexture = self:GetItemInfoSafe(itemID)
|
||||
|
||||
if itemCategory ~= "Trade Goods" then
|
||||
return false
|
||||
end
|
||||
|
||||
if not itemTexture then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Normalize and check texture name
|
||||
local tex = string.lower(itemTexture)
|
||||
tex = string.gsub(tex, "^interface\\\\icons\\\\", "")
|
||||
|
||||
if string.find(tex, "inv_misc_herb") or string.find(tex, "misc_herb") then
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
@@ -27,6 +27,8 @@ function BagScanner:ScanBag(bagID)
|
||||
local bagType = "regular"
|
||||
if addon.Modules.Utils:IsSoulBag(bagID) then
|
||||
bagType = "soul"
|
||||
elseif addon.Modules.Utils:IsHerbBag(bagID) then
|
||||
bagType = "herb"
|
||||
elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then
|
||||
bagType = "ammo"
|
||||
end
|
||||
|
||||
@@ -30,6 +30,8 @@ function BankScanner:ScanBankBag(bagID)
|
||||
local bagType = "regular"
|
||||
if addon.Modules.Utils:IsSoulBag(bagID) then
|
||||
bagType = "soul"
|
||||
elseif addon.Modules.Utils:IsHerbBag(bagID) then
|
||||
bagType = "herb"
|
||||
elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then
|
||||
bagType = "ammo"
|
||||
end
|
||||
|
||||
+225
-85
@@ -172,25 +172,28 @@ end
|
||||
--===========================================================================
|
||||
|
||||
local function DetectSpecializedBags(bagIDs)
|
||||
local containers = {
|
||||
soul = {},
|
||||
quiver = {},
|
||||
ammo = {},
|
||||
regular = {}
|
||||
}
|
||||
local containers = {
|
||||
soul = {},
|
||||
herb = {},
|
||||
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
|
||||
if bagType == "soul" then
|
||||
table.insert(containers.soul, bagID)
|
||||
elseif bagType == "herb" then
|
||||
table.insert(containers.herb, 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
|
||||
@@ -749,13 +752,13 @@ function SortEngine:AnalyzeContainer(bagIDs, containerType)
|
||||
local totalOutOfPlace = 0
|
||||
local totalItems = 0
|
||||
|
||||
-- Analyze specialized bags separately
|
||||
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
|
||||
local sortedItems = SortItems(items)
|
||||
-- Analyze specialized bags separately
|
||||
for _, bagType in ipairs({"soul", "herb", "quiver", "ammo"}) do
|
||||
local specialBags = containers[bagType]
|
||||
for _, bagID in ipairs(specialBags) do
|
||||
local items = CollectItems({bagID})
|
||||
if table.getn(items) > 0 then
|
||||
local sortedItems = SortItems(items)
|
||||
local targetPositions = BuildTargetPositions({bagID}, table.getn(items))
|
||||
|
||||
for i, item in ipairs(sortedItems) do
|
||||
@@ -827,23 +830,27 @@ function SortEngine:AnalyzeContainer(bagIDs, containerType)
|
||||
end
|
||||
end
|
||||
|
||||
if totalOutOfPlace == 0 then
|
||||
return {passes = 0, itemsOutOfPlace = 0, totalItems = totalItems, alreadySorted = true}
|
||||
end
|
||||
if totalOutOfPlace == 0 then
|
||||
return {passes = 0, itemsOutOfPlace = 0, totalItems = totalItems, alreadySorted = true}
|
||||
end
|
||||
|
||||
-- Estimate passes (same logic as before)
|
||||
local displacementRatio = totalOutOfPlace / math.max(1, totalItems)
|
||||
local estimatedPasses
|
||||
-- Estimate passes (aligned to single-pass executor; cap at 6)
|
||||
local displacementRatio = totalOutOfPlace / math.max(1, totalItems)
|
||||
local estimatedPasses
|
||||
|
||||
if displacementRatio < 0.1 then
|
||||
estimatedPasses = 1
|
||||
elseif displacementRatio < 0.3 then
|
||||
estimatedPasses = 2
|
||||
elseif displacementRatio < 0.5 then
|
||||
estimatedPasses = 3
|
||||
else
|
||||
estimatedPasses = 4
|
||||
end
|
||||
if displacementRatio < 0.10 then
|
||||
estimatedPasses = 1
|
||||
elseif displacementRatio < 0.30 then
|
||||
estimatedPasses = 2
|
||||
elseif displacementRatio < 0.50 then
|
||||
estimatedPasses = 3
|
||||
elseif displacementRatio < 0.70 then
|
||||
estimatedPasses = 4
|
||||
elseif displacementRatio < 0.85 then
|
||||
estimatedPasses = 5
|
||||
else
|
||||
estimatedPasses = 6
|
||||
end
|
||||
|
||||
return {
|
||||
passes = estimatedPasses,
|
||||
@@ -853,6 +860,61 @@ function SortEngine:AnalyzeContainer(bagIDs, containerType)
|
||||
}
|
||||
end
|
||||
|
||||
-- Execute exactly ONE full sorting pass over bags (used by safety wrapper)
|
||||
function SortEngine:SortBagsPass()
|
||||
local bagIDs = addon.Constants.BAGS
|
||||
|
||||
-- Phase 1: Detect specialized bags
|
||||
local containers = DetectSpecializedBags(bagIDs)
|
||||
|
||||
-- Phase 2: Route specialized items to their bags
|
||||
local routeCount = RouteSpecializedItems(bagIDs, containers)
|
||||
|
||||
-- Phase 3: Consolidate stacks in ALL bags (including specialized)
|
||||
local consolidateCount = ConsolidateStacks(bagIDs)
|
||||
|
||||
-- Phase 4: Sort items WITHIN each specialized bag (soul, herb, quiver, ammo)
|
||||
local specializedMoves = 0
|
||||
for _, bagType in ipairs({"soul", "herb", "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: Two-phase sort for regular bags in a single pass
|
||||
local regularMoves = 0
|
||||
local regularBagIDs = containers.regular
|
||||
if table.getn(regularBagIDs) > 0 then
|
||||
-- Re-collect current state from regular bags
|
||||
local allItems = CollectItems(regularBagIDs)
|
||||
local nonGreys, greys = SplitGreyItems(allItems)
|
||||
|
||||
-- 1) Non-greys: standard sort to the front positions only
|
||||
if table.getn(nonGreys) > 0 then
|
||||
local sortedNonGreys = SortItems(nonGreys)
|
||||
local frontPositions = BuildTargetPositions(regularBagIDs, table.getn(sortedNonGreys))
|
||||
regularMoves = regularMoves + (ApplySort(regularBagIDs, sortedNonGreys, frontPositions) or 0)
|
||||
end
|
||||
|
||||
-- 2) Greys: end->start across bags
|
||||
local afterItems = CollectItems(regularBagIDs)
|
||||
local _, greysNow = SplitGreyItems(afterItems)
|
||||
if table.getn(greysNow) > 0 then
|
||||
local tailPositions = BuildGreyTailPositions(regularBagIDs, table.getn(greysNow))
|
||||
regularMoves = regularMoves + (ApplySort(regularBagIDs, greysNow, tailPositions) or 0)
|
||||
end
|
||||
end
|
||||
|
||||
return routeCount + consolidateCount + specializedMoves + regularMoves
|
||||
end
|
||||
|
||||
function SortEngine:SortBags()
|
||||
local bagIDs = addon.Constants.BAGS
|
||||
|
||||
@@ -865,14 +927,14 @@ function SortEngine:SortBags()
|
||||
-- Phase 3: Consolidate stacks in ALL bags (including specialized)
|
||||
local consolidateCount = ConsolidateStacks(bagIDs)
|
||||
|
||||
-- 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
|
||||
-- Phase 4: Sort items WITHIN each specialized bag (soul, herb, quiver, ammo)
|
||||
local specializedMoves = 0
|
||||
for _, bagType in ipairs({"soul", "herb", "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)
|
||||
@@ -881,9 +943,7 @@ function SortEngine:SortBags()
|
||||
end
|
||||
end
|
||||
|
||||
-- Phase 5: Two-phase sort for regular bags:
|
||||
-- 1) Sort non-grey items normally across all regular bags (front-compaction)
|
||||
-- 2) Pack grey items (quality 0) at the end tail (last bag backwards), spilling into previous bags.
|
||||
-- Phase 5: Two-phase sort for regular bags (multi-pass legacy; kept for direct calls)
|
||||
local regularMoves = 0
|
||||
local regularBagIDs = containers.regular
|
||||
if table.getn(regularBagIDs) > 0 then
|
||||
@@ -920,6 +980,63 @@ function SortEngine:SortBags()
|
||||
return routeCount + consolidateCount + specializedMoves + regularMoves
|
||||
end
|
||||
|
||||
-- Execute exactly ONE full sorting pass over bank (used by safety wrapper)
|
||||
function SortEngine:SortBankPass()
|
||||
if not addon.Modules.BankScanner:IsBankOpen() then
|
||||
addon:Print("Bank must be open to sort!")
|
||||
return 0
|
||||
end
|
||||
|
||||
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)
|
||||
|
||||
-- Phase 3: Consolidate stacks
|
||||
local consolidateCount = ConsolidateStacks(bagIDs)
|
||||
|
||||
-- Phase 4: Sort items WITHIN each specialized bag (single pass)
|
||||
local specializedMoves = 0
|
||||
for _, bagType in ipairs({"soul", "herb", "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 moved = ApplySort({bagID}, items, targetPositions)
|
||||
specializedMoves = specializedMoves + moved
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Phase 5: Regular bank bags — single pass
|
||||
local regularBagIDs = containers.regular
|
||||
local regularMoves = 0
|
||||
if table.getn(regularBagIDs) > 0 then
|
||||
local allItems = CollectItems(regularBagIDs)
|
||||
local nonGreys, greys = SplitGreyItems(allItems)
|
||||
|
||||
if table.getn(nonGreys) > 0 then
|
||||
local sortedNonGreys = SortItems(nonGreys)
|
||||
local frontPositions = BuildTargetPositions(regularBagIDs, table.getn(sortedNonGreys))
|
||||
regularMoves = regularMoves + (ApplySort(regularBagIDs, sortedNonGreys, frontPositions) or 0)
|
||||
end
|
||||
|
||||
local afterItems = CollectItems(regularBagIDs)
|
||||
local _, greysNow = SplitGreyItems(afterItems)
|
||||
if table.getn(greysNow) > 0 then
|
||||
local tailPositions = BuildGreyTailPositions(regularBagIDs, table.getn(greysNow))
|
||||
regularMoves = regularMoves + (ApplySort(regularBagIDs, greysNow, tailPositions) or 0)
|
||||
end
|
||||
end
|
||||
|
||||
return routeCount + consolidateCount + specializedMoves + regularMoves
|
||||
end
|
||||
|
||||
function SortEngine:SortBank()
|
||||
if not addon.Modules.BankScanner:IsBankOpen() then
|
||||
addon:Print("Bank must be open to sort!")
|
||||
@@ -937,23 +1054,23 @@ function SortEngine:SortBank()
|
||||
-- Phase 3: Consolidate stacks
|
||||
local consolidateCount = ConsolidateStacks(bagIDs)
|
||||
|
||||
-- Phase 4: Sort items WITHIN each specialized bag (multi-pass to avoid mid-bag holes)
|
||||
local specializedMoves = 0
|
||||
for _, bagType in ipairs({"soul", "quiver", "ammo"}) do
|
||||
local specialBags = containers[bagType]
|
||||
for _, bagID in ipairs(specialBags) do
|
||||
local maxPasses = 4
|
||||
for pass = 1, maxPasses do
|
||||
local items = CollectItems({bagID})
|
||||
if table.getn(items) == 0 then break end
|
||||
items = SortItems(items)
|
||||
local targetPositions = BuildTargetPositions({bagID}, table.getn(items))
|
||||
local moved = ApplySort({bagID}, items, targetPositions)
|
||||
specializedMoves = specializedMoves + moved
|
||||
if moved == 0 then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Phase 4: Sort items WITHIN each specialized bag (multi-pass to avoid mid-bag holes)
|
||||
local specializedMoves = 0
|
||||
for _, bagType in ipairs({"soul", "herb", "quiver", "ammo"}) do
|
||||
local specialBags = containers[bagType]
|
||||
for _, bagID in ipairs(specialBags) do
|
||||
local maxPasses = 4
|
||||
for pass = 1, maxPasses do
|
||||
local items = CollectItems({bagID})
|
||||
if table.getn(items) == 0 then break end
|
||||
items = SortItems(items)
|
||||
local targetPositions = BuildTargetPositions({bagID}, table.getn(items))
|
||||
local moved = ApplySort({bagID}, items, targetPositions)
|
||||
specializedMoves = specializedMoves + moved
|
||||
if moved == 0 then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Phase 5: Regular bank bags — same two-phase approach (non-greys first, greys to tail)
|
||||
local regularBagIDs = containers.regular
|
||||
@@ -1005,26 +1122,27 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
addon:Print("Sorting %s... (%d/%d items need sorting, estimated %d passes)",
|
||||
sortType, analysis.itemsOutOfPlace, analysis.totalItems, analysis.passes)
|
||||
|
||||
local passCount = 0
|
||||
local maxPasses = math.max(analysis.passes, 1) -- Use estimated passes, minimum 1
|
||||
local safetyLimit = math.max(maxPasses * 2, 6) -- Reasonable upper bound
|
||||
local totalMoves = 0
|
||||
local passCount = 0
|
||||
local maxPasses = math.max(analysis.passes, 1) -- Use estimated passes, minimum 1
|
||||
local safetyLimit = math.max(maxPasses * 2, 6) -- Reasonable upper bound
|
||||
local totalMoves = 0
|
||||
local noProgressPasses = 0
|
||||
|
||||
addon:Print("Starting %s sort (estimated: %d passes, safety limit: %d)", sortType, maxPasses, safetyLimit)
|
||||
|
||||
local function DoSortPass()
|
||||
passCount = passCount + 1
|
||||
|
||||
-- Perform one sort pass
|
||||
local moveCount = sortFunction()
|
||||
totalMoves = totalMoves + moveCount
|
||||
-- Perform one sort pass
|
||||
local moveCount = sortFunction()
|
||||
totalMoves = totalMoves + moveCount
|
||||
|
||||
-- Check if sorting is complete by re-analyzing
|
||||
local currentAnalysis = analyzeFunction()
|
||||
|
||||
if currentAnalysis.alreadySorted then
|
||||
-- Sorting is complete!
|
||||
addon:Print("%s sort complete! (%d passes, %d total moves)", sortType, passCount, totalMoves)
|
||||
if currentAnalysis.alreadySorted then
|
||||
-- Sorting is complete!
|
||||
addon:Print("%s sort complete! (%d passes, %d total moves)", sortType, passCount, totalMoves)
|
||||
|
||||
-- Final update
|
||||
local frame = CreateFrame("Frame")
|
||||
@@ -1035,10 +1153,10 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
updateFrame()
|
||||
end
|
||||
end)
|
||||
elseif passCount >= safetyLimit then
|
||||
-- Hit safety limit but not fully sorted
|
||||
addon:Print("%s sort stopped at safety limit! (%d/%d items still need sorting after %d passes)",
|
||||
sortType, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems, passCount)
|
||||
elseif passCount >= safetyLimit then
|
||||
-- Hit safety limit but not fully sorted
|
||||
addon:Print("%s sort stopped at safety limit! (%d/%d items still need sorting after %d passes)",
|
||||
sortType, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems, passCount)
|
||||
|
||||
-- Final update
|
||||
local frame = CreateFrame("Frame")
|
||||
@@ -1049,11 +1167,33 @@ function SortEngine:ExecuteSort(sortFunction, analyzeFunction, updateFrame, sort
|
||||
updateFrame()
|
||||
end
|
||||
end)
|
||||
else
|
||||
-- More sorting needed
|
||||
local remainingRatio = currentAnalysis.itemsOutOfPlace / math.max(1, currentAnalysis.totalItems)
|
||||
addon:Print("%s Pass %d: %d moves, %d/%d items remaining (%.1f%%)",
|
||||
sortType, passCount, moveCount, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems, remainingRatio * 100)
|
||||
else
|
||||
-- No progress guard: stop if we make no moves repeatedly
|
||||
if moveCount == 0 then
|
||||
noProgressPasses = noProgressPasses + 1
|
||||
else
|
||||
noProgressPasses = 0
|
||||
end
|
||||
|
||||
if noProgressPasses >= 3 then
|
||||
addon:Print("%s sort stopped due to no progress after %d passes (items remaining: %d/%d)",
|
||||
sortType, passCount, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems)
|
||||
-- Final update
|
||||
local frame = CreateFrame("Frame")
|
||||
local startTime = GetTime()
|
||||
frame:SetScript("OnUpdate", function()
|
||||
if GetTime() - startTime >= 0.7 then
|
||||
frame:SetScript("OnUpdate", nil)
|
||||
updateFrame()
|
||||
end
|
||||
end)
|
||||
return
|
||||
end
|
||||
|
||||
-- More sorting needed
|
||||
local remainingRatio = currentAnalysis.itemsOutOfPlace / math.max(1, currentAnalysis.totalItems)
|
||||
addon:Print("%s Pass %d: %d moves, %d/%d items remaining (%.1f%%)",
|
||||
sortType, passCount, moveCount, currentAnalysis.itemsOutOfPlace, currentAnalysis.totalItems, remainingRatio * 100)
|
||||
|
||||
-- PROGRESSIVE DELAY: Calculate delay based on remaining complexity
|
||||
local baseDelay = 0.7
|
||||
|
||||
+57
-45
@@ -236,10 +236,11 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName)
|
||||
local perRow = addon.Modules.DB:GetSetting("bagColumns") or 10
|
||||
local itemContainer = getglobal("Guda_BagFrame_ItemContainer")
|
||||
|
||||
-- Separate bags into regular, soul, and ammo/quiver types
|
||||
local regularBags = {}
|
||||
local soulBags = {}
|
||||
local ammoQuiverBags = {}
|
||||
-- Separate bags into regular, soul, herb, and ammo/quiver types
|
||||
local regularBags = {}
|
||||
local soulBags = {}
|
||||
local herbBags = {}
|
||||
local ammoQuiverBags = {}
|
||||
|
||||
for _, bagID in ipairs(addon.Constants.BAGS) do
|
||||
-- Skip hidden bags
|
||||
@@ -251,44 +252,55 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName)
|
||||
bagType = bag and bag.bagType or "regular"
|
||||
else
|
||||
-- For current character, detect bag type in real-time
|
||||
if addon.Modules.Utils:IsSoulBag(bagID) then
|
||||
bagType = "soul"
|
||||
elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then
|
||||
bagType = "ammo"
|
||||
else
|
||||
bagType = "regular"
|
||||
end
|
||||
end
|
||||
if addon.Modules.Utils:IsSoulBag(bagID) then
|
||||
bagType = "soul"
|
||||
elseif addon.Modules.Utils:IsHerbBag(bagID) then
|
||||
bagType = "herb"
|
||||
elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then
|
||||
bagType = "ammo"
|
||||
else
|
||||
bagType = "regular"
|
||||
end
|
||||
end
|
||||
|
||||
if bagType == "soul" then
|
||||
table.insert(soulBags, bagID)
|
||||
elseif bagType == "ammo" then
|
||||
table.insert(ammoQuiverBags, bagID)
|
||||
else
|
||||
table.insert(regularBags, bagID)
|
||||
end
|
||||
end
|
||||
end
|
||||
if bagType == "soul" then
|
||||
table.insert(soulBags, bagID)
|
||||
elseif bagType == "herb" then
|
||||
table.insert(herbBags, bagID)
|
||||
elseif bagType == "ammo" then
|
||||
table.insert(ammoQuiverBags, bagID)
|
||||
else
|
||||
table.insert(regularBags, bagID)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Build display order: regular bags -> soul bags -> ammo/quiver bags -> keyring
|
||||
local bagsToShow = {}
|
||||
for _, bagID in ipairs(regularBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = false})
|
||||
end
|
||||
-- Build display order: regular bags -> soul bags -> herb bags -> ammo/quiver bags -> keyring
|
||||
local bagsToShow = {}
|
||||
for _, bagID in ipairs(regularBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = false})
|
||||
end
|
||||
|
||||
-- Add soul bags with spacing marker
|
||||
if table.getn(soulBags) > 0 then
|
||||
for i, bagID in ipairs(soulBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)})
|
||||
end
|
||||
end
|
||||
if table.getn(soulBags) > 0 then
|
||||
for i, bagID in ipairs(soulBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)})
|
||||
end
|
||||
end
|
||||
|
||||
-- Add ammo/quiver bags with spacing marker
|
||||
if table.getn(ammoQuiverBags) > 0 then
|
||||
for i, bagID in ipairs(ammoQuiverBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)})
|
||||
end
|
||||
end
|
||||
-- Add herb bags with spacing marker
|
||||
if table.getn(herbBags) > 0 then
|
||||
for i, bagID in ipairs(herbBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)})
|
||||
end
|
||||
end
|
||||
|
||||
-- Add ammo/quiver bags with spacing marker
|
||||
if table.getn(ammoQuiverBags) > 0 then
|
||||
for i, bagID in ipairs(ammoQuiverBags) do
|
||||
table.insert(bagsToShow, {bagID = bagID, needsSpacing = (i == 1)})
|
||||
end
|
||||
end
|
||||
|
||||
-- Add keyring at the end if toggled on and not hidden
|
||||
if showKeyring and not hiddenBags[-2] then
|
||||
@@ -299,8 +311,8 @@ function BagFrame:DisplayItems(bagData, isOtherChar, charName)
|
||||
local bagID = bagInfo.bagID
|
||||
local bag = bagData[bagID]
|
||||
|
||||
-- Add spacing before soul, ammo/quiver, or keyring sections
|
||||
if bagInfo.needsSpacing then
|
||||
-- Add spacing before soul, herb, ammo/quiver, or keyring sections
|
||||
if bagInfo.needsSpacing then
|
||||
if col > 0 then
|
||||
-- Move to next row if not at start of row
|
||||
col = 0
|
||||
@@ -1167,12 +1179,12 @@ function Guda_BagFrame_Sort()
|
||||
return
|
||||
end
|
||||
|
||||
local success, message = addon.Modules.SortEngine:ExecuteSort(
|
||||
function() return addon.Modules.SortEngine:SortBags() end,
|
||||
function() return addon.Modules.SortEngine:AnalyzeBags() end,
|
||||
function() BagFrame:Update() end,
|
||||
"bags"
|
||||
)
|
||||
local success, message = addon.Modules.SortEngine:ExecuteSort(
|
||||
function() return addon.Modules.SortEngine:SortBagsPass() end,
|
||||
function() return addon.Modules.SortEngine:AnalyzeBags() end,
|
||||
function() BagFrame:Update() end,
|
||||
"bags"
|
||||
)
|
||||
|
||||
if not success and message == "already sorted" then
|
||||
addon:Print("Bags are already sorted!")
|
||||
|
||||
+85
-32
@@ -196,28 +196,90 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName)
|
||||
local perRow = addon.Modules.DB:GetSetting("bankColumns") or 10
|
||||
local itemContainer = getglobal("Guda_BankFrame_ItemContainer")
|
||||
|
||||
for _, bagID in ipairs(addon.Constants.BANK_BAGS) do
|
||||
-- Skip bags that are hidden
|
||||
if not hiddenBankBags[bagID] then
|
||||
local bag = bankData[bagID]
|
||||
-- Separate bank bags into regular, soul, herb, and ammo/quiver types
|
||||
local regularBags = {}
|
||||
local soulBags = {}
|
||||
local herbBags = {}
|
||||
local ammoQuiverBags = {}
|
||||
|
||||
-- Get slot count for this bag
|
||||
local numSlots
|
||||
if isOtherChar and bag and bag.numSlots then
|
||||
-- Use stored slot count for other characters
|
||||
numSlots = bag.numSlots
|
||||
for _, bagID in ipairs(addon.Constants.BANK_BAGS) do
|
||||
if not hiddenBankBags[bagID] then
|
||||
local bagType
|
||||
if isOtherChar then
|
||||
-- Use saved bag type for other characters
|
||||
local bagSaved = bankData and bankData[bagID]
|
||||
bagType = bagSaved and bagSaved.bagType or "regular"
|
||||
else
|
||||
-- Use current character's bag slot count
|
||||
numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
|
||||
-- Live detection for current character when bank is open
|
||||
if addon.Modules.Utils:IsSoulBag(bagID) then
|
||||
bagType = "soul"
|
||||
elseif addon.Modules.Utils:IsHerbBag(bagID) then
|
||||
bagType = "herb"
|
||||
elseif addon.Modules.Utils:IsAmmoQuiverBag(bagID) then
|
||||
bagType = "ammo"
|
||||
else
|
||||
bagType = "regular"
|
||||
end
|
||||
end
|
||||
|
||||
-- Only show bags that have slots
|
||||
if numSlots and numSlots > 0 then
|
||||
-- Iterate through ALL slots (1 to numSlots) to show empty slots too
|
||||
if bagType == "soul" then
|
||||
table.insert(soulBags, bagID)
|
||||
elseif bagType == "herb" then
|
||||
table.insert(herbBags, bagID)
|
||||
elseif bagType == "ammo" then
|
||||
table.insert(ammoQuiverBags, bagID)
|
||||
else
|
||||
table.insert(regularBags, bagID)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Build display order: regular -> soul -> herb -> ammo/quiver
|
||||
local bagsToShow = {}
|
||||
for _, bagID in ipairs(regularBags) do
|
||||
table.insert(bagsToShow, { bagID = bagID, needsSpacing = false })
|
||||
end
|
||||
if table.getn(soulBags) > 0 then
|
||||
for i, bagID in ipairs(soulBags) do
|
||||
table.insert(bagsToShow, { bagID = bagID, needsSpacing = (i == 1) })
|
||||
end
|
||||
end
|
||||
if table.getn(herbBags) > 0 then
|
||||
for i, bagID in ipairs(herbBags) do
|
||||
table.insert(bagsToShow, { bagID = bagID, needsSpacing = (i == 1) })
|
||||
end
|
||||
end
|
||||
if table.getn(ammoQuiverBags) > 0 then
|
||||
for i, bagID in ipairs(ammoQuiverBags) do
|
||||
table.insert(bagsToShow, { bagID = bagID, needsSpacing = (i == 1) })
|
||||
end
|
||||
end
|
||||
|
||||
for _, bagInfo in ipairs(bagsToShow) do
|
||||
local bagID = bagInfo.bagID
|
||||
local bag = bankData and bankData[bagID]
|
||||
|
||||
-- Add spacing before first of each specialized section
|
||||
if bagInfo.needsSpacing then
|
||||
if col > 0 then
|
||||
col = 0
|
||||
row = row + 1
|
||||
end
|
||||
row = row + 0.5
|
||||
end
|
||||
|
||||
-- Get slot count for this bag
|
||||
local numSlots
|
||||
if isOtherChar and bag and bag.numSlots then
|
||||
numSlots = bag.numSlots
|
||||
else
|
||||
numSlots = addon.Modules.Utils:GetBagSlotCount(bagID)
|
||||
end
|
||||
|
||||
if numSlots and numSlots > 0 then
|
||||
for slot = 1, numSlots do
|
||||
local itemData = bag and bag.slots and bag.slots[slot] or nil
|
||||
|
||||
-- Check if item matches search filter
|
||||
local matchesFilter = self:PassesSearchFilter(itemData)
|
||||
|
||||
-- Ensure a per-bag parent frame exists and carries the bag ID
|
||||
@@ -232,33 +294,24 @@ function BankFrame:DisplayItems(bankData, isOtherChar, charName)
|
||||
bankBagParent = bankBagParents[bagID]
|
||||
|
||||
local button = Guda_GetItemButton(bankBagParent)
|
||||
if button.isBagSlot then break end
|
||||
|
||||
-- Ensure this is NOT a bag slot button
|
||||
if button.isBagSlot then
|
||||
break
|
||||
end
|
||||
button.inUse = true
|
||||
|
||||
button.inUse = true -- Mark this button as actively in use
|
||||
|
||||
-- Position button
|
||||
local xPos = x + (col * (buttonSize + spacing))
|
||||
local yPos = y - (row * (buttonSize + spacing))
|
||||
|
||||
button:ClearAllPoints()
|
||||
button:SetPoint("TOPLEFT", itemContainer, "TOPLEFT", xPos, yPos)
|
||||
|
||||
-- Set item data with filter match info
|
||||
-- Pass isReadOnlyMode to disable interaction for saved banks
|
||||
Guda_ItemButton_SetItem(button, bagID, slot, itemData, true, isOtherChar and charName or nil, matchesFilter, isReadOnlyMode)
|
||||
|
||||
-- Advance position
|
||||
col = col + 1
|
||||
if col >= perRow then
|
||||
col = 0
|
||||
row = row + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -608,12 +661,12 @@ function Guda_BankFrame_Sort()
|
||||
return
|
||||
end
|
||||
|
||||
local success, message = addon.Modules.SortEngine:ExecuteSort(
|
||||
function() return addon.Modules.SortEngine:SortBank() end,
|
||||
function() return addon.Modules.SortEngine:AnalyzeBank() end,
|
||||
function() BankFrame:Update() end,
|
||||
"bank"
|
||||
)
|
||||
local success, message = addon.Modules.SortEngine:ExecuteSort(
|
||||
function() return addon.Modules.SortEngine:SortBankPass() end,
|
||||
function() return addon.Modules.SortEngine:AnalyzeBank() end,
|
||||
function() BankFrame:Update() end,
|
||||
"bank"
|
||||
)
|
||||
|
||||
if not success and message == "already sorted" then
|
||||
addon:Print("Bank is already sorted!")
|
||||
|
||||
@@ -876,6 +876,31 @@ function Guda_ItemButton_OnEnter(self)
|
||||
GameTooltip:SetBagItem(self.bagID, self.slotID)
|
||||
end
|
||||
GameTooltip:Show()
|
||||
|
||||
-- Debug: print hovered item's texture path to chat
|
||||
-- Uses GetItemInfo on the hovered item's ID
|
||||
--if self.hasItem then
|
||||
-- local link = nil
|
||||
-- if self.itemData and self.itemData.link then
|
||||
-- link = self.itemData.link
|
||||
-- else
|
||||
-- -- Fallback to live bag query
|
||||
-- link = GetContainerItemLink(self.bagID, self.slotID)
|
||||
-- end
|
||||
-- if link and addon and addon.Modules and addon.Modules.Utils and addon.Modules.Utils.ExtractItemID then
|
||||
-- local itemID = addon.Modules.Utils:ExtractItemID(link)
|
||||
-- if itemID and addon.Modules.Utils.GetItemInfoSafe then
|
||||
-- local name, itemLink, itemRarity, itemLevel, itemCategory, itemType, itemStackCount,
|
||||
-- itemSubType, itemTexture, itemEquipLoc, itemSellPrice = addon.Modules.Utils:GetItemInfoSafe(itemID)
|
||||
-- if addon and addon.Print then
|
||||
-- addon:Print("itemTexture: %s", tostring(itemTexture))
|
||||
-- addon:Print("itemCategory: %s", tostring(itemCategory))
|
||||
-- addon:Print("itemType: %s", tostring(itemType))
|
||||
-- addon:Print("itemType: %s", tostring(itemType))
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
--end
|
||||
-- Handle merchant sell cursor (same approach as BagShui)
|
||||
if MerchantFrame:IsShown() and not self.isBank and not self.otherChar and self.hasItem then
|
||||
ShowContainerSellCursor(self.bagID, self.slotID)
|
||||
|
||||
Reference in New Issue
Block a user