Fix up quickload to work with the reworked queues
This commit is contained in:
@@ -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)
|
||||
|
@@ -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 = islot.slot.items and #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
|
||||
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
|
||||
|
||||
|
@@ -25,6 +25,8 @@ MyModGlobal.InventorySlot = {
|
||||
self.inventory = inventory
|
||||
self.slotIndex1 = slotIndex1
|
||||
self.slotIndex0 = slotIndex1 - 1
|
||||
self.stackSize = 0
|
||||
self.maxStackSize = 0
|
||||
-- self:update()
|
||||
|
||||
if inventory and inventory.slots and #inventory.slots > 0 then
|
||||
@@ -40,6 +42,24 @@ MyModGlobal.InventorySlot = {
|
||||
|
||||
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
|
||||
if not self.slot then
|
||||
MyModGlobal.debugPrint("Error pretending moved but it was moved to nil slot")
|
||||
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
|
||||
@@ -71,7 +91,9 @@ MyModGlobal.InventorySlot = {
|
||||
tostring(self.inventory), self.slotIndex1, self.slotIndex0, tostring(self.item), self.stackSize,
|
||||
self.maxStackSize)
|
||||
end,
|
||||
---@param self InventorySlot
|
||||
---@param predicate? fun(slot: InventorySlot): boolean
|
||||
---@return InventorySlot[]
|
||||
getNearbySlots = function(self, predicate)
|
||||
predicate = predicate or function() return true end
|
||||
|
||||
@@ -114,14 +136,44 @@ MyModGlobal.InventorySlot = {
|
||||
|
||||
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
|
||||
MyModGlobal.debugPrint(string.format(
|
||||
"%s can fit 0 of %s because it already contains an item that is not stackable with %s (%s)",
|
||||
tostring(self), tostring(itemPrefab), tostring(itemPrefab), tostring(self.item.Prefab)))
|
||||
return 0
|
||||
end
|
||||
-- The slot is empty - we can fit as many as the game tells us
|
||||
if not self.item then
|
||||
MyModGlobal.debugPrint(string.format("%s can fit %d of %s because it is empty", tostring(self),
|
||||
itemPrefab.GetMaxStackSize(self.inventory), tostring(itemPrefab)))
|
||||
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
|
||||
MyModGlobal.debugPrint(string.format("%s can fit %d of %s because it contains %d items", tostring(self),
|
||||
self.maxStackSize - self.stackSize, tostring(itemPrefab), self.stackSize))
|
||||
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
|
||||
-- hash = function(self)
|
||||
-- return string.format("%s:%d:%d", tostring(self.inventory), self.slotIndex1, self.slotIndex0)
|
||||
-- end
|
||||
}
|
||||
|
||||
---@class ItemMoveRequest
|
||||
---@field A InventorySlot
|
||||
---@field B InventorySlot
|
||||
---@field what Barotrauma.Item
|
||||
---@field where InventorySlot
|
||||
---@field allowSwap boolean
|
||||
---@field allowCombine boolean
|
||||
|
||||
@@ -131,39 +183,45 @@ do
|
||||
local enabled = true
|
||||
---@type ItemMoveRequest[]
|
||||
local itemMoveQueue = {}
|
||||
local rate = 10
|
||||
local rate = 100
|
||||
local perIteration = 6
|
||||
local function processQueue()
|
||||
MyModGlobal.debugPrint("Processing queue")
|
||||
-- MyModGlobal.debugPrint("Processing queue")
|
||||
Timer.Wait(processQueue, rate)
|
||||
if not enabled then return end
|
||||
if #itemMoveQueue == 0 then return end
|
||||
---@type ItemMoveRequest
|
||||
local moveRequest = table.remove(itemMoveQueue, 1)
|
||||
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.B.inventory.TryPutItem(moveRequest.A.item, moveRequest.B.slotIndex0,
|
||||
moveRequest.allowSwap, moveRequest.allowCombine, Character.Controlled, true)
|
||||
if not success then
|
||||
MyModGlobal.debugPrint(string.format("Failed moving item from %s to %s", tostring(moveRequest.A),
|
||||
tostring(moveRequest.B)))
|
||||
-- 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, Character.Controlled, true)
|
||||
if not success then
|
||||
MyModGlobal.debugPrint(string.format("Failed moving item from %s to %s", tostring(moveRequest.what),
|
||||
tostring(moveRequest.where)))
|
||||
end
|
||||
end
|
||||
end
|
||||
processQueue()
|
||||
|
||||
---@param A InventorySlot
|
||||
---@param B InventorySlot
|
||||
---@param allowSwap boolean
|
||||
---@param allowCombine boolean
|
||||
enqueueMove = function(A, B, allowSwap, allowCombine)
|
||||
MyModGlobal.debugPrint(string.format("Enqueuing move from %s to %s", tostring(A), tostring(B)))
|
||||
---@param what Barotrauma.Item
|
||||
---@param where InventorySlot
|
||||
---@param allowSwap? boolean
|
||||
---@param allowCombine? boolean
|
||||
enqueueMove = function(what, where, allowSwap, allowCombine)
|
||||
MyModGlobal.debugPrint(string.format("Enqueuing move from %s to %s", tostring(what), tostring(where)))
|
||||
table.insert(itemMoveQueue, {
|
||||
A = A,
|
||||
B = B,
|
||||
allowSwap = allowSwap,
|
||||
allowCombine = allowCombine,
|
||||
what = what,
|
||||
where = where,
|
||||
allowSwap = allowSwap or false,
|
||||
allowCombine = allowCombine ~= false,
|
||||
})
|
||||
-- We will very optimistically pretend that this will 100% for sure work
|
||||
where:pretendMoved(what)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -202,15 +260,15 @@ end
|
||||
---@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
|
||||
---@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
|
||||
@@ -222,9 +280,9 @@ local function ensureOptionsDefaults(options)
|
||||
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 or false
|
||||
options.loadRefs = options.loadRefs == true
|
||||
options.itemRef = options.itemRef or nil
|
||||
options.recurse = options.recurse or true
|
||||
options.recurse = options.recurse == true
|
||||
return options
|
||||
end
|
||||
|
||||
@@ -242,13 +300,13 @@ do
|
||||
---@return EnqueueOptions, string?
|
||||
enqueueItem = function(item, options)
|
||||
options = ensureOptionsDefaults(options)
|
||||
if not item then return options.itemQueue, "No item" end
|
||||
if not item then return options, "No item" end
|
||||
|
||||
local ok, stop = options.itemPredicate(item, options.itemRef)
|
||||
if ok then
|
||||
options.itemQueue[#options.itemQueue + 1] = item
|
||||
end
|
||||
if stop then return options.itemQueue, "Stop" end
|
||||
if stop then return options, "Stop" end
|
||||
|
||||
local err
|
||||
if item.OwnInventory then
|
||||
|
Reference in New Issue
Block a user