Add crafting functionality for networked turtle with recipe management

This commit is contained in:
MayaTheShy
2026-03-16 00:04:14 -04:00
parent 32ae5611e7
commit cd3e4e1fd9

View File

@@ -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
-------------------------------------------------