Add crafting functionality to inventoryClient for display of available recipes and crafting status
This commit is contained in:
@@ -38,12 +38,15 @@ local activity = {
|
||||
smelting = false,
|
||||
defragging = false,
|
||||
composting = false,
|
||||
crafting = false,
|
||||
}
|
||||
|
||||
local activeAlerts = {}
|
||||
local smeltingPaused = false
|
||||
local disabledRecipes = {}
|
||||
local SMELTABLE = {} -- populated from master broadcast
|
||||
local CRAFTABLE = {} -- populated from master broadcast
|
||||
local craftTurtleOk = false
|
||||
local connected = false -- true once first state received
|
||||
|
||||
-------------------------------------------------
|
||||
@@ -175,6 +178,67 @@ local function hitTest(x, y)
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
-------------------------------------------------
|
||||
-- Crafting helpers (display-only, no peripheral calls)
|
||||
-------------------------------------------------
|
||||
|
||||
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
|
||||
|
||||
local function canCraftRecipe(recipe)
|
||||
local ingredients = getRecipeIngredients(recipe)
|
||||
local itemTotals = {}
|
||||
for _, item in ipairs(cache.itemList) do
|
||||
itemTotals[item.name] = item.total
|
||||
end
|
||||
for itemName, needed in pairs(ingredients) do
|
||||
if (itemTotals[itemName] or 0) < needed then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function maxCraftBatches(recipe)
|
||||
local ingredients = getRecipeIngredients(recipe)
|
||||
local itemTotals = {}
|
||||
for _, item in ipairs(cache.itemList) do
|
||||
itemTotals[item.name] = item.total
|
||||
end
|
||||
local minBatches = math.huge
|
||||
for itemName, needed in pairs(ingredients) do
|
||||
local batches = math.floor((itemTotals[itemName] or 0) / needed)
|
||||
if batches < minBatches then minBatches = batches end
|
||||
end
|
||||
if minBatches == math.huge then return 0 end
|
||||
return minBatches
|
||||
end
|
||||
|
||||
local function getMissingIngredients(recipe)
|
||||
local ingredients = getRecipeIngredients(recipe)
|
||||
local itemTotals = {}
|
||||
for _, item in ipairs(cache.itemList) do
|
||||
itemTotals[item.name] = item.total
|
||||
end
|
||||
local missing = {}
|
||||
for itemName, needed in pairs(ingredients) do
|
||||
local have = itemTotals[itemName] or 0
|
||||
if have < needed then
|
||||
table.insert(missing, { name = itemName, have = have, need = needed })
|
||||
end
|
||||
end
|
||||
return missing
|
||||
end
|
||||
|
||||
-------------------------------------------------
|
||||
-- Touch zone helpers
|
||||
-------------------------------------------------
|
||||
|
||||
local function addSmelterZone(x1, y1, x2, y2, action, data)
|
||||
table.insert(smelterPendingZones, {
|
||||
x1 = x1, y1 = y1, x2 = x2, y2 = y2,
|
||||
@@ -599,14 +663,22 @@ local function drawSmelterDashboard()
|
||||
|
||||
-- ===== Tab row =====
|
||||
monFill(4, colors.black)
|
||||
local tabStatusBg = smelterView == "status" and colors.purple or colors.gray
|
||||
local tabRecipesBg = smelterView == "recipes" and colors.purple or colors.gray
|
||||
local tabStatusBg = smelterView == "status" and colors.purple or colors.gray
|
||||
local tabSmeltBg = smelterView == "smelt" and colors.purple or colors.gray
|
||||
local tabCraftBg = smelterView == "craft" and colors.purple or colors.gray
|
||||
local tabMissingBg = smelterView == "missing" and colors.purple or colors.gray
|
||||
local bx1, by1, bx2, by2
|
||||
bx1, by1, bx2, by2 = drawButton(2, 4, "Status", colors.white, tabStatusBg)
|
||||
addSmelterZone(bx1, by1, bx2, by2, "tab", "status")
|
||||
|
||||
bx1, by1, bx2, by2 = drawButton(bx2 + 2, 4, "Recipes", colors.white, tabRecipesBg)
|
||||
addSmelterZone(bx1, by1, bx2, by2, "tab", "recipes")
|
||||
bx1, by1, bx2, by2 = drawButton(bx2 + 2, 4, "Smelt", colors.white, tabSmeltBg)
|
||||
addSmelterZone(bx1, by1, bx2, by2, "tab", "smelt")
|
||||
|
||||
bx1, by1, bx2, by2 = drawButton(bx2 + 2, 4, "Craft", colors.white, tabCraftBg)
|
||||
addSmelterZone(bx1, by1, bx2, by2, "tab", "craft")
|
||||
|
||||
bx1, by1, bx2, by2 = drawButton(bx2 + 2, 4, "Missing", colors.white, tabMissingBg)
|
||||
addSmelterZone(bx1, by1, bx2, by2, "tab", "missing")
|
||||
|
||||
if smelterView == "status" then
|
||||
-- ===== Furnace Status View =====
|
||||
@@ -705,8 +777,8 @@ local function drawSmelterDashboard()
|
||||
row = row + 1
|
||||
end
|
||||
|
||||
else
|
||||
-- ===== Recipe Manager View =====
|
||||
elseif smelterView == "smelt" then
|
||||
-- ===== Smelt Recipe Manager View =====
|
||||
-- Build item totals lookup from itemList
|
||||
local itemTotals = {}
|
||||
for _, item in ipairs(cache.itemList) do
|
||||
@@ -800,6 +872,163 @@ local function drawSmelterDashboard()
|
||||
row = row + 1
|
||||
end
|
||||
|
||||
while row <= h - 2 do
|
||||
monFill(row, colors.black)
|
||||
row = row + 1
|
||||
end
|
||||
|
||||
elseif smelterView == "craft" then
|
||||
-- ===== Available Crafting Recipes =====
|
||||
|
||||
-- Turtle status on tab row
|
||||
local tLabel = craftTurtleOk and " Turtle OK " or " No Turtle "
|
||||
local tBg = craftTurtleOk and colors.lime or colors.red
|
||||
local tFg = craftTurtleOk and colors.black or colors.white
|
||||
monWrite(w - #tLabel, 4, tLabel, tFg, tBg)
|
||||
|
||||
local availList = {}
|
||||
for idx, recipe in ipairs(CRAFTABLE) do
|
||||
if canCraftRecipe(recipe) then
|
||||
local short = recipe.output:gsub("^minecraft:", ""):gsub("_", " ")
|
||||
short = short:sub(1,1):upper() .. short:sub(2)
|
||||
local batches = maxCraftBatches(recipe)
|
||||
table.insert(availList, {
|
||||
idx = idx,
|
||||
short = short,
|
||||
count = recipe.count,
|
||||
batches = batches,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
monFill(5, colors.gray)
|
||||
local makeCol = w - 6
|
||||
monWrite(2, 5, "#", colors.lightGray, colors.gray)
|
||||
monWrite(4, 5, "Output", colors.lightGray, colors.gray)
|
||||
monWrite(math.floor(w * 0.45), 5, "Yield", colors.lightGray, colors.gray)
|
||||
monWrite(math.floor(w * 0.60), 5, "Can Make", colors.lightGray, colors.gray)
|
||||
monWrite(makeCol, 5, "Go", colors.lightGray, colors.gray)
|
||||
|
||||
local maxRows = h - 8
|
||||
if maxRows < 1 then maxRows = 1 end
|
||||
smelterTotalPages = math.max(1, math.ceil(#availList / maxRows))
|
||||
if smelterPage > smelterTotalPages then smelterPage = smelterTotalPages end
|
||||
if smelterPage < 1 then smelterPage = 1 end
|
||||
|
||||
local startIdx = (smelterPage - 1) * maxRows + 1
|
||||
local endIdx = math.min(startIdx + maxRows - 1, #availList)
|
||||
|
||||
local row = 6
|
||||
if #availList == 0 then
|
||||
monFill(7, colors.black)
|
||||
monCenter(7, "No recipes available to craft", colors.gray, colors.black)
|
||||
row = 8
|
||||
else
|
||||
for i = startIdx, endIdx do
|
||||
local r = availList[i]
|
||||
local y = row
|
||||
local rowBg = ((i - startIdx) % 2 == 0) and colors.black or colors.gray
|
||||
monFill(y, rowBg)
|
||||
|
||||
monWrite(2, y, string.format("%2d", i), colors.lightBlue, rowBg)
|
||||
|
||||
local maxNameLen = math.floor(w * 0.40)
|
||||
local nameDisplay = r.short
|
||||
if #nameDisplay > maxNameLen then
|
||||
nameDisplay = nameDisplay:sub(1, maxNameLen - 2) .. ".."
|
||||
end
|
||||
monWrite(4, y, nameDisplay, colors.white, rowBg)
|
||||
|
||||
monWrite(math.floor(w * 0.45), y, "x" .. r.count, colors.yellow, rowBg)
|
||||
monWrite(math.floor(w * 0.60), y,
|
||||
string.format("x%d", r.batches), colors.lime, rowBg)
|
||||
|
||||
if craftTurtleOk then
|
||||
monWrite(makeCol, y, " MAKE ", colors.white, colors.green)
|
||||
addSmelterZone(makeCol, y, makeCol + 5, y, "craft", r.idx)
|
||||
else
|
||||
monWrite(makeCol, y, " ---- ", colors.gray, colors.black)
|
||||
end
|
||||
|
||||
row = row + 1
|
||||
end
|
||||
end
|
||||
|
||||
while row <= h - 2 do
|
||||
monFill(row, colors.black)
|
||||
row = row + 1
|
||||
end
|
||||
|
||||
elseif smelterView == "missing" then
|
||||
-- ===== Unavailable Crafting Recipes =====
|
||||
|
||||
local missList = {}
|
||||
for idx, recipe in ipairs(CRAFTABLE) do
|
||||
if not canCraftRecipe(recipe) then
|
||||
local short = recipe.output:gsub("^minecraft:", ""):gsub("_", " ")
|
||||
short = short:sub(1,1):upper() .. short:sub(2)
|
||||
local missing = getMissingIngredients(recipe)
|
||||
local parts = {}
|
||||
for _, m in ipairs(missing) do
|
||||
local mShort = m.name:gsub("^minecraft:", ""):gsub("_", " ")
|
||||
table.insert(parts, string.format("%s %d/%d", mShort, m.have, m.need))
|
||||
end
|
||||
table.insert(missList, {
|
||||
idx = idx,
|
||||
short = short,
|
||||
count = recipe.count,
|
||||
summary = table.concat(parts, ", "),
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
monFill(5, colors.gray)
|
||||
monWrite(2, 5, "#", colors.lightGray, colors.gray)
|
||||
monWrite(4, 5, "Output", colors.lightGray, colors.gray)
|
||||
monWrite(math.floor(w * 0.35), 5, "Missing (have/need)", colors.lightGray, colors.gray)
|
||||
|
||||
local maxRows = h - 8
|
||||
if maxRows < 1 then maxRows = 1 end
|
||||
smelterTotalPages = math.max(1, math.ceil(#missList / maxRows))
|
||||
if smelterPage > smelterTotalPages then smelterPage = smelterTotalPages end
|
||||
if smelterPage < 1 then smelterPage = 1 end
|
||||
|
||||
local startIdx = (smelterPage - 1) * maxRows + 1
|
||||
local endIdx = math.min(startIdx + maxRows - 1, #missList)
|
||||
|
||||
local row = 6
|
||||
if #missList == 0 then
|
||||
monFill(7, colors.black)
|
||||
monCenter(7, "All recipes can be crafted!", colors.lime, colors.black)
|
||||
row = 8
|
||||
else
|
||||
for i = startIdx, endIdx do
|
||||
local r = missList[i]
|
||||
local y = row
|
||||
local rowBg = ((i - startIdx) % 2 == 0) and colors.black or colors.gray
|
||||
monFill(y, rowBg)
|
||||
|
||||
monWrite(2, y, string.format("%2d", i), colors.lightBlue, rowBg)
|
||||
|
||||
local nameCol = math.floor(w * 0.35) - 5
|
||||
local nameDisplay = r.short .. " x" .. r.count
|
||||
if #nameDisplay > nameCol then
|
||||
nameDisplay = nameDisplay:sub(1, nameCol - 2) .. ".."
|
||||
end
|
||||
monWrite(4, y, nameDisplay, colors.white, rowBg)
|
||||
|
||||
local missCol = math.floor(w * 0.35)
|
||||
local missW = w - missCol - 1
|
||||
local summaryDisplay = r.summary
|
||||
if #summaryDisplay > missW then
|
||||
summaryDisplay = summaryDisplay:sub(1, missW - 2) .. ".."
|
||||
end
|
||||
monWrite(missCol, y, summaryDisplay, colors.red, rowBg)
|
||||
|
||||
row = row + 1
|
||||
end
|
||||
end
|
||||
|
||||
while row <= h - 2 do
|
||||
monFill(row, colors.black)
|
||||
row = row + 1
|
||||
@@ -822,14 +1051,27 @@ local function drawSmelterDashboard()
|
||||
|
||||
-- ===== Bottom accent =====
|
||||
monFill(h, colors.purple)
|
||||
local enabledCount = 0
|
||||
local totalRecipes = 0
|
||||
for _ in pairs(SMELTABLE) do totalRecipes = totalRecipes + 1 end
|
||||
for inputName in pairs(SMELTABLE) do
|
||||
if not disabledRecipes[inputName] then enabledCount = enabledCount + 1 end
|
||||
local bottomMsg = ""
|
||||
if smelterView == "status" or smelterView == "smelt" then
|
||||
local enabledCount = 0
|
||||
local totalRecipes = 0
|
||||
for _ in pairs(SMELTABLE) do totalRecipes = totalRecipes + 1 end
|
||||
for inputName in pairs(SMELTABLE) do
|
||||
if not disabledRecipes[inputName] then enabledCount = enabledCount + 1 end
|
||||
end
|
||||
bottomMsg = string.format(" Smelt: %d/%d enabled ", enabledCount, totalRecipes)
|
||||
if activity.smelting then bottomMsg = " SMELTING... " end
|
||||
elseif smelterView == "craft" then
|
||||
bottomMsg = " Tap MAKE to craft "
|
||||
if activity.crafting then bottomMsg = " CRAFTING... " end
|
||||
elseif smelterView == "missing" then
|
||||
local totalC = #CRAFTABLE
|
||||
local availC = 0
|
||||
for _, r in ipairs(CRAFTABLE) do
|
||||
if canCraftRecipe(r) then availC = availC + 1 end
|
||||
end
|
||||
bottomMsg = string.format(" Available: %d/%d recipes ", availC, totalC)
|
||||
end
|
||||
local bottomMsg = string.format(" Recipes: %d/%d enabled ", enabledCount, totalRecipes)
|
||||
if activity.smelting then bottomMsg = " SMELTING... " end
|
||||
monCenter(h, bottomMsg, colors.pink, colors.purple)
|
||||
|
||||
draw.setVisible(true)
|
||||
@@ -966,6 +1208,15 @@ local function handleSmelterTouch(x, y)
|
||||
smelterPage = smelterPage + 1
|
||||
end
|
||||
smelterNeedsRedraw = true
|
||||
|
||||
elseif action == "craft" then
|
||||
sendToMaster({ type = "craft", recipeIdx = data })
|
||||
local recipe = CRAFTABLE[data]
|
||||
if recipe then
|
||||
local short = recipe.output:gsub("^minecraft:", ""):gsub("_", " ")
|
||||
print("[CRAFT-UI] Craft request sent: " .. short)
|
||||
end
|
||||
smelterNeedsRedraw = true
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1051,6 +1302,12 @@ local function main()
|
||||
if message.smeltable then
|
||||
SMELTABLE = message.smeltable
|
||||
end
|
||||
if message.craftable then
|
||||
CRAFTABLE = message.craftable
|
||||
end
|
||||
if message.craftTurtleOk ~= nil then
|
||||
craftTurtleOk = message.craftTurtleOk
|
||||
end
|
||||
|
||||
if not connected then
|
||||
connected = true
|
||||
@@ -1071,6 +1328,18 @@ local function main()
|
||||
else
|
||||
print("[WARN] " .. statusMessage)
|
||||
end
|
||||
|
||||
elseif channel == CLIENT_CHANNEL and type(message) == "table" and message.type == "craft_result" then
|
||||
-- Craft result from master
|
||||
statusMessage = message.message or ""
|
||||
statusColor = message.success and colors.lime or colors.red
|
||||
statusTimer = 5
|
||||
smelterNeedsRedraw = true
|
||||
if message.success then
|
||||
print("[OK] " .. statusMessage)
|
||||
else
|
||||
print("[WARN] " .. statusMessage)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
Reference in New Issue
Block a user