Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
355bfe4df6 | |||
a9490c039e | |||
88187358e8 | |||
bb7dc1da32 | |||
21eda6a5ac | |||
50d84f5ba5 | |||
856dc8b305 | |||
91b8576385 |
23
CykaQuick/.vscode/settings.json
vendored
Normal file
23
CykaQuick/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"highlight.regex.workspace.regexes": [
|
||||
{
|
||||
"name": "namedGroups",
|
||||
"regexes": [
|
||||
{
|
||||
"regex": "(?:(\\?<[^>]+>)[^)]+)",
|
||||
"decorations": [
|
||||
{
|
||||
"backgroundColor": "#000000",
|
||||
"color": "#1CAFC4"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Lua.diagnostics.libraryFiles": "Enable",
|
||||
"Lua.workspace.library": [
|
||||
"../Meta/Types/client",
|
||||
"../Meta/Types/shared"
|
||||
],
|
||||
}
|
@@ -7,7 +7,6 @@ if SERVER then
|
||||
require("Cyka.xpticker")
|
||||
else
|
||||
---@class MyModGlobal
|
||||
---@field CONFIG {QUICKSTACK_KEYS: Keys, FABRICATOR_KEY: Keys, MAX_BUY: Keys, NESTED_CONTAINERS: boolean, DEBUG_MODE: boolean}
|
||||
---@field MOD_NAME string
|
||||
---@field MOD_VERSION string
|
||||
---@field DumpTable fun(table: table, depth?: number)
|
||||
|
@@ -4,7 +4,7 @@ if not CLIENT then return end
|
||||
local dump = require("Cyka.dump")
|
||||
local quickstack = require("Cyka.quickstack")
|
||||
|
||||
---@class ItemLocation
|
||||
---@class ItemLocationDistance
|
||||
---@field item Barotrauma.Item
|
||||
---@field distance number
|
||||
|
||||
|
@@ -60,7 +60,7 @@ local function tryStackCursorItem()
|
||||
end
|
||||
itemTree = quickstack.sortItemTree(itemTree)
|
||||
|
||||
local itemsToMove = {}
|
||||
local options = {}
|
||||
local now = Timer.GetTime()
|
||||
for _, slot in ipairs(slots) do
|
||||
local runAfter = slotThrottle[slot] or 0
|
||||
@@ -68,7 +68,7 @@ local function tryStackCursorItem()
|
||||
goto continue
|
||||
end
|
||||
-- MyModGlobal.debugPrint(string.format("Enqueuing slot: %s, before: %d", tostring(slot), #itemsToMove))
|
||||
utils.enqueueSlot(slot.slot, itemsToMove)
|
||||
options = utils.enqueueSlot(slot.inventory.slots[slot.slotIndex1], options)
|
||||
-- MyModGlobal.debugPrint(string.format("Enqueuing slot: %s, after: %d", tostring(slot), #itemsToMove))
|
||||
slotThrottle[slot] = now + 1
|
||||
::continue::
|
||||
@@ -79,7 +79,7 @@ local function tryStackCursorItem()
|
||||
-- -- MyModGlobal.debugPrint(string.format("Enqueued %d items from the inventory slot", #itemsToMove))
|
||||
-- MyModGlobal.DumpTable(itemTree)
|
||||
|
||||
quickstack.tryMoveItems(itemsToMove, itemTree, true)
|
||||
quickstack.tryMoveItems(options.itemQueue, itemTree, true)
|
||||
-- local errors = quickstack.tryMoveItems(itemsToMove, itemTree, true)
|
||||
-- for _, error in ipairs(errors) do
|
||||
-- MyModGlobal.debugPrint(string.format("Error moving item: %s", error))
|
||||
@@ -101,14 +101,7 @@ local function setTargetInventory()
|
||||
-- If we get multiple we'll use the first valid one
|
||||
-- Although everything is valid to us...
|
||||
for _, slot in ipairs(slots) do
|
||||
local item
|
||||
local items = slot.slot.items
|
||||
if not items or #items == 0 then
|
||||
print(string.format("Slot is empty, setting target inventory to %s", tostring(slot.inventory)))
|
||||
targetInventory = slot.inventory
|
||||
goto continue
|
||||
end
|
||||
item = items[1]
|
||||
local item = slot.item
|
||||
if not item then
|
||||
print(string.format("Item in slot is nil, setting target inventory to %s", tostring(slot.inventory)))
|
||||
targetInventory = slot.inventory
|
||||
|
@@ -3,7 +3,7 @@ if not CLIENT then return end
|
||||
local utils = require("Cyka.utils")
|
||||
local dump = require("Cyka.dump")
|
||||
|
||||
---@return {item: Barotrauma.Item, fabricator: Barotrauma.FabricatorComponent}, string?
|
||||
---@return {item: Barotrauma.Item, fabricator: Barotrauma.Items.Components.Fabricator}?, string?
|
||||
local function getOpenFabricator()
|
||||
-- Get the controlled character
|
||||
local controlledCharacter = Character.Controlled
|
||||
@@ -29,8 +29,8 @@ end
|
||||
---@field targetItem {identifier: string, name: string, amount: number}
|
||||
---@field requiredItems {amount: number, minCondition: number, maxCondition: number, prefabs: string[]}[]
|
||||
|
||||
---@param fabricator Barotrauma.FabricatorComponent
|
||||
---@return RecipeInfo, string?
|
||||
---@param fabricator Barotrauma.Items.Components.Fabricator
|
||||
---@return RecipeInfo?, string?
|
||||
local function getSelectedRecipeRequirements(fabricator)
|
||||
-- local openFabricator, err = getOpenFabricator()
|
||||
-- if err then return nil, err end
|
||||
@@ -73,7 +73,7 @@ local function tryStackFabricator(character)
|
||||
MyModGlobal.debugPrint("Character inventory is nil.")
|
||||
return
|
||||
end
|
||||
---@type Barotrauma.ItemInventory.Slot
|
||||
---@type Barotrauma.Inventory.ItemSlot
|
||||
local bagSlot = inventory.slots[MyModGlobal.BAG_SLOT]
|
||||
if not bagSlot then
|
||||
MyModGlobal.debugPrint("Bag slot not found.")
|
||||
@@ -91,14 +91,14 @@ local function tryStackFabricator(character)
|
||||
end
|
||||
|
||||
local fabricator, err = getOpenFabricator()
|
||||
if err then
|
||||
if err or not fabricator then
|
||||
print(string.format("Error getting open fabricator: %s", err))
|
||||
return
|
||||
end
|
||||
|
||||
local recipe
|
||||
recipe, err = getSelectedRecipeRequirements(fabricator.fabricator)
|
||||
if err then
|
||||
if err or not recipe then
|
||||
print(string.format("Error getting selected recipe requirements: %s", err))
|
||||
return
|
||||
end
|
||||
@@ -147,7 +147,10 @@ local function tryStackFabricator(character)
|
||||
-- dump(itemsOnSubmarine)
|
||||
-- MyModGlobal.DumpTable(toGet)
|
||||
|
||||
local items, _ = utils.enqueueAllOwnedItems({}, filter)
|
||||
local items, _ = utils.enqueueAllOwnedItems({
|
||||
recurse = true,
|
||||
itemPredicate = filter
|
||||
})
|
||||
-- if err then
|
||||
-- print(string.format("Error enqueueing all owned items: %s", err))
|
||||
-- return
|
||||
|
@@ -4,12 +4,12 @@ if not CLIENT then return end
|
||||
---@return Barotrauma.Location.StoreInfo[], string?
|
||||
local function getCurrentStore()
|
||||
if not Game or not Game.GameSession or not Game.GameSession.Campaign then
|
||||
return nil, "No game session found"
|
||||
return {}, "No game session found"
|
||||
end
|
||||
|
||||
local map = Game.GameSession.Campaign.Map
|
||||
if not map or not map.CurrentLocation or not map.CurrentLocation.Stores then
|
||||
return nil, "No map found"
|
||||
return {}, "No map found"
|
||||
end
|
||||
|
||||
local location = map.CurrentLocation
|
||||
@@ -17,13 +17,13 @@ local function getCurrentStore()
|
||||
-- Otherwise, determine which store is active by checking the cargo manager
|
||||
local cargoManager = Game.GameSession.Campaign.CargoManager
|
||||
if not cargoManager then
|
||||
return nil, "No cargo manager found"
|
||||
return {}, "No cargo manager found"
|
||||
end
|
||||
|
||||
-- Find which store has items in the cart
|
||||
local stores = {}
|
||||
for _, store in pairs(location.Stores) do
|
||||
if #cargoManager:GetBuyCrateItems(store) > 0 then
|
||||
if #cargoManager:GetBuyCrateItems() > 0 then
|
||||
stores[#stores + 1] = store
|
||||
end
|
||||
end
|
||||
@@ -47,7 +47,7 @@ local function tryBuy()
|
||||
for _, store in ipairs(stores) do
|
||||
local toAdd = {}
|
||||
-- Get items available at the store
|
||||
local items = cargoManager:GetBuyCrateItems(store)
|
||||
local items = cargoManager:GetBuyCrateItems()
|
||||
for item in items do
|
||||
-- We have already added this many of item
|
||||
toAdd[item.ItemPrefab.Identifier.Value] = {
|
||||
@@ -55,6 +55,7 @@ local function tryBuy()
|
||||
prefab = item.ItemPrefab -- Store the ItemPrefab object
|
||||
}
|
||||
end
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
for item in store.Stock do
|
||||
-- So if we add the total amount available
|
||||
-- We get the amount we have to add to buy entire stock
|
||||
@@ -68,6 +69,7 @@ local function tryBuy()
|
||||
if info.quantity > 0 then
|
||||
MyModGlobal.debugPrint(string.format("Adding %d of %s to the buy crate", info.quantity, idValue))
|
||||
-- Use the stored ItemPrefab object, not the string identifier
|
||||
---@diagnostic disable-next-line: undefined-field
|
||||
cargoManager:ModifyItemQuantityInBuyCrate(store.Identifier, info.prefab, info.quantity)
|
||||
end
|
||||
end
|
||||
|
@@ -16,14 +16,13 @@ local LOAD_MAP = require("Cyka.quickreload_loadmap")
|
||||
---@param inventory Barotrauma.ItemInventory
|
||||
---@return InventorySlot[]
|
||||
local function getSlots(inventory)
|
||||
---@type InventorySlot[]
|
||||
local slots = {}
|
||||
---@type Barotrauma.Inventory.ItemSlot[]
|
||||
local inventorySlots = inventory.slots
|
||||
for i, inventorySlot in ipairs(inventorySlots) do
|
||||
slots[#slots + 1] = {
|
||||
inventory = inventory,
|
||||
slotIndex = i - 1,
|
||||
slot = inventorySlot
|
||||
}
|
||||
for i, _ in ipairs(inventorySlots) do
|
||||
local invSlot = MyModGlobal.InventorySlot.new(inventory, i)
|
||||
slots[#slots + 1] = invSlot
|
||||
end
|
||||
return slots
|
||||
end
|
||||
@@ -36,7 +35,11 @@ local function getItemsPerSlot(slots)
|
||||
---@type table<InventorySlot, Barotrauma.Item[]>
|
||||
local movableBySlot = {}
|
||||
-- Get all the items and then we will sort them by condition and shit
|
||||
utils.enqueueAllPlayerItems({}, function(ititem, itemRef)
|
||||
utils.enqueuePlayerItems({
|
||||
recurse = true,
|
||||
loadRefs = true,
|
||||
itemPredicate = function(ititem, itemRef)
|
||||
MyModGlobal.debugPrint(string.format("Checking item: %s", tostring(ititem)))
|
||||
-- We don't want to take oxygen out of our diving suit to load our plasma cutter
|
||||
-- Most loadable items have 1 capacity
|
||||
-- But some have 2 or 3 (coil speargun)
|
||||
@@ -48,7 +51,7 @@ local function getItemsPerSlot(slots)
|
||||
-- dump(slots)
|
||||
-- MyModGlobal.debugPrint(ititem.Prefab.Identifier.Value)
|
||||
for _, inventorySlot in ipairs(slots) do
|
||||
local canMove = inventorySlot.inventory.CanBePutInSlot(ititem, inventorySlot.slotIndex)
|
||||
local canMove = inventorySlot.inventory.CanBePutInSlot(ititem, inventorySlot.slotIndex1 - 1)
|
||||
-- MyModGlobal.debugPrint(string.format("Can move to slot %d: %s", inventorySlot.slotIndex, tostring(canMove)))
|
||||
if canMove then
|
||||
movableBySlot[inventorySlot] = movableBySlot[inventorySlot] or {}
|
||||
@@ -57,7 +60,8 @@ local function getItemsPerSlot(slots)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end, true)
|
||||
end
|
||||
})
|
||||
return movableBySlot
|
||||
end
|
||||
|
||||
@@ -93,11 +97,11 @@ local function printPermissibleItems(movableBySlot)
|
||||
end
|
||||
|
||||
|
||||
---@param slot InventorySlot
|
||||
---@param invSlot InventorySlot
|
||||
---@param preferMinCondition boolean
|
||||
local function tryReloadSlot(slot, preferMinCondition)
|
||||
local function tryReloadSlot(invSlot, preferMinCondition)
|
||||
---@type Barotrauma.Item
|
||||
local item = slot.slot.items[1]
|
||||
local item = invSlot.item
|
||||
if not item then
|
||||
MyModGlobal.debugPrint("No item in slot")
|
||||
return
|
||||
@@ -115,13 +119,10 @@ local function tryReloadSlot(slot, preferMinCondition)
|
||||
MyModGlobal.debugPrint("No slots")
|
||||
return
|
||||
end
|
||||
-- MyModGlobal.debugPrint("Slots:")
|
||||
-- dump(slots)
|
||||
|
||||
---@type table<InventorySlot, Barotrauma.Item[]>
|
||||
local movableBySlot = getItemsPerSlot(slots)
|
||||
-- MyModGlobal.debugPrint("Movable by slot:")
|
||||
-- dump(movableBySlot)
|
||||
|
||||
local permissibleItems = LOAD_MAP[tostring(item.Prefab.Identifier.Value)]
|
||||
if not permissibleItems then
|
||||
@@ -156,22 +157,19 @@ local function tryReloadSlot(slot, preferMinCondition)
|
||||
-- We loaded as many as we have been allowed to
|
||||
-- And we do this check up front because an item may already
|
||||
-- Be partially loaded
|
||||
local nowHave = #inventorySlot.slot.items
|
||||
local nowHave = inventorySlot.stackSize
|
||||
if nowHave >= permissible then
|
||||
-- MyModGlobal.debugPrint(string.format(
|
||||
-- "Finished processing item: %s. Current slot has reached the permissible limit of %d items.",
|
||||
-- tostring(ititem.Prefab.Identifier.Value), permissible))
|
||||
MyModGlobal.debugPrint(string.format(
|
||||
"Finished processing item: %s. Current slot has reached the permissible limit of %d items.",
|
||||
tostring(ititem.Prefab.Identifier.Value), permissible))
|
||||
break
|
||||
end
|
||||
|
||||
local moved = inventorySlot.inventory.TryPutItem(ititem, inventorySlot.slotIndex, false, true, Character.Controlled, true)
|
||||
-- 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
|
||||
if not inventorySlot:canFit(ititem.Prefab) then
|
||||
break
|
||||
end
|
||||
utils.enqueueMove(ititem, inventorySlot)
|
||||
numMoved = numMoved + 1
|
||||
|
||||
-- else
|
||||
-- MyModGlobal.debugPrint(string.format("Not permissible: %s", tostring(ititem.Prefab.Identifier.Value)))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -5,20 +5,14 @@ if not CLIENT then return end
|
||||
local utils = require("Cyka.utils")
|
||||
local dump = require("Cyka.dump")
|
||||
|
||||
---@class ItemLocation
|
||||
---@field inventory Barotrauma.ItemInventory
|
||||
---@field slotIndex number
|
||||
---@field depth number
|
||||
---@field maxFits number
|
||||
|
||||
-- The resulting item tree is a table where the key is an ID of an item
|
||||
-- And the value is an object that represents where that item is located
|
||||
-- In our inventory
|
||||
-- Special case are empty slots where any item fits
|
||||
---@param inventory Barotrauma.ItemInventory
|
||||
---@param itemTree table<string, ItemLocation[]>
|
||||
---@param depth number
|
||||
---@return table<string, ItemLocation[]>
|
||||
---@param inventory Barotrauma.Inventory
|
||||
---@param itemTree? table<string, InventorySlot[]>
|
||||
---@param depth? number
|
||||
---@return table<string, InventorySlot[]>
|
||||
local function buildItemTree(inventory, itemTree, depth)
|
||||
itemTree = itemTree or {}
|
||||
depth = depth or 0
|
||||
@@ -29,38 +23,24 @@ local function buildItemTree(inventory, itemTree, depth)
|
||||
|
||||
-- One slot can have one item but multiple of it
|
||||
-- The number of an item in a slot is #slot.items
|
||||
for slotIndex, slot in ipairs(inventory.slots) do
|
||||
-- MyModGlobal.debugPrint(string.format("Building item tree for inventory at slot index: %d", slotIndex))
|
||||
-- MyModGlobal.debugPrint(string.format("Slot %d has %d items", slotIndex, #slot.items))
|
||||
if #slot.items == 0 then
|
||||
for slotIndex, _ in ipairs(inventory.slots) do
|
||||
local invSlot = MyModGlobal.InventorySlot.new(inventory, slotIndex):with({ depth = depth })
|
||||
if not invSlot.item then
|
||||
-- MyModGlobal.debugPrint(string.format("Slot %d is empty, adding to itemTree as 'empty'", slotIndex))
|
||||
itemTree['empty'] = itemTree['empty'] or {}
|
||||
itemTree['empty'][#itemTree['empty'] + 1] = {
|
||||
inventory = inventory,
|
||||
slotIndex = slotIndex - 1,
|
||||
maxFits = 60,
|
||||
depth = depth
|
||||
}
|
||||
itemTree['empty'][#itemTree['empty'] + 1] = invSlot
|
||||
-- MyModGlobal.debugPrint(string.format("Added empty slot to itemTree at index: %d", slotIndex))
|
||||
else
|
||||
---@type Barotrauma.Item
|
||||
local item = slot.items[1]
|
||||
local identifier = item.Prefab.Identifier.Value
|
||||
-- MyModGlobal.debugPrint(string.format("Found item: %s with identifier: %s", item.Name, identifier))
|
||||
local identifier = invSlot.item.Prefab.Identifier.Value
|
||||
itemTree[identifier] = itemTree[identifier] or {}
|
||||
-- We DO want even slots with maxFits = 0
|
||||
-- Because that indicates that we DO HAVE the item
|
||||
-- At all
|
||||
-- And based on that we decide to move it
|
||||
itemTree[identifier][#itemTree[identifier] + 1] = {
|
||||
inventory = inventory,
|
||||
slotIndex = slotIndex - 1,
|
||||
maxFits = slot.HowManyCanBePut(item.Prefab),
|
||||
depth = depth
|
||||
}
|
||||
itemTree[identifier][#itemTree[identifier] + 1] = invSlot
|
||||
-- MyModGlobal.debugPrint(string.format("Added item to itemTree under identifier: %s", identifier))
|
||||
|
||||
local tags = item.Prefab.Tags
|
||||
local tags = invSlot.item.Prefab.Tags
|
||||
local shouldSuss = false
|
||||
for tag in tags do
|
||||
if tag.value:find("container") then
|
||||
@@ -71,7 +51,7 @@ local function buildItemTree(inventory, itemTree, depth)
|
||||
|
||||
if shouldSuss then
|
||||
-- MyModGlobal.debugPrint(string.format("Searching inside %s for nested containers", item.Name))
|
||||
buildItemTree(item.OwnInventory, itemTree, depth + 1)
|
||||
buildItemTree(invSlot.item.OwnInventory, itemTree, depth + 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -81,19 +61,20 @@ local function buildItemTree(inventory, itemTree, depth)
|
||||
end
|
||||
|
||||
-- We would like to fill larger stacks first
|
||||
---@param itemTree table<string, ItemLocation[]>
|
||||
---@return table<string, ItemLocation[]>
|
||||
---@param itemTree table<string, InventorySlot[]>
|
||||
---@return table<string, InventorySlot[]>
|
||||
local function sortItemTree(itemTree)
|
||||
for _, item in pairs(itemTree) do
|
||||
table.sort(item, function(a, b)
|
||||
---@cast a ItemLocation
|
||||
---@cast b ItemLocation
|
||||
---@cast a InventorySlot
|
||||
---@cast b InventorySlot
|
||||
local maxfitsA, maxfitsB = a:maxFits(), b:maxFits()
|
||||
if a.depth ~= b.depth then
|
||||
return a.depth < b.depth
|
||||
elseif a.maxFits ~= b.maxFits then
|
||||
return a.maxFits > b.maxFits
|
||||
elseif maxfitsA ~= maxfitsB then
|
||||
return maxfitsA > maxfitsB
|
||||
else
|
||||
return a.slotIndex < b.slotIndex
|
||||
return a.slotIndex0 < b.slotIndex0
|
||||
end
|
||||
end)
|
||||
end
|
||||
@@ -102,16 +83,16 @@ local function sortItemTree(itemTree)
|
||||
end
|
||||
|
||||
---@param item Barotrauma.Item
|
||||
---@param itemTree table<string, ItemLocation[]>
|
||||
---@param itemTree table<string, InventorySlot[]>
|
||||
---@param force boolean
|
||||
---@return string
|
||||
---@return string?
|
||||
local function tryMoveItem(item, itemTree, force)
|
||||
-- MyModGlobal.debugPrint(string.format("Attempting to move item: %s", item.Prefab.Identifier.Value))
|
||||
force = force or false
|
||||
local location = itemTree[item.Prefab.Identifier.Value]
|
||||
if not location and not force then
|
||||
-- MyModGlobal.debugPrint("No locations for item, not stacking (not forced)")
|
||||
return nil, "No locations for item, not stacking (not forced)"
|
||||
return "No locations for item, not stacking (not forced)"
|
||||
end
|
||||
-- MyModGlobal.debugPrint(string.format("Attempting to move item: %s", item.Prefab.Identifier.Value))
|
||||
-- MyModGlobal.DumpTable(location)
|
||||
@@ -121,23 +102,13 @@ local function tryMoveItem(item, itemTree, force)
|
||||
-- First try to move to existing stacks
|
||||
for _, itemLocation in ipairs(location) do
|
||||
-- We cannot stack items with decreased condition
|
||||
local canBePut = itemLocation.inventory.CanBePutInSlot(item.Prefab, itemLocation.slotIndex, item.Condition)
|
||||
-- MyModGlobal.debugPrint(string.format("Can be put in slot %d: %s", itemLocation.slotIndex, tostring(canBePut)))
|
||||
|
||||
if itemLocation.maxFits > 0 and canBePut then
|
||||
moved = moved or
|
||||
itemLocation.inventory.TryPutItem(item, itemLocation.slotIndex, false, true, Character.Controlled,
|
||||
true)
|
||||
if moved then
|
||||
itemLocation.maxFits = itemLocation.inventory.HowManyCanBePut(item.Prefab, itemLocation.slotIndex)
|
||||
local canFit = itemLocation:canFit(item.Prefab)
|
||||
if canFit then
|
||||
-- There's no more guess work, if we call move then we must be sure we can move
|
||||
utils.enqueueMove(item, itemLocation)
|
||||
moved = true
|
||||
break
|
||||
end
|
||||
-- if moved then
|
||||
-- MyModGlobal.debugPrint(string.format("Moved item to existing stack at slot index: %d", itemLocation .slotIndex))
|
||||
-- else
|
||||
-- MyModGlobal.debugPrint(string.format("Failed to move item to existing stack at slot index: %d", itemLocation .slotIndex))
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -149,29 +120,17 @@ local function tryMoveItem(item, itemTree, force)
|
||||
return "No empty slots found"
|
||||
end
|
||||
for _, itemLocation in ipairs(itemTree['empty']) do
|
||||
local maxFits = itemLocation.maxFits
|
||||
-- These empty slots are not guranteed to be empty, ironically
|
||||
-- After we insert an item into one it's no longer empty
|
||||
-- But it still is in the empty table
|
||||
-- So we want to make sure we can insert our item
|
||||
-- Into the maybe empty slots
|
||||
itemLocation.maxFits = itemLocation.inventory.HowManyCanBePut(item.Prefab, itemLocation.slotIndex)
|
||||
|
||||
if maxFits > 0 then
|
||||
-- MyModGlobal.debugPrint(string.format("Trying to move item to empty slot at index: %d", itemLocation.slotIndex))
|
||||
moved = moved or
|
||||
itemLocation.inventory.TryPutItem(item, itemLocation.slotIndex, true, false, Character.Controlled,
|
||||
true)
|
||||
if moved then
|
||||
itemLocation.maxFits = itemLocation.inventory.HowManyCanBePut(item.Prefab, itemLocation.slotIndex)
|
||||
local canFit = itemLocation:canFit(item.Prefab)
|
||||
if canFit then
|
||||
utils.enqueueMove(item, itemLocation)
|
||||
moved = true
|
||||
break
|
||||
end
|
||||
-- if moved then
|
||||
-- MyModGlobal.debugPrint(string.format("Moved item to empty slot at index: %d", itemLocation.slotIndex))
|
||||
-- else
|
||||
-- MyModGlobal.debugPrint(string.format("Failed to move item to empty slot at index: %d", itemLocation.slotIndex))
|
||||
-- end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -185,8 +144,8 @@ local function tryMoveItem(item, itemTree, force)
|
||||
end
|
||||
|
||||
---@param items Barotrauma.Item[]
|
||||
---@param itemTree table<string, ItemLocation[]>
|
||||
---@param force boolean
|
||||
---@param itemTree table<string, InventorySlot[]>
|
||||
---@param force? boolean
|
||||
---@return string[]
|
||||
local function tryMoveItems(items, itemTree, force)
|
||||
force = force or false
|
||||
@@ -202,7 +161,7 @@ local function tryMoveItems(items, itemTree, force)
|
||||
end
|
||||
|
||||
---@param character Barotrauma.Character
|
||||
---@return table<string, ItemLocation[]>, string
|
||||
---@return table<string, InventorySlot[]>, string?
|
||||
local function tryBuildCharacterItemTree(character)
|
||||
local itemTree = {}
|
||||
-- MyModGlobal.debugPrint(string.format("Preparing to stack items into the bag..."))
|
||||
@@ -267,7 +226,7 @@ end
|
||||
-- Function to quickly stack items from inventory to containers
|
||||
-- 6 and 7 are hands
|
||||
-- 9..18 are main slots
|
||||
local inventorySlotsToStack = { 6, 7, }
|
||||
-- local inventorySlotsToStack = { 6, 7, }
|
||||
-- local inventorySlotsToStack = { 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }
|
||||
---@param character Barotrauma.Character
|
||||
local function quickStackItems(character)
|
||||
@@ -276,12 +235,10 @@ local function quickStackItems(character)
|
||||
-- Then stack all items from the parent inventory into the mouseover container
|
||||
local mouseover = utils.getFirstSlotUnderCursor()
|
||||
if mouseover then
|
||||
---@type Barotrauma.Item
|
||||
local slotItem = mouseover.slot.items[1]
|
||||
local itemInventory = slotItem.OwnInventory
|
||||
local itemInventory = mouseover.item.OwnInventory
|
||||
if itemInventory then
|
||||
MyModGlobal.debugPrint(string.format("Item inventory found: %s", tostring(itemInventory)))
|
||||
local err = stackToContainer(slotItem)
|
||||
local err = stackToContainer(mouseover.item)
|
||||
if err then
|
||||
MyModGlobal.debugPrint(string.format("Error stacking items to container: %s", err))
|
||||
end
|
||||
@@ -341,6 +298,7 @@ local function quickStackItems(character)
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- TODO: enqueueOpenContainers?
|
||||
local openContainers = utils.getOpenContainers()
|
||||
for _, container in ipairs(openContainers) do
|
||||
local inventories = container.OwnInventories
|
||||
@@ -367,48 +325,38 @@ local function stackToCursor()
|
||||
return
|
||||
end
|
||||
|
||||
local item, slot
|
||||
local function predicate(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
|
||||
-- UPDATE: OK well that was a stupid idea, it returns an error for other shit as well
|
||||
-- What other shit? Wish I knew
|
||||
-- So we'll use HowManyCanBePut instead...
|
||||
local moved = slot.inventory.TryPutItem(ititem, slot.slotIndex - 1, false, true, Character.Controlled,
|
||||
true)
|
||||
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
|
||||
local maxFits = slot.inventory.HowManyCanBePut(ititem.Prefab, slot.slotIndex - 1)
|
||||
if maxFits <= 0 then
|
||||
MyModGlobal.debugPrint(string.format("Item %s has no more fits in slot %d", ititem.Name, slot
|
||||
.slotIndex - 1))
|
||||
return false, true
|
||||
for _, invSlot in ipairs(slots) do
|
||||
if invSlot:canFit(ititem.Prefab) then
|
||||
utils.enqueueMove(ititem, invSlot)
|
||||
end
|
||||
end
|
||||
local haveSpace = false
|
||||
for _, invSlot in ipairs(slots) do
|
||||
-- Empty slot or has space for more items
|
||||
if (invSlot.stackSize < invSlot.maxStackSize) or not invSlot.item then
|
||||
haveSpace = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not haveSpace then return false, true end
|
||||
end
|
||||
|
||||
---@type EnqueueOptions
|
||||
local options = {
|
||||
itemPredicate = predicate,
|
||||
recurse = true,
|
||||
}
|
||||
-- We gotta do a little juggling...
|
||||
for _, sslot in ipairs(slots) do
|
||||
slot = sslot
|
||||
local items
|
||||
if not slot.slot.items or #slot.slot.items == 0 then
|
||||
for _, invSlot in ipairs(slots) do
|
||||
if not invSlot.item then
|
||||
MyModGlobal.debugPrint("No items in slot")
|
||||
goto continue
|
||||
end
|
||||
|
||||
item = slot.slot.items[1]
|
||||
MyModGlobal.debugPrint(string.format("Stacking all player items to %s", item.Prefab.Identifier.Value))
|
||||
items = {}
|
||||
utils.enqueueAllPlayerItems(items, predicate)
|
||||
utils.enqueueOpenContainers(items, predicate)
|
||||
MyModGlobal.debugPrint(string.format("Stacking all player items to %s", invSlot.item.Prefab.Identifier.Value))
|
||||
utils.enqueuePlayerItems(options)
|
||||
utils.enqueueOpenContainers(options)
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
@@ -420,45 +368,38 @@ local function stackAllToCursor()
|
||||
return
|
||||
end
|
||||
|
||||
for _, slot in ipairs(slots) do
|
||||
local item, predicate
|
||||
if not slot.slot.items or #slot.slot.items == 0 then
|
||||
local function predicate(ititem)
|
||||
for _, invSlot in ipairs(slots) do
|
||||
if invSlot:canFit(ititem.Prefab) then
|
||||
utils.enqueueMove(ititem, invSlot)
|
||||
end
|
||||
end
|
||||
local haveSpace = false
|
||||
for _, invSlot in ipairs(slots) do
|
||||
-- Empty slot or has space for more items
|
||||
if (invSlot.stackSize < invSlot.maxStackSize) or not invSlot.item then
|
||||
haveSpace = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not haveSpace then return false, true end
|
||||
end
|
||||
|
||||
---@type EnqueueOptions
|
||||
local options = {
|
||||
itemPredicate = predicate,
|
||||
recurse = true,
|
||||
}
|
||||
|
||||
for _, invSlot in ipairs(slots) do
|
||||
if not invSlot.item then
|
||||
MyModGlobal.debugPrint("No items in slot")
|
||||
goto continue
|
||||
end
|
||||
|
||||
item = slot.slot.items[1]
|
||||
MyModGlobal.debugPrint(string.format("Stacking all items to %s", item.Prefab.Identifier.Value))
|
||||
predicate = 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
|
||||
-- UPDATE: OK well that was a stupid idea, it returns an error for other shit as well
|
||||
-- What other shit? Wish I knew
|
||||
-- So we'll use HowManyCanBePut instead...
|
||||
local moved = slot.inventory.TryPutItem(ititem, slot.slotIndex - 1, false, true, Character.Controlled,
|
||||
true)
|
||||
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
|
||||
local maxFits = slot.inventory.HowManyCanBePut(ititem.Prefab, slot.slotIndex - 1)
|
||||
if maxFits <= 0 then
|
||||
MyModGlobal.debugPrint(string.format("Item %s has no more fits in slot %d", ititem.Name, slot
|
||||
.slotIndex - 1))
|
||||
return false, true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
utils.enqueueAllSubmarineItems({}, predicate)
|
||||
utils.enqueueAllPlayerItems({}, predicate)
|
||||
|
||||
MyModGlobal.debugPrint(string.format("Stacking all items to %s", invSlot.item.Prefab.Identifier.Value))
|
||||
utils.enqueueSubmarineItems(options)
|
||||
utils.enqueuePlayerItems(options)
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
@@ -2,106 +2,49 @@
|
||||
if not CLIENT then return end
|
||||
|
||||
local utils = require("Cyka.utils")
|
||||
local dump = require("Cyka.dump")
|
||||
|
||||
---@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 = {}
|
||||
---@param invSlot InventorySlot
|
||||
local function tryUnloadSlot(invSlot)
|
||||
---@type table<Barotrauma.ItemPrefab, boolean>
|
||||
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
|
||||
|
||||
local itemInventory = invSlot.item.OwnInventory
|
||||
if not itemInventory then
|
||||
MyModGlobal.debugPrint("No inventory for item")
|
||||
return
|
||||
end
|
||||
MyModGlobal.debugPrint(string.format("Enqueuing inventory %s", tostring(itemInventory)))
|
||||
|
||||
local toUnload = utils.enqueueInventory(itemInventory, {
|
||||
itemPredicate = function(item)
|
||||
toUnloadByPrefab[item.Prefab] = true
|
||||
return true
|
||||
end,
|
||||
recurse = false,
|
||||
})
|
||||
MyModGlobal.debugPrint(string.format("Moving %d items to unload %s", #toUnload.itemQueue, tostring(invSlot.item)))
|
||||
|
||||
-- Where can we put our toUnload items?
|
||||
local nearbySlots = findSlotsThat(slot.inventory, function(islot)
|
||||
local isEmpty = #islot.slot.items == 0
|
||||
local nearbySlots = invSlot:getNearbySlots(function(islot)
|
||||
local isEmpty = not islot.item
|
||||
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
|
||||
for prefab, _ in pairs(toUnloadByPrefab) do
|
||||
local canFit = islot:canFit(prefab)
|
||||
if canFit then return true end
|
||||
end
|
||||
return false
|
||||
end)
|
||||
-- print("Before sorting:")
|
||||
-- dump(nearbySlots)
|
||||
MyModGlobal.debugPrint(string.format("Into %d nearby slots", #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 _, iitem in ipairs(toUnload.itemQueue) do
|
||||
for _, nearbySlot in ipairs(nearbySlots) do
|
||||
local canAccept = nearbySlot.inventory.CanBePutInSlot(iitem.Prefab, nearbySlot.slotIndex)
|
||||
local canAccept = nearbySlot:canFit(iitem.Prefab)
|
||||
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
|
||||
utils.enqueueMove(iitem, nearbySlot)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -110,12 +53,12 @@ end
|
||||
local function tryUnloadCursorItem()
|
||||
local slots, err = utils.getSlotsUnderCursor()
|
||||
if err then
|
||||
-- MyModGlobal.debugPrint(string.format("Error getting inventory slot: %s", err))
|
||||
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")
|
||||
MyModGlobal.debugPrint("No items in slot")
|
||||
return
|
||||
end
|
||||
|
||||
|
@@ -1,15 +1,290 @@
|
||||
-- luacheck: globals Character MyModGlobal
|
||||
-- luacheck: globals Character MyModGlobal Timer _
|
||||
-- luacheck: max line length 420
|
||||
|
||||
---@class ItemRefs
|
||||
---@field item Barotrauma.Item
|
||||
---@field inventory Barotrauma.ItemInventory
|
||||
---@field slot Barotrauma.ItemInventory.Slot
|
||||
---@class Barotrauma.Inventory.ItemSlot
|
||||
---@field items Barotrauma.Item[]
|
||||
|
||||
---@class HollowInventorySlot
|
||||
---@field inventory? Barotrauma.Inventory
|
||||
---@field slotIndex1? number Lua based item slots
|
||||
---@field slotIndex0? number Barotrauma API based item slots
|
||||
---@field item? Barotrauma.Item
|
||||
---@field stackSize? number
|
||||
---@field maxStackSize? number
|
||||
---@field depth? number Currently almost always 0
|
||||
|
||||
-- local globalInventorySlotCache = {}
|
||||
---@class InventorySlot ---@field slot Barotrauma.Inventory.ItemSlot
|
||||
---@field inventory Barotrauma.Inventory
|
||||
---@field slotIndex1 number Lua based item slots
|
||||
---@field slotIndex0 number Barotrauma API based item slots
|
||||
---@field item Barotrauma.Item
|
||||
---@field stackSize number
|
||||
---@field maxStackSize number
|
||||
---@field depth number Currently almost always 0
|
||||
-- ---@field lastUpdated number
|
||||
MyModGlobal.InventorySlot = {
|
||||
---@param inventory Barotrauma.Inventory
|
||||
---@param slotIndex1 number
|
||||
---@return InventorySlot
|
||||
new = function(inventory, slotIndex1)
|
||||
local self = setmetatable({}, {
|
||||
__index = MyModGlobal.InventorySlot
|
||||
})
|
||||
self.inventory = inventory
|
||||
self.slotIndex1 = slotIndex1
|
||||
self.slotIndex0 = slotIndex1 - 1
|
||||
self.stackSize = 0
|
||||
self.maxStackSize = 0
|
||||
self.depth = 0
|
||||
-- self:update()
|
||||
|
||||
if inventory and inventory.slots and #inventory.slots > 0 then
|
||||
self.slot = inventory.slots[slotIndex1]
|
||||
end
|
||||
if self.slot and self.slot.items and #self.slot.items > 0 then
|
||||
self.item = self.slot.items[1]
|
||||
self.stackSize = #self.slot.items
|
||||
-- At this point inventory has to exist
|
||||
-- If it didn't slot wouldn't either and then this wouldn't either
|
||||
self.maxStackSize = self.item.Prefab.GetMaxStackSize(inventory)
|
||||
end
|
||||
|
||||
return self
|
||||
end,
|
||||
--- A very weird builder indeed
|
||||
---@param self InventorySlot
|
||||
---@param other HollowInventorySlot
|
||||
with = function(self, other)
|
||||
if other.inventory ~= nil then
|
||||
self.inventory = other.inventory
|
||||
end
|
||||
if other.slotIndex1 ~= nil then
|
||||
self.slotIndex1 = other.slotIndex1
|
||||
end
|
||||
if other.slotIndex0 ~= nil then
|
||||
self.slotIndex0 = other.slotIndex0
|
||||
end
|
||||
if other.item ~= nil then
|
||||
self.item = other.item
|
||||
end
|
||||
if other.stackSize ~= nil then
|
||||
self.stackSize = other.stackSize
|
||||
end
|
||||
if other.maxStackSize ~= nil then
|
||||
self.maxStackSize = other.maxStackSize
|
||||
end
|
||||
if other.depth ~= nil then
|
||||
self.depth = other.depth
|
||||
end
|
||||
return self
|
||||
end,
|
||||
---@param self InventorySlot
|
||||
---@param item Barotrauma.Item
|
||||
pretendMoved = function(self, item)
|
||||
if not self.inventory then
|
||||
MyModGlobal.debugPrint("Error pretending moved but it was moved to nil inventory")
|
||||
return
|
||||
end
|
||||
-- Slot was previously empty, we want to figure out its max stack for the new item
|
||||
if not self.item then
|
||||
self.maxStackSize = item.Prefab.GetMaxStackSize(self.inventory)
|
||||
end
|
||||
self.item = item
|
||||
self.stackSize = self.stackSize + 1
|
||||
end,
|
||||
update = function(self)
|
||||
-- self.lastUpdated = Timer.GetTime()
|
||||
if not self.inventory then
|
||||
MyModGlobal.debugPrint("Error updating inventory slot, inventory not found")
|
||||
return
|
||||
end
|
||||
if not self.inventory.slots then
|
||||
MyModGlobal.debugPrint("Error updating inventory slot, inventory has no slots")
|
||||
return
|
||||
end
|
||||
|
||||
local slot = self.inventory.slots[self.slotIndex1]
|
||||
if not slot then
|
||||
MyModGlobal.debugPrint("Error updating inventory slot, slot not found")
|
||||
return
|
||||
end
|
||||
self.slot = slot
|
||||
if not slot.items or #slot.items == 0 then
|
||||
-- MyModGlobal.debugPrint("Error updating inventory slot, slot is empty")
|
||||
return
|
||||
end
|
||||
self.item = slot.items[1]
|
||||
self.stackSize = #slot.items
|
||||
self.maxStackSize = self.item.Prefab.GetMaxStackSize(self.inventory)
|
||||
end,
|
||||
__tostring = function(self)
|
||||
return string.format(
|
||||
"InventorySlot(inventory=%s, item=%s, stackSize=%d, maxStackSize=%d, slotIndex1=%d, slotIndex0=%d)",
|
||||
tostring(self.inventory), tostring(self.item), self.stackSize, self.maxStackSize, self.slotIndex1,
|
||||
self.slotIndex0)
|
||||
end,
|
||||
---@param self InventorySlot
|
||||
---@param predicate? fun(slot: InventorySlot): boolean
|
||||
---@return InventorySlot[]
|
||||
getNearbySlots = function(self, predicate)
|
||||
predicate = predicate or function() return true end
|
||||
|
||||
local slotsPerRow = 900
|
||||
local ok, err = pcall(function()
|
||||
slotsPerRow = self.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
|
||||
|
||||
local slots = {}
|
||||
for slotIndex, _ in ipairs(self.inventory.slots) do
|
||||
local inventorySlot = MyModGlobal.InventorySlot.new(self.inventory, slotIndex)
|
||||
if predicate(inventorySlot) then
|
||||
slots[#slots + 1] = inventorySlot
|
||||
end
|
||||
end
|
||||
|
||||
local slotx, sloty = getGridPos(self.slotIndex0)
|
||||
table.sort(slots, function(a, b)
|
||||
local ax, ay = getGridPos(a.slotIndex0)
|
||||
local bx, by = getGridPos(b.slotIndex0)
|
||||
|
||||
-- Chebyshev distance
|
||||
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.slotIndex0 < b.slotIndex0
|
||||
end
|
||||
return distA < distB
|
||||
end)
|
||||
|
||||
return slots
|
||||
end,
|
||||
--- TODO: What about item condition?
|
||||
---@param self InventorySlot
|
||||
---@param itemPrefab Barotrauma.ItemPrefab
|
||||
---@return number
|
||||
howManyCanFit = function(self, itemPrefab)
|
||||
-- There is an item in the slot and it's not stackable with itemPrefab
|
||||
if self.item and not self.item.Prefab.Equals(itemPrefab) then
|
||||
return 0
|
||||
end
|
||||
-- The slot is empty - we can fit as many as the game tells us
|
||||
if not self.item then
|
||||
return itemPrefab.GetMaxStackSize(self.inventory)
|
||||
end
|
||||
-- The slot has an item that is stackable with itemPrefab
|
||||
-- We can fit as many as to fill the stack
|
||||
return self.maxStackSize - self.stackSize
|
||||
end,
|
||||
---@param self InventorySlot
|
||||
---@param itemPrefab Barotrauma.ItemPrefab
|
||||
---@return boolean
|
||||
canFit = function(self, itemPrefab)
|
||||
return self:howManyCanFit(itemPrefab) > 0
|
||||
end,
|
||||
---@param self InventorySlot
|
||||
---@return number
|
||||
maxFits = function(self)
|
||||
return self.maxStackSize - self.stackSize
|
||||
end
|
||||
-- hash = function(self)
|
||||
-- return string.format("%s:%d:%d", tostring(self.inventory), self.slotIndex1, self.slotIndex0)
|
||||
-- end
|
||||
}
|
||||
|
||||
---@class ItemMoveRequest
|
||||
---@field what Barotrauma.Item
|
||||
---@field where InventorySlot
|
||||
---@field allowSwap boolean
|
||||
---@field allowCombine boolean
|
||||
|
||||
local enqueueMove
|
||||
do
|
||||
-- A bit of cheeky scoping
|
||||
local enabled = true
|
||||
---@type ItemMoveRequest[]
|
||||
local itemMoveQueue = {}
|
||||
---@type table<Barotrauma.Item, boolean>
|
||||
local itemLookup = {}
|
||||
local rate = 500
|
||||
local perIteration = 30
|
||||
local noQueue = true
|
||||
-- rate / 1000 is ms to seconds and *perIteraion is number of items per second
|
||||
local maxQueueSize = 10 * (1000 / rate * perIteration)
|
||||
local function processQueue()
|
||||
if noQueue then return end
|
||||
-- MyModGlobal.debugPrint("Processing queue")
|
||||
Timer.Wait(processQueue, rate)
|
||||
if not enabled then return end
|
||||
if #itemMoveQueue == 0 then return end
|
||||
local iterations = math.min(perIteration, #itemMoveQueue)
|
||||
for _ = 1, iterations do
|
||||
---@type ItemMoveRequest
|
||||
local moveRequest = table.remove(itemMoveQueue, 1)
|
||||
|
||||
-- TODO: Maybe try and figure out if we CAN put A into B
|
||||
moveRequest.allowCombine = moveRequest.allowCombine or false
|
||||
moveRequest.allowSwap = moveRequest.allowSwap or false
|
||||
local success = moveRequest.where.inventory.TryPutItem(moveRequest.what, moveRequest.where.slotIndex0,
|
||||
moveRequest.allowSwap, moveRequest.allowCombine, nil)
|
||||
if not success then
|
||||
MyModGlobal.debugPrint(string.format("Failed moving item from %s to %s", tostring(moveRequest.what),
|
||||
tostring(moveRequest.where:__tostring())))
|
||||
end
|
||||
itemLookup[moveRequest.what] = nil
|
||||
end
|
||||
end
|
||||
processQueue()
|
||||
|
||||
---@param what Barotrauma.Item
|
||||
---@param where InventorySlot
|
||||
---@param allowSwap? boolean
|
||||
---@param allowCombine? boolean
|
||||
enqueueMove = function(what, where, allowSwap, allowCombine)
|
||||
allowCombine = allowCombine == true
|
||||
allowSwap = allowSwap == true
|
||||
if noQueue then
|
||||
local success = where.inventory.TryPutItem(what, where.slotIndex0,
|
||||
allowSwap, allowCombine, nil)
|
||||
if not success then
|
||||
MyModGlobal.debugPrint(string.format("Failed moving item from %s to %s", tostring(what),
|
||||
tostring(where:__tostring())))
|
||||
end
|
||||
where:pretendMoved(what)
|
||||
else
|
||||
if #itemMoveQueue >= maxQueueSize then
|
||||
MyModGlobal.debugPrint("Queue is full, skipping move")
|
||||
return
|
||||
end
|
||||
if itemLookup[what] then
|
||||
MyModGlobal.debugPrint("Item is already in the queue, skipping move")
|
||||
return
|
||||
end
|
||||
MyModGlobal.debugPrint(string.format("Enqueuing move from %s to %s, now in queue %d/%d", tostring(what),
|
||||
tostring(where:__tostring()), #itemMoveQueue, maxQueueSize))
|
||||
table.insert(itemMoveQueue, {
|
||||
what = what,
|
||||
where = where,
|
||||
allowSwap = allowSwap or false,
|
||||
allowCombine = allowCombine ~= false,
|
||||
})
|
||||
itemLookup[what] = true
|
||||
-- We will very optimistically pretend that this will 100% for sure work
|
||||
where:pretendMoved(what)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@class InventorySlot
|
||||
---@field slot Barotrauma.ItemSlot
|
||||
---@field inventory Barotrauma.ItemInventory
|
||||
---@field slotIndex number
|
||||
|
||||
---@return Barotrauma.Item[], string?
|
||||
local function getOpenContainers()
|
||||
@@ -23,7 +298,9 @@ end
|
||||
---@return Barotrauma.Item, string?
|
||||
local function getFirstOpenContainer()
|
||||
local containers, err = getOpenContainers()
|
||||
---@diagnostic disable-next-line: return-type-mismatch
|
||||
if err then return nil, err end
|
||||
---@diagnostic disable-next-line: return-type-mismatch
|
||||
if #containers == 0 then return nil, "No open containers" end
|
||||
return containers[1], nil
|
||||
end
|
||||
@@ -32,194 +309,232 @@ end
|
||||
-- And enqueueItem calls enqueueInventory
|
||||
-- So unless we define them both before using them
|
||||
-- We will get an error saying either is undefined
|
||||
-- TODO: Rework these enqueue functions to accept a params object
|
||||
-- That will house all optional parameters
|
||||
-- And in that include recurse boolean
|
||||
|
||||
---@class ItemRefs
|
||||
---@field item Barotrauma.Item
|
||||
---@field inventory Barotrauma.Inventory
|
||||
---@field slot Barotrauma.Inventory.ItemSlot
|
||||
---@field slotIndex1 number
|
||||
|
||||
---@class EnqueueOptions
|
||||
---@field itemQueue? Barotrauma.Item[]
|
||||
---@field slotQueue? Barotrauma.Inventory.ItemSlot[]
|
||||
---@field inventoryQueue? Barotrauma.Inventory[]
|
||||
---@field itemPredicate? fun(item: Barotrauma.Item, itemRef: ItemRefs): boolean
|
||||
---@field slotPredicate? fun(slot: Barotrauma.Inventory.ItemSlot, itemRef: ItemRefs): boolean
|
||||
---@field inventoryPredicate? fun(inventory: Barotrauma.Inventory, itemRef: ItemRefs): boolean
|
||||
---@field loadRefs? boolean
|
||||
---@field itemRef? ItemRefs
|
||||
---@field recurse? boolean
|
||||
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions
|
||||
local function ensureOptionsDefaults(options)
|
||||
options = options or {}
|
||||
options.itemQueue = options.itemQueue or {}
|
||||
options.slotQueue = options.slotQueue or {}
|
||||
options.inventoryQueue = options.inventoryQueue or {}
|
||||
options.itemPredicate = options.itemPredicate or function() return true end
|
||||
options.slotPredicate = options.slotPredicate or function() return true end
|
||||
options.inventoryPredicate = options.inventoryPredicate or function() return true end
|
||||
options.loadRefs = options.loadRefs == true
|
||||
options.itemRef = options.itemRef or {}
|
||||
options.recurse = options.recurse == true
|
||||
return options
|
||||
end
|
||||
|
||||
local enqueueItem
|
||||
local enqueueSlot
|
||||
local enqueueInventory
|
||||
local enqueuePlayerItems
|
||||
local enqueueOpenContainers
|
||||
local allPlayerItems
|
||||
local allSubmarineItems
|
||||
local allOwnedItems
|
||||
local _
|
||||
|
||||
---@alias FilterPredicate fun(item: Barotrauma.Item, inventoryRef?: Barotrauma.ItemInventory, slotRef: Barotrauma.ItemInventory.Slot): boolean
|
||||
|
||||
-- Loading refs is optional because it MAY have a performance impact
|
||||
local enqueueSubmarineItems
|
||||
local enqueueAllOwnedItems
|
||||
|
||||
do
|
||||
---@param item Barotrauma.Item
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@param loadRefs? boolean
|
||||
---@param itemRef? ItemRefs
|
||||
---@return Barotrauma.Item[], string?
|
||||
enqueueItem = function(item, queue, predicate, loadRefs, itemRef)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
itemRef = itemRef or {}
|
||||
-- debugPrint(string.format("Enqueuing item: %s", item.Prefab.Identifier.Value))
|
||||
-- local err
|
||||
-- This should make it breadth first, right...?
|
||||
-- No, not yet...
|
||||
if not item then return queue, "No item" end
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueItem = function(item, options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
if not item then return options, "No item" end
|
||||
|
||||
local ok, stop = predicate(item, itemRef)
|
||||
local ok, stop = options.itemPredicate(item, options.itemRef)
|
||||
if ok then
|
||||
queue[#queue + 1] = item
|
||||
options.itemQueue[#options.itemQueue + 1] = item
|
||||
end
|
||||
if stop then return queue, "Stop" end
|
||||
if stop then return options, "Stop" end
|
||||
|
||||
local err
|
||||
if item.OwnInventory then
|
||||
-- As far as I know every item has only one inventory
|
||||
-- Only machines have multiple
|
||||
-- So inventrorY should be fine here
|
||||
-- debugPrint("Item has its own inventory, enqueuing inventory...")
|
||||
if loadRefs then
|
||||
itemRef.item = item
|
||||
queue, _ = enqueueInventory(item.OwnInventory, queue, predicate, loadRefs, itemRef)
|
||||
if options.recurse then
|
||||
if options.loadRefs then
|
||||
options.itemRef.item = item
|
||||
options.inventoryQueue, err = enqueueInventory(item.OwnInventory, options)
|
||||
else
|
||||
queue, _ = enqueueInventory(item.OwnInventory, queue, predicate, itemRef)
|
||||
options.inventoryQueue, err = enqueueInventory(item.OwnInventory, options)
|
||||
end
|
||||
-- if err then
|
||||
-- debugPrint(string.format("Error enqueuing inventory: %s", err))
|
||||
-- end
|
||||
end
|
||||
-- debugPrint(string.format("Item enqueued. Current queue size: %d", #queue))
|
||||
return queue, nil
|
||||
end
|
||||
return options, err
|
||||
end
|
||||
|
||||
---@param slot Barotrauma.ItemInventory.Slot
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@param loadRefs? boolean
|
||||
---@param itemRef? ItemRefs
|
||||
---@return Barotrauma.Item[], string?
|
||||
enqueueSlot = function(slot, queue, predicate, loadRefs, itemRef)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
itemRef = itemRef or {}
|
||||
-- debugPrint(string.format("Enqueuing slot with %d items.", #slot.items))
|
||||
-- We don't want to shadow queue
|
||||
local err
|
||||
-- If the slot is empty there's nothing to iterate
|
||||
-- And we will naturally return queue as is
|
||||
if not slot then return queue, "No slot" end
|
||||
if not slot.items then return queue, "No items" end
|
||||
---@param slot Barotrauma.Inventory.ItemSlot
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueSlot = function(slot, options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
if not slot then return options, "No slot" end
|
||||
if not slot.items then return options, "No items" end
|
||||
|
||||
local ok, stop = options.slotPredicate(slot, options.itemRef)
|
||||
if ok then
|
||||
options.slotQueue[#options.slotQueue + 1] = slot
|
||||
end
|
||||
if stop then return options, "Stop" end
|
||||
|
||||
for _, item in ipairs(slot.items) do
|
||||
-- Only the final leaf nodes decide upon the predicate
|
||||
if loadRefs then
|
||||
itemRef.slot = slot
|
||||
queue, err = enqueueItem(item, queue, predicate, loadRefs, itemRef)
|
||||
else
|
||||
queue, err = enqueueItem(item, queue, predicate)
|
||||
end
|
||||
if err then
|
||||
return queue, err
|
||||
end
|
||||
end
|
||||
-- debugPrint(string.format("Finished enqueuing slot. Current queue size: %d", #queue))
|
||||
return queue
|
||||
end
|
||||
|
||||
---@param inventory Barotrauma.ItemInventory
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@param loadRefs? boolean
|
||||
---@param itemRef? ItemRefs
|
||||
---@return Barotrauma.Item[], string?
|
||||
enqueueInventory = function(inventory, queue, predicate, loadRefs, itemRef)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
itemRef = itemRef or {}
|
||||
-- debugPrint(string.format("Enqueuing inventory with %d slots.", #inventory.slots))
|
||||
-- We redeclare err every iteration so it doesn't spill over
|
||||
local err
|
||||
if not inventory then return queue, "No inventory" end
|
||||
if not inventory.slots then return queue, "No slots" end
|
||||
|
||||
for _, slot in ipairs(inventory.slots) do
|
||||
-- Only the final leaf nodes decide upon the predicate
|
||||
if loadRefs then
|
||||
itemRef.inventory = inventory
|
||||
queue, err = enqueueSlot(slot, queue, predicate, loadRefs, itemRef)
|
||||
if options.loadRefs then
|
||||
options.itemRef.slot = slot
|
||||
options, err = enqueueItem(item, options)
|
||||
else
|
||||
queue, err = enqueueSlot(slot, queue, predicate)
|
||||
options, err = enqueueItem(item, options)
|
||||
end
|
||||
if err then
|
||||
return queue, err
|
||||
return options, err
|
||||
end
|
||||
end
|
||||
-- debugPrint(string.format("Finished enqueuing inventory. Current queue size: %d", #queue))
|
||||
return queue
|
||||
return options
|
||||
end
|
||||
|
||||
local relevantPlayerInventorySlots = { 6, 7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, MyModGlobal.BAG_SLOT }
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@param loadRefs? boolean
|
||||
---@return Barotrauma.Item[], string?
|
||||
allPlayerItems = function(queue, predicate, loadRefs)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
---@param inventory Barotrauma.Inventory
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueInventory = function(inventory, options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
if not inventory then return options, "No inventory" end
|
||||
if not inventory.slots then return options, "No slots" end
|
||||
|
||||
local ok, stop = options.inventoryPredicate(inventory, options.itemRef)
|
||||
if ok then
|
||||
options.inventoryQueue[#options.inventoryQueue + 1] = inventory
|
||||
end
|
||||
if stop then return options, "Stop" end
|
||||
|
||||
for i, slot in ipairs(inventory.slots) do
|
||||
local err
|
||||
if options.loadRefs then
|
||||
options.itemRef.inventory = inventory
|
||||
options.itemRef.slot = slot
|
||||
options.itemRef.slotIndex1 = i
|
||||
options, err = enqueueSlot(slot, options)
|
||||
else
|
||||
options, err = enqueueSlot(slot, options)
|
||||
end
|
||||
if err then
|
||||
return options, err
|
||||
end
|
||||
end
|
||||
return options
|
||||
end
|
||||
|
||||
local relevantPlayerInventorySlots = {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
}
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueuePlayerItems = function(options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
|
||||
local character = Character.Controlled
|
||||
if not character then return queue, "No character" end
|
||||
|
||||
if not character then return options, "No character" end
|
||||
local inventory = character.Inventory
|
||||
if not inventory then return queue, "No inventory" end
|
||||
if not inventory then return options, "No inventory" end
|
||||
|
||||
options.loadRefs = true
|
||||
local originalItemPredicate = options.itemPredicate or function() return true end
|
||||
options.itemPredicate = function(item)
|
||||
if not item then return false end
|
||||
local parentInventory = item.ParentInventory
|
||||
if not parentInventory then return false end
|
||||
if not parentInventory.Equals(inventory) then return false end
|
||||
return originalItemPredicate(item, options.itemRef)
|
||||
end
|
||||
local originalSlotPredicate = options.slotPredicate or function() return true end
|
||||
options.slotPredicate = function(slot, itemRef)
|
||||
if not slot then return false end
|
||||
if itemRef.slotIndex1 and relevantPlayerInventorySlots[itemRef.slotIndex1] then
|
||||
return originalSlotPredicate(slot, itemRef)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
for _, slotid in ipairs(relevantPlayerInventorySlots) do
|
||||
local slot = inventory.slots[slotid]
|
||||
local err
|
||||
options, err = enqueueInventory(inventory, options)
|
||||
if err then return options, err end
|
||||
|
||||
if not slot then goto continue end
|
||||
if #slot.items == 0 then goto continue end
|
||||
|
||||
queue, err = enqueueSlot(slot, queue, predicate, loadRefs)
|
||||
if err then return queue, err end
|
||||
|
||||
::continue::
|
||||
return options
|
||||
end
|
||||
|
||||
return queue
|
||||
end
|
||||
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@param loadRefs? boolean
|
||||
---@return Barotrauma.Item[], string?
|
||||
enqueueOpenContainers = function(queue, predicate, loadRefs)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueOpenContainers = function(options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
|
||||
local containers, err = getOpenContainers()
|
||||
if err then return queue, err end
|
||||
if err then return options, err end
|
||||
|
||||
for _, container in ipairs(containers) do
|
||||
local inventories = container.OwnInventories
|
||||
if not inventories then goto continue end
|
||||
for containerInventory in inventories do
|
||||
queue, err = enqueueInventory(containerInventory, queue, predicate, loadRefs)
|
||||
if err then return queue, err end
|
||||
options, err = enqueueInventory(containerInventory, options)
|
||||
if err then return options, err end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
|
||||
return queue
|
||||
return options
|
||||
end
|
||||
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@return Barotrauma.Item[], string?
|
||||
allSubmarineItems = function(queue, predicate)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueSubmarineItems = function(options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
|
||||
-- This only exists so predicate does not explode
|
||||
-- Even if its empty
|
||||
local itemRef = {}
|
||||
|
||||
local character = Character.Controlled
|
||||
if not character then return queue, "No character" end
|
||||
|
||||
if not character then return options, "No character" end
|
||||
local submarine = character.Submarine
|
||||
if not submarine then return queue, "No submarine" end
|
||||
if not submarine then return options, "No submarine" end
|
||||
|
||||
for item in submarine.GetItems(false) do
|
||||
-- We do NOT want to call enqueueItem here because enqueueItem
|
||||
@@ -227,32 +542,30 @@ allSubmarineItems = function(queue, predicate)
|
||||
-- And this call (GetItems) already gets all items
|
||||
-- So we would be doing double the work (at best case)
|
||||
-- It also means we won't have refs here which sucks
|
||||
local ok, stop = predicate(item, itemRef)
|
||||
local ok, stop = options.itemPredicate(item, itemRef)
|
||||
if ok then
|
||||
queue[#queue + 1] = item
|
||||
options.itemQueue[#options.itemQueue + 1] = item
|
||||
end
|
||||
if stop then return queue, "Stop" end
|
||||
if stop then return options, "Stop" end
|
||||
end
|
||||
|
||||
return queue
|
||||
return options
|
||||
end
|
||||
|
||||
---@param queue Barotrauma.Item[]
|
||||
---@param predicate? FilterPredicate
|
||||
---@param loadRefs? boolean
|
||||
---@return Barotrauma.Item[], string?
|
||||
allOwnedItems = function(queue, predicate, loadRefs)
|
||||
queue = queue or {}
|
||||
predicate = predicate or function() return true end
|
||||
---@param options EnqueueOptions
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueAllOwnedItems = function(options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
|
||||
local err
|
||||
queue, err = allPlayerItems(queue, predicate, loadRefs)
|
||||
if err then return queue, err end
|
||||
options, err = enqueuePlayerItems(options)
|
||||
if err then return options, err end
|
||||
|
||||
queue, err = allSubmarineItems(queue, predicate)
|
||||
if err then return queue, err end
|
||||
options, err = enqueueSubmarineItems(options)
|
||||
if err then return options, err end
|
||||
|
||||
return queue
|
||||
return options
|
||||
end
|
||||
end
|
||||
|
||||
-- There is actually no need to recurse deep
|
||||
@@ -260,8 +573,8 @@ end
|
||||
-- And not an item in an item in the inventory
|
||||
-- So in theory we only need to recurse 1 deep
|
||||
---@param inventory Barotrauma.Inventory
|
||||
---@param slots InventorySlot[]
|
||||
---@param depth number
|
||||
---@param slots? InventorySlot[]
|
||||
---@param depth? number
|
||||
---@return InventorySlot[], string?
|
||||
local function getMouseoverSlots(inventory, slots, depth)
|
||||
slots = slots or {}
|
||||
@@ -269,7 +582,7 @@ local function getMouseoverSlots(inventory, slots, depth)
|
||||
if depth > 1 then return slots, nil end
|
||||
|
||||
local visualSlots = inventory.visualSlots
|
||||
if not visualSlots then return nil, "Inventory has no visual slots" end
|
||||
if not visualSlots then return slots, "Inventory has no visual slots" end
|
||||
|
||||
for i, visualSlot in ipairs(visualSlots) do
|
||||
local item
|
||||
@@ -305,11 +618,8 @@ local function getMouseoverSlots(inventory, slots, depth)
|
||||
|
||||
::mouseover::
|
||||
if visualSlot:MouseOn() then
|
||||
slots[#slots + 1] = {
|
||||
inventory = inventory,
|
||||
slotIndex = i,
|
||||
slot = slot
|
||||
}
|
||||
local inventorySlot = MyModGlobal.InventorySlot.new(inventory, i)
|
||||
slots[#slots + 1] = inventorySlot
|
||||
end
|
||||
|
||||
::continue::
|
||||
@@ -320,15 +630,17 @@ end
|
||||
|
||||
---@return InventorySlot[], string?
|
||||
local function getSlotsUnderCursor()
|
||||
local slots = {}
|
||||
-- Make sure we have a controlled character
|
||||
local controlledCharacter = Character.Controlled
|
||||
if not controlledCharacter then return nil, "No controlled character" end
|
||||
if not controlledCharacter then return slots, "No controlled character" end
|
||||
|
||||
local inventory = controlledCharacter.Inventory
|
||||
if not inventory then return nil, "No inventory" end
|
||||
if not inventory then return slots, "No inventory" end
|
||||
|
||||
local mouseoverSlots, err = getMouseoverSlots(inventory)
|
||||
if err then return mouseoverSlots, err end
|
||||
local err
|
||||
slots, err = getMouseoverSlots(inventory, slots)
|
||||
if err then return slots, err end
|
||||
|
||||
-- Even if we don't get them we're still fine
|
||||
local openContainers, _ = getOpenContainers()
|
||||
@@ -337,35 +649,30 @@ local function getSlotsUnderCursor()
|
||||
for _, container in ipairs(openContainers) do
|
||||
local containerInventories = container.OwnInventories
|
||||
for containerInventory in containerInventories do
|
||||
local slot
|
||||
if not containerInventory or not containerInventory.visualSlots then
|
||||
MyModGlobal.debugPrint("Container inventory has no visual slots")
|
||||
goto continue
|
||||
end
|
||||
for i, visualSlot in ipairs(containerInventory.visualSlots) do
|
||||
if visualSlot:MouseOn() then
|
||||
slot = containerInventory.slots[i]
|
||||
mouseoverSlots[#mouseoverSlots + 1] = {
|
||||
inventory = containerInventory,
|
||||
slotIndex = i,
|
||||
slot = slot
|
||||
}
|
||||
local inventorySlot = MyModGlobal.InventorySlot.new(containerInventory, i)
|
||||
slots[#slots + 1] = inventorySlot
|
||||
end
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
end
|
||||
|
||||
return mouseoverSlots, nil
|
||||
return slots, nil
|
||||
end
|
||||
|
||||
---@return InventorySlot, string?
|
||||
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
|
||||
if err then return slots, err end
|
||||
if #slots == 0 then return slots, "No slots found under cursor" end
|
||||
for _, slot in ipairs(slots) do
|
||||
if slot.slot.items and #slot.slot.items > 0 then
|
||||
if slot.item then
|
||||
return slot
|
||||
end
|
||||
end
|
||||
@@ -376,12 +683,13 @@ return {
|
||||
enqueueItem = enqueueItem,
|
||||
enqueueSlot = enqueueSlot,
|
||||
enqueueInventory = enqueueInventory,
|
||||
enqueueAllPlayerItems = allPlayerItems,
|
||||
enqueueAllSubmarineItems = allSubmarineItems,
|
||||
enqueueAllOwnedItems = allOwnedItems,
|
||||
enqueuePlayerItems = enqueuePlayerItems,
|
||||
enqueueSubmarineItems = enqueueSubmarineItems,
|
||||
enqueueAllOwnedItems = enqueueAllOwnedItems,
|
||||
enqueueOpenContainers = enqueueOpenContainers,
|
||||
getOpenContainers = getOpenContainers,
|
||||
getFirstOpenContainer = getFirstOpenContainer,
|
||||
getSlotsUnderCursor = getSlotsUnderCursor,
|
||||
getFirstSlotUnderCursor = getFirstSlotUnderCursor,
|
||||
enqueueMove = enqueueMove,
|
||||
}
|
||||
|
Reference in New Issue
Block a user