Fix up quickload to work with the reworked queues
This commit is contained in:
@@ -7,7 +7,6 @@ if SERVER then
|
|||||||
require("Cyka.xpticker")
|
require("Cyka.xpticker")
|
||||||
else
|
else
|
||||||
---@class MyModGlobal
|
---@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_NAME string
|
||||||
---@field MOD_VERSION string
|
---@field MOD_VERSION string
|
||||||
---@field DumpTable fun(table: table, depth?: number)
|
---@field DumpTable fun(table: table, depth?: number)
|
||||||
|
@@ -2,106 +2,49 @@
|
|||||||
if not CLIENT then return end
|
if not CLIENT then return end
|
||||||
|
|
||||||
local utils = require("Cyka.utils")
|
local utils = require("Cyka.utils")
|
||||||
|
local dump = require("Cyka.dump")
|
||||||
|
|
||||||
---@param inventory Barotrauma.ItemInventory
|
---@param invSlot InventorySlot
|
||||||
---@param predicate fun(slot: InventorySlot): boolean
|
local function tryUnloadSlot(invSlot)
|
||||||
---@return InventorySlot[], string?
|
---@type table<Barotrauma.ItemPrefab, boolean>
|
||||||
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 = {}
|
|
||||||
local toUnloadByPrefab = {}
|
local toUnloadByPrefab = {}
|
||||||
local inventorySlots = inventory.slots
|
|
||||||
for _, inventorySlot in ipairs(inventorySlots) do
|
local itemInventory = invSlot.item.OwnInventory
|
||||||
for _, inventoryItem in ipairs(inventorySlot.items) do
|
if not itemInventory then
|
||||||
toUnload[#toUnload + 1] = inventoryItem
|
MyModGlobal.debugPrint("No inventory for item")
|
||||||
-- This will only serve as O(1) lookup
|
return
|
||||||
toUnloadByPrefab[inventoryItem.Prefab] = true
|
|
||||||
end
|
|
||||||
end
|
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?
|
-- Where can we put our toUnload items?
|
||||||
local nearbySlots = findSlotsThat(slot.inventory, function(islot)
|
local nearbySlots = invSlot:getNearbySlots(function(islot)
|
||||||
local isEmpty = #islot.slot.items == 0
|
local isEmpty = islot.slot.items and #islot.slot.items == 0
|
||||||
if isEmpty then return true end
|
if isEmpty then return true end
|
||||||
|
|
||||||
for _, prefab in ipairs(toUnloadByPrefab) do
|
for prefab, _ in pairs(toUnloadByPrefab) do
|
||||||
local canAccept = islot.inventory.CanBePutInSlot(prefab, islot.slotIndex)
|
local canFit = islot:canFit(prefab)
|
||||||
if canAccept then return true end
|
if canFit then return true end
|
||||||
end
|
end
|
||||||
return false
|
return false
|
||||||
end)
|
end)
|
||||||
-- print("Before sorting:")
|
MyModGlobal.debugPrint(string.format("Into %d nearby slots", #nearbySlots))
|
||||||
-- dump(nearbySlots)
|
|
||||||
|
|
||||||
-- Some inventories don't have slots per row, like the player inventory
|
|
||||||
local slotsPerRow = 900
|
|
||||||
local ok, err = pcall(function()
|
|
||||||
slotsPerRow = slot.inventory.slotsPerRow
|
|
||||||
end)
|
|
||||||
if not ok then
|
|
||||||
MyModGlobal.debugPrint(string.format("Error getting slots per row: %s", err))
|
|
||||||
end
|
|
||||||
|
|
||||||
local getGridPos = function(slotIndex)
|
for _, iitem in ipairs(toUnload.itemQueue) do
|
||||||
local x = slotIndex % slotsPerRow
|
|
||||||
local y = math.floor(slotIndex / slotsPerRow)
|
|
||||||
return x, y
|
|
||||||
end
|
|
||||||
|
|
||||||
-- We are offsetting here by 1 because the backend uses 0-indexed slots
|
|
||||||
-- And the lua uses 1-indexed slots
|
|
||||||
-- We are trying to match the backend behavior for sorting
|
|
||||||
local slotx, sloty = getGridPos(slot.slotIndex - 1)
|
|
||||||
-- print(string.format("Slot position %d: %d, %d", slot.slotIndex, slotx, sloty))
|
|
||||||
table.sort(nearbySlots, function(a, b)
|
|
||||||
local ax, ay = getGridPos(a.slotIndex)
|
|
||||||
local bx, by = getGridPos(b.slotIndex)
|
|
||||||
|
|
||||||
local distA = math.max(math.abs(ax - slotx), math.abs(ay - sloty))
|
|
||||||
local distB = math.max(math.abs(bx - slotx), math.abs(by - sloty))
|
|
||||||
|
|
||||||
if distA == distB then
|
|
||||||
return a.slotIndex < b.slotIndex
|
|
||||||
end
|
|
||||||
return distA < distB
|
|
||||||
end)
|
|
||||||
-- print(string.format("Current slot: %d at (%d, %d)", slot.slotIndex, slotx, sloty))
|
|
||||||
|
|
||||||
for _, iitem in ipairs(toUnload) do
|
|
||||||
for _, nearbySlot in ipairs(nearbySlots) do
|
for _, nearbySlot in ipairs(nearbySlots) do
|
||||||
local canAccept = nearbySlot.inventory.CanBePutInSlot(iitem.Prefab, nearbySlot.slotIndex)
|
local canAccept = nearbySlot:canFit(iitem.Prefab)
|
||||||
if canAccept then
|
if canAccept then
|
||||||
local moved = nearbySlot.inventory.TryPutItem(iitem, nearbySlot.slotIndex, true, false,
|
utils.enqueueMove(iitem, nearbySlot)
|
||||||
Character.Controlled, true)
|
break
|
||||||
-- print(string.format("Moved item %s to slot %d", iitem.Name, nearbySlot.slotIndex))
|
|
||||||
if moved then break end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -110,12 +53,12 @@ end
|
|||||||
local function tryUnloadCursorItem()
|
local function tryUnloadCursorItem()
|
||||||
local slots, err = utils.getSlotsUnderCursor()
|
local slots, err = utils.getSlotsUnderCursor()
|
||||||
if err then
|
if err then
|
||||||
-- MyModGlobal.debugPrint(string.format("Error getting inventory slot: %s", err))
|
MyModGlobal.debugPrint(string.format("Error getting inventory slot: %s", err))
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if not slots or #slots == 0 then
|
if not slots or #slots == 0 then
|
||||||
-- MyModGlobal.debugPrint("No items in slot")
|
MyModGlobal.debugPrint("No items in slot")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@@ -25,6 +25,8 @@ MyModGlobal.InventorySlot = {
|
|||||||
self.inventory = inventory
|
self.inventory = inventory
|
||||||
self.slotIndex1 = slotIndex1
|
self.slotIndex1 = slotIndex1
|
||||||
self.slotIndex0 = slotIndex1 - 1
|
self.slotIndex0 = slotIndex1 - 1
|
||||||
|
self.stackSize = 0
|
||||||
|
self.maxStackSize = 0
|
||||||
-- self:update()
|
-- self:update()
|
||||||
|
|
||||||
if inventory and inventory.slots and #inventory.slots > 0 then
|
if inventory and inventory.slots and #inventory.slots > 0 then
|
||||||
@@ -40,6 +42,24 @@ MyModGlobal.InventorySlot = {
|
|||||||
|
|
||||||
return self
|
return self
|
||||||
end,
|
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)
|
update = function(self)
|
||||||
-- self.lastUpdated = Timer.GetTime()
|
-- self.lastUpdated = Timer.GetTime()
|
||||||
if not self.inventory then
|
if not self.inventory then
|
||||||
@@ -71,7 +91,9 @@ MyModGlobal.InventorySlot = {
|
|||||||
tostring(self.inventory), self.slotIndex1, self.slotIndex0, tostring(self.item), self.stackSize,
|
tostring(self.inventory), self.slotIndex1, self.slotIndex0, tostring(self.item), self.stackSize,
|
||||||
self.maxStackSize)
|
self.maxStackSize)
|
||||||
end,
|
end,
|
||||||
|
---@param self InventorySlot
|
||||||
---@param predicate? fun(slot: InventorySlot): boolean
|
---@param predicate? fun(slot: InventorySlot): boolean
|
||||||
|
---@return InventorySlot[]
|
||||||
getNearbySlots = function(self, predicate)
|
getNearbySlots = function(self, predicate)
|
||||||
predicate = predicate or function() return true end
|
predicate = predicate or function() return true end
|
||||||
|
|
||||||
@@ -114,14 +136,44 @@ MyModGlobal.InventorySlot = {
|
|||||||
|
|
||||||
return slots
|
return slots
|
||||||
end,
|
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)
|
-- hash = function(self)
|
||||||
-- return string.format("%s:%d:%d", tostring(self.inventory), self.slotIndex1, self.slotIndex0)
|
-- return string.format("%s:%d:%d", tostring(self.inventory), self.slotIndex1, self.slotIndex0)
|
||||||
-- end
|
-- end
|
||||||
}
|
}
|
||||||
|
|
||||||
---@class ItemMoveRequest
|
---@class ItemMoveRequest
|
||||||
---@field A InventorySlot
|
---@field what Barotrauma.Item
|
||||||
---@field B InventorySlot
|
---@field where InventorySlot
|
||||||
---@field allowSwap boolean
|
---@field allowSwap boolean
|
||||||
---@field allowCombine boolean
|
---@field allowCombine boolean
|
||||||
|
|
||||||
@@ -131,39 +183,45 @@ do
|
|||||||
local enabled = true
|
local enabled = true
|
||||||
---@type ItemMoveRequest[]
|
---@type ItemMoveRequest[]
|
||||||
local itemMoveQueue = {}
|
local itemMoveQueue = {}
|
||||||
local rate = 10
|
local rate = 100
|
||||||
|
local perIteration = 6
|
||||||
local function processQueue()
|
local function processQueue()
|
||||||
MyModGlobal.debugPrint("Processing queue")
|
-- MyModGlobal.debugPrint("Processing queue")
|
||||||
Timer.Wait(processQueue, rate)
|
Timer.Wait(processQueue, rate)
|
||||||
if not enabled then return end
|
if not enabled then return end
|
||||||
if #itemMoveQueue == 0 then return end
|
if #itemMoveQueue == 0 then return end
|
||||||
|
local iterations = math.min(perIteration, #itemMoveQueue)
|
||||||
|
for _ = 1, iterations do
|
||||||
---@type ItemMoveRequest
|
---@type ItemMoveRequest
|
||||||
local moveRequest = table.remove(itemMoveQueue, 1)
|
local moveRequest = table.remove(itemMoveQueue, 1)
|
||||||
|
|
||||||
-- TODO: Maybe try and figure out if we CAN put A into B
|
-- TODO: Maybe try and figure out if we CAN put A into B
|
||||||
moveRequest.allowCombine = moveRequest.allowCombine or false
|
moveRequest.allowCombine = moveRequest.allowCombine or false
|
||||||
moveRequest.allowSwap = moveRequest.allowSwap or false
|
moveRequest.allowSwap = moveRequest.allowSwap or false
|
||||||
local success = moveRequest.B.inventory.TryPutItem(moveRequest.A.item, moveRequest.B.slotIndex0,
|
local success = moveRequest.where.inventory.TryPutItem(moveRequest.what, moveRequest.where.slotIndex0,
|
||||||
moveRequest.allowSwap, moveRequest.allowCombine, Character.Controlled, true)
|
moveRequest.allowSwap, moveRequest.allowCombine, Character.Controlled, true)
|
||||||
if not success then
|
if not success then
|
||||||
MyModGlobal.debugPrint(string.format("Failed moving item from %s to %s", tostring(moveRequest.A),
|
MyModGlobal.debugPrint(string.format("Failed moving item from %s to %s", tostring(moveRequest.what),
|
||||||
tostring(moveRequest.B)))
|
tostring(moveRequest.where)))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
processQueue()
|
processQueue()
|
||||||
|
|
||||||
---@param A InventorySlot
|
---@param what Barotrauma.Item
|
||||||
---@param B InventorySlot
|
---@param where InventorySlot
|
||||||
---@param allowSwap boolean
|
---@param allowSwap? boolean
|
||||||
---@param allowCombine boolean
|
---@param allowCombine? boolean
|
||||||
enqueueMove = function(A, B, allowSwap, allowCombine)
|
enqueueMove = function(what, where, allowSwap, allowCombine)
|
||||||
MyModGlobal.debugPrint(string.format("Enqueuing move from %s to %s", tostring(A), tostring(B)))
|
MyModGlobal.debugPrint(string.format("Enqueuing move from %s to %s", tostring(what), tostring(where)))
|
||||||
table.insert(itemMoveQueue, {
|
table.insert(itemMoveQueue, {
|
||||||
A = A,
|
what = what,
|
||||||
B = B,
|
where = where,
|
||||||
allowSwap = allowSwap,
|
allowSwap = allowSwap or false,
|
||||||
allowCombine = allowCombine,
|
allowCombine = allowCombine ~= false,
|
||||||
})
|
})
|
||||||
|
-- We will very optimistically pretend that this will 100% for sure work
|
||||||
|
where:pretendMoved(what)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -202,15 +260,15 @@ end
|
|||||||
---@field slotIndex1 number
|
---@field slotIndex1 number
|
||||||
|
|
||||||
---@class EnqueueOptions
|
---@class EnqueueOptions
|
||||||
---@field itemQueue Barotrauma.Item[]
|
---@field itemQueue? Barotrauma.Item[]
|
||||||
---@field slotQueue Barotrauma.Inventory.ItemSlot[]
|
---@field slotQueue? Barotrauma.Inventory.ItemSlot[]
|
||||||
---@field inventoryQueue Barotrauma.Inventory[]
|
---@field inventoryQueue? Barotrauma.Inventory[]
|
||||||
---@field itemPredicate fun(item: Barotrauma.Item, itemRef: ItemRefs): boolean
|
---@field itemPredicate? fun(item: Barotrauma.Item, itemRef: ItemRefs): boolean
|
||||||
---@field slotPredicate fun(slot: Barotrauma.Inventory.ItemSlot, itemRef: ItemRefs): boolean
|
---@field slotPredicate? fun(slot: Barotrauma.Inventory.ItemSlot, itemRef: ItemRefs): boolean
|
||||||
---@field inventoryPredicate fun(inventory: Barotrauma.Inventory, itemRef: ItemRefs): boolean
|
---@field inventoryPredicate? fun(inventory: Barotrauma.Inventory, itemRef: ItemRefs): boolean
|
||||||
---@field loadRefs boolean
|
---@field loadRefs? boolean
|
||||||
---@field itemRef ItemRefs
|
---@field itemRef? ItemRefs
|
||||||
---@field recurse boolean
|
---@field recurse? boolean
|
||||||
|
|
||||||
---@param options EnqueueOptions
|
---@param options EnqueueOptions
|
||||||
---@return EnqueueOptions
|
---@return EnqueueOptions
|
||||||
@@ -222,9 +280,9 @@ local function ensureOptionsDefaults(options)
|
|||||||
options.itemPredicate = options.itemPredicate or function() return true end
|
options.itemPredicate = options.itemPredicate or function() return true end
|
||||||
options.slotPredicate = options.slotPredicate or function() return true end
|
options.slotPredicate = options.slotPredicate or function() return true end
|
||||||
options.inventoryPredicate = options.inventoryPredicate 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.itemRef = options.itemRef or nil
|
||||||
options.recurse = options.recurse or true
|
options.recurse = options.recurse == true
|
||||||
return options
|
return options
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -242,13 +300,13 @@ do
|
|||||||
---@return EnqueueOptions, string?
|
---@return EnqueueOptions, string?
|
||||||
enqueueItem = function(item, options)
|
enqueueItem = function(item, options)
|
||||||
options = ensureOptionsDefaults(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)
|
local ok, stop = options.itemPredicate(item, options.itemRef)
|
||||||
if ok then
|
if ok then
|
||||||
options.itemQueue[#options.itemQueue + 1] = item
|
options.itemQueue[#options.itemQueue + 1] = item
|
||||||
end
|
end
|
||||||
if stop then return options.itemQueue, "Stop" end
|
if stop then return options, "Stop" end
|
||||||
|
|
||||||
local err
|
local err
|
||||||
if item.OwnInventory then
|
if item.OwnInventory then
|
||||||
|
Reference in New Issue
Block a user