Advertisement
HydrantHunter

ccDHD Lite 2.0

Jul 13th, 2015
737
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 74.30 KB | None | 0 0
  1. --[[  LanteaCraft  ]]--
  2. --[[  and SGCraft  ]]--
  3. --[[  ccDHD  Lite  ]]--
  4. --[[    by Dog     ]]--
  5. --[[     a k a     ]]--
  6. --[[ HydrantHunter ]]--
  7. --[[  pastebin:    ]]--
  8. --[[     ieFgzNMf  ]]--
  9. local ccDHDliteVer = "2.0.00"
  10. --[[
  11. Tested with/requires:
  12.   - Minecraft 1.7.10+ AND ComputerCraft 1.75+ || LanteaCraft LC2-16+ | OR | SGCraft1.11.x-mc1.7.10+
  13.   - An advanced computer connected directly or via modem/network cable to...
  14.   - A Stargate (LanteaCraft) or Stargate/CC adapter (SGCraft)
  15.  
  16. Special thanks to theoriginalbit (custom read function, newButton function)
  17.  
  18. IMPORTANT NOTE: DO NOT EDIT this script - all variables are set automatically
  19. ]]--
  20. local termX, termY = term.getSize()
  21. local gate, screen, currentGate, incomingAddress, updateTimer, drawHelpScreen, userInput
  22. local kernelState, irisState, lcGate, gateChange, addrRedraw, continueDialing, spinTime, autoDisengage = false, false, false, false, false, false, false, false
  23. local gatePage, gatePages, logPage, logPages, abCount, chevronNumber, fuelPercent, MAXFUEL_LC, MAXFUEL_SG = 1, 1, 1, 1, 1, 0, 0, 100, 200000
  24. local callHistory, guiElements = { }, { }
  25. local addressBook = { { name = "NEW GATE", addr = "ADDRESS", rating = "U", iris = "none", callDrop = false, note = "short note", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }, }
  26. local thisGate, gateStatus, gateTarget, cachedInput = "!!ERROR!!", "Idle", "No Target", ""
  27. local gateData, historyData = "/data/DHDgates", "/data/DHDhistory"
  28. local classifications = {
  29.   B = { order = 1, color = colors.blue, label = "Base/Outpost/Hub" };
  30.   H = { order = 2, color = colors.lightBlue, label = "Home/Camp" };
  31.   V = { order = 3, color = colors.brown, label = "Village" };
  32.   M = { order = 4, color = colors.purple, label = "Misc/Special" };
  33.   S = { order = 5, color = colors.green, label = "Safe/Secured" };
  34.   C = { order = 6, color = colors.orange, label = "Caution" };
  35.   D = { order = 7, color = colors.red, label = "Danger" };
  36.   U = { order = 8, color = colors.lightGray, label = "Unk/Unclassified", long = "Unknown/Unclassified" };
  37. }
  38. local sgStates = {
  39.   Idle = "Idle",
  40.   Dialing = "Dialing",
  41.   Dialling = "Dialing",
  42.   Opening = "Dialing",
  43.   Connected = "Connected",
  44.   Closing = "Closing",
  45.   Offline = "Offline",
  46. }
  47.  
  48. --# custom read function courtesy of theoriginalbit (modified by Dog)
  49. local function read(_mask, _history, _limit, _noTerminate, _noMouse, _gField)
  50.   if _mask and type(_mask) ~= "string" then
  51.     error("Invalid parameter #1: Expected string, got " .. type(_mask), 2)
  52.   end
  53.   if _history and type(_history) ~= "table" then
  54.     error("Invalid parameter #2: Expected table, got " .. type(_history), 2)
  55.   end
  56.   if _limit and type(_limit) ~= "number" then
  57.     error("Invalid parameter #3: Expected number, got " .. type(_limit), 2)
  58.   end
  59.   if _noTerminate and type(_noTerminate) ~= "boolean" then
  60.     error("Invalid argument #4: Expected boolean, got " .. nativeType(_noTerminate), 2)
  61.   end
  62.   if _noMouse and type(_noMouse) ~= "boolean" then
  63.     error("Invalid argument #5: Expected boolean, got " .. nativeType(_noMouse), 2)
  64.   end
  65.   if _gField and type(_gField) ~= "boolean" then
  66.     error("Invalid argument #6: Expected boolean, got " .. nativeType(_gField), 2)
  67.   end
  68.   term.setCursorBlink(true)
  69.   local symbols = { ["A"] = true, ["B"] = true, ["C"] = true, ["D"] = true, ["E"] = true , ["F"] = true, ["G"] = true, ["H"] = true, ["I"] = true, ["J"] = true, ["K"] = true, ["L"] = true, ["M"] = true, ["N"] = true, ["O"] = true, ["P"] = true, ["Q"] = true, ["R"] = true, ["S"] = true, ["T"] = true, ["U"] = true, ["V"] = true, ["W"] = true, ["X"] = true, ["Y"] = true, ["Z"] = true, ["0"] = true, ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, ["5"] = true, ["6"] = true, ["7"] = true, ["8"] = true, ["9"] = true, ["-"] = true, ["+"] = true }
  70.   local mouseLimit = _limit or 0
  71.   local input = ""
  72.   cachedInput = ""
  73.   local pos = 0
  74.   local historyPos = nil
  75.   local pullEvent = _noTerminate and os.pullEventRaw or os.pullEvent
  76.   local sw, sh = term.getSize()
  77.   local sx, sy = term.getCursorPos()
  78.   local function redraw(_special)
  79.     local scroll = (sx + pos >= sw and (sx + pos) - sw or 0)
  80.     local replace = _special or _mask
  81.     local output = replace and (string.rep(replace, math.ceil(#input / #replace) - scroll)):sub(1, #input) or input:sub(scroll + 1)
  82.     term.setCursorPos(sx, sy)
  83.     term.write(output)
  84.     term.setCursorPos(sx + pos - scroll, sy)
  85.   end
  86.   local nativeScroll = term.scroll
  87.   term.scroll = function(_n) local ok, err = pcall(function() return nativeScroll(_n) end) if ok then sy = sy - _n return err end error(err, 2) end
  88.   local function historyHandler(value)
  89.     redraw(' ')
  90.     if value == -1 or value == keys.up then
  91.       if not historyPos then
  92.         historyPos = #_history
  93.       elseif historyPos > 1 then
  94.         historyPos = historyPos - 1
  95.       end
  96.     else
  97.       if historyPos ~= nil and historyPos < #_history then
  98.         historyPos = historyPos + 1
  99.       elseif historyPos == #_history then
  100.         historyPos = nil
  101.       end
  102.     end
  103.     if historyPos and #_history > 0 then
  104.       input = string.sub(_history[historyPos], 1, _limit) or ""
  105.       pos = #input
  106.       cachedInput = input
  107.       if not _limit then mouseLimit = pos end
  108.     else
  109.       input = ""
  110.       pos = 0
  111.       cachedInput = input
  112.       if not _limit then mouseLimit = 0 end
  113.     end
  114.   end
  115.   while true do
  116.     local event, code, mX, mY = pullEvent()
  117.     if event == "char" and (not _limit or #input < _limit) then
  118.       local goodData = false
  119.       if _gField then
  120.         if symbols[code:upper()] then
  121.           code = code:upper()
  122.           goodData = true
  123.         end
  124.       else
  125.         goodData = true
  126.       end
  127.       if goodData then
  128.         input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  129.         pos = pos + 1
  130.         cachedInput = input
  131.         if not _limit then mouseLimit = math.min(mouseLimit + 1, sw - (sw - sx)) end
  132.       end
  133.     elseif event == "paste" and (not _limit or #input < _limit) then
  134.       if _limit and #input + #code > _limit then
  135.         code = code:sub(1, _limit - #input)
  136.       end
  137.       if _gField then
  138.         local newWord, glyph = { }, ""
  139.         for i = 1, #code do
  140.           glyph = string.upper(code:sub(i, i))
  141.           newWord[i] = symbols[glyph] and glyph or "?"
  142.         end
  143.         code = table.concat(newWord)
  144.       end
  145.       input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  146.       pos = pos + #code
  147.       cachedInput = input
  148.       if not _limit then mouseLimit = math.min(mouseLimit + #code, sw - (sw - sx)) end
  149.     elseif event == "key" then
  150.       if code == keys.enter or code == keys.numPadEnter then
  151.         cachedInput = ""
  152.         break
  153.       elseif code == keys.backspace and pos > 0 then
  154.         redraw(' ')
  155.         input = input:sub(1, math.max(pos - 1, 0)) .. input:sub(pos + 1)
  156.         pos = math.max(pos - 1, 0)
  157.         cachedInput = input
  158.         if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
  159.       elseif code == keys.delete and pos < #input then
  160.         redraw(' ')
  161.         input = input:sub(1, pos)..input:sub(pos + 2)
  162.         cachedInput = input
  163.         if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
  164.       elseif code == keys.home then
  165.         pos = 0
  166.       elseif code == keys["end"] then
  167.         pos = #input
  168.       elseif code == keys.left and pos > 0 then
  169.         pos = math.max(pos - 1, 0)
  170.       elseif code == keys.right and pos < #input then
  171.         pos = math.min(pos + 1, #input)
  172.       elseif _history and (code == keys.up or code == keys.down) then
  173.         historyHandler(code)
  174.       end
  175.     elseif event == "mouse_click" and not _noMouse and ((mX < sx or mX >= sx + mouseLimit) or (mY ~= sy)) then
  176.       cachedInput = ""
  177.       break
  178.     elseif event == "mouse_scroll" and _history then
  179.       historyHandler(code)
  180.     end
  181.     redraw(_mask)
  182.   end
  183.   term.scroll = nativeScroll
  184.   term.setCursorBlink(false)
  185.   if sy + 1 > sh then
  186.     term.scroll(sy + 1 - sh)
  187.     term.setCursorPos(1, sy)
  188.   else
  189.     term.setCursorPos(1, sy + 1)
  190.   end
  191.   return input
  192. end
  193.  
  194. --# newButton function courtesy of theoriginalbit (modified by Dog)
  195. local function newButton(x, y, w, h, text, bc, tc, action, name, mbtn)
  196.   w = math.max(type(w) == "number" and w or 1, 1)
  197.   h = math.max(type(h) == "number" and h or 1, 1)
  198.   mbtn = (type(mbtn) == "number" and mbtn >= 1 and mbtn <= 3) and mbtn or 1
  199.   action = action or function() end
  200.   name = (type(name) == "string" and #name > 0) and name or "noName"
  201.   local hx = x + math.ceil((w - #text) / 2)
  202.   local hy = y + math.ceil(h / 2) - 1
  203.   local enabled = true
  204.   local dbc, dtc = colors.lightGray, colors.gray
  205.   return {
  206.     getType = function()
  207.       return "button"
  208.     end;
  209.     getName = function()
  210.       return name
  211.     end;
  212.     setDisabledColors = function(bc, tc)
  213.       dbc = bc
  214.       dtc = tc
  215.     end;
  216.     setText = function(t)
  217.       text = t or ""
  218.       hx = x + math.ceil((w - #text) / 2) --# recalculate offset
  219.     end;
  220.     setEnabled = function(e)
  221.       enabled = e == true
  222.     end;
  223.     render = function()
  224.       term.setBackgroundColor(enabled and bc or dbc)
  225.       term.setTextColor(enabled and tc or dtc)
  226.       for i = 0, h - 1 do
  227.         term.setCursorPos(x, y + i)
  228.         write(string.rep(' ', w))
  229.       end
  230.       term.setCursorPos(hx, hy)
  231.       write(text)
  232.     end;
  233.     processEvent = function(event, button, xPos, yPos)
  234.       if enabled and event == "mouse_click" and button == mbtn and
  235.         xPos >= x and xPos <= (x + w - 1) and
  236.         yPos >= y and yPos <= (y + h - 1) then
  237.         return action()
  238.       end
  239.     end;
  240.   }
  241. end
  242.  
  243. local function saveData(fileName)
  244.   local dhdData = fs.open(fileName, "w")
  245.   dhdData.write(textutils.serialize(addressBook))
  246.   dhdData.close()
  247.   if fileName == gateData then gateChange = false end
  248. end
  249.  
  250. local function ingestData(fileName)
  251.   if fileName == "logs" then
  252.     if fs.exists(historyData) then
  253.       for log_entry in io.lines(historyData) do
  254.         table.insert(callHistory, 1, log_entry)
  255.       end
  256.     end
  257.     logPages = math.max(1, math.ceil(#callHistory / 11)) --# paginate call logs
  258.   else
  259.     if fs.exists(fileName) then
  260.       for i = abCount, 1, -1 do
  261.         addressBook[i] = nil
  262.       end
  263.       local dhdData = fs.open(fileName, "r")
  264.       addressBook = textutils.unserialize(dhdData.readAll())
  265.       dhdData.close()
  266.       abCount = #addressBook
  267.       local saveIt = false
  268.       if addressBook[1].callDrop == nil then
  269.         for i = 1, abCount do
  270.           addressBook[i].callDrop = false
  271.         end
  272.         saveIt = true
  273.       end
  274.       for i = 1, abCount do
  275.         if addressBook[i].iris == "open" then
  276.           addressBook[i].iris, saveIt = true, true
  277.         elseif addressBook[i].iris == "close" then
  278.           addressBook[i].iris, saveIt = false, true
  279.         end
  280.       end
  281.       if saveIt and fileName == gateData then saveData(gateData) end
  282.       gatePage, gatePages = 1, math.ceil(abCount / 21) --# paginate address book
  283.       gateChange = fileName ~= gateData
  284.     end
  285.   end
  286. end
  287.  
  288. local function mergeData()                            --# merge two address books into one
  289.   if not fs.exists("/disk/data/DHDgates") then return end
  290.   local dhdData = fs.open("/disk/data/DHDgates", "r")
  291.   local newGates = textutils.unserialize(dhdData.readAll())
  292.   dhdData.close()
  293.   local matchFound, abName, abNote, abDim, ngName, ngNote, ngDim = false --# matchFound indicates if a matching gate was found or not
  294.   for i = 1, #newGates do                             --# start cycling through the 'new' list of gates
  295.     for j = 1, abCount do                             --# search the address book for a matching address
  296.       if newGates[i].addr == addressBook[j].addr then --# if the gate is already in the address book...
  297.         matchFound = true                             --# ...set matchFound to true and...
  298.         abName, ngName = addressBook[j].name, newGates[i].name
  299.         if (abName == "Name" or abName == "NO GATES" or abName == "NEW GATE" or abName == addressBook[j].addr) and ngName ~= newGates[i].addr and ngName ~= "NEW GATE" and ngName ~= "NO GATES" and ngName ~= "Name" then
  300.           addressBook[j].name = ngName
  301.           gateChange = true
  302.         end
  303.         abNote, ngNote = addressBook[j].note, newGates[i].note
  304.         if (abNote == "short note" or abNote == "Discovered gate" or abNote == "Added from logs") and ngNote ~= "short note" and ngNote ~= "Discovered gate" and ngNote ~= "Added from logs" then
  305.           addressBook[j].note = ngNote
  306.           gateChange = true
  307.         end
  308.         if addressBook[j].rating == "U" and newGates[i].rating ~= "U" then
  309.           addressBook[j].rating = newGates[i].rating
  310.           gateChange = true
  311.         end
  312.         abDim, ngDim = addressBook[j].loc.dim, newGates[i].loc.dim
  313.         if (abDim == "Overworld" and ngDim ~= "Overworld" and ngDim ~= "Unspecified") or (abDim == "Unspecified" and ngDim ~= "Unspecified") then
  314.           addressBook[j].loc.dim = ngDim
  315.           gateChange = true
  316.         end
  317.         if addressBook[j].loc.x == 0 and addressBook[j].loc.y == 0 and addressBook[j].loc.z == 0 and (newGates[i].loc.x ~= 0 or newGates[i].loc.y ~= 0 or newGates[i].loc.z ~= 0) then
  318.           addressBook[j].loc.x, addressBook[j].loc.y, addressBook[j].loc.z = newGates[i].loc.x, newGates[i].loc.y, newGates[i].loc.z
  319.           gateChange = true
  320.         end
  321.         break                                         --# stop the address book search loop and move on to the next gate in the 'new' list
  322.       end
  323.     end
  324.     if matchFound then                                --# if a match was found...
  325.       matchFound = false                              --# ...reset the variable for the next iteration of the loop
  326.     else                                              --# otherwise...
  327.       addressBook[abCount + 1], abCount = { }, abCount + 1 --# initialize a new address book entry
  328.       for k, v in pairs(newGates[i]) do               --# loop through the gate entries...
  329.         addressBook[abCount][k] = v                   --# ...and add them to the new address book entry
  330.       end
  331.       addressBook[abCount].iris = "none"
  332.       addressBook[abCount].callDrop = false
  333.       gateChange = true                               --# indicate that address book data has changed
  334.     end
  335.   end
  336.   gatePages = math.ceil(abCount / 24)                 --# repaginate AddressBook
  337. end
  338.  
  339. local function recordSessionData()                        --# Human readable log files
  340.   local logAddress = gateTarget or "ERROR"                --# set logAddress
  341.   logAddress = gateTarget == "Wormhole " and "N/A" or logAddress
  342.   local callDirection = incomingAddress and "Incoming" or "Outgoing" --# set callDirection
  343.   local callTime = textutils.formatTime(os.time(), false) --# format the time
  344.   if #callTime == 7 then callTime = " " .. callTime end   --# move single digit hour times to the right one space
  345.   local dataLine = tostring(os.day()) .. " @ " .. callTime .. " <" .. callDirection .. "> " .. logAddress
  346.   local callArchive = fs.open(historyData, fs.exists(historyData) and "a" or "w")
  347.   callArchive.writeLine(dataLine)
  348.   callArchive.close()
  349.   local previousCall = fs.open("/data/DHDlastCall", "w")  --# record last call
  350.   previousCall.writeLine(dataLine)
  351.   previousCall.close()
  352. end
  353.  
  354. local function getFuelLevel()
  355.   return (lcGate and math.min(100, math.floor((100 / MAXFUEL_LC) * 100)) or math.min(100, math.floor((gate.energyAvailable() / MAXFUEL_SG) * 100)))
  356. end
  357.  
  358. local function assignColor(gateNumber)
  359.   return classifications[addressBook[gateNumber].rating].color or colors.lightGray
  360. end
  361.  
  362. local function assignRating(gateNumber)
  363.   return classifications[addressBook[gateNumber].rating].long or (classifications[addressBook[gateNumber].rating].label or "Unknown/Unclassified")
  364. end
  365.  
  366. local function clearCallHistory()
  367.   for i = #callHistory, 1, -1 do
  368.     callHistory[i] = nil
  369.   end
  370. end
  371.  
  372. local function clearScreen()
  373.   term.setBackgroundColor(colors.black)
  374.   term.clear()
  375. end
  376.  
  377. local function clearArea(area, bgColor)
  378.   local x, y, w, h --# w, h are NOT width, height - they are the stopX, stopY values
  379.   if area == "lower" then
  380.     x, y, w, h = 1, 4, termX, termY
  381.   elseif area == "data" then
  382.     x, y, w, h = 14, 5, termX, termY - 1
  383.   end
  384.   paintutils.drawFilledBox(x, y, w, h, bgColor or colors.black)
  385. end
  386.  
  387. local function redrawInput()
  388.   term.setCursorPos(screen == "gatePage" and 31 or 24, termY - 2)
  389.   term.setBackgroundColor(colors.black)
  390.   term.setTextColor(colors.white)
  391.   if cachedInput ~= "" then term.write(cachedInput) end
  392. end
  393.  
  394. local function drawAddressButton()
  395.   term.setBackgroundColor(colors.blue)
  396.   term.setTextColor(gateChange and colors.lime or colors.white)
  397.   term.setCursorPos(2, 13)
  398.   term.write("Addr Book")
  399. end
  400.  
  401. local function drawControlSwitches(iris, callDrop)
  402.   if iris ~= nil then
  403.     term.setCursorPos(31, 5)
  404.     if iris == "none" then
  405.       term.setBackgroundColor(colors.green)
  406.       term.write(" ")
  407.       term.setBackgroundColor(colors.gray)
  408.       term.write("  ")
  409.       term.setBackgroundColor(colors.red)
  410.       term.write(" ")
  411.     else
  412.       term.setBackgroundColor(iris and colors.green or colors.gray)
  413.       term.write("  ")
  414.       term.setBackgroundColor(iris and colors.gray or colors.red)
  415.       term.write("  ")
  416.     end
  417.   end
  418.   if callDrop ~= nil then
  419.     term.setCursorPos(31, 7)
  420.     term.setBackgroundColor(callDrop and colors.green or colors.gray)
  421.     term.write("  ")
  422.     term.setBackgroundColor(callDrop and colors.gray or colors.red)
  423.     term.write("  ")
  424.   end
  425. end
  426.  
  427. local function drawPopUp(which)
  428.   if which == "clearLogs" then
  429.     term.setBackgroundColor(colors.blue)
  430.     term.setTextColor(colors.white)
  431.     term.setCursorPos(20, math.floor(termY / 2) - 2)
  432.     term.write(" Clear Logs? ")
  433.     paintutils.drawFilledBox(20, math.floor(termY / 2) - 1, 32, math.floor(termY /2 ) + 1, colors.gray)
  434.     term.setBackgroundColor(colors.green)
  435.     term.setCursorPos(21, math.floor(termY / 2))
  436.     term.write(" YES ")
  437.     term.setBackgroundColor(colors.red)
  438.     term.setCursorPos(27, math.floor(termY / 2))
  439.     term.write(" N O ")
  440.   elseif which == "exodus" then
  441.     term.setBackgroundColor(colors.blue)
  442.     term.setTextColor(colors.white)
  443.     term.setCursorPos(20, 9)
  444.     term.write("  Save Addr Book?  ")
  445.     paintutils.drawFilledBox(20, 10, 38, 12, colors.gray)
  446.     term.setBackgroundColor(colors.green)
  447.     term.setCursorPos(22, 11)
  448.     term.write(" Save ")
  449.     term.setBackgroundColor(colors.orange)
  450.     term.setCursorPos(31, 11)
  451.     term.write(" Quit ")
  452.   elseif which == "abManagement" then
  453.     term.setBackgroundColor(colors.blue)
  454.     term.setTextColor(colors.white)
  455.     term.setCursorPos(16, 10)
  456.     term.write(" Address Book Mgt. ")
  457.     paintutils.drawFilledBox(16, 11, 34, 17, colors.gray)
  458.     local buttonName
  459.     for _, button in pairs(guiElements.addrBookMgt) do
  460.       buttonName = button.getName()
  461.       if buttonName == "ABSave" then
  462.         button.setEnabled(gateChange)
  463.       elseif buttonName == "ABLoad" then
  464.         button.setEnabled(gateChange)
  465.       elseif buttonName == "ABImport" then
  466.         button.setEnabled(fs.exists("/disk/data/DHDgates"))
  467.       elseif buttonName == "ABExport" then
  468.         button.setEnabled(fs.exists("/disk"))
  469.       end
  470.       button.render()
  471.     end
  472.   elseif which == "page" then
  473.     term.setBackgroundColor(colors.gray)
  474.     term.setTextColor(colors.white)
  475.     for i = 1, 3 do
  476.       term.setCursorPos(screen == "gatePage" and 29 or 22, termY - 4 + i)
  477.       term.write(i == 1 and " :Page: " or "        ")
  478.     end
  479.     term.setBackgroundColor(colors.black)
  480.     term.setCursorPos(screen == "gatePage" and 30 or 23, termY - 2)
  481.     term.write("      ")
  482.   end
  483. end
  484.  
  485. local function drawHeader()
  486.   term.setTextColor(colors.white)
  487.   local line = string.rep(" ", 48)
  488.   for i = 1, 3 do
  489.     term.setCursorPos(1, i)
  490.     term.setBackgroundColor(colors.blue)
  491.     term.write(line)
  492.     term.setBackgroundColor(colors.red)
  493.     term.write(i == 2 and " X " or "   ")
  494.   end
  495.   term.setBackgroundColor(colors.blue)
  496.   term.setTextColor(colors.lightBlue)
  497.   local headerText = thisGate
  498.   for i = 1, abCount do
  499.     if addressBook[i].addr == thisGate then
  500.       headerText = addressBook[i].name .. " (" .. thisGate .. ")"
  501.       break
  502.     end
  503.   end
  504.   term.setCursorPos(math.floor((termX - #headerText) / 2), 2)
  505.   term.write(headerText)
  506. end
  507.  
  508. local function drawFooter()
  509.   term.setBackgroundColor(colors.gray)
  510.   term.setTextColor(colors.lightGray)
  511.   term.setCursorPos((screen == "main" or screen == "manage") and 13 or 1, termY)
  512.   term.write(string.rep(" ", (screen == "main" or screen == "manage") and termX - 12 or termX))
  513.   local cX = (screen == "main" or screen == "manage") and (math.ceil(termX / 2) - (#tostring(gatePage) + #tostring(gatePages) + 4) / 2) + 7 or (math.ceil(termX / 2) - (#tostring(logPage) + #tostring(logPages) + 4) / 2)
  514.   term.setCursorPos(cX, termY)
  515.   term.write((screen == "main" or screen == "manage") and (tostring(gatePage) .. " of " .. tostring(gatePages)) or (tostring(logPage) .. " of " .. tostring(logPages)))
  516.   if ((screen == "main" or screen == "manage") and gatePages > 1) or screen == "logs" then
  517.     for _, button in pairs((screen == "main" or screen == "manage") and guiElements.mainFooter or guiElements.logButtons) do
  518.       button.render()
  519.     end
  520.   end
  521. end
  522.  
  523. local function drawGateFooter()
  524.   term.setCursorPos(1, termY)
  525.   if gateChange then
  526.     term.setBackgroundColor(colors.orange)
  527.     term.setTextColor(colors.black)
  528.     term.write(" Change detected: Remember to save the Address Book ")
  529.     term.setBackgroundColor(colors.black)
  530.   else
  531.     term.setBackgroundColor(colors.black)
  532.     term.write(string.rep(" ", termX))
  533.   end
  534. end
  535.  
  536. do
  537.   local helpTable = {
  538.     [1] = { x = 2, y = 5, text = "Main Screen" };
  539.     [2] = { x = 2, y = 10, text = "Addr Book Btn" };
  540.     [3] = { x = 4, y = 12, text = "(popup)" };
  541.     [4] = { x = 2, y = 16, text = "Log Screen" };
  542.     [5] = { y = 6, text = "Left click" };
  543.     [6] = { y = 7, text = "Right click" };
  544.     [7] = { y = 8, text = "Middle click" };
  545.     [8] = { y = 11, text = "Right click" };
  546.     [9] = { y = 13, text = "Import/Export" };
  547.     [10] = { y = 14, text = "Load/Save" };
  548.     [11] = { y = 17, text = "Left click" };
  549.     [12] = { y = 18, text = "Right click" };
  550.     [13] = { y = 7, text = "an address to view/edit details" };
  551.     [14] = { y = 8, text = "an address to delete" };
  552.     [15] = { y = 11, text = "Save/update the Address Book" };
  553.     [16] = { y = 13, text = "Copy Addr Book FROM/TO floppy disk" };
  554.     [17] = { y = 14, text = "Load/Save the Address Book" };
  555.     [18] = { y = 17, text = "an address to dial" };
  556.     [19] = { y = 18, text = "an address to add to Address Book" };
  557.   }
  558.  
  559.   drawHelpScreen = function()
  560.     clearArea("lower", colors.white)
  561.     paintutils.drawFilledBox(1, 4, 15, termY, colors.gray)
  562.     term.setTextColor(colors.black)
  563.     for i = 1, 4 do
  564.       term.setCursorPos(helpTable[i].x, helpTable[i].y)
  565.       term.write(helpTable[i].text)
  566.     end
  567.     term.setTextColor(colors.lightGray)
  568.     for i = 5, 12 do
  569.       term.setCursorPos(2, helpTable[i].y)
  570.       term.write(helpTable[i].text)
  571.     end
  572.     term.setBackgroundColor(colors.white)
  573.     term.setTextColor(colors.gray)
  574.     term.setCursorPos(17, 6)
  575.     term.write(lcGate and "an address to dial/pause" or "an address to dial")
  576.     for i = 13, 19 do
  577.       term.setCursorPos(17, helpTable[i].y)
  578.       term.write(helpTable[i].text)
  579.     end
  580.   end
  581. end
  582.  
  583. local function drawLogData()
  584.   if logPage == logPages and logPages > 1 then
  585.     paintutils.drawFilledBox(1, 6, termX, termY, colors.white)
  586.   end
  587.   drawFooter()
  588.   term.setBackgroundColor(colors.white)
  589.   local currentEntry = ((logPage - 1) * 11) + 1 --# Set the first entry to show (based on page number)
  590.   local spacer = string.rep(" ", 9)
  591.   local callHistoryCount = #callHistory
  592.   local callDir, callAddr, addr, match
  593.   for i = currentEntry, math.min(currentEntry + 10, callHistoryCount) do --# Display logs
  594.     callDir = callHistory[i]:sub(callHistory[i]:find("<") + 1, callHistory[i]:find(">") - 1)
  595.     term.setTextColor((callDir == "Inbound" or callDir == "Incoming") and colors.gray or ((callDir == "Outbound" or callDir == "Outgoing") and colors.lightGray or colors.red))
  596.     term.setCursorPos(2, i - currentEntry + 7)  --# position entry
  597.     term.write(callHistory[i]:sub(1, callHistory[i]:find("@") - 2) .. "   ") --# write day
  598.     term.setCursorPos(8, i - currentEntry + 7)  --# position entry
  599.     term.write(callHistory[i]:sub(callHistory[i]:find("@") + 2, callHistory[i]:find("M")) .. " ") --# write time
  600.     term.setCursorPos(18, i - currentEntry + 7) --# position entry
  601.     term.write(callDir .. " ")                  --# write direction
  602.     term.setCursorPos(28, i - currentEntry + 7) --# position entry
  603.     callAddr = callHistory[i]:sub(callHistory[i]:find(">") + 2) --# set address
  604.     term.setTextColor((callDir == "Inbound" or callDir == "Incoming") and colors.cyan or ((callDir == "Outbound" or callDir == "Outgoing") and colors.lightBlue or colors.orange))
  605.     term.write(callAddr .. string.rep(" ", 9 - #callAddr)) --# write address
  606.     term.setCursorPos(39, i - currentEntry + 7) --# position entry
  607.     term.setTextColor((callDir == "Inbound" or callDir == "Incoming") and colors.gray or ((callDir == "Outbound" or callDir == "Outgoing") and colors.lightGray or colors.red))
  608.     match = false
  609.     for j = 1, abCount do
  610.       addr = addressBook[j].addr
  611.       if addr == callAddr or addr:sub(1, 7) == callAddr or addr == callAddr:sub(1, 7) then
  612.         term.write(addressBook[j].name .. string.rep(" ", 12 - #addressBook[j].name)) --# write name
  613.         match = true
  614.         break
  615.       end
  616.     end
  617.     if not match then term.write("         ") end
  618.   end
  619. end
  620.  
  621. local function drawLogScreen()
  622.   if not callHistory[1] then
  623.     term.setCursorPos(2, 6)
  624.     term.setBackgroundColor(colors.white)
  625.     term.setTextColor(colors.gray)
  626.     term.write("No Logs")
  627.     for _, button in pairs(guiElements.logButtons) do
  628.       button.setEnabled(false)
  629.     end
  630.     return
  631.   end
  632.   for _, button in pairs(guiElements.logButtons) do
  633.     button.setEnabled(true)
  634.   end
  635.   term.setBackgroundColor(colors.gray)                              --#
  636.   term.setTextColor(colors.white)                                   --#
  637.   term.setCursorPos(1, 4)                                           --# Header
  638.   term.write(string.rep(" ", termX))                                --#
  639.   term.setCursorPos(1, 5)                                           --#
  640.   term.write(" Day   Time     Vector   Address                   ") --#
  641.   drawLogData()
  642. end
  643.  
  644. local function drawRatingList(rating)
  645.   local txtColor
  646.   term.setTextColor(colors.white)
  647.   term.setBackgroundColor(colors.blue)
  648.   term.setCursorPos(18, 6)
  649.   term.write("   Classification   ")
  650.   paintutils.drawFilledBox(18, 7, 37, 16, colors.gray)
  651.   for k, v in pairs(classifications) do
  652.     if rating == k then
  653.       txtColor = rating == "U" and colors.white or v.color
  654.       paintutils.drawPixel(37, v.order + 7, v.color) --# current rating color pip
  655.     else
  656.       txtColor = colors.lightGray
  657.     end
  658.     paintutils.drawPixel(18, v.order + 7, v.color) --# color pip
  659.     term.setBackgroundColor(colors.gray)
  660.     term.setTextColor(txtColor)
  661.     term.setCursorPos(20, v.order + 7)
  662.     term.write(v.label)
  663.   end
  664. end
  665.  
  666. local function drawGateData()
  667.   term.setTextColor(colors.lightBlue)
  668.   term.setCursorPos(11, 5)
  669.   term.write(addressBook[currentGate].name .. string.rep(" ", 12 - #addressBook[currentGate].name))
  670.   term.setTextColor(colors.brown)
  671.   term.setCursorPos(13, 13)
  672.   term.write(addressBook[currentGate].loc.dim .. string.rep(" ", 19 - #addressBook[currentGate].loc.dim))
  673.   local gateNum = tostring(currentGate)
  674.   term.setCursorPos(termX - 6, 5)
  675.   term.setTextColor(colors.white)
  676.   term.write(gateNum .. string.rep(" ", 6 - #gateNum))
  677.   term.setCursorPos(8, 9)
  678.   term.write(addressBook[currentGate].note .. string.rep(" ", 43 - #addressBook[currentGate].note))
  679.   term.setTextColor(colors.yellow)
  680.   term.setCursorPos(11, 7)
  681.   term.write(addressBook[currentGate].addr .. "  ")
  682.   local ratingWord = assignRating(currentGate)
  683.   term.setTextColor(assignColor(currentGate))
  684.   term.setCursorPos(18, 11)
  685.   term.write(ratingWord .. string.rep(" ", 20 - #ratingWord))
  686.   if not lcGate then drawControlSwitches(addressBook[currentGate].iris, addressBook[currentGate].callDrop) term.setBackgroundColor(colors.black) end
  687.   term.setCursorPos(termX - 14, 7)
  688.   term.setTextColor(colors.gray)
  689.   term.write(addressBook[currentGate].addr == thisGate and "< This Gate >" or "             ")
  690.   local xStr, yStr, zStr = tostring(addressBook[currentGate].loc.x), tostring(addressBook[currentGate].loc.y), tostring(addressBook[currentGate].loc.z)
  691.   term.setTextColor(colors.lightGray)
  692.   term.setCursorPos(14, 15)
  693.   term.write(xStr .. string.rep(" ", 9 - #xStr))
  694.   term.setCursorPos(28, 15)
  695.   term.write(yStr .. string.rep(" ", 9 - #yStr))
  696.   term.setCursorPos(42, 15)
  697.   term.write(zStr .. string.rep(" ", 9 - #zStr))
  698.   drawGateFooter()
  699. end
  700.  
  701. local function drawGateLabels()
  702.   clearArea("lower")
  703.   term.setTextColor(colors.gray)
  704.   term.setCursorPos(2, 5)
  705.   term.write(lcGate and "Name:                                    #" or "Name:                  Iris:             #")
  706.   term.setCursorPos(2, 7)
  707.   term.write(lcGate and "Address:" or "Address:               Drop:")
  708.   term.setCursorPos(2, 9)
  709.   term.write("Note:")
  710.   term.setCursorPos(2, 11)
  711.   term.write("Classification:")
  712.   term.setCursorPos(2, 13)
  713.   term.write("Dimension:")
  714.   term.setCursorPos(2, 15)
  715.   term.write("Coords:  x:            y:            z:")
  716.   drawGateData()
  717. end
  718.  
  719. local function drawAddressList()
  720.   local xPos, yPos = 14, 5
  721.   local gName, spacer, addr, highlight
  722.   local magicNumber = ((gatePage - 1) * 20) + gatePage --# 21 gates/page
  723.   for i = magicNumber, math.min(abCount, gatePage * 21) do
  724.     addr = addressBook[i].addr
  725.     if addr == gateTarget or addr:sub(1, 7) == gateTarget or addr == gateTarget:sub(1, 7) then
  726.       highlight = true
  727.     else
  728.       highlight = false
  729.     end
  730.     term.setBackgroundColor(addr == thisGate and colors.gray or assignColor(i))
  731.     term.setTextColor((addr == thisGate or addr == thisGate:sub(1, 7) or addr:sub(1, 7) == thisGate) and colors.lightGray or (highlight and colors.black or colors.white))
  732.     gName = addressBook[i].name
  733.     spacer = (12 - #gName) / 2
  734.     term.setCursorPos(xPos, yPos)
  735.     term.write(string.rep(" ", math.floor(spacer)) .. gName .. string.rep(" ", math.ceil(spacer)))
  736.     yPos = yPos + 2
  737.     if yPos >= termY - 1 then xPos = xPos + 13 yPos = 5 end
  738.   end
  739.   addrRedraw = false
  740. end
  741.  
  742. local function drawIrisStatus()
  743.   paintutils.drawLine(10, 9, 11, 9, irisState and colors.lime or colors.orange)
  744.   if screen == "gatePage" then redrawInput() end
  745. end
  746.  
  747. local function drawIrisWaitingStatus()
  748.   term.setBackgroundColor(colors.lightBlue)
  749.   term.setCursorPos(2, 9)
  750.   term.setTextColor(irisState and colors.orange or colors.green)
  751.   term.write("I r i s")
  752. end
  753.  
  754. local function drawIrisButton()
  755.   term.setBackgroundColor(colors.lightBlue)
  756.   term.setTextColor(colors.white)
  757.   term.setCursorPos(2, 9)
  758.   term.write("I r i s")
  759.   drawIrisStatus()
  760. end
  761.  
  762. local function drawDHDStatus()
  763.   paintutils.drawFilledBox(1, 4, 12, 7, fuelPercent > 5 and colors.white or colors.red)
  764.   term.setTextColor(colors.blue)
  765.   term.setCursorPos(2, 5)
  766.   term.write(gateStatus)
  767.   if lcGate then
  768.     term.setTextColor(gateStatus == "Dialing" and colors.lightGray or colors.gray)
  769.   else
  770.     term.setTextColor((gateStatus == "Dialing" or gateStatus == "Incoming") and colors.lightGray or colors.gray)
  771.   end
  772.   term.setCursorPos(2, 6)
  773.   term.write(fuelPercent > 5 and gateTarget or (gateTarget == "No Target" and "Low Fuel" or gateTarget))
  774.   if addrRedraw then drawAddressList() end
  775.   if screen == "gatePage" then drawPopUp("page") redrawInput() end
  776. end
  777.  
  778. local function drawLeftPane()
  779.   drawDHDStatus()
  780.   paintutils.drawFilledBox(1, 8, 12, termY, colors.gray)
  781.   for _, button in pairs(guiElements.mainButtons) do
  782.     button.render()
  783.   end
  784.   if gateChange then drawAddressButton() end
  785.   drawIrisStatus()
  786. end
  787.  
  788. local function drawMainScreen()
  789.   drawLeftPane()
  790.   drawFooter()
  791.   drawAddressList()
  792. end
  793.  
  794. local function flashDial(name, x, y, gateNum)
  795.   local gColor = assignColor(gateNum)
  796.   local spacer = (12 - #name) / 2
  797.   local gLabel = string.rep(" ", math.floor(spacer)) .. name .. string.rep(" ", math.ceil(spacer))
  798.   term.setBackgroundColor(colors.black)
  799.   term.setTextColor(gColor)
  800.   term.setCursorPos(x, y)
  801.   term.write(gLabel)
  802.   sleep(0.1)
  803.   term.setBackgroundColor(gColor)
  804.   term.setTextColor(colors.white)
  805.   term.setCursorPos(x, y)
  806.   term.write(gLabel)
  807.   addrRedraw = true
  808. end
  809.  
  810. local function flashChoice(x, y, txtCol, bgCol, text)
  811.   term.setBackgroundColor(bgCol)
  812.   term.setTextColor(txtCol)
  813.   term.setCursorPos(x, y)
  814.   term.write(text)
  815.   sleep(0.1)
  816.   term.setBackgroundColor(txtCol)
  817.   term.setTextColor(bgCol)
  818.   term.setCursorPos(x, y)
  819.   term.write(text)
  820. end
  821.  
  822. local function addNewAddress(newEntry, fast)
  823.   abCount, gateChange = abCount + 1, true
  824.   addressBook[abCount] = { name = newEntry or "NEW GATE", addr = newEntry or "ADDRESS", rating = "U", iris = "none", callDrop = false, note = newEntry and "Added from logs" or "short note", loc = { x = 0, y = 0, z = 0, dim = "Unspecified", }, }
  825.   gatePages = math.ceil(abCount / 21)
  826.   if fast then
  827.     if gatePage == gatePages then drawAddressList() end
  828.     drawFooter()
  829.     drawAddressButton()
  830.   else
  831.     screen = "gate"
  832.     currentGate = abCount
  833.     drawGateLabels()
  834.   end
  835. end
  836.  
  837. local function disengageChevron(auto) --# LanteaCraft
  838.   if auto then autoDisengage = true end
  839.   pcall(gate.deactivateChevron)
  840.   --local success = pcall(gate.deactivateChevron)
  841.   --if success then
  842.     gateStatus = "Closing"
  843.     gateTarget = gateTarget:sub(1, numChevrons)
  844.     if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  845.       drawDHDStatus()
  846.     end
  847.   --end
  848. end
  849.  
  850. local function hangUp() --# LanteaCraft & SGCraft
  851.   local onHook = false
  852.   if lcGate then
  853.     if gateStatus == "Connected" then
  854.  
  855.       onHook = pcall(gate.disengageStargate)
  856.       --if onHook then gateStatus = "Closing" end
  857.       gateStatus = "Closing"
  858.  
  859.       if gate.getActivatedChevrons() > 0 then disengageChevron(true) end
  860.     elseif gateStatus == "Dialing" or gateStatus == "Paused" then
  861.       continueDialing = false
  862.       while spinTime do
  863.         os.queueEvent("spinWait")
  864.         os.pullEvent("spinWait")
  865.       end
  866.       disengageChevron(true)
  867.     end
  868.   else
  869.     onHook = gate.disconnect()
  870.     if onHook then gateStatus = "Closing" end
  871.   end
  872.   if onHook then
  873.     gateTarget = "No Target"
  874.     incomingAddress = nil
  875.     chevronNumber = 0
  876.   end
  877. end
  878.  
  879. local function lockChevron(glyphNum) --# LanteaCraft
  880.   --local dialStatus = false
  881.   if continueDialing then
  882.     --[[
  883.     dialStatus = pcall(gate.selectGlyph, gateTarget:sub(glyphNum, glyphNum))
  884.     if not dialStatus then return false end
  885.     dialStatus = pcall(gate.activateChevron)
  886.     ]]--
  887.     pcall(gate.selectGlyph, gateTarget:sub(glyphNum, glyphNum))
  888.     pcall(gate.activateChevron)
  889.     return true
  890.   end
  891.   --return dialStatus
  892.   return false
  893. end
  894.  
  895. local function pauseDial() --# LanteaCraft
  896.   if gateStatus == "Dialing" then
  897.     continueDialing = false
  898.     gateStatus = "Paused"
  899.   elseif gateStatus == "Paused" then
  900.     continueDialing = true
  901.     if lockChevron(chevronNumber + 1) then
  902.       gateStatus = "Dialing"
  903.     else
  904.       continueDialing = false
  905.       gateStatus = "Paused"
  906.     end
  907.   end
  908.   --fuelPercent = getFuelLevel()
  909.   term.setBackgroundColor(fuelPercent > 5 and colors.white or colors.red)
  910.   term.setTextColor(colors.blue)
  911.   term.setCursorPos(2, 5)
  912.   term.write(gateStatus .. " ")
  913.   if screen == "gatePage" then redrawInput() end
  914. end
  915.  
  916. local function dialOut(addr) --# LanteaCraft & SGCraft
  917.   if lcGate then
  918.     gateTarget = addr
  919.     continueDialing = true
  920.     if lockChevron(1) then
  921.       gateStatus = "Dialing"
  922.       drawDHDStatus()
  923.     else
  924.       hangUp()
  925.     end
  926.   else
  927.     local ok = gate.dial(addr)
  928.     if ok then
  929.       gateTarget = addr
  930.       gateStatus = "Dialing"
  931.       drawDHDStatus()
  932.     end
  933.   end
  934. end
  935.  
  936. local function toggleIris() --# LanteaCraft & SGCraft
  937.   local ok
  938.   if lcGate then
  939.     if irisState then
  940.       pcall(gate.openIris)
  941.     else
  942.       pcall(gate.closeIris)
  943.     end
  944.     ok = true
  945.   else
  946.     if irisState then
  947.       ok = gate.openIris()
  948.     else
  949.       ok = gate.closeIris()
  950.     end
  951.   end
  952.   if ok then drawIrisWaitingStatus() end
  953. end
  954.  
  955. local function manageIris()
  956.   local ok
  957.   for i = 1, abCount do
  958.     if addressBook[i].addr == incomingAddress or addressBook[i].addr:sub(1, 7) == incomingAddress or addressBook[i].addr == incomingAddress:sub(1, 7) then
  959.       if lcGate and type(addressBook[i].iris) == "boolean" then
  960.         if addressBook[i].iris then
  961.           pcall(gate.openIris)
  962.         else
  963.           pcall(gate.closeIris)
  964.         end
  965.         ok = true
  966.       elseif not lcGate and type(addressBook[i].iris) == "boolean" then
  967.         if addressBook[i].iris then
  968.           ok = gate.openIris()
  969.         else
  970.           ok = gate.closeIris()
  971.         end
  972.       end
  973.       if ok and (((addressBook[i].iris and irisState) or (not addressBook[i].iris and not irisState)) and (screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus")) then
  974.         drawIrisWaitingStatus()
  975.         if screen == "gatePage" then redrawInput() end
  976.       end
  977.       break
  978.     end
  979.   end
  980. end
  981.  
  982. local function lcIrisMonitor() --# LanteaCraft
  983.   local event
  984.   while true do
  985.     event = os.pullEvent()
  986.     if event == "irisDestroyed" or event == "irisOpened" then
  987.       irisState = false
  988.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  989.         drawIrisButton()
  990.       end
  991.     elseif event == "irisClosed" then
  992.       irisState = true
  993.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  994.         drawIrisButton()
  995.       end
  996.     end
  997.   end
  998. end
  999.  
  1000. local function sgIrisMonitor() --# SGCraft
  1001.   while true do
  1002.     local irisEvent, _, newState, oldState = os.pullEvent("sgIrisStateChange")
  1003.     if newState == "Closed" or newState == "Open" or newState == "Offline" then
  1004.       irisState = newState == "Closed"
  1005.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1006.         drawIrisButton()
  1007.       end
  1008.     end
  1009.   end
  1010. end
  1011.  
  1012. local function sgGateMonitor() --# SGCraft
  1013.   while true do
  1014.     local sgEvent, _, newState, oldState = os.pullEvent("sgStargateStateChange")
  1015.     gateStatus = sgStates[newState] or "Unknown"
  1016.     if gateStatus == "Closing" or gateStatus == "Idle" or gateStatus == "Offline" then
  1017.       gateTarget = "No Target"
  1018.       incomingAddress = nil
  1019.       chevronNumber = 0
  1020.     elseif gateStatus == "Connected" then
  1021.       if gateTarget:find("?") then
  1022.         gateTarget = gateTarget:sub(1, gateTarget:find("?") - 1)
  1023.       end
  1024.       fuelPercent = getFuelLevel()
  1025.       recordSessionData()
  1026.       if not incomingAddress then updateTimer = os.startTimer(5) end
  1027.     end
  1028.     if (gateStatus == "Idle" or gateStatus == "Offline" or gateStatus == "Connected") and (screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus") then
  1029.       drawAddressList()
  1030.     end
  1031.     if gateStatus ~= "Dialing" and (screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus") then
  1032.       drawDHDStatus()
  1033.     end
  1034.   end
  1035. end
  1036.  
  1037. local function lcGateMonitor() --# LanteaCraft
  1038.   local event, numChevrons
  1039.   while true do
  1040.     event = os.pullEvent()
  1041.     if event == "disengageGlyph" then
  1042.       numChevrons = gate.getActivatedChevrons()
  1043.       if numChevrons and type(numChevrons) == "number" and numChevrons > 0 then
  1044.         if autoDisengage then disengageChevron() end
  1045.       else
  1046.         gateStatus = "Idle"
  1047.         gateTarget = "No Target"
  1048.         incomingAddress = nil
  1049.         chevronNumber = 0
  1050.         autoDisengage = false
  1051.         if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1052.           addrRedraw = true
  1053.           drawDHDStatus()
  1054.         end
  1055.       end
  1056.     elseif event == "spinToGlyph" then
  1057.       spinTime = true
  1058.     elseif event == "connect" then
  1059.       gateStatus = "Connected"
  1060.       spinTime = false
  1061.       if gateTarget:find("?") then gateTarget = gate.getActivatedGlyphs() end
  1062.       if not incomingAddress then updateTimer = os.startTimer(5) end
  1063.       --gateTarget = incomingAddress or gateTarget
  1064.       --if incomingAddress then manageIris() end
  1065.  
  1066.       fuelPercent = getFuelLevel()
  1067.       autoDisengage = false
  1068.       if gateTarget == "No Target" then
  1069.         gateTarget = "Wormhole "
  1070.         incomingAddress = true
  1071.       end
  1072.  
  1073.       recordSessionData()
  1074.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1075.         --if incomingAddress then addrRedraw = true end
  1076.         drawDHDStatus()
  1077.       end
  1078.     elseif event == "disconnect" then
  1079.       gateStatus = "Idle"
  1080.       gateTarget = "No Target"
  1081.       incomingAddress = nil
  1082.       chevronNumber = 0
  1083.       autoDisengage = false
  1084.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1085.         addrRedraw = true
  1086.         drawDHDStatus()
  1087.       end
  1088.     end
  1089.   end
  1090. end
  1091.  
  1092. local function lcChevronEncoder() --# LanteaCraft
  1093.   while true do
  1094.     os.pullEvent("engageGlyph")
  1095.     spinTime = false
  1096.     chevronNumber = chevronNumber + 1
  1097.     if gateTarget:find("?") or gateTarget == "No Target" then gateTarget = gate.getActivatedGlyphs() .. string.rep("?", 9 - gate.getActivatedChevrons()) end
  1098.     if chevronNumber == #gateTarget then
  1099.       continueDialing = false
  1100.       pcall(gate.engageStargate)
  1101.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1102.         drawDHDStatus()
  1103.       end
  1104.     else
  1105.       local drawStatus = false
  1106.       if gateTarget:find("?") and chevronNumber == 1 then
  1107.         gateStatus = "Dialing"
  1108.         drawStatus = true
  1109.       end
  1110.       if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1111.         if drawStatus then drawDHDStatus() end
  1112.         --fuelPercent = getFuelLevel()
  1113.         term.setCursorPos(2, 6)
  1114.         term.setBackgroundColor(fuelPercent > 5 and colors.white or colors.red)
  1115.         term.setTextColor(chevronNumber > 0 and colors.gray or colors.lightGray)
  1116.         term.write(chevronNumber > 0 and gateTarget:sub(1, chevronNumber) or gateTarget)
  1117.         if screen == "gatePage" then redrawInput() end
  1118.       end
  1119.       if continueDialing and not lockChevron(chevronNumber + 1) then
  1120.         continueDialing = false
  1121.         gateStatus = "Paused"
  1122.         if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1123.           drawDHDStatus()
  1124.         end
  1125.       end
  1126.     end
  1127.   end
  1128. end
  1129.  
  1130. local function sgChevronEncoder() --# SGCraft
  1131.   local sgEvent, _, chevron
  1132.   while true do
  1133.     sgEvent, _, chevronNumber, chevron = os.pullEvent("sgChevronEngaged")
  1134.     if gateTarget:find("?") then
  1135.       gateTarget = gateTarget:sub(1, chevronNumber - 1) .. chevron .. string.rep("?", 9 - chevronNumber)
  1136.     end
  1137.     local drawStatus = false
  1138.     if gateTarget == "No Target" then
  1139.       gateTarget = chevron .. "????????"
  1140.       gateStatus = "Dialing"
  1141.       drawStatus = true
  1142.     end
  1143.     if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1144.       if drawStatus then drawDHDStatus() end
  1145.       --fuelPercent = getFuelLevel()
  1146.       term.setCursorPos(2, 6)
  1147.       term.setBackgroundColor(fuelPercent > 5 and colors.white or colors.red)
  1148.       term.setTextColor(colors.gray)
  1149.       term.write(gateTarget:sub(1, chevronNumber))
  1150.       if incomingAddress then
  1151.         term.setTextColor(colors.lightGray)
  1152.         term.write(gateTarget:sub(chevronNumber + 1))
  1153.       end
  1154.       if screen == "gatePage" then redrawInput() end
  1155.     end
  1156.   end
  1157. end
  1158.  
  1159. local function sgChevronUnencoded() --# SGCraft
  1160.   local sgEvent, _
  1161.   while true do
  1162.     sgEvent, _, chevronNumber = os.pullEvent("sgChevronUnset")
  1163.     if gateStatus == "Dialing" then
  1164.       if chevronNumber == 0 then
  1165.         gateTarget, gateStatus = "No Target", "Idle"
  1166.       else
  1167.         gateTarget = gateTarget:sub(1, chevronNumber) .. string.rep("?", 9 - chevronNumber)
  1168.       end
  1169.       drawDHDStatus()
  1170.     end
  1171.   end
  1172. end
  1173.  
  1174. --[[
  1175. local function gateClosing() --# LanteaCraft
  1176.   while true do
  1177.     os.pullEvent("sgWormholeClosing")
  1178.     gateStatus = "Closing"
  1179.     gateTarget = "No Target"
  1180.     incomingAddress = nil
  1181.     chevronNumber = 0
  1182.     if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1183.       addrRedraw = true
  1184.       drawDHDStatus()
  1185.     end
  1186.   end
  1187. end
  1188. ]]--
  1189.  
  1190. local function incomingCall() --# LanteaCraft & SGCraft
  1191.   local _, incomingChevron
  1192.   while true do
  1193.     if lcGate then
  1194.       _, incomingChevron = os.pullEvent("sgIncoming")
  1195.       incomingAddress = incomingAddress and incomingAddress .. incomingChevron or incomingChevron
  1196.     else
  1197.       os.pullEvent("sgDialIn")
  1198.       incomingAddress = gate.remoteAddress()
  1199.     end
  1200.     gateStatus = "Incoming"
  1201.     gateTarget = incomingAddress
  1202.     if not lcGate then
  1203.       for i = 1, abCount do
  1204.         if (addressBook[i].addr == gateTarget or addressBook[i].addr:sub(1, 7) == gateTarget or addressBook[i].addr == gateTarget:sub(1, 7)) and addressBook[i].callDrop then
  1205.           hangUp()
  1206.           break
  1207.         end
  1208.       end
  1209.       if incomingAddress then manageIris() end
  1210.     end
  1211.     if screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus" then
  1212.       if not lcGate then addrRedraw = true end
  1213.       drawDHDStatus()
  1214.     end
  1215.   end
  1216. end
  1217.  
  1218. local function changePage()
  1219.   if gatePage == gatePages and gatePages > 1 then clearArea("data") end
  1220.   drawAddressList()
  1221.   drawFooter()
  1222. end
  1223.  
  1224. local function inputExodus()
  1225.   while true do
  1226.     local _, button, x, y = os.pullEvent("mouse_click")
  1227.     if y > 8 and y < 13 and x > 19 and x < 39 then
  1228.       if y == 11 and x > 21 and x < 28 and button == 1 then
  1229.         flashChoice(22, 11, colors.green, colors.white, " Save ")
  1230.         saveData(gateData)
  1231.         kernelState = false
  1232.         return
  1233.       elseif y == 11 and x > 30 and x < 37 and button == 1 then
  1234.         flashChoice(31, 11, colors.orange, colors.white, " Quit ")
  1235.         kernelState = false
  1236.         return
  1237.       end
  1238.     else
  1239.       screen = "main"
  1240.       paintutils.drawFilledBox(20, 9, 38, 12, colors.black) --# clear pop-up
  1241.       drawAddressList()
  1242.       return
  1243.     end
  1244.   end
  1245. end
  1246.  
  1247. local function inputHelp()
  1248.   while true do
  1249.     local event, data, x, y = os.pullEvent()
  1250.     if event == "mouse_click" then
  1251.       if x > termX - 3 and y < 4 and data == 1 then
  1252.         screen = "main"
  1253.         clearArea("lower")
  1254.         drawMainScreen()
  1255.         return
  1256.       end
  1257.     elseif event == "key" and data == keys.f1 then
  1258.       screen = "main"
  1259.       clearArea("lower")
  1260.       drawMainScreen()
  1261.       return
  1262.     end
  1263.   end
  1264. end
  1265.  
  1266. local function inputLogChangePage()
  1267.   term.setBackgroundColor(colors.black)
  1268.   term.setTextColor(colors.white)
  1269.   term.setCursorPos(24, termY - 2)
  1270.   local newPage = tonumber(read(nil, nil, 4))
  1271.   logPage = newPage or logPage
  1272.   logPage = math.floor(math.max(1, math.min(logPage, logPages)))
  1273.   screen = "logs"
  1274.   paintutils.drawFilledBox(22, termY - 3, 30, termY - 1, colors.white) --# clear pop-up
  1275.   drawLogData()
  1276. end
  1277.  
  1278. local function inputLogScreen()
  1279.   while true do
  1280.     local event, data, x, y = os.pullEvent()
  1281.     if event == "mouse_click" then
  1282.       if x > termX - 3 and y < 4 and data == 1 then
  1283.         clearCallHistory()
  1284.         screen = "main"
  1285.         clearArea("lower")
  1286.         drawMainScreen()
  1287.         return
  1288.       elseif x > 20 and x < 31 and y == termY and logPages > 1 then
  1289.         local pageStr, pagesStr = tostring(logPage), tostring(logPages)
  1290.         flashChoice((math.ceil(termX / 2) - (#pageStr + #pagesStr + 4) / 2), termY, colors.gray, colors.lightGray, pageStr .. " of " .. pagesStr)
  1291.         screen = "logPage"
  1292.         drawPopUp("page")
  1293.         return
  1294.       elseif y > 6 and y < termY - 1 and x > 27 and x < 37 then
  1295.         local currentEntry = ((logPage - 1) * 11) + 1 --# Set the first entry to show (based on page number)
  1296.         if callHistory[currentEntry + y - 7] then
  1297.           local targetAddress = callHistory[currentEntry + y - 7]:sub(callHistory[currentEntry + y - 7]:find(">") + 2)
  1298.           local taLen = #targetAddress
  1299.           if data == 1 and (taLen == 7 or taLen == 9) then
  1300.             clearCallHistory()
  1301.             screen = "main"
  1302.             dialOut(targetAddress)
  1303.             clearArea("lower")
  1304.             drawMainScreen()
  1305.           elseif data == 2 and (taLen == 7 or taLen == 9) then
  1306.             clearCallHistory()
  1307.             local foundAddr = false
  1308.             for i = 1, abCount do
  1309.               if targetAddress == addressBook[i].addr then
  1310.                 screen = "gate"
  1311.                 currentGate = i
  1312.                 drawGateLabels()
  1313.                 foundAddr = true
  1314.                 break
  1315.               end
  1316.             end
  1317.             if not foundAddr then addNewAddress(targetAddress) end
  1318.           end
  1319.           return
  1320.         end
  1321.       else
  1322.         local modeSwitch = false
  1323.         for _, click in pairs(guiElements.logButtons) do
  1324.           modeSwitch = click.processEvent(event, data, x, y)
  1325.           if modeSwitch then return end
  1326.         end
  1327.       end
  1328.     elseif event == "mouse_scroll" and ((data == 1 and logPage < logPages) or (data == -1 and logPage > 1)) then
  1329.       logPage = logPage + data
  1330.       drawLogData()
  1331.     elseif event == "key" and logPages > 1 then --# pageUp, pageDn, Home, End
  1332.       if (data == keys.pageUp and logPage > 1) or (data == keys.pageDown and logPage < logPages) then
  1333.         logPage = data == keys.pageUp and logPage - 1 or logPage + 1
  1334.       elseif (data == keys.home and logPage > 1) or (data == keys["end"] and logPage < logPages) then
  1335.         logPage = data == keys.home and 1 or logPages
  1336.       end
  1337.       drawLogData()
  1338.     end
  1339.   end
  1340. end
  1341.  
  1342. local function inputClearLogs()
  1343.   while true do
  1344.     local _, button, x, y = os.pullEvent("mouse_click")
  1345.     if y == math.floor(termY / 2) then
  1346.       if x > math.floor(termX / 2) - 5 and x < math.floor(termX / 2) + 1 and button == 1 then     --# yes
  1347.         flashChoice(math.floor(termX / 2) - 4, math.floor(termY / 2), colors.green, colors.white, " YES ")
  1348.         screen = "logs"
  1349.         local clearLog = fs.open(historyData, "w")
  1350.         clearLog.close()
  1351.         clearCallHistory()
  1352.         logPage, logPages = 1, 1
  1353.         clearArea("lower", colors.white)
  1354.         drawLogScreen()
  1355.         return
  1356.       elseif x > math.floor(termX / 2) + 1 and x < math.floor(termX / 2) + 7 and button == 1 then --# no
  1357.         flashChoice(math.floor(termX / 2) + 2, math.floor(termY / 2), colors.red, colors.white, " N O ")
  1358.         screen = "logs"
  1359.         paintutils.drawFilledBox(20, math.floor(termY / 2) - 2, 32, math.floor(termY / 2 ) + 1, colors.white)
  1360.         drawLogData()
  1361.         return
  1362.       end
  1363.     end
  1364.   end
  1365. end
  1366.  
  1367. local function inputAddressBookMgt()
  1368.   while true do
  1369.     local event, button, x, y = os.pullEvent("mouse_click")
  1370.     local modeSwitch = false
  1371.     for _, click in pairs(guiElements.addrBookMgt) do      --# process buttons
  1372.       modeSwitch = click.processEvent(event, button, x, y) --# carry out action for clicked button
  1373.       if modeSwitch then return end
  1374.     end
  1375.   end
  1376. end
  1377.  
  1378. local function inputGateRating()
  1379.   local selected = false
  1380.   while true do
  1381.     local _, button, x, y = os.pullEvent("mouse_click")
  1382.     if x > 17 and x < 38 and y > 5 and y < 17 then
  1383.       if button == 1 and y > 7 and y < 16 then
  1384.         for k, v in pairs(classifications) do
  1385.           if y == v.order + 7 then
  1386.             if addressBook[currentGate].rating ~= k then
  1387.               addressBook[currentGate].rating = k
  1388.               gateChange = true
  1389.             end
  1390.             selected = true
  1391.             break
  1392.           end
  1393.         end
  1394.         if selected then break end
  1395.       end
  1396.     else
  1397.       break
  1398.     end
  1399.   end
  1400.   paintutils.drawFilledBox(18, 6, 37, 17, colors.black) --# clear menu body
  1401.   term.setTextColor(colors.gray)
  1402.   term.setCursorPos(25, 7)
  1403.   term.write("Drop:")
  1404.   term.setCursorPos(25, 15)
  1405.   term.write("y:")
  1406.   screen = "gate"
  1407.   drawGateData()
  1408. end
  1409.  
  1410. local function inputViewGate()
  1411.   while true do
  1412.     local event, data, x, y = os.pullEvent()
  1413.     if event == "mouse_click" then
  1414.       if x > termX - 3 and y < 4 and data == 1 then
  1415.         screen = "main"
  1416.         currentGate = nil
  1417.         clearArea("lower")
  1418.         drawMainScreen()
  1419.         return
  1420.       elseif y == 5 and data == 1 then  --# Name, auto-iris, gate order
  1421.         if x > 10 and x < 23 then       --# Name
  1422.           term.setTextColor(colors.gray)
  1423.           term.setCursorPos(11, 5)
  1424.           term.write(addressBook[currentGate].name)
  1425.           term.setTextColor(colors.lightBlue)
  1426.           term.setCursorPos(11, 5)
  1427.           local newName = read(nil, { addressBook[currentGate].addr, addressBook[currentGate].name }, 12)
  1428.           if newName ~= "" and newName ~= addressBook[currentGate].name then
  1429.             addressBook[currentGate].name = newName
  1430.             gateChange = true
  1431.             if addressBook[currentGate].addr == thisGate then drawHeader() end
  1432.           end
  1433.           term.setTextColor(colors.lightBlue)
  1434.           term.setCursorPos(11, 5)
  1435.           term.write(addressBook[currentGate].name .. string.rep(" ", 12 - #addressBook[currentGate].name))
  1436.         elseif x > 30 and x < 35 and not lcGate then --# iris blacklist/whitelist
  1437.           if addressBook[currentGate].iris == "none" then
  1438.             addressBook[currentGate].iris = true
  1439.           elseif addressBook[currentGate].iris then
  1440.             addressBook[currentGate].iris = false
  1441.           else
  1442.             addressBook[currentGate].iris = "none"
  1443.           end
  1444.           drawControlSwitches(addressBook[currentGate].iris)
  1445.           gateChange = true
  1446.         elseif x > termX - 7 and x < termX - 6 + #tostring(currentGate) then --# change entry #
  1447.           term.setCursorPos(termX - 6, 5)
  1448.           term.write(string.rep(" ", 6))
  1449.           term.setTextColor(colors.gray)
  1450.           term.setCursorPos(termX - 6, 5)
  1451.           term.write(tostring(currentGate))
  1452.           term.setTextColor(colors.white)
  1453.           term.setCursorPos(termX - 6, 5)
  1454.           local newPos = tonumber(read(nil, { tostring(currentGate) }, 3))
  1455.           if newPos and newPos ~= currentGate then
  1456.             local tempGate = { }
  1457.             for k, v in pairs(addressBook[currentGate]) do
  1458.               tempGate[k] = v
  1459.             end
  1460.             table.remove(addressBook, currentGate)
  1461.             table.insert(addressBook, newPos, tempGate)
  1462.             currentGate = newPos
  1463.             gateChange = true
  1464.           end
  1465.           term.setCursorPos(termX - 6, 5)
  1466.           term.write(string.rep(" ", 6))
  1467.           term.setCursorPos(termX - 6, 5)
  1468.           term.setTextColor(colors.white)
  1469.           term.write(tostring(currentGate))
  1470.         end
  1471.       elseif y == 7 then
  1472.         if x > 10 and x < 20 then  --# Address
  1473.           if data == 1 then
  1474.             term.setTextColor(colors.gray)
  1475.             term.setCursorPos(11, 7)
  1476.             term.write(addressBook[currentGate].addr)
  1477.             term.setTextColor(colors.yellow)
  1478.             term.setCursorPos(11, 7)
  1479.             local newAddress = read(nil, { addressBook[currentGate].addr }, 9, nil, nil, true)
  1480.             local naLen = #newAddress
  1481.             if newAddress and (naLen == 7 or naLen == 9) and newAddress ~= addressBook[currentGate].addr and not newAddress:find("?") then
  1482.               addressBook[currentGate].addr = newAddress
  1483.               local gNames = { "NEW GATE", "NO GATES", "Name" }
  1484.               for _, tName in ipairs(gNames) do
  1485.                 if addressBook[currentGate].name == tName then
  1486.                   addressBook[currentGate].name = newAddress
  1487.                   term.setTextColor(colors.lightBlue)
  1488.                   term.setCursorPos(11, 5)
  1489.                   term.write(addressBook[currentGate].name .. string.rep(" ", 12 - #addressBook[currentGate].name))
  1490.                   break
  1491.                 end
  1492.               end
  1493.               gateChange = true
  1494.               if addressBook[currentGate].addr == thisGate then drawHeader() end
  1495.             end
  1496.             term.setTextColor(colors.yellow)
  1497.             term.setCursorPos(11, 7)
  1498.             term.write(addressBook[currentGate].addr .. "  ")
  1499.           elseif data == 2 and addressBook[currentGate].addr ~= thisGate then
  1500.             local dial = addressBook[currentGate].addr
  1501.             screen = "main"
  1502.             currentGate = nil
  1503.             clearArea("lower")
  1504.             dialOut(dial)
  1505.             drawMainScreen()
  1506.             return
  1507.           end
  1508.         elseif x > 30 and x < 35 and data == 1 and not lcGate then --# callDrop
  1509.           addressBook[currentGate].callDrop = not addressBook[currentGate].callDrop
  1510.           drawControlSwitches(nil, addressBook[currentGate].callDrop)
  1511.           gateChange = true
  1512.         end
  1513.       elseif y == 9 and x > 7 and x < 8 + #addressBook[currentGate].note:sub(1, 43) and data == 1 then --# Note
  1514.         term.setTextColor(colors.gray)
  1515.         term.setCursorPos(8, 9)
  1516.         term.write(addressBook[currentGate].note:sub(1, 43))
  1517.         term.setTextColor(colors.white)
  1518.         term.setCursorPos(8, 9)
  1519.         local newNote = read(nil, { addressBook[currentGate].note }, 43)
  1520.         if newNote ~= "" and newNote ~= addressBook[currentGate].note then
  1521.           addressBook[currentGate].note = newNote
  1522.           gateChange = true
  1523.         end
  1524.         term.setTextColor(colors.white)
  1525.         term.setCursorPos(8, 9)
  1526.         term.write(addressBook[currentGate].note:sub(1, 43) .. string.rep(" ", 43 - #addressBook[currentGate].note:sub(1, 43)))
  1527.       elseif y == 11 and x > 17 and x < 18 + #assignRating(currentGate) and data == 1 then --# Classification
  1528.         screen = "rating"
  1529.         drawRatingList(addressBook[currentGate].rating)
  1530.         return
  1531.       elseif y == 13 and x > 12 and x < 13 + #addressBook[currentGate].loc.dim and data == 1 then --# Dimension
  1532.         term.setTextColor(colors.gray)
  1533.         term.setCursorPos(13, 13)
  1534.         term.write(addressBook[currentGate].loc.dim)
  1535.         term.setTextColor(colors.brown)
  1536.         term.setCursorPos(13, 13)
  1537.         local newDim = read(nil, { "Overworld", "Nether", "The End", addressBook[currentGate].loc.dim }, 19)
  1538.         if newDim ~= "" and newDim ~= addressBook[currentGate].loc.dim then
  1539.           addressBook[currentGate].loc.dim = newDim
  1540.           gateChange = true
  1541.         end
  1542.         term.setCursorPos(13, 13)
  1543.         term.write(addressBook[currentGate].loc.dim .. string.rep(" ", 19 - #addressBook[currentGate].loc.dim))
  1544.       elseif y == 15 and data == 1 then                       --# x, y, z coords
  1545.         local xStr, yStr, zStr = tostring(addressBook[currentGate].loc.x), tostring(addressBook[currentGate].loc.y), tostring(addressBook[currentGate].loc.z)
  1546.         if x > 13 and x < 14 + #xStr then
  1547.           term.setTextColor(colors.gray)
  1548.           term.setCursorPos(14, 15)
  1549.           term.write(xStr)
  1550.           term.setTextColor(colors.lightGray)
  1551.           term.setCursorPos(14, 15)
  1552.           local newX = tonumber(read(nil, { xStr }, 9))
  1553.           if newX and newX ~= addressBook[currentGate].loc.x then
  1554.             addressBook[currentGate].loc.x = newX
  1555.             xStr = tostring(newX)
  1556.             gateChange = true
  1557.           end
  1558.           term.setTextColor(colors.lightGray)
  1559.           term.setCursorPos(14, 15)
  1560.           term.write(xStr .. string.rep(" ", 9 - #xStr))
  1561.         elseif x > 27 and x < 28 + #yStr then
  1562.           term.setTextColor(colors.gray)
  1563.           term.setCursorPos(28, 15)
  1564.           term.write(yStr)
  1565.           term.setTextColor(colors.lightGray)
  1566.           term.setCursorPos(28, 15)
  1567.           local newY = tonumber(read(nil, { yStr }, 9))
  1568.           if newY and newY ~= addressBook[currentGate].loc.y then
  1569.             addressBook[currentGate].loc.y = newY
  1570.             yStr = tostring(newY)
  1571.             gateChange = true
  1572.           end
  1573.           term.setTextColor(colors.lightGray)
  1574.           term.setCursorPos(28, 15)
  1575.           term.write(yStr .. string.rep(" ", 9 - #yStr))
  1576.         elseif x > 41 and x < 42 + #zStr then
  1577.           term.setTextColor(colors.gray)
  1578.           term.setCursorPos(42, 15)
  1579.           term.write(zStr)
  1580.           term.setTextColor(colors.lightGray)
  1581.           term.setCursorPos(42, 15)
  1582.           local newZ = tonumber(read(nil, { zStr }, 9))
  1583.           if newZ and newZ ~= addressBook[currentGate].loc.z then
  1584.             addressBook[currentGate].loc.z = newZ
  1585.             zStr = tostring(newZ)
  1586.             gateChange = true
  1587.           end
  1588.           term.setTextColor(colors.lightGray)
  1589.           term.setCursorPos(42, 15)
  1590.           term.write(zStr .. string.rep(" ", 9 - #zStr))
  1591.         end
  1592.       end
  1593.       if gateChange then drawGateFooter() end
  1594.     elseif event == "mouse_scroll" and ((data == 1 and currentGate < abCount) or (data == -1 and currentGate > 1)) then
  1595.       currentGate = currentGate + data
  1596.       drawGateData()
  1597.     end
  1598.   end
  1599. end
  1600.  
  1601. local function inputMainChangePage()
  1602.   term.setBackgroundColor(colors.black)
  1603.   term.setTextColor(colors.white)
  1604.   term.setCursorPos(31, termY - 2)
  1605.   local newPage = tonumber(read(nil, nil, 4))
  1606.   gatePage = newPage or gatePage
  1607.   gatePage = math.floor(math.max(1, math.min(gatePage, gatePages)))
  1608.   screen = "main"
  1609.   paintutils.drawFilledBox(29, termY - 3, 37, termY - 1, colors.black) --# clear pop-up
  1610.   changePage()
  1611. end
  1612.  
  1613. local function inputMainScreen()
  1614.   local event, data, x, y, continue
  1615.   while true do
  1616.     event, data, x, y = os.pullEvent()
  1617.     if event == "mouse_click" then
  1618.       continue = true
  1619.       if x > termX - 3 and y < 4 and data == 1 then --# 'X' (quit)
  1620.         if gateChange then
  1621.           screen = "exodus"
  1622.           drawPopUp("exodus")
  1623.           return
  1624.         end
  1625.         kernelState = false
  1626.         return
  1627.       elseif x > 27 and x < 38 and y == termY and gatePages > 1 then
  1628.         local pageStr, pagesStr = tostring(gatePage), tostring(gatePages)
  1629.         flashChoice((math.ceil(termX / 2) - (#pageStr + #pagesStr + 4) / 2) + 7, termY, colors.gray, colors.lightGray, pageStr .. " of " .. pagesStr)
  1630.         screen = "gatePage"
  1631.         drawPopUp("page")
  1632.         return
  1633.       end
  1634.       local xPos, yPos = 14, 5
  1635.       local magicNumber = ((gatePage - 1) * 20) + gatePage
  1636.       for i = magicNumber, math.min(abCount, gatePage * 21) do
  1637.         if x >= xPos and x <= xPos + 11 and y == yPos then
  1638.           if data == 1 then                  --# dial address
  1639.             if addressBook[i].addr == thisGate then
  1640.               continue = false
  1641.               break                          --# thisGate - don't dial
  1642.             else
  1643.               if gateStatus == "Idle" then
  1644.                 flashDial(addressBook[i].name, xPos, yPos, i)
  1645.                 dialOut(addressBook[i].addr) --# dial out
  1646.                 continue = false
  1647.                 break
  1648.               elseif lcGate and (gateStatus == "Dialing" or gateStatus == "Paused") and gateTarget == addressBook[i].addr then
  1649.                 flashDial(addressBook[i].name, xPos, yPos, i)
  1650.                 pauseDial()                  --# pause/resume dialing
  1651.                 drawAddressList()
  1652.                 continue = false
  1653.                 break
  1654.               end
  1655.             end
  1656.           elseif data == 2 then              --# view/edit address
  1657.             flashDial(addressBook[i].name, xPos, yPos, i)
  1658.             screen = "gate"
  1659.             currentGate = i
  1660.             drawGateLabels()
  1661.             return
  1662.           elseif data == 3 and abCount > 1 then --# delete address if there is more than one entry in the address book
  1663.             flashDial(addressBook[i].name, xPos, yPos, i)
  1664.             table.remove(addressBook, i)
  1665.             abCount = abCount - 1
  1666.             gatePages = math.ceil(abCount / 21)
  1667.             gatePage = math.min(gatePage, gatePages)
  1668.             if gatePage == gatePages then clearArea("data") end
  1669.             drawAddressList()
  1670.             drawFooter()
  1671.             drawAddressButton()
  1672.             gateChange = true
  1673.             continue = false
  1674.             break
  1675.           end
  1676.         else
  1677.           yPos = yPos + 2
  1678.           if yPos >= termY - 1 then xPos = xPos + 13 yPos = 5 end
  1679.         end
  1680.       end
  1681.       if continue then
  1682.         local modeSwitch = false
  1683.         for _, click in pairs(guiElements.mainButtons) do    --# process buttons
  1684.           modeSwitch = click.processEvent(event, data, x, y) --# carry out action for clicked button
  1685.           if modeSwitch then return end
  1686.         end
  1687.         for _, click in pairs(guiElements.mainFooter) do
  1688.           click.processEvent(event, data, x, y)
  1689.         end
  1690.       end
  1691.     elseif event == "mouse_scroll" and ((data == 1 and gatePage < gatePages) or (data == -1 and gatePage > 1)) then
  1692.       gatePage = gatePage + data
  1693.       changePage()
  1694.     elseif event == "key" then --# pageUp, pageDn, Home, End, F1
  1695.       if (data == keys.pageUp and gatePage > 1) or (data == keys.pageDown and gatePage < gatePages) then
  1696.         gatePage = data == keys.pageUp and gatePage - 1 or gatePage + 1
  1697.         changePage()
  1698.       elseif (data == keys.home and gatePage > 1) or (data == keys["end"] and gatePage < gatePages) then
  1699.         gatePage = data == keys.home and 1 or gatePages
  1700.         changePage()
  1701.       elseif data == keys.f1 then
  1702.         screen = "help"
  1703.         drawHelpScreen()
  1704.         return
  1705.       end
  1706.     end
  1707.   end
  1708. end
  1709.  
  1710. do
  1711.   local screens = {
  1712.     main = function() inputMainScreen() end;
  1713.     gate = function() inputViewGate() end;
  1714.     gatePage = function() inputMainChangePage() end;
  1715.     rating = function() inputGateRating() end;
  1716.     manage = function() inputAddressBookMgt() end;
  1717.     logs = function() inputLogScreen() end;
  1718.     logPage = function() inputLogChangePage() end;
  1719.     clearLogs = function() inputClearLogs() end;
  1720.     help = function() inputHelp() end;
  1721.     exodus = function() inputExodus() end;
  1722.   }
  1723.  
  1724.   userInput = function()
  1725.     repeat
  1726.       if screens[screen] then screens[screen]() end
  1727.     until not kernelState
  1728.   end
  1729. end
  1730.  
  1731. local function dataPoller()
  1732.   local _, timer, newFuel, update
  1733.   while true do
  1734.     _, timer = os.pullEvent("timer")
  1735.     if timer == updateTimer then
  1736.       newFuel = getFuelLevel()
  1737.       update = newFuel ~= fuelPercent
  1738.       fuelPercent = newFuel
  1739.       if update and (screen == "main" or screen == "gatePage" or screen == "manage" or screen == "exodus") then drawDHDStatus() end
  1740.       if gateStatus == "Connected" and not incomingAddress then updateTimer = os.startTimer(5) end
  1741.     end
  1742.   end
  1743. end
  1744.  
  1745. local function errorMessage(id)
  1746.   clearScreen()
  1747.   term.setCursorPos(2, 2)
  1748.   if id == "color" then
  1749.     write("This program requires an advanced computer")
  1750.     term.setCursorPos(1, 5)
  1751.   elseif id == "size" then
  1752.     write("This program requires a 51x18 or 51x19 screen")
  1753.     term.setCursorPos(1, 5)
  1754.   elseif id == "gate" then
  1755.     term.setTextColor(colors.red)
  1756.     term.write("Unable to locate a stargate")
  1757.     term.setTextColor(colors.white)
  1758.     term.setCursorPos(2, 4)
  1759.     term.write("Please connect a stargate/adapter directly")
  1760.     term.setCursorPos(2, 5)
  1761.     term.write("or via modem and network cables")
  1762.     term.setCursorPos(1, 8)
  1763.   end
  1764. end
  1765.  
  1766. if not term.isColor() or pocket or turtle then return errorMessage("color") end
  1767. if not os.getComputerLabel() then os.setComputerLabel("ccDHD.cc#" .. tostring(os.getComputerID())) end
  1768. if termX ~= 51 or termY < 18 or termY > 19 then return errorMessage("size") end
  1769. gate = peripheral.find("stargate")
  1770. if not gate then
  1771.   lcGate = true
  1772.   gate = peripheral.find("StargateBase")
  1773.   if gate and not gate.isValid() then gate = nil end
  1774. end
  1775. if not gate then return errorMessage("gate") end
  1776. if lcGate then
  1777.   local irisStates = {
  1778.     OPEN = "Open";
  1779.     CLOSED = "Closed";
  1780.     OPENING = "Closed";
  1781.     CLOSING = "Open";
  1782.     NONE = "Open";
  1783.   }
  1784.   thisGate = gate.getStargateAddressString()
  1785.   thisGate = thisGate ~= "default address" and thisGate or "!!ERROR!!"
  1786.   local chevs = gate.getActivatedChevrons()
  1787.   local glyphs = gate.getActivatedGlyphs()
  1788.   if glyphs and chevs > 0 then
  1789.     gateStatus = chevs == 9 and "Connected" or "Dialing"
  1790.     gateTarget = chevs == 9 and glyphs or glyphs .. string.rep("?", 9 - chevs)
  1791.     chevronNumber = chevs
  1792.   else
  1793.     gateStatus = "Idle"
  1794.     gateTarget = "No Target"
  1795.   end
  1796.   irisState = irisStates[gate.getIrisState()] == "Closed"
  1797. else
  1798.   thisGate = gate.localAddress()
  1799.   local gateState, callDirection
  1800.   gateState, chevronNumber, callDirection = gate.stargateState()
  1801.   if gateState == "Offline" then return errorMessage("gate") end
  1802.   local irisStates = {
  1803.     Offline = "Open",
  1804.     Open = "Open",
  1805.     Opening = "Closed",
  1806.     Closed = "Closed",
  1807.     Closing = "Open",
  1808.   }
  1809.   irisState = irisStates[gate.irisState()] == "Closed"
  1810.   gateStatus = sgStates[gateState] or "Unknown"
  1811.   if gateStatus == "Dialing" or gateStatus == "Connected" then
  1812.     gateTarget = gate.remoteAddress()
  1813.     if callDirection == "Incoming" then
  1814.       incomingAddress = gateTarget
  1815.       if gateStatus == "Dialing" then gateStatus = "Incoming" end
  1816.     end
  1817.   end
  1818. end
  1819. fuelPercent = getFuelLevel()
  1820. if fs.exists(gateData) then
  1821.   ingestData(gateData)
  1822. else
  1823.   if not fs.exists("/data") then fs.makeDir("/data") end
  1824.   addressBook[1].name = thisGate
  1825.   addressBook[1].addr = thisGate
  1826.   saveData(gateData)
  1827. end
  1828. screen = "main"
  1829. kernelState = true
  1830. guiElements = {
  1831.   mainButtons = {
  1832.     newButton(1, 9, 11, 1, "I r i s  ", colors.lightBlue, colors.white, function() toggleIris() drawIrisStatus() return false end, "Iris", 1);
  1833.     newButton(1, 11, 11, 1, "END Call ", colors.orange, colors.black, function() if gateStatus == "Dialing" or gateStatus == "Paused" or gateStatus == "Connected" or gateStatus == "Incoming" then flashChoice(1, 11, colors.orange, colors.black, " END Call  ") hangUp() drawDHDStatus() end return false end, "EndCall", 1);
  1834.     newButton(1, 13, 11, 1, "Addr Book", colors.blue, colors.white, function() flashChoice(1, 13, colors.blue, gateChange and colors.lime or colors.white, " Addr Book ") screen = "manage" drawPopUp("abManagement") return true end, "AddrBook", 1);
  1835.     newButton(1, 13, 11, 1, "Addr Book", colors.blue, colors.white, function() if gateChange then flashChoice(1, 13, colors.blue, colors.white, " Addr Book ") saveData(gateData) end return false end, "SaveAB", 2);
  1836.     newButton(1, 15, 11, 1, "New Gate ", colors.green, colors.white, function() flashChoice(1, 15, colors.green, colors.white, " New Gate  ") addNewAddress() return true end, "NewGate", 1);
  1837.     newButton(1, 15, 11, 1, "New Gate ", colors.green, colors.white, function() flashChoice(1, 15, colors.green, colors.white, " New Gate  ") addNewAddress(nil, true) return false end, "NewGateFast", 3);
  1838.     newButton(1, 17, 11, 1, "Logs     ", colors.yellow, colors.gray, function() flashChoice(1, 17, colors.yellow, colors.gray, " Logs      ") screen = "logs" ingestData("logs") clearArea("lower", colors.white) drawLogScreen() return true end, "Logs", 1);
  1839.     newButton(1, termY, 11, 1, "[F1] Help", colors.gray, colors.lightGray, function() screen = "help" drawHelpScreen() return true end, "Help", 1);
  1840.   },
  1841.   mainFooter = {
  1842.     newButton(22, termY, 2, 1, "<<", colors.gray, colors.lightGray, function() if gatePage > 1 then gatePage = 1 changePage() end return false end, "firstPage", 1);
  1843.     newButton(25, termY, 1, 1, "<", colors.gray, colors.lightGray, function() if gatePage > 1 then gatePage = gatePage - 1 changePage() end return false end, "pageMinus", 1);
  1844.     newButton(40, termY, 1, 1, ">", colors.gray, colors.lightGray, function() if gatePage < gatePages then gatePage = gatePage + 1 changePage() end return false end, "pagePlus", 1);
  1845.     newButton(42, termY, 2, 1, ">>", colors.gray, colors.lightGray, function() if gatePage < gatePages then gatePage = gatePages changePage() end return false end, "lastPage", 1);
  1846.   },
  1847.   logButtons = {
  1848.     newButton(termX - 7, 5, 7, 1, "CLEAR", colors.lightGray, colors.black, function() flashChoice(termX - 7, 5, colors.lightGray, colors.black, " CLEAR ") screen = "clearLogs" drawPopUp("clearLogs") return true end, "logPageClear", 1);
  1849.     newButton(14, termY, 2, 1, "<<", colors.gray, colors.lightGray, function() if logPage > 1 then logPage = 1 drawLogData() end return false end, "logPageHome", 1);
  1850.     newButton(17, termY, 1, 1, "<", colors.gray, colors.lightGray, function() if logPage > 1 then logPage = logPage - 1 drawLogData() end return false end, "logPageMinus", 1);
  1851.     newButton(34, termY, 1, 1, ">", colors.gray, colors.lightGray, function() if logPage < logPages then logPage = logPage + 1 drawLogData() end return false end, "logPagePlus", 1);
  1852.     newButton(36, termY, 2, 1, ">>", colors.gray, colors.lightGray, function() if logPage < logPages then logPage = logPages drawLogData() end return false end, "logPageEnd", 1);
  1853.   },
  1854.   addrBookMgt = {
  1855.     newButton(17, 12, 8, 1, "Import", colors.yellow, colors.black, function() flashChoice(17, 12, colors.yellow, colors.black, " Import ") mergeData() if gateChange then saveData(gateData) drawAddressList() drawFooter() end drawAddressButton() drawPopUp("abManagement") return false end, "ABImport", 1);
  1856.     newButton(26, 12, 8, 1, "Export", colors.orange, colors.black, function() flashChoice(26, 12, colors.orange, colors.black, " Export ") if not fs.exists("/disk/data") then fs.makeDir("/disk/data") end saveData("/disk/data/DHDgates") drawPopUp("abManagement") return false end, "ABExport", 1);
  1857.     newButton(17, 14, 8, 1, "Load", colors.lightBlue, colors.white, function() flashChoice(17, 14, colors.lightBlue, colors.white, "  Load  ") ingestData(gateData) clearArea("data") drawAddressList() drawFooter() drawAddressButton() drawPopUp("abManagement") return false end, "ABLoad", 1);
  1858.     newButton(26, 14, 8, 1, "Save", colors.green, colors.white, function() flashChoice(26, 14, colors.green, colors.white, "  Save  ") saveData(gateData) drawAddressButton() drawPopUp("abManagement") return false end, "ABSave", 1);
  1859.     newButton(22, 16, 7, 1, "Close", colors.red, colors.white, function() flashChoice(22, 16, colors.red, colors.white, " Close ") screen = "main" paintutils.drawFilledBox(15, 10, 35, 17, colors.black) drawAddressList() return true end, "ABClose", 1);
  1860.   },
  1861. }
  1862. clearScreen()
  1863. drawHeader()
  1864. drawMainScreen()
  1865. if gateStatus == "Connected" and not incomingAddress then updateTimer = os.startTimer(5) end
  1866. repeat
  1867.   if lcGate then
  1868.     parallel.waitForAny(userInput, dataPoller, incomingCall, lcChevronEncoder, lcGateMonitor, lcIrisMonitor)
  1869.   else
  1870.     parallel.waitForAny(userInput, dataPoller, incomingCall, sgChevronEncoder, sgChevronUnencoded, sgGateMonitor, sgIrisMonitor)
  1871.   end
  1872. until not kernelState
  1873. clearScreen()
  1874. term.setTextColor(colors.white)
  1875. term.setCursorPos(1, 1)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement