From 959e666400e79d8d810544b2aafb4085d44c4cc2 Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sat, 29 Mar 2025 23:19:33 +0100 Subject: [PATCH] Completely rework the stacking to be a little more better --- QuickStackToBag/Lua/Autorun/init.lua | 193 +++++++++++++++++++++++---- 1 file changed, 168 insertions(+), 25 deletions(-) diff --git a/QuickStackToBag/Lua/Autorun/init.lua b/QuickStackToBag/Lua/Autorun/init.lua index 044a00a..8c33fb1 100644 --- a/QuickStackToBag/Lua/Autorun/init.lua +++ b/QuickStackToBag/Lua/Autorun/init.lua @@ -115,6 +115,94 @@ local function moveItemsTo(slot, itemLocation) end end +---@param item Barotrauma.Item +---@param itemTree table +---@return string +local function tryMoveItem(item, itemTree) + local location = itemTree[item.Prefab.Identifier.Value] + if not location then return nil, "No locations for item, not stacking" end + + local moved = false + -- First try to move to existing stacks + for _, itemLocation in ipairs(location) do + if itemLocation.maxFits > 0 then + moved = moved or itemLocation.inventory.TryPutItem(item, itemLocation.slotIndex, false, true, nil) + itemLocation.maxFits = itemLocation.inventory.HowManyCanBePut(item.Prefab, itemLocation.slotIndex) + end + end + + -- If we can not find an existing stack + -- Then move to any of the empty slots + if not moved then + for _, itemLocation in ipairs(itemTree['empty']) do + moved = moved or itemLocation.inventory.TryPutItem(item, itemLocation.slotIndex, false, true, nil) + itemLocation.maxFits = itemLocation.inventory.HowManyCanBePut(item.Prefab, itemLocation.slotIndex) + end + end + + -- If we still can not move the item give up + if not moved then return "Failed to find valid location for item" end + return nil +end + +---@param items Barotrauma.Item[] +---@param itemTree table +---@return string[] +local function tryMoveItems(items, itemTree) + local errs = {} + for _, item in ipairs(items) do + local err = tryMoveItem(item, itemTree) + -- oops, this one failed, continue... + if err then + errs[#errs + 1] = string.format("Failed to move item: %s", item.Prefab.Identifier.Value) + end + end + return errs +end + +---@param item Barotrauma.Item +---@param queue Barotrauma.Item[] +---@return Barotrauma.Item[], string +local function enqueueItem(item, queue) + queue = queue or {} + queue[#queue + 1] = item + return queue +end + +---@param slot Barotrauma.ItemInventory.Slot +---@param queue Barotrauma.Item[] +---@return Barotrauma.Item[], string +local function enqueueSlot(slot, queue) + queue = queue or {} + -- We don't want to shadow queue + local err + -- If the slot is empty there's nothing to iterate + -- And we will naturally return queue as is + for _, item in ipairs(slot.items) do + queue, err = enqueueItem(item, queue) + if err then + print("Error enqueuing item: " .. err) + end + end + return queue +end + +---@param inventory Barotrauma.ItemInventory +---@param queue Barotrauma.Item[] +---@return Barotrauma.Item[], string[] +local function enqueueInventory(inventory, queue) + queue = queue or {} + -- We don't want to shadow queue + local err + for _, slot in ipairs(inventory.slots) do + queue, err = enqueueSlot(slot, queue) + if err then + print("Error enqueuing slot: " .. err) + end + end + return queue +end + local function stackInventoryItems(inventory, itemTree) debugPrint("Starting to stack inventory items...") for slotIndex, slot in ipairs(inventory.slots) do @@ -152,6 +240,43 @@ local function stackInventoryItems(inventory, itemTree) debugPrint("Completed stacking inventory items.") end +local function stackPlayerInventoryItems(inventory, itemTree) + debugPrint("Starting to stack player inventory items...") + for slotIndex, slot in ipairs(inventory.slots) do + debugPrint("Checking slot index: " .. slotIndex) + -- Cannot stack items if there are no items... + if #slot.items > 0 then + ---@type Barotrauma.Item + local item = slot.items[1] + local identifier = item.Prefab.Identifier.Value + debugPrint("Item at slot " .. slotIndex .. " is " .. identifier) + + ---@type ItemLocation[] + local locations = itemTree[identifier] + -- If there are no locations for this item + -- Then there's nowhere to move it + if locations then + for _, location in ipairs(locations) do + moveItemsTo(slot, location) + if #slot.items == 0 then break end + end + + -- If we have processed all the locations and we still have items to move + -- Then put them into the empty slots: + if #slot.items > 0 then + for _, location in ipairs(itemTree['empty']) do + moveItemsTo(slot, location) + if #slot.items == 0 then break end + end + end + end + else + debugPrint("Slot index " .. slotIndex .. " is empty.") + end + end + debugPrint("Completed stacking inventory items.") +end + -- This is a bit fucking sucky..... -- But I really don't know better -- Maybe it will be fine... @@ -186,6 +311,32 @@ local function sortItemtreeBySlots(itemTree) return itemTree end +---@param inventory Barotrauma.ItemInventory +---@return table, string +local function tryBuildItemTree(inventory) + local itemTree = {} + debugPrint("Preparing to stack items into the bag...") + local bagSlot = inventory.slots[8] + if bagSlot then + debugPrint("Bag slot found at index 8 with " .. #bagSlot.items .. " items.") + if #bagSlot.items > 0 then + local item = bagSlot.items[1] + debugPrint("Found item in bag slot: " .. item.Name) + if item and item.OwnInventory then + debugPrint("Item has its own inventory, building item tree for it...") + itemTree = buildItemTree(item.OwnInventory, itemTree) + else + return itemTree, "Bag does not have its own inventory." + end + else + return itemTree, "Bag slot is empty." + end + else + return itemTree, "No bag slot found at index 8." + end + return itemTree, nil +end + -- Function to quickly stack items from inventory to containers local function quickStackItems(character) if not character then @@ -201,35 +352,27 @@ local function quickStackItems(character) return end - local itemTree = {} - itemTree = buildItemTree(inventory, itemTree) + local itemTree, err = tryBuildItemTree(inventory) + if err then + debugPrint("Error building item tree: " .. err) + return + end itemTree = sortItemtreeBySlots(itemTree) - debugPrint("Preparing to stack items into the bag...") - local bagSlot = inventory.slots[8] - if bagSlot then - debugPrint("Bag slot found at index 8 with " .. #bagSlot.items .. " items.") - if #bagSlot.items > 0 then - local item = bagSlot.items[1] - debugPrint("Found item in bag slot: " .. item.Name) - if item and item.OwnInventory then - debugPrint("Item has its own inventory, building item tree for it...") - itemTree = buildItemTree(item.OwnInventory, itemTree) - else - debugPrint("Item does not have its own inventory.") - end - else - debugPrint("Bag slot is empty.") - end - else - debugPrint("No bag slot found at index 8.") + local toMove = enqueueInventory(inventory) + for _, item in ipairs(toMove) do + print("Item: " .. item.Prefab.Identifier.Value) + end + local errors = tryMoveItems(toMove, itemTree) + for _, error in ipairs(errors) do + print("Error stacking item: " .. error) end - stackInventoryItems(item.OwnInventory, itemTree) - local openContainerInventory = getOpenContainer() - if openContainerInventory then - stackInventoryItems(openContainerInventory, itemTree) - end + -- stackPlayerInventoryItems(inventory, itemTree) + -- local openContainerInventory = getOpenContainer() + -- if openContainerInventory then + -- stackInventoryItems(openContainerInventory, itemTree) + -- end --local handItems = {} --for _, slotIndex in ipairs(CONFIG.HAND_SLOTS) do