Advertisement
samuelask

Omega Site

Apr 12th, 2025 (edited)
527
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 32.71 KB | None | 0 0
  1. -- Script for Bunker Control
  2. local component = require("component")
  3. local thread = require("thread")
  4. local event = require("event")
  5. local term = require("term")
  6. local serialization = require("serialization")
  7. local sides = require("sides")
  8. local colors = require("colors")
  9. local os = require("os")
  10. local computer = require("computer")
  11. local math = require("math")
  12. local modem = component.modem
  13. local gpu = component.gpu
  14. local sg = component.stargate
  15. local zpm = component.zpmhub
  16. local tunnel = component.tunnel
  17. local reds = component.redstone
  18. local data = component.data
  19. local count = component.countdown
  20. local keyboard = require("keyboard")
  21. local lastMessageId = nil
  22. local shared = { status = nil, stop = false }
  23. local seenStrings = {}
  24. local seenStrings2 = {}
  25. IDC = "1337"
  26. user = ""
  27. Local_Name = "Omega Site"
  28. Short_Name = "[Omega]"
  29. running = true
  30. tesla = false
  31. lights = false
  32. iris_status = false
  33. charging = false
  34. lockdown = true
  35. dialingyes = false
  36. gotthecode = false
  37. zpms_up = false
  38. lockDetected = false
  39. valid = false
  40. lab = "1439d23f-eecc-4cc6-86f6-adb828a402aa"
  41. local mcolors = {
  42.   [0] = "White", "Orange", "Magenta", "Light Blue", "Yellow", "Lime",
  43.   "Pink", "Gray", "Light Gray", "Cyan", "Purple", "Blue", "Brown",
  44.   "Green", "Red", "Black"
  45. }
  46. power = false
  47. -- Configuration
  48. beacon = true
  49. modem.open(123)
  50. local screenWidth, screenHeight = gpu.getResolution()
  51. local function loadGlyphTable(path)
  52.   local chunk, err = loadfile(path)
  53.   if not chunk then
  54.     error("Failed to load glyph file: " .. err)
  55.   end
  56.  
  57.   local success, result = pcall(chunk)
  58.   if not success then
  59.     error("Error running glyph file: " .. result)
  60.   end
  61.  
  62.   if type(result) ~= "table" then
  63.     error("Glyph file did not return a table!")
  64.   end
  65.  
  66.   return result
  67. end
  68. local GlyphImages = loadGlyphTable("/home/glyphsUN.lua")
  69. local default_messages = {
  70.   {
  71.     from = "Omega Site",
  72.     shrt = "[Omega]",
  73.     user = "Unknown entity",
  74.     to = Local_Name,
  75.     header = "ComE",
  76.     message = "",
  77.     timestamp = 7
  78.   },
  79.   {
  80.     from = "Alpha Site",
  81.     shrt = "[ALERT]",
  82.     user = "Rosewood, Head of Security",
  83.     to = Local_Name,
  84.     header = "LOCKDOWN INITIATED",
  85.     message = "",
  86.     timestamp = 6
  87.   },
  88.   {
  89.     from = "Laboratory 1",
  90.     shrt = "[Lab 1]",
  91.     user = "Jack, Shipment officer",
  92.     to = Local_Name,
  93.     header = "Missing Shipment",
  94.     message = "",
  95.     timestamp = 5
  96.   },
  97.   {
  98.     from = "Laboratory 2",
  99.     shrt = "[Lab 2]",
  100.     user = "Doctor Greywall",
  101.     to = Local_Name,
  102.     header = "Lab results",
  103.     message = "",
  104.     timestamp = 4
  105.   },
  106.   {
  107.     from = "Omega Site",
  108.     shrt = "[Omega]",
  109.     user = "Bryan Haddoc",
  110.     to = Local_Name,
  111.     header = "Iris installed",
  112.     message = "",
  113.     timestamp = 3
  114.   },
  115.   {
  116.     from = "High Command",
  117.     shrt = "[High cmd]",
  118.     user = "Secretary General Coldwell",
  119.     to = Local_Name,
  120.     header = "Increased activity READ ME",
  121.     message = "",
  122.     timestamp = 2
  123.   },
  124.   {
  125.     from = "Laboratory 2",
  126.     shrt = "[Lab 2]",
  127.     user = "Doctor Greywall",
  128.     to = Local_Name,
  129.     header = "Eggsacks",
  130.     message = "",
  131.     timestamp = 1
  132.   },
  133. }
  134.  
  135. function drawMessageBox(message_data)
  136.   local screenWidth, _ = gpu.getResolution()
  137.  
  138.   -- Prepare message lines
  139.   local lines = {
  140.     "New message received from " .. message_data.from,
  141.     "Subject: " .. message_data.shrt .. " " .. message_data.header,
  142.     "Message: " .. message_data.message,
  143.     "User: " .. message_data.user
  144.   }
  145.  
  146.   -- Determine max line length
  147.   local maxLength = 0
  148.   for _, line in ipairs(lines) do
  149.     if #line > maxLength then
  150.       maxLength = #line
  151.     end
  152.   end
  153.  
  154.   -- Box styling
  155.   local padding = 2
  156.   local boxWidth = maxLength + padding * 2
  157.   local boxHeight = #lines + 2 -- top + bottom border
  158.  
  159.   local startX = screenWidth - boxWidth + 1
  160.   local startY = 1
  161.  
  162.   -- Draw top border
  163.   gpu.set(startX, startY, "┌" .. string.rep("─", boxWidth - 2) .. "┐")
  164.  
  165.   -- Draw box sides and empty space
  166.   for i = 1, boxHeight - 2 do
  167.     gpu.set(startX, startY + i, "│" .. string.rep(" ", boxWidth - 2) .. "│")
  168.   end
  169.  
  170.   -- Draw bottom border
  171.   gpu.set(startX, startY + boxHeight - 1, "└" .. string.rep("─", boxWidth - 2) .. "┘")
  172.  
  173.   -- Write text inside the box
  174.   for i, line in ipairs(lines) do
  175.     gpu.set(startX + padding, startY + i, line)
  176.   end
  177. end
  178.  
  179. -- UI Drawing Function
  180. local function draw_ui()
  181.     term.clear()
  182.     print("Omega Site Control")
  183.     print("[SPACE] Toggle Emergency Beacon [" .. (beacon and "ON" or "OFF") .. "]")
  184.     print("[D] Toggle Perimeter Defense[" .. (tesla and "ON" or "OFF") .. "]")
  185.     print("[L] Toggle Perimeter Lights[" .. (lights and "ON" or "OFF") .. "]")
  186.     print("[U] Update orders from high command")
  187.     print("[M] Messages")
  188.     print("[C] Charge Gate")
  189.     print("[Q] Dialing Menu")
  190. end
  191.  
  192. -- UI Drawing Function messages
  193. local function draw_ui_messages()
  194.     term.clear()
  195.     print("Messages")
  196.     print("[R] Read messages")
  197.     print("[S] Send message")
  198.     print("[E] Exit")
  199. end
  200.  
  201. -- UI Drawing Function dialing
  202. local function draw_ui_dialing()
  203.     term.clear()
  204.     print("Dialing Menu")
  205.     if not dialingyes then
  206.         if lockdown then
  207.             print("[WARNING] Lockdown in effect, iris will turn on when dialing out!")
  208.             print("[L] Disable Lockdown")
  209.         elseif not lockdown then
  210.             print("[L] Enable Lockdown")
  211.         end
  212.         print("\n[1] Dial Epsilon Site")
  213.         print("[2] Dial Laboratory 1")
  214.         print("[3] Dial Laboratory 2")
  215.         print("[4] Dial Alpha Site")
  216.         print("[5] Dial High Command")
  217.         print("[E] Exit")
  218.     elseif dialingyes then
  219.         print("Dialing: ") -- Fix later, in the middle of the screen and an abort function
  220.         print("[E] Exit")
  221.     end
  222. end
  223. local function is_floppy_valid()
  224.  
  225.   local privFile = io.open("/rPrivate.key", "r")
  226.   if not privFile then
  227.     print("[✘] Missing /rPrivate.key!")
  228.     return false
  229.   end
  230.  
  231.   local privSerialized = privFile:read("*a")
  232.   privFile:close()
  233.  
  234.   local rPrivate = data.deserializeKey(serialization.unserialize(privSerialized), "ec-private")
  235.  
  236.   for address in component.list("disk_drive") do
  237.     local drive = component.proxy(address)
  238.  
  239.     local ok, present = pcall(function() return not drive.isEmpty() end)
  240.     if not ok then
  241.       print("[DEBUG] Error calling isEmpty(): " .. tostring(present))
  242.     elseif present then
  243.  
  244.       local fsAddress = drive.media()
  245.       if fsAddress then
  246.         local fs = component.proxy(fsAddress)
  247.  
  248.         if fs.exists("/key.dat") then
  249.           local f = fs.open("/key.dat", "r")
  250.           if f then
  251.             local content = fs.read(f, math.huge)
  252.             fs.close(f)
  253.  
  254.             if content then
  255.               local packet = serialization.unserialize(content)
  256.               local sPublic = data.deserializeKey(serialization.unserialize(packet.header.sPublic), "ec-public")
  257.               local iv = packet.header.iv
  258.               local encryptedData = packet.data
  259.  
  260.               local sharedKey = data.md5(data.ecdh(rPrivate, sPublic))
  261.               local decrypted = data.decrypt(encryptedData, sharedKey, iv)
  262.  
  263.               if decrypted then
  264.                 local result = serialization.unserialize(decrypted)
  265.                 if result == "supersecretpassword" then
  266.                   return true
  267.                 else
  268.                 end
  269.               else
  270.               end
  271.             else
  272.             end
  273.           else
  274.           end
  275.         else
  276.         end
  277.       else
  278.       end
  279.     else
  280.     end
  281.   end
  282.   return false
  283. end
  284. local function sendscore(score, player)
  285.     modem.send(lab, 123, "score", score, player)
  286. end
  287. local function place_cmd_chest()
  288.     modem.send(lab, 123, "place")
  289. end
  290. local function check_scoreboard_cmd(scoreboard)
  291.     if type(user) == "string" and not seenStrings[user] then
  292.         modem.send(lab, 123, "check_scoreboard", user, scoreboard)
  293.         seenStrings[user] = true
  294.     end
  295. end
  296. local function decryptkey()
  297.   term.clear()
  298.   print("HIGH CMD SECURITY KEY NEEDED")
  299.   print("Place the Floppy Disk Security key inside the Disk Drive")
  300.   print("Press [E] to exit.")
  301.   local animY = 6 -- Row to draw the blinking text
  302.   while true do
  303.     local visible = math.floor(os.clock() * 1.5) % 2 == 0
  304.     local msg = "Waiting for Security Key..."
  305.     local x = math.floor((screenWidth - #msg) / 2)
  306.     if visible then
  307.         gpu.set(x, animY, msg)
  308.     else
  309.         gpu.set(x, animY, string.rep(" ", #msg))
  310.     end
  311.     if is_floppy_valid() then
  312.       print("\n[✔] Valid Security Key Detected.")
  313.       lockdown = false
  314.       sendscore("not_lockdown", user)
  315.       os.sleep(1)
  316.       return true
  317.     end
  318.  
  319.     local ev, _, _, code = event.pull(0.1, "key_down")
  320.     if ev == "key_down" then
  321.       print("You pressed key ID:", code)
  322.       if code == keyboard.keys.e then
  323.         print("[✖] Authorization Aborted.")
  324.         return false
  325.       end
  326.     end
  327.     os.sleep(0.1)
  328.   end
  329. end
  330. local function sendsound(sound, pos)
  331.     modem.send(lab, 123, "sound", sound, pos)
  332. end
  333. local function power_lab()
  334.     if power then
  335.     labmessage = "power_on"
  336.     modem.send(lab, 123, labmessage)
  337.     elseif not power then
  338.     labmessage = "power_off"
  339.     modem.send(lab, 123, labmessage)
  340.     end
  341. end
  342. local function generator(_, _, _, _, redvalue, mcolor)
  343.     if mcolors[mcolor] == "Brown" and redvalue < 1 then
  344.         power = false
  345.         power_lab()
  346.         if tesla then
  347.             tesla = not tesla
  348.             reds.setBundledOutput(sides.bottom, colors.yellow, 0)
  349.         end
  350.         if lights then
  351.             lights = not lights
  352.             reds.setBundledOutput(sides.bottom, colors.green, 255)
  353.         end
  354.         sg.disengageGate()
  355.         computer.shutdown(true)
  356.     elseif mcolors[mcolor] == "Brown" and redvalue > 0 then
  357.         power = true
  358.         power_lab()
  359.     end
  360. end
  361. -- Function to send commands
  362. local function send_command(address, value, header)
  363.  
  364.   local data = {
  365.     from = Local_Name,
  366.     shrt = Short_Name,
  367.     user = user,
  368.     to = address,
  369.     header = header,
  370.     message = value,
  371.     timestamp = os.time()
  372.   }
  373.     local serialized_data = serialization.serialize(data)
  374.     tunnel.send(serialized_data)
  375. end
  376. local function handle_incomingwh()
  377.     if not iris_status then
  378.         iris_status = true
  379.         if not charging then
  380.             zpm.toggleSlots()
  381.             zpms_up = true
  382.         end
  383.         sg.toggleIris()
  384.         print("INCOMING WORMHOLE!.")
  385.     end
  386.     epsil = false
  387. end
  388. local function handle_idccode(_, _, _, code)
  389.     if code == IDC then
  390.         if not gotthecode then
  391.             if iris_status then
  392.                 iris_status = false
  393.                 sg.toggleIris()
  394.                 zpm.toggleSlots()
  395.                 gotthecode = true
  396.                 zpms_up = false
  397.             end
  398.         end
  399.     end
  400. end
  401. local function handle_closingwh()
  402.     if iris_status then
  403.         sg.toggleIris()
  404.         iris_status = false
  405.         if zpms_up then
  406.             zpm.toggleSlots()
  407.             zpms_up = false
  408.         end
  409.     end
  410.     gotthecode = false
  411. end
  412. function drawLine(x1, y1, x2, y2, char)
  413.     local dx = math.abs(x2 - x1)
  414.     local dy = math.abs(y2 - y1)
  415.     local sx = x1 < x2 and 1 or -1
  416.     local sy = y1 < y2 and 1 or -1
  417.     local err = dx - dy
  418.  
  419.     while true do
  420.         gpu.set(x1, y1, char)
  421.         if x1 == x2 and y1 == y2 then break end
  422.         local e2 = 2 * err
  423.         if e2 > -dy then err = err - dy; x1 = x1 + sx end
  424.         if e2 < dx then err = err + dx; y1 = y1 + sy end
  425.     end
  426. end
  427. -- Thread function
  428. local function gateStatusThread()
  429.   while not shared.stop do
  430.     shared.status = { sg.getGateStatus() }
  431.     os.sleep(1)  -- Adjust frequency as needed
  432.   end
  433. end
  434. local function clocktimer()
  435.     local screenWidth, screenHeight = gpu.getResolution()
  436.     local centerX = math.floor(screenWidth / 2)
  437.     local centerY = math.floor(screenHeight / 2)
  438.     local radius = 10
  439.     local aspectX = 2
  440.     shared.stop = false
  441.     local statusThread = thread.create(gateStatusThread)
  442.    
  443.     -- Clear and draw clock face
  444.     term.clear()
  445.     for angle = 0, 2 * math.pi, math.pi / 120 do
  446.         local x = centerX + math.floor(math.cos(angle) * radius * aspectX)
  447.         local y = centerY + math.floor(math.sin(angle) * radius)
  448.         gpu.set(x, y, "●")
  449.     end
  450.  
  451.     -- Draw center
  452.     gpu.set(centerX, centerY, "•")
  453.    
  454.     -- Prepare digital clock line area
  455.     local digitalY = centerY + radius + 2
  456.     gpu.fill(1, digitalY, screenWidth, 1, " ")
  457.    
  458.     local startTime = computer.uptime()
  459.     local duration = 60
  460.     count.setCountdown(1200)
  461.     -- Animate hand and digital countdown
  462.     while true do
  463.         local gateStatus = shared.status and shared.status[1]
  464.         local elapsed = computer.uptime() - startTime
  465.         local timeLeft = duration - elapsed
  466.         if timeLeft <= 0 then break end
  467.         if gateStatus ~= "unstable" and gateStatus ~= "open" then count.setCountdown(0) break end
  468.        
  469.         local angle = -(2 * math.pi / duration) * timeLeft - math.pi / 2
  470.  
  471.         local endX = centerX + math.floor(math.cos(angle) * (radius - 1) * aspectX)
  472.         local endY = centerY + math.floor(math.sin(angle) * (radius - 1))
  473.  
  474.         -- Draw hand
  475.         drawLine(centerX, centerY, endX, endY, "*")
  476.  
  477.         -- Format and print digital timer
  478.         local ms = math.floor((timeLeft % 1) * 100)
  479.         local sec = math.floor(timeLeft) % 60
  480.         local timeStr = string.format("%02d:%02d", sec, ms)
  481.         local textX = math.floor((screenWidth - #timeStr) / 2)
  482.  
  483.         gpu.set(textX, digitalY, string.rep(" ", #timeStr))
  484.         gpu.set(textX, digitalY, timeStr)
  485.  
  486.         os.sleep(0.05)
  487.  
  488.         -- Clear hand
  489.         drawLine(centerX, centerY, endX, endY, " ")
  490.     end
  491.     -- Shutdown gate
  492.    
  493.     sg.disengageGate()
  494.     epsil = false
  495.     -- Clean up the thread
  496.    
  497.     shared.stop = true
  498.     statusThread:kill()
  499.     valid = false
  500. end
  501. local function dialout()
  502.     if lockdown then
  503.         if sg.getIrisState() == "OPENED" or "OPENING" then
  504.             iris_status = true
  505.             if not zpms_up then
  506.                 zpm.toggleSlots()
  507.                 zpms_up = true
  508.             end
  509.             sg.toggleIris()
  510.         end
  511.     end
  512.     if not lockdown then
  513.         if epsil then
  514.             sendscore("dialed_puzzle", user)
  515.         end
  516.     end
  517.     valid = true
  518.     sg.engageGate()
  519.     clocktimer()
  520.     os.sleep(1.5)
  521. end
  522. local function checkiris()
  523.     local irisstate = sg.getIrisState()
  524.     local gateStatus, openState, _, _ = sg.getGateStatus()
  525.     if (irisstate == "CLOSED" or irisstate == "CLOSING") and irisstate ~= "OPEN" and irisstate ~= "OPENING" then
  526.         if gateStatus == "open" then
  527.             iris_status = true
  528.         elseif gateStatus == "idle" then
  529.             sg.toggleIris()
  530.             zpm.toggleSlots()  
  531.         end
  532.     elseif (irisstate == "OPENED" or irisstate == "OPENING") and irisstate ~= "CLOSED" and irisstate ~= "CLOSING" then
  533.  
  534.     end
  535. end
  536. function errormessage(ER)
  537.   local screenWidth, screenHeight = gpu.getResolution()
  538.  
  539.   -- Split multiline message into table of lines
  540.   local lines = {}
  541.   for line in ER:gmatch("[^\n]+") do
  542.     table.insert(lines, line)
  543.   end
  544.    
  545.   -- Box styling
  546.   local padding = 2
  547.   local maxLength = 0
  548.   for _, line in ipairs(lines) do
  549.     if #line > maxLength then maxLength = #line end
  550.   end
  551.  
  552.   local boxWidth = maxLength + padding * 2
  553.   local boxHeight = #lines + padding * 2
  554.  
  555.   -- Centered position (same logic as drawChargingProgress)
  556.   local boxX = math.floor((screenWidth - boxWidth) / 2)
  557.   local boxY = math.floor((screenHeight - boxHeight) / 2)
  558.  
  559.   -- Draw top border
  560.   gpu.set(boxX, boxY, "┌" .. string.rep("─", boxWidth - 2) .. "┐")
  561.  
  562.   -- Draw [ERROR] centered at the top
  563.   local errorLabel = "[ERROR]"
  564.   local errorX = boxX + math.floor((boxWidth - #errorLabel) / 2)
  565.   gpu.set(errorX, boxY + 1, errorLabel)
  566.  
  567.   -- Draw sides and content
  568.   for i = 1, boxHeight - 2 do
  569.     local lineY = boxY + i + 1
  570.     local lineContent = lines[i - 1] or ""
  571.     local line = "│" .. string.rep(" ", padding) .. lineContent .. string.rep(" ", boxWidth - #lineContent - padding * 2) .. "│"
  572.     gpu.set(boxX, lineY, line)
  573.   end
  574.  
  575.   -- Draw bottom border
  576.   gpu.set(boxX, boxY + boxHeight - 1, "└" .. string.rep("─", boxWidth - 2) .. "┘")
  577.  
  578.   -- Optional: Pause before clearing
  579.   os.sleep(5)
  580.  
  581.   -- Clear box area (restore background)
  582.   for i = 0, boxHeight - 1 do
  583.     gpu.set(boxX, boxY + i, string.rep(" ", boxWidth))
  584.   end
  585. end
  586.  
  587. local function handle_closefail(_, _, reason)
  588.     print("SG Failed: ".. reason)
  589. end
  590.  
  591. local function handle_failure(_, _, reason)
  592.     print("SG Failed: ".. reason)
  593. end
  594.  
  595. local function handle_unidialer()
  596.     if not valid then
  597.         if sg.getIrisState() == "OPENED" or "OPENING" then
  598.             iris_status = true
  599.             if not zpms_up then
  600.                 zpm.toggleSlots()
  601.                 zpms_up = true
  602.             end
  603.         sg.toggleIris()
  604.         end
  605.         ::unistop::
  606.         local status, failur, description = sg.disengageGate()
  607.         if not status then
  608.             goto unistop
  609.         else
  610.             ermessage = "Unauthorized dialing with Universe Dialer.\nStopping dialing..."
  611.             errormessage(ermessage)
  612.         end
  613.     end
  614. end
  615.    
  616. -- Internal draw counter
  617. local glyphSpacing = 14 -- horizontal space per glyph block
  618. local glyphStartX = 14   -- starting X position
  619. local glyphStartY = 10  -- starting Y position
  620.  
  621. local function displayglyph(name)
  622.   local glyphImage = GlyphImages[name]
  623.   if not glyphImage then
  624.     print("[!] Unknown glyph from GlyphImages: " .. tostring(glyphName))
  625.     print("[!] Glyph we are trying to print: " .. name)
  626.     return
  627.   end
  628.  
  629.   -- Calculate current glyph X position based on count
  630.   local xPos = glyphStartX + (glyphDrawCount * glyphSpacing)
  631.   local yPos = glyphStartY
  632.  
  633.   -- Draw each line of the glyph vertically
  634.   local lineOffset = 0
  635.   for line in glyphImage:gmatch("[^\r\n]+") do
  636.     gpu.set(xPos, yPos + lineOffset, line)
  637.     lineOffset = lineOffset + 1
  638.   end
  639.  
  640.   -- Move the cursor for the next glyph
  641.   glyphDrawCount = glyphDrawCount + 1
  642. end
  643. -- Function to engage a symbol and wait for confirmation
  644. local function engageSymbolAndWait(glyph, expectedSymbolCount)
  645.     local success = false
  646.     lockDetected = false
  647.    
  648.     -- Define the event handler function
  649.     local function onGlyphEngaged(_, _, _, symbolCount, lock, glyphName)
  650.         if glyphName == glyph and symbolCount == expectedSymbolCount then
  651.             displayglyph(glyphName)
  652.             success = true
  653.         end
  654.  
  655.         -- If this is the last glyph, confirm `lock` is true
  656.         if lock then
  657.             lockDetected = true
  658.         end
  659.     end
  660.  
  661.     -- Call `sg.engageSymbol` and wait for confirmation
  662.  
  663.     local result = sg.engageSymbol(glyph)
  664.    
  665.     if result ~= "stargate_spin" then
  666.         ermessage = "Failed to initiate glyph engagement: " .. glyph .. "\nError Details: " .. (result and result.stargate_failure or "No failure info")
  667.         errormessage(ermessage)
  668.         return false, false
  669.     end
  670.    
  671.     event.listen("stargate_spin_chevron_engaged", onGlyphEngaged)
  672.    
  673.     -- Wait for the event to confirm success
  674.     local timeout = 20 -- Timeout in seconds
  675.     local elapsed = 0
  676.     while not success and elapsed < timeout do
  677.         os.sleep(0.1)
  678.         elapsed = elapsed + 0.1
  679.     end
  680.  
  681.     -- Cleanup the event listener
  682.     event.ignore("stargate_spin_chevron_engaged", onGlyphEngaged)
  683.  
  684.     if not success then
  685.         ermessage = "Timeout waiting for glyph engagement: " .. glyph
  686.         errormessage(ermessage)
  687.         return false, false
  688.     elseif success then
  689.         return true, lockDetected
  690.     end
  691. end
  692. local function formatNumberWithDots(n)
  693.   local s = tostring(n)
  694.   local formatted = s:reverse():gsub("(%d%d%d)", "%1."):reverse()
  695.   if formatted:sub(1, 1) == "." then
  696.     formatted = formatted:sub(2)
  697.   end
  698.   return formatted
  699. end
  700. local function dialgate(address)
  701.     term.clear()
  702.     AddressBuffer = address
  703.     glyphDrawCount = 0
  704.     local requirement = sg.getEnergyRequiredToDial(table.unpack(AddressBuffer))
  705.     local stored_energy = sg.getEnergyStored()
  706.     local total_energy_required = requirement.open + (requirement.keepAlive * 1200)
  707.     if type(requirement) == "table" and requirement.canOpen == false then
  708.         ermessage = "NOT ENOUGH POWER TO OPEN: ".. formatNumberWithDots(total_energy_required).." RF NEEDED"
  709.         errormessage(ermessage)
  710.     elseif stored_energy > total_energy_required then
  711.             sendsound("jsg:universe_dialer_start_dial", "@a[x=636,y=71,z=498,dx=3,dy=2,dz=6]")
  712.             for i, glyph in ipairs(address) do
  713.                 local gateStatus = sg.getGateStatus()
  714.                 local success, lock = engageSymbolAndWait(glyph, i)
  715.                 if not success then
  716.                     return
  717.                 end
  718.                 if lock then
  719.                     dialout()
  720.                 end
  721.                 os.sleep(1)
  722.             end
  723.  
  724.     elseif stored_energy < total_energy_required then
  725.         ermessage = "NOT ENOUGH POWER TO OPEN: ".. formatNumberWithDots(total_energy_required).." RF NEEDED"
  726.         errormessage(ermessage)
  727.     end
  728. end
  729. -- Function to read messages
  730. local function read_message()
  731.   local path = "/messages.dat"
  732.   local file = io.open(path, "r")
  733.   if not file then
  734.     print("No messages found.")
  735.     os.sleep(2)
  736.     return
  737.   end
  738.  
  739.   local content = file:read("*a")
  740.   file:close()
  741.  
  742.   local messages = serialization.unserialize(content)
  743.  
  744.   if not messages or #messages == 0 then
  745.     print("Inbox empty.")
  746.     os.sleep(2)
  747.     return
  748.   end
  749.  
  750.   -- Sort by timestamp descending (newest first)
  751.   table.sort(messages, function(a, b)
  752.     return tonumber(a.timestamp) > tonumber(b.timestamp)
  753.   end)
  754.  
  755.   while true do
  756.     term.clear()
  757.     print("Inbox")
  758.     print("︱NEWEST︱")
  759.  
  760.     for i, msg in ipairs(messages) do
  761.       print(string.format("[%d] ︱ %s %s ︱", i, msg.shrt or "", msg.header or ""))
  762.     end
  763.  
  764.     print("︱OLDEST︱")
  765.     print("")
  766.     print("[E] Exit")
  767.     local answer = io.read()
  768.  
  769.     if answer == "e" or answer == "E" then
  770.       break
  771.     else
  772.       local index = tonumber(answer)
  773.       if index and messages[index] then
  774.         local msg = messages[index]
  775.         term.clear()
  776.         print(string.format("︱ %s %s ︱", msg.shrt or "", msg.header or ""))
  777.         print("")
  778.         print(msg.message or "")
  779.         print("")
  780.         print(msg.user or "Unknown sender")
  781.         print("")
  782.         print("[E] Exit")
  783.         local _ = io.read()
  784.       end
  785.     end
  786.   end
  787. end
  788.  
  789. -- Function to send messages
  790. local function send_message()
  791.     ::continue1::
  792.     term.clear()
  793.     print("Pick receiver")
  794.     print("[1] Epsilon Site")
  795.     print("[2] Laboratory 1")
  796.     print("[3] Laboratory 2")
  797.     print("[4] High Command")
  798.     print("[E] Exit")
  799.     local answer = io.read()
  800.     if answer == "1" then
  801.         send_to = "Epsilon Site"
  802.         print("Epsilon Site Confirmed, please type the subject of your message and press enter")
  803.         print("")
  804.         local header = io.read()
  805.         term.clear()
  806.         print("Subject: " .. header)
  807.         print("")
  808.         print("Please type your message")
  809.         print("")
  810.         local message = io.read()
  811.         term.clear()
  812.         print("From: " .. Local_Name)
  813.         print("Subject: " .. header)
  814.         print("Message: " .. message)
  815.         print("")
  816.         print("Do you want to send this message to ".. send_to .."?")
  817.         io.write("Y/n: ")
  818.         local answer = io.read()
  819.         if answer == "Y" or answer == "y" then
  820.             send_command(send_to, message, header)
  821.         end
  822.         term.clear()
  823.         goto continue1
  824.     elseif answer == "2" then
  825.         send_to = "Laboratory 1"
  826.         term.clear()
  827.         print("Laboratory 1 Confirmed, please type the subject of your message and press enter")
  828.         print("")
  829.         local header = io.read()
  830.         term.clear()
  831.         print("Subject: " .. header)
  832.         print("")
  833.         print("Please type your message")
  834.         print("")
  835.         local message = io.read()
  836.         term.clear()
  837.         print("From: " .. Local_Name)
  838.         print("Subject: " .. header)
  839.         print("Message: " .. message)
  840.         print("")
  841.         print("Do you want to send this message to ".. send_to .."?")
  842.         io.write("Y/n: ")
  843.         local answer = io.read()
  844.         if answer == "Y" or answer == "y" then
  845.             send_command(send_to, message, header)
  846.         end
  847.         term.clear()
  848.         goto continue1
  849.     elseif answer == "3" then
  850.         send_to = "Laboratory 2"
  851.         term.clear()
  852.         print("Laboratory 2 Confirmed, please type the subject of your message and press enter")
  853.         print("")
  854.         local header = io.read()
  855.         term.clear()
  856.         print("Subject: " .. header)
  857.         print("")
  858.         print("Please type your message")
  859.         print("")
  860.         local message = io.read()
  861.         term.clear()
  862.         print("From: " .. Local_Name)
  863.         print("Subject: " .. header)
  864.         print("Message: " .. message)
  865.         print("")
  866.         print("Do you want to send this message to ".. send_to .."?")
  867.         io.write("Y/n: ")
  868.         local answer = io.read()
  869.         if answer == "Y" or answer == "y" then
  870.             send_command(send_to, message, header)
  871.         end
  872.         term.clear()
  873.         goto continue1
  874.     elseif answer == "4" then
  875.         send_to = "High Command"
  876.         term.clear()
  877.         print("High Command Confirmed, please type the subject of your message and press enter.")
  878.         print("")
  879.         local header = io.read()
  880.         term.clear()
  881.         print("Subject: " .. header)
  882.         print("")
  883.         print("Please type your message")
  884.         print("")
  885.         local message = io.read()
  886.         term.clear()
  887.         print("From: " .. Local_Name)
  888.         print("Subject: " .. header)
  889.         print("Message: " .. message)
  890.         print("")
  891.         print("Do you want to send this message to ".. send_to .."?")
  892.         io.write("Y/n: ")
  893.         local answer = io.read()
  894.         if answer == "Y" or answer == "y" then
  895.             send_command(send_to, message, header)
  896.         end
  897.         term.clear()
  898.         goto continue1
  899.     elseif answer == "e" or answer == "E" then
  900.    
  901.     else
  902.         goto continue1
  903.     end
  904. end
  905. local function init_message_file()
  906.   local path = "/messages.dat"
  907.   local f = io.open(path, "r")
  908.   if not f then
  909.     -- Save default messages to file
  910.     local file = io.open(path, "w")
  911.     if file then
  912.       file:write(serialization.serialize(default_messages))
  913.       file:close()
  914.     end
  915.   else
  916.     f:close()
  917.   end
  918. end
  919. local function save_message(message_data)
  920.   local filepath = "/messages.dat"
  921.   local messages = {}
  922.  
  923.   -- Load existing messages if file exists
  924.   local file = io.open(filepath, "r")
  925.   if file then
  926.     local content = file:read("*a")
  927.     file:close()
  928.     if content and #content > 0 then
  929.       messages = serialization.unserialize(content) or {}
  930.     end
  931.   end
  932.  
  933.   -- Add new message
  934.   table.insert(messages, message_data)
  935.  
  936.   -- Sort by timestamp
  937.   table.sort(messages, function(a, b)
  938.     return a.timestamp < b.timestamp
  939.   end)
  940.  
  941.   -- Save updated messages back to file
  942.   file = io.open(filepath, "w")
  943.   if file then
  944.     file:write(serialization.serialize(messages))
  945.     file:close()
  946.   end
  947. end
  948. local function getpowerstatus()
  949.     if reds.getBundledInput(sides.bottom, colors.brown) > 0 then
  950.         power = true
  951.         return "power_on"
  952.     else
  953.         power = false
  954.         return "power_off"
  955.     end
  956. end
  957. local function handle_modem_message(_, _, from, port, _, message, data, playern, scoreboar)
  958.     if port == 123 or port == 0 then
  959.         if message == "error" then
  960.             print("")
  961.             print("[ERROR] " .. data)
  962.         elseif message == "sent_message" then
  963.             print("")
  964.             print("Sent message to " .. data)
  965.         elseif message == "new_message" then
  966.             local message_data = serialization.unserialize(data)
  967.             if message_data.timestamp ~= lastMessageId then
  968.                 lastMessageId = message_data.timestamp
  969.                 drawMessageBox(message_data)
  970.                 save_message(message_data)
  971.             end
  972.         elseif message == "code" then
  973.             print("[Code] " .. data)
  974.             if data == IDC then
  975.                 if iris_status then
  976.                     iris_status = false
  977.                     sg.toggleIris()
  978.                     zpm.toggleSlots()
  979.                 end
  980.             end
  981.         elseif message == "lockdown" then
  982.             lockdown = true
  983.         elseif message == "getpowerstatus" then
  984.             local status = getpowerstatus()
  985.             modem.send(lab, 123, "powerstatus", status)
  986.         elseif message == "scoreboard_checked" then
  987.             if type(playern) == "string" and not seenStrings2[playern] then
  988.             seenStrings2[playern] = true
  989.                 if data < 1 then
  990.                     if scoreboar == "power" then
  991.                         place_cmd_chest()
  992.                     end
  993.                 end
  994.             end
  995.         end
  996.     end
  997. end
  998. function drawChargingProgress()
  999.   local screenWidth, screenHeight = gpu.getResolution()
  1000.   local label = "Charging Gate..."
  1001.   local labelX = math.floor((screenWidth - #label) / 2) + 1
  1002.   local labelY = math.floor(screenHeight / 2)
  1003.  
  1004.   -- Progress bar settings
  1005.   local barWidth = 30
  1006.   local barX = math.floor((screenWidth - barWidth) / 2) + 1
  1007.   local barY = labelY + 2
  1008.   local totalTime = 34 -- seconds
  1009.   local updateRate = 0.1 -- how often to update in seconds
  1010.   local steps = math.floor(totalTime / updateRate)
  1011.  
  1012.   -- Clear label and bar area
  1013.   for i = 0, 3 do
  1014.     gpu.set(1, labelY + i, string.rep(" ", screenWidth))
  1015.   end
  1016.  
  1017.   -- Draw label
  1018.   gpu.set(labelX, labelY, label)
  1019.  
  1020.   -- Progress bar loop
  1021.   for i = 0, steps do
  1022.  
  1023.     if iris_status then
  1024.     draw_ui()
  1025.     term.setCursor(1, barY + 2)
  1026.     print("[!] Charging aborted: Iris status triggered.")
  1027.     return end
  1028.  
  1029.     local filled = math.floor((i / steps) * barWidth)
  1030.     local empty = barWidth - filled
  1031.     gpu.set(barX, barY, "[" .. string.rep("█", filled) .. string.rep(" ", empty) .. "]")
  1032.     os.sleep(updateRate)
  1033.   end
  1034. end
  1035. local function chargegate()
  1036.     charging = true
  1037.     zpms_up = true
  1038.     zpm.toggleSlots()
  1039.     drawChargingProgress()
  1040.     if not iris_status then
  1041.         zpm.toggleSlots()
  1042.         zpms_up = false
  1043.     end
  1044.     charging = false
  1045. end
  1046. -- Keyboard Input Listener
  1047. local function key_listener(_, _, char, code, playerName)
  1048.     local key = string.char(char)
  1049.     user = playerName
  1050.     check_scoreboard_cmd("power")
  1051.     event.ignore("key_down", key_listener)
  1052.     if key == "u" then
  1053.             term.clear()
  1054.             print("Connecting to central network...")
  1055.             os.sleep(3)
  1056.             print("[ERROR] No connection")
  1057.             os.sleep(0.5)
  1058.             print("[ERROR] Diagnosing...")
  1059.             os.sleep(3)
  1060.             print("[✔] Receiver [ONLINE]")
  1061.             print("[✔] Transmitter [ONLINE]")
  1062.             print("[ERROR] High Command blocked or not responding")
  1063.             print("[⚠] Can not receive orders!")
  1064.             print("")
  1065.             print("Please follow standard Emergency Procedures")
  1066.             os.sleep(8)
  1067.             draw_ui()
  1068.     elseif key == "d" then
  1069.             if power then
  1070.                 if tesla then
  1071.                     tesla = not tesla
  1072.                     reds.setBundledOutput(sides.bottom, colors.yellow, 0)
  1073.                     draw_ui()
  1074.                     print("Turned Off Perimeter Defense")
  1075.                 elseif not tesla then
  1076.                     tesla = not tesla
  1077.                     reds.setBundledOutput(sides.bottom, colors.yellow, 255)
  1078.                     draw_ui()
  1079.                     print("Turned On Perimeter Defense")
  1080.                 end
  1081.             elseif not power then
  1082.                 print("[ERROR] ZPM Offline, Cannot turn on Perimeter Defense")
  1083.             end
  1084.     elseif key == "l" then
  1085.             if power then
  1086.                 if lights then
  1087.                     lights = not lights
  1088.                     reds.setBundledOutput(sides.bottom, colors.green, 255)
  1089.                     draw_ui()
  1090.                     print("Turned Off Perimeter Lights")
  1091.                 elseif not lights then
  1092.                     lights = not lights
  1093.                     reds.setBundledOutput(sides.bottom, colors.green, 0)
  1094.                     draw_ui()
  1095.                     print("Turned On Perimeter Lights")
  1096.                 end
  1097.             elseif not power then
  1098.                 print("[ERROR] ZPM Offline, Cannot turn on Perimeter Lights")
  1099.             end
  1100.     elseif key == "q" then
  1101.             if power then
  1102.                 ::continue3::
  1103.                 draw_ui_dialing()
  1104.                 local answer = io.read()
  1105.                 if answer == "l" or answer == "L" then
  1106.                     if lockdown then
  1107.                         decryptkey()
  1108.                     elseif not lockdown then
  1109.                         lockdown = true
  1110.                     end
  1111.                     goto continue3
  1112.                 elseif answer == "1" then
  1113.                     DialEpsilon = {"Glyph 24","Glyph 13","Glyph 30","Glyph 11","Glyph 6","Glyph 12","Glyph 2","Glyph 15","Glyph 17"}
  1114.                     epsil = true
  1115.                     dialgate(DialEpsilon)
  1116.                     goto continue3
  1117.                 elseif answer == "2" then
  1118.                     send_message()
  1119.                     goto continue3
  1120.                 elseif answer == "3" then
  1121.                     send_message()
  1122.                     goto continue3
  1123.                 elseif answer == "4" then
  1124.                     send_message()
  1125.                     goto continue3
  1126.                 elseif answer == "5" then
  1127.                     send_message()
  1128.                     goto continue3
  1129.                 elseif answer == "e" or answer == "E" then
  1130.                     draw_ui()
  1131.                 else
  1132.                     goto continue3
  1133.                 end    
  1134.             elseif not power then
  1135.                 print("[ERROR] ZPM Offline, Cannot dial")
  1136.             end            
  1137.     elseif code == 57 then
  1138.         if beacon then
  1139.             print("Confirm Beacon Shutdown")
  1140.             io.write("Y/n: ")
  1141.             local answer = io.read()
  1142.             if answer == "y" or answer == "Y" then
  1143.                 beacon = not beacon
  1144.                 print("Confirmed")
  1145.                 os.sleep(3)
  1146.                 draw_ui()
  1147.             else
  1148.                 print("Aborted!")
  1149.                 os.sleep(3)
  1150.                 draw_ui()
  1151.             end
  1152.         elseif not beacon then
  1153.             beacon = not beacon
  1154.             draw_ui()
  1155.             print("Emergency Beacon Online!")
  1156.            
  1157.         end
  1158.     elseif key == "m" then
  1159.         ::continue::
  1160.         draw_ui_messages()
  1161.         local answer = io.read()
  1162.         if answer == "r" or answer == "R" then
  1163.             read_message()
  1164.             goto continue
  1165.         elseif answer == "s" or answer == "S" then
  1166.             send_message()
  1167.             goto continue
  1168.         elseif answer == "e" or answer == "E" then
  1169.             draw_ui()
  1170.         else
  1171.             goto continue
  1172.         end
  1173.     elseif key == "c" then
  1174.         if power then
  1175.             if iris_status then
  1176.                 print("[!] Cannot Charge when Iris is up!.")
  1177.             elseif not iris_status then
  1178.                 chargegate()
  1179.                 draw_ui()
  1180.             end
  1181.         elseif not power then
  1182.             print("[ERROR] ZPM Offline, Cannot charge")
  1183.         end
  1184.     end
  1185.     event.listen("key_down", key_listener)
  1186. end
  1187. if reds.getBundledOutput(sides.bottom, colors.yellow) > 0 then
  1188.     tesla = true
  1189. end
  1190. if reds.getBundledOutput(sides.bottom, colors.green) < 1 then
  1191.     lights = true
  1192. end
  1193. init_message_file()
  1194. local status = getpowerstatus()
  1195. sg.engageGate()
  1196. checkiris()
  1197. draw_ui()
  1198. messages = event.listen("modem_message", handle_modem_message)
  1199. incomingwh = event.listen("stargate_incoming_wormhole", handle_incomingwh)
  1200. gotcode = event.listen("received_code", handle_idccode)
  1201. closingwh = event.listen("stargate_wormhole_closed_fully", handle_closingwh)
  1202. unidialer = event.listen("stargate_open", handle_unidialer)
  1203. failure = event.listen("stargate_failed", handle_failure)
  1204. closefail = event.listen("stargate_close", handle_closefail)
  1205. powergenerator = event.listen("redstone_changed", generator)
  1206. event.listen("key_down", key_listener)
  1207. while running do
  1208.     os.sleep(0.5)
  1209. end
  1210.  
  1211. os.sleep(1)
  1212. os.exit()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement