Factor out fabricator stack
This commit is contained in:
@@ -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)
|
|
||||||
|
|
||||||
----------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
----------------------------------------------------------------------------------------------------
|
----------------------------------------------------------------------------------------------------
|
||||||
|
174
QuickStackToBag/Lua/Cyka/fabricatorstack.lua
Normal file
174
QuickStackToBag/Lua/Cyka/fabricatorstack.lua
Normal 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)
|
Reference in New Issue
Block a user