Factor out fabricator stack

This commit is contained in:
2025-03-30 14:38:07 +02:00
parent ba01a65a1a
commit 482e44e7d6
2 changed files with 174 additions and 172 deletions

View File

@@ -182,178 +182,6 @@ LuaUserData.MakeMethodAccessible(Descriptors["Barotrauma.CargoManager"], "GetCon
---------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------
---@return {item: Barotrauma.Item, fabricator: Barotrauma.FabricatorComponent}, string?
local function getOpenFabricator()
-- Get the controlled character
local controlledCharacter = Character.Controlled
if not controlledCharacter then return nil, "No controlled character found" end
-- Check if the character has a selected item
local selectedItem = controlledCharacter.SelectedItem
if not selectedItem then return nil, "No selected item found" end
-- Check if the selected item has a Fabricator component
local fabricator = Game.GetFabricatorComponent(selectedItem)
if not fabricator then return nil, "No fabricator component found" end
return {
item = selectedItem,
fabricator = fabricator
}
end
--- Recipes can have multiple inputs, for example ammo
--- Can be made either out of copper iron or steel, 1 of either
---@class RecipeInfo
---@field targetItem {identifier: string, name: string, amount: number}
---@field requiredItems {amount: number, minCondition: number, maxCondition: number, prefabs: string[]}[]
---@param fabricator Barotrauma.FabricatorComponent
---@return RecipeInfo, string?
local function getSelectedRecipeRequirements(fabricator)
-- local openFabricator, err = getOpenFabricator()
-- if err then return nil, err end
-- local fabricator = openFabricator.fabricator
local selectedRecipe = fabricator.SelectedItem
if not selectedRecipe then return nil, "No selected recipe found" end
local requiredItems = {}
for requiredItem in selectedRecipe.RequiredItems do
local itemInfo = {
amount = tonumber(requiredItem.Amount),
minCondition = tonumber(requiredItem.MinCondition),
maxCondition = tonumber(requiredItem.MaxCondition),
prefabs = {}
}
for prefab in requiredItem.ItemPrefabs do
itemInfo.prefabs[#itemInfo.prefabs + 1] = prefab.Identifier.Value
end
requiredItems[#requiredItems + 1] = itemInfo
end
return {
targetItem = {
identifier = selectedRecipe.TargetItem.Identifier,
name = selectedRecipe.TargetItem.Name,
amount = selectedRecipe.Amount
},
requiredItems = requiredItems
}
end
-- Hook into player control to listen for key press
Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptable)
if not PlayerInput.KeyHit(CONFIG.FABRICATOR_KEY) then return end
-- TODO: Maybe get items from entire sub...?
-- There's no point getting recipes if we don't have all of this bullshit
---@type Barotrauma.Character
local character = instance
if not character then
debugPrint("Character instance is nil.")
return
end
---@type Barotrauma.CharacterInventory
local inventory = character.Inventory
if not inventory then
debugPrint("Character inventory is nil.")
return
end
---@type Barotrauma.ItemInventory.Slot
local bagSlot = inventory.slots[BAG_SLOT]
if not bagSlot then
debugPrint("Bag slot not found.")
return
end
if #bagSlot.items == 0 then
debugPrint("Bag slot is empty.")
return
end
---@type Barotrauma.Item
local bagItem = bagSlot.items[1]
if not bagItem then
debugPrint("Bag item not found.")
return
end
local fabricator, err = getOpenFabricator()
if err then
print(string.format("Error getting open fabricator: %s", err))
return
end
local recipe
recipe, err = getSelectedRecipeRequirements(fabricator.fabricator)
if err then
print(string.format("Error getting selected recipe requirements: %s", err))
return
end
-- DumpTable(recipe)
-- TODO: Maybe make it so every press cycles the input
-- For recipes that have multiple prefabs
-- But then again what if it has 3 items with 4 prefabs each..
-- Is that 4 iterations or 3*4 iterations?
-- We can not use #toFind because we can remove 0th item
-- Which means it's no longer contiguous
-- Which means #toFind returns 0
local toFind = recipe.requiredItems
local remaining = #toFind
---@type Barotrauma.Item[]
local toGet = {}
---@type fun(item: Barotrauma.Item): boolean, boolean
local filter = function(item)
local found = false
-- DumpTable(toFind)
-- toFind are all items we need to find
for i, itemInfo in pairs(toFind) do
-- prefabs are all items that satisfy the requirements
for _, prefab in ipairs(itemInfo.prefabs) do
-- debugPrint(string.format("Checking %s against %s", item.Prefab.Identifier.Value, prefab))
if item.Prefab.Identifier.Value == prefab then
-- debugPrint(string.format("That'll do %s %s", item.Prefab.Identifier.Value, prefab))
toGet[#toGet + 1] = item
itemInfo.amount = itemInfo.amount - 1
found = true
break
end
end
if itemInfo.amount <= 0 then
-- debugPrint(string.format("Removing %s from toFind", itemInfo.prefabs[1]))
toFind[i] = nil
remaining = remaining - 1
end
if found then break end
end
-- DumpTable(toFind)
-- debugPrint(string.format("Found %s %s", item.Prefab.Identifier.Value, tostring(remaining)))
return found, remaining == 0
end
-- DumpTable(toGet)
local items = enqueueInventory(bagItem.OwnInventory, {}, filter)
-- DumpTable(items)
-- TODO: This might explode... Oh well?
local inputInventory = fabricator.item.OwnInventories[1]
for iinventory in fabricator.item.OwnInventories do
if #iinventory.slots > 1 then
inputInventory = iinventory
break
end
end
local slot = -1
local previous = nil
for _, item in ipairs(items) do
if previous ~= item.Prefab.Identifier then slot = slot + 1 end
inputInventory.TryPutItem(item, slot, false, true, nil)
previous = item.Prefab.Identifier
end
DumpTable(items)
end, Hook.HookMethodType.After)
---------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------

View File

@@ -0,0 +1,174 @@
local utils = require("Cyka.utils")
---@return {item: Barotrauma.Item, fabricator: Barotrauma.FabricatorComponent}, string?
local function getOpenFabricator()
-- Get the controlled character
local controlledCharacter = Character.Controlled
if not controlledCharacter then return nil, "No controlled character found" end
-- Check if the character has a selected item
local selectedItem = controlledCharacter.SelectedItem
if not selectedItem then return nil, "No selected item found" end
-- Check if the selected item has a Fabricator component
local fabricator = Game.GetFabricatorComponent(selectedItem)
if not fabricator then return nil, "No fabricator component found" end
return {
item = selectedItem,
fabricator = fabricator
}
end
--- Recipes can have multiple inputs, for example ammo
--- Can be made either out of copper iron or steel, 1 of either
---@class RecipeInfo
---@field targetItem {identifier: string, name: string, amount: number}
---@field requiredItems {amount: number, minCondition: number, maxCondition: number, prefabs: string[]}[]
---@param fabricator Barotrauma.FabricatorComponent
---@return RecipeInfo, string?
local function getSelectedRecipeRequirements(fabricator)
-- local openFabricator, err = getOpenFabricator()
-- if err then return nil, err end
-- local fabricator = openFabricator.fabricator
local selectedRecipe = fabricator.SelectedItem
if not selectedRecipe then return nil, "No selected recipe found" end
local requiredItems = {}
for requiredItem in selectedRecipe.RequiredItems do
local itemInfo = {
amount = tonumber(requiredItem.Amount),
minCondition = tonumber(requiredItem.MinCondition),
maxCondition = tonumber(requiredItem.MaxCondition),
prefabs = {}
}
for prefab in requiredItem.ItemPrefabs do
itemInfo.prefabs[#itemInfo.prefabs + 1] = prefab.Identifier.Value
end
requiredItems[#requiredItems + 1] = itemInfo
end
return {
targetItem = {
identifier = selectedRecipe.TargetItem.Identifier,
name = selectedRecipe.TargetItem.Name,
amount = selectedRecipe.Amount
},
requiredItems = requiredItems
}
end
-- Hook into player control to listen for key press
Hook.Patch("Barotrauma.Character", "ControlLocalPlayer", function(instance, ptable)
if not PlayerInput.KeyHit(MyModGlobal.CONFIG.FABRICATOR_KEY) then return end
-- TODO: Maybe get items from entire sub...?
-- There's no point getting recipes if we don't have all of this bullshit
---@type Barotrauma.Character
local character = instance
if not character then
MyModGlobal.debugPrint("Character instance is nil.")
return
end
---@type Barotrauma.CharacterInventory
local inventory = character.Inventory
if not inventory then
MyModGlobal.debugPrint("Character inventory is nil.")
return
end
---@type Barotrauma.ItemInventory.Slot
local bagSlot = inventory.slots[BAG_SLOT]
if not bagSlot then
MyModGlobal.debugPrint("Bag slot not found.")
return
end
if #bagSlot.items == 0 then
MyModGlobal.debugPrint("Bag slot is empty.")
return
end
---@type Barotrauma.Item
local bagItem = bagSlot.items[1]
if not bagItem then
MyModGlobal.debugPrint("Bag item not found.")
return
end
local fabricator, err = getOpenFabricator()
if err then
print(string.format("Error getting open fabricator: %s", err))
return
end
local recipe
recipe, err = getSelectedRecipeRequirements(fabricator.fabricator)
if err then
print(string.format("Error getting selected recipe requirements: %s", err))
return
end
-- MyModGlobal.DumpTable(recipe)
-- TODO: Maybe make it so every press cycles the input
-- For recipes that have multiple prefabs
-- But then again what if it has 3 items with 4 prefabs each..
-- Is that 4 iterations or 3*4 iterations?
-- We can not use #toFind because we can remove 0th item
-- Which means it's no longer contiguous
-- Which means #toFind returns 0
local toFind = recipe.requiredItems
local remaining = #toFind
---@type Barotrauma.Item[]
local toGet = {}
---@type fun(item: Barotrauma.Item): boolean, boolean
local filter = function(item)
local found = false
-- MyModGlobal.DumpTable(toFind)
-- toFind are all items we need to find
for i, itemInfo in pairs(toFind) do
-- prefabs are all items that satisfy the requirements
for _, prefab in ipairs(itemInfo.prefabs) do
-- MyModGlobal.debugPrint(string.format("Checking %s against %s", item.Prefab.Identifier.Value, prefab))
if item.Prefab.Identifier.Value == prefab then
-- MyModGlobal.debugPrint(string.format("That'll do %s %s", item.Prefab.Identifier.Value, prefab))
toGet[#toGet + 1] = item
itemInfo.amount = itemInfo.amount - 1
found = true
break
end
end
if itemInfo.amount <= 0 then
-- MyModGlobal.debugPrint(string.format("Removing %s from toFind", itemInfo.prefabs[1]))
toFind[i] = nil
remaining = remaining - 1
end
if found then break end
end
-- MyModGlobal.DumpTable(toFind)
-- MyModGlobal.debugPrint(string.format("Found %s %s", item.Prefab.Identifier.Value, tostring(remaining)))
return found, remaining == 0
end
-- MyModGlobal.DumpTable(toGet)
local items = utils.enqueueInventory(bagItem.OwnInventory, {}, filter)
-- MyModGlobal.DumpTable(items)
-- TODO: This might explode... Oh well?
local inputInventory = fabricator.item.OwnInventories[1]
for iinventory in fabricator.item.OwnInventories do
if #iinventory.slots > 1 then
inputInventory = iinventory
break
end
end
local slot = -1
local previous = nil
for _, item in ipairs(items) do
if previous ~= item.Prefab.Identifier then slot = slot + 1 end
inputInventory.TryPutItem(item, slot, false, true, nil)
previous = item.Prefab.Identifier
end
MyModGlobal.DumpTable(items)
end, Hook.HookMethodType.After)