-- luacheck: globals Character MyModGlobal Timer local cursormacroer = require("Cyka.cursormacroer") local dump = require("Cyka.dump") ---@class InventorySlot ---@field slot Barotrauma.ItemSlot ---@field inventory Barotrauma.ItemInventory ---@field slotIndex number ---@param inventory Barotrauma.ItemInventory ---@param predicate fun(slot: InventorySlot): boolean ---@return InventorySlot[], string? local function findSlotsThat(inventory, predicate) local slots = {} for i, slot in ipairs(inventory.slots) do local inventorySlot = { slot = slot, inventory = inventory, slotIndex = i - 1 } if predicate(inventorySlot) then slots[#slots + 1] = inventorySlot end end return slots end ---@param slot InventorySlot local function tryUnloadSlot(slot) ---@type Barotrauma.Item local item = slot.slot.items[1] if not item then MyModGlobal.debugPrint("No item in slot") return end local inventory = item.OwnInventory if not inventory then MyModGlobal.debugPrint("Item has no own inventory") return end local toUnload = {} local toUnloadByPrefab = {} local inventorySlots = inventory.slots for _, inventorySlot in ipairs(inventorySlots) do for _, inventoryItem in ipairs(inventorySlot.items) do toUnload[#toUnload + 1] = inventoryItem -- This will only serve as O(1) lookup toUnloadByPrefab[inventoryItem.Prefab] = true end end -- Where can we put our toUnload items? local nearbySlots = findSlotsThat(slot.inventory, function(islot) local isEmpty = #islot.slot.items == 0 if isEmpty then return true end for _, prefab in ipairs(toUnloadByPrefab) do local canAccept = islot.inventory.CanBePutInSlot(prefab, islot.slotIndex) if canAccept then return true end end return false end) -- print("Before sorting:") -- dump(nearbySlots) local slotsPerRow = slot.inventory.slotsPerRow or 1 local getGridPos = function(slotIndex) local x = slotIndex % slotsPerRow local y = math.floor(slotIndex / slotsPerRow) return x, y end -- We are offsetting here by 1 because the backend uses 0-indexed slots -- And the lua uses 1-indexed slots -- We are trying to match the backend behavior for sorting local slotx, sloty = getGridPos(slot.slotIndex - 1) -- print(string.format("Slot position %d: %d, %d", slot.slotIndex, slotx, sloty)) table.sort(nearbySlots, function(a, b) local ax, ay = getGridPos(a.slotIndex) local bx, by = getGridPos(b.slotIndex) local distA = math.max(math.abs(ax - slotx), math.abs(ay - sloty)) local distB = math.max(math.abs(bx - slotx), math.abs(by - sloty)) if distA == distB then return a.slotIndex < b.slotIndex end return distA < distB end) -- print(string.format("Current slot: %d at (%d, %d)", slot.slotIndex, slotx, sloty)) for _, iitem in ipairs(toUnload) do for _, nearbySlot in ipairs(nearbySlots) do local canAccept = nearbySlot.inventory.CanBePutInSlot(iitem.Prefab, nearbySlot.slotIndex) if canAccept then local moved = nearbySlot.inventory.TryPutItem(iitem, nearbySlot.slotIndex, true, false, nil) -- print(string.format("Moved item %s to slot %d", iitem.Name, nearbySlot.slotIndex)) if moved then break end end end end end local function tryUnloadCursorItem() local slots, err = cursormacroer.getInventorySlotsUnderCursor() if err then -- MyModGlobal.debugPrint(string.format("Error getting inventory slot: %s", err)) return end if not slots or #slots == 0 then -- MyModGlobal.debugPrint("No items in slot") return end for _, slot in ipairs(slots) do tryUnloadSlot(slot) end -- local canAccept = inventorySlot.CanBePutInSlot(item, inventorySlot.slotIndex, item.Condition) -- if canAccept then -- toUnload[#toUnload + 1] = inventoryItem -- end -- local slots = findSlotsThat(inventory, function(slot) -- local canAccept -- end) -- local inventory = targetInventory -- -- MyModGlobal.debugPrint(string.format("Target inventory: %s", tostring(inventory))) -- if not inventory then -- local controlledCharacter = Character.Controlled -- if not controlledCharacter then -- -- MyModGlobal.debugPrint("No controlled character found") -- return -- end -- local cinventory = controlledCharacter.Inventory -- if not cinventory or not cinventory.slots then -- -- MyModGlobal.debugPrint("No inventory found") -- return -- end -- local bagSlot = cinventory.slots[MyModGlobal.BAG_SLOT] -- if not bagSlot or not bagSlot.items or not bagSlot.items[1] then -- -- MyModGlobal.debugPrint("No bag slot found") -- return -- end -- local bagItem = bagSlot.items[1] -- if not bagItem or not bagItem.OwnInventory then -- -- MyModGlobal.debugPrint("Bag item has no own inventory") -- return -- end -- local bagInventory = bagItem.OwnInventory -- if not bagInventory or not bagInventory.slots then -- -- MyModGlobal.debugPrint("Bag inventory has no slots") -- return -- end -- inventory = bagInventory -- end -- if not inventory then -- -- MyModGlobal.debugPrint("No inventory found") -- return -- end -- local itemTree -- itemTree, err = quickstack.buildItemTree(inventory) -- if err then -- -- MyModGlobal.debugPrint(string.format("Error building item tree: %s", err)) -- return -- end -- itemTree = quickstack.sortItemTree(itemTree) -- local itemsToMove = {} -- local now = Timer.GetTime() -- for _, slot in ipairs(slots) do -- local runAfter = slotThrottle[slot] or 0 -- if now < runAfter then -- goto continue -- end -- -- MyModGlobal.debugPrint(string.format("Enqueuing slot: %s, before: %d", tostring(slot), #itemsToMove)) -- utils.enqueueSlot(slot.slot, itemsToMove) -- -- MyModGlobal.debugPrint(string.format("Enqueuing slot: %s, after: %d", tostring(slot), #itemsToMove)) -- slotThrottle[slot] = now + 1 -- ::continue:: -- end -- -- for _, item in ipairs(itemsToMove) do -- -- MyModGlobal.debugPrint(string.format("Enqueued item: %s", tostring(item))) -- -- end -- -- -- MyModGlobal.debugPrint(string.format("Enqueued %d items from the inventory slot", #itemsToMove)) -- -- MyModGlobal.DumpTable(itemTree) -- quickstack.tryMoveItems(itemsToMove, itemTree, true) -- -- local errors = quickstack.tryMoveItems(itemsToMove, itemTree, true) -- -- for _, error in ipairs(errors) do -- -- MyModGlobal.debugPrint(string.format("Error moving item: %s", error)) -- -- end end return { tryUnloadCursorItem = tryUnloadCursorItem, }