Files
barotrauma-localmods/CykaQuick/Lua/Cyka/quickreload.lua
2025-04-01 20:05:00 +02:00

195 lines
6.5 KiB
Lua

-- luacheck: globals Character MyModGlobal Timer CLIENT
-- luacheck: max line length 420
if not CLIENT then return end
local utils = require("Cyka.utils")
local dump = require("Cyka.dump")
-- Some items allow multiple items to be loaded
-- Such as welders and cutters
-- But we don't really want to disqualify those programmatically
-- Because it is not so obvious what item we REALLY want to load
-- So we will just hardcode tools and their whitelisted magazines
---@type table<string, table<string, number>>
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, _ in ipairs(inventorySlots) do
local invSlot = MyModGlobal.InventorySlot.new(inventory, i)
slots[#slots + 1] = invSlot
end
return slots
end
---@param slots InventorySlot[]
---@return table<InventorySlot, Barotrauma.Item[]>
local function getItemsPerSlot(slots)
-- How many items can we move to what slot
-- We don't yet know what can fit into what slot
---@type table<InventorySlot, Barotrauma.Item[]>
local movableBySlot = {}
-- Get all the items and then we will sort them by condition and shit
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)
if itemRef and itemRef.inventory and itemRef.inventory.Capacity < 4 then
-- MyModGlobal.debugPrint(string.format("Skipping small inventory %s", tostring(itemRef.inventory)))
return false
end
-- MyModGlobal.debugPrint("Checking item:")
-- dump(slots)
-- MyModGlobal.debugPrint(ititem.Prefab.Identifier.Value)
for _, inventorySlot in ipairs(slots) do
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 {}
movableBySlot[inventorySlot][#movableBySlot[inventorySlot] + 1] = ititem
return true
end
end
return false
end
})
return movableBySlot
end
---@param movableBySlot table<InventorySlot, Barotrauma.Item[]>
---@return table<InventorySlot, table<Barotrauma.ItemPrefab, boolean>>
local function getPermissibleItemsPerSlot(movableBySlot)
-- The point of this exercise is to eliminate slots that can have
-- Multiple items
-- What do we put into those? Any? All?
-- What if those slots belong to a container?
-- Are we reloading 30 slot containers?
---@type table<InventorySlot, table<Barotrauma.ItemPrefab, boolean>>
local permissibleItemsPerSlot = {}
for inventorySlot, items in pairs(movableBySlot) do
for _, ititem in ipairs(items) do
local thisone = tostring(ititem.Prefab.Identifier.Value)
permissibleItemsPerSlot[inventorySlot] = permissibleItemsPerSlot[inventorySlot] or {}
permissibleItemsPerSlot[inventorySlot][thisone] = true
end
end
return permissibleItemsPerSlot
end
---@param movableBySlot table<InventorySlot, Barotrauma.Item[]>
local function printPermissibleItems(movableBySlot)
local permissibleItemsPerSlot = getPermissibleItemsPerSlot(movableBySlot)
MyModGlobal.debugPrint("Can load:")
for _, loadableItems in pairs(permissibleItemsPerSlot) do
for loadableItem, _ in pairs(loadableItems) do
MyModGlobal.debugPrint(" " .. loadableItem)
end
end
end
---@param invSlot InventorySlot
---@param preferMinCondition boolean
local function tryReloadSlot(invSlot, preferMinCondition)
---@type Barotrauma.Item
local item = invSlot.item
if not item then
MyModGlobal.debugPrint("No item in slot")
return
end
MyModGlobal.debugPrint(string.format("Reloading item %s", item.Prefab.Identifier.Value))
local inventory = item.OwnInventory
if not inventory then
MyModGlobal.debugPrint("Item has no own inventory")
return
end
---@type InventorySlot[]
local slots = getSlots(inventory)
if #slots == 0 then
MyModGlobal.debugPrint("No slots")
return
end
---@type table<InventorySlot, Barotrauma.Item[]>
local movableBySlot = getItemsPerSlot(slots)
-- MyModGlobal.debugPrint("Movable by slot:")
local permissibleItems = LOAD_MAP[tostring(item.Prefab.Identifier.Value)]
if not permissibleItems then
MyModGlobal.debugPrint("No permissible items for " .. tostring(item.Prefab.Identifier.Value))
printPermissibleItems(movableBySlot)
return
end
-- Sort items by condition (asc or desc) but also
-- Make sure items with 0 condition are at the end
-- We don't really want to load those
for _, items in pairs(movableBySlot) do
table.sort(items, function(a, b)
if a.Condition == 0 and b.Condition ~= 0 then
return false
elseif a.Condition ~= 0 and b.Condition == 0 then
return true
elseif preferMinCondition then
return a.Condition < b.Condition
else
return a.Condition > b.Condition
end
end)
end
-- dump(movableBySlot)
local numMoved = 0
for inventorySlot, items in pairs(movableBySlot) do
for _, ititem in ipairs(items) do
local permissible = permissibleItems[tostring(ititem.Prefab.Identifier.Value)]
if permissible then
-- 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.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))
break
end
if not inventorySlot:canFit(ititem.Prefab) then
break
end
utils.enqueueMove(ititem, inventorySlot)
numMoved = numMoved + 1
end
end
end
MyModGlobal.debugPrint(string.format("Moved %d items to load %s", numMoved, tostring(item.Prefab.Identifier.Value)))
end
---@param preferMinCondition boolean Prefer items with lowest condition
local function tryReloadCursorItem(preferMinCondition)
local slots, err = utils.getSlotsUnderCursor()
if err then
MyModGlobal.debugPrint(string.format("Error getting slots under cursor: %s", err))
return
end
for _, slot in ipairs(slots) do
tryReloadSlot(slot, preferMinCondition)
end
end
return {
tryReloadCursorItem = tryReloadCursorItem,
}