feat: implement auto-crafting feature for excess stock management

This commit is contained in:
MayaTheShy
2026-03-25 18:07:26 -04:00
parent 2c99169ce9
commit f327f82677
6 changed files with 99 additions and 0 deletions

14
data/auto_craft.lua Normal file
View File

@@ -0,0 +1,14 @@
-- Auto-craft rules: items that should be automatically crafted when stock exceeds reserve.
-- The system will keep `reserve` of the input item and craft all excess into the output.
-- Requires a crafting turtle to be connected.
--
-- Format: { input = "mod:item", reserve = N, output = "mod:output_item" }
-- The output item must have a recipe in data/craftable.lua (or learned via recipeBook).
-- Recursive crafting is used, so intermediate steps are handled automatically.
return {
-- Convert excess bamboo into planks (keeps 128 raw bamboo as reserve)
{ input = "minecraft:bamboo", reserve = 128, output = "minecraft:bamboo_planks" },
-- Convert excess bamboo blocks into planks too (keeps 64 blocks as reserve)
{ input = "minecraft:bamboo_block", reserve = 64, output = "minecraft:bamboo_planks" },
}

View File

@@ -443,6 +443,25 @@ local function main()
end end
end, end,
-- Task 5c: Auto-craft excess items into target products
function()
if #cfg.AUTO_CRAFT_RULES == 0 then
while true do sleep(3600) end
end
sleep(12) -- let initial scan + discard settle first
log.info("AUTOCRAFT", "Auto-craft active with %d rule(s)", #cfg.AUTO_CRAFT_RULES)
while true do
if ctx.craftTurtleName then
activity.autocrafting = true
state.needsRedraw = true
pcall(ops.autoCraft)
activity.autocrafting = false
state.needsRedraw = true
end
sleep(cfg.AUTO_CRAFT_INTERVAL)
end
end,
-- Task 6: Low-stock alert checker -- Task 6: Low-stock alert checker
function() function()
sleep(5) sleep(5)

View File

@@ -64,6 +64,9 @@ C.TRASH_DROPPERS = { -- droppers facing lava/void for destroying
} }
C.DISCARD_INTERVAL = 5 -- seconds between discard checks C.DISCARD_INTERVAL = 5 -- seconds between discard checks
-- Auto-craft (overridable via config file)
C.AUTO_CRAFT_INTERVAL = 10 -- seconds between auto-craft checks
-- Peripheral -- Peripheral
C.PERIPHERAL_CACHE_TTL = 5 C.PERIPHERAL_CACHE_TTL = 5
@@ -126,6 +129,7 @@ function C.loadConfig()
if cfg.compostHopper then C.COMPOST_HOPPER = cfg.compostHopper end if cfg.compostHopper then C.COMPOST_HOPPER = cfg.compostHopper end
if cfg.trashDroppers then C.TRASH_DROPPERS = cfg.trashDroppers end if cfg.trashDroppers then C.TRASH_DROPPERS = cfg.trashDroppers end
if cfg.discardInterval then C.DISCARD_INTERVAL = cfg.discardInterval end if cfg.discardInterval then C.DISCARD_INTERVAL = cfg.discardInterval end
if cfg.autoCraftInterval then C.AUTO_CRAFT_INTERVAL = cfg.autoCraftInterval end
if cfg.stockLimits then if cfg.stockLimits then
for item, limit in pairs(cfg.stockLimits) do for item, limit in pairs(cfg.stockLimits) do
C.STOCK_LIMITS[item] = limit -- merge / override per-item C.STOCK_LIMITS[item] = limit -- merge / override per-item
@@ -149,6 +153,7 @@ local _compostData = dofile(_path("data/compostable.lua"))
C.COMPOSTABLE = _compostData.items C.COMPOSTABLE = _compostData.items
C.COMPOST_TRASH = _compostData.trash C.COMPOST_TRASH = _compostData.trash
C.STOCK_LIMITS = dofile(_path("data/stock_limits.lua")) C.STOCK_LIMITS = dofile(_path("data/stock_limits.lua"))
C.AUTO_CRAFT_RULES = dofile(_path("data/auto_craft.lua"))
C.LOW_STOCK_ALERTS = dofile(_path("data/alerts.lua")) C.LOW_STOCK_ALERTS = dofile(_path("data/alerts.lua"))
-- Recipe book: merges built-in recipes + user-learned recipes -- Recipe book: merges built-in recipes + user-learned recipes

