Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local serialization = require("serialization")
- local filesystem = require("filesystem")
- local component = require("component")
- local computer = require("computer")
- local event = require("event")
- local side = require("sides")
- local term = require("term")
- local gpu = component.gpu
- local modem
- local alphabet = {"Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliet", "Kilo", "Lima", "Mike", "November", "Oscar", "Papa", "Quebec", "Romeo", "Sierra", "Tango", "Uniform", "Victor", "Whiskey", "X-ray", "Yankee", "Zulu"}
- local w, h = term.getViewport()
- local halfh = math.floor(h/2)
- local coords = {}
- coords.x = {}
- coords.x.start = {}
- coords.x.start.circle = 2
- coords.x.start.buttons = 2
- coords.x.start.log = h
- coords.x.start.console = 4
- coords.x.stop = {}
- coords.x.stop.circle = h-2
- coords.x.stop.buttons = halfh-2
- coords.x.stop.log = w-1
- coords.x.stop.console = w-1
- coords.x.target = 0.0
- coords.y = {}
- coords.y.start = {}
- coords.y.start.circle = 2
- coords.y.start.buttons = halfh+2
- coords.y.start.log = 2
- coords.y.start.console = h-1
- coords.y.stop = {}
- coords.y.stop.circle = h
- coords.y.stop.buttons = h-3
- coords.y.stop.log = h-3
- coords.y.stop.console = h-1
- coords.y.target = 0.0
- local logBuffer = {}
- local logIndex = 0
- local inputCommand = ""
- local sanitizer -- used to "clean" input
- local frequency = 1
- local radius = 0.0
- local bckColor = 0x000000
- local forColor = 0x00FF00
- local configPath = "home/config.txt"
- local configFile
- local countdown = 10
- local countdownIndex = 0
- local countdownDo = false
- local broadcastDo = true
- local launchCommit = false
- local launchConfirm = false
- local launchCommence = false
- local checkList = false
- local allReady = true
- local waitAll = true
- local canLaunch = false
- local frequency_old = frequency
- local eventRes = {}
- local keywords = {}
- local buttons = {}
- local data = {}
- local pads = {}
- local pads_coords = {}
- local matrixCircle = {}
- -- circle subsequence
- local function circleSub(mat, xc, yc, x, y)
- mat[xc+x][yc+y] = -1
- mat[xc-x][yc+y] = -1
- mat[xc+x][yc-y] = -1
- mat[xc-x][yc-y] = -1
- mat[xc+y][yc+x] = -1
- mat[xc-y][yc+x] = -1
- mat[xc+y][yc-x] = -1
- mat[xc-y][yc-x] = -1
- return
- end
- -- draws a cicle starting at (posX, posY), with radius rad (Breseham's algorithm)
- local function circle(posX, posY, rad)
- local matrix = {}
- local diameter = 2*rad+1
- for i=1,diameter,1 do
- matrix[i] = {}
- for k=1,diameter,1 do
- matrix[i][k] = 0
- end
- end
- local X = 0
- local Y = rad
- local D = 3 - 2 * rad
- circleSub(matrix, posX+rad-1, posY+rad-1, X, Y)
- while (Y >= X) do
- if (D > 0) then
- Y = Y - 1
- D = D + 4 * (X - Y) + 10
- else
- D = D + 4 * X + 6
- end
- X = X + 1
- circleSub(matrix, posX+rad-1, posY+rad-1, X, Y)
- end
- return matrix
- end
- -- actually draws the circle
- local function circleDraw(matrix, posX, posY, rad)
- local diameter = 2*rad+1
- term.setCursor(posX, posY)
- for i=1,diameter,1 do
- for k=1,diameter,1 do
- if (matrix[i][k] == -1) then
- term.write("@")
- elseif (matrix[i][k] == 1) then
- term.write("X")
- else
- term.write(" ")
- end
- term.write(" ")
- end
- term.setCursor(posX, posY+i)
- end
- return
- end
- -- random target distribution calculation
- local function bomb(r)
- local x, y
- local mx, my
- local dm = #matrixCircle
- pads_coords = {}
- for i=1,dm,1 do
- for k=1,dm,1 do
- if (matrixCircle[i][k] == 1) then
- matrixCircle[i][k] = 0
- end
- end
- end
- for i=1,#pads,1 do
- pads_coords[i] = {}
- if (r > 0.0) then
- repeat
- x = math.random(2*r)
- y = math.random(2*r)
- mx = math.floor(x/(2*r+1)*dm)
- my = math.floor(y/(2*r+1)*dm)
- mx = (mx > 0) and mx or 1
- my = (my > 0) and my or 1
- until ((x-r-1)^2 + (y-r-1)^2 < r^2-1 and matrixCircle[my][mx] ~= 1 and matrixCircle[my][mx] ~= -1)
- matrixCircle[my][mx] = 1
- else
- x = 0.0
- y = 0.0
- matrixCircle[math.floor(dm/2)][math.floor(dm/2)] = 1
- end
- pads_coords[i][1] = x - r + coords.x.target
- pads_coords[i][2] = y - r + coords.y.target
- end
- return
- end
- -- returns an array of the specified peripheral
- local function multiPer(peripheral)
- local C = 1
- local peripheralArray = {}
- for add, n in component.list(peripheral) do
- peripheralArray[C] = component.proxy(add)
- C = C + 1
- end
- return peripheralArray
- end
- -- clears the log
- local function clearLog()
- term.setCursor(coords.x.start.log, coords.y.start.log)
- for i=coords.y.start.log,coords.y.stop.log,1 do
- term.setCursor(coords.x.start.log, i)
- for k=coords.x.start.log,coords.x.stop.log,1 do
- term.write(" ")
- end
- end
- term.setCursor(coords.x.start.log, coords.y.start.log)
- return 0
- end
- -- prints a single line in custom format to fit in the log space
- local function logPrint(line, index)
- local x, y
- local char
- local localIndex = index
- term.setCursor(coords.x.start.log, coords.y.start.log + localIndex)
- x, y = term.getCursor()
- if (y > coords.y.stop.log) then
- localIndex = clearLog()
- end
- term.write(line:sub(1,1)=="1" and "> " or "< ")
- for k=2,#line,1 do
- x, y = term.getCursor()
- char = line:sub(k,k)
- if (y > coords.y.stop.log) then
- localIndex = clearLog()
- end
- if (x > coords.x.stop.log or char == "\n") then
- localIndex = localIndex + 1
- term.setCursor(coords.x.start.log, coords.y.start.log + localIndex)
- end
- if (char ~= "\n") then
- term.write(char)
- end
- end
- localIndex = localIndex + 1
- return localIndex
- end
- -- segments a string containing spaces
- local function segment(line, value)
- local k_word = {}
- local k = 1
- local char = ""
- if (line == nil) then
- return nil
- end
- for i=1,#line,1 do
- char = line:sub(i,i)
- if (char == value or char == nil) then
- k = k + 1
- else
- if (k_word[k] == nil) then k_word[k] = "" end
- k_word[k] = k_word[k] .. char
- end
- end
- return k_word
- end
- -- saves data in the configPath path
- local function saveData()
- data = {frequency, broadcastDo, coords.x.target, coords.y.target, radius, countdown, countdownDo, forColor, bckColor, waitAll}
- configFile = filesystem.open(configPath, "w")
- if (configFile == nil) then
- table.insert(logBuffer, 1 .. "Save failed")
- return false
- end
- configFile:write(serialization.serialize(data))
- configFile:close()
- table.insert(logBuffer, 1 .. "Save succeded")
- return true
- end
- -- updates data and pads
- local function update(matrix, posX, posY, rad)
- pads = multiPer("ntm_launch_pad")
- table.insert(logBuffer, 1 .. "Detected " .. #pads .. " launch pads")
- bomb(radius)
- circleDraw(matrix, posX, posY, rad)
- return
- end
- -- allocates a new button entry
- local function newButton(buffer, name1, name2, sx, sy, ex, ey)
- table.insert(buffer, {label1=name1, label2=name2, posX=sx, posY=sy, endX=ex, endY=ey})
- return
- end
- -- checks the value with the button, then inverts it
- local function checkButton(button, value, posX, posY)
- if (posX >= button.posX and posX <= button.endX and posY >= button.posY and posY <= button.endY) then
- value = not value
- end
- gpu.setBackground(forColor)
- gpu.setForeground(bckColor)
- term.setCursor(button.posX, button.posY)
- term.write(("%9s"):format(value and button.label2 or button.label1))
- gpu.setBackground(bckColor)
- gpu.setForeground(forColor)
- return value
- end
- -- unpacks the data vector
- local function unpack()
- frequency = data[1]
- broadcastDo = data[2]
- coords.x.target = data[3]
- coords.y.target = data[4]
- radius = data[5]
- countdown = data[6]
- countdownDo = data[7]
- forColor = data[8]
- bckColor = data[9]
- waitAll = data[10]
- return
- end
- data = {frequency, broadcastDo, coords.x.target, coords.y.target, radius, countdown, countdownDo, forColor, bckColor, waitAll}
- table.insert(logBuffer, 1 .. "Checking for " .. configPath .. "...")
- if (filesystem.exists(configPath)) then
- table.insert(logBuffer, 1 .. "Success. Reading...")
- configFile = filesystem.open(configPath, "r")
- data = serialization.unserialize(configFile:read(filesystem.size(configPath)*8))
- else
- table.insert(logBuffer, 1 .. "Failed. Creating new...")
- configFile = filesystem.open(configPath, "w")
- configFile:write(serialization.serialize(data))
- end
- configFile:close()
- unpack()
- frequency_old = frequency
- table.insert(logBuffer, 1 .. "Config complete")
- newButton(buttons, "LISTEN", "BROADCAST", coords.x.start.buttons+2, coords.y.start.buttons+1, coords.x.start.buttons+10, coords.y.start.buttons+1)
- newButton(buttons, "INSTANT", "COUNTDOWN", coords.x.start.buttons+2, coords.y.start.buttons+3, coords.x.start.buttons+10, coords.y.start.buttons+3)
- newButton(buttons, "FIREALL", "WAITALL", coords.x.start.buttons+2, coords.y.start.buttons+5, coords.x.start.buttons+10, coords.y.start.buttons+5)
- modem = multiPer("modem")
- modem = modem[1]
- term.clear()
- checkButton(buttons[1], broadcastDo, 0, 0)
- checkButton(buttons[2], countdownDo, 0, 0)
- checkButton(buttons[3], waitAll, 0, 0)
- gpu.setBackground(bckColor)
- gpu.setForeground(forColor)
- for i=2,w-1,1 do
- term.setCursor(i, 1)
- term.write("-")
- term.setCursor(i, h-2)
- term.write("-")
- term.setCursor(i, h)
- term.write("-")
- end
- term.setCursor(2, halfh+1)
- for i=2,h-2,1 do
- term.write("-")
- end
- for i=2,h-3,1 do
- term.setCursor(1, i)
- term.write("|")
- term.setCursor(h-1, i)
- term.write("|")
- term.setCursor(w, i)
- term.write("|")
- end
- term.setCursor(1, 1)
- term.write("+")
- term.setCursor(h-1, 1)
- term.write("+")
- term.setCursor(w, 1)
- term.write("+")
- term.setCursor(1, h-2)
- term.write("+")
- term.setCursor(h-1, h-2)
- term.write("+")
- term.setCursor(w, h-2)
- term.write("+")
- term.setCursor(1, halfh+1)
- term.write("+")
- term.setCursor(h-1, halfh+1)
- term.write("+")
- term.setCursor(1, h)
- term.write("+")
- term.setCursor(w, h)
- term.write("+")
- term.setCursor(1, h-1)
- term.write("| $")
- term.setCursor(w, h-1)
- term.write("|")
- matrixCircle = circle(coords.x.start.circle, coords.y.start.circle, math.floor(halfh/2)-1)
- update(matrixCircle, coords.x.start.circle, coords.y.start.circle, math.floor(halfh/2)-1)
- while (true) do
- if (not launchCommence) then
- eventRes = {term.pull(1)}
- term.setCursor(coords.x.start.console, coords.y.start.console)
- for i=coords.x.start.console,coords.x.stop.console,1 do
- term.write(" ")
- end
- if (modem ~= nil) then
- if (not modem.isOpen(frequency) or frequency ~= frequency_old) then
- modem.close(frequency_old)
- modem.open(frequency)
- table.insert(logBuffer, 1 .. "Changed freq " .. frequency_old .. " -> " .. frequency)
- frequency_old = frequency
- end
- end
- if (eventRes[1] == "key_down") then
- if (eventRes[3] == 0) then
- -- input reset
- inputCommand = ""
- elseif (eventRes[3] == 8) then
- -- backspace
- inputCommand = inputCommand:sub(1, -2)
- elseif (eventRes[3] >= 32 and eventRes[3] <= 126) then
- -- characters input
- inputCommand = inputCommand .. string.char(eventRes[3])
- elseif (eventRes[3] == 13) then
- -- return
- table.insert(logBuffer, 0 .. inputCommand)
- inputCommand = string.lower(inputCommand)
- keywords = segment(inputCommand, " ")
- inputCommand = ""
- if (keywords[1] == "get") then
- if (keywords[2] == "frequency") then
- table.insert(logBuffer, 1 .. frequency)
- elseif (keywords[2] == "radius") then
- table.insert(logBuffer, 1 .. radius)
- elseif (keywords[2] == "countdown") then
- table.insert(logBuffer, 1 .. countdown)
- elseif (keywords[2] == "coords") then
- table.insert(logBuffer, 1 .. "X: " .. coords.x.target .. ", Z: " .. coords.y.target)
- else
- table.insert(logBuffer, 1 .. "Usage: get [-a]\n -a: specified value\n- frequency\n- radius\n- countdown\n- coords")
- end
- elseif (keywords[1] == "set") then
- sanitizer = tonumber(keywords[3])
- if (keywords[2] == "frequency") then
- if (sanitizer ~= nil) then
- frequency = ((sanitizer > 65535) and 65535 or ((sanitizer < 1) and 1 or sanitizer))
- table.insert(logBuffer, 1 .. "Changed frequency to " .. frequency)
- else
- table.insert(logBuffer, 1 .. "Must be a number 1-65535")
- end
- elseif (keywords[2] == "radius") then
- if (sanitizer ~= nil) then
- radius = (sanitizer < 0.0) and 0.0 or sanitizer
- table.insert(logBuffer, 1 .. "Changed bombing radius to " .. radius)
- else
- table.insert(logBuffer, 1 .. "Must be a number >= 0.0")
- end
- elseif (keywords[2] == "countdown") then
- if (sanitizer ~= nil) then
- countdown = (sanitizer < 0) and 0 or math.floor(sanitizer)
- table.insert(logBuffer, 1 .. "Changed the countdown to " .. countdown)
- else
- table.insert(logBuffer, 1 .. "Must be a number > 0")
- end
- else
- sanitizer = segment(keywords[2], "/")
- if (type(sanitizer) == "table") then
- sanitizer[1] = tonumber(sanitizer[1])
- sanitizer[2] = tonumber(sanitizer[2])
- if (sanitizer[1] ~= nil and sanitizer[2] ~= nil) then
- coords.x.target = sanitizer[1]
- coords.y.target = sanitizer[2]
- table.insert(logBuffer, 1 .. "Changed target coords to X: " .. coords.x.target .. ", Z: " .. coords.y.target)
- else
- table.insert(logBuffer, 1 .. "Must use the format X/Z")
- end
- else
- table.insert(logBuffer, 1 .. "Usage: set [-a] [-n]\n -a: specified value\n- frequency\n- radius\n- countdown\n -n: number 0-65535")
- end
- end
- elseif (keywords[1] == "clear") then
- logIndex = clearLog()
- elseif (keywords[1] == "update") then
- update(matrixCircle, coords.x.start.circle, coords.y.start.circle, math.floor(halfh/2)-1)
- elseif (keywords[1] == "help") then
- table.insert(logBuffer, 1 .. "Avaiable commands: (for more info about a specific command type as shown)\nset: sets a value to the specified field\nget: returns a value from the specified field\nupdate: checks for avaiable pads and returns the total\nsave: saves the current configuration\nreboot: reboots the system\ncommit: commits to launch\nexport: exports config on all avaiable systems (must be on the same frequency)")
- elseif (keywords[1] == "save") then
- saveData()
- elseif (keywords[1] == "export") then
- if (modem == nil) then
- table.insert(logBuffer, 1 .. "No network card detected")
- else
- if (broadcastDo) then
- saveData()
- data[2] = false
- modem.broadcast(frequency, serialization.serialize(data))
- else
- table.insert(logBuffer, 1 .. "Program in LISTEN mode. Change to BROADCAST")
- end
- end
- elseif (keywords[1] == "reboot") then
- table.insert(logBuffer, 1 .. "Rebooting...")
- computer.shutdown(true)
- elseif (keywords[1] == "commit" or launchCommit) then
- if (launchCommit) then
- launchCommit = false
- if (keywords[1] == "y") then
- launchConfirm = true
- checkList = false
- table.insert(logBuffer, 1 .. "Committed")
- table.insert(logBuffer, 1 .. "STARTING LAUNCH SEQUENCE")
- else
- launchConfirm = false
- table.insert(logBuffer, 1 .. "Aborted")
- end
- else
- launchCommit = true
- computer.beep(1000, 1)
- table.insert(logBuffer, 1 .. "Commit to launch " .. #pads .. " base(s)? (Y/n)")
- end
- else
- table.insert(logBuffer, 1 .. "Unknown command. type 'help' for help")
- end
- end
- elseif (eventRes[1] == "touch") then
- broadcastDo = checkButton(buttons[1], broadcastDo, eventRes[3], eventRes[4])
- countdownDo = checkButton(buttons[2], countdownDo, eventRes[3], eventRes[4])
- waitAll = checkButton(buttons[3], waitAll, eventRes[3], eventRes[4])
- elseif (eventRes[1] == "modem_message" and (not broadcastDo)) then
- if (type(eventRes[6]) == "string") then
- sanitizer = serialization.unserialize(eventRes[6])
- if (type(sanitizer) == "table") then
- if (#sanitizer == 10) then
- data = sanitizer
- unpack()
- saveData()
- table.insert(logBuffer, 1 .. "Data from " .. eventRes[3] .. " at " .. math.floor(eventRes[5]*100.0)/100.0 .. "m away")
- elseif (#sanitizer == 2) then
- if (type(sanitizer[1]) == "boolean" and type(sanitizer[2]) == "boolean") then
- launchConfirm = sanitizer[1]
- checkList = sanitizer[2]
- end
- end
- end
- end
- end
- else
- os.sleep(0.5)
- end
- if (launchConfirm and not checkList) then
- if (modem ~= nil and broadcastDo) then
- modem.broadcast(frequency, serialization.serialize({true, false}))
- end
- checkList = true
- table.insert(logBuffer, 1 .. "CHECKING PADS STATUS...")
- allReady = true
- update(matrixCircle, coords.x.start.circle, coords.y.start.circle, math.floor(halfh/2)-1)
- for i=1,#pads,1 do
- canLaunch = pads[i].canLaunch()
- if (not canLaunch) then
- allReady = false
- end
- table.insert(logBuffer, 1 .. ("%02d"):format(i) .. " -> " .. (canLaunch and "Ready" or "Pending"))
- end
- if (allReady or not waitAll) then
- table.insert(logBuffer, 1 .. "Commence")
- launchCommence = true
- countdownIndex = 0
- else
- table.insert(logBuffer, 1 .. "Not all ready")
- launchCommence = false
- end
- end
- if (launchCommence) then
- if (countdownDo and countdownIndex < countdown) then
- countdownIndex = countdownIndex + 1
- table.insert(logBuffer, 1 .. countdown - countdownIndex)
- computer.beep(2000, 0.5)
- else
- launchCommence = false
- for i=1,#pads,1 do
- pads[i].launch(pads_coords[i][1], pads_coords[i][2])
- end
- table.insert(logBuffer, 1 .. "LAUNCH!")
- end
- end
- term.setCursor(coords.x.start.console, coords.y.start.console)
- term.write(inputCommand)
- if (#logBuffer > 0) then
- for i,line in ipairs(logBuffer) do
- logIndex = logPrint(line, logIndex)
- end
- logBuffer = {}
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment