362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
			
		
		
	
	
			362 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Lua
		
	
	
	
	
	
| -- luacheck: globals Character MyModGlobal
 | |
| -- luacheck: max line length 420
 | |
| 
 | |
| ---@class ItemRefs
 | |
| ---@field item Barotrauma.Item
 | |
| ---@field inventory Barotrauma.ItemInventory
 | |
| ---@field slot Barotrauma.ItemInventory.Slot
 | |
| 
 | |
| ---@class InventorySlot
 | |
| ---@field slot Barotrauma.ItemSlot
 | |
| ---@field inventory Barotrauma.ItemInventory
 | |
| ---@field slotIndex number
 | |
| 
 | |
| -- We got to do this shit because enqueueInventory calls enqueueItem
 | |
| -- And enqueueItem calls enqueueInventory
 | |
| -- So unless we define them both before using them
 | |
| -- We will get an error saying either is undefined
 | |
| local enqueueItem
 | |
| local enqueueSlot
 | |
| local enqueueInventory
 | |
| 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
 | |
| 
 | |
| ---@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
 | |
| 
 | |
|     local ok, stop = predicate(item, itemRef)
 | |
|     if ok then
 | |
|         queue[#queue + 1] = item
 | |
|     end
 | |
|     if stop then return queue, "Stop" end
 | |
|     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)
 | |
|         else
 | |
|             queue, _ = enqueueInventory(item.OwnInventory, queue, predicate, itemRef)
 | |
|         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
 | |
| 
 | |
| ---@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
 | |
| 
 | |
|     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))
 | |
|     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)
 | |
|         else
 | |
|             queue, err = enqueueSlot(slot, queue, predicate)
 | |
|         end
 | |
|         if err then
 | |
|             return queue, err
 | |
|         end
 | |
|     end
 | |
