From 482e44e7d67738dbe22702d51c8b9474e6ee495c Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sun, 30 Mar 2025 14:38:07 +0200 Subject: [PATCH] Factor out fabricator stack --- QuickStackToBag/Lua/Autorun/init.lua | 172 ------------------ QuickStackToBag/Lua/Cyka/fabricatorstack.lua | 174 +++++++++++++++++++ 2 files changed, 174 insertions(+), 172 deletions(-) create mode 100644 QuickStackToBag/Lua/Cyka/fabricatorstack.lua diff --git a/QuickStackToBag/Lua/Autorun/init.lua b/QuickStackToBag/Lua/Autorun/init.lua index 74af1f9..7a3d08a 100644 --- a/QuickStackToBag/Lua/Autorun/init.lua +++ b/QuickStackToBag/Lua/Autorun/init.lua @@ -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) ---------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------- diff --git a/QuickStackToBag/Lua/Cyka/fabricatorstack.lua b/QuickStackToBag/Lua/Cyka/fabricatorstack.lua new file mode 100644 index 0000000..f57aef7 --- /dev/null +++ b/QuickStackToBag/Lua/Cyka/fabricatorstack.lua @@ -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)