-- luacheck: globals Character MyModGlobal Timer local utils = require("Cyka.utils") ---@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) -- Some inventories don't have slots per row, like the player inventory local slotsPerRow = 900 local ok, err = pcall(function() slotsPerRow = slot.inventory.slotsPerRow end) if not ok then MyModGlobal.debugPrint(string.format("Error getting slots per row: %s", err)) end 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, Character.Controlled, true) -- 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 = utils.getSlotsUnderCursor() 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 end return { tryUnloadCursorItem = tryUnloadCursorItem, }