if SERVER then return end -- Register necessary types and make fields accessible LuaUserData.RegisterType("Barotrauma.Items.Components.ItemContainer+SlotRestrictions") LuaUserData.RegisterType( 'System.Collections.Immutable.ImmutableArray`1[[Barotrauma.Items.Components.ItemContainer+SlotRestrictions, Barotrauma]]') LuaUserData.MakeFieldAccessible(Descriptors['Barotrauma.Items.Components.ItemContainer'], 'slotRestrictions') LuaUserData.MakeFieldAccessible(Descriptors['Barotrauma.ItemInventory'], 'slots') LuaUserData.MakeFieldAccessible(Descriptors["Barotrauma.CharacterInventory"], "slots") -- Configuration local CONFIG = { TRIGGER_KEY = Keys.F, -- Key to press for quick stacking BAG_TAGS = { "container", "cargocontainer" }, -- Tags that identify bags/containers MAX_ITEMS_TO_PROCESS = 100, -- Safety limit to prevent infinite loops PREFER_EXISTING_STACKS = true, -- Prefer stacking into existing item stacks CHECK_ALL_CONTAINERS = true, -- Check all containers in inventory, not just bag slot PRIMARY_BAG_SLOT = 8, -- Slot number for the primary bag (typically slot 8) SKIP_HAND_SLOTS = true, -- Skip hand slots when processing items HAND_SLOTS = { 6, 7 }, -- Slot numbers for hands (typically slots 6 and 7) SKIP_DIVING_GEAR = true, -- Skip diving gear when processing items DIVING_GEAR_TAGS = { "divingsuit", "divingmask", "oxygensource" } -- Tags for diving gear } -- Function to check if an item is a bag/container local function isContainer(item) if not item then return false end for _, tag in ipairs(CONFIG.BAG_TAGS) do if item.HasTag(tag) then return true end end return false end -- Function to check if an item is diving gear local function isDivingGear(item) if not CONFIG.SKIP_DIVING_GEAR or not item then return false end for _, tag in ipairs(CONFIG.DIVING_GEAR_TAGS) do if item.HasTag(tag) then return true end end return false end -- Function to find all containers in inventory local function findContainersInInventory(character) local containers = {} local inventory = character.Inventory if not inventory or not inventory.slots then return containers end -- First add the primary bag if available (priority container) if #inventory.slots >= CONFIG.PRIMARY_BAG_SLOT then for _, item in ipairs(inventory.slots[CONFIG.PRIMARY_BAG_SLOT].items) do if isContainer(item) and item.OwnInventory then table.insert(containers, item) end end end -- Add all other containers if configured to do so if CONFIG.CHECK_ALL_CONTAINERS then for slotIndex, slot in ipairs(inventory.slots) do -- Skip the primary bag slot as we already processed it if slotIndex == CONFIG.PRIMARY_BAG_SLOT then goto continue end -- Skip hand slots if configured to do so if CONFIG.SKIP_HAND_SLOTS then local isHandSlot = false for _, handSlot in ipairs(CONFIG.HAND_SLOTS) do if slotIndex == handSlot then isHandSlot = true break end end if isHandSlot then goto continue end end for _, item in ipairs(slot.items) do if isContainer(item) and item.OwnInventory and not isDivingGear(item) then table.insert(containers, item) end end ::continue:: end end return containers end -- Function to check if an item can be stacked with another local function canStackWith(targetItem, sourceItem) if not targetItem or not sourceItem then return false end -- Check if items have same identifier if not targetItem.Prefab.Identifier.Equals(sourceItem.Prefab.Identifier) then return false end -- Additional stacking checks could be added here return true end -- Function to try stacking an item into a specific slot in container local function tryStackItemInSlot(container, sourceItem, slotIndex) if not container or not sourceItem then return false end local slot = container.slots[slotIndex + 1] -- +1 because Lua is 1-indexed but slots are 0-indexed -- Check if slot exists if not slot then return false end -- Try to find an existing stack of the same type for _, targetItem in ipairs(slot.items) do if canStackWith(targetItem, sourceItem) then return container.TryPutItem(sourceItem, slotIndex, true, false, nil) end end -- If no existing stack and slot has space, try to put item there if container.CanBePutInSlot(sourceItem, slotIndex) then return container.TryPutItem(sourceItem, slotIndex, false, false, nil) end return false end -- Function to quick stack items into containers local function quickStackToContainers(character, containers) if not character or #containers == 0 then return end local playerInv = character.Inventory local processedCount = 0 local stackedCount = 0 -- Process inventory slots for slotIndex = 1, #playerInv.slots do -- Skip hand slots if configured to do so if CONFIG.SKIP_HAND_SLOTS then local isHandSlot = false for _, handSlot in ipairs(CONFIG.HAND_SLOTS) do if slotIndex == handSlot then isHandSlot = true break end end if isHandSlot then goto continue end end local slot = playerInv.slots[slotIndex] if not slot then goto continue end -- Process items in the slot for i = #slot.items, 1, -1 do -- Iterate backwards to safely remove local item = slot.items[i] -- Declare success variable outside any potential goto jumps local success = false -- Skip containers themselves if isContainer(item) then goto nextItem end -- Skip diving gear if configured to do so if isDivingGear(item) then goto nextItem end processedCount = processedCount + 1 if processedCount > CONFIG.MAX_ITEMS_TO_PROCESS then print("QuickStack: Safety limit reached") return stackedCount end -- Try to stack the item into the containers -- Try each container in order for _, container in ipairs(containers) do local containerInv = container.OwnInventory if not containerInv then goto nextContainer end -- First try to stack with existing stacks if configured to do so if CONFIG.PREFER_EXISTING_STACKS then for bagSlotIndex = 0, #containerInv.slots - 1 do for _, containerItem in ipairs(containerInv.slots[bagSlotIndex + 1].items) do if canStackWith(containerItem, item) then success = tryStackItemInSlot(containerInv, item, bagSlotIndex) if success then break end end end if success then break end end end -- If not stacked yet, try any valid slot if not success then for bagSlotIndex = 0, #containerInv.slots - 1 do success = tryStackItemInSlot(containerInv, item, bagSlotIndex) if success then break end end end if success then stackedCount = stackedCount + 1 break -- Stop trying containers if we succeeded end ::nextContainer:: end ::nextItem:: end ::continue:: end return stackedCount end -- Hook into the player control to listen for key press Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptable) if not PlayerInput.KeyHit(CONFIG.TRIGGER_KEY) then return end local character = instance if not character then return end -- Find all containers in inventory local containers = findContainersInInventory(character) if #containers == 0 then Game.ShowChatMessage("No containers found in inventory", ChatMessageType.Server) return end -- Perform quick stacking local stackedCount = quickStackToContainers(character, containers) -- Notify player if stackedCount and stackedCount > 0 then Game.ShowChatMessage("Quick stacked " .. stackedCount .. " items to containers", ChatMessageType.Server) else Game.ShowChatMessage("No items could be stacked to containers", ChatMessageType.Server) end end, Hook.HookMethodType.After)