View File

@@ -80,6 +80,7 @@ local function getActivityString()
if activity.defragging then table.insert(parts, "DEFRAG") end if activity.defragging then table.insert(parts, "DEFRAG") end
if activity.composting then table.insert(parts, "COMPOST") end if activity.composting then table.insert(parts, "COMPOST") end
if activity.discarding then table.insert(parts, "DISCARD") end if activity.discarding then table.insert(parts, "DISCARD") end
if activity.autocrafting then table.insert(parts, "AUTOCRAFT") end
if #parts > 0 then if #parts > 0 then
return table.concat(parts, " | ") return table.concat(parts, " | ")
end end
@@ -93,6 +94,7 @@ local function getBottomMessage()
elseif activity.defragging then return "DEFRAGMENTING..." elseif activity.defragging then return "DEFRAGMENTING..."
elseif activity.composting then return "COMPOSTING..." elseif activity.composting then return "COMPOSTING..."
elseif activity.discarding then return "DISCARDING EXCESS..." elseif activity.discarding then return "DISCARDING EXCESS..."
elseif activity.autocrafting then return "AUTO-CRAFTING..."
end end
return "Tap item to order" return "Tap item to order"
end end

View File

@@ -851,6 +851,64 @@ function O.discardExcess()
return didWork return didWork
end end
-------------------------------------------------
-- Auto-craft excess stock into target items
-------------------------------------------------
function O.autoCraft()
if #cfg.AUTO_CRAFT_RULES == 0 then return false end
if not ctx.craftEngine then return false end
if not ctx.craftTurtleName then return false end
local catalogue = cache.catalogue
local didWork = false
for _, rule in ipairs(cfg.AUTO_CRAFT_RULES) do
local inputName = rule.input
local reserve = rule.reserve or 0
local outputName = rule.output
if catalogue[inputName] then
local totalInStorage = 0
for _, src in ipairs(catalogue[inputName]) do
totalInStorage = totalInStorage + src.total
end
local excess = totalInStorage - reserve
if excess > 0 then
-- Figure out how many input items per craft batch
local recipe = cfg.recipeBook.getCraftingRecipe(outputName)
if recipe then
local ingredients = cfg.recipeBook.getIngredients(recipe)
local inputPerCraft = ingredients[inputName] or 0
if inputPerCraft > 0 then
-- How many batches can we do with the excess?
local batches = math.floor(excess / inputPerCraft)
-- Limit to a reasonable amount per cycle to avoid blocking
batches = math.min(batches, 64)
if batches > 0 then
local craftCount = batches * recipe.count
log.info("AUTOCRAFT", "%s: %d excess (reserve %d), crafting %d x %s",
inputName, excess, reserve, craftCount, outputName)
local ok, err = O.recursiveCraft(outputName, craftCount)
if ok then
didWork = true
log.info("AUTOCRAFT", "Crafted %s x%d", outputName, craftCount)
else
log.warn("AUTOCRAFT", "Failed to craft %s: %s", outputName, tostring(err))
end
end
end
else
log.warn("AUTOCRAFT", "No recipe found for output: %s", outputName)
end
end
end
end
return didWork
end
------------------------------------------------- -------------------------------------------------
-- Low-stock alert checker -- Low-stock alert checker
------------------------------------------------- -------------------------------------------------

View File

@@ -42,6 +42,7 @@ S.activity = {
composting = false, composting = false,
crafting = false, crafting = false,
discarding = false, discarding = false,
autocrafting = false,
} }
------------------------------------------------- -------------------------------------------------