diff --git a/craftingTurtle.lua b/craftingTurtle.lua index c66ce80..4c7eef8 100644 --- a/craftingTurtle.lua +++ b/craftingTurtle.lua @@ -1,20 +1,26 @@ -- Crafting Turtle Script -- Run this on the crafting turtle (turtle with crafting table). --- Polls its own crafting grid slots and auto-crafts when items appear. --- No modem required — works via the wired network inventory access. +-- Listens for craft commands from the master via wired modem. +-- Pulls ingredients from chests, crafts, and pushes results back. +-- Requires a wired modem attached to the turtle. + +------------------------------------------------- +-- Configuration +------------------------------------------------- + +-- Modem channels (must match inventoryManager.lua) +local CRAFT_CHANNEL = 4203 -- master -> turtle (craft requests) +local CRAFT_REPLY_CHANNEL = 4204 -- turtle -> master (craft results) -- Crafting grid slots in a turtle's 4x4 inventory local CRAFT_SLOTS = {1, 2, 3, 5, 6, 7, 9, 10, 11} --- How long to wait after detecting items before crafting, --- giving the master time to finish placing all ingredients. -local SETTLE_DELAY = 1.0 - --- Polling interval (seconds) -local POLL_INTERVAL = 0.5 +------------------------------------------------- +-- Setup +------------------------------------------------- print("=================================") -print(" Crafting Turtle (Auto-Craft)") +print(" Crafting Turtle (Modem-Based)") print("=================================") print("") @@ -25,17 +31,72 @@ if not turtle or not turtle.craft then return end -print("Polling crafting slots for items...") -print("Ready. Items placed in grid will be auto-crafted.") -print("") +-- Find wired modem and get our network name +local modem = nil +local modemSide = nil +local selfName = nil -local function hasCraftingItems() - for _, slot in ipairs(CRAFT_SLOTS) do - if turtle.getItemCount(slot) > 0 then - return true +for _, side in ipairs({"top", "bottom", "left", "right", "front", "back"}) do + if peripheral.getType(side) == "modem" then + local m = peripheral.wrap(side) + if m.getNameLocal then + local name = m.getNameLocal() + if name then + modem = m + modemSide = side + selfName = name + break + end end end - return false +end + +if not modem or not selfName then + print("[ERR] No wired modem found!") + print(" Attach a wired modem to the turtle") + print(" and connect it to the network.") + return +end + +print("[OK] Modem: " .. modemSide) +print("[OK] Network name: " .. selfName) + +-- Open channel for receiving craft commands +modem.open(CRAFT_CHANNEL) +print("[OK] Listening on channel " .. CRAFT_CHANNEL) + +-- Wrap our own inventory peripheral for pullItems/pushItems +local selfInv = peripheral.wrap(selfName) +if not selfInv then + print("[ERR] Cannot wrap own peripheral: " .. selfName) + print(" Make sure the wired modem is connected.") + return +end + +print("[OK] Self-inventory peripheral ready") +print("") +print("Waiting for craft commands from master...") +print("") + +------------------------------------------------- +-- Helpers +------------------------------------------------- + +local function clearInventory(chests) + -- Push all items from all 16 turtle slots back to chests + local cleared = 0 + for slot = 1, 16 do + if turtle.getItemCount(slot) > 0 then + for _, chestName in ipairs(chests) do + local ok, n = pcall(selfInv.pushItems, chestName, slot) + if ok and n and n > 0 then + cleared = cleared + n + break + end + end + end + end + return cleared end local function describeGrid() @@ -49,40 +110,150 @@ local function describeGrid() return table.concat(parts, "\n") end -while true do - if hasCraftingItems() then - -- Wait for the master to finish placing all ingredients - print("[CRAFT] Items detected in grid, waiting " .. SETTLE_DELAY .. "s...") - sleep(SETTLE_DELAY) +------------------------------------------------- +-- Craft command handler +------------------------------------------------- - -- Check again — items might have been pulled back (abort) - if hasCraftingItems() then - print("[CRAFT] Grid contents:") - print(describeGrid()) - print("[CRAFT] Attempting craft...") +local function handleCraftCommand(message) + -- message format from master: + -- { + -- type = "craft_request", + -- recipeIdx = , + -- output = , -- expected output item name + -- slots = { [turtleSlot] = { chestName=..., chestSlot=..., itemName=..., count=... }, ... }, + -- returnChests = { "chest_0", "chest_1", ... }, + -- } - local ok, err = turtle.craft() + local slots = message.slots + local returnChests = message.returnChests + local output = message.output or "unknown" - if ok then - -- Show what was crafted - local results = {} - for slot = 1, 16 do - local detail = turtle.getItemDetail(slot) - if detail then - table.insert(results, string.format("%s x%d", detail.name, detail.count)) - end - end - print("[CRAFT] Success! Result: " .. table.concat(results, ", ")) - else - print("[CRAFT] Failed: " .. tostring(err)) - end + if not slots or not returnChests then + print("[CRAFT] Invalid command: missing slots or returnChests") + return { type = "craft_result", success = false, error = "Invalid command" } + end - -- Wait for master to pull results before polling again - sleep(2) + print(string.format("[CRAFT] Received craft request: %s", output)) + + -- 1. Clear turtle inventory (safety — should be empty) + clearInventory(returnChests) + + -- 2. Pull ingredients from chests into crafting grid slots + local placedItems = {} -- turtleSlot -> itemName + local allPlaced = true + + for turtleSlotStr, info in pairs(slots) do + local turtleSlot = tonumber(turtleSlotStr) + local chestName = info.chestName + local chestSlot = info.chestSlot + local itemName = info.itemName + local count = info.count or 1 + + print(string.format("[CRAFT] Pulling %s from %s slot %d -> turtle slot %d", + itemName, chestName, chestSlot, turtleSlot)) + + local ok, n = pcall(selfInv.pullItems, chestName, chestSlot, count, turtleSlot) + if ok and n and n > 0 then + placedItems[turtleSlot] = itemName + print(string.format("[CRAFT] Placed %s x%d in slot %d", itemName, n, turtleSlot)) else - print("[CRAFT] Items removed before crafting (aborted by master)") + if not ok then + print(string.format("[CRAFT] pullItems error: %s", tostring(n))) + else + print(string.format("[CRAFT] pullItems returned %s (expected %d)", tostring(n), count)) + end + allPlaced = false + break end end - sleep(POLL_INTERVAL) + if not allPlaced then + -- Abort: push everything back + print("[CRAFT] Failed to place all ingredients, aborting") + local returnedItems = {} + for slot, itemName in pairs(placedItems) do + returnedItems[tostring(slot)] = itemName + end + clearInventory(returnChests) + return { + type = "craft_result", + success = false, + error = "Failed to pull ingredients", + returnedItems = returnedItems, + } + end + + -- 3. Craft + print("[CRAFT] Grid contents:") + print(describeGrid()) + print("[CRAFT] Attempting craft...") + + local craftOk, craftErr = turtle.craft() + + if not craftOk then + print("[CRAFT] Failed: " .. tostring(craftErr)) + -- Collect what's still in the grid for the master to track + local returnedItems = {} + for slot, itemName in pairs(placedItems) do + returnedItems[tostring(slot)] = itemName + end + -- Push ingredients back + clearInventory(returnChests) + return { + type = "craft_result", + success = false, + error = "Craft failed: " .. tostring(craftErr), + returnedItems = returnedItems, + } + end + + -- 4. Collect results info + local results = {} + local totalOutput = 0 + for slot = 1, 16 do + local detail = turtle.getItemDetail(slot) + if detail then + table.insert(results, { name = detail.name, count = detail.count, slot = slot }) + totalOutput = totalOutput + detail.count + end + end + + print("[CRAFT] Success! Output:") + for _, r in ipairs(results) do + print(string.format(" %s x%d", r.name, r.count)) + end + + -- 5. Push results back to chests + local pushed = clearInventory(returnChests) + print(string.format("[CRAFT] Pushed %d items back to storage", pushed)) + + return { + type = "craft_result", + success = true, + output = output, + totalOutput = totalOutput, + results = results, + } +end + +------------------------------------------------- +-- Main loop: listen for modem commands +------------------------------------------------- + +while true do + local event, side, channel, replyChannel, message, distance = os.pullEvent("modem_message") + + if channel == CRAFT_CHANNEL and type(message) == "table" then + if message.type == "craft_request" then + local result = handleCraftCommand(message) + -- Send result back to master + modem.transmit(CRAFT_REPLY_CHANNEL, CRAFT_CHANNEL, result) + elseif message.type == "ping" then + -- Health check from master + modem.transmit(CRAFT_REPLY_CHANNEL, CRAFT_CHANNEL, { + type = "pong", + name = selfName, + }) + end + end end