SNiPeRzCiinema

Untitled

Oct 14th, 2026 (edited)
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 10.86 KB | None | 0 0
  1. -- Builder resource dashboard: shows what MineColonies builders still need
  2. -- and lets you pull items from AE2 into a stash with a [PULL] button.
  3. -- Written for Advanced Peripherals 1.21.1-0.8.x
  4.  
  5. ---------------------------------------------------------------------------
  6. -- CONFIG
  7. ---------------------------------------------------------------------------
  8.  
  9. -- Where to export to. Either a side relative to the ME Bridge ("up", "north", ...)
  10. -- or a peripheral name on the wired network ("minecraft:barrel_0").
  11. -- Leave as nil to auto-detect on startup.
  12. local EXPORT_TARGET = nil
  13.  
  14. -- Builder huts to watch
  15. local targetBuilders = {
  16.     { name = "Builder 1", pos = { x = -700, y = 72, z = -1393 } },
  17.     { name = "Builder 2", pos = { x = -715, y = 72, z = -1393 } },
  18. }
  19.  
  20. local REFRESH_SECONDS = 5
  21. local STATUS_SECONDS  = 8   -- how long a status message stays up
  22.  
  23. ---------------------------------------------------------------------------
  24. -- PERIPHERALS
  25. ---------------------------------------------------------------------------
  26.  
  27. local colony  = peripheral.find("colony_integrator") or peripheral.find("colonyIntegrator")
  28. local me      = peripheral.find("me_bridge") or peripheral.find("meBridge")
  29. local monitor = peripheral.find("monitor")
  30.  
  31. if not colony  then error("Colony Integrator not found!") end
  32. if not me      then error("ME Bridge not found!") end
  33. if not monitor then error("Monitor not found!") end
  34.  
  35. -- Warn if more than one bridge is reachable: directions are relative to
  36. -- whichever one we picked, which may not be the one next to the stash.
  37. do
  38.     local bridges = { peripheral.find("me_bridge") }
  39.     print("Using ME Bridge: " .. peripheral.getName(me))
  40.     if #bridges > 1 then
  41.         print("WARNING: " .. #bridges .. " ME Bridges found. Directions are relative to the one above.")
  42.     end
  43. end
  44.  
  45. local monitorName = peripheral.getName(monitor)
  46. monitor.setTextScale(0.5)
  47.  
  48. ---------------------------------------------------------------------------
  49. -- EXPORT TARGET DETECTION
  50. ---------------------------------------------------------------------------
  51.  
  52. -- Probe a target with an item that can't exist. If the bridge complains
  53. -- that the inventory is missing, the target is bad; any other answer means
  54. -- it found a container there. Nothing is actually moved.
  55. local PROBE_ITEM = "minecraft:structure_void"
  56.  
  57. local function targetIsValid(target)
  58.    -- Never export into the bridge itself (that just loops back into AE2)
  59.    if target == peripheral.getName(me) then return false end
  60.    if peripheral.hasType(target, "me_bridge") or peripheral.hasType(target, "meBridge") then
  61.        return false
  62.    end
  63.    local ok, res, err = pcall(me.exportItem, target, { name = PROBE_ITEM, count = 1 })
  64.    if not ok then return false end
  65.    return not (res == nil and err == "INVENTORY_NOT_FOUND")
  66. end
  67.  
  68. local function detectTarget()
  69.    local candidates = { "up", "down", "north", "south", "east", "west",
  70.                         "top", "bottom", "left", "right", "front", "back" }
  71.  
  72.    -- Also try any inventory on the wired network (barrels, chests, etc.)
  73.    for _, name in ipairs(peripheral.getNames()) do
  74.        if peripheral.hasType(name, "inventory") then
  75.            table.insert(candidates, name)
  76.        end
  77.    end
  78.  
  79.    for _, target in ipairs(candidates) do
  80.        if targetIsValid(target) then return target end
  81.    end
  82.    return nil
  83. end
  84.  
  85. if EXPORT_TARGET then
  86.    if not targetIsValid(EXPORT_TARGET) then
  87.        print("WARNING: EXPORT_TARGET '" .. EXPORT_TARGET .. "' reports no inventory.")
  88.    end
  89. else
  90.    EXPORT_TARGET = detectTarget()
  91.    if EXPORT_TARGET then
  92.        print("Auto-detected export target: " .. EXPORT_TARGET)
  93.    else
  94.        print("WARNING: No export target found. Put the stash against the ME Bridge,")
  95.        print("or connect it with a wired modem and set EXPORT_TARGET.")
  96.    end
  97. end
  98.  
  99. ---------------------------------------------------------------------------
  100. -- HELPERS
  101. ---------------------------------------------------------------------------
  102.  
  103. local activeButtons = {}
  104. local statusMessage, statusColor, statusExpires = "", colors.cyan, 0
  105.  
  106. local function setStatus(msg, color)
  107.    statusMessage = msg
  108.    statusColor   = color or colors.cyan
  109.    statusExpires = os.clock() + STATUS_SECONDS
  110. end
  111.  
  112. local function getItemId(res)
  113.    if type(res) ~= "table" then return nil end
  114.    if type(res.item) == "table" and res.item.name then return res.item.name end
  115.    if type(res.item) == "string" then return res.item end
  116.    return res.name
  117. end
  118.  
  119. -- needs - available - already on its way to the builder
  120. local function getStillNeededCount(res)
  121.    if type(res) ~= "table" then return 0 end
  122.    if res.status == "AVAIL" or res.status == "NOT_NEEDED" then return 0 end
  123.    local missing = (res.needs or res.needed or 0)
  124.                  - (res.available or 0)
  125.                  - (res.delivering or 0)
  126.    return math.max(missing, 0)
  127. end
  128.  
  129. local function getCleanDisplayName(res, itemId)
  130.    if type(res) == "table" then
  131.        if type(res.item) == "table" and res.item.displayName then
  132.            return (res.item.displayName:gsub("^%[(.*)%]$", "%1"))
  133.        end
  134.        if res.displayName and res.displayName ~= "" then
  135.            return (res.displayName:gsub("^%[(.*)%]$", "%1"))
  136.        end
  137.    end
  138.    local name = tostring(itemId or "Unknown"):match(":(.+)") or tostring(itemId)
  139.    name = name:gsub("_", " ")
  140.    return (name:gsub("(%a)([%w']*)", function(a, b) return a:upper() .. b:lower() end))
  141. end
  142.  
  143. -- AP 0.8 returns a table (or a number on some builds) on success
  144. local function exportedAmount(res)
  145.    if type(res) == "number" then return res end
  146.    if type(res) == "table" then return res.count or res.amount or 0 end
  147.    return 0
  148. end
  149.  
  150. local function fit(text, width)
  151.    if #text <= width then return text end
  152.    return text:sub(1, math.max(width - 3, 0)) .. "..."
  153. end
  154.  
  155. ---------------------------------------------------------------------------
  156. -- DRAWING
  157. ---------------------------------------------------------------------------
  158.  
  159. local function drawDashboard()
  160.    local w, h = monitor.getSize()
  161.    monitor.setBackgroundColor(colors.black)
  162.    monitor.clear()
  163.  
  164.    local title = " STILL NEEDED BUILDER RESOURCES "
  165.    local pad = math.max(math.floor((w - #title) / 2), 0)
  166.    monitor.setCursorPos(1, 1)
  167.    monitor.setTextColor(colors.yellow)
  168.    monitor.write(fit(string.rep("=", pad) .. title .. string.rep("=", w - pad - #title), w))
  169.  
  170.    if statusMessage ~= "" and os.clock() > statusExpires then statusMessage = "" end
  171.    if statusMessage ~= "" then
  172.        monitor.setCursorPos(1, 2)
  173.        monitor.setTextColor(statusColor)
  174.        monitor.write(fit(statusMessage, w))
  175.    end
  176.  
  177.    activeButtons = {}
  178.    local btnLabel = " [PULL] "
  179.    local btnX = w - #btnLabel + 1
  180.    local y = 4
  181.    local shown, hidden = 0, 0
  182.  
  183.    for _, builder in ipairs(targetBuilders) do
  184.        local ok, resources = pcall(colony.getBuilderResources, builder.pos)
  185.        if not ok then
  186.            if y <= h then
  187.                monitor.setCursorPos(2, y)
  188.                monitor.setTextColor(colors.red)
  189.                monitor.write(fit(builder.name .. ": " .. tostring(resources), w - 2))
  190.                y = y + 2
  191.            end
  192.        elseif type(resources) == "table" then
  193.            for _, res in ipairs(resources) do
  194.                local missing = getStillNeededCount(res)
  195.                local itemId = getItemId(res)
  196.                if missing > 0 and itemId then
  197.                    if y > h then
  198.                        hidden = hidden + 1
  199.                    else
  200.                        local displayName = getCleanDisplayName(res, itemId)
  201.                        local line = string.format("%dx %s -> %s", missing, displayName, builder.name)
  202.  
  203.                        monitor.setCursorPos(2, y)
  204.                        monitor.setTextColor(colors.white)
  205.                        monitor.write(fit(line, btnX - 3))
  206.  
  207.                        monitor.setCursorPos(btnX, y)
  208.                        monitor.setBackgroundColor(colors.lime)
  209.                        monitor.setTextColor(colors.black)
  210.                        monitor.write(btnLabel)
  211.                        monitor.setBackgroundColor(colors.black)
  212.  
  213.                        table.insert(activeButtons, {
  214.                            minX = btnX, maxX = btnX + #btnLabel - 1, y = y,
  215.                            itemId = itemId, count = missing, displayName = displayName,
  216.                        })
  217.                        y = y + 2
  218.                        shown = shown + 1
  219.                    end
  220.                end
  221.            end
  222.        end
  223.    end
  224.  
  225.    if shown == 0 and hidden == 0 then
  226.        monitor.setCursorPos(2, 4)
  227.        monitor.setTextColor(colors.gray)
  228.        monitor.write("All required materials are already provided!")
  229.    elseif hidden > 0 then
  230.        monitor.setCursorPos(2, h)
  231.        monitor.setTextColor(colors.gray)
  232.        monitor.write(string.format("+%d more not shown", hidden))
  233.    end
  234. end
  235.  
  236. ---------------------------------------------------------------------------
  237. -- EXPORTING
  238. ---------------------------------------------------------------------------
  239.  
  240. local function exportToStash(btn)
  241.    if not EXPORT_TARGET then
  242.        setStatus("FAILED: No export target configured", colors.red)
  243.        return
  244.    end
  245.  
  246.    setStatus(string.format("Exporting %dx %s...", btn.count, btn.displayName))
  247.    drawDashboard()
  248.  
  249.    -- AP 0.8 signature: exportItem(target, filter)
  250.    local ok, res, err = pcall(me.exportItem, EXPORT_TARGET, { name = btn.itemId, count = btn.count })
  251.  
  252.    if not ok then
  253.        setStatus("ERROR: " .. tostring(res), colors.red)
  254.        return
  255.    end
  256.  
  257.    local sent = exportedAmount(res)
  258.    if sent >= btn.count then
  259.        setStatus(string.format("SUCCESS: Sent %dx %s", sent, btn.displayName), colors.lime)
  260.    elseif sent > 0 then
  261.        setStatus(string.format("PARTIAL: Sent %d/%d %s (low stock or stash full)",
  262.            sent, btn.count, btn.displayName), colors.orange)
  263.    else
  264.        setStatus(string.format("FAILED: %s (%s)", btn.displayName, tostring(err or "none in AE2")),
  265.            colors.red)
  266.    end
  267. end
  268.  
  269. ---------------------------------------------------------------------------
  270. -- MAIN LOOP
  271. ---------------------------------------------------------------------------
  272.  
  273. drawDashboard()
  274. local timerId = os.startTimer(REFRESH_SECONDS)
  275.  
  276. while true do
  277.    local event, p1, p2, p3 = os.pullEvent()
  278.  
  279.    if event == "monitor_touch" and p1 == monitorName then
  280.        for _, btn in ipairs(activeButtons) do
  281.            if p3 == btn.y and p2 >= btn.minX and p2 <= btn.maxX then
  282.                exportToStash(btn)
  283.                drawDashboard()
  284.                break
  285.            end
  286.        end
  287.  
  288.    elseif event == "timer" and p1 == timerId then
  289.        drawDashboard()
  290.        timerId = os.startTimer(REFRESH_SECONDS)
  291.    end
  292. end
Advertisement
Add Comment
Please, Sign In to add comment