Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Builder resource dashboard: shows what MineColonies builders still need
- -- and lets you pull items from AE2 into a stash with a [PULL] button.
- -- Written for Advanced Peripherals 1.21.1-0.8.x
- ---------------------------------------------------------------------------
- -- CONFIG
- ---------------------------------------------------------------------------
- -- Where to export to. Either a side relative to the ME Bridge ("up", "north", ...)
- -- or a peripheral name on the wired network ("minecraft:barrel_0").
- -- Leave as nil to auto-detect on startup.
- local EXPORT_TARGET = nil
- -- Builder huts to watch
- local targetBuilders = {
- { name = "Builder 1", pos = { x = -700, y = 72, z = -1393 } },
- { name = "Builder 2", pos = { x = -715, y = 72, z = -1393 } },
- }
- local REFRESH_SECONDS = 5
- local STATUS_SECONDS = 8 -- how long a status message stays up
- ---------------------------------------------------------------------------
- -- PERIPHERALS
- ---------------------------------------------------------------------------
- local colony = peripheral.find("colony_integrator") or peripheral.find("colonyIntegrator")
- local me = peripheral.find("me_bridge") or peripheral.find("meBridge")
- local monitor = peripheral.find("monitor")
- if not colony then error("Colony Integrator not found!") end
- if not me then error("ME Bridge not found!") end
- if not monitor then error("Monitor not found!") end
- -- Warn if more than one bridge is reachable: directions are relative to
- -- whichever one we picked, which may not be the one next to the stash.
- do
- local bridges = { peripheral.find("me_bridge") }
- print("Using ME Bridge: " .. peripheral.getName(me))
- if #bridges > 1 then
- print("WARNING: " .. #bridges .. " ME Bridges found. Directions are relative to the one above.")
- end
- end
- local monitorName = peripheral.getName(monitor)
- monitor.setTextScale(0.5)
- ---------------------------------------------------------------------------
- -- EXPORT TARGET DETECTION
- ---------------------------------------------------------------------------
- -- Probe a target with an item that can't exist. If the bridge complains
- -- that the inventory is missing, the target is bad; any other answer means
- -- it found a container there. Nothing is actually moved.
- local PROBE_ITEM = "minecraft:structure_void"
- local function targetIsValid(target)
- -- Never export into the bridge itself (that just loops back into AE2)
- if target == peripheral.getName(me) then return false end
- if peripheral.hasType(target, "me_bridge") or peripheral.hasType(target, "meBridge") then
- return false
- end
- local ok, res, err = pcall(me.exportItem, target, { name = PROBE_ITEM, count = 1 })
- if not ok then return false end
- return not (res == nil and err == "INVENTORY_NOT_FOUND")
- end
- local function detectTarget()
- local candidates = { "up", "down", "north", "south", "east", "west",
- "top", "bottom", "left", "right", "front", "back" }
- -- Also try any inventory on the wired network (barrels, chests, etc.)
- for _, name in ipairs(peripheral.getNames()) do
- if peripheral.hasType(name, "inventory") then
- table.insert(candidates, name)
- end
- end
- for _, target in ipairs(candidates) do
- if targetIsValid(target) then return target end
- end
- return nil
- end
- if EXPORT_TARGET then
- if not targetIsValid(EXPORT_TARGET) then
- print("WARNING: EXPORT_TARGET '" .. EXPORT_TARGET .. "' reports no inventory.")
- end
- else
- EXPORT_TARGET = detectTarget()
- if EXPORT_TARGET then
- print("Auto-detected export target: " .. EXPORT_TARGET)
- else
- print("WARNING: No export target found. Put the stash against the ME Bridge,")
- print("or connect it with a wired modem and set EXPORT_TARGET.")
- end
- end
- ---------------------------------------------------------------------------
- -- HELPERS
- ---------------------------------------------------------------------------
- local activeButtons = {}
- local statusMessage, statusColor, statusExpires = "", colors.cyan, 0
- local function setStatus(msg, color)
- statusMessage = msg
- statusColor = color or colors.cyan
- statusExpires = os.clock() + STATUS_SECONDS
- end
- local function getItemId(res)
- if type(res) ~= "table" then return nil end
- if type(res.item) == "table" and res.item.name then return res.item.name end
- if type(res.item) == "string" then return res.item end
- return res.name
- end
- -- needs - available - already on its way to the builder
- local function getStillNeededCount(res)
- if type(res) ~= "table" then return 0 end
- if res.status == "AVAIL" or res.status == "NOT_NEEDED" then return 0 end
- local missing = (res.needs or res.needed or 0)
- - (res.available or 0)
- - (res.delivering or 0)
- return math.max(missing, 0)
- end
- local function getCleanDisplayName(res, itemId)
- if type(res) == "table" then
- if type(res.item) == "table" and res.item.displayName then
- return (res.item.displayName:gsub("^%[(.*)%]$", "%1"))
- end
- if res.displayName and res.displayName ~= "" then
- return (res.displayName:gsub("^%[(.*)%]$", "%1"))
- end
- end
- local name = tostring(itemId or "Unknown"):match(":(.+)") or tostring(itemId)
- name = name:gsub("_", " ")
- return (name:gsub("(%a)([%w']*)", function(a, b) return a:upper() .. b:lower() end))
- end
- -- AP 0.8 returns a table (or a number on some builds) on success
- local function exportedAmount(res)
- if type(res) == "number" then return res end
- if type(res) == "table" then return res.count or res.amount or 0 end
- return 0
- end
- local function fit(text, width)
- if #text <= width then return text end
- return text:sub(1, math.max(width - 3, 0)) .. "..."
- end
- ---------------------------------------------------------------------------
- -- DRAWING
- ---------------------------------------------------------------------------
- local function drawDashboard()
- local w, h = monitor.getSize()
- monitor.setBackgroundColor(colors.black)
- monitor.clear()
- local title = " STILL NEEDED BUILDER RESOURCES "
- local pad = math.max(math.floor((w - #title) / 2), 0)
- monitor.setCursorPos(1, 1)
- monitor.setTextColor(colors.yellow)
- monitor.write(fit(string.rep("=", pad) .. title .. string.rep("=", w - pad - #title), w))
- if statusMessage ~= "" and os.clock() > statusExpires then statusMessage = "" end
- if statusMessage ~= "" then
- monitor.setCursorPos(1, 2)
- monitor.setTextColor(statusColor)
- monitor.write(fit(statusMessage, w))
- end
- activeButtons = {}
- local btnLabel = " [PULL] "
- local btnX = w - #btnLabel + 1
- local y = 4
- local shown, hidden = 0, 0
- for _, builder in ipairs(targetBuilders) do
- local ok, resources = pcall(colony.getBuilderResources, builder.pos)
- if not ok then
- if y <= h then
- monitor.setCursorPos(2, y)
- monitor.setTextColor(colors.red)
- monitor.write(fit(builder.name .. ": " .. tostring(resources), w - 2))
- y = y + 2
- end
- elseif type(resources) == "table" then
- for _, res in ipairs(resources) do
- local missing = getStillNeededCount(res)
- local itemId = getItemId(res)
- if missing > 0 and itemId then
- if y > h then
- hidden = hidden + 1
- else
- local displayName = getCleanDisplayName(res, itemId)
- local line = string.format("%dx %s -> %s", missing, displayName, builder.name)
- monitor.setCursorPos(2, y)
- monitor.setTextColor(colors.white)
- monitor.write(fit(line, btnX - 3))
- monitor.setCursorPos(btnX, y)
- monitor.setBackgroundColor(colors.lime)
- monitor.setTextColor(colors.black)
- monitor.write(btnLabel)
- monitor.setBackgroundColor(colors.black)
- table.insert(activeButtons, {
- minX = btnX, maxX = btnX + #btnLabel - 1, y = y,
- itemId = itemId, count = missing, displayName = displayName,
- })
- y = y + 2
- shown = shown + 1
- end
- end
- end
- end
- end
- if shown == 0 and hidden == 0 then
- monitor.setCursorPos(2, 4)
- monitor.setTextColor(colors.gray)
- monitor.write("All required materials are already provided!")
- elseif hidden > 0 then
- monitor.setCursorPos(2, h)
- monitor.setTextColor(colors.gray)
- monitor.write(string.format("+%d more not shown", hidden))
- end
- end
- ---------------------------------------------------------------------------
- -- EXPORTING
- ---------------------------------------------------------------------------
- local function exportToStash(btn)
- if not EXPORT_TARGET then
- setStatus("FAILED: No export target configured", colors.red)
- return
- end
- setStatus(string.format("Exporting %dx %s...", btn.count, btn.displayName))
- drawDashboard()
- -- AP 0.8 signature: exportItem(target, filter)
- local ok, res, err = pcall(me.exportItem, EXPORT_TARGET, { name = btn.itemId, count = btn.count })
- if not ok then
- setStatus("ERROR: " .. tostring(res), colors.red)
- return
- end
- local sent = exportedAmount(res)
- if sent >= btn.count then
- setStatus(string.format("SUCCESS: Sent %dx %s", sent, btn.displayName), colors.lime)
- elseif sent > 0 then
- setStatus(string.format("PARTIAL: Sent %d/%d %s (low stock or stash full)",
- sent, btn.count, btn.displayName), colors.orange)
- else
- setStatus(string.format("FAILED: %s (%s)", btn.displayName, tostring(err or "none in AE2")),
- colors.red)
- end
- end
- ---------------------------------------------------------------------------
- -- MAIN LOOP
- ---------------------------------------------------------------------------
- drawDashboard()
- local timerId = os.startTimer(REFRESH_SECONDS)
- while true do
- local event, p1, p2, p3 = os.pullEvent()
- if event == "monitor_touch" and p1 == monitorName then
- for _, btn in ipairs(activeButtons) do
- if p3 == btn.y and p2 >= btn.minX and p2 <= btn.maxX then
- exportToStash(btn)
- drawDashboard()
- break
- end
- end
- elseif event == "timer" and p1 == timerId then
- drawDashboard()
- timerId = os.startTimer(REFRESH_SECONDS)
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment