Advertisement
RealHero

Untitled

Apr 24th, 2021 (edited)
152
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 12.31 KB | None | 0 0
  1. -- OpenPeripheral terminal glasses demo
  2. -- by boq
  3.  
  4. local com = require("component") -- библиотека компонентов
  5. local gl = com.openperipheral_bridge -- компонент мост
  6. local event = require "event"
  7.  
  8.  
  9. if gl == nil then
  10.     print("Peripheral not found")
  11.     return
  12. end
  13.  
  14. gl.clear()
  15.  
  16. -- helper function and constants
  17. function pack(...)
  18.     return {...}
  19. end
  20.  
  21. FORMAT_CHAR = "\194\167"
  22. FORMAT_BOLD = FORMAT_CHAR .. 'l'
  23.  
  24. -- NOTE
  25. -- this program works with multiple users (i.e. multiple players attached to same terminal) so it may be more complicated then single user variant
  26. -- if it's not needed, programs may as well operate only on global surface
  27. local players = {}
  28.  
  29. local attachedCount = 0
  30. local capturedCount = 0
  31.  
  32. -- initialize global display surface
  33. local labelPlayers = gl.addText(-1, 0, "Player count: 0/0");
  34. labelPlayers.setScreenAnchor("right", "top") -- screen origin (0,0) for this object will be in top right corner
  35. labelPlayers.setObjectAnchor("right", "bottom") -- "center" of object will be on bottom right corner
  36. labelPlayers.setRotation(-90) -- object will be rotated by 90 degrees counter-clockwise around object anchor (bottom right corner)
  37.  
  38. local function initializePlayer(name, uuid)
  39.     local playerData = { name = name, uuid = uuid}
  40.  
  41.     -- now we can use private surface, visible only to owner player
  42.     playerData.surface = gl.getSurfaceByUUID(playerData.uuid)
  43.     labelPlayer = playerData.surface.addText(0, 0, "Hello " .. playerData.name)
  44.     labelPlayer.setAlignment("middle","top") -- identical as 'labelPlayer.setScreenAnchor("middle","top") labelPlayer.setObjectAnchor("middle","top")'
  45.  
  46.     -- Minecraft color codes are interpreted (http://minecraft.gamepedia.com/Formatting_codes) - but since Lua does not support Unicode, you need to use "\194\167" instead of "\x00A7"
  47.     playerData.labelWaiting = playerData.surface.addText(0, 0, FORMAT_BOLD .. "CAPTURE MODE: OFF", 0xFF0000)
  48.     playerData.labelWaiting.setAlignment("right", "bottom")
  49.  
  50.     -- add to global list
  51.     -- general hint: it's always safer to use UUID instead of names!
  52.     players[playerData.uuid] = playerData
  53. end
  54.  
  55. local function changeCounter(deltaAttached, deltaCaptured)
  56.     attachedCount = attachedCount + deltaAttached
  57.     capturedCount = capturedCount + deltaCaptured
  58.     labelPlayers.setText("Player count: " .. attachedCount .. "/" .. capturedCount)
  59. end
  60.  
  61. -- initialize already attached players
  62. for _, user in pairs(gl.getUsers()) do
  63.     initializePlayer(user.name, user.uuid)
  64.     changeCounter(1, 0)
  65. end
  66.  
  67. local timerCallbacks = {}
  68.  
  69. gl.sync() -- this operation is needed to make changes visible to player
  70. -- classic event loop
  71. while true do
  72.     evt = {event.pull()}
  73.     -- full list of glasses events: https://gist.github.com/boq/9a098b89ee102cbf1b2b
  74.     print(evt.name)
  75.     --  easy break condition - it's just ComputerCraft event
  76.     if evt[1] == "key" then
  77.         break -- exit loop
  78.     -- player started wearing glasses
  79.     elseif evt[1] == "glasses_attach" then
  80.         local name = evt[3]
  81.         local uuid = evt[4]
  82.         initializePlayer(name, uuid)
  83.         changeCounter(1, 0)
  84.     -- player logged out or took off glasses
  85.     elseif evt[1] == "glasses_detach" then
  86.         -- components on private surface are automatically cleared when player removes glasses
  87.         local playerUuid = evt[4]
  88.         players[playerUuid] = nil
  89.         changeCounter(-1,0)
  90.     -- player used wireless keyboard - computer will now start getting keyboard and mouse events
  91.     elseif evt[1] == "glasses_capture" then
  92.         changeCounter(0, 1)
  93.         local playerUuid = evt[4]
  94.         local playerData = players[playerUuid]
  95.  
  96.         if playerData then
  97.             local surface = playerData.surface
  98.             playerData.labelWaiting.setText(FORMAT_BOLD .. "CAPTURE MODE: ON")
  99.             playerData.labelWaiting.setColor(0x00FF00)
  100.  
  101.             -- middle boxes - will be used as bottons and to present click position
  102.             local function createBox(color)
  103.                 local box = surface.addBox(0,0, 25, 25, color)
  104.                 box.setScreenAnchor("middle","middle")
  105.                 box.setUserdata(color) -- userdata is variable that can be used to store anything (no type or content restrictions, except for functions). It's also not synchronized to clients
  106.                 return box
  107.             end
  108.  
  109.             createBox(0xFF0000).setObjectAnchor("left", "top")
  110.             createBox(0x00FF00).setObjectAnchor("right", "top")
  111.             createBox(0x0000FF).setObjectAnchor("right", "bottom")
  112.             createBox(0xFFFFFF).setObjectAnchor("left", "bottom")
  113.  
  114.             -- horizontal progress bar
  115.             surface.addBox(0,20, 100, 5, 0x000000).setAlignment("middle","top")
  116.  
  117.             -- vertical progress bar
  118.             surface.addBox(0,0, 5, 100, 0x000000).setAlignment("right","middle")
  119.  
  120.             playerData.horizontalMarker = surface.addBox(0, 20, 5, 5, 0xFFFFFF)
  121.             playerData.horizontalMarker.setAlignment("middle","top")
  122.  
  123.             playerData.verticalMarker = surface.addBox(0, 0, 5, 5, 0xFFFFFF)
  124.             playerData.verticalMarker.setAlignment("right","middle")
  125.  
  126.             -- pseudo-console stuff
  127.             playerData.textInput = surface.addText(0,0,">", 0x00FF00)
  128.             playerData.textInput.setScreenAnchor("left", "middle")
  129.             playerData.textInput.setObjectAnchor("left", "bottom")
  130.  
  131.             playerData.textOutput = surface.addText(0,0,"", 0x0000FF)
  132.             playerData.textOutput.setScreenAnchor("left", "middle")
  133.             playerData.textOutput.setObjectAnchor("left", "top")
  134.             playerData.textOutput.setVisible(false)
  135.  
  136.             -- mouse button indicator
  137.             surface.addBox(10, 10, 19, 9, 0xAAAAAA).setObjectAnchor("middle","top") -- button indicator background
  138.  
  139.             playerData.mouseButtons = {}
  140.  
  141.             local function createMouseButton(deltaX)
  142.                 local button = surface.addBox(10 + deltaX, 12, 5, 5, 0xFF0000)
  143.                 button.setZ(5) -- move it over box (default Z == 0)
  144.                 button.setVisible(false)
  145.                 button.setObjectAnchor("middle","top")
  146.                 return button
  147.             end
  148.  
  149.             -- mouse and keyboard codes are same as LWJGL, see http://minecraft.gamepedia.com/Key_codes
  150.  
  151.             playerData.mouseButtons[0] = createMouseButton(-5) -- left button
  152.             playerData.mouseButtons[1] = createMouseButton(5) -- right button
  153.             playerData.mouseButtons[2] = createMouseButton(0) -- middle button
  154.         end
  155.     -- player exited from keyboard GUI
  156.     elseif evt[1] == "glasses_release" then
  157.         changeCounter(0, -1)
  158.         local playerUuid = evt[4]
  159.         local playerData = players[playerUuid]
  160.  
  161.         if playerData then
  162.             -- recreate display to initial state
  163.             playerData.surface.clear()
  164.             initializePlayer(playerData.name, playerData.uuid)
  165.         end
  166.     -- player pressed mouse button anywhere in GUI
  167.     elseif evt[1] == "glasses_mouse_down" then
  168.         local playerUuid = evt[4]
  169.         local playerData = players[playerUuid]
  170.  
  171.         if playerData then
  172.             local buttonId = evt[5]
  173.             -- changing visibility is faster than recreating and removing element every time
  174.             local button = playerData.mouseButtons[buttonId]
  175.             if button then
  176.                 button.setVisible(true)
  177.             end
  178.         end
  179.     -- player released mouse button anywhere in GUI
  180.     elseif evt[1] == "glasses_mouse_up" then
  181.         local playerUuid = evt[4]
  182.         local playerData = players[playerUuid]
  183.  
  184.         local buttonId = evt[5]
  185.  
  186.         if playerData then
  187.             button = playerData.mouseButtons[buttonId]
  188.             if button then
  189.                 button.setVisible(false)
  190.             end
  191.         end
  192.  
  193.     -- player clicked on component (if you got this event, then you won't get glasses_mouse_down. Same for glasses_component_mouse_up and glasses_mouse_up)
  194.     elseif evt[1] == "glasses_component_mouse_down" then
  195.         local isPrivate = evt[6]
  196.         if isPrivate then
  197.             local playerUuid = evt[4]
  198.             local playerData = players[playerUuid]
  199.  
  200.             if playerData then
  201.                 local objectId = evt[5]
  202.                 local component = playerData.surface.getObjectById(objectId)
  203.                 local userdata = component.getUserdata()
  204.                 if userdata then -- only rectangles in middle have that value filled
  205.                     -- capture control is special object used to control few properties of keyboard GUI
  206.                     local capture = gl.getCaptureControl(playerUuid)
  207.                     capture.setBackground(userdata)
  208.                     playerData.horizontalMarker.setColor(userdata)
  209.                     playerData.verticalMarker.setColor(userdata)
  210.                 end
  211.  
  212.                 -- click position is given in pixels from top left component corner
  213.                 local clickX = evt[7]
  214.                 local clickY = evt[8]
  215.  
  216.                 -- -0.5, since anchor is in middle of screen
  217.                 playerData.horizontalMarker.setX((clickX / 25.0 - 0.5) * 100)
  218.                 playerData.verticalMarker.setY((clickY / 25.0 - 0.5) * 100)
  219.             end
  220.         end
  221.     elseif evt[1] == "glasses_key_down" then
  222.         local playerUuid = evt[4]
  223.         local playerData = players[playerUuid]
  224.  
  225.         if playerData then
  226.             local code = evt[5] -- control code, see http://minecraft.gamepedia.com/Key_codes for keymap
  227.             local char = evt[6] -- may be 0 for some control characters
  228.  
  229.             local input = playerData.textInput
  230.             local contents = input.getText()
  231.             if code == 14 then -- backspace
  232.                 if contents:len() > 1 then
  233.                     input.setText(contents:sub(0, -2))
  234.                 end
  235.             elseif code == 28 then -- enter
  236.                 input.setText(">")
  237.  
  238.                 local function setOutput(text, color)
  239.                     playerData.textOutput.setText(text)
  240.                     playerData.textOutput.setColor(color)
  241.                     playerData.textOutput.setVisible(true)
  242.                     local timerId = os.startTimer(1)
  243.                     timerCallbacks[timerId] = function ()
  244.                         playerData.textOutput.setVisible(false)
  245.                     end
  246.                 end
  247.  
  248.                 -- alternative to writing tons of ifs. Normally would be initialized once before main loop, but this is demo/semi-tutorial
  249.                 local commands = {}
  250.  
  251.                 function commands.hello(playerData)
  252.                     setOutput("Hi!", 0xFFFF00)
  253.                 end
  254.  
  255.                 function commands.exit(playerData)
  256.                     -- manually exit keyboard GUI. Player can do it itself by using escape button (same as normal Minecraft GUIs)
  257.                     gl.getCaptureControl(playerData.uuid).stopCapturing()
  258.                 end
  259.  
  260.                 -- sorry, couldn't resist
  261.                 local function createColorCommand(color)
  262.                     return function(playerData)
  263.                         playerData.textInput.setColor(color)
  264.                         setOutput("OK", color)
  265.                     end
  266.                 end
  267.  
  268.                 commands.red = createColorCommand(0xFF0000)
  269.                 commands.green = createColorCommand(0x00FF00)
  270.                 commands.blue = createColorCommand(0x0000FF)
  271.  
  272.                 command = contents:sub(2, -1)
  273.                 commandSub = commands[command] -- skip console prompt
  274.  
  275.                 if commandSub then
  276.                     commandSub(playerData)
  277.                 else
  278.                     setOutput("invalid command: " .. command, 0xFF0000)
  279.                 end
  280.  
  281.             elseif string.byte(char) >= 0x20 then -- skip unprintable control chars
  282.                 input.setText(contents .. char)
  283.             end
  284.         end
  285.     elseif evt[1] == "timer" then
  286.         local timerId = evt[2]
  287.         callback = timerCallbacks[timerId]
  288.         if callback then callback() end
  289.         timerCallbacks[timerId] = nil
  290.     end
  291.  
  292.     -- sync() shouldn't be called often
  293.     -- if called without changes, nothing will happen
  294.     -- this method is synchronized - i.e. it can be executed only once per game tick (1/20 s). Other instructions don't have that limit
  295.     gl.sync()
  296. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement