local luaPrint = print function isScreen(side, id) if wrap(side, id) then return true end return false end -- "Class" for handler for remote devices (via a wired modem) local RemoteHandler = {} RemoteHandler.new = function(side, id) local handler = {} handler.modem = peripheral.wrap(side) if handler.modem == nil then return end handler.side = side handler.id = id handler.name = "monitor_" .. id handler.isHandler = true handler.isRemoteHandler = true handler.clear = function() handler.modem.callRemote(handler.name, "clear") end handler.write = function(txt) return handler.modem.callRemote(handler.name, "write", txt) end handler.clearLine = function() handler.modem.callRemote(handler.name, "clearLine") end handler.getCursorPos = function() local a, b = handler.modem.callRemote(handler.name, "getCursorPos") return a, b end handler.getCursor = handler.getCursorPos handler.getCursorXPos = function() local a, b = handler.modem.callRemote(handler.name, "getCursorPos") return a end handler.getCursorX = handler.getCursorXPos handler.getX = handler.getCursorXPos handler.getCursorYPos = function() local a, b = handler.modem.callRemote(handler.name, "getCursorPos") return b end handler.getCursorY = handler.getCursorYPos handler.getY = handler.getCursorYPos handler.setCursorPos = function(a, b) handler.modem.callRemote(handler.name, "setCursorPos", a, b) end handler.setCursor = handler.setCursorPos handler.setCursorXPos = function(x) local a, b = handler.modem.callRemote(handler.name, "getCursorPos") handler.modem.callRemote(handler.name, "setCursorPos", x, b) end handler.setCursorX = handler.setCursorXPos handler.setX = handler.setCursorXPos handler.setCursorYPos = function(y) local a, b = handler.modem.callRemote(handler.name, "getCursorPos") handler.modem.callRemote(handler.name, "setCursorPos", a, y) end handler.setCursorY = handler.setCursorYPos handler.setY = handler.setCursorYPos handler.setCursorBlink = function(b) handler.modem.callRemote(handler.name, "setCursorBlink", b) end handler.setBlink = handler.setCursorBlink handler.isColor = function() return handler.modem.callRemote(handler.name, "isColor") end handler.getSize = function() local a, b = handler.modem.callRemote(handler.name, "getSize") return a, b end handler.getXSize = function() local a, b = handler.modem.callRemote(handler.name, "getSize") return a end handler.getYSize = function() local a, b = handler.modem.callRemote(handler.name, "getSize") return b end handler.scroll = function(n) handler.modem.callRemote(handler.name, "scroll", n) end handler.setTextScale = function(s) handler.modem.callRemote(handler.name, "setTextScale", s) end if handler.isColor() then handler.setTextColor = function(color) handler.modem.callRemote(handler.name, "setTextColor", color) end handler.setBackgroundColor = function(color) handler.modem.callRemote(handler.name, "setBackgroundColor", color) end handler.setBackColor = handler.setBackgroundColor handler.setColors = function(col1, col2) if col1 ~= nil then handler.modem.callRemote(handler.name, "setTextColor", col1) else handler.modem.callRemote(handler.name, "setTextColor", colours.white) end if col2 ~= nil then handler.modem.callRemote(handler.name, "setBackgroundColor", col2) else handler.modem.callRemote(handler.name, "setBackgroundColor", colours.black) end end end -- adding the additional API functions handler.reset = function() handler.modem.callRemote(handler.name, "clear") handler.modem.callRemote(handler.name, "setCursorPos", 1, 1) end handler.nextLine = function(scrolling) nextLine(handler.side, handler.id, scrolling) end handler.nextLines = function(n, scrolling) nextLines(n, handler.side, handler.id, scrolling) end handler.print = function(text, scrolling) return print(text, handler.side, handler.id, scrolling) end handler.println = function(text, scrolling) handler.print(text, handler.side, handler.id, scrolling) handler.nextLine(scrolling) return text end handler.left = function(text, scrolling) return left(text, handler.side, handler.id, scrolling) end return handler end -- "Class" for a normal handler (using this add methods that implements the function added by this API) local Handler = {} Handler.new = function(side) local handler = peripheral.wrap(side) if handler == nil then return end handler.side = side handler.isHandler = true handler.isRemoteHandler = false handler.getCursor = handler.getCursorPos handler.getCursorXPos = function() local a, b = handler.getCursorPos() return a end handler.getCursorX = handler.getCursorXPos handler.getX = handler.getCursorXPos handler.getCursorYPos = function() local a, b = handler.getCursorPos() return b end handler.getCursorY = handler.getCursorYPos handler.getY = handler.getCursorYPos handler.setCursor = handler.setCursorPos handler.setCursorXPos = function(x) local a, b = handler.getCursorPos() handler.setCursorPos(x, b) end handler.setCursorX = handler.setCursorXPos handler.setX = handler.setCursorXPos handler.setCursorYPos = function(y) local a, b = handler.getCursorPos() handler.setCursorPos(a, y) end handler.setCursorY = handler.setCursorYPos handler.setY = handler.setCursorYPos handler.getXSize = function() local a, b = handler.getSize() return a end handler.getYSize = function() local a, b = handler.getSize() return b end handler.setBlink = handler.setCursorBlink if handler.isColor() then handler.setBackColor = handler.setBackgroundColor handler.setColors = function(col1, col2) if col1 ~= nil then handler.setTextColor(col1) else handler.setTextColor(colours.white) end if col2 ~= nil then handler.setBackgroundColor(col2) else handler.setBackgroundColor(colours.black) end end end -- adding the additional API functions handler.reset = function() handler.clear() handler.setCursorPos(1, 1) end handler.nextLine = function(scrolling) nextLine(handler.side, nil, scrolling) end handler.nextLines = function(n, scrolling) nextLines(n, handler.side, nil, scrolling) end handler.print = function(text, scrolling) return print(text, handler.side, nil, scrolling) end handler.println = function(text, scrolling) handler.print(text, handler.side, nil, scrolling) handler.nextLine(scrolling) return text end handler.left = function(text, scrolling) return left(text, handler.side, nil, scrolling) end return handler end -- wrap around 'side', if 'side' is a modem, 'id' designate the number id of the monitor targetted (name structure is "monitor_id") function wrap(side, id) if side == nil then return term elseif peripheral.isPresent(side) then if peripheral.getType(side) == "monitor" then return Handler.new(side) elseif peripheral.getType(side) == "modem" and id ~= nil then local modem = peripheral.wrap(side) if modem.isPresentRemote and modem.isPresentRemote("monitor_" .. id) then return RemoteHandler.new(side, id) end end end luaPrint("wrap: invalid input").crash() end -------------------------------------------------------------------------------------------------------------------------- -------------------------------------------REDEFINITION-OF-STANDARD-FUNCTIONS--------------------------------------------- -------------------------------------------------------------------------------------------------------------------------- -- Clear the screen function clear(side, id) wrap(side, id).clear() end -- Clear the line at the cursor's position function clearLine(side, id) wrap(side, id).clearLine() end -- Write a text at the cursor's position function write (text, side, id) return wrap(side, id).write(text) end -- Get the cursor's position -- getCursorPos, getCursor -- getCursorXPos, getCursorX, getX -- getCursorYPos, getCursorY, getY function getCursorPos(side, id) local a, b = wrap(side, id).getCursorPos() return a, b end getCursor = getCursorPos function getCursorXPos(side, id) local a, b = getCursorPos(side, id) return a end getCursorX = getCursorXPos getX = getCursorXPos function getCursorYPos(side, id) local a, b = getCursorPos(side, id) return b end getCursorY = getCursorYPos getY = getCursorYPos -- Set the cursor's position -- setCursorPos, setCursor -- setCursorXPos, setCursorX -- setCursorYPos, setCursorY function setCursorPos(x, y, side, id) wrap(side, id).setCursorPos(x, y) end setCursor = setCursorPos function setCursorXPos(x, side, id) local handler = wrap(side, id) local a, b = handler.getCursorPos() handler.setCursorPos(x, b) end setCursorX = setCursorXPos setX = setCursorXPos function setCursorYPos(y, side, id) local handler = wrap(side, id) local a, b = handler.getCursorPos() handler.setCursorPos(a, y) end setCursorY = setCursorYPos setY = setCursorYPos -- Set weither or not the cursor is blinking -- setCursorBlink, setBlink function setCursorBlink(b, side, id) wrap(side, id).setCursorBlink(b) end setBlink = setCursorBlink -- true if advanced, false if not function isColor(side, id) return wrap(side, id).isColor() end -- Get the screen's side -- getSize -- getXSize -- getYSize function getSize(side, id) local a, b = wrap(side, id).getSize() return a, b end function getXSize(side, id) local a, b = wrap(side, id).getCursorPos() return a end function getYSize(side, id) local a, b = wrap(side, id).getCursorPos() return b end -- Scroll the screen upward function scroll(n, side, id) wrap(side, id).scroll(n) end -- Set the scale of the monitor (has no effect on a terminal) function setTextScale(x, side, id) if side ~= nil then wrap(side, id).setTextScale(x) return true end return false end -- Set the text color (has no effect on a non-advanced screen function setTextColor(color, side, id) local handler = wrap(side, id) if handler.isColor() then handler.setTextColor(color) return true end return false end -- Set the background color (has no effect on a non-advanced screen function setBackgroundColor(color, side, id) local handler = wrap(side, id) if handler.isColor() then handler.setBackgroundColor(color) return true end return false end setBackColor = setBackgroundColor -------------------------------------------------------------------------------------------------------------------------- --------------------------------------------ADDITIONAL-SIMPLE-FUNCTIONS--------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------- -- clear the screen, then set the cursor back to (1,1) function reset(side, id) clear(side, id) setCursorPos(1, 1, side, id) end -- set the colors of both the background and the text function setColors(col1, col2, side, id) if handler.isColor() then if col1 ~= nil then handler.setTextColor(col1) else handler.setTextColor(colours.white) end if col2 ~= nil then handler.setBackgroundColor(col2) else handler.setBackgroundColor(colours.black) end return true end return false end -- go to the next line, return the number of lines scrolled -- if scrolling is true, will scroll the screen if on the last line function nextLine(side, id, scrolling) local x, y = getCursor(side, id) local a, b = getSize(side, id) if y < b then setCursor(1, y+1, side, id) return 0 elseif y == b and scrolling then scroll(1, side, id) setCursor(1, y, side, id) return 1 end setCursor(1, y, side, id) return 0 end -- multilines version function nextLines(n, side, id, scrolling) local x, y = getCursor(side, id) local a, b = getSize(side, id) if y+n <= b then if y+n > 0 then setCursor(1, y+n, side, id) else setCursor(1, 1, side, id) end return 0 end if scrolling then scroll(y+n-b, side, id) setCursor(1, b, side, id) return y+n-b end setCursor(1, b, side, id) return 0 end -- print the text given to screen -- this function recognize the "\n" character and interprete them accordingly (with a new line) -- see nextLine function for the usage of "scrolling" argument" function print(text, side, id, scrolling) local handler = wrap(side, id) for i=1, text:len() do if text:sub(i, i) == "\n" then handler.nextLine(scrolling) else handler.write(text:sub(i, i)) end end return text end -- print the given text and go to the next line -- this function recognize the "\n" character and interprete them accordingly (with a new line) -- see nextLine function for the usage of "scrolling" argument" function println(text, side, id, scrolling) local handler = wrap(side, id) handler.print(text, scrolling) handler.nextLine(scrolling) return text end -------------------------------------------------------------------------------------------------------------------------- --------------------------------------------ALIGNMENT-AND-SPACE-PARSING-FUNCTIONS----------------------------------------- -------------------------------------------------------------------------------------------------------------------------- -- return a structured table separating the lines and words -- the table is an integer array, going from 1 to n, where the line #n is the nth line of the text -- each line has a table containing an alternance of integers representing an amonth of blank spaces, and of string representing the words -- so an odd index for a line means the number of spaces, while an even number for a word local function parseWords(str) local words = {} words[1] = {} words[1][1] = 0 local line = 1 local element = 1 for i=1, str:len() do local char = str:sub(i, i) if char == "\n" then line = line+1 words[line] = {} element = 1 words[line][1] = 0 elseif char == " " then if element/2 == math.floor(element/2) then element = element+1 words[line][element] = 1 else words[line][element] = words[line][element]+1 end else if element/2 == math.floor(element/2) then words[line][element] = words[line][element] .. char else element = element+1 words[line][element] = char end end end return words end -- insert into an integer array table, value at key, if the value already exists, all the next elements are shifted the the next key local function table_insert(t, key, value) if type(key) ~= "number" then return false end if key > #t then t[#t+1] = value elseif key < 1 then table_append(t, 1, value) else for i=#t, key-1, -1 do t[i+1] = t[i] end t[key] = value end return t end -- remove elements from the integer array table t, from and including index start, till and including index ending local function table_remove(t, start, ending) if not ending then ending = start start = 1 end if not start then start = 1 ending = #t end for i=start, #t do if i > ending then t[i-ending-1+start] = t[i] end t[i] = nil end return t end -- return a strict duplicate of the table, reccursively dupplicating tables included as elements local function table_dupplicate(t) local newTable = {} for k, e in pairs(t) do if type(e) == "table" then newTable[k] = table_dupplicate(e) else newTable[k] = e end end return newTable end -- concatenate words from and including index a, till and including index b, at the given line -- this is to be applied on a table resulting from the above function "parseWords" local function concat_words(t, line, a, b) if not t or type(t) ~= "table" then return "" end local lineT = t[line] if not a then a = 1 b = #lineT end if not b then b = a a = 1 end if b-a <= 0 then return "" end local str = "" for i=a, b do if i/2 == math.floor(i/2) then str = str .. lineT[i] elseif lineT[i] > 0 then str = str .. string.rep(" ", lineT[i]) end end return str end -- create a structured table showings lines to print in a screen of a given width -- ignore_trailing to true indicate that starting and trailing spaces are removed from the result -- the table is an integer array like the parseWords result -- each line is made up of 3 elements of indexes {1,2,3} -- [1] contains the starting spaces -- [2] contains the line body text -- [3] contains the trailing spaces local function parseLines(words, width, ignore_trailing) words = table_dupplicate(words) local lines = {} local i=1 while i <= #words do lines[i] = {} local wordsLine = words[i] if ignore_trailing then wordsLine[1] = 0 if (#wordsLine)/2 ~= math.floor((#wordsLine)/2) then wordsLine[#wordsLine] = 0 end end local saveJ = nil for j=1, #wordsLine do saveJ = j --wrap("back", 0).print("Concatenation of line " .. i .. "\n until words #" .. j .. ':\n "' .. concat_words(words, i, j) .. '"\n', true) if concat_words(words, i, j):len() > width then saveJ = -1 if j == 1 then -- wrap("back", 0).print("j == 1\n", true) lines[i][1] = 0 lines[i][2] = "" lines[i][3] = width wordsLine[1] = wordsLine[1] - width if ignore_trailing then lines[i][3] = 0 wordsLine[1] = 0 end table_insert(words, i+1, wordsLine) break elseif j == 2 then -- wrap("back", 0).print("j == 2\n", true) lines[i][1] = 0 if wordsLine[1] == 0 then lines[i][2] = string.sub(wordsLine[2], 1, width) lines[i][3] = 0 wordsLine[2] = string.sub(wordsLine[2], width+1) table_insert(words, i+1, wordsLine) else lines[i][2] = "" lines[i][3] = wordsLine[1] if ignore_trailing then lines[i][3] = 0 end wordsLine[1] = 0 end table_insert(words, i+1, wordsLine) break elseif j == 3 then -- wrap("back", 0).print("j == 3\n", true) lines[i][1] = wordsLine[1] lines[i][2] = string.rep(" ", wordsLine[1]) .. wordsLine[2] lines[i][3] = 0 table_remove(wordsLine, 2) if ignore_trailing then lines[i][1] = 0 wordsLine[1] = 0 end table_insert(words, i+1, wordsLine) break elseif j/2 == math.floor(j/2) then -- wrap("back", 0).print("j > 3 and last is odd\n", true) lines[i][1] = wordsLine[1] lines[i][2] = concat_words(words, i, j-2) lines[i][3] = wordsLine[j-1] table_remove(wordsLine, j-1) table_insert(wordsLine, 1, 0) if ignore_trailing then lines[i][1] = 0 lines[i][3] = 0 end table_insert(words, i+1, wordsLine) break else -- wrap("back", 0).print("j > 3 and last is even\n", true) lines[i][1] = wordsLine[1] lines[i][2] = concat_words(words, i, j-1) lines[i][3] = 0 table_remove(wordsLine, j-1) -- in that case, the issue is with a set of spaces, so it's removed from the list so that the next word is sent away wordsLine[1] = 0 if ignore_trailing then lines[i][1] = 0 wordsLine[1] = 0 end table_insert(words, i+1, wordsLine) break end end end if not saveJ then lines[i][1] = 0 lines[i][2] = "" lines[i][3] = 0 elseif saveJ > 0 then lines[i][1] = wordsLine[1] if (#wordsLine)/2 == math.floor((#wordsLine)/2) then lines[i][2] = concat_words(words, i, #wordsLine) lines[i][3] = 0 else lines[i][2] = concat_words(words, i, (#wordsLine)-1) lines[i][3] = wordsLine[#wordsLine] end if ignore_trailing then lines[i][1] = 0 lines[i][3] = 0 end end --wrap("back", 0).print("(" .. lines[i][1] .. ', "' .. lines[i][2] .. '", ' .. lines[i][3] .. ")\n", true) i = i+1 end return lines end -- print a left-aligned version of the text, parsed with the above functions to send to the next line any overflowing words -- this function recognize the "\n" character and interprete them accordingly (with a new line) -- see nextLine function for the usage of "scrolling" argument" -- if scrolling in disabled, it will only print the lines of text that fits the given space function left(text, side, id, scrolling) local handler = wrap(side, id) local width, height = handler.getSize() local x, y = handler.getCursor() term.clear() term.setCursorPos(1, 1) local words = parseWords(text) local lines = parseLines(words, width) if x > 1 then handler.nextLine(scrolling) end local str = "" if scrolling then for i=1, #lines do local tmp = string.rep(" ", lines[i][1]) .. lines[i][2] handler.write(tmp) str = str .. tmp if i < #lines then handler.nextLine(true) str = str .. "\n" end end else for i=y, height do if not lines[i-y+1] then break end local tmp = string.rep(" ", lines[i-y+1][1]) .. lines[i-y+1][2] handler.write(tmp) str = str .. tmp if i < height then handler.nextLine(false) str = str .. "\n" end end end return text end