Advertisement
HydrantHunter

InnKeeper

Jul 1st, 2014
510
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 94.54 KB | None | 0 0
  1. --[[   InnKeeper   ]]--
  2. --[[    by Dog     ]]--
  3. --[[     aka       ]]--
  4. --[[ HydrantHunter ]]--
  5. --[[   pastebin    ]]--
  6. --[[   Lj7Zw0nz    ]]--
  7. local ikVer = "1.1.00"
  8. --[[
  9. Tested with/requires:
  10.   - Minecraft            1.6.4 | 1.7.10
  11.   - ComputerCraft        1.58+ | 1.73+
  12.   - Gopher's Peripherals 2.13  | 2.2
  13.   - An advanced computer
  14.   - One or more biolocks
  15. ]]--
  16. --# "biolock", IDprint, attachName, learnedName, accessLevel
  17. local screen = "lock"
  18. local roomEntry, confirmTimer, printer, innKeeperKernel
  19. local kernelState, confirmState, scannerError, newInstall = true, false, false, false
  20. local bioScanners, scannerList, roomData, configData, scannerErrors = { }, { }, { }, { }, { }
  21. local pageNum, numPages, scanPage, scanPages, staffPage, staffPages, numDays = 1, 1, 1, 1, 1, 1, 0
  22. local roomColors = { available = colors.green, checkout = colors.brown, occupied = colors.lightBlue, unavailable = colors.orange, reserved = colors.purple, service = colors.red }
  23. local termX, termY = term.getSize()
  24.  
  25. -- custom read function courtesy of theoriginalbit (modified by Dog)
  26. local function read(_mask, _history, _limit, _noTerminate, _noMouse)
  27.   if _mask and type(_mask) ~= "string" then
  28.     error("Invalid parameter #1: Expected string, got " .. type(_mask), 2)
  29.   end
  30.   if _history and type(_history) ~= "table" then
  31.     error("Invalid parameter #2: Expected table, got " .. type(_history), 2)
  32.   end
  33.   if _limit and type(_limit) ~= "number" then
  34.     error("Invalid parameter #3: Expected number, got " .. type(_limit), 2)
  35.   end
  36.   if _noTerminate and type(_noTerminate) ~= "boolean" then
  37.     error("Invalid argument #4: Expected boolean, got " .. nativeType(_noTerminate), 2)
  38.   end
  39.   if _noMouse and type(_noMouse) ~= "boolean" then
  40.     error("Invalid argument #5: Expected boolean, got " .. nativeType(_noMouse), 2)
  41.   end
  42.   term.setCursorBlink(true)
  43.   local mouseLimit = _limit or 0
  44.   local input = ""
  45.   local pos = 0
  46.   local historyPos = nil
  47.   local pullEvent = _noTerminate and os.pullEventRaw or os.pullEvent
  48.   local sw, sh = term.getSize()
  49.   local sx, sy = term.getCursorPos()
  50.   local function redraw(_special)
  51.     local scroll = (sx + pos >= sw and (sx + pos) - sw or 0)
  52.     local replace = _special or _mask
  53.     local output = replace and (string.rep(replace, math.ceil(#input / #replace) - scroll)):sub(1, #input) or input:sub(scroll + 1)
  54.     term.setCursorPos(sx, sy)
  55.     term.write(output)
  56.     term.setCursorPos(sx + pos - scroll, sy)
  57.   end
  58.   local nativeScroll = term.scroll
  59.   term.scroll = function(_n) local ok, err = pcall(function() return nativeScroll(_n) end) if ok then sy = sy - _n return err end error(err, 2) end
  60.   while true do
  61.     local event, code, mX, mY = pullEvent()
  62.     if event == "char" and (not _limit or #input < _limit) then
  63.       input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  64.       pos = pos + 1
  65.       if not _limit then mouseLimit = math.min(mouseLimit + 1, sw - (sw - sx)) end
  66.     elseif event == "paste" and (not _limit or #input < _limit) then
  67.       if _limit and #input + #code > _limit then
  68.         code = code:sub(1, _limit - #input)
  69.       end
  70.       input = input:sub(1, pos) .. code .. input:sub(pos + 1)
  71.       pos = pos + #code
  72.       if not _limit then mouseLimit = math.min(mouseLimit + #code, sw - (sw - sx)) end
  73.     elseif event == "key" then
  74.       if code == keys.enter or code == keys.numPadEnter then
  75.         break
  76.       elseif code == keys.backspace and pos > 0 then
  77.         redraw(' ')
  78.         input = input:sub(1, math.max(pos - 1, 0)) .. input:sub(pos + 1)
  79.         pos = math.max(pos - 1, 0)
  80.         if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
  81.       elseif code == keys.delete and pos < #input then
  82.         redraw(' ')
  83.         input = input:sub(1, pos) .. input:sub(pos + 2)
  84.         if not _limit then mouseLimit = math.max(mouseLimit - 1, 0) end
  85.       elseif code == keys.home then
  86.         pos = 0
  87.       elseif code == keys["end"] then
  88.         pos = #input
  89.       elseif code == keys.left and pos > 0 then
  90.         pos = math.max(pos - 1, 0)
  91.       elseif code == keys.right and pos < #input then
  92.         pos = math.min(pos + 1, #input)
  93.       elseif _history and code == keys.up or code == keys.down then
  94.         redraw(' ')
  95.         if code == keys.up then
  96.           if not historyPos then
  97.             historyPos = #_history
  98.           elseif historyPos > 1 then
  99.             historyPos = historyPos - 1
  100.           end
  101.         else
  102.           if historyPos ~= nil and historyPos < #_history then
  103.             historyPos = math.max(historyPos + 1, #_history)
  104.           elseif historyPos == #_history then
  105.             historyPos = nil
  106.           end
  107.         end
  108.         if historyPos and #_history > 0 then
  109.           input = string.sub(_history[historyPos], 1, _limit) or ""
  110.           pos = #input
  111.           if not _limit then mouseLimit = pos end
  112.         else
  113.           input = ""
  114.           pos = 0
  115.           if not _limit then mouseLimit = 0 end
  116.         end
  117.       end
  118.     elseif event == "mouse_click" and not _noMouse and ((mX < sx or mX >= sx + mouseLimit) or (mY ~= sy)) then
  119.       break
  120.     end
  121.     redraw(_mask)
  122.   end
  123.   term.scroll = nativeScroll
  124.   term.setCursorBlink(false)
  125.   if sy + 1 > sh then
  126.     term.scroll(sy + 1 - sh)
  127.     term.setCursorPos(1, sy)
  128.   else
  129.     term.setCursorPos(1, sy + 1)
  130.   end
  131.   return input
  132. end
  133.  
  134. local function clearScreen()
  135.  term.setBackgroundColor(colors.black)
  136.  term.clear()
  137. end
  138.  
  139. local function clearLowerScreen(bgColor)
  140.   term.setBackgroundColor(bgColor or colors.black)
  141.   local line = string.rep(" ", termX)
  142.   for i = 4, termY do                     --# clear the display area
  143.     term.setCursorPos(1, i)
  144.     term.write(line)
  145.   end
  146. end
  147.  
  148. local function clearDataArea()
  149.   term.setBackgroundColor(colors.black)
  150.   local line = string.rep(" ", 34)
  151.   for i = 4, termY do                     --# clear the data area
  152.     term.setCursorPos(18, i)
  153.     term.write(line)
  154.   end
  155. end
  156.  
  157. local function header(bgColor)
  158.   bgColor = bgColor or colors.blue
  159.   term.setTextColor(bgColor == colors.red and colors.red or colors.white)
  160.   local line = string.rep(" ", termX - 3)
  161.   for i = 1, 3 do                         --# header
  162.     term.setBackgroundColor(bgColor)
  163.     term.setCursorPos(1, i)
  164.     term.write(line)
  165.     term.setBackgroundColor(colors.red)
  166.     term.write(i == 2 and " X " or "   ") --# exit button
  167.   end
  168.   term.setBackgroundColor(bgColor)
  169.   term.setTextColor(bgColor == colors.red and colors.orange or colors.lightBlue)
  170.   local title = configData.facilityName or "InnKeeper"
  171.   term.setCursorPos(math.ceil(termX / 2) - math.floor(#title / 2), 2)
  172.   term.write(title)                       --# title
  173. end
  174.  
  175. local function pageNumber()
  176.   term.setBackgroundColor(colors.gray)
  177.   term.setCursorPos(18, termY)
  178.   term.write(string.rep(" ", 34))
  179.   term.setTextColor(colors.lightGray)
  180.   term.setCursorPos(24, termY)
  181.   term.write("<< <              > >>")
  182.   local pageNumElement = tostring(pageNum) .. " of " .. tostring(numPages)
  183.   term.setCursorPos(35 - math.floor(#pageNumElement / 2), termY)
  184.   term.write(pageNumElement)
  185. end
  186.  
  187. local function scannerCount()
  188.   term.setBackgroundColor(colors.lightGray)
  189.   local line = string.rep(" ", 16)
  190.   for i = 17, 19 do                --# box/body
  191.     term.setCursorPos(1, i)
  192.     term.write(line)
  193.   end
  194.   term.setTextColor(colors.gray)
  195.   term.setCursorPos(2, 18)
  196.   term.write("Scanners:")
  197.   term.setTextColor(colors.black)
  198.   term.setCursorPos(12, 18)
  199.   term.write(tostring(#bioScanners))
  200. end
  201.  
  202. local function numberOfGuestsInResidency()
  203.   local gCount = 0
  204.   for i = 1, #roomData do
  205.     if (roomData[i].state == "occupied" or roomData[i].state == "service") and #roomData[i].guests > 0 then
  206.       gCount = gCount + #roomData[i].guests
  207.     end
  208.   end
  209.   return gCount
  210. end
  211.  
  212. local function innStats()
  213.   term.setBackgroundColor(colors.lightGray)
  214.   local line = string.rep(" ", 16)
  215.   for i = 4, 8 do                  --# box/body
  216.     term.setCursorPos(1, i)
  217.     term.write(line)
  218.   end
  219.   term.setCursorPos(2, 5)
  220.   term.setTextColor(colors.gray)
  221.   term.write("Rooms:  ")
  222.   term.setTextColor(colors.black)
  223.   term.write(tostring(#roomData))
  224.   term.setTextColor(colors.gray)
  225.   term.setCursorPos(2, 7)
  226.   term.write("Guests: ")
  227.   term.setTextColor(colors.black)
  228.   term.write(tostring(numberOfGuestsInResidency()))
  229. end
  230.  
  231. local function roomInfoButtons()
  232.   term.setBackgroundColor(colors.cyan)
  233.   term.setTextColor(colors.white)
  234.   local word = " Scanners "
  235.   for i = 1, 10 do
  236.     term.setCursorPos(termX - 2, i + 5)
  237.     term.write(" " .. word:sub(i, i) .. " ")
  238.   end
  239.   if roomData[roomEntry].state == "checkout" then
  240.     term.setCursorPos(34, termY - 2)
  241.     term.setBackgroundColor(colors.green)
  242.     term.write("   Invoice   ")
  243.   end
  244.   if #roomData > 1 and (roomData[roomEntry].state == "available" or roomData[roomEntry].state == "unavailable") then
  245.     term.setCursorPos(34, termY)
  246.     term.setBackgroundColor(confirmState and colors.orange or colors.red)
  247.     term.write(confirmState and "   CONFIRM   " or " Delete Room ")
  248.   end
  249. end
  250.  
  251. local function roomInfoLabels()
  252.   term.setBackgroundColor(colors.white)
  253.   term.setTextColor(colors.black)
  254.   term.setCursorPos(2, 5)
  255.   term.write("Room #              Status")
  256.   term.setCursorPos(2, 7)
  257.   term.write("Guests")
  258.   term.setCursorPos(22, 7)
  259.   term.write("Notes/Messages")
  260.   term.setCursorPos(2, termY - 2)
  261.   term.write("Check-IN            Day")
  262.   term.setCursorPos(2, termY - 1)
  263.   term.write("Check-OUT           Day")
  264.   if roomData[roomEntry].state ~= "checkout" then
  265.     term.setCursorPos(34, termY - 2)
  266.     term.write("Cr/Day: ")
  267.     term.setBackgroundColor(colors.lightGray)
  268.     term.write("     ")           --# room rate
  269.   end
  270.   term.setBackgroundColor(colors.gray)
  271.   term.setTextColor(colors.white)
  272.   term.setCursorPos(16, 5)
  273.   term.write("#")
  274.   term.setBackgroundColor(colors.lightGray)
  275.   term.setTextColor(colors.gray)
  276.   term.setCursorPos(17, 5)
  277.   term.write(string.rep(" ", 3 - #tostring(roomEntry)) .. tostring(roomEntry))
  278.   term.setBackgroundColor(colors.green)
  279.   term.setTextColor((#roomData[roomEntry].guests < 5 and roomData[roomEntry].state ~= "unavailable" and roomData[roomEntry].state ~= "service" and roomData[roomEntry].state ~= "checkout") and colors.white or colors.gray)
  280.   term.setCursorPos(10, 7)
  281.   term.write("+")                 --# add guest
  282.   term.setTextColor(#roomData[roomEntry].notes < 5 and colors.white or colors.gray)
  283.   term.setCursorPos(38, 7)
  284.   term.write("+")                 --# add note
  285.   term.setBackgroundColor(colors.lightGray)
  286.   term.setCursorPos(10, 5)
  287.   term.write("     ")             --# room number
  288.   term.setCursorPos(29, 5)
  289.   term.write(string.rep(" ", 13)) --# room state
  290.   term.setBackgroundColor(colors.gray)
  291.   term.setTextColor(colors.lightGray)
  292.   term.write("v")
  293.   term.setTextColor(colors.white)
  294.   term.setBackgroundColor(colors.lightGray)
  295.   term.setCursorPos(10, 7)
  296.   local lineA = string.rep(" ", 18)
  297.   local lineB = string.rep(" ", 25)
  298.   for i = 9, 15 do
  299.     term.setCursorPos(2, i)       --# guest list
  300.     term.write(lineA)
  301.     term.setCursorPos(22, i)      --# note/message list
  302.     term.write(lineB)
  303.   end
  304. end
  305.  
  306. local function roomInfoData()
  307.   local colorWheel = { available = colors.lime, checkout = colors.brown, occupied = colors.lightBlue, unavailable = colors.orange, reserved = colors.purple, service = colors.red }
  308.   term.setTextColor(colors.white)
  309.   term.setCursorPos(14 - #tostring(roomData[roomEntry].roomNumber), 5)
  310.   term.write(tostring(roomData[roomEntry].roomNumber)) --# room#
  311.   term.setCursorPos(30, 5)
  312.   term.setTextColor(colorWheel[roomData[roomEntry].state])
  313.   term.setBackgroundColor(colors.lightGray)
  314.   term.write(roomData[roomEntry].state)                --# room state
  315.   local uNames = { }
  316.   if roomData[roomEntry].scanners[1] then
  317.     local bio = peripheral.wrap(roomData[roomEntry].scanners[1])
  318.     bio.unlock(configData.masterPass)
  319.     uNames = { bio.getLearnedNames() }
  320.     bio.lock(configData.masterPass)
  321.   end
  322.   local found
  323.   for i = 1, #roomData[roomEntry].guests do            --# guest list
  324.     found = false
  325.     for j = 1, #uNames do
  326.       if uNames[j] == roomData[roomEntry].guests[i].name then found = true break end
  327.     end
  328.     term.setTextColor(found and colors.white or colors.red)
  329.     term.setBackgroundColor(colors.lightGray)
  330.     term.setCursorPos(3, 9 + i)
  331.     term.write(roomData[roomEntry].guests[i].name:sub(1, 15))
  332.     term.setTextColor(roomData[roomEntry].state == "checkout" and colors.gray or colors.white)
  333.     term.setCursorPos(19, 9 + i)
  334.     term.setBackgroundColor(colors.red)
  335.     term.write("x")                                    --# 'remove guest' button
  336.   end
  337.   for i = 1, #roomData[roomEntry].notes do             --# notes list
  338.     term.setTextColor(colors.white)
  339.     term.setBackgroundColor(colors.lightGray)
  340.     term.setCursorPos(23, 9 + i)
  341.     term.write(roomData[roomEntry].notes[i]:sub(1, 22))
  342.     term.setTextColor(roomData[roomEntry].state == "checkout" and colors.gray or colors.white)
  343.     term.setCursorPos(46, 9 + i)
  344.     term.setBackgroundColor(colors.red)
  345.     term.write("x")                                    --# 'remove note' button
  346.   end
  347.   term.setBackgroundColor(colors.white)
  348.   term.setTextColor(colors.lightGray)
  349.   term.setCursorPos(12, termY - 2)
  350.   term.write(tostring(roomData[roomEntry].checkIn.ciTime))
  351.   term.setCursorPos(26, termY - 2)
  352.   term.write(tostring(roomData[roomEntry].checkIn.ciDay))
  353.   term.setCursorPos(12, termY - 1)
  354.   term.write(tostring(roomData[roomEntry].checkOut.coTime))
  355.   term.setCursorPos(26, termY - 1)
  356.   term.write(tostring(roomData[roomEntry].checkOut.coDay))
  357.   term.setBackgroundColor(colors.lightGray)
  358.   if roomData[roomEntry].state ~= "checkout" then
  359.     term.setTextColor((roomData[roomEntry].state ~= "occupied" and roomData[roomEntry].state ~="reserved" and roomData[roomEntry].state ~= "service") and colors.white or colors.gray)
  360.     local rate = tostring(roomData[roomEntry].dailyRate)
  361.     term.setCursorPos(46 - #rate, termY - 2)
  362.     term.write(rate)
  363.   end
  364.   term.setTextColor(colors.gray)
  365.   term.setCursorPos(12, 7)
  366.   term.write(" " .. tostring(#roomData[roomEntry].guests) .. " ")
  367.   term.setCursorPos(40, 7)
  368.   term.write(" " .. tostring(#roomData[roomEntry].notes) .. " ")
  369. end
  370.  
  371. local function roomInfo()
  372.   screen = "room"
  373.   clearLowerScreen(colors.white)
  374.   roomInfoLabels()
  375.   roomInfoData()
  376.   roomInfoButtons()
  377. end
  378.  
  379. local function roomList()
  380.   screen = "main"
  381.   local sX, sY = 20, 5
  382.   term.setTextColor(colors.white)
  383.   if #roomData > 0 then
  384.     local roomNumStr, roomNumLen
  385.     local magicNumber = (((pageNum - 1) * 27) + pageNum)
  386.     for i = magicNumber, math.min(magicNumber + 27, #roomData) do
  387.       term.setBackgroundColor(roomColors[roomData[i].state])
  388.       roomNumStr = tostring(roomData[i].roomNumber)
  389.       roomNumLen = #roomNumStr / 2
  390.       term.setCursorPos(sX, sY)
  391.       term.write(string.rep(" ", 3 - math.floor(roomNumLen)) .. roomNumStr .. string.rep(" ", 3 - math.ceil(roomNumLen)))
  392.       sY = sY + 2
  393.       if sY >= termY - 1 then sY = 5 sX = sX + 8 end
  394.     end
  395.   else
  396.     term.setCursorPos(sX, sY)
  397.     term.write("No rooms to manage")
  398.   end
  399.   pageNumber()
  400. end
  401.  
  402. local function leftPane()
  403.   term.setBackgroundColor(colors.gray)
  404.   local line = string.rep(" ", 17)
  405.   for i = 4, termY do             --# sidebar body
  406.     term.setCursorPos(1, i)
  407.     term.write(line)
  408.   end
  409.   innStats()                      --# number of rooms and guests
  410.   term.setBackgroundColor(colors.gray)
  411.   term.setTextColor(colors.lightGray)
  412.   term.setCursorPos(2, 10)
  413.   term.write("Search:")
  414.   term.setBackgroundColor(colors.white)
  415.   term.setCursorPos(1, 11)
  416.   term.write(string.rep(" ", 16)) --# search entry area
  417.   term.setCursorPos(1, 13)
  418.   term.setTextColor(colors.white)
  419.   term.setBackgroundColor(colors.cyan)
  420.   term.write("   Add a room   ")  --# add room button
  421.   term.setBackgroundColor(colors.blue)
  422.   term.setCursorPos(1, 15)
  423.   term.write("    Settings    ")  --# settings button
  424.   scannerCount()                  --# number of scanners
  425. end
  426.  
  427. local function lockScreen()
  428.   screen = "lock"
  429.   clearLowerScreen(colors.white)
  430.   term.setTextColor(colors.lightGray)
  431.   term.setCursorPos(12, 8)
  432.   term.write("Please log in . . .")
  433.   term.setTextColor(colors.gray)
  434.   term.setCursorPos(12, 11)
  435.   term.write("Password: ")
  436.   term.setBackgroundColor(colors.lightGray)
  437.   term.setTextColor(colors.white)
  438.   term.write(string.rep(" ", 17)) --# password entry area
  439. end
  440.  
  441. local function guestSearchHelp()
  442.   term.setBackgroundColor(colors.white)
  443.   term.setCursorPos(1, 11)
  444.   term.write(string.rep(" ", 16))
  445.   for i = 1, 7 do
  446.     term.setCursorPos(19, 5 + i)
  447.     term.write(string.rep(" ", 30))
  448.   end
  449.   term.setTextColor(colors.black)
  450.   term.setCursorPos(22, 7)
  451.   term.write("*  = all guests")
  452.   term.setCursorPos(22, 8)
  453.   term.write("*o = all in occupancy")
  454.   term.setCursorPos(22, 9)
  455.   term.write("*r = all in reservation")
  456.   term.setCursorPos(24, 11)
  457.   term.setTextColor(colors.lightGray)
  458.   term.write("Click mouse to close")
  459.   term.setBackgroundColor(colors.blue)
  460.   term.setCursorPos(19, 5)
  461.   term.write("         Search Help          ")
  462.   os.pullEvent("mouse_click")
  463. end
  464.  
  465. local function guestSearchNoResults()
  466.   term.setBackgroundColor(colors.white)
  467.   term.setCursorPos(1, 11)
  468.   term.write(string.rep(" ", 16))
  469.   for i = 1, 5 do
  470.     term.setCursorPos(19, 4 + i)
  471.     term.write(string.rep(" ", 30))
  472.   end
  473.   term.setCursorPos(26, 6)
  474.   term.setTextColor(colors.black)
  475.   term.write("No Search Results")
  476.   term.setCursorPos(25, 8)
  477.   term.setTextColor(colors.lightGray)
  478.   term.write("Click mouse to close")
  479.   os.pullEvent("mouse_click")
  480. end
  481.  
  482. local function guestSearchResultsList(searchResults, searchPage, searchPages)
  483.   term.setBackgroundColor(colors.blue)
  484.   term.setTextColor(colors.lightBlue)
  485.   term.setCursorPos(19, 5)
  486.   term.write(" Name                    Room ")
  487.   term.setBackgroundColor(colors.white)
  488.   local line = string.rep(" ", 30)
  489.   for i = 6, 14 do
  490.     term.setCursorPos(19, i)
  491.     term.write(line)
  492.   end
  493.   term.setBackgroundColor(colors.gray)
  494.   for i = 15, 17 do
  495.     term.setCursorPos(19, i)
  496.     term.write(line)
  497.   end
  498.   local yPos = 7
  499.   local magicNumber = (((searchPage - 1) * 6) + searchPage)
  500.   for i = magicNumber, math.min(magicNumber + 6, #searchResults) do
  501.     term.setBackgroundColor(colors.white)
  502.     term.setTextColor(roomColors[roomData[searchResults[i].entry].state])
  503.     term.setCursorPos(20, yPos)
  504.     term.write(searchResults[i].name:sub(1, 15))                         --# name
  505.     term.setTextColor(colors.lightGray)
  506.     term.write(" " .. string.rep(".", 21 - #searchResults[i].name:sub(1, 15))) --# separator
  507.     term.setBackgroundColor(colors.lightGray)
  508.     term.setTextColor(colors.gray)
  509.     term.setCursorPos(43, yPos)
  510.     term.write(string.rep(" ", 4 - #tostring(searchResults[i].room)) .. tostring(searchResults[i].room) .. " ")                   --# room number
  511.     yPos = yPos + 1
  512.   end
  513.   term.setBackgroundColor(colors.gray)
  514.   term.setTextColor(colors.lightGray)
  515.   term.setCursorPos(23, 15)
  516.   term.write("<< <              > >>")
  517.   local pages = tostring(searchPage) .. " of " .. tostring(searchPages)
  518.   term.setCursorPos(34 - math.floor(#pages / 2), 15)
  519.   term.write(pages)
  520.   term.setBackgroundColor(colors.white)
  521.   term.setCursorPos(2, 11)
  522.   term.write(string.rep(" ", 15))
  523.   term.setCursorPos(2, 11)
  524.   term.setTextColor(colors.gray)
  525.   term.write(tostring(#searchResults))
  526.   term.write(#searchResults == 1 and " Result" or " Results")
  527.   term.setCursorPos(29, 17)
  528.   term.setBackgroundColor(colors.red)
  529.   term.setTextColor(colors.white)
  530.   term.write(" C L O S E ")
  531. end
  532.  
  533. local function guestSearchResults(searchResults)
  534.   screen = "search"
  535.   local searchPage, searchPages = 1, math.ceil(#searchResults / 7)
  536.   guestSearchResultsList(searchResults, searchPage, searchPages) --# send search results to list creator
  537.   while true do
  538.     local event, data, x, y = os.pullEvent()
  539.     if event == "mouse_click" and data == 1 then
  540.       if (x > 28 and x < 40 and y == termY - 2) or (x > termX - 3 and y < 4) then --# close
  541.         clearDataArea()
  542.         term.setBackgroundColor(colors.white)
  543.         term.setCursorPos(2, 11)
  544.         term.write(string.rep(" ", 15))
  545.         roomList()
  546.         break
  547.       elseif y == 15 then
  548.         if (x > 22 and x < 25) or (x > 42 and x < 45) then               --# Home / End
  549.           searchPage = (x > 22 and x < 25) and 1 or searchPages
  550.           guestSearchResultsList(searchResults, searchPage, searchPages) --# redraw list on new page
  551.         elseif (x > 25 and x < 27) or (x > 40 and x < 42) then           --# Page back / Page forward
  552.           searchPage = (x > 25 and x < 27) and math.max(1, searchPage - 1) or math.min(searchPage + 1, searchPages)
  553.           guestSearchResultsList(searchResults, searchPage, searchPages) --# redraw list on new page
  554.         end
  555.       else
  556.         local yPos, found = 7, false
  557.         local magicNumber = (((searchPage - 1) * 6) + searchPage)
  558.         for i = magicNumber, math.min(magicNumber + 6, #searchResults) do
  559.           if x > 19 and x < termX - 3 and y == yPos then             --# open the room sheet for the guest
  560.             screen = "room"
  561.             roomEntry = searchResults[i].entry
  562.             found = true
  563.             roomInfo()
  564.             break
  565.           else
  566.             yPos = yPos + 1
  567.           end
  568.         end
  569.         if found then break end
  570.       end
  571.     elseif event == "mouse_scroll" then
  572.       searchPage = data == 1 and math.min(searchPage + 1, searchPages) or math.max(1, searchPage - 1)
  573.       guestSearchResultsList(searchResults, searchPage, searchPages)   --# redraw list on new page
  574.     elseif event == "key" then
  575.       if data == keys.home or data == keys["end"] then                 --# Home / End
  576.         searchPage = data == keys.home and 1 or searchPages
  577.         guestSearchResultsList(searchResults, searchPage, searchPages) --# redraw list on new page
  578.       elseif data == keys.pageUp or data == keys.pageDown then         --# Page forward / back
  579.         searchPage = data == keys.pageUp and math.min(searchPage + 1, searchPages) or math.max(1, searchPage - 1)
  580.         guestSearchResultsList(searchResults, searchPage, searchPages) --# redraw list on new page
  581.       end
  582.     end
  583.   end
  584. end
  585.  
  586. local function guestSearch(word)
  587.   local searchResults, found = { }, false
  588.   if word ~= "?" and word:lower() ~= "help" then
  589.     for i = 1, #roomData do
  590.       for j = 1, #roomData[i].guests do
  591.         if string.lower(roomData[i].guests[j].name):find(string.lower(word)) or word == "*" then
  592.           found = true
  593.         elseif string.lower(word) == "*o" and roomData[i].state ~= "reserved" then
  594.           found = true
  595.         elseif string.lower(word) == "*r" and roomData[i].state == "reserved" then
  596.           found = true
  597.         end
  598.         if found then
  599.           searchResults[#searchResults + 1] = { }
  600.           searchResults[#searchResults].name = roomData[i].guests[j].name
  601.           searchResults[#searchResults].room = roomData[i].roomNumber
  602.           searchResults[#searchResults].entry = i
  603.           found = false
  604.         end
  605.       end
  606.     end
  607.   end
  608.   if searchResults[1] then
  609.     clearDataArea()
  610.     guestSearchResults(searchResults) --# if there are results, send them to the results handler
  611.   elseif word == "?" or string.lower(word) == "help" then
  612.     guestSearchHelp()
  613.     clearDataArea()
  614.     roomList()
  615.   else                                --# otherwise draw a pop-up with no results
  616.     guestSearchNoResults()
  617.     clearDataArea()
  618.     roomList()
  619.   end
  620. end
  621.  
  622. local function saveData(which)
  623.   local dataFile, dataContents
  624.   if not fs.exists("/data") then fs.makeDir("/data") end
  625.   if which == "room" then
  626.     dataFile = "/data/innRooms"
  627.     dataContents = roomData
  628.   elseif which == "config" then
  629.     dataFile = "/data/innSettings"
  630.     dataContents = configData
  631.   else
  632.     error("saveData: no data target specified", 0)
  633.   end
  634.   local thisFile = fs.open(dataFile, "w")
  635.   thisFile.write(textutils.serialize(dataContents))
  636.   thisFile.close()
  637. end
  638.  
  639. local function listScannerErrors(page, pages)
  640.   local magicNumber = ((page - 1) * 2) + page
  641.   term.setTextColor(colors.white)
  642.   term.setBackgroundColor(colors.gray)
  643.   local line = string.rep(" ", 30)
  644.   for i = 6, 17 do                       --# body
  645.     term.setCursorPos(12, i)
  646.     term.write(line)
  647.   end
  648.   local yPos = 8
  649.   for i = magicNumber, math.min(magicNumber + 2, #scannerErrors) do --# errors
  650.     term.setTextColor(colors.lightGray)
  651.     term.setCursorPos(14, yPos)
  652.     term.write(tostring(i) .. ": ")
  653.     term.setTextColor(colors.lightBlue)
  654.     term.write(tostring(scannerErrors[i].room))
  655.     term.setTextColor(colors.orange)
  656.     term.setCursorPos(40 - #scannerErrors[i].name, yPos)
  657.     term.write(scannerErrors[i].name)
  658.     term.setTextColor(colors.red)
  659.     term.setCursorPos(27 - math.ceil(#scannerErrors[i].message / 2), yPos + 1)
  660.     term.write(scannerErrors[i].message)
  661.     yPos = yPos + 3
  662.   end
  663.   term.setTextColor(colors.lightGray)
  664.   term.setCursorPos(24, 17)
  665.   term.write(tostring(page) .. " of " .. tostring(pages))
  666. end
  667.  
  668. local function scannerErrorScreen()
  669.   local sePage, sePages = 1, math.ceil(#scannerErrors / 3)
  670.   clearLowerScreen()
  671.   term.setBackgroundColor(colors.red)
  672.   term.setCursorPos(12, 5)
  673.   term.write(string.rep(" ", 30))    --# header
  674.   term.setTextColor(colors.white)
  675.   term.setCursorPos(18, 5)
  676.   term.write("Scanner Error Log")    --# title
  677.   listScannerErrors(sePage, sePages)
  678.   term.setTextColor(colors.white)
  679.   term.setBackgroundColor(colors.lightGray)
  680.   term.setCursorPos(12, 18)
  681.   term.write(string.rep(" ", 30))
  682.   term.setBackgroundColor(colors.orange)
  683.   term.setCursorPos(22, 18)
  684.   term.write(" Continue ")
  685.   while true do
  686.     local event, data, x, y = os.pullEvent()
  687.     if event == "mouse_click" and data == 1 and x > 21 and x < 32 and y == 18 then
  688.       for i = #scannerErrors, 1, -1 do
  689.         table.remove(scannerErrors, i)
  690.       end
  691.       return
  692.     elseif event == "mouse_scroll" then
  693.       sePage = data == 1 and math.min(sePage + 1, sePages) or math.max(1, sePage - 1)
  694.       listScannerErrors(sePage, sePages)
  695.     end
  696.   end
  697. end
  698.  
  699. local function logScannerError(scannerID, data, errorMsg)
  700.   scannerErrors[#scannerErrors + 1] = { }
  701.   scannerErrors[#scannerErrors].name = scannerID or "n/a"
  702.   scannerErrors[#scannerErrors].room = data or "0"
  703.   scannerErrors[#scannerErrors].message = errorMsg or "Unspecified"
  704. end
  705.  
  706. local function removeMissingScanners()
  707.   local badScanners = { }
  708.   badScanners[#badScanners + 1] = configData.masterScanner    --# create a list of ALL recorded scanners
  709.   for i = 1, #roomData do                                     --#
  710.     for j = 1, #roomData[i].scanners do                       --#
  711.       badScanners[#badScanners + 1] = roomData[i].scanners[j] --#
  712.     end
  713.   end
  714.   for i = 1, #bioScanners do                                  --# ALL discovered scanners (bioScanners) ...
  715.     for j = #badScanners, 1, -1 do                            --# ... vs. recorded scanners (badScanners)
  716.       if bioScanners[i] == badScanners[j] then                --# if a discovered scanner matches a recorded scanner...
  717.         table.remove(badScanners, j)                          --# ...then remove it from the badScanners table
  718.         break
  719.       end
  720.     end
  721.   end --# we now have a table of scanners (badScanners) that are recorded, but aren't connected
  722.   for i = #badScanners, 1, -1 do
  723.     if badScanners[i] == configData.masterScanner then --# this is really bad
  724.       configData.masterScanner = nil                   --# user will have to re-select the master scanner
  725.       table.remove(badScanners, i)
  726.     else
  727.       local badFind = false
  728.       for j = 1, #roomData do
  729.         for k = #roomData[j].scanners, 1, -1 do
  730.           if badScanners[i] == roomData[j].scanners[k] then
  731.             logScannerError(roomData[j].scanners[k], "Room " .. tostring(roomData[j].roomNumber), "Scanner Missing")
  732.             table.remove(roomData[j].scanners, k)
  733.             table.remove(badScanners, i)
  734.             saveData("room")
  735.             badFind = true
  736.             break
  737.           end
  738.         end
  739.         if badFind then break end
  740.       end
  741.     end
  742.   end
  743.   if badScanners[1] then error("removeMissingScanners: " .. tostring(#badScanners) .. " badScanners remaining", 0) end --# this is seriously bad, this shouldn't happen
  744. end
  745.  
  746. local function addEntityToScanner(scannerID, uName, uPrint)
  747.   local scanner = peripheral.wrap(scannerID)
  748.   if scanner then
  749.     if scanner.unlock(configData.masterPass) then
  750.       scanner.learn(uName, uPrint, 1)
  751.       scanner.lock(configData.masterPass)
  752.     end
  753.   end
  754. end
  755.  
  756. local function addGuestToScanners(uName, uPrint)
  757.   for i = 1, #roomData[roomEntry].scanners do
  758.     addEntityToScanner(roomData[roomEntry].scanners[i], uName, uPrint)
  759.   end
  760. end
  761.  
  762. local function addStaffMemberToScanners(uName, uPrint)
  763.   for i = 1, #bioScanners do
  764.     addEntityToScanner(bioScanners[i], uName, uPrint)
  765.   end
  766. end
  767.  
  768. local function removeEntityFromScanner(scannerID, uName)
  769.   local scanner = peripheral.wrap(scannerID)
  770.   if scanner then
  771.     if scanner.unlock(configData.masterPass) then
  772.       scanner.forget(uName)
  773.       scanner.lock(configData.masterPass)
  774.     end
  775.   end
  776. end
  777.  
  778. local function removeGuestFromScanners(uName)
  779.   for i = 1, #roomData[roomEntry].scanners do --# staff are checked before removal is called
  780.     removeEntityFromScanner(roomData[roomEntry].scanners[i], uName)
  781.   end
  782. end
  783.  
  784. local function removeStaffMemberFromScanners(uName, uPrint)
  785.   for i = 1, #bioScanners do
  786.     removeEntityFromScanner(bioScanners[i], uName)
  787.   end
  788.   for j = 1, #roomData do --# this adds the staff member back as a guest, if the staff member is registered also as a guest
  789.     for k = 1, #roomData[j].guests do
  790.       if roomData[j].guests[k].idPrint == uPrint then
  791.         for l = 1, #roomData[j].scanners do
  792.           addEntityToScanner(roomData[j].scanners[l], uName, uPrint)
  793.         end
  794.         break
  795.       end
  796.     end
  797.   end
  798. end
  799.  
  800. local function deleteNotes()
  801.   for i = #roomData[roomEntry].notes, 1, -1 do
  802.     table.remove(roomData[roomEntry].notes, i)
  803.   end
  804. end
  805.  
  806. local function removeGuest(guestNum, _tableRemove)
  807.   local found = false
  808.   for i = 1, #configData.staff do
  809.     if roomData[roomEntry].guests[guestNum].idPrint == configData.staff[i].idPrint then
  810.       found = true
  811.       break
  812.     end
  813.   end
  814.   if not found then
  815.     removeGuestFromScanners(roomData[roomEntry].guests[guestNum].name)
  816.   end
  817.   if roomData[roomEntry].state == "checkout" then
  818.     return
  819.   end
  820.   if #roomData[roomEntry].guests == 1 then
  821.     if roomData[roomEntry].state == "reserved" then
  822.       roomData[roomEntry].state = "available"
  823.       deleteNotes()
  824.     elseif roomData[roomEntry].state == "occupied" then
  825.       roomData[roomEntry].state = "checkout"
  826.       roomData[roomEntry].checkOut.coTime = textutils.formatTime(os.time(), false)
  827.       roomData[roomEntry].checkOut.coDay = os.day()
  828.       numDays = roomData[roomEntry].checkOut.coDay - roomData[roomEntry].checkIn.ciDay
  829.       return
  830.     end
  831.   end
  832.   if _tableRemove then table.remove(roomData[roomEntry].guests, guestNum) end
  833.   saveData("room")
  834. end
  835.  
  836. local function changeGuestField(name)
  837.   term.setBackgroundColor(colors.lightGray)
  838.   term.setTextColor(colors.white)
  839.   term.setCursorPos(19, name and 7 or 9)
  840.   term.write(string.rep(" ", 20))
  841.   term.setCursorPos(19, name and 7 or 9)
  842. end
  843.  
  844. local function changeGuestName(guestNum)
  845.   changeGuestField(true)
  846.   local newName = read(nil, nil, 20)
  847.   if newName ~= "" then
  848.     if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  849.       local scanner
  850.       for i = 1, #roomData[roomEntry].scanners do
  851.         scanner = peripheral.wrap(roomData[roomEntry].scanners[i])
  852.         if scanner then
  853.           if scanner.unlock(configData.masterPass) then
  854.             scanner.forget(roomData[roomEntry].guests[guestNum].name)
  855.             scanner.learn(newName, roomData[roomEntry].guests[guestNum].idPrint, 1)
  856.             scanner.lock(configData.masterPass)
  857.           end
  858.         end
  859.       end
  860.     end
  861.     return newName
  862.   else
  863.     return roomData[roomEntry].guests[guestNum].name
  864.   end
  865. end
  866.  
  867. local function changeGuestPrint(guestNum)
  868.   changeGuestField()
  869.   local newPrint = read(nil, nil, 20)
  870.   if newPrint ~= "" then
  871.     if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  872.       local scanner
  873.       for i = 1, #roomData[roomEntry].scanners do
  874.         scanner = peripheral.wrap(roomData[roomEntry].scanners[i])
  875.         if scanner then
  876.           if scanner.unlock(configData.masterPass) then
  877.             scanner.forget(roomData[roomEntry].guests[guestNum].name)
  878.             scanner.learn(roomData[roomEntry].guests[guestNum].name, newPrint, 1)
  879.             scanner.lock(configData.masterPass)
  880.           end
  881.         end
  882.       end
  883.     end
  884.     return newPrint
  885.   else
  886.     return roomData[roomEntry].guests[guestNum].idPrint
  887.   end
  888. end
  889.  
  890. local function accountInfoForm(uName, idPrint, scanned, staffMember)
  891.   term.setTextColor(colors.lightBlue)
  892.   term.setBackgroundColor(colors.blue)
  893.   term.setCursorPos(9, 5)
  894.   term.write(string.rep(" ", 33))
  895.   local txPos = staffMember and 18 or 17
  896.   term.setCursorPos(txPos, 5)
  897.   term.write(staffMember and "Staff Information" or "Guest Information") --# Dialogue title
  898.   term.setBackgroundColor(colors.gray)
  899.   local line = string.rep(" ", 33)
  900.   for i = 6, 14 do                      --# Dialogue body
  901.     term.setCursorPos(9, i)
  902.     term.write(line)
  903.   end
  904.   term.setBackgroundColor(colors.lightGray)
  905.   term.setCursorPos(18, 7)
  906.   term.write(string.rep(" ", 22))       --# 'Name' input box
  907.   term.setCursorPos(18, 9)
  908.   term.write(string.rep(" ", 22))       --# 'Print' input box
  909.   term.setBackgroundColor(colors.gray)
  910.   term.setTextColor(colors.lightGray)
  911.   term.setCursorPos(11, 7)
  912.   term.write("Name:  ")
  913.   term.setTextColor(colors.white)
  914.   term.setBackgroundColor(colors.lightGray)
  915.   term.write(" " .. uName:sub(1, 20))   --# Name
  916.   term.setBackgroundColor(colors.gray)
  917.   term.setTextColor(colors.lightGray)
  918.   term.setCursorPos(11, 9)
  919.   term.write("Print: ")
  920.   term.setTextColor(scanned and colors.gray or colors.white)
  921.   term.setBackgroundColor(colors.lightGray)
  922.   term.write(" " .. idPrint:sub(1, 20)) --# Print
  923.   term.setBackgroundColor(colors.gray)
  924.   term.setTextColor(scanned and colors.lime or colors.orange)
  925.   local xPos = staffMember and 16 or 19
  926.   term.setCursorPos(xPos, 11)
  927.   term.write(idPrint:find("idPrint") and "Scan in " or "Scanned ")
  928.   if staffMember then                   --# Staff title and buttons
  929.     term.write("Staff Member")
  930.     term.setBackgroundColor(colors.green)
  931.     term.setTextColor(colors.white)
  932.     term.setCursorPos(16, 13)
  933.     term.write(" CONFIRM ")
  934.     term.setBackgroundColor(colors.red)
  935.     term.setCursorPos(28, 13)
  936.     term.write(" DELETE ")
  937.   else
  938.     term.write("Guest")                 --# Guest title and button
  939.     term.setCursorPos(20, 13)
  940.     term.setBackgroundColor(colors.red)
  941.     term.setTextColor(colors.white)
  942.     term.write(" C L O S E ")
  943.   end
  944. end
  945.  
  946. local function viewGuestInfo(guestNum)
  947.   if guestNum == 0 then
  948.     roomData[roomEntry].guests[#roomData[roomEntry].guests + 1] = { }
  949.     guestNum = #roomData[roomEntry].guests
  950.     roomData[roomEntry].guests[guestNum].name = "Guest-" .. tostring(roomData[roomEntry].roomNumber) .. "-" .. tostring(math.random(1000, 9999))
  951.     roomData[roomEntry].guests[guestNum].idPrint = "idPrint-" .. tostring(roomData[roomEntry].roomNumber) .. "-" .. tostring(math.random(1000, 9999))
  952.     roomData[roomEntry].guests[guestNum].scanned = false
  953.     saveData("room")
  954.   end
  955.   accountInfoForm(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint, roomData[roomEntry].guests[guestNum].scanned)
  956.   while true do
  957.     local event, data, x, y = os.pullEvent()
  958.     if event == "biolock" and x == configData.masterScanner then
  959.       roomData[roomEntry].guests[guestNum].name = y or roomData[roomEntry].guests[guestNum].name
  960.       roomData[roomEntry].guests[guestNum].name = (roomData[roomEntry].guests[guestNum].name ~= "") and roomData[roomEntry].guests[guestNum].name or data
  961.       roomData[roomEntry].guests[guestNum].idPrint = data
  962.       roomData[roomEntry].guests[guestNum].scanned = true
  963.       saveData("room")
  964.       if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  965.         addGuestToScanners(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint)
  966.       end
  967.       accountInfoForm(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint, roomData[roomEntry].guests[guestNum].scanned)
  968.     elseif event == "mouse_click" and data == 1 then
  969.       if x > 19 and x < 31 and y == 13 then
  970.         if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  971.           addGuestToScanners(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint)
  972.         end
  973.         roomInfo()
  974.         break
  975.       elseif x > 17 and x < 40 and y == 7 then
  976.         roomData[roomEntry].guests[guestNum].name = changeGuestName(guestNum)     --# change guest name
  977.         saveData("room")
  978.         if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  979.           addGuestToScanners(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint)
  980.         end
  981.         accountInfoForm(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint, roomData[roomEntry].guests[guestNum].scanned)
  982.       elseif x > 17 and x < 40 and y == 9 and not roomData[roomEntry].guests[guestNum].scanned then
  983.         roomData[roomEntry].guests[guestNum].idPrint = changeGuestPrint(guestNum) --# change guest print
  984.         saveData("room")
  985.         if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  986.           addGuestToScanners(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint)
  987.         end
  988.         accountInfoForm(roomData[roomEntry].guests[guestNum].name, roomData[roomEntry].guests[guestNum].idPrint, roomData[roomEntry].guests[guestNum].scanned)
  989.       end
  990.     end
  991.   end
  992. end
  993.  
  994. local function staffInfo()
  995.   staffPages = math.max(1, math.ceil(#configData.staff / 4))
  996.   staffPage = math.min(staffPage, staffPages)
  997.   term.setBackgroundColor(colors.lightGray)
  998.   local line = string.rep(" ", 34)
  999.   for i = 13, 17 do
  1000.     term.setCursorPos(2, i)
  1001.     term.write(line)
  1002.   end
  1003.   local bio = peripheral.wrap(configData.masterScanner)
  1004.   bio.unlock(configData.masterPass)
  1005.   local uNames = { bio.getLearnedNames() }
  1006.   bio.lock(configData.masterPass)
  1007.   local sX, sY, found = 3, 14, false
  1008.   local magicNumber = (((staffPage - 1) * 3) + staffPage)
  1009.   for i = magicNumber, math.min(magicNumber + 3, #configData.staff) do
  1010.     term.setCursorPos(sX, sY)
  1011.     found = false
  1012.     for j = 1, #uNames do
  1013.       if uNames[j] == configData.staff[i].name then found = true break end
  1014.     end
  1015.     term.setTextColor(found and colors.white or colors.red)
  1016.     term.write(configData.staff[i].name:sub(1, 15))
  1017.     sY = sY + 2
  1018.     if sY > termY - 2 then sX = sX + 17 sY = 14 end
  1019.   end
  1020.   term.setBackgroundColor(colors.white)
  1021.   term.setTextColor(colors.lightGray)
  1022.   term.setCursorPos(9, termY - 1)
  1023.   term.write("<< <            > >>")
  1024.   local pageInfo = tostring(staffPage) .. " of " .. tostring(staffPages)
  1025.   term.setCursorPos(19 - math.floor(#pageInfo / 2), termY - 1)
  1026.   term.write(pageInfo)
  1027. end
  1028.  
  1029. local function settingsData()
  1030.   term.setBackgroundColor(colors.lightGray)
  1031.   term.setTextColor(colors.white)
  1032.   term.setCursorPos(19, 5)
  1033.   term.write(string.rep(" ", 17))
  1034.   term.setCursorPos(20, 5)
  1035.   term.write(configData.masterScanner)    --# masterScanner
  1036.   term.setCursorPos(19, 7)
  1037.   term.write(" ")
  1038.   term.write(string.rep("*", 15))         --# masterPass
  1039.   term.write(" ")
  1040.   term.setCursorPos(19, 9)
  1041.   term.write(" ")
  1042.   term.write(string.rep("*", 15))         --# screenPass
  1043.   term.write(" ")
  1044.   term.setCursorPos(19, 11)
  1045.   term.write(string.rep(" ", 5))
  1046.   term.setTextColor(colors.gray)
  1047.   local staffNumCount = tostring(#configData.staff)
  1048.   term.setCursorPos(23 - #staffNumCount, 11)
  1049.   term.write(staffNumCount)               --# number of staff
  1050.   term.setBackgroundColor(colors.green)
  1051.   term.setTextColor(colors.white)
  1052.   term.setCursorPos(25, 11)
  1053.   term.write("+")                         --# add staff button
  1054. end
  1055.  
  1056. local function settingsScreen()
  1057.   clearLowerScreen(colors.white)
  1058.   screen = "settings"
  1059.   term.setBackgroundColor(colors.white)
  1060.   term.setTextColor(colors.black)
  1061.   term.setCursorPos(2, 5)
  1062.   term.write("Master Scanner")
  1063.   term.setCursorPos(2, 7)
  1064.   term.write("Master Password")
  1065.   term.setCursorPos(2, 9)
  1066.   term.write("Screen Password")
  1067.   term.setCursorPos(2, 11)
  1068.   term.write("Staff")
  1069.   term.setTextColor(colors.gray)
  1070.   term.setCursorPos(37, 5)
  1071.   term.write("Front Desk")
  1072.   term.setCursorPos(37, 7)
  1073.   term.write("All scanners")
  1074.   term.setCursorPos(37, 9)
  1075.   term.write("Main Screen")
  1076.   term.setCursorPos(37, 13)
  1077.   term.write("All staff are")
  1078.   term.setCursorPos(37, 14)
  1079.   term.write("automatically")
  1080.   term.setCursorPos(37, 15)
  1081.   term.write("added to all")
  1082.   term.setCursorPos(37, 16)
  1083.   term.write("biometric")
  1084.   term.setCursorPos(37, 17)
  1085.   term.write("scanners")
  1086.   settingsData()
  1087.   staffInfo()
  1088. end
  1089.  
  1090. local function oldPasswordChangePopup(master)
  1091.   term.setBackgroundColor(colors.gray)
  1092.   term.setTextColor(colors.white)
  1093.   local line = string.rep(" ", 19)
  1094.   local startI = master and 4 or 6
  1095.   local endI = master and 11 or 13
  1096.   for i = startI, endI do
  1097.     term.setCursorPos(18, i)
  1098.     term.write(line)
  1099.   end
  1100.   term.setCursorPos(19, master and 5 or 7)
  1101.   term.write("Current  Password")
  1102.   term.setCursorPos(21, master and 9 or 11)
  1103.   term.write("Enter nothing")
  1104.   term.setCursorPos(23, master and 10 or 12)
  1105.   term.write("to cancel")
  1106.   term.setBackgroundColor(colors.lightGray)
  1107. end
  1108.  
  1109. local function newPasswordChangePopup(master)
  1110.   term.setBackgroundColor(colors.gray)
  1111.   term.setCursorPos(19, master and 5 or 7)
  1112.   term.write("  New  Password   ")
  1113.   term.setBackgroundColor(colors.lightGray)
  1114.   term.setCursorPos(19, master and 7 or 9)
  1115.   term.write(string.rep(" ", 17))
  1116.   term.setCursorPos(20, master and 7 or 9)
  1117. end
  1118.  
  1119. local function getOldPassword(master)
  1120.   term.setCursorPos(19, master and 7 or 9)
  1121.   term.write(string.rep(" ", 17))
  1122.   term.setCursorPos(20, master and 7 or 9)
  1123.   return read("*", nil, 15)
  1124. end
  1125.  
  1126. local function changeScreenPassword()
  1127.   oldPasswordChangePopup()
  1128.   local oldPass
  1129.   while true do
  1130.     oldPass = getOldPassword()
  1131.     if oldPass == configData.screenPass then
  1132.       break
  1133.     elseif oldPass == "" then
  1134.       settingsScreen()
  1135.       return
  1136.     end
  1137.   end
  1138.   newPasswordChangePopup()
  1139.   local newPass = read("*", nil, 15)
  1140.   if newPass and newPass ~= "" then
  1141.     configData.screenPass = newPass
  1142.     saveData("config")
  1143.   end
  1144.   settingsScreen()
  1145. end
  1146.  
  1147. local function changeMasterPassword()
  1148.   oldPasswordChangePopup(true)
  1149.   local oldPass
  1150.   while true do
  1151.     oldPass = getOldPassword(true)
  1152.     if oldPass == configData.masterPass then
  1153.       break
  1154.     elseif oldPass == "" then
  1155.       settingsScreen()
  1156.       return
  1157.     end
  1158.   end
  1159.   newPasswordChangePopup(true)
  1160.   local newPass = read("*", nil, 15)
  1161.   if newPass and newPass ~= "" then
  1162.     configData.masterPass = newPass
  1163.     local scanner
  1164.     for i = 1, #bioScanners do
  1165.       scanner = peripheral.wrap(bioScanners[i])
  1166.       if scanner then
  1167.         if scanner.unlock(oldPass) then
  1168.           scanner.lock(newPass)
  1169.         end
  1170.       end
  1171.     end
  1172.     saveData("config")
  1173.   end
  1174.   settingsScreen()
  1175. end
  1176.  
  1177. local function changeStaffField(name)
  1178.   term.setBackgroundColor(colors.lightGray)
  1179.   term.setTextColor(colors.white)
  1180.   term.setCursorPos(19, name and 7 or 9)
  1181.   term.write(string.rep(" ", 20))
  1182.   term.setCursorPos(19, name and 7 or 9)
  1183. end
  1184.  
  1185. local function changeStaffName(staffNum)
  1186.   changeStaffField(true)
  1187.   local newName = read(nil, nil, 20)
  1188.   if newName and newName ~= "" then
  1189.     removeStaffMemberFromScanners(configData.staff[staffNum].name, configData.staff[staffNum].idPrint)
  1190.     addStaffMemberToScanners(newName, configData.staff[staffNum].idPrint)
  1191.     return newName
  1192.   else
  1193.     return configData.staff[staffNum].name
  1194.   end
  1195. end
  1196.  
  1197. local function changeStaffPrint(staffNum)
  1198.   changeStaffField()
  1199.   local newPrint = read(nil, nil, 20)
  1200.   if newPrint ~= "" then
  1201.     removeStaffMemberFromScanners(configData.staff[staffNum].name, configData.staff[staffNum].idPrint)
  1202.     addStaffMemberToScanners(configData.staff[staffNum].name, newPrint)
  1203.     return newPrint
  1204.   else
  1205.     return configData.staff[staffNum].idPrint
  1206.   end
  1207. end
  1208.  
  1209. local function viewStaffInfo(staffNum)
  1210.   if staffNum == 0 then
  1211.     configData.staff[#configData.staff + 1] = { }
  1212.     staffNum = #configData.staff
  1213.     configData.staff[staffNum].name = "Staff-" .. tostring(#configData.staff) .. "-" .. tostring(math.random(1000, 9999))
  1214.     configData.staff[staffNum].idPrint = "idPrint-" .. tostring(#configData.staff) .. "-" .. tostring(math.random(1000, 9999))
  1215.     configData.staff[staffNum].scanned = false
  1216.     saveData("config")
  1217.   end
  1218.   accountInfoForm(configData.staff[staffNum].name, configData.staff[staffNum].idPrint, configData.staff[staffNum].scanned, true)
  1219.   while true do
  1220.     local event, data, x, y = os.pullEvent()
  1221.     if event == "biolock" and x == configData.masterScanner then
  1222.       configData.staff[staffNum].name = y or configData.staff[staffNum].name
  1223.       configData.staff[staffNum].name = (configData.staff[staffNum].name ~= "") and configData.staff[staffNum].name or data
  1224.       configData.staff[staffNum].idPrint = data
  1225.       configData.staff[staffNum].scanned = true
  1226.       saveData("config")
  1227.       addStaffMemberToScanners(configData.staff[staffNum].name, configData.staff[staffNum].idPrint)
  1228.       accountInfoForm(configData.staff[staffNum].name, configData.staff[staffNum].idPrint, configData.staff[staffNum].scanned, true)
  1229.     elseif event == "mouse_click" and data == 1 then
  1230.       if x > 15 and x < 25 and y == 13 then                                               --# close dialogue
  1231.         addStaffMemberToScanners(configData.staff[staffNum].name, configData.staff[staffNum].idPrint)
  1232.         settingsScreen()
  1233.         break
  1234.       elseif x > 27 and x < 36 and y == 13 then                                           --# delete staff member
  1235.         removeStaffMemberFromScanners(configData.staff[staffNum].name, configData.staff[staffNum].idPrint)
  1236.         table.remove(configData.staff, staffNum)
  1237.         saveData("config")
  1238.         settingsScreen()
  1239.         break
  1240.       elseif x > 17 and x < 40 and y == 7 then                                            --# change name
  1241.         configData.staff[staffNum].name = changeStaffName(staffNum)
  1242.         saveData("config")
  1243.         accountInfoForm(configData.staff[staffNum].name, configData.staff[staffNum].idPrint, configData.staff[staffNum].scanned, true)
  1244.       elseif x > 17 and x < 40 and y == 9 and not configData.staff[staffNum].scanned then --# change idPrint
  1245.         configData.staff[staffNum].idPrint = changeStaffPrint(staffNum)
  1246.         saveData("config")
  1247.         accountInfoForm(configData.staff[staffNum].name, configData.staff[staffNum].idPrint, configData.staff[staffNum].scanned, true)
  1248.       end
  1249.     end
  1250.   end
  1251. end
  1252.  
  1253. local function deleteNote(noteNum)
  1254.   table.remove(roomData[roomEntry].notes, noteNum)
  1255.   saveData("room")
  1256.   roomInfoLabels()
  1257.   roomInfoData()
  1258.   roomInfoButtons()
  1259. end
  1260.  
  1261. local function editNote(noteNum)
  1262.   term.setBackgroundColor(colors.lightGray)
  1263.   term.setTextColor(colors.white)
  1264.   while true do
  1265.     term.setCursorPos(5, 7)
  1266.     term.write(string.rep(" ", 41))
  1267.     term.setCursorPos(6, 7)
  1268.     local thisNote = read(nil, nil, 41)
  1269.     if thisNote == "" then
  1270.       term.setCursorPos(6, 7)
  1271.       term.write(roomData[roomEntry].notes[noteNum])
  1272.       return roomData[roomEntry].notes[noteNum]
  1273.     else
  1274.       return thisNote
  1275.     end
  1276.   end
  1277. end
  1278.  
  1279. local function viewNoteForm(noteNum)
  1280.   term.setBackgroundColor(colors.blue)
  1281.   term.setTextColor(colors.lightBlue)
  1282.   term.setCursorPos(4, 5)
  1283.   term.write(string.rep(" ", 45))
  1284.   term.setCursorPos(5, 5)
  1285.   term.write("Note")
  1286.   local noteNumStr = tostring(noteNum)
  1287.   term.setCursorPos(48 - #noteNumStr, 5)
  1288.   term.write(noteNumStr)
  1289.   term.setBackgroundColor(colors.gray)
  1290.   term.setTextColor(colors.white)
  1291.   local line = string.rep(" ", 45)
  1292.   for i = 6, 11 do
  1293.     term.setCursorPos(4, i)
  1294.     term.write(line)
  1295.   end
  1296.   term.setBackgroundColor(colors.lightGray)
  1297.   term.setCursorPos(5, 7)
  1298.   term.write(string.rep(" ", 43))
  1299.   term.setCursorPos(6, 7)
  1300.   term.write(roomData[roomEntry].notes[noteNum])
  1301. end
  1302.  
  1303. local function viewNote(noteNum)
  1304.   if noteNum == 0 then --# new note
  1305.     roomData[roomEntry].notes[#roomData[roomEntry].notes + 1] = "New Note"
  1306.     noteNum = #roomData[roomEntry].notes
  1307.   end
  1308.   viewNoteForm(noteNum)
  1309.   term.setCursorPos(21, 10)
  1310.   term.setBackgroundColor(colors.red)
  1311.   term.setTextColor(colors.white)
  1312.   term.write(" C L O S E ")
  1313.   while true do
  1314.     local _, button, x, y = os.pullEvent("mouse_click")
  1315.     if x > 4 and x < 49 and y == 7 and button == 1 then
  1316.       local newNote = editNote(noteNum)
  1317.       roomData[roomEntry].notes[noteNum] = newNote
  1318.     elseif x > 20 and x < 32 and y == 10 and button == 1 then
  1319.       break
  1320.     end
  1321.   end
  1322.   saveData("room")
  1323.   roomInfo()
  1324. end
  1325.  
  1326. local function helpScreen()
  1327.   clearLowerScreen(colors.white)
  1328.   term.setTextColor(colors.gray)
  1329.   term.setCursorPos(15, 7)
  1330.   term.write("Open/Close Help Screen")
  1331.   term.setCursorPos(15, 9)
  1332.   term.write("'X' at top right to lock the screen")
  1333.   term.setCursorPos(15, 14)
  1334.   term.write("Room number directly to change room #")
  1335.   term.setCursorPos(15, 16)
  1336.   term.setBackgroundColor(colors.gray)
  1337.   term.setTextColor(colors.white)
  1338.   term.write("#")
  1339.   term.setBackgroundColor(colors.white)
  1340.   term.setTextColor(colors.gray)
  1341.   term.write(" to the right of Room# to re-order")
  1342.   term.setBackgroundColor(colors.gray)
  1343.   local line = string.rep(" ", 13)
  1344.   for i = 4, termY do
  1345.     term.setCursorPos(1, i)
  1346.     term.write(line)
  1347.   end
  1348.   term.setTextColor(colors.black)
  1349.   term.setCursorPos(2, 5)
  1350.   term.write("Main Screen")
  1351.   term.setCursorPos(2, 12)
  1352.   term.write("Room Screen")
  1353.   term.setTextColor(colors.lightGray)
  1354.   term.setCursorPos(2, 7)
  1355.   term.write("F1")
  1356.   term.setCursorPos(2, 9)
  1357.   term.write("Right click")
  1358.   term.setCursorPos(2, 14)
  1359.   term.write("Left click")
  1360.   term.setCursorPos(2, 16)
  1361.   term.write("Left click")
  1362.   while true do
  1363.     local event, data, x, y = os.pullEvent()
  1364.     if (event == "key" and data == keys.f1) or (event == "mouse_click" and data == 1 and x > termX - 3 and y < 4) then
  1365.       clearDataArea()
  1366.       leftPane()
  1367.       roomList()
  1368.       break
  1369.     end
  1370.   end
  1371. end
  1372.  
  1373. local function assignDeassignScanner(scannerID, learn)
  1374.   local sides = { "top", "bottom", "left", "right", "back" }
  1375.   local scanner = peripheral.wrap(scannerID)
  1376.   if not scanner then return end
  1377.   if scanner.unlock(configData.masterPass) then
  1378.     for _, side in pairs(sides) do
  1379.       scanner.forgetProgram(side)
  1380.     end
  1381.     local uNames = { scanner.getLearnedNames() }
  1382.     for i = 1, #uNames do
  1383.       scanner.forget(uNames[i])
  1384.     end
  1385.     if learn then
  1386.       for _, side in pairs(sides) do
  1387.         scanner.program(side, 1, 60, configData.redstone)
  1388.       end
  1389.       for i = 1, #configData.staff do
  1390.         scanner.learn(configData.staff[i].name, configData.staff[i].idPrint, 1)
  1391.       end
  1392.       if roomData[roomEntry].state == "occupied" or roomData[roomEntry].state == "service" then
  1393.         for i = 1, #roomData[roomEntry].guests do
  1394.           scanner.learn(roomData[roomEntry].guests[i].name, roomData[roomEntry].guests[i].idPrint, 1)
  1395.         end
  1396.       end
  1397.     end
  1398.     scanner.lock(configData.masterPass)
  1399.   end
  1400. end
  1401.  
  1402. local function assignDeassignMasterScanner(learn)
  1403.   local scanner = peripheral.wrap(configData.masterScanner)
  1404.   if not scanner then configData.masterScanner = nil return end --# this is bad and shouldn't happen
  1405.   if scanner.unlock(configData.masterPass) then
  1406.     for _, side in pairs(rs.getSides()) do
  1407.       scanner.forgetProgram(side)
  1408.     end
  1409.     local uNames = { scanner.getLearnedNames() }
  1410.     for i = 1, #uNames do
  1411.       scanner.forget(uNames[i])
  1412.     end
  1413.     if learn then
  1414.       for i = 1, #configData.staff do
  1415.         scanner.learn(configData.staff[i].name, configData.staff[i].idPrint, 1)
  1416.       end
  1417.     else
  1418.       configData.masterScanner = nil
  1419.     end
  1420.     scanner.lock(configData.masterPass)
  1421.   end
  1422. end
  1423.  
  1424. local function findUnusedScanners()
  1425.   local unusedScanners = { }
  1426.   for i = 1, #bioScanners do
  1427.     if bioScanners[i] ~= configData.masterScanner then
  1428.       unusedScanners[#unusedScanners + 1] = bioScanners[i]
  1429.     end
  1430.   end
  1431.   for i = 1, #roomData do
  1432.     for j = 1, #roomData[i].scanners do
  1433.       for k = #unusedScanners, 1, -1 do
  1434.         if roomData[i].scanners[j] == unusedScanners[k] then
  1435.           table.remove(unusedScanners, k)
  1436.           break
  1437.         end
  1438.       end
  1439.     end
  1440.   end
  1441.   return unusedScanners
  1442. end
  1443.  
  1444. local function unassignedScanners()
  1445.   for i = #scannerList, 1, -1 do
  1446.     table.remove(scannerList, i)
  1447.   end
  1448.   scannerList = findUnusedScanners()
  1449.   scanPages = math.max(1, math.ceil(#scannerList / 8))
  1450.   scanPage = math.min(scanPage, scanPages)
  1451.   local sX, sY = 2, 5
  1452.   term.setBackgroundColor(colors.white)
  1453.   term.setTextColor(colors.lightGray)
  1454.   term.setCursorPos(sX, sY)
  1455.   term.write("Scanners Available:  (" .. tostring(#scannerList) .. ") ")
  1456.   term.setCursorPos(4, 17)
  1457.   term.write("<< <              > >>")
  1458.   local sPages = tostring(scanPage) .. " of " .. tostring(scanPages)
  1459.   term.setCursorPos(15 - math.floor(#sPages / 2), 17)
  1460.   term.write(sPages)
  1461.   term.setBackgroundColor(colors.lightGray)
  1462.   term.setTextColor(colors.white)
  1463.   local line = string.rep(" ", 27)
  1464.   for i = 1, 9 do                         --# draw the 'box'
  1465.     term.setCursorPos(sX, sY + 1 + i)
  1466.     term.write(line)
  1467.   end
  1468.   sX, sY = 3, 8
  1469.   local magicNumber = (((scanPage - 1) * 7) + scanPage)
  1470.   for i = magicNumber, math.min(magicNumber + 7, #scannerList) do
  1471.     term.setCursorPos(sX, sY)
  1472.     term.write(scannerList[i]:sub(1, 12))
  1473.     sY = sY + 2
  1474.     if sY > 14 then sX = sX + 13 sY = 8 end
  1475.   end
  1476. end
  1477.  
  1478. local function scannersAssignedBox(master)
  1479.   term.setBackgroundColor(colors.white)
  1480.   term.setTextColor(colors.lightGray)
  1481.   term.setCursorPos(33, 5)
  1482.   term.write(master and "Scanner Assigned:" or "Scanners Assigned:")
  1483.   term.setBackgroundColor(colors.lightGray)
  1484.   term.setTextColor(colors.white)
  1485.   local line = string.rep(" ", 14)
  1486.   for i = 1, 9 do
  1487.     term.setCursorPos(35, 6 + i)
  1488.     term.write(line)
  1489.   end
  1490. end
  1491.  
  1492. local function masterAssignedScanners()
  1493.   scannersAssignedBox(true)
  1494.   term.setCursorPos(36, 8)
  1495.   term.write(configData.masterScanner and configData.masterScanner:sub(1, 12) or "")
  1496. end
  1497.  
  1498. local function assignedScanners()
  1499.   scannersAssignedBox()
  1500.   for i = 1, #roomData[roomEntry].scanners do
  1501.     term.setCursorPos(36, 6 + (2 * i))
  1502.     term.write(roomData[roomEntry].scanners[i]:sub(1, 12))
  1503.   end
  1504. end
  1505.  
  1506. local function scannerListing(which)
  1507.   unassignedScanners()
  1508.   if which then
  1509.     masterAssignedScanners()
  1510.   else
  1511.     assignedScanners()
  1512.   end
  1513. end
  1514.  
  1515. local function selectScanners()
  1516.   while true do
  1517.     local event, data, x, y = os.pullEvent()
  1518.     if event == "mouse_click" and data == 1 then
  1519.       if x > termX - 3 and y < 4 then return end
  1520.       if x > 35 and x < 48 and y > 7 and y < 15 then
  1521.         local scannerNumber = 0
  1522.         for i = 8, 14, 2 do
  1523.           scannerNumber = scannerNumber + 1
  1524.           if y == i and roomData[roomEntry].scanners[scannerNumber] then
  1525.             assignDeassignScanner(roomData[roomEntry].scanners[scannerNumber])
  1526.             table.remove(roomData[roomEntry].scanners, scannerNumber)
  1527.             saveData("room")
  1528.             scannerListing()
  1529.             break
  1530.           end
  1531.         end
  1532.       elseif y == 17 then
  1533.         if (x > 3 and x < 6) or (x > 23 and x < 26) then --# Home / End
  1534.           scanPage = (x > 3 and x < 6) and 1 or scanPages
  1535.           unassignedScanners()
  1536.         elseif (x > 6 and x < 8) or (x > 21 and x < 23) then --# Page back / Page forward
  1537.           scanPage = (x > 6 and x < 8) and math.max(1, scanPage - 1) or math.min(scanPage + 1, scanPages)
  1538.           unassignedScanners()
  1539.         end
  1540.       else
  1541.         local sX, sY = 2, 8
  1542.         local magicNumber = (((scanPage - 1) * 7) + scanPage)
  1543.         for i = magicNumber, math.min(magicNumber + 7, #scannerList) do
  1544.           if x > sX and x < sX + 13 and y == sY then
  1545.             if #roomData[roomEntry].scanners < 4 then
  1546.               roomData[roomEntry].scanners[#roomData[roomEntry].scanners + 1] = scannerList[i]
  1547.               assignDeassignScanner(scannerList[i], true)
  1548.               saveData("room")
  1549.               scannerListing()
  1550.             end
  1551.             break
  1552.           else
  1553.             sY = sY + 2
  1554.             if sY > 14 then sX = sX + 13 sY = 8 end
  1555.           end
  1556.         end
  1557.       end
  1558.     elseif event == "mouse_scroll" then
  1559.       scanPage = data == 1 and math.min(scanPage + 1, scanPages) or math.max(1, scanPage - 1)
  1560.       unassignedScanners()
  1561.     elseif event == "key" then
  1562.       if data == keys.home or data == keys["end"] then --# Home / End
  1563.         scanPage = data == keys.home and 1 or scanPages
  1564.         unassignedScanners()
  1565.       elseif data == keys.pageUp or data == keys.pageDown then --# Page forward / back
  1566.         scanPage = data == keys.pageUp and math.min(scanPage + 1, scanPages) or math.max(1, scanPage - 1)
  1567.         unassignedScanners()
  1568.       end
  1569.     end
  1570.   end
  1571. end
  1572.  
  1573. local function selectMasterScanner()
  1574.   while true do
  1575.     local event, data, x, y = os.pullEvent()
  1576.     if event == "mouse_click" and data == 1 then
  1577.       if x > termX - 3 and y < 4 then
  1578.         if newInstall then
  1579.           kernelState = false
  1580.           return
  1581.         else
  1582.           if configData.masterScanner ~= nil then return end
  1583.         end
  1584.       end
  1585.       if newInstall and configData.masterScanner ~= nil then
  1586.         if x > 20 and x < 34 and y == termY - 1 then return end
  1587.       end
  1588.       if x > 35 and x < 48 and y == 8 then
  1589.         if configData.masterScanner then
  1590.           assignDeassignMasterScanner()
  1591.           if not newInstall then saveData("config") end
  1592.           scannerListing(true)
  1593.         end
  1594.       elseif y == 17 then
  1595.         if (x > 3 and x < 6) or (x > 23 and x < 26) then --# Home / End
  1596.           scanPage = (x > 3 and x < 6) and 1 or scanPages
  1597.           unassignedScanners()
  1598.         elseif (x > 6 and x < 8) or (x > 21 and x < 23) then --# Page Back / Page forward
  1599.           scanPage = (x > 6 and x < 8) and math.max(1, scanPage - 1) or math.min(scanPage + 1, scanPages)
  1600.           unassignedScanners()
  1601.         end
  1602.       else
  1603.         local sX, sY = 2, 8
  1604.         local magicNumber = (((scanPage - 1) * 7) + scanPage)
  1605.         for i = magicNumber, math.min(magicNumber + 7, #scannerList) do
  1606.           if x > sX and x < sX + 11 and y == sY then
  1607.             if not configData.masterScanner then
  1608.               configData.masterScanner = scannerList[i]
  1609.               assignDeassignMasterScanner(true)
  1610.               if not newInstall then saveData("config") end
  1611.               scannerListing(true)
  1612.             end
  1613.             break
  1614.           else
  1615.             sY = sY + 2
  1616.             if sY > 14 then sX = sX + 13 sY = 8 end
  1617.           end
  1618.         end
  1619.       end
  1620.     elseif event == "mouse_scroll" then
  1621.       scanPage = data == 1 and math.min(scanPage + 1, scanPages) or math.max(scanPage - 1, 1)
  1622.       unassignedScanners()
  1623.     elseif event == "key" then
  1624.       if data == keys.home or data == keys["end"] then --# Home / End
  1625.         scanPage = data == keys.home and 1 or scanPages
  1626.         unassignedScanners()
  1627.       elseif data == keys.pageUp or data == keys.pageDown then --# Page forward / back
  1628.         scanPage = data == keys.pageUp and math.min(scanPage + 1, scanPages) or math.max(1, scanPage - 1)
  1629.         unassignedScanners()
  1630.       end
  1631.     end
  1632.   end
  1633. end
  1634.  
  1635. local function resetRoom(_delete)
  1636.   roomData[roomEntry].checkIn.ciTime = 0
  1637.   roomData[roomEntry].checkIn.ciDay = 0
  1638.   roomData[roomEntry].checkOut.coTime = 0
  1639.   roomData[roomEntry].checkOut.coDay = 0
  1640.   numDays = 0
  1641.   for i = #roomData[roomEntry].guests, 1, -1 do
  1642.     removeGuest(i, _delete)
  1643.   end
  1644. end
  1645.  
  1646. local function clearRoomStatusMenu()
  1647.   local yPos = 4
  1648.   term.setBackgroundColor(colors.black)
  1649.   local line = string.rep(" ", 15)
  1650.   for i = 1, 10 do
  1651.     yPos = yPos + 1
  1652.     term.setCursorPos(28, yPos)
  1653.     term.write(line)
  1654.   end
  1655. end
  1656.  
  1657. local function selectRoomStatus(_quick)
  1658.   local yPos, stateChanged = 4, false
  1659.   term.setBackgroundColor(colors.lightGray)
  1660.   if _quick then
  1661.     local line = string.rep(" ", 15)
  1662.     for i = 1, 10 do
  1663.       yPos = yPos + 1
  1664.       term.setCursorPos(28, yPos)
  1665.       term.write(line)
  1666.     end
  1667.   end
  1668.   yPos = 5
  1669.   term.setBackgroundColor(colors.gray)
  1670.   local line = string.rep(" ", 13)
  1671.   for i = 1, 8 do
  1672.     yPos = yPos + 1
  1673.     term.setCursorPos(29, yPos)
  1674.     term.write(line)
  1675.   end
  1676.   yPos = 6
  1677.   for k, v in pairs(roomColors) do
  1678.     yPos = yPos + 1
  1679.     term.setCursorPos(30, yPos)
  1680.     term.setTextColor(v)
  1681.     term.write(k)
  1682.   end
  1683.   yPos = 6
  1684.   local _, button, x, y = os.pullEvent("mouse_click")
  1685.   for k, v in pairs(roomColors) do
  1686.     yPos = yPos + 1
  1687.     if x > 29 and x < 41 and y == yPos and button == 1 then
  1688.       if roomData[roomEntry].state ~= k then
  1689.         stateChanged = true
  1690.       end
  1691.       roomData[roomEntry].state = k
  1692.       break
  1693.     end
  1694.   end
  1695.   if roomData[roomEntry].state == "occupied" and stateChanged then
  1696.     if roomData[roomEntry].checkIn.ciTime == 0 then
  1697.       roomData[roomEntry].checkIn.ciTime = textutils.formatTime(os.time(), false)
  1698.       roomData[roomEntry].checkIn.ciDay = os.day()
  1699.     end
  1700.     for i = 1, #roomData[roomEntry].guests do
  1701.       addGuestToScanners(roomData[roomEntry].guests[i].name, roomData[roomEntry].guests[i].idPrint)
  1702.     end
  1703.     roomData[roomEntry].checkOut.coTime = 0
  1704.     roomData[roomEntry].checkOut.coDay = 0
  1705.     numDays = 0
  1706.   elseif roomData[roomEntry].state == "checkout" and stateChanged then
  1707.     roomData[roomEntry].checkOut.coTime = textutils.formatTime(os.time(), false)
  1708.     roomData[roomEntry].checkOut.coDay = os.day()
  1709.     numDays = roomData[roomEntry].checkOut.coDay - roomData[roomEntry].checkIn.ciDay
  1710.     for i = 1, #roomData[roomEntry].guests do
  1711.       removeGuest(i) --# remove guests from scanners
  1712.     end
  1713.   elseif roomData[roomEntry].state == "reserved" and stateChanged then
  1714.     resetRoom()
  1715.   elseif roomData[roomEntry].state == "available" and stateChanged then
  1716.     resetRoom(true)
  1717.     deleteNotes()
  1718.   elseif roomData[roomEntry].state == "unavailable" and stateChanged then
  1719.     resetRoom(true)
  1720.   end
  1721.   saveData("room")
  1722.   if _quick then clearRoomStatusMenu() roomList() else roomInfo() end
  1723. end
  1724.  
  1725. local function changeRoomNumber()
  1726.   term.setBackgroundColor(colors.lightGray)
  1727.   term.setTextColor(colors.white)
  1728.   term.setCursorPos(11, 5)
  1729.   term.write("   ")
  1730.   term.setCursorPos(11, 5)
  1731.   local newRoomNum = tonumber(read(nil, nil, 3))
  1732.   if newRoomNum and newRoomNum > 0 then
  1733.     local found = false
  1734.     for i = 1, #roomData do
  1735.       if roomData[i].roomNumber == newRoomNum then
  1736.         found = true
  1737.       end
  1738.     end
  1739.     if not found then
  1740.       roomData[roomEntry].roomNumber = newRoomNum
  1741.       saveData("room")
  1742.     end
  1743.   end
  1744. end
  1745.  
  1746. local function moveRoom()
  1747.   term.setBackgroundColor(colors.lightGray)
  1748.   term.setTextColor(colors.white)
  1749.   term.setCursorPos(17, 5)
  1750.   term.write("   ")
  1751.   term.setCursorPos(17, 5)
  1752.   local newPos = tonumber(read(nil, nil, 3))
  1753.   if newPos and newPos > 0 and newPos <= #roomData and newPos ~= roomEntry then
  1754.     local tempRoomData = { }
  1755.     for k, v in pairs(roomData[roomEntry]) do
  1756.       tempRoomData[k] = v
  1757.     end
  1758.     table.remove(roomData, roomEntry)
  1759.     table.insert(roomData, newPos, tempRoomData)
  1760.     roomEntry = newPos
  1761.     saveData("room")
  1762.   end
  1763. end
  1764.  
  1765. local function deleteRoom()
  1766.   for i = #roomData[roomEntry].scanners, 1, -1 do
  1767.     assignDeassignScanner(roomData[roomEntry].scanners[i])
  1768.   end
  1769.   table.remove(roomData, roomEntry)
  1770.   saveData("room")
  1771. end
  1772.  
  1773. local function createRoom(roomNum)
  1774.   roomData[#roomData + 1] = { }
  1775.   roomData[#roomData]["roomNumber"] = roomNum
  1776.   roomData[#roomData]["scanners"] = { }
  1777.   roomData[#roomData]["notes"] = { "Assign scanners" }
  1778.   roomData[#roomData]["guests"] = { }
  1779.   roomData[#roomData]["checkIn"] = { ciTime = 0, ciDay = 0 }
  1780.   roomData[#roomData]["checkOut"] = { coTime = 0, coDay = 0 }
  1781.   roomData[#roomData]["dailyRate"] = 5
  1782.   roomData[#roomData]["state"] = "unavailable"
  1783. end
  1784.  
  1785. local function newRoom()
  1786.   local highest = 1
  1787.   for i = 1, #roomData do
  1788.     highest = math.max(highest, roomData[i].roomNumber)
  1789.   end
  1790.   createRoom(highest + 1)
  1791.   roomEntry = #roomData
  1792.   saveData("room")
  1793.   clearLowerScreen(colors.white)
  1794.   roomInfo()
  1795. end
  1796.  
  1797. local function cancelTimer(button)
  1798.   confirmState = false
  1799.   confirmTimer = nil
  1800.   if button and #roomData > 1 and (roomData[roomEntry].state == "available" or roomData[roomEntry].state == "unavailable") then
  1801.     term.setTextColor(colors.white)
  1802.     term.setBackgroundColor(colors.red)
  1803.     term.setCursorPos(34, termY)
  1804.     term.write(" Delete Room ")
  1805.   end
  1806. end
  1807.  
  1808. local function pageInput()
  1809.   term.setCursorPos(33, termY - 2)
  1810.   local newPage = tonumber(read(nil, nil, 4))
  1811.   if newPage then
  1812.     newPage = math.max(1, newPage)
  1813.     newPage = math.min(newPage, numPages)
  1814.     pageNum = newPage
  1815.   end
  1816.   clearDataArea()
  1817.   roomList()
  1818. end
  1819.  
  1820. local function selectPage()
  1821.   term.setBackgroundColor(colors.blue)
  1822.   term.setTextColor(colors.white)
  1823.   term.setCursorPos(31, termY - 4)
  1824.   term.write(" :Page: ")
  1825.   term.setBackgroundColor(colors.gray)
  1826.   local line = string.rep(" ", 8)
  1827.   for i = termY - 3, termY - 1 do
  1828.     term.setCursorPos(31, i)
  1829.     term.write(line)
  1830.   end
  1831.   term.setBackgroundColor(colors.lightGray)
  1832.   term.setCursorPos(32, termY - 2)
  1833.   term.write(string.rep(" ", 6))
  1834.   pageInput()
  1835. end
  1836.  
  1837. local function printerMessage(message)
  1838.   term.setBackgroundColor(colors.blue)
  1839.   term.setTextColor(colors.white)
  1840.   term.setCursorPos(17, 7)
  1841.   term.write(string.rep(" ", 19))
  1842.   term.setCursorPos(23, 7)
  1843.   term.write("Printer")
  1844.   term.setBackgroundColor(colors.gray)
  1845.   local line = string.rep(" ", 19)
  1846.   for i = 8, 10 do
  1847.     term.setCursorPos(17, i)
  1848.     term.write(line)
  1849.   end
  1850.   term.setCursorPos(math.ceil(termX / 2) - math.floor((#message) / 2), 9)
  1851.   term.write(message)
  1852. end
  1853.  
  1854. local function screenInvoice(iDate)
  1855.   clearLowerScreen(colors.white)
  1856.   term.setTextColor(colors.gray)
  1857.   local iRoom = "Room " .. tostring(roomData[roomEntry].roomNumber)
  1858.   term.setCursorPos(2, 5)
  1859.   term.write(iRoom)
  1860.   term.setCursorPos(termX - (#iDate + 4), 5)
  1861.   term.write("Day " .. iDate)
  1862.   term.setTextColor(colors.black)
  1863.   term.setCursorPos(2, 6)
  1864.   term.write("Guests:")
  1865.   term.setBackgroundColor(colors.lightGray)
  1866.   local line = string.rep(" ", 41)
  1867.   for i = 6, termY - 4 do
  1868.     term.setCursorPos(10, i)
  1869.     term.write(line)
  1870.   end
  1871.   local yOffset = 5
  1872.   for i = 1, #roomData[roomEntry].guests do
  1873.     term.setTextColor(colors.black)
  1874.     term.setCursorPos(11, 1 + yOffset)
  1875.     term.write(roomData[roomEntry].guests[i].name)
  1876.     term.setTextColor(colors.gray)
  1877.     term.setCursorPos(12, 2 + yOffset)
  1878.     term.write(roomData[roomEntry].guests[i].idPrint)
  1879.     yOffset = yOffset + 2
  1880.   end
  1881.   term.setBackgroundColor(colors.white)
  1882.   term.setTextColor(colors.lightGray)
  1883.   term.setCursorPos(2, termY - 2)
  1884.   term.write(numDays .. " days @ " .. roomData[roomEntry].dailyRate .. " credits/day")
  1885.   term.setCursorPos(2, termY - 1)
  1886.   term.write("Total: ")
  1887.   term.setTextColor(colors.black)
  1888.   term.write(tostring(roomData[roomEntry].dailyRate * numDays):sub(1, termX - 8) .. " credits")
  1889.   if printer then
  1890.     term.setBackgroundColor(colors.yellow)
  1891.     term.setCursorPos(35, termY - 1)
  1892.     term.write(" P r i n t ")
  1893.   end
  1894. end
  1895.  
  1896. local function printInvoice(iDate)
  1897.   local numPaper = printer.getPaperLevel()
  1898.   local numInk = printer.getInkLevel()
  1899.   if numPaper > 0 and numInk > 0 then
  1900.     if printer.newPage() then
  1901.       printerMessage("Invoice: Printing")
  1902.       printer.setPageTitle(configData.facilityName .. " Invoice")
  1903.       local pX, pY = printer.getPageSize()
  1904.       printer.setCursorPos(1, 1)
  1905.       printer.write(configData.facilityName .. " Invoice")
  1906.       printer.setCursorPos(1, 3)
  1907.       printer.write("Room " .. tostring(roomData[roomEntry].roomNumber))
  1908.       printer.setCursorPos(1, 4)
  1909.       printer.write("Day " .. iDate)
  1910.       printer.setCursorPos(1, 6)
  1911.       printer.write("Guests...")
  1912.       local yOffset = 7
  1913.       for i = 1, #roomData[roomEntry].guests do
  1914.         printer.setCursorPos(1, 1 + yOffset)
  1915.         printer.write(roomData[roomEntry].guests[i].name)
  1916.         printer.setCursorPos(2, 2 + yOffset)
  1917.         printer.write(roomData[roomEntry].guests[i].idPrint)
  1918.         yOffset = yOffset + 2
  1919.       end
  1920.       printer.setCursorPos(2, pY - 2)
  1921.       printer.write(numDays .. " days @ " .. roomData[roomEntry].dailyRate .. " cr/day")
  1922.       printer.setCursorPos(2, pY -1 )
  1923.       printer.write("Total: " .. tostring(roomData[roomEntry].dailyRate * numDays):sub(1, pX - 8) .. " credits")
  1924.       if printer.endPage() then
  1925.         printerMessage("Invoice Printed")
  1926.         os.pullEvent("mouse_click")
  1927.         screenInvoice(iDate)
  1928.         return
  1929.       else
  1930.         printerMessage("Paper jam")
  1931.         os.pullEvent("mouse_click")
  1932.         screenInvoice(iDate)
  1933.       end
  1934.     else
  1935.       printerMessage("Paper jam")
  1936.       os.pullEvent("mouse_click")
  1937.       screenInvoice(iDate)
  1938.     end
  1939.   else
  1940.     local errorMessage = "Resources"
  1941.     if numPaper < 1 then errorMessage = "Out of paper!" end
  1942.     if numInk < 1 then errorMessage = "Out of ink!" end
  1943.     printerMessage(errorMessage)
  1944.     os.pullEvent("mouse_click")
  1945.     screenInvoice(iDate)
  1946.   end
  1947. end
  1948.  
  1949. local function viewInvoice(iDate)
  1950.   screenInvoice(iDate)
  1951.   while true do
  1952.     local _, button, x, y = os.pullEvent("mouse_click")
  1953.     if x > termX - 3 and y < 4 and button == 1 then
  1954.       clearLowerScreen(colors.white)
  1955.       roomInfo()
  1956.       return
  1957.     elseif x > 34 and x < 46 and y == termY - 1 and button == 1 and printer then
  1958.       printInvoice(iDate)
  1959.     end
  1960.   end
  1961. end
  1962.  
  1963. local function changeDailyRate()
  1964.   term.setTextColor(colors.white)
  1965.   term.setBackgroundColor(colors.lightGray)
  1966.   term.setCursorPos(43, termY - 2)
  1967.   term.write("   ")
  1968.   term.setCursorPos(43, termY - 2)
  1969.   local newRate = tonumber(read(nil, nil, 3))
  1970.   if newRate and newRate > 0 then
  1971.     roomData[roomEntry].dailyRate = newRate
  1972.     saveData("room")
  1973.   end
  1974.   term.setCursorPos(43, termY - 2)
  1975.   term.write("   ")
  1976.   local rateStr = tostring(roomData[roomEntry].dailyRate)
  1977.   term.setCursorPos(46 - #rateStr, termY - 2)
  1978.   term.write(rateStr)
  1979. end
  1980.  
  1981. local function roomInput()
  1982.   local event, data, x, y = os.pullEvent()
  1983.   if event == "mouse_click" and data == 1 then
  1984.     if x > termX - 3 and y < 4 then                    --# X / Quit
  1985.       cancelTimer()
  1986.       clearLowerScreen()
  1987.       roomList()
  1988.       leftPane()
  1989.     elseif x > 9 and x < 15 and y == 5 then            --# change room number
  1990.       cancelTimer(true)
  1991.       changeRoomNumber()
  1992.       roomInfoLabels()
  1993.       roomInfoData()
  1994.       roomInfoButtons()
  1995.     elseif x == 16 and y == 5 then                     --# move room
  1996.       cancelTimer(true)
  1997.       moveRoom(roomEntry)
  1998.       roomInfoLabels()
  1999.       roomInfoData()
  2000.       roomInfoButtons()
  2001.     elseif x > 28 and x < 43 and y == 5 then           --# change room status
  2002.       cancelTimer(true)
  2003.       selectRoomStatus()
  2004.     elseif x == 10 and y == 7 and #roomData[roomEntry].guests < 5 and roomData[roomEntry].state ~= "unavailable" and roomData[roomEntry].state ~= "service" and roomData[roomEntry].state ~= "checkout" then --# add guest
  2005.       cancelTimer(true)
  2006.       if roomData[roomEntry].state == "available" then roomData[roomEntry].state = "reserved" end
  2007.       viewGuestInfo(0)
  2008.     elseif x == 38 and y == 7 and #roomData[roomEntry].notes < 5 then --# add note
  2009.       cancelTimer(true)
  2010.       viewNote(0)
  2011.     elseif x > termX - 3 and y > 5 and y < 16 then     --# select scanners
  2012.       clearLowerScreen(colors.white)
  2013.       cancelTimer()
  2014.       scannerListing()
  2015.       selectScanners()
  2016.       roomInfo()
  2017.     elseif x > 2 and x < 20 and y > 9 and y < 15 then  --# select or delete guest
  2018.       if x == 19 and y - 9 <= #roomData[roomEntry].guests then --# delete
  2019.         cancelTimer(true)
  2020.         removeGuest(y - 9, true)
  2021.         roomInfoLabels()
  2022.         roomInfoData()
  2023.         roomInfoButtons()
  2024.       elseif y - 9 <= #roomData[roomEntry].guests then --# view guest info
  2025.         cancelTimer(true)
  2026.         viewGuestInfo(y - 9)
  2027.       end
  2028.     elseif x > 23 and x < 47 and y > 9 and y < 15 then --# select or delete note/message
  2029.       if x == 46 and y - 9 <= #roomData[roomEntry].notes then --# delete
  2030.         cancelTimer(true)
  2031.         deleteNote(y - 9)
  2032.       elseif y - 9 <= #roomData[roomEntry].notes then  --# view notes/messages
  2033.         cancelTimer(true)
  2034.         viewNote(y - 9)
  2035.       end
  2036.     elseif y == termY - 2 and x > 41 and x < 47 and roomData[roomEntry].state ~= "checkout" and roomData[roomEntry].state ~= "occupied" and roomData[roomEntry].state ~="reserved" and roomData[roomEntry].state ~= "service" then --# change rate
  2037.       cancelTimer(true)
  2038.       changeDailyRate()
  2039.     elseif y == termY - 2 and x > 33 and x < 47 and roomData[roomEntry].state == "checkout" then --# invoice
  2040.       local iDate = roomData[roomEntry].checkOut.coDay .. " @ " .. roomData[roomEntry].checkOut.coTime
  2041.       cancelTimer()
  2042.       viewInvoice(iDate)
  2043.     elseif y == termY and x > 33 and x < 47 then       --# delete room
  2044.       if confirmState then
  2045.         deleteRoom(roomEntry)
  2046.         confirmTimer = nil
  2047.         confirmState = false
  2048.         clearLowerScreen()
  2049.         roomList()
  2050.         leftPane()
  2051.       else
  2052.         if #roomData > 1 and (roomData[roomEntry].state == "available" or roomData[roomEntry].state == "unavailable") then
  2053.           confirmTimer = os.startTimer(1.5)
  2054.           confirmState = true
  2055.           term.setTextColor(colors.white)
  2056.           term.setBackgroundColor(colors.orange)
  2057.           term.setCursorPos(34, termY)
  2058.           term.write("   CONFIRM   ")
  2059.         end
  2060.       end
  2061.     end
  2062.   elseif event == "timer" and data == confirmTimer then
  2063.     cancelTimer(true)
  2064.   end
  2065. end
  2066.  
  2067. local function guestSearchInput()
  2068.   term.setBackgroundColor(colors.white)
  2069.   term.setTextColor(colors.gray)
  2070.   term.setCursorPos(2, 11)
  2071.   local sWord = read(nil, nil, 15)
  2072.   if sWord ~= "" then
  2073.     guestSearch(sWord)
  2074.   end
  2075. end
  2076.  
  2077. local function settingsInput()
  2078.   local event, data, x, y = os.pullEvent()
  2079.   if event == "mouse_click" and data == 1 then
  2080.     if x > termX - 3 and y < 4 then          --# X / Quit
  2081.       clearLowerScreen()
  2082.       roomList()
  2083.       leftPane()
  2084.     elseif x > 18 and x < 36 and y == 5 then --# select master scanner
  2085.       clearLowerScreen(colors.white)
  2086.       scannerListing(true)
  2087.       selectMasterScanner()
  2088.       settingsScreen()
  2089.     elseif x > 18 and x < 36 and y == 7 then --# change master scanner password
  2090.       changeMasterPassword()
  2091.     elseif x > 18 and x < 36 and y == 9 then --# change screen lock password
  2092.       changeScreenPassword()
  2093.     elseif x == 25 and y == 11 then          --# add new staff member
  2094.       viewStaffInfo(0)
  2095.     elseif y == termY - 1 then
  2096.       if (x > 8 and x < 11) or (x > 26 and x < 29) then --# Home / End
  2097.         staffPage = (x > 8 and x < 11) and 1 or staffPages
  2098.         staffInfo()
  2099.       elseif (x > 11 and x < 13) or (x > 24 and x < 26) then --# Page back / Page forward
  2100.         staffPage = (x > 11 and x < 13) and math.max(1, staffPage - 1) or math.min(staffPage + 1, staffPages)
  2101.         staffInfo()
  2102.       end
  2103.     else                                     --# staff member selection
  2104.       local sX, sY = 3, 14
  2105.       local magicNumber = (((staffPage - 1) * 3) + staffPage)
  2106.       for i = magicNumber, math.min(magicNumber + 3, #configData.staff) do
  2107.         if x >= sX and x <= sX + 15 and y == sY then
  2108.           viewStaffInfo(i)
  2109.           break
  2110.         else
  2111.           sY = sY + 2
  2112.           if sY > termY - 2 then sX = sX + 17 sY = 14 end
  2113.         end
  2114.       end
  2115.     end
  2116.   elseif event == "mouse_scroll" then
  2117.     staffPage = data == 1 and math.min(staffPage + 1, staffPages) or math.max(1, staffPage - 1)
  2118.     staffInfo()
  2119.   elseif event == "key" then
  2120.     if data == keys.home or data == keys["end"] then --# Home / End
  2121.       staffPage = data == keys.home and 1 or staffPages
  2122.       staffInfo()
  2123.     elseif data == keys.pageUp or data == keys.pageDown then --# Page forward / back
  2124.       staffPage = data == keys.pageUp and math.min(staffPage + 1, staffPages) or math.max(1, staffPage - 1)
  2125.       staffInfo()
  2126.     end
  2127.   end
  2128. end
  2129.  
  2130. local function lockInput()
  2131.   while true do
  2132.     term.setCursorPos(23, 11)
  2133.     local unlockPass = read("*", nil, 15, true, true)
  2134.     term.setCursorPos(23, 11)
  2135.     term.write(string.rep(" ", 15))
  2136.     if unlockPass == configData.screenPass then
  2137.       clearLowerScreen()
  2138.       roomList()
  2139.       leftPane()
  2140.       return
  2141.     end
  2142.   end
  2143. end
  2144.  
  2145. local function mainInput()
  2146.   local event, data, x, y = os.pullEvent()
  2147.   if event == "mouse_click" then
  2148.     if x > termX - 3 and y < 4 then --# big X
  2149.       if data == 1 then             --# quit
  2150.         kernelState = false
  2151.         clearScreen()
  2152.         term.setCursorPos(1, 1)
  2153.         return
  2154.       elseif data == 2 then         --# Lock
  2155.         lockScreen()
  2156.       end
  2157.     elseif x < 17 and data == 1 then
  2158.       if y == 11 then
  2159.         guestSearchInput()          --# guest search
  2160.       elseif y == 13 and #roomData < 999 then
  2161.         newRoom()                   --# add a room
  2162.       elseif y == 15 then
  2163.         settingsScreen()            --# settings
  2164.       end
  2165.     elseif y == termY then
  2166.       if x > 28 and x < 38 and y == termY and numPages > 1 and data == 1 then
  2167.         selectPage()                --# page selection
  2168.       elseif (x > 23 and x < 25) or (x > 43 and x < 46) then --# home / end
  2169.         pageNum = (x > 23 and x < 25) and 1 or numPages
  2170.         if pageNum == numPages and numPages > 1 then clearDataArea() end
  2171.         roomList()
  2172.       elseif (x > 25 and x < 28) or (x > 41 and x < 43) then --# page - / page +
  2173.         pageNum = (x > 25 and x < 28) and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
  2174.         if pageNum == numPages and numPages > 1 then clearDataArea() end
  2175.         roomList()
  2176.       end
  2177.     else
  2178.       local sX, sY = 20, 5          --# select a room
  2179.       local magicNumber = (((pageNum - 1) * 27) + pageNum)
  2180.       for i = magicNumber, math.min(magicNumber + 27, #roomData) do
  2181.         if x >= sX and x <= sX + 5 and y == sY then
  2182.           roomEntry = i
  2183.           if data == 1 then
  2184.             roomInfo()
  2185.             break
  2186.           elseif data == 2 then
  2187.             selectRoomStatus(true)
  2188.             break
  2189.           end
  2190.         else
  2191.           sY = sY + 2
  2192.           if sY > termY - 1 then sX = sX + 8 sY = 5 end
  2193.         end
  2194.       end
  2195.     end
  2196.   elseif event == "mouse_scroll" then
  2197.     pageNum = data == 1 and math.min(pageNum + 1, numPages) or math.max(1, pageNum - 1)
  2198.     if pageNum == numPages and numPages > 1 then clearDataArea() end
  2199.     roomList()
  2200.   elseif event == "key" then
  2201.     if data == keys.f1 then
  2202.       helpScreen()
  2203.     elseif data == keys.home or data == keys["end"] then --# Home / End
  2204.       pageNum = data == keys.home and 1 or numPages
  2205.       if pageNum == numPages and numPages > 1 then clearDataArea() end
  2206.       roomList()
  2207.     elseif data == keys.pageUp or data == keys.pageDown then --# Page forward / Page back
  2208.       pageNum = data == keys.pageUp and math.max(1, pageNum - 1) or math.min(pageNum + 1, numPages)
  2209.       roomList()
  2210.     end
  2211.   end
  2212. end
  2213.  
  2214. do
  2215.   local input = {
  2216.     lock = function() lockInput() end,
  2217.     main = function() mainInput() end,
  2218.     room = function() roomInput() end,
  2219.     settings = function() settingsInput() end,
  2220.   }
  2221.  
  2222.   innKeeperKernel = function()
  2223.     while kernelState do
  2224.       if input[screen] then input[screen]() end
  2225.     end
  2226.   end
  2227. end
  2228.  
  2229. local function cancelFirstRun()
  2230.   clearScreen()
  2231.   term.setTextColor(colors.red)
  2232.   term.setCursorPos(1, 1)
  2233.   term.write("Innkeeper setup cancelled...")
  2234.   term.setTextColor(colors.white)
  2235.   term.setCursorPos(1, 4)
  2236.   return false
  2237. end
  2238.  
  2239. local function firstRun()
  2240.   if not os.getComputerLabel() then os.setComputerLabel("InnKeeper") end
  2241.   local gotSettings = fs.exists("/data/innSettings")
  2242.   header()
  2243.   clearLowerScreen(colors.white) --# Welcome
  2244.   term.setTextColor(colors.blue)
  2245.   term.setCursorPos(13, 6)
  2246.   term.write("Welcome to InnKeeper Setup!")
  2247.   term.setCursorPos(6, 8)
  2248.   term.write("The next few pages will walk you through")
  2249.   term.setCursorPos(7, 10)
  2250.   term.write("setting up InnKeeper for your facility.")
  2251.   term.setTextColor(colors.white)
  2252.   term.setCursorPos(22, 14)
  2253.   term.setBackgroundColor(colors.green)
  2254.   term.write("  Start  ")
  2255.   while true do
  2256.     local _, button, x, y = os.pullEvent("mouse_click")
  2257.     if x > termX - 3 and y < 4 and button == 1 then
  2258.       return cancelFirstRun()
  2259.     elseif x > 21 and x < 31 and y == 14 and button == 1 then
  2260.       break
  2261.     end
  2262.   end
  2263.   if not gotSettings then
  2264.     clearLowerScreen(colors.white) --# Name
  2265.     term.setTextColor(colors.blue)
  2266.     term.setCursorPos(2, 5)
  2267.     term.write("Please choose a name for this facility...")
  2268.     term.setTextColor(colors.lightGray)
  2269.     term.setCursorPos(2, 9)
  2270.     term.write("Enter nothing to exit setup")
  2271.     term.setTextColor(colors.white)
  2272.     term.setBackgroundColor(colors.gray)
  2273.     term.setCursorPos(2, 7)
  2274.     term.write(string.rep(" ", 46))
  2275.     term.setCursorPos(3, 7)
  2276.     configData.facilityName = read(nil, nil, 44, nil, true)
  2277.     if configData.facilityName == "" then return cancelFirstRun() end
  2278.     clearLowerScreen(colors.white) --# Screen-lock password
  2279.     term.setTextColor(colors.blue)
  2280.     term.setCursorPos(5, 5)
  2281.     term.write("Please enter a screen lock password")
  2282.     term.setTextColor(colors.gray)
  2283.     term.setCursorPos(5, 7)
  2284.     term.write("Program access is limited by this password")
  2285.     term.setTextColor(colors.lightGray)
  2286.     term.setCursorPos(14, 12)
  2287.     term.write("Enter nothing to exit setup")
  2288.     term.setBackgroundColor(colors.gray)
  2289.     term.setTextColor(colors.white)
  2290.     term.setCursorPos(14, 9)
  2291.     term.write(string.rep(" ", 27))
  2292.     term.setCursorPos(15, 9)
  2293.     configData.screenPass = read(nil, nil, 25, nil, true)
  2294.     if configData.screenPass == "" then return cancelFirstRun() end
  2295.   end
  2296.   if not fs.exists("/data/innRooms") then
  2297.     clearLowerScreen(colors.white) --# Number of rooms
  2298.     term.setTextColor(colors.blue)
  2299.     term.setCursorPos(2, 5)
  2300.     term.write("How many rooms will InnKeeper be managing? ")
  2301.     term.setBackgroundColor(colors.gray)
  2302.     term.write("     ")
  2303.     term.setBackgroundColor(colors.white)
  2304.     term.setTextColor(colors.gray)
  2305.     term.setCursorPos(2, 7)
  2306.     term.write("You may add/remove rooms at any time")
  2307.     term.setTextColor(colors.lightGray)
  2308.     term.setCursorPos(2, 9)
  2309.     term.write("Enter nothing to exit setup")
  2310.     term.setBackgroundColor(colors.gray)
  2311.     term.setTextColor(colors.white)
  2312.     term.setCursorPos(46, 5)
  2313.     local numRooms = tonumber(read(nil, nil, 3, nil, true))
  2314.     if not numRooms then return cancelFirstRun() end
  2315.     for i = 1, numRooms do createRoom(i) end
  2316.     numPages = math.ceil(#roomData / 28)
  2317.   end
  2318.   if not gotSettings then
  2319.     clearLowerScreen(colors.white) --# Redsdtone
  2320.     term.setTextColor(colors.blue)
  2321.     term.setCursorPos(4, 5)
  2322.     term.write("Is redstone ")
  2323.     term.setTextColor(colors.green)
  2324.     term.write("ON")
  2325.     term.setTextColor(colors.blue)
  2326.     term.write(" or ")
  2327.     term.setTextColor(colors.red)
  2328.     term.write("OFF")
  2329.     term.setTextColor(colors.blue)
  2330.     term.write(" when doors are CLOSED?")
  2331.     term.setTextColor(colors.gray)
  2332.     term.setCursorPos(9, 7)
  2333.     term.write("Standard doors are ")
  2334.     term.setTextColor(colors.red)
  2335.     term.write("OFF")
  2336.     term.setTextColor(colors.gray)
  2337.     term.write(" when closed")
  2338.     term.setCursorPos(3, 8)
  2339.     term.write("Pistons/Forcefields are ")
  2340.     term.setTextColor(colors.green)
  2341.     term.write("ON")
  2342.     term.setTextColor(colors.gray)
  2343.     term.write(" when extended/closed")
  2344.     term.setCursorPos(4, 10)
  2345.     term.write("If this facility requires the ")
  2346.     term.setTextColor(colors.green)
  2347.     term.write("ON")
  2348.     term.setTextColor(colors.gray)
  2349.     term.write(" setting, use")
  2350.     term.setCursorPos(11, 11)
  2351.     term.write("only a single scanner per door")
  2352.     term.setTextColor(colors.white)
  2353.     term.setBackgroundColor(colors.green)
  2354.     term.setCursorPos(16, 13)
  2355.     term.write("   O N   ")
  2356.     term.setCursorPos(28, 13)
  2357.     term.setBackgroundColor(colors.red)
  2358.     term.write("  O F F  ")
  2359.     while true do
  2360.       local _, button, x, y = os.pullEvent("mouse_click")
  2361.       if x > termX - 3 and y < 4 and button == 1 then
  2362.         return cancelFirstRun()
  2363.       elseif y == 13 and button == 1 then
  2364.         if x > 15 and x < 24 then
  2365.           configData.redstone = true
  2366.           break
  2367.         elseif x > 27 and x < 37 then
  2368.           configData.redstone = false
  2369.           break
  2370.         end
  2371.       end
  2372.     end
  2373.     clearLowerScreen(colors.white) --# Master scanner password
  2374.     term.setTextColor(colors.blue)
  2375.     term.setCursorPos(5, 5)
  2376.     term.write("Please enter a system-wide scanner password")
  2377.     term.setTextColor(colors.gray)
  2378.     term.setCursorPos(5, 7)
  2379.     term.write("All scanners are locked with this password")
  2380.     term.setTextColor(colors.lightGray)
  2381.     term.setCursorPos(14, 12)
  2382.     term.write("Enter nothing to exit setup")
  2383.     term.setBackgroundColor(colors.gray)
  2384.     term.setTextColor(colors.white)
  2385.     term.setCursorPos(14, 9)
  2386.     term.write(string.rep(" ", 27))
  2387.     term.setCursorPos(15, 9)
  2388.     configData.masterPass = read(nil, nil, 25, nil, true)
  2389.     if configData.masterPass == "" then return cancelFirstRun() end
  2390.     local scanner
  2391.     for i = #bioScanners, 1, -1 do --# prepare scanners
  2392.       scanner = peripheral.wrap(bioScanners[i])
  2393.       if scanner then
  2394.         if scanner.unlock(configData.masterPass) or (scanner.lock(configData.masterPass) and scanner.unlock(configData.masterPass)) then
  2395.           for _, side in pairs(rs.getSides()) do
  2396.             scanner.forgetProgram(side)
  2397.           end
  2398.           local oldAccounts = { scanner.getLearnedNames() }
  2399.           for j = 1, #oldAccounts do
  2400.             scanner.forget(oldAccounts[j])
  2401.           end
  2402.           scanner.lock(configData.masterPass)
  2403.         else
  2404.           logScannerError(bioScanners[i], "0", "Different Password")
  2405.           table.remove(bioScanners, i)
  2406.         end
  2407.       else
  2408.         logScannerError(bioScanners[i], "0", "Scanner Missing")
  2409.         table.remove(bioScanners, i)
  2410.       end
  2411.     end
  2412.     clearLowerScreen(colors.white) --# Front desk scanner - you are about to...
  2413.     term.setTextColor(colors.blue)
  2414.     term.setCursorPos(3, 7)
  2415.     term.write("You are about to select the front desk scanner")
  2416.     term.setTextColor(colors.white)
  2417.     term.setBackgroundColor(colors.green)
  2418.     term.setCursorPos(18, 10)
  2419.     term.write(" C o n t i n u e ")
  2420.     while true do
  2421.       local _, button, x, y = os.pullEvent("mouse_click")
  2422.       if x > termX - 3 and y < 4 and button == 1 then
  2423.         return cancelFirstRun()
  2424.       elseif x > 16 and x < 34 and y == 10 and button == 1 then
  2425.         break
  2426.       end
  2427.     end
  2428.     clearLowerScreen(colors.white) --# Select front desk scanner
  2429.     scannerListing(true)
  2430.     term.setCursorPos(21, termY - 1)
  2431.     term.setBackgroundColor(colors.green)
  2432.     term.setTextColor(colors.white)
  2433.     term.write(" N E X T  >> ")
  2434.     configData.staff = { }
  2435.     selectMasterScanner()
  2436.     if not kernelState then return cancelFirstRun() end
  2437.   end
  2438.   clearLowerScreen(colors.white)   --# Outro
  2439.   term.setTextColor(colors.blue)
  2440.   term.setCursorPos(7, 6)
  2441.   term.write("Don't forget to add your staff members")
  2442.   term.setCursorPos(5, 7)
  2443.   term.write("(including yourself) in InnKeeper settings.")
  2444.   term.setTextColor(colors.white)
  2445.   term.setBackgroundColor(colors.green)
  2446.   term.setCursorPos(18, 10)
  2447.   term.write(" Start InnKeeper ")
  2448.   while true do
  2449.     local _, button, x, y = os.pullEvent("mouse_click")
  2450.     if x > termX - 3 and y < 4 and button == 1 then
  2451.       return cancelFirstRun()
  2452.     elseif x > 17 and x < 35 and y == 10 and button == 1 then
  2453.       break
  2454.     end
  2455.   end
  2456.   if not fs.exists("/data/innSettings") then saveData("config") end
  2457.   if not fs.exists("/data/innRooms") then saveData("room") end
  2458.   newInstall = false
  2459.   return true
  2460. end
  2461.  
  2462. local function quickSelectMasterScanner()
  2463.   header()
  2464.   clearLowerScreen(colors.white) --# Front desk scanner - you are about to...
  2465.   term.setTextColor(colors.blue)
  2466.   term.setCursorPos(3, 7)
  2467.   term.write("The front desk scanner is missing, or locked")
  2468.   term.setCursorPos(3, 8)
  2469.   term.write("with an unknown password.")
  2470.   term.setCursorPos(3, 10)
  2471.   term.write("Please select a replacement front desk scanner.")
  2472.   term.setTextColor(colors.white)
  2473.   term.setBackgroundColor(colors.green)
  2474.   term.setCursorPos(18, 13)
  2475.   term.write(" C o n t i n u e ")
  2476.   while true do
  2477.     local _, button, x, y = os.pullEvent("mouse_click")
  2478.     if x > termX - 3 and y < 4 and button == 1 then
  2479.       return cancelFirstRun()
  2480.     elseif x > 16 and x < 34 and y == 13 and button == 1 then
  2481.       break
  2482.     end
  2483.   end
  2484.   clearLowerScreen(colors.white)
  2485.   scannerListing(true)
  2486.   term.setCursorPos(21, termY - 1)
  2487.   term.setBackgroundColor(colors.green)
  2488.   term.setTextColor(colors.white)
  2489.   term.write(" N E X T  >> ")
  2490.   selectMasterScanner()          --# Select front desk scanner
  2491.   if kernelState then
  2492.     newInstall = false
  2493.     saveData("config")
  2494.     return true
  2495.   else
  2496.     return cancelFirstRun()
  2497.   end
  2498. end
  2499.  
  2500. local function initHardware()
  2501.   local notFind, found, perp = false, false, nil
  2502.   for _, side in pairs(rs.getSides()) do
  2503.     if peripheral.getType(side) == "biolock" then --# directly connected biometric scanners
  2504.       bioScanners[#bioScanners + 1] = side
  2505.     elseif peripheral.getType(side) == "modem" and not peripheral.call(side, "isWireless") then
  2506.       for _, name in pairs(peripheral.call(side, "getNamesRemote")) do
  2507.         if peripheral.getType(name) == "biolock" then --# remotely connected biometric scanners
  2508.           bioScanners[#bioScanners + 1] = name
  2509.         end
  2510.       end
  2511.     end
  2512.   end
  2513.   for _, side in pairs(rs.getSides()) do
  2514.     if peripheral.getType(side) == "printer" then --# directly connected printer
  2515.       printer = peripheral.wrap(side)
  2516.       break
  2517.     elseif peripheral.getType(side) == "modem" and not peripheral.call(side, "isWireless") then
  2518.       for _, name in pairs(peripheral.call(side, "getNamesRemote")) do
  2519.         if peripheral.getType(name) == "printer" then --# remotely connected printer
  2520.           printer = peripheral.wrap(name)
  2521.           found = true
  2522.           break
  2523.         end
  2524.       end
  2525.       if found then break end
  2526.     end
  2527.   end
  2528.   if not bioScanners[1] then
  2529.     notFind = true
  2530.     perp = "a Biometric Scanner"
  2531.   end
  2532.   if termX < 51 or termY < 19 then
  2533.     notFind = true
  2534.     perp = "full screen"
  2535.   end
  2536.   if not term.isColor() or pocket or turtle then
  2537.     notFind = true
  2538.     perp = "an Advanced Computer"
  2539.   end
  2540.   if notFind then
  2541.     clearScreen()
  2542.     term.setTextColor(colors.white)
  2543.     term.setCursorPos(2, 2)
  2544.     write("InnKeeper requires " .. perp)
  2545.     term.setCursorPos(1, 5)
  2546.     return false
  2547.   end
  2548.   return true
  2549. end
  2550.  
  2551. local function initScanners()
  2552.   local sides = { "top", "bottom", "left", "right", "back" }
  2553.   for i = #bioScanners, 1, -1 do
  2554.     local scanner = peripheral.wrap(bioScanners[i])
  2555.     if scanner then                   --# redundant check
  2556.       if scanner.unlock(configData.masterPass) or (scanner.lock(configData.masterPass) and scanner.unlock(configData.masterPass)) then --# unlock scanners (take posession of unlocked scanners)
  2557.         for _, side in pairs(sides) do
  2558.           scanner.forgetProgram(side) --# erase all programs
  2559.         end
  2560.         local bioNames = { scanner.getLearnedNames() }
  2561.         for j = 1, #bioNames do
  2562.           scanner.forget(bioNames[j]) --# remove all accounts
  2563.         end
  2564.         if configData.masterScanner == bioScanners[i] then --# if this is the front desk scanner...
  2565.           for j = 1, #configData.staff do
  2566.             scanner.learn(configData.staff[j].name, configData.staff[j].idPrint, 1) --# ...learn staff info
  2567.           end
  2568.         else
  2569.           for j = 1, #roomData do
  2570.             for k = 1, #roomData[j].scanners do
  2571.               if roomData[j].scanners[k] == bioScanners[i] then     --# if this scanner is a room scanner then relearn everything
  2572.                 for _, side in pairs(sides) do
  2573.                   scanner.program(side, 1, 60, configData.redstone) --# learn door program
  2574.                 end
  2575.                 for l = 1, #configData.staff do
  2576.                   scanner.learn(configData.staff[l].name, configData.staff[l].idPrint, 1)       --# learn staff info
  2577.                 end
  2578.                 if #roomData[j].guests > 0 and roomData[j].state ~= "reserved" then
  2579.                   for m = 1, #roomData[j].guests do
  2580.                     scanner.learn(roomData[j].guests[m].name, roomData[j].guests[m].idPrint, 1) --# learn guest info
  2581.                   end
  2582.                 end
  2583.               end
  2584.             end
  2585.           end
  2586.         end
  2587.         scanner.lock(configData.masterPass) --# lock the scanner
  2588.       else
  2589.         local room, found = "0", false
  2590.         if configData.masterScanner == bioScanners[i] then
  2591.           room = "Front Desk"
  2592.           configData.masterScanner = nil
  2593.         else
  2594.           for j = 1, #roomData do
  2595.             for k = #roomData[j].scanners, 1, -1 do
  2596.               if roomData[j].scanners[k] == bioScanners[i] then
  2597.                 room = "Room " .. tostring(roomData[j].roomNumber)
  2598.                 table.remove(roomData[j].scanners, k)
  2599.                 found = true
  2600.                 saveData("room")
  2601.                 break
  2602.               end
  2603.             end
  2604.             if found then break end
  2605.           end
  2606.         end
  2607.         if room ~= "Front Desk" then logScannerError(bioScanners[i], room, "Different Password") end
  2608.         table.remove(bioScanners, i)
  2609.       end
  2610.     else
  2611.       if room ~= "Front Desk" then logScannerError(bioScanners[i], room, "Scanner Missing") end
  2612.       table.remove(bioScanners, i)
  2613.     end
  2614.   end
  2615. end
  2616.  
  2617. if not initHardware() then return end
  2618. if fs.exists("/data/innSettings") then
  2619.   local configFile = fs.open("/data/innSettings", "r")
  2620.   configData = textutils.unserialize(configFile.readAll())
  2621.   configFile.close()
  2622. end
  2623. if fs.exists("/data/innRooms") then
  2624.   local roomFile = fs.open("/data/innRooms", "r")
  2625.   roomData = textutils.unserialize(roomFile.readAll())
  2626.   roomFile.close()
  2627.   numPages = math.ceil(#roomData / 28)
  2628. end
  2629. if not fs.exists("/data/innRooms") or not fs.exists("/data/innSettings") then
  2630.   newInstall = true
  2631.   if not firstRun() then return end
  2632. end
  2633. local success = true
  2634. removeMissingScanners()
  2635. initScanners()
  2636. if not configData.masterScanner then
  2637.   newInstall = true
  2638.   success = quickSelectMasterScanner()
  2639. end
  2640. if not success then return end
  2641. if scannerErrors[1] then
  2642.   header(colors.red)
  2643.   scannerErrorScreen()
  2644. end
  2645. if configData.masterScanner and kernelState then --# redundant check
  2646.   header()
  2647.   lockScreen()
  2648.   innKeeperKernel() --# main loop
  2649. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement