mfd.lua
--- Display for general shipboard use. Mimics a 16x52 DOS-esque terminal, because I like the aesthetic. -- Yeah. -- # That's **rad**. --@classmod MFD MFD = {} -------------------------------------------------------------------------------- --- Initialisation methods. -- For creating and Initialising MFD object. -- @section Initialisation -------------------------------------------------------------------------------- ---------------------------------------- --- Creates a new intance of the MFD class. -- @tparam string _instanceName Name for the consoles and such. Should be identical to object's unique name. -- @tparam int _x X screen coordinate of MFD's top-left point. -- @tparam int _y Y screen coordinate of MFD's top-left point. -- @treturn table A new instance of the MFD class. -- @usage myMFD = MFD:new("myMFD",423,406) -- @todo Break off menu buttons and alert boxes into little objects. function MFD:new( _instanceName, _x, _y ) MFD.__index = MFD local self = setmetatable({}, MFD) self._instanceName = _instanceName ---------------------------------- -- The container that holds all of the things self.container = Geyser.Label:new({ name = self._instanceName..".container", x = _x, y = _y, width = 527, height = 312, color = "green", }) self.container:setStyleSheet([[background-color: "near_black";]]) ---------------------------------- -- The console itself self.console = Geyser.MiniConsole:new({ name = self._instanceName..".console", x = 5, y = 4, width = 520, height = 308, color = "black", }, self.container) self.console:setFontSize(12) self.console:clear() ---------------------------------- self.alertBox1 = Geyser.Label:new({ name = self._instanceName..".alertBox1", x = 100, y = 100, width = 200, height = 100, color = "cyan", }, self.container) --self.alertBox1:setStyleSheet([[background-color: #EEEEEE;]]) ---------------------------------- self.scanlines = Geyser.Label:new({ name = self._instanceName..".scanlines", x = 0, y = 0, width = 527, height = 312, color = "cyan", }, self.container) self.scanlines:setStyleSheet([[background-color: #00000000;]]) self.scanlines:setBackgroundImage(cloudDir .. [[GUI\images\MFD_scanlines.png]]) self.scanlines:raise() self.scanlines:hide() ---------------------------------- self.alertBox1:hide() self:cls() self:write("Done!", 24, 8) self:drawBorder() return self end ------------------------------------------------------------------------------------------------ --- Notification methods. -- For drawing notifications on-screen. -- @section notification --- Raises an on-screen notification, with multiple options for styling. -- Raises a notification on screen, centered in lower-middle. -- @tparam string text string The notification text to display. -- @tparam int|float displayTime int How many seconds to show the notification. 0 leaves notification indefinitely. -- @tparam int fontSize int The font size to use (default 12). -- @tparam[opt] string bgColour Colour to use for alert background. Defaults to MFD's current primary if not set. -- @tparam[opt] string fgColour Colour to use for foreground text. -- @usage myMFD:notify("Testing...", 160, 50, 2, 12, "#004400", "yellow") -- @todo Reduce external dependencies -- MFD:notify(text,width,height,displayTime,fontSize,bgColour,fgColour) function MFD:notify(text, displayTime, fontSize, bgColour, fgColour) if alertTimer then killTimer(alertTimer) end -- ! This is bad! Burn this global! if isNight and not bgColour then bgColour = bgColour or "#bd7a00" elseif not bgColour then bgColour = bgColour or "#00ff00" end local fontChoice = "Arial Rounded MT Bold" local MFD_width = tonumber(string.match(self.container.width, "%d+")) local MFD_height = tonumber(string.match(self.container.height, "%d+")) local alertCSS = [[ background-color: ]]..bgColour..[[; font-size: ]]..tonumber(fontSize) .. [[px; font-family: "]]..fontChoice..[[", monospace; qproperty-alignment: 'AlignHCenter | AlignVCenter'; ]] ---------------------------------- -- Let's calculate how big we need the backing fill to be. -- We're going to do this by calculating the average dimensions of each character, at our font/size. local fontWidth, fontHeight = calcFontSize(fontSize, fontChoice) -- We'll then see how many linebreaks we have, and what the longest line is. local splitTable = string.split(string.gsub(text, "%<BR%>", "<br>"), "<br>") -- Make a little table out of the input line, split by linebreaks. local numColumns = 0 -- Number of columns in display text local numRows = 0 -- Number of rows in display text for k, v in ipairs(splitTable) do numColumns = math.max(string.len(v), numColumns) -- numColumns is set to the larger of the two values. numRows = k -- We're starting at index 1 because Lua is silly, so we can get away with this. end -- Multiply the number of rows and the number columns each by their respective character dimension. -- Then we have a box near-enough shaped to fit the text. -- Then, add some padding for ease of reading. local boxWidth = (fontWidth * numColumns) + 60 -- About 10px padding per side. local boxHeight = (fontHeight * numRows) + 20 -- About 10px padding per side. -- You did it! Gold star! ♥★ ---------------------------------- self.alertBox1:setStyleSheet(alertCSS) self.alertBox1:resize(boxWidth, boxHeight) self.alertBox1:move((MFD_width / 2) - (boxWidth / 2), ((MFD_height / 2) + (MFD_height / 4)) - (boxHeight / 2)) self.alertBox1:raise() self.scanlines:raise() self.alertBox1:clear() self.alertBox1:show() self.alertBox1:echo(text, fgColour, tostring(fontSize)) if displayTime > 0 then alertTimer = tempTimer(displayTime, function() self.alertBox1:hide() alertTimer = nil end) end end ------------------------------------------------------------------------------------------------ --- Page methods. -- Stuff for drawing full pages of text to the MFD screen in a single routine. -- @section page --- Writes UACS and related data to the screen. -- @usage myMFD:writeNavPage() -- @todo Make this thing useful at all. function MFD:writeNavPage() self:cls() self:write("POSITION", 9, 1) self:write("Current easting: ", 2, 3) self:write(string.format("%4d", uacs.easting), 20, 3, "turquoise") self:write("Current southing: ", 2, 4) self:write(string.format("%4d", uacs.southing), 20, 4, "turquoise") self:write("CEP: ", 2, 6) self:write(uacs.error, 7, 6, "turquoise") self:write("DESTINATION", 34, 1) self:write("Name: ", 28, 3) self:write("----------", 34, 3, "turquoise") self:write("Location: ", 28, 4) self:write(uacs.easting..","..uacs.southing, 38, 4, "turquoise") self:write("Distance: ", 28, 6) self:write(uacs.error, 7, 6, "turquoise") self:write("Bearing: ", 28, 6) self:write(uacs.error, 7, 6, "turquoise") --self:write("Current easting: "..string.format("%4d",uacs.easting).." |", 1, 1) if not navUpdateResult then navUpdateResult = "..." end self:write("Calibration: "..navUpdateResult, 2, 12) self:drawBorderNav() end ------------------------------------------------------------------------------------------------ --- Query methods. -- Stuff for retrieving data to help us draw to the screen. -- @section query --- Retrieves current time of day from Time object, if present. -- Defaults to green if no Time object. -- @return HTML colour string, depending on Time:isNight(). -- @usage html_colour = MFD:getColour() -- @todo Redirect to new clock object, this is stupid! function MFD:getColour() if isNight then if isNight then return "orange" else return "green" end else return "green" end end ------------------------------------------------------------------------------------------------ --- Output methods. -- Stuff for outputting to the MFD screen. -- @section drawing --- Resets and erases the entire screen, and readies for writing. -- Clears the entirety of the consoles, and fills it with spaces so write functions can successfully locate. -- @usage myMFD:cls() myMFD:drawBorder() function MFD:cls() self.console:clear() cecho(self._instanceName..".console", "<grey:near_black>"..string.rep(string.rep(" ", 52) .. "\n", 16)) end -- Will overwrite any text beneath the border! -- @tparam string colour *[optional]* Colour to draw border in, as HTML colour name. -- @usage myMFD:drawBorder("cyan") function MFD:drawBorder(colour) if not colour then colour = MFD.getColour() end self:write("╔══════════════════════════════════════════════════╗", 0, 0, colour) for i = 1, 14 do self:write("║", 0, i, colour) self:write("║", 51, i, colour) end self:write("╚══════════════════════════════════════════════════╝", 0, 15, colour) end --- Draws a basic border around the edges of the screen, arranged for Nav page. -- Will overwrite any text beneath the border! -- @tparam string colour *[optional]* Colour to draw border in, as HTML colour name. -- @usage myMFD:drawBorderNav("forest_green") function MFD:drawBorderNav(colour) self:write("╔════════════════════════╦╦════════════════════════╗", 0, 0, colour) self:write("║", 0, 1, colour) self:write("║", 25, 1, colour) self:write("║", 26, 1, colour) self:write("║", 51, 1, colour) self:write("╠════════════════════════╣╠════════════════════════╣", 0, 2, colour) for i = 3, 9 do self:write("║", 0, i, colour) self:write("║", 25, i, colour) self:write("║", 26, i, colour) self:write("║", 51, i, colour) end self:write("╠════════════════════════╩╩════════════════════════╣", 0, 10, colour) for i = 11, 14 do self:write("║", 0, i, colour) self:write("║", 51, i, colour) end self:write("╚══════════════════════════════════════════════════╝", 0, 15, colour) end --- Places an underscore at the given X,Y with optional blinking. -- Purely aesthetic, serves no functional purpose. -- @param inputX* ***num*** X coordinate of cursor position. -- @param inputY* ***num*** Y coordinate of cursor position. -- @param doBlink* ***bool*** Blink cursor at the rate of cursorRate if true. -- @todo Actually make this do anything... -- @usage myMFD:setCursor(25,12,true) function MFD:setCursor(inputX, inputY, doBlink) --moveCursor("") end --- Writes text to the MFD. -- -- Writes text of arbitrary length, position, and colour to the MFD. Overwrites existing text for the length of the string. -- @tparam string writeText The text to be written to the MFD. -- @tparam int writeX X position of cursor, where text will be inserted. 0-51 -- @tparam int writeY Y position of cursor, where text will be inserted. 0-15 -- @tparam[opt = "aaa"] string fgColour Colour to be used for the text foreground colour, as HTML colour name. -- @tparam[opt = "aaa"] string bgColour Colour to be used for the text background colour, as HTML colour name. -- @treturn bool True, if write successful. -- @usage myMFD:write("Beginning Δcal routine...", 2, 2, "green", "dim_grey") function MFD:write(writeText, writeX, writeY, fgColour, bgColour) local fgColour = fgColour or self:getColour() local bgColour = bgColour or "near_black" local printColour = "<"..fgColour..":"..bgColour..">" local writeTextLength = utf8.len(writeText) writeTextLength = math.max(writeTextLength, 0) if moveCursor(self._instanceName..".console", writeX, writeY) then selectSection(self._instanceName..".console", writeX, writeTextLength) creplace(self._instanceName..".console", printColour..writeText) return true end return false end