|     -- debugPrint(string.format("Finished enqueuing inventory. Current queue size: %d", #queue))
 | |
|     return queue
 | |
| 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
 | |
| 
 | |
|     local character = Character.Controlled
 | |
|     if not character then return queue, "No character" end
 | |
| 
 | |
|     local inventory = character.Inventory
 | |
|     if not inventory then return queue, "No inventory" end
 | |
| 
 | |
|     for _, slotid in ipairs(relevantPlayerInventorySlots) do
 | |
|         local slot = inventory.slots[slotid]
 | |
|         local err
 | |
| 
 | |
|         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::
 | |
|     end
 | |
| 
 | |
|     return queue
 | |
| 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
 | |
|     -- 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
 | |
| 
 | |
|     local submarine = character.Submarine
 | |
|     if not submarine then return queue, "No submarine" end
 | |
| 
 | |
|     for item in submarine.GetItems(false) do
 | |
|         -- We do NOT want to call enqueueItem here because enqueueItem
 | |
|         -- Is recursive
 | |
|         -- 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)
 | |
|         if ok then
 | |
|             queue[#queue + 1] = item
 | |
|         end
 | |
|         if stop then return queue, "Stop" end
 | |
|     end
 | |
| 
 | |
|     return queue
 | |
| 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
 | |
| 
 | |
|     local err
 | |
|     queue, err = allPlayerItems(queue, predicate, loadRefs)
 | |
|     if err then return queue, err end
 | |
| 
 | |
|     queue, err = allSubmarineItems(queue, predicate)
 | |
|     if err then return queue, err end
 | |
| 
 | |
|     return queue
 | |
| end
 | |
| 
 | |
| ---@return Barotrauma.Item[], string?
 | |
| local function getOpenContainers()
 | |
|     local controlledCharacter = Character.Controlled
 | |
|     if not controlledCharacter then return {}, "No controlled character" end
 | |
|     local selectedItem = controlledCharacter.SelectedItem
 | |
|     if not selectedItem then return {}, "No selected item" end
 | |
|     return { selectedItem }, nil
 | |
| end
 | |
| 
 | |
| ---@return Barotrauma.Item, string?
 | |
| local function getFirstOpenContainer()
 | |
|     local containers, err = getOpenContainers()
 | |
|     if err then return nil, err end
 | |
|     if #containers == 0 then return nil, "No open containers" end
 | |
|     return containers[1], nil
 | |
| end
 | |
| 
 | |
| -- There is actually no need to recurse deep
 | |
| -- Because we can only have an item in the inventory open
 | |
| -- 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
 | |
| ---@return InventorySlot[], string?
 | |
| local function getMouseoverSlots(inventory, slots, depth)
 | |
|     slots = slots or {}
 | |
|     depth = depth or 0
 | |
|     if depth > 1 then return slots, nil end
 | |
| 
 | |
|     local visualSlots = inventory.visualSlots
 | |
|     if not visualSlots then return nil, "Inventory has no visual slots" end
 | |
| 
 | |
|     for i, visualSlot in ipairs(visualSlots) do
 | |
|         local item
 | |
|         local itemInventory
 | |
|         -- local err
 | |
| 
 | |
|         local slot = inventory.slots[i]
 | |
|         if not slot then
 | |
|             -- MyModGlobal.debugPrint("Slot is not a valid slot")
 | |
|             goto continue
 | |
|         end
 | |
| 
 | |
|         if #slot.items == 0 then
 | |
|             goto mouseover
 | |
|         end
 | |
| 
 | |
|         item = slot.items[1]
 | |
|         if not item then
 | |
|             goto mouseover
 | |
|         end
 | |
| 
 | |
|         itemInventory = item.OwnInventory
 | |
|         if not itemInventory then
 | |
|             goto mouseover
 | |
|         end
 | |
| 
 | |
|         -- print("Before: " .. #slots)--
 | |
|         getMouseoverSlots(itemInventory, slots, depth + 1)
 | |
|         -- if err then
 | |
|         -- 	MyModGlobal.debugPrint(string.format("Error getting mouseover slots: %s", err))
 | |
|         -- end
 | |
|         -- print("After: " .. #slots)
 | |
| 
 | |
|         ::mouseover::
 | |
|         if visualSlot:MouseOn() then
 | |
|             slots[#slots + 1] = {
 | |
|                 inventory = inventory,
 | |
|                 slotIndex = i,
 | |
|                 slot = slot
 | |
|             }
 | |
|         end
 | |
| 
 | |
|         ::continue::
 | |
|     end
 | |
| 
 | |
|     return slots, nil
 | |
| end
 | |
| 
 | |
| ---@return InventorySlot[], string?
 | |
| local function getSlotsUnderCursor()
 | |
|     -- Make sure we have a controlled character
 | |
|     local controlledCharacter = Character.Controlled
 | |
|     if not controlledCharacter then return nil, "No controlled character" end
 | |
| 
 | |
|     local inventory = controlledCharacter.Inventory
 | |
|     if not inventory then return nil, "No inventory" end
 | |
| 
 | |
|     local mouseoverSlots, err = getMouseoverSlots(inventory)
 | |
|     if err then return mouseoverSlots, err end
 | |
| 
 | |
|     -- Even if we don't get them we're still fine
 | |
|     local openContainers, _ = getOpenContainers()
 | |
|     -- if err then return mouseoverSlots, err end
 | |
| 
 | |
|     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
 | |
|                     }
 | |
|                 end
 | |
|             end
 | |
|             ::continue::
 | |
|         end
 | |
|     end
 | |
| 
 | |
|     return mouseoverSlots, 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
 | |
|     for _, slot in ipairs(slots) do
 | |
|         if #slot.items > 0 then
 | |
|             return slot
 | |
|         end
 | |
|     end
 | |
|     return slots[1]
 | |
| end
 | |
| 
 | |
| return {
 | |
|     enqueueItem = enqueueItem,
 | |
|     enqueueSlot = enqueueSlot,
 | |
|     enqueueInventory = enqueueInventory,
 | |
|     enqueueAllPlayerItems = allPlayerItems,
 | |
|     enqueueAllSubmarineItems = allSubmarineItems,
 | |
|     enqueueAllOwnedItems = allOwnedItems,
 | |
|     getOpenContainers = getOpenContainers,
 | |
|     getFirstOpenContainer = getFirstOpenContainer,
 | |
|     getSlotsUnderCursor = getSlotsUnderCursor,
 | |
|     getFirstSlotUnderCursor = getFirstSlotUnderCursor,
 | |
| }
 |