local g = require("simple-graphics") local mon = peripheral.wrap("monitor_24") local W, H = mon.getSize() local RUNNING = true -- Flag to indicate global program state. -- Export flags local EXPORTING = false -- Flag to indicate if we're exporting. local EXPORT_SKIP = false -- Flag to indicate we should skip the current item. local EXPORT_REPORT = false -- Flag to indicate we should report the current item as invalid. local function startsWith(str, start) return str:sub(1, #start) == start end local function getTotalItemCount(itemList) local total = 0 for name, value in pairs(itemList) do if not startsWith(name, "__") then total = total + value end end local count = 1 if itemList.__COUNT__ then count = itemList.__COUNT__ end return count * total end local function findItem(items, name) for _, item in pairs(items) do if item.name == name then return item end end return nil end local function attemptItemExport(name, count) local P_NAME = "meBridge_4" local p = peripheral.wrap(P_NAME) if p == nil then return nil, "Missing peripheral "..P_NAME end local func = function() return p.exportItem({name=name, count=count}, "south") end local success, result = pcall(func) if success then return result else return nil, result end end local function playNotes(instrument, pitches, delay) local speaker = peripheral.find("speaker") if speaker == nil then return end delay = delay or 0.25 for i, pitch in pairs(pitches) do speaker.playNote(instrument, 3, pitch) if i < #pitches then os.sleep(delay) end end end local function drawProgress(p) local discreteWidth = W - 2 local filled = p * discreteWidth g.drawXLine(mon, 2, W-1, 5, colors.lightGray) g.drawXLine(mon, 2, 2+filled-1, 5, colors.green) local s = string.format("%.0f%%", p*100) for i = 1, #s do local x = 2+i local c = s:sub(i,i) local bg = colors.lightGray if x < filled then bg = colors.green end g.drawText(mon, x, 5, c, colors.white, bg) end end local function setProgressText(text) g.drawXLine(mon, 2, W-1, 6, colors.gray) if text ~= nil and #text > 0 then g.drawText(mon, 2, 6, text, colors.white, colors.gray) end end local function clearMainPanel() g.fillRect(mon, 1, 7, W, H-6, colors.black) end local function clearExportPanel() g.fillRect(mon, 1, 7, W, H-9, colors.black) end local function promptForUrlInput() clearMainPanel() drawProgress(0) setProgressText(nil) g.drawText(mon, 1, 8, "Please enter a schematic URL that", colors.white, colors.black) g.drawText(mon, 1, 9, "you've obtained from", colors.white, colors.black) g.drawText(mon, 1, 10, "schematics.andrewlalis.com in the", colors.white, colors.black) g.drawText(mon, 1, 11, "computer to your right to continue.", colors.white, colors.black) g.clear(term, colors.black) g.drawText(term, 1, 1, "Paste your schematic URL here (CTRL+V): ", colors.white) g.drawText(term, 2, 2, "*Press enter without pasting to quit*", colors.gray) term.setCursorPos(1, 3) term.setTextColor(colors.lightGray) local link = io.read() if link == nil or #link == 0 then g.drawText(term, 1, 8, "No URL entered. Quitting.", colors.white) return nil else g.drawText(term, 1, 8, "URL was pasted. Please continue on the monitor.", colors.white) return link end end local function fetchItemLists(url) local response = http.get(url) if response == nil then return nil, "HTTP request failed." end if response.getResponseCode() ~= 200 then return nil, "HTTP code " .. response.getResponseCode() end local rawText = response.readAll() response.close() local itemLists, jsonErr = textutils.unserializeJSON(rawText) if not itemLists then return nil, "Failed to parse JSON: "..jsonErr end return itemLists end local function exportItem(name, count) clearExportPanel() g.drawText(mon, 1, 7, "Exporting "..count.." of", colors.white, colors.black) g.drawText(mon, 1, 8, name, colors.lime, colors.black) -- Check for flags and do different stuff if so. if EXPORT_REPORT then -- Report the item issue to the schematic site. g.drawText(mon, 1, 10, "Reporting item. Skipping to next one.", colors.yellow, colors.black) http.post("https://schematics.andrewlalis.com/item-reports", name) g.drawText(mon, 1, 11, "Contact an admin if urgent.", colors.yellow, colors.black) os.sleep(3) return count elseif EXPORT_SKIP then -- Skip this item. g.drawText(mon, 1, 10, "Skipping this item.", colors.yellow, colors.black) os.sleep(2) return count end local me = peripheral.find("meBridge") if me == nil then g.drawText(mon, 1, 10, "Error: No \"meBridge\" peripheral.", colors.red, colors.black) g.drawText(mon, 1, 11, "Attach one please.", colors.red, colors.black) os.sleep(0.5) return 0 end local allItems, err = me.listItems() if allItems == nil or #allItems < 5 then g.drawText(mon, 1, 10, "Error: Couldn't list AE items.", colors.red, colors.black) g.drawText(mon, 1, 11, "Msg: " .. err, colors.red, colors.black) os.sleep(0.5) return 0 end local item = findItem(allItems, name) if item ~= nil and item.amount > 0 then local exported, err = attemptItemExport(name, count) if exported ~= nil then if exported == 0 then g.drawText(mon, 1, 10, "Exported 0 items. Make sure there is", colors.yellow, colors.black) g.drawText(mon, 1, 11, "space in the output container.", colors.yellow, colors.black) else g.drawText(mon, 1, 10, "Exported " .. exported .. " items.", colors.white, colors.black) end os.sleep(0.5) return exported end g.drawText(mon, 1, 10, "Transfer failed: " .. err, colors.red, colors.black) os.sleep(0.5) return 0 else g.drawText(mon, 1, 10, "Item isn't present in the AE system.", colors.yellow, colors.black) g.drawText(mon, 1, 11, "Please add some, craft, or skip.", colors.yellow, colors.black) os.sleep(0.5) return 0 end end local function exportSchematics(itemLists) -- First build a list of instances of schematics, each with a task list. local totalItemCount = 0 local listInstances = {} for _, list in pairs(itemLists) do totalItemCount = totalItemCount + getTotalItemCount(list) for i = 1, list.__COUNT__ do local instance = { name = list.__NAME__, instanceNumber = i, countOfThisType = list.__COUNT__, tasks = {} } for name, amount in pairs(list) do if not startsWith(name, "__") then table.insert(instance.tasks, {name=name, amount=amount}) end end table.insert(listInstances, instance) end end -- Now execute on that task list, quitting if the EXPORTING flag goes false. local totalItemsExported = 0 local instanceIndex = 1 while instanceIndex <= #listInstances and EXPORTING do local listInstance = listInstances[instanceIndex] setProgressText(listInstance.instanceNumber.."/"..listInstance.countOfThisType.." "..listInstance.name) local taskIndex = 1 while taskIndex <= #listInstance.tasks and EXPORTING do local task = listInstance.tasks[taskIndex] local itemsExported = 0 -- Reset item-specific control flags. EXPORT_REPORT = false EXPORT_SKIP = false while itemsExported < task.amount and EXPORTING do local exportedCount = exportItem(task.name, task.amount - itemsExported) itemsExported = itemsExported + exportedCount totalItemsExported = totalItemsExported + exportedCount drawProgress(totalItemsExported / totalItemCount) end taskIndex = taskIndex + 1 end instanceIndex = instanceIndex + 1 end -- Done! Show a small message, then set the EXPORTING flag to false. EXPORTING = false RUNNING = false -- Exit back to the desktop. for i = 1, 3 do -- Queue up some no-op touch events make sure the event handler quits. os.queueEvent("monitor_touch", "monitor_24", 1, 1) end clearMainPanel() g.drawText(mon, 1, 8, "Export complete!", colors.lime, colors.black) g.drawText(mon, 1, 10, totalItemsExported .. " items exported.", colors.white, colors.black) playNotes("bell", {8, 12, 16, 20}, 0.5) os.sleep(3) end local function handleExportEvents() while EXPORTING do local event, monName, x, y = os.pullEvent("monitor_touch") if monName == "monitor_24" and y >= H-2 then if x >= 27 then EXPORTING = false playNotes("harp", {12, 8}) elseif x >= 14 then EXPORT_REPORT = true playNotes("hat", {14, 10}) else EXPORT_SKIP = true playNotes("snare", {12}) end end end end local function handleSchematicUrl(url) clearMainPanel() if url == nil then g.drawText(mon, 1, 8, "No URL was entered. Quitting.", colors.white, colors.black) RUNNING = false os.sleep(2) return end g.drawText(mon, 1, 7, "Fetching item lists from URL...", colors.lightGray, colors.black) local itemLists, err = fetchItemLists(url) if not itemLists then g.drawText(mon, 1, 8, "Failed to fetch item lists.", colors.red, colors.black) g.drawText(mon, 1, 9, err, colors.red, colors.black) os.sleep(3) return end g.drawText(mon, 1, 8, "Got lists for "..(#itemLists).." schematics:", colors.white, colors.black) local y = 9 for i, itemList in pairs(itemLists) do local count = itemList.__COUNT__ local name = itemList.__NAME__ g.drawText(mon, 2, y, count.."x "..name, colors.white, colors.black) y = y+1 if y == H-3 and i < #itemLists then local numRemaining = #itemLists - i g.drawText(mon, 2, y, "... and "..numRemaining.." more.") break end end -- Draw continue and cancel buttons and wait for user input. g.fillRect(mon, 1, H-2, 18, 3, colors.gray) g.drawTextCenter(mon, 9, H-1, "Cancel", colors.white) g.fillRect(mon, 19, H-2, 21, 3, colors.green) g.drawTextCenter(mon, 29, H-1, "Continue", colors.white) local waiting = true while waiting do local event, monName, x, y = os.pullEvent("monitor_touch") if monName == "monitor_24" and y >= H-2 then if x <= 18 then -- Canceled, so quit the whole application. playNotes("harp", {12, 8}) RUNNING = false return else playNotes("bell", {12, 16}) waiting = false end end end -- Write instructions clearMainPanel() g.drawText(mon, 1, 8, "Export beginning shortly. Please read", colors.white, colors.black) g.drawText(mon, 1, 9, "these instructions beforehand:", colors.white, colors.black) g.drawText(mon, 1, 10, "Press", colors.white, colors.black) g.drawText(mon, 7, 10, "Skip", colors.white, colors.blue) g.drawText(mon, 13, 10, "to skip an item.", colors.white, colors.black) g.drawText(mon, 1, 11, "Press", colors.white, colors.black) g.drawText(mon, 7, 11, "Report", colors.white, colors.orange) g.drawText(mon, 14, 11, "to report an invalid item.", colors.white, colors.black) g.drawText(mon, 1, 12, "Press", colors.white, colors.black) g.drawText(mon, 7, 12, "Quit", colors.white, colors.red) g.drawText(mon, 12, 12, "to stop the export.", colors.white, colors.black) -- Draw control buttons. g.fillRect(mon, 1, H-2, 13, 3, colors.blue) g.drawText(mon, 5, H-1, "Skip", colors.white) g.fillRect(mon, 14, H-2, 13, 3, colors.orange) g.drawText(mon, 17, H-1, "Report", colors.white) g.fillRect(mon, 27, H-2, 13, 3, colors.red) g.drawText(mon, 31, H-1, "Quit", colors.white) for i = 1, 5 do g.drawText(mon, 1, H-4, "Starting in "..(5-i+1), colors.white, colors.black) os.sleep(1) end EXPORTING = true local fExport = function() exportSchematics(itemLists) end local fHandleEvents = function() handleExportEvents() end parallel.waitForAll(fExport, fHandleEvents) end -- MAIN SCRIPT g.clear(mon, colors.black) g.fillRect(mon, 1, 1, W, 3, colors.yellow) g.drawTextCenter(mon, W/2, 2, "Schematic Exporter", colors.black) g.fillRect(mon, 1, 4, W, 3, colors.gray) g.drawText(mon, 2, 4, "Progress", colors.white) g.drawXLine(mon, 2, W-1, 5, colors.lightGray) while RUNNING do local url = promptForUrlInput() handleSchematicUrl(url) end