Files
barotrauma-localmods/QuickStackToBag/Lua/Cyka/quickreload.lua
2025-03-30 21:28:09 +02:00

230 lines
7.5 KiB
Lua

-- luacheck: globals Character MyModGlobal Timer
local cursormacroer = require("Cyka.cursormacroer")
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, boolean>>
local LOAD_MAP = {
}
---@param inventory Barotrauma.ItemInventory
---@return InventorySlot[]
local function getSlots(inventory)
local slots = {}
local inventorySlots = inventory.slots
for i, inventorySlot in ipairs(inventorySlots) do
slots[#slots + 1] = {
inventory = inventory,
slotIndex = i - 1,
slot = inventorySlot
}
end
return slots
end
---@param from Barotrauma.ItemInventory
---@param slots InventorySlot[]
---@return table<InventorySlot, Barotrauma.Item[]>
local function getItemsPerSlot(from, 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.enqueueInventory(from, {}, function(ititem)
-- 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.slotIndex)
-- 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][thisone] = true
end
end
return permissibleItemsPerSlot
end
---@param slot InventorySlot
---@param preferMinCondition boolean
local function tryReloadSlot(slot, preferMinCondition)
---@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 character = Character.Controlled
if not character then
MyModGlobal.debugPrint("No character")
return
end
local characterInventory = character.Inventory
if not characterInventory then
MyModGlobal.debugPrint("No character inventory")
return
end
---@type InventorySlot[]
local slots = getSlots(inventory)
-- MyModGlobal.debugPrint("Slots:")
-- dump(slots)
---@type table<InventorySlot, Barotrauma.Item[]>
local movableBySlot = getItemsPerSlot(characterInventory, slots)
-- MyModGlobal.debugPrint("Movable by slot:")
-- dump(movableBySlot)
local permissibleItems = LOAD_MAP[tostring(item.Prefab.Identifier.Value)]
if not permissibleItems then
MyModGlobal.debugPrint("No permissible items for item")
local permissibleItemsPerSlot = getPermissibleItemsPerSlot(movableBySlot)
MyModGlobal.debugPrint("Can load per slot:")
dump(permissibleItemsPerSlot)
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.ConditionPercentage == 0 and b.ConditionPercentage ~= 0 then
return false
elseif a.ConditionPercentage ~= 0 and b.ConditionPercentage == 0 then
return true
elseif preferMinCondition then
return a.ConditionPercentage < b.ConditionPercentage
else
return a.ConditionPercentage > b.ConditionPercentage
end
end)
end
-- dump(movableBySlot)
for inventorySlot, items in pairs(movableBySlot) do
for _, ititem in ipairs(items) do
local moved = inventorySlot.inventory.TryPutItem(ititem, inventorySlot.slotIndex, false, true, nil)
-- 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
end
end
-- -- Where can we put our toUnload items?
-- local nearbySlots = findSlotsThat(slot.inventory, function(islot)
-- local isEmpty = #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
-- end
-- return false
-- end)
-- -- print("Before sorting:")
-- -- 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)
-- 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
-- local canAccept = nearbySlot.inventory.CanBePutInSlot(iitem.Prefab, nearbySlot.slotIndex)
-- if canAccept then
-- local moved = nearbySlot.inventory.TryPutItem(iitem, nearbySlot.slotIndex, true, false, nil)
-- -- print(string.format("Moved item %s to slot %d", iitem.Name, nearbySlot.slotIndex))
-- if moved then break end
-- end
-- end
-- end
end
---@param minCondition boolean Prefer items with lowest condition
local function tryReloadCursorItem(preferMinCondition)
local slots, err = cursormacroer.getInventorySlotsUnderCursor()
if err then
-- 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")
return
end
for _, slot in ipairs(slots) do
tryReloadSlot(slot, preferMinCondition)
end
end
return {
tryReloadCursorItem = tryReloadCursorItem,
}