Refactor inventory manager for touch UI support and improved dashboard functionality
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
-- Inventory Manager: Auto-sort barrel & order items to dropper
|
-- Inventory Manager: Touch UI on monitor
|
||||||
-- Main computer (networked). Computer 1 sits next to dropper_0 and auto-dispenses.
|
-- Main computer (networked). Computer 1 sits next to dropper_0 and auto-dispenses.
|
||||||
|
|
||||||
local DROPPER_NAME = "minecraft:dropper_9"
|
local DROPPER_NAME = "minecraft:dropper_9"
|
||||||
@@ -11,7 +11,6 @@ local DASH_REFRESH = 3 -- seconds between dashboard refreshes
|
|||||||
-- Inventory helpers
|
-- Inventory helpers
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
-- Get all chest peripheral names on the network
|
|
||||||
local function getChests()
|
local function getChests()
|
||||||
local chests = {}
|
local chests = {}
|
||||||
for _, name in ipairs(peripheral.getNames()) do
|
for _, name in ipairs(peripheral.getNames()) do
|
||||||
@@ -22,7 +21,6 @@ local function getChests()
|
|||||||
return chests
|
return chests
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Scan a single inventory: returns { [itemName] = { total=N, slots={ [slot]={name,count} } } }
|
|
||||||
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
|
||||||
@@ -37,7 +35,6 @@ local function scanInventory(deviceName)
|
|||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Build a full catalogue: itemName -> { {chest=name, total=N}, ... }
|
|
||||||
local function buildCatalogue()
|
local function buildCatalogue()
|
||||||
local catalogue = {}
|
local catalogue = {}
|
||||||
for _, chest in ipairs(getChests()) do
|
for _, chest in ipairs(getChests()) do
|
||||||
@@ -53,11 +50,10 @@ local function buildCatalogue()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Monitor Dashboard
|
-- Monitor setup
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
local mon = nil
|
local mon = nil
|
||||||
local buf = nil -- offscreen buffer to prevent flickering
|
|
||||||
|
|
||||||
local function setupMonitor()
|
local function setupMonitor()
|
||||||
mon = peripheral.wrap(MONITOR_SIDE)
|
mon = peripheral.wrap(MONITOR_SIDE)
|
||||||
@@ -67,52 +63,91 @@ local function setupMonitor()
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Drawing target (buf or mon, set in drawDashboard)
|
-------------------------------------------------
|
||||||
local draw = nil
|
-- UI State
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
local selectedAmount = 1
|
||||||
|
local amountOptions = {1, 4, 8, 16, 32, 64}
|
||||||
|
local statusMessage = ""
|
||||||
|
local statusColor = colors.white
|
||||||
|
local statusTimer = 0
|
||||||
|
|
||||||
|
-- Touch zones: list of {x1, y1, x2, y2, action, data}
|
||||||
|
local touchZones = {}
|
||||||
|
|
||||||
|
local function addZone(x1, y1, x2, y2, action, data)
|
||||||
|
table.insert(touchZones, {
|
||||||
|
x1 = x1, y1 = y1, x2 = x2, y2 = y2,
|
||||||
|
action = action, data = data
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function hitTest(x, y)
|
||||||
|
for _, zone in ipairs(touchZones) do
|
||||||
|
if x >= zone.x1 and x <= zone.x2 and y >= zone.y1 and y <= zone.y2 then
|
||||||
|
return zone.action, zone.data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
-- Drawing helpers
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
-- Helpers (draw to buffer)
|
|
||||||
local function monWrite(x, y, text, fg, bg)
|
local function monWrite(x, y, text, fg, bg)
|
||||||
draw.setCursorPos(x, y)
|
mon.setCursorPos(x, y)
|
||||||
if fg then draw.setTextColor(fg) end
|
if fg then mon.setTextColor(fg) end
|
||||||
if bg then draw.setBackgroundColor(bg) end
|
if bg then mon.setBackgroundColor(bg) end
|
||||||
draw.write(text)
|
mon.write(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function monFill(y, color)
|
local function monFill(y, color)
|
||||||
local w, _ = draw.getSize()
|
local w, _ = mon.getSize()
|
||||||
draw.setCursorPos(1, y)
|
mon.setCursorPos(1, y)
|
||||||
draw.setBackgroundColor(color)
|
mon.setBackgroundColor(color)
|
||||||
draw.write(string.rep(" ", w))
|
mon.write(string.rep(" ", w))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function monCenter(y, text, fg, bg)
|
local function monCenter(y, text, fg, bg)
|
||||||
local w, _ = draw.getSize()
|
local w, _ = mon.getSize()
|
||||||
local x = math.floor((w - #text) / 2) + 1
|
local x = math.floor((w - #text) / 2) + 1
|
||||||
monWrite(x, y, text, fg, bg)
|
monWrite(x, y, text, fg, bg)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function monBar(x, y, width, ratio, barColor, bgColor)
|
local function monBar(x, y, width, ratio, barColor, bgColor)
|
||||||
local filled = math.floor(ratio * width)
|
local filled = math.floor(ratio * width)
|
||||||
draw.setCursorPos(x, y)
|
mon.setCursorPos(x, y)
|
||||||
draw.setBackgroundColor(barColor)
|
mon.setBackgroundColor(barColor)
|
||||||
draw.write(string.rep(" ", filled))
|
mon.write(string.rep(" ", filled))
|
||||||
draw.setBackgroundColor(bgColor)
|
mon.setBackgroundColor(bgColor)
|
||||||
draw.write(string.rep(" ", width - filled))
|
mon.write(string.rep(" ", width - filled))
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Draw the full dashboard (double-buffered)
|
local function drawButton(x, y, text, fg, bg, padLeft, padRight)
|
||||||
|
padLeft = padLeft or 1
|
||||||
|
padRight = padRight or 1
|
||||||
|
local full = string.rep(" ", padLeft) .. text .. string.rep(" ", padRight)
|
||||||
|
monWrite(x, y, full, fg, bg)
|
||||||
|
return x, y, x + #full - 1, y
|
||||||
|
end
|
||||||
|
|
||||||
|
-------------------------------------------------
|
||||||
|
-- Dashboard
|
||||||
|
-------------------------------------------------
|
||||||
|
|
||||||
|
-- Cached item list for touch mapping
|
||||||
|
local cachedItems = {}
|
||||||
|
|
||||||
local function drawDashboard()
|
local function drawDashboard()
|
||||||
if not mon then return end
|
if not mon then return end
|
||||||
|
|
||||||
local w, h = mon.getSize()
|
local w, h = mon.getSize()
|
||||||
|
touchZones = {}
|
||||||
|
|
||||||
-- Create offscreen buffer
|
mon.setBackgroundColor(colors.black)
|
||||||
buf = window.create(mon, 1, 1, w, h, false)
|
mon.clear()
|
||||||
draw = buf
|
|
||||||
|
|
||||||
-- Clear buffer
|
|
||||||
draw.setBackgroundColor(colors.black)
|
|
||||||
draw.clear()
|
|
||||||
|
|
||||||
-- ===== Title bar =====
|
-- ===== Title bar =====
|
||||||
monFill(1, colors.blue)
|
monFill(1, colors.blue)
|
||||||
@@ -128,23 +163,13 @@ local function drawDashboard()
|
|||||||
table.insert(statusParts, string.format(" Chests: %d", #chests))
|
table.insert(statusParts, string.format(" Chests: %d", #chests))
|
||||||
table.insert(statusParts, dropperOk and "Dropper: OK" or "Dropper: --")
|
table.insert(statusParts, dropperOk and "Dropper: OK" or "Dropper: --")
|
||||||
table.insert(statusParts, barrelOk and "Barrel: OK" or "Barrel: --")
|
table.insert(statusParts, barrelOk and "Barrel: OK" or "Barrel: --")
|
||||||
local statusLine = table.concat(statusParts, " | ")
|
monWrite(2, 2, table.concat(statusParts, " | "), colors.white, colors.gray)
|
||||||
|
|
||||||
monWrite(2, 2, statusLine, colors.white, colors.gray)
|
|
||||||
|
|
||||||
-- Dropper/Barrel status indicators
|
|
||||||
local indicatorX = w - 10
|
|
||||||
if indicatorX > #statusLine + 3 then
|
|
||||||
monWrite(indicatorX, 2, dropperOk and " \7 " or " x ", dropperOk and colors.lime or colors.red, colors.gray)
|
|
||||||
monWrite(indicatorX + 4, 2, barrelOk and " \7 " or " x ", barrelOk and colors.lime or colors.red, colors.gray)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ===== Divider =====
|
-- ===== Divider =====
|
||||||
monFill(3, colors.lightBlue)
|
monFill(3, colors.lightBlue)
|
||||||
monCenter(3, string.rep("\140", math.min(w - 4, 60)), colors.cyan, colors.lightBlue)
|
monCenter(3, string.rep("-", math.min(w - 4, 60)), colors.cyan, colors.lightBlue)
|
||||||
|
|
||||||
-- ===== Storage capacity bar =====
|
-- ===== Storage capacity =====
|
||||||
-- Count total slots and used slots across all chests
|
|
||||||
local totalSlots = 0
|
local totalSlots = 0
|
||||||
local usedSlots = 0
|
local usedSlots = 0
|
||||||
for _, chestName in ipairs(chests) do
|
for _, chestName in ipairs(chests) do
|
||||||
@@ -159,12 +184,10 @@ local function drawDashboard()
|
|||||||
local freeSlots = totalSlots - usedSlots
|
local freeSlots = totalSlots - usedSlots
|
||||||
local usedRatio = totalSlots > 0 and (usedSlots / totalSlots) or 0
|
local usedRatio = totalSlots > 0 and (usedSlots / totalSlots) or 0
|
||||||
|
|
||||||
local capY = 4
|
monFill(4, colors.black)
|
||||||
monFill(capY, colors.black)
|
|
||||||
local capLabel = string.format(" Storage: %d/%d slots (%d free)", usedSlots, totalSlots, freeSlots)
|
local capLabel = string.format(" Storage: %d/%d slots (%d free)", usedSlots, totalSlots, freeSlots)
|
||||||
monWrite(2, capY, capLabel, colors.lightGray, colors.black)
|
monWrite(2, 4, capLabel, colors.lightGray, colors.black)
|
||||||
|
|
||||||
-- Draw capacity bar
|
|
||||||
local barStart = #capLabel + 4
|
local barStart = #capLabel + 4
|
||||||
local barWidth = w - barStart - 2
|
local barWidth = w - barStart - 2
|
||||||
if barWidth > 4 then
|
if barWidth > 4 then
|
||||||
@@ -173,73 +196,82 @@ local function drawDashboard()
|
|||||||
elseif usedRatio > 0.7 then barColor = colors.orange
|
elseif usedRatio > 0.7 then barColor = colors.orange
|
||||||
elseif usedRatio > 0.5 then barColor = colors.yellow
|
elseif usedRatio > 0.5 then barColor = colors.yellow
|
||||||
end
|
end
|
||||||
monBar(barStart, capY, barWidth, usedRatio, barColor, colors.gray)
|
monBar(barStart, 4, barWidth, usedRatio, barColor, colors.gray)
|
||||||
-- Show percentage on the bar
|
|
||||||
local pctStr = string.format(" %d%% ", math.floor(usedRatio * 100))
|
local pctStr = string.format(" %d%% ", math.floor(usedRatio * 100))
|
||||||
local pctX = barStart + math.floor(barWidth / 2) - math.floor(#pctStr / 2)
|
local pctX = barStart + math.floor(barWidth / 2) - math.floor(#pctStr / 2)
|
||||||
monWrite(pctX, capY, pctStr, colors.white, barColor)
|
monWrite(pctX, 4, pctStr, colors.white, barColor)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ===== Amount selector (row 5) =====
|
||||||
|
monFill(5, colors.black)
|
||||||
|
monWrite(2, 5, "Qty:", colors.lightGray, colors.black)
|
||||||
|
local btnX = 7
|
||||||
|
for _, amt in ipairs(amountOptions) do
|
||||||
|
local label = tostring(amt)
|
||||||
|
local bg = (amt == selectedAmount) and colors.cyan or colors.gray
|
||||||
|
local fg = (amt == selectedAmount) and colors.white or colors.lightGray
|
||||||
|
local x1, y1, x2, y2 = drawButton(btnX, 5, label, fg, bg)
|
||||||
|
addZone(x1, y1, x2, y2, "amount", amt)
|
||||||
|
btnX = x2 + 2
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Scan button on the right
|
||||||
|
local scanLabel = " Refresh "
|
||||||
|
local scanX = w - #scanLabel - 1
|
||||||
|
local sx1, sy1, sx2, sy2 = drawButton(scanX, 5, "Refresh", colors.white, colors.green, 1, 1)
|
||||||
|
addZone(sx1, sy1, sx2, sy2, "scan", nil)
|
||||||
|
|
||||||
|
-- ===== Column headers (row 6) =====
|
||||||
|
local row = 7
|
||||||
|
monFill(row, colors.gray)
|
||||||
|
monWrite(2, row, "#", colors.lightGray, colors.gray)
|
||||||
|
monWrite(5, row, "Item", colors.lightGray, colors.gray)
|
||||||
|
monWrite(w - 22, row, "Qty", colors.lightGray, colors.gray)
|
||||||
|
monWrite(w - 14, row, "Stock", colors.lightGray, colors.gray)
|
||||||
|
monWrite(w - 1, row, ">", colors.lightGray, colors.gray)
|
||||||
|
row = row + 1
|
||||||
|
|
||||||
-- ===== Item catalogue =====
|
-- ===== Item catalogue =====
|
||||||
local catalogue = buildCatalogue()
|
local catalogue = buildCatalogue()
|
||||||
|
|
||||||
-- Collect and sort items
|
|
||||||
local itemList = {}
|
local itemList = {}
|
||||||
local grandTotal = 0
|
local grandTotal = 0
|
||||||
for itemName, sources in pairs(catalogue) do
|
for itemName, sources in pairs(catalogue) do
|
||||||
local total = 0
|
local total = 0
|
||||||
for _, s in ipairs(sources) do total = total + s.total end
|
for _, s in ipairs(sources) do total = total + s.total end
|
||||||
grandTotal = grandTotal + total
|
grandTotal = grandTotal + total
|
||||||
table.insert(itemList, { name = itemName, total = total, sources = #sources })
|
table.insert(itemList, { name = itemName, total = total })
|
||||||
end
|
end
|
||||||
table.sort(itemList, function(a, b) return a.total > b.total end)
|
table.sort(itemList, function(a, b) return a.total > b.total end)
|
||||||
|
cachedItems = itemList
|
||||||
|
|
||||||
-- Header row
|
|
||||||
local row = 6
|
|
||||||
monFill(row, colors.gray)
|
|
||||||
monWrite(2, row, "#", colors.lightGray, colors.gray)
|
|
||||||
monWrite(5, row, "Item", colors.lightGray, colors.gray)
|
|
||||||
monWrite(w - 22, row, "Qty", colors.lightGray, colors.gray)
|
|
||||||
monWrite(w - 14, row, "Stock", colors.lightGray, colors.gray)
|
|
||||||
row = row + 1
|
|
||||||
|
|
||||||
-- Find max for bar scaling
|
|
||||||
local maxCount = 0
|
local maxCount = 0
|
||||||
for _, item in ipairs(itemList) do
|
for _, item in ipairs(itemList) do
|
||||||
if item.total > maxCount then maxCount = item.total end
|
if item.total > maxCount then maxCount = item.total end
|
||||||
end
|
end
|
||||||
if maxCount == 0 then maxCount = 1 end
|
if maxCount == 0 then maxCount = 1 end
|
||||||
|
|
||||||
-- Item rows
|
local maxRows = h - row - 3
|
||||||
local maxRows = h - row - 3 -- leave space for footer
|
|
||||||
for i, item in ipairs(itemList) do
|
for i, item in ipairs(itemList) do
|
||||||
if i > maxRows then break end
|
if i > maxRows then break end
|
||||||
|
|
||||||
local y = row
|
local y = row
|
||||||
local short = item.name:gsub("^minecraft:", ""):gsub("_", " ")
|
local short = item.name:gsub("^minecraft:", ""):gsub("_", " ")
|
||||||
-- Capitalize first letter
|
|
||||||
short = short:sub(1,1):upper() .. short:sub(2)
|
short = short:sub(1,1):upper() .. short:sub(2)
|
||||||
|
|
||||||
-- Truncate if too long
|
|
||||||
local maxNameLen = w - 30
|
local maxNameLen = w - 30
|
||||||
if #short > maxNameLen then
|
if #short > maxNameLen then
|
||||||
short = short:sub(1, maxNameLen - 2) .. ".."
|
short = short:sub(1, maxNameLen - 2) .. ".."
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Alternating row background
|
|
||||||
local rowBg = (i % 2 == 0) and colors.gray or colors.black
|
local rowBg = (i % 2 == 0) and colors.gray or colors.black
|
||||||
|
|
||||||
monFill(y, rowBg)
|
monFill(y, rowBg)
|
||||||
|
|
||||||
-- Index number
|
-- Index
|
||||||
monWrite(2, y, string.format("%2d", i), colors.lightBlue, rowBg)
|
monWrite(2, y, string.format("%2d", i), colors.lightBlue, rowBg)
|
||||||
|
-- Name
|
||||||
-- Item name
|
|
||||||
monWrite(5, y, short, colors.white, rowBg)
|
monWrite(5, y, short, colors.white, rowBg)
|
||||||
|
|
||||||
-- Quantity
|
-- Quantity
|
||||||
local qtyStr = tostring(item.total)
|
monWrite(w - 22, y, tostring(item.total), colors.yellow, rowBg)
|
||||||
monWrite(w - 22, y, qtyStr, colors.yellow, rowBg)
|
|
||||||
|
|
||||||
-- Stock bar
|
-- Stock bar
|
||||||
local ratio = item.total / maxCount
|
local ratio = item.total / maxCount
|
||||||
@@ -249,34 +281,41 @@ local function drawDashboard()
|
|||||||
end
|
end
|
||||||
monBar(w - 14, y, 12, ratio, barColor, rowBg == colors.gray and colors.lightGray or colors.gray)
|
monBar(w - 14, y, 12, ratio, barColor, rowBg == colors.gray and colors.lightGray or colors.gray)
|
||||||
|
|
||||||
|
-- Order button
|
||||||
|
monWrite(w - 1, y, ">", colors.orange, rowBg)
|
||||||
|
|
||||||
|
-- Entire row is a touch zone
|
||||||
|
addZone(1, y, w, y, "order", i)
|
||||||
|
|
||||||
row = row + 1
|
row = row + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ===== Status message (above footer) =====
|
||||||
|
local msgY = h - 2
|
||||||
|
monFill(msgY, colors.black)
|
||||||
|
if statusTimer > 0 and #statusMessage > 0 then
|
||||||
|
monCenter(msgY, statusMessage, statusColor, colors.black)
|
||||||
|
end
|
||||||
|
|
||||||
-- ===== Footer =====
|
-- ===== Footer =====
|
||||||
local footerY = h - 1
|
local footerY = h - 1
|
||||||
monFill(footerY, colors.gray)
|
monFill(footerY, colors.gray)
|
||||||
monWrite(2, footerY,
|
monWrite(2, footerY,
|
||||||
string.format(" Total: %d items across %d types ", grandTotal, #itemList),
|
string.format(" Total: %d items | %d types ", grandTotal, #itemList),
|
||||||
colors.white, colors.gray)
|
colors.white, colors.gray)
|
||||||
|
|
||||||
-- Timestamp
|
|
||||||
local timeStr = textutils.formatTime(os.time(), true)
|
local timeStr = textutils.formatTime(os.time(), true)
|
||||||
monWrite(w - #timeStr - 1, footerY, timeStr, colors.lightGray, colors.gray)
|
monWrite(w - #timeStr - 1, footerY, timeStr, colors.lightGray, colors.gray)
|
||||||
|
|
||||||
-- Bottom accent
|
-- Bottom accent
|
||||||
monFill(h, colors.blue)
|
monFill(h, colors.blue)
|
||||||
monCenter(h, " \4 Auto-Sort Active \4 ", colors.lightBlue, colors.blue)
|
monCenter(h, " Tap item to order ", colors.lightBlue, colors.blue)
|
||||||
|
|
||||||
-- Flush buffer to monitor in one go (no flicker)
|
|
||||||
buf.setVisible(true)
|
|
||||||
buf.setVisible(false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Barrel auto-sort
|
-- Barrel auto-sort
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
-- Move all items from the barrel into matching chests (or first chest with space)
|
|
||||||
local function sortBarrel()
|
local function sortBarrel()
|
||||||
local barrel = peripheral.wrap(BARREL_NAME)
|
local barrel = peripheral.wrap(BARREL_NAME)
|
||||||
if not barrel then return end
|
if not barrel then return end
|
||||||
@@ -290,7 +329,6 @@ local function sortBarrel()
|
|||||||
for slot, item in pairs(contents) do
|
for slot, item in pairs(contents) do
|
||||||
local moved = 0
|
local moved = 0
|
||||||
|
|
||||||
-- Try chests that already hold this item first
|
|
||||||
if catalogue[item.name] then
|
if catalogue[item.name] then
|
||||||
for _, entry in ipairs(catalogue[item.name]) do
|
for _, entry in ipairs(catalogue[item.name]) do
|
||||||
local n = barrel.pushItems(entry.chest, slot)
|
local n = barrel.pushItems(entry.chest, slot)
|
||||||
@@ -302,7 +340,6 @@ local function sortBarrel()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If some remain, push to any chest with space
|
|
||||||
if moved < item.count then
|
if moved < item.count then
|
||||||
for _, chest in ipairs(chests) do
|
for _, chest in ipairs(chests) do
|
||||||
local n = barrel.pushItems(chest, slot)
|
local n = barrel.pushItems(chest, slot)
|
||||||
@@ -324,19 +361,23 @@ end
|
|||||||
-- Order
|
-- Order
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
-- Order items: move from chest(s) to dropper
|
|
||||||
-- Computer 1 will auto-detect and dispense
|
|
||||||
local function orderItem(itemName, amount)
|
local function orderItem(itemName, amount)
|
||||||
local catalogue = buildCatalogue()
|
local catalogue = buildCatalogue()
|
||||||
|
|
||||||
if not catalogue[itemName] then
|
if not catalogue[itemName] then
|
||||||
|
statusMessage = "Not found: " .. itemName:gsub("^minecraft:", "")
|
||||||
|
statusColor = colors.red
|
||||||
|
statusTimer = 3
|
||||||
print("[ERR] Item not found: " .. itemName)
|
print("[ERR] Item not found: " .. itemName)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local dropper = peripheral.wrap(DROPPER_NAME)
|
local dropper = peripheral.wrap(DROPPER_NAME)
|
||||||
if not dropper then
|
if not dropper then
|
||||||
print("[ERR] Dropper not found: " .. DROPPER_NAME)
|
statusMessage = "Dropper offline!"
|
||||||
|
statusColor = colors.red
|
||||||
|
statusTimer = 3
|
||||||
|
print("[ERR] Dropper not found")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -344,14 +385,13 @@ local function orderItem(itemName, amount)
|
|||||||
for _, entry in ipairs(catalogue[itemName]) do
|
for _, entry in ipairs(catalogue[itemName]) do
|
||||||
local chest = peripheral.wrap(entry.chest)
|
local chest = peripheral.wrap(entry.chest)
|
||||||
if chest then
|
if chest then
|
||||||
-- Find slots in this chest with the target item
|
|
||||||
for slot, slotItem in pairs(chest.list()) do
|
for slot, slotItem in pairs(chest.list()) do
|
||||||
if slotItem.name == itemName then
|
if slotItem.name == itemName then
|
||||||
local toMove = math.min(remaining, slotItem.count)
|
local toMove = math.min(remaining, slotItem.count)
|
||||||
local moved = chest.pushItems(DROPPER_NAME, slot, toMove)
|
local moved = chest.pushItems(DROPPER_NAME, slot, toMove)
|
||||||
if moved and moved > 0 then
|
if moved and moved > 0 then
|
||||||
remaining = remaining - moved
|
remaining = remaining - moved
|
||||||
print(string.format("[ORDER] %s x%d from %s -> dropper", itemName, moved, entry.chest))
|
print(string.format("[ORDER] %s x%d from %s", itemName, moved, entry.chest))
|
||||||
end
|
end
|
||||||
if remaining <= 0 then break end
|
if remaining <= 0 then break end
|
||||||
end
|
end
|
||||||
@@ -360,129 +400,109 @@ local function orderItem(itemName, amount)
|
|||||||
if remaining <= 0 then break end
|
if remaining <= 0 then break end
|
||||||
end
|
end
|
||||||
|
|
||||||
if remaining > 0 then
|
local sent = amount - remaining
|
||||||
print(string.format("[WARN] Only moved %d/%d of %s", amount - remaining, amount, itemName))
|
local short = itemName:gsub("^minecraft:", ""):gsub("_", " ")
|
||||||
|
if sent > 0 then
|
||||||
|
statusMessage = string.format("Ordered %s x%d", short, sent)
|
||||||
|
statusColor = colors.lime
|
||||||
|
print(string.format("[OK] Ordered %s x%d", short, sent))
|
||||||
|
else
|
||||||
|
statusMessage = "Could not order " .. short
|
||||||
|
statusColor = colors.red
|
||||||
end
|
end
|
||||||
|
statusTimer = 3
|
||||||
|
|
||||||
print("[OK] Items in dropper. Computer 1 will dispense.")
|
return sent > 0
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Display
|
-- Touch handler
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
local function showCatalogue()
|
local function handleTouch(x, y)
|
||||||
local catalogue = buildCatalogue()
|
local action, data = hitTest(x, y)
|
||||||
print("")
|
if not action then return end
|
||||||
print("=== Item Catalogue ===")
|
|
||||||
print("")
|
if action == "amount" then
|
||||||
local index = 1
|
selectedAmount = data
|
||||||
local items = {}
|
print("[UI] Amount set to " .. data)
|
||||||
for itemName, sources in pairs(catalogue) do
|
|
||||||
local total = 0
|
elseif action == "order" then
|
||||||
for _, s in ipairs(sources) do total = total + s.total end
|
local idx = data
|
||||||
-- Strip "minecraft:" prefix for cleaner display
|
if cachedItems[idx] then
|
||||||
local short = itemName:gsub("^minecraft:", "")
|
local item = cachedItems[idx]
|
||||||
print(string.format(" %2d. %-30s x%d", index, short, total))
|
orderItem(item.name, selectedAmount)
|
||||||
items[index] = itemName
|
end
|
||||||
index = index + 1
|
|
||||||
|
elseif action == "scan" then
|
||||||
|
statusMessage = "Refreshing..."
|
||||||
|
statusColor = colors.cyan
|
||||||
|
statusTimer = 2
|
||||||
|
print("[UI] Manual refresh")
|
||||||
end
|
end
|
||||||
print("")
|
|
||||||
return items
|
-- Redraw immediately after touch
|
||||||
|
pcall(drawDashboard)
|
||||||
end
|
end
|
||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Main loop
|
-- Main
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
local function main()
|
local function main()
|
||||||
print("=================================")
|
print("=================================")
|
||||||
print(" Inventory Manager v2")
|
print(" Inventory Manager v2 (Touch)")
|
||||||
print("=================================")
|
print("=================================")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
-- Check dropper
|
|
||||||
if peripheral.wrap(DROPPER_NAME) then
|
if peripheral.wrap(DROPPER_NAME) then
|
||||||
print("[OK] Dropper found: " .. DROPPER_NAME)
|
print("[OK] Dropper: " .. DROPPER_NAME)
|
||||||
else
|
else
|
||||||
print("[WARN] Dropper not found: " .. DROPPER_NAME)
|
print("[WARN] Dropper not found: " .. DROPPER_NAME)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check barrel
|
|
||||||
if peripheral.wrap(BARREL_NAME) then
|
if peripheral.wrap(BARREL_NAME) then
|
||||||
print("[OK] Barrel found: " .. BARREL_NAME)
|
print("[OK] Barrel: " .. BARREL_NAME)
|
||||||
else
|
else
|
||||||
print("[WARN] Barrel not found: " .. BARREL_NAME)
|
print("[WARN] Barrel not found: " .. BARREL_NAME)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Setup monitor
|
|
||||||
if setupMonitor() then
|
if setupMonitor() then
|
||||||
print("[OK] Monitor found on: " .. MONITOR_SIDE)
|
print("[OK] Monitor: " .. MONITOR_SIDE)
|
||||||
else
|
else
|
||||||
print("[WARN] No monitor on " .. MONITOR_SIDE .. ". Dashboard disabled.")
|
print("[WARN] No monitor on " .. MONITOR_SIDE)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
print("")
|
||||||
|
print("Console shows log output. Use the monitor to order items.")
|
||||||
print("")
|
print("")
|
||||||
|
|
||||||
-- Run barrel watcher, dashboard, and command prompt in parallel
|
|
||||||
parallel.waitForAny(
|
parallel.waitForAny(
|
||||||
-- Task 1: Watch barrel for new items
|
-- Task 1: Barrel auto-sort
|
||||||
function()
|
function()
|
||||||
while true do
|
while true do
|
||||||
sortBarrel()
|
pcall(sortBarrel)
|
||||||
sleep(POLL_INTERVAL)
|
sleep(POLL_INTERVAL)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Task 2: Dashboard refresh
|
-- Task 2: Dashboard refresh + status timer
|
||||||
function()
|
function()
|
||||||
while true do
|
while true do
|
||||||
pcall(drawDashboard)
|
pcall(drawDashboard)
|
||||||
|
if statusTimer > 0 then
|
||||||
|
statusTimer = statusTimer - DASH_REFRESH
|
||||||
|
end
|
||||||
sleep(DASH_REFRESH)
|
sleep(DASH_REFRESH)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
|
|
||||||
-- Task 3: Interactive command prompt
|
-- Task 3: Touch event listener
|
||||||
function()
|
function()
|
||||||
while true do
|
while true do
|
||||||
local items = showCatalogue()
|
local event, side, x, y = os.pullEvent("monitor_touch")
|
||||||
|
print(string.format("[TOUCH] x=%d y=%d", x, y))
|
||||||
print("Commands:")
|
handleTouch(x, y)
|
||||||
print(" order <number> [amount] - Dispense an item")
|
|
||||||
print(" scan - Refresh catalogue")
|
|
||||||
print(" quit - Exit")
|
|
||||||
print("")
|
|
||||||
write("> ")
|
|
||||||
local input = read()
|
|
||||||
local parts = {}
|
|
||||||
for word in input:gmatch("%S+") do
|
|
||||||
table.insert(parts, word)
|
|
||||||
end
|
|
||||||
|
|
||||||
local cmd = parts[1]
|
|
||||||
|
|
||||||
if cmd == "order" or cmd == "o" then
|
|
||||||
local idx = tonumber(parts[2])
|
|
||||||
local amt = tonumber(parts[3]) or 1
|
|
||||||
if idx and items[idx] then
|
|
||||||
orderItem(items[idx], amt)
|
|
||||||
else
|
|
||||||
print("[ERR] Invalid item number.")
|
|
||||||
end
|
|
||||||
|
|
||||||
elseif cmd == "scan" or cmd == "s" then
|
|
||||||
print("Rescanning...")
|
|
||||||
|
|
||||||
elseif cmd == "quit" or cmd == "q" then
|
|
||||||
print("Shutting down.")
|
|
||||||
return
|
|
||||||
|
|
||||||
else
|
|
||||||
print("[ERR] Unknown command: " .. tostring(cmd))
|
|
||||||
end
|
|
||||||
|
|
||||||
print("")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user