Implement auto-smelting functionality with furnace management and item handling

This commit is contained in:
MayaTheShy
2026-03-15 22:27:12 -04:00
parent d8a08528b9
commit 1a80b7d0d4

View File

@@ -6,6 +6,98 @@ local BARREL_NAME = "minecraft:barrel_0"
local POLL_INTERVAL = 2 -- seconds between barrel checks local POLL_INTERVAL = 2 -- seconds between barrel checks
local MONITOR_SIDE = "left" local MONITOR_SIDE = "left"
local SCAN_INTERVAL = 3 -- seconds between background scans local SCAN_INTERVAL = 3 -- seconds between background scans
local SMELT_INTERVAL = 3 -- seconds between furnace checks
local SMELT_RESERVE = 64 -- keep at least 1 stack of each raw material
-------------------------------------------------
-- Furnace types to manage
-------------------------------------------------
local FURNACE_TYPES = {
"minecraft:furnace",
"minecraft:smoker",
"minecraft:blast_furnace",
}
-- Furnace slots: 1 = input, 2 = fuel, 3 = output (standard Minecraft)
local SLOT_INPUT = 1
local SLOT_FUEL = 2
local SLOT_OUTPUT = 3
-------------------------------------------------
-- Smeltable items: input -> output
-- Items in chests matching a key here get auto-smelted.
-- Add/remove entries to control what gets cooked.
-------------------------------------------------
local SMELTABLE = {
-- Ores
["minecraft:raw_iron"] = "minecraft:iron_ingot",
["minecraft:raw_gold"] = "minecraft:gold_ingot",
["minecraft:raw_copper"] = "minecraft:copper_ingot",
["minecraft:iron_ore"] = "minecraft:iron_ingot",
["minecraft:gold_ore"] = "minecraft:gold_ingot",
["minecraft:copper_ore"] = "minecraft:copper_ingot",
["minecraft:deepslate_iron_ore"] = "minecraft:iron_ingot",
["minecraft:deepslate_gold_ore"] = "minecraft:gold_ingot",
["minecraft:deepslate_copper_ore"] = "minecraft:copper_ingot",
["minecraft:ancient_debris"] = "minecraft:netherite_scrap",
-- Sand / stone
["minecraft:sand"] = "minecraft:glass",
["minecraft:red_sand"] = "minecraft:glass",
["minecraft:cobblestone"] = "minecraft:stone",
["minecraft:stone"] = "minecraft:smooth_stone",
["minecraft:clay_ball"] = "minecraft:brick",
["minecraft:netherrack"] = "minecraft:nether_brick",
["minecraft:sandstone"] = "minecraft:smooth_sandstone",
-- Food
["minecraft:beef"] = "minecraft:cooked_beef",
["minecraft:porkchop"] = "minecraft:cooked_porkchop",
["minecraft:chicken"] = "minecraft:cooked_chicken",
["minecraft:mutton"] = "minecraft:cooked_mutton",
["minecraft:rabbit"] = "minecraft:cooked_rabbit",
["minecraft:cod"] = "minecraft:cooked_cod",
["minecraft:salmon"] = "minecraft:cooked_salmon",
["minecraft:potato"] = "minecraft:baked_potato",
["minecraft:kelp"] = "minecraft:dried_kelp",
-- Misc
["minecraft:wet_sponge"] = "minecraft:sponge",
["minecraft:cactus"] = "minecraft:green_dye",
["minecraft:sea_pickle"] = "minecraft:lime_dye",
["minecraft:log"] = "minecraft:charcoal",
["minecraft:oak_log"] = "minecraft:charcoal",
["minecraft:spruce_log"] = "minecraft:charcoal",
["minecraft:birch_log"] = "minecraft:charcoal",
["minecraft:jungle_log"] = "minecraft:charcoal",
["minecraft:acacia_log"] = "minecraft:charcoal",
["minecraft:dark_oak_log"] = "minecraft:charcoal",
["minecraft:mangrove_log"] = "minecraft:charcoal",
["minecraft:cherry_log"] = "minecraft:charcoal",
}
-- Fuel items, ordered by preference (best first)
-- burn_time = how many items one fuel smelts
local FUEL_LIST = {
{ name = "minecraft:coal", burn_time = 8 },
{ name = "minecraft:charcoal", burn_time = 8 },
{ name = "minecraft:coal_block", burn_time = 80 },
{ name = "minecraft:blaze_rod", burn_time = 12 },
{ name = "minecraft:dried_kelp_block", burn_time = 20 },
{ name = "minecraft:lava_bucket", burn_time = 100 },
{ name = "minecraft:oak_planks", burn_time = 1.5 },
{ name = "minecraft:spruce_planks",burn_time = 1.5 },
{ name = "minecraft:birch_planks", burn_time = 1.5 },
{ name = "minecraft:jungle_planks",burn_time = 1.5 },
{ name = "minecraft:acacia_planks",burn_time = 1.5 },
{ name = "minecraft:dark_oak_planks",burn_time = 1.5 },
{ name = "minecraft:mangrove_planks",burn_time = 1.5 },
{ name = "minecraft:cherry_planks",burn_time = 1.5 },
{ name = "minecraft:stick", burn_time = 0.5 },
}
-- Build a set for quick lookup
local FUEL_SET = {}
for _, f in ipairs(FUEL_LIST) do FUEL_SET[f.name] = true end
------------------------------------------------- -------------------------------------------------
-- Cached data (updated by background scanner) -- Cached data (updated by background scanner)
@@ -32,6 +124,7 @@ local activity = {
sorting = false, -- barrel sort in progress sorting = false, -- barrel sort in progress
dispensing = false, -- order in progress dispensing = false, -- order in progress
scanning = false, -- background scan in progress scanning = false, -- background scan in progress
smelting = false, -- auto-smelt in progress
} }
------------------------------------------------- -------------------------------------------------
@@ -48,6 +141,18 @@ local function getChests()
return chests return chests
end end
local function getFurnaces()
local furnaces = {}
for _, ftype in ipairs(FURNACE_TYPES) do
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == ftype then
table.insert(furnaces, name)
end
end
end
return furnaces
end
local function scanInventory(deviceName) local function scanInventory(deviceName)
local inv = peripheral.wrap(deviceName) local inv = peripheral.wrap(deviceName)
if not inv then return {} end if not inv then return {} end
@@ -120,6 +225,17 @@ local function refreshCache()
cache.dropperOk = peripheral.wrap(DROPPER_NAME) ~= nil cache.dropperOk = peripheral.wrap(DROPPER_NAME) ~= nil
cache.barrelOk = peripheral.wrap(BARREL_NAME) ~= nil cache.barrelOk = peripheral.wrap(BARREL_NAME) ~= nil
-- Count furnaces
local furnaceCount = 0
for _, ftype in ipairs(FURNACE_TYPES) do
for _, name in ipairs(peripheral.getNames()) do
if peripheral.getType(name) == ftype then
furnaceCount = furnaceCount + 1
end
end
end
cache.furnaceCount = furnaceCount
activity.scanning = false activity.scanning = false
end end
@@ -274,11 +390,15 @@ local function drawDashboard()
table.insert(statusParts, string.format(" Chests: %d", cache.chestCount)) table.insert(statusParts, string.format(" Chests: %d", cache.chestCount))
table.insert(statusParts, cache.dropperOk and "Dropper: OK" or "Dropper: --") table.insert(statusParts, cache.dropperOk and "Dropper: OK" or "Dropper: --")
table.insert(statusParts, cache.barrelOk and "Barrel: OK" or "Barrel: --") table.insert(statusParts, cache.barrelOk and "Barrel: OK" or "Barrel: --")
if cache.furnaceCount and cache.furnaceCount > 0 then
table.insert(statusParts, string.format("Furnaces: %d", cache.furnaceCount))
end
-- Activity indicators -- Activity indicators
local actParts = {} local actParts = {}
if activity.sorting then table.insert(actParts, "SORTING") end if activity.sorting then table.insert(actParts, "SORTING") end
if activity.dispensing then table.insert(actParts, "DISPENSING") end if activity.dispensing then table.insert(actParts, "DISPENSING") end
if activity.smelting then table.insert(actParts, "SMELTING") end
if activity.scanning then table.insert(actParts, "SCANNING") end if activity.scanning then table.insert(actParts, "SCANNING") end
monWrite(2, 2, table.concat(statusParts, " | "), colors.white, colors.gray) monWrite(2, 2, table.concat(statusParts, " | "), colors.white, colors.gray)
@@ -528,6 +648,8 @@ local function drawDashboard()
local bottomMsg = " Tap item to order " local bottomMsg = " Tap item to order "
if activity.dispensing then if activity.dispensing then
bottomMsg = " DISPENSING... " bottomMsg = " DISPENSING... "
elseif activity.smelting then
bottomMsg = " SMELTING... "
elseif activity.sorting then elseif activity.sorting then
bottomMsg = " SORTING BARREL... " bottomMsg = " SORTING BARREL... "
end end
@@ -592,6 +714,122 @@ local function sortBarrel()
needsRedraw = true needsRedraw = true
end end
-------------------------------------------------
-- Auto-smelt
-------------------------------------------------
local function autoSmelt()
local furnaces = getFurnaces()
if #furnaces == 0 then return end
local chests = getChests()
local catalogue = cache.catalogue
local didWork = false
for _, fname in ipairs(furnaces) do
local furnace = peripheral.wrap(fname)
if furnace then
local contents = furnace.list()
-- 1) Pull finished output (slot 3) back to chests
if contents[SLOT_OUTPUT] then
for _, chest in ipairs(chests) do
local n = furnace.pushItems(chest, SLOT_OUTPUT)
if n and n > 0 then
print(string.format("[SMELT] Output %s x%d -> %s",
contents[SLOT_OUTPUT].name, n, chest))
didWork = true
break
end
end
end
-- Re-read after output pull
contents = furnace.list()
-- 2) Refuel if fuel slot is empty or low
local fuelItem = contents[SLOT_FUEL]
local needFuel = not fuelItem or fuelItem.count < 8
if needFuel then
for _, fuel in ipairs(FUEL_LIST) do
if catalogue[fuel.name] then
for _, source in ipairs(catalogue[fuel.name]) do
local chest = peripheral.wrap(source.chest)
if chest then
for slot, slotItem in pairs(chest.list()) do
if slotItem.name == fuel.name then
local toMove = math.min(16, slotItem.count)
local n = chest.pushItems(fname, slot, toMove, SLOT_FUEL)
if n and n > 0 then
print(string.format("[SMELT] Fuel %s x%d -> %s",
fuel.name, n, fname))
didWork = true
needFuel = false
break
end
end
end
end
if not needFuel then break end
end
end
if not needFuel then break end
end
end
-- 3) Load smeltable items into empty input slot
local inputItem = contents[SLOT_INPUT]
if not inputItem then
-- Find something smeltable in chests
for itemName, _ in pairs(SMELTABLE) do
if catalogue[itemName] then
-- Count total of this item across all chests
local totalInStorage = 0
for _, src in ipairs(catalogue[itemName]) do
totalInStorage = totalInStorage + src.total
end
-- Only smelt the excess beyond SMELT_RESERVE
local available = totalInStorage - SMELT_RESERVE
if available <= 0 then
-- Skip: need to keep reserve
else
local loaded = false
local remaining = math.min(available, 64)
for _, source in ipairs(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 toMove = math.min(slotItem.count, remaining)
local n = chest.pushItems(fname, slot, toMove, SLOT_INPUT)
if n and n > 0 then
print(string.format("[SMELT] Input %s x%d -> %s (reserve %d)",
itemName, n, fname, math.max(0, totalInStorage - n)))
didWork = true
remaining = remaining - n
if remaining <= 0 then
loaded = true
break
end
end
end
end
end
if loaded then break end
end
if loaded or remaining < math.min(available, 64) then break end
end
end
end
end
end
end
return didWork
end
------------------------------------------------- -------------------------------------------------
-- Order -- Order
------------------------------------------------- -------------------------------------------------
@@ -801,7 +1039,24 @@ local function main()
end end
end, end,
-- Task 3: Dashboard redraw (event-driven, checks every 0.1s) -- Task 3: Auto-smelt
function()
while true do
activity.smelting = true
needsRedraw = true
local ok, didWork = pcall(autoSmelt)
activity.smelting = false
needsRedraw = true
-- If work was done, scan sooner to update cache
if ok and didWork then
pcall(refreshCache)
needsRedraw = true
end
sleep(SMELT_INTERVAL)
end
end,
-- Task 4: Dashboard redraw (event-driven, checks every 0.1s)
function() function()
needsRedraw = true needsRedraw = true
while true do while true do
@@ -821,7 +1076,7 @@ local function main()
end end
end, end,
-- Task 4: Touch event listener -- Task 5: Touch event listener
function() function()
while true do while true do
local event, side, x, y = os.pullEvent("monitor_touch") local event, side, x, y = os.pullEvent("monitor_touch")