Files
Inventory-Manager-CC/craftingTurtle.lua

286 lines
9.1 KiB
Lua

-- Crafting Turtle Script
-- Run this on the crafting turtle (turtle with crafting table).
-- 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.
-------------------------------------------------
-- Default configuration (overridden by .turtle_config)
-------------------------------------------------
-- 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}
-------------------------------------------------
-- Load config from file if present
-------------------------------------------------
local _baseDir = fs.getDir(shell.getRunningProgram())
local function _path(rel) return fs.combine(_baseDir, rel) end
local TURTLE_CONFIG_FILE = _path(".turtle_config")
local function loadConfig()
if not fs.exists(TURTLE_CONFIG_FILE) then return end
local f = fs.open(TURTLE_CONFIG_FILE, "r")
local data = f.readAll()
f.close()
local ok, cfg = pcall(textutils.unserialiseJSON, data)
if not ok or not cfg then
print("[WARN] Failed to parse " .. TURTLE_CONFIG_FILE)
return
end
if cfg.craftChannel then CRAFT_CHANNEL = cfg.craftChannel end
if cfg.craftReplyChannel then CRAFT_REPLY_CHANNEL = cfg.craftReplyChannel end
print("[CONFIG] Loaded from " .. TURTLE_CONFIG_FILE)
end
loadConfig()
-------------------------------------------------
-- Setup
-------------------------------------------------
print("=================================")
print(" Crafting Turtle (Modem-Based)")
print("=================================")
print("")
-- Verify this is a crafting turtle
if not turtle or not turtle.craft then
print("[ERR] No turtle.craft() available!")
print(" This must be a Crafting Turtle.")
return
end
-- Find wired modem and get our network name
local modem = nil
local modemSide = nil
local selfName = nil
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
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()
local parts = {}
for _, slot in ipairs(CRAFT_SLOTS) do
local detail = turtle.getItemDetail(slot)
if detail then
table.insert(parts, string.format(" slot %d: %s x%d", slot, detail.name, detail.count))
end
end
return table.concat(parts, "\n")
end
-------------------------------------------------
-- Craft command handler
-------------------------------------------------
local function handleCraftCommand(message)
-- message format from master:
-- {
-- type = "craft_request",
-- recipeIdx = <number>,
-- output = <string>, -- expected output item name
-- slots = { [turtleSlot] = { chestName=..., chestSlot=..., itemName=..., count=... }, ... },
-- returnChests = { "chest_0", "chest_1", ... },
-- }
local slots = message.slots
local returnChests = message.returnChests
local output = message.output or "unknown"
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
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
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
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