From cd3e4e1fd954ed0e8844f130f6a10f17c696a94b Mon Sep 17 00:00:00 2001 From: MayaTheShy Date: Mon, 16 Mar 2026 00:04:14 -0400 Subject: [PATCH] Add crafting functionality for networked turtle with recipe management --- inventoryManager.lua | 426 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 426 insertions(+) diff --git a/inventoryManager.lua b/inventoryManager.lua index e936432..4c52805 100644 --- a/inventoryManager.lua +++ b/inventoryManager.lua @@ -20,6 +20,10 @@ local BROADCAST_CHANNEL = 4200 local ORDER_CHANNEL = 4201 local BROADCAST_INTERVAL = 1 -- seconds between state broadcasts +-- Crafting turtle +local CRAFT_CHANNEL = 4203 +local CRAFT_REPLY_CHANNEL = 4204 + ------------------------------------------------- -- Furnace types to manage ------------------------------------------------- @@ -339,6 +343,229 @@ local COMPOST_RESERVE = 16 local COMPOST_DROPPER = "minecraft:dropper_10" local COMPOST_HOPPER = "minecraft:hopper_0" +------------------------------------------------- +-- Crafting recipes (for networked crafting turtle) +-- grid: 9 entries mapping to turtle slots 1-3, 5-7, 9-11 +------------------------------------------------- + +local GRID_TO_SLOT = {1, 2, 3, 5, 6, 7, 9, 10, 11} + +local CRAFTABLE = { + -- Basic materials + { + output = "minecraft:oak_planks", + count = 4, + grid = { + "minecraft:oak_log", nil, nil, + nil, nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:spruce_planks", + count = 4, + grid = { + "minecraft:spruce_log", nil, nil, + nil, nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:birch_planks", + count = 4, + grid = { + "minecraft:birch_log", nil, nil, + nil, nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:stick", + count = 4, + grid = { + "minecraft:oak_planks", nil, nil, + "minecraft:oak_planks", nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:oak_slab", + count = 6, + grid = { + "minecraft:oak_planks", "minecraft:oak_planks", "minecraft:oak_planks", + nil, nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:torch", + count = 4, + grid = { + "minecraft:coal", nil, nil, + "minecraft:stick", nil, nil, + nil, nil, nil, + }, + }, + -- Crafting & storage + { + output = "minecraft:crafting_table", + count = 1, + grid = { + "minecraft:oak_planks", "minecraft:oak_planks", nil, + "minecraft:oak_planks", "minecraft:oak_planks", nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:chest", + count = 1, + grid = { + "minecraft:oak_planks", "minecraft:oak_planks", "minecraft:oak_planks", + "minecraft:oak_planks", nil, "minecraft:oak_planks", + "minecraft:oak_planks", "minecraft:oak_planks", "minecraft:oak_planks", + }, + }, + { + output = "minecraft:barrel", + count = 1, + grid = { + "minecraft:oak_planks", "minecraft:oak_slab", "minecraft:oak_planks", + "minecraft:oak_planks", nil, "minecraft:oak_planks", + "minecraft:oak_planks", "minecraft:oak_slab", "minecraft:oak_planks", + }, + }, + { + output = "minecraft:hopper", + count = 1, + grid = { + "minecraft:iron_ingot", nil, "minecraft:iron_ingot", + "minecraft:iron_ingot", "minecraft:chest", "minecraft:iron_ingot", + nil, "minecraft:iron_ingot", nil, + }, + }, + -- Building + { + output = "minecraft:furnace", + count = 1, + grid = { + "minecraft:cobblestone", "minecraft:cobblestone", "minecraft:cobblestone", + "minecraft:cobblestone", nil, "minecraft:cobblestone", + "minecraft:cobblestone", "minecraft:cobblestone", "minecraft:cobblestone", + }, + }, + { + output = "minecraft:ladder", + count = 3, + grid = { + "minecraft:stick", nil, "minecraft:stick", + "minecraft:stick", "minecraft:stick", "minecraft:stick", + "minecraft:stick", nil, "minecraft:stick", + }, + }, + { + output = "minecraft:glass_pane", + count = 16, + grid = { + "minecraft:glass", "minecraft:glass", "minecraft:glass", + "minecraft:glass", "minecraft:glass", "minecraft:glass", + nil, nil, nil, + }, + }, + { + output = "minecraft:iron_bars", + count = 16, + grid = { + "minecraft:iron_ingot", "minecraft:iron_ingot", "minecraft:iron_ingot", + "minecraft:iron_ingot", "minecraft:iron_ingot", "minecraft:iron_ingot", + nil, nil, nil, + }, + }, + -- Tools & combat + { + output = "minecraft:bucket", + count = 1, + grid = { + "minecraft:iron_ingot", nil, "minecraft:iron_ingot", + nil, "minecraft:iron_ingot", nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:arrow", + count = 4, + grid = { + "minecraft:flint", nil, nil, + "minecraft:stick", nil, nil, + "minecraft:feather", nil, nil, + }, + }, + -- Redstone + { + output = "minecraft:piston", + count = 1, + grid = { + "minecraft:oak_planks", "minecraft:oak_planks", "minecraft:oak_planks", + "minecraft:cobblestone", "minecraft:iron_ingot", "minecraft:cobblestone", + "minecraft:cobblestone", "minecraft:redstone", "minecraft:cobblestone", + }, + }, + { + output = "minecraft:rail", + count = 16, + grid = { + "minecraft:iron_ingot", nil, "minecraft:iron_ingot", + "minecraft:iron_ingot", "minecraft:stick", "minecraft:iron_ingot", + "minecraft:iron_ingot", nil, "minecraft:iron_ingot", + }, + }, + { + output = "minecraft:powered_rail", + count = 6, + grid = { + "minecraft:gold_ingot", nil, "minecraft:gold_ingot", + "minecraft:gold_ingot", "minecraft:stick", "minecraft:gold_ingot", + "minecraft:gold_ingot", "minecraft:redstone", "minecraft:gold_ingot", + }, + }, + -- Food & misc + { + output = "minecraft:bread", + count = 1, + grid = { + "minecraft:wheat", "minecraft:wheat", "minecraft:wheat", + nil, nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:paper", + count = 3, + grid = { + "minecraft:sugar_cane", "minecraft:sugar_cane", "minecraft:sugar_cane", + nil, nil, nil, + nil, nil, nil, + }, + }, + { + output = "minecraft:compass", + count = 1, + grid = { + nil, "minecraft:iron_ingot", nil, + "minecraft:iron_ingot", "minecraft:redstone", "minecraft:iron_ingot", + nil, "minecraft:iron_ingot", nil, + }, + }, + { + output = "minecraft:clock", + count = 1, + grid = { + nil, "minecraft:gold_ingot", nil, + "minecraft:gold_ingot", "minecraft:redstone", "minecraft:gold_ingot", + nil, "minecraft:gold_ingot", nil, + }, + }, +} + ------------------------------------------------- -- Low-stock alerts -- When a tracked item drops below 'min', an alert @@ -394,6 +621,7 @@ local activity = { smelting = false, -- auto-smelt in progress defragging = false, -- defrag in progress composting = false, -- auto-compost in progress + crafting = false, -- crafting in progress } ------------------------------------------------- @@ -658,6 +886,7 @@ local smelterMon = nil local smelterMonName = nil local networkModem = nil local networkModemName = nil +local craftTurtleName = nil local function setupMonitor() mon = peripheral.wrap(MONITOR_SIDE) @@ -1167,6 +1396,203 @@ local function drawDashboard() touchZones = pendingZones end +------------------------------------------------- +-- Crafting helpers +------------------------------------------------- + +--- Get ingredient counts from a recipe's grid +local function getRecipeIngredients(recipe) + local ingredients = {} + for _, item in ipairs(recipe.grid) do + if item then + ingredients[item] = (ingredients[item] or 0) + 1 + end + end + return ingredients +end + +--- Check if a recipe can be crafted with current stock +local function canCraftRecipe(recipe) + local ingredients = getRecipeIngredients(recipe) + for itemName, needed in pairs(ingredients) do + local have = 0 + if cache.catalogue[itemName] then + for _, src in ipairs(cache.catalogue[itemName]) do + have = have + src.total + end + end + if have < needed then return false end + end + return true +end + +--- How many times a recipe can be crafted +local function maxCraftBatches(recipe) + local ingredients = getRecipeIngredients(recipe) + local minBatches = math.huge + for itemName, needed in pairs(ingredients) do + local have = 0 + if cache.catalogue[itemName] then + for _, src in ipairs(cache.catalogue[itemName]) do + have = have + src.total + end + end + local batches = math.floor(have / needed) + if batches < minBatches then minBatches = batches end + end + if minBatches == math.huge then return 0 end + return minBatches +end + +--- Get list of missing ingredients for a recipe +local function getMissingIngredients(recipe) + local ingredients = getRecipeIngredients(recipe) + local missing = {} + for itemName, needed in pairs(ingredients) do + local have = 0 + if cache.catalogue[itemName] then + for _, src in ipairs(cache.catalogue[itemName]) do + have = have + src.total + end + end + if have < needed then + table.insert(missing, { + name = itemName, + have = have, + need = needed, + }) + end + end + return missing +end + +--- Execute a craft via the networked turtle +local function craftItem(recipeIdx) + local recipe = CRAFTABLE[recipeIdx] + if not recipe then return false, "Invalid recipe" end + if not craftTurtleName then return false, "No turtle" end + if not networkModem then return false, "No modem" end + + local turtleInv = peripheral.wrap(craftTurtleName) + if not turtleInv then return false, "Turtle offline" end + + activity.crafting = true + needsRedraw = true + smelterNeedsRedraw = true + + local chests = getChests() + + -- 1. Clear turtle inventory (pull everything out) + local tc = turtleInv.list() + if tc then + for slot, item in pairs(tc) do + for _, ch in ipairs(chests) do + local n = turtleInv.pushItems(ch, slot) + if n and n > 0 then + adjustCache(item.name, ch, n) + break + end + end + end + end + + -- 2. Push ingredients into correct grid slots + for gridPos = 1, 9 do + local itemName = recipe.grid[gridPos] + if itemName then + local turtleSlot = GRID_TO_SLOT[gridPos] + local placed = false + if cache.catalogue[itemName] then + for _, source in ipairs(cache.catalogue[itemName]) do + local chest = peripheral.wrap(source.chest) + if chest then + for slot, slotItem in pairs(chest.list()) do + if slotItem.name == itemName then + local n = chest.pushItems(craftTurtleName, slot, 1, turtleSlot) + if n and n > 0 then + adjustCache(itemName, source.chest, -n) + placed = true + break + end + end + end + end + if placed then break end + end + end + if not placed then + -- Cleanup: pull items back from turtle + print("[CRAFT] Missing ingredient, aborting") + local cleanup = turtleInv.list() + if cleanup then + for s, it in pairs(cleanup) do + for _, ch in ipairs(chests) do + local n = turtleInv.pushItems(ch, s) + if n and n > 0 then + adjustCache(it.name, ch, n) + break + end + end + end + end + activity.crafting = false + needsRedraw = true + smelterNeedsRedraw = true + return false, "Missing ingredient" + end + end + end + + -- 3. Signal turtle to craft + networkModem.transmit(CRAFT_CHANNEL, CRAFT_REPLY_CHANNEL, { type = "craft", count = 1 }) + print(string.format("[CRAFT] Sent craft request: %s", recipe.output)) + + -- 4. Wait for reply with timeout + local timer = os.startTimer(5) + local success = false + local errMsg = "Turtle timeout" + while true do + local event = {os.pullEvent()} + if event[1] == "modem_message" and event[3] == CRAFT_REPLY_CHANNEL + and type(event[5]) == "table" and event[5].type == "craft_result" then + os.cancelTimer(timer) + success = event[5].success + errMsg = event[5].error + break + elseif event[1] == "timer" and event[2] == timer then + break + end + end + + -- 5. Pull all items from turtle back to chests + tc = turtleInv.list() + if tc then + for s, it in pairs(tc) do + for _, ch in ipairs(chests) do + local n = turtleInv.pushItems(ch, s) + if n and n > 0 then + adjustCache(it.name, ch, n) + print(string.format("[CRAFT] Result %s x%d -> %s", it.name, n, ch)) + break + end + end + end + end + + activity.crafting = false + needsRedraw = true + smelterNeedsRedraw = true + + if success then + local short = recipe.output:gsub("^minecraft:", ""):gsub("_", " ") + print(string.format("[CRAFT] OK: %s x%d", short, recipe.count)) + return true + else + print(string.format("[CRAFT] Failed: %s", errMsg or "unknown")) + return false, errMsg or "Craft failed" + end +end + ------------------------------------------------- -- Smelter Dashboard -------------------------------------------------