Implement quickstack to existing stacks in inventory

This commit is contained in:
2025-03-30 23:26:48 +02:00
parent a210fe27c6
commit a56a1897bc
4 changed files with 93 additions and 36 deletions

View File

@@ -18,6 +18,7 @@ MyModGlobal = {
FIX = Keys.R,
UNLOAD = Keys.E,
RELOAD = Keys.R,
STACK_TO_CURSOR = Keys.G,
NESTED_CONTAINERS = true,
DEBUG_MODE = true,
},
@@ -86,6 +87,15 @@ Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptab
quickstack.quickStackItems(instance)
end, Hook.HookMethodType.After)
Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptable)
if not PlayerInput.KeyHit(MyModGlobal.CONFIG.STACK_TO_CURSOR) then return end
if not PlayerInput.IsShiftDown() then
quickstack.stackToCursor()
else
quickstack.stackAllToCursor()
end
end, Hook.HookMethodType.After)
Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptable)
if not PlayerInput.KeyHit(MyModGlobal.CONFIG.FABRICATOR_KEY) then return end
fabricatorstack.tryStackFabricator(instance)

View File

@@ -1,4 +1,5 @@
-- luacheck: globals MyModGlobal Character
-- luacheck: max line length 420
local utils = require("Cyka.utils")
---@class ItemLocation
@@ -188,37 +189,6 @@ local function tryMoveItems(items, itemTree, force)
return errs
end
---@return Barotrauma.Item[]
local function getOpenContainers()
-- MyModGlobal.debugPrint("Attempting to find open container...")
-- local containers = {}
-- for item in Item.ItemList do
-- ---@cast item Barotrauma.Item
-- local isok = true
-- isok = isok and item ~= nil
-- isok = isok and item.OwnInventory ~= nil
-- isok = isok and item.OwnInventory.visualSlots ~= nil
-- isok = isok and #item.OwnInventory.visualSlots > 0
-- -- I don't know what rootContainer is
-- -- It seems to be the parent of the current item...?
-- -- Maybe the world object...
-- -- Either way - static objects that we may open have it
-- -- And our own inventory does not
-- -- So it's a good selector for now
-- isok = isok and item.rootContainer ~= nil
-- if isok then
-- containers[#containers + 1] = item
-- end
-- end
local controlledCharacter = Character.Controlled
if not controlledCharacter then return {} end
local selectedItem = controlledCharacter.SelectedItem
if not selectedItem then return {} end
return { selectedItem }
end
---@param character Barotrauma.Character
---@return table<string, ItemLocation[]>, string
local function tryBuildCharacterItemTree(character)
@@ -310,7 +280,7 @@ local function quickStackItems(character)
end
end
local openContainers = getOpenContainers()
local openContainers = utils.getOpenContainers()
for _, container in ipairs(openContainers) do
local inventories = container.OwnInventories
MyModGlobal.debugPrint(string.format("Found %d inventories in the open container", #inventories))
@@ -329,6 +299,78 @@ local function quickStackItems(character)
end
end
local function stackToCursor()
local slots, err = utils.getSlotsUnderCursor()
if err then
MyModGlobal.debugPrint(string.format("Error getting slots under cursor: %s", err))
return
end
for _, slot in ipairs(slots) do
local item
if not slot.slot.items or #slot.slot.items == 0 then
MyModGlobal.debugPrint("No items in slot")
goto continue
end
item = slot.slot.items[1]
utils.enqueueAllPlayerItems({}, function(ititem)
if ititem.Prefab.Identifier.Value == item.Prefab.Identifier.Value then
if item == ititem then return false end
-- We are moving items in the predicate because we expect to only
-- Select a small subset of all items
-- And it is much easier to let the game decide when we can not move
-- Any more items (via return value of TryPutItem)
-- And we then know that we can safely stop
local moved = slot.inventory.TryPutItem(ititem, slot.slotIndex - 1, false, true, nil)
if not moved then
MyModGlobal.debugPrint(string.format("Failed to move item %s to slot %d", ititem.Name, slot
.slotIndex - 1))
return false, true
end
end
end)
::continue::
end
end
local function stackAllToCursor()
local slots, err = utils.getSlotsUnderCursor()
if err then
MyModGlobal.debugPrint(string.format("Error getting slots under cursor: %s", err))
return
end
for _, slot in ipairs(slots) do
local item
if not slot.slot.items or #slot.slot.items == 0 then
MyModGlobal.debugPrint("No items in slot")
goto continue
end
item = slot.slot.items[1]
utils.enqueueAllOwnedItems({}, function(ititem)
if ititem.Prefab.Identifier.Value == item.Prefab.Identifier.Value then
if item == ititem then return false end
-- We are moving items in the predicate because we expect to only
-- Select a small subset of all items
-- And it is much easier to let the game decide when we can not move
-- Any more items (via return value of TryPutItem)
-- And we then know that we can safely stop
local moved = slot.inventory.TryPutItem(ititem, slot.slotIndex - 1, false, true, nil)
if not moved then
MyModGlobal.debugPrint(string.format("Failed to move item %s to slot %d", ititem.Name, slot
.slotIndex - 1))
return false, true
end
end
end)
::continue::
end
end
return {
buildItemTree = buildItemTree,
tryBuildCharacterItemTree = tryBuildCharacterItemTree,
@@ -336,5 +378,6 @@ return {
tryMoveItem = tryMoveItem,
tryMoveItems = tryMoveItems,
quickStackItems = quickStackItems,
getOpenContainers = getOpenContainers
stackToCursor = stackToCursor,
stackAllToCursor = stackAllToCursor,
}

View File

@@ -1,6 +1,5 @@
-- luacheck: globals Character MyModGlobal Timer
local cursormacroer = require("Cyka.cursormacroer")
local dump = require("Cyka.dump")
local utils = require("Cyka.utils")
---@param inventory Barotrauma.ItemInventory
---@param predicate fun(slot: InventorySlot): boolean

View File

@@ -333,7 +333,12 @@ local function getFirstSlotUnderCursor()
local slots, err = getSlotsUnderCursor()
if err then return nil, err end
if #slots == 0 then return nil, "No slots found under cursor" end
return slots[1], nil
for _, slot in ipairs(slots) do
if #slot.items > 0 then
return slot
end
end
return slots[1]
end
return {