-- luacheck: globals Character MyModGlobal Timer local cursormacroer = require("Cyka.cursormacroer") local utils = require("Cyka.utils") local dump = require("Cyka.dump") -- Some items allow multiple items to be loaded -- Such as welders and cutters -- But we don't really want to disqualify those programmatically -- Because it is not so obvious what item we REALLY want to load -- So we will just hardcode tools and their whitelisted magazines ---@type table> local LOAD_MAP = { } ---@param inventory Barotrauma.ItemInventory ---@return InventorySlot[] local function getSlots(inventory) local slots = {} local inventorySlots = inventory.slots for i, inventorySlot in ipairs(inventorySlots) do slots[#slots + 1] = { inventory = inventory, slotIndex = i - 1, slot = inventorySlot } end return slots end ---@param from Barotrauma.ItemInventory ---@param slots InventorySlot[] ---@return table local function getItemsPerSlot(from, slots) -- How many items can we move to what slot -- We don't yet know what can fit into what slot ---@type table local movableBySlot = {} -- Get all the items and then we will sort them by condition and shit utils.enqueueInventory(from, {}, function(ititem) -- MyModGlobal.debugPrint("Checking item:") -- dump(slots) -- MyModGlobal.debugPrint(ititem.Prefab.Identifier.Value) for _, inventorySlot in ipairs(slots) do local canMove = inventorySlot.inventory.CanBePutInSlot(ititem, inventorySlot.slotIndex) -- MyModGlobal.debugPrint(string.format("Can move to slot %d: %s", inventorySlot.slotIndex, tostring(canMove))) if canMove then movableBySlot[inventorySlot] = movableBySlot[inventorySlot] or {} movableBySlot[inventorySlot][#movableBySlot[inventorySlot] + 1] = ititem return true end end return false end) return movableBySlot end ---@param movableBySlot table ---@return table> local function getPermissibleItemsPerSlot(movableBySlot) -- The point of this exercise is to eliminate slots that can have -- Multiple items -- What do we put into those? Any? All? -- What if those slots belong to a container? -- Are we reloading 30 slot containers? ---@type table> local permissibleItemsPerSlot = {} for inventorySlot, items in pairs(movableBySlot) do for _, ititem in ipairs(items) do local thisone = tostring(ititem.Prefab.Identifier.Value) permissibleItemsPerSlot[inventorySlot][thisone] = true end end return permissibleItemsPerSlot end ---@param slot InventorySlot ---@param preferMinCondition boolean local function tryReloadSlot(slot, preferMinCondition) ---@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 character = Character.Controlled if not character then MyModGlobal.debugPrint("No character") return end local characterInventory = character.Inventory if not characterInventory then MyModGlobal.debugPrint("No character inventory") return end ---@type InventorySlot[] local slots = getSlots(inventory) -- MyModGlobal.debugPrint("Slots:") -- dump(slots) ---@type table local movableBySlot = getItemsPerSlot(characterInventory, slots) -- MyModGlobal.debugPrint("Movable by slot:") -- dump(movableBySlot) local permissibleItems = LOAD_MAP[tostring(item.Prefab.Identifier.Value)] if not permissibleItems then MyModGlobal.debugPrint("No permissible items for item") local permissibleItemsPerSlot = getPermissibleItemsPerSlot(movableBySlot) MyModGlobal.debugPrint("Can load per slot:") dump(permissibleItemsPerSlot) return end -- Sort items by condition (asc or desc) but also -- Make sure items with 0 condition are at the end -- We don't really want to load those for _, items in pairs(movableBySlot) do table.sort(items, function(a, b) if a.ConditionPercentage == 0 and b.ConditionPercentage ~= 0 then return false elseif a.ConditionPercentage ~= 0 and b.ConditionPercentage == 0 then return true elseif preferMinCondition then return a.ConditionPercentage < b.ConditionPercentage else return a.ConditionPercentage > b.ConditionPercentage end end) end -- dump(movableBySlot) for inventorySlot, items in pairs(movableBySlot) do for _, ititem in ipairs(items) do local moved = inventorySlot.inventory.TryPutItem(ititem, inventorySlot.slotIndex, false, true, nil) -- When the slot is full no more will be able to be moved -- And tat that point we're done with that slot if not moved then break end 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, nil) -- -- print(string.format("Moved item %s to slot %d", iitem.Name, nearbySlot.slotIndex)) -- if moved then break end -- end -- end -- end end ---@param minCondition boolean Prefer items with lowest condition local function tryReloadCursorItem(preferMinCondition) 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 tryReloadSlot(slot, preferMinCondition) end end return { tryReloadCursorItem = tryReloadCursorItem, }