JustDoesGames

Npac

Sep 3rd, 2020 (edited)
2,065
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.29 KB | None | 0 0
  1.  
  2. --[[
  3.  
  4. Npac
  5. Just Does Games
  6.  
  7. =====  Beta v1.0  =====
  8.  
  9. Init Release
  10.  
  11. =====  Beta v1.0  =====
  12. =====  Beta v1.1  =====
  13.  
  14. +Added Lazers
  15. +Increased the amount of blocks being updated to those around the player
  16. +Level seed can now be changed by specifying in the args (type '<program> help')
  17. +Removed 'air' from block list and labeled it as 0
  18. +Added red, green, and blue blocks for demo
  19. +Removed help on startup
  20.  
  21. =====  Beta v1.1  =====
  22. =====  Beta v1.2  =====
  23.  
  24. --+ General +--
  25.  
  26. +Fully rewrote the entire code to decrease code length
  27. +Restyled patch notes
  28. +Redid how seeds are read and used
  29. +Seeds can now be set before game starts
  30. +Added death screen for appropriate gamemodes
  31. +Added white block for default seed
  32. +World generation happens after establishing a connection (if specified)
  33.  
  34. --+ Multiplayer v.b.1.0 +--
  35. FULLY KEEP IN MIND THAT THE MULTIPLAYER ASPECT IS IN IT'S OWN BETA
  36.  
  37. +Added Multiplayer functionality (Client to Client also in beta so expect bugs)
  38. +Added usages for both 'host' and 'join'
  39. +Both playesr and blocks move on both users screens
  40. +Host and Client bullets move on the same frame regardless of when they shot it
  41. +The client is given 5 seconds to look at the mapdata and the seed to plot out what to do
  42. +Rednet was choosen because of how insecure and pointless this game is
  43. +Rednet opens a protocol of 'npac' and a hostname specified by the user
  44. +UI now displays what the other user is currently holding
  45.  
  46. =====  Beta v1.2  =====
  47. =====  Beta v1.3  =====
  48.  
  49. --+ General +-
  50.  
  51. +Death/error screens now wait for 2 seconds instead of 5
  52. +Lazers now have a cycle limit of 300 instead of inf.
  53. +Fixed mapdata for white block
  54. +Added new animation for starting the game for both singleplayer and multiplayer
  55. +Added color to both the death and crash screens for both singleplayer and multiplayer
  56.  
  57. --+ Multiplayer v.b.1.1 +--
  58.  
  59. +Rednet will now open the top port automatically if on ccemux
  60. +Bullets can now kill players standing next to each other
  61. +Rednet will now send instead of broadcast (had it to broadcast for debug stuff and forgot to change it lol)
  62. +Host will now search for games that are already existing. this means it will take slightly longer to start
  63. +Changed the style of text for connecting (more on the host than the client)
  64. +Bullets can no longer crash the game due to nil values (still possible most likely. just report them to me on discord)
  65.  
  66. =====  Beta v1.3  =====
  67.  
  68. --]]
  69.  
  70. -- Error Processing and init --
  71.  
  72. if ccemux then
  73.     ccemux.attach("top", "wireless_modem")
  74. end
  75.  
  76. local w,h = term.getSize() h=h-1 term.setCursorBlink(false)
  77. local r,e,pid
  78.  
  79. -- Error Processing and init --
  80.  
  81. -- Init Functions and Variables --
  82.  
  83. local clr, cp, st, sb = term.clear, term.setCursorPos, term.setTextColor, term.setBackgroundColor
  84. local shifting = false
  85.  
  86. local sprites = {
  87.     player = {"\30","\31","\17","\16"},
  88.     bullet = {"|","|","-","-"}
  89. }
  90.  
  91. local player = {
  92.     x=1, y=1, direction=1, isDead=false, score=0, isBullet=false,
  93.     bullet={x=0,y=0,direction=0,isBullet=true,active=0},
  94.     inventory={},
  95.     worldmod={x=0,y=0,change=0}
  96. }
  97.  
  98. local firstDraw = true -- WHYYYYYYY
  99. -- Init Functions and Variables --
  100.  
  101. -- World Generation --
  102.  
  103. local map = {}
  104. local maptileprops = {
  105.     { -- wall
  106.         name = "Wall (White)", -- Used for Usage (Host)
  107.         bcolor = colors.white, -- background color
  108.         tcolor = colors.white, -- text color
  109.         sprite = " ", -- sprite
  110.         collision = true, -- collision
  111.         disappear = false, -- disappear on collision
  112.         deadly = false, -- kill player on collision
  113.         grabable = true, -- grabable
  114.         value = 0, -- value on collection
  115.     },
  116.     { -- wall
  117.         name = "Wall (Red)", -- Used for Usage (Host)
  118.         bcolor = colors.red, -- background color
  119.         tcolor = colors.white, -- text color
  120.         sprite = " ", -- sprite
  121.         collision = true, -- collision
  122.         disappear = false, -- disappear on collision
  123.         deadly = false, -- kill player on collision
  124.         grabable = true, -- grabable
  125.         value = 0, -- value on collection
  126.     },
  127.     { -- wall
  128.         name = "Wall (Green)", -- Used for Usage (Host)
  129.         bcolor = colors.green, -- background color
  130.         tcolor = colors.white, -- text color
  131.         sprite = " ", -- sprite
  132.         collision = true, -- collision
  133.         disappear = false, -- disappear on collision
  134.         deadly = false, -- kill player on collision
  135.         grabable = true, -- grabable
  136.         value = 0, -- value on collection
  137.     },
  138.     { -- wall
  139.         name = "Wall (Blue)", -- Used for Usage (Host)
  140.         bcolor = colors.blue, -- background color
  141.         tcolor = colors.white, -- text color
  142.         sprite = " ", -- sprite
  143.         collision = true, -- collision
  144.         disappear = false, -- disappear on collision
  145.         deadly = false, -- kill player on collision
  146.         grabable = true, -- grabable
  147.         value = 0, -- value on collection
  148.     },
  149.     { -- coin
  150.         name = "Coin", -- Used for Usage (Host)
  151.         bcolor = colors.black, -- background color
  152.         tcolor = colors.orange, -- text color
  153.         sprite = "\4", -- sprite
  154.         collision = false, -- collision
  155.         disappear = true, -- disappear on collision
  156.         deadly = false, -- kill player on collision
  157.         grabable = false, -- grabable
  158.         value = 10, -- value on collection
  159.     }
  160. }
  161. local seed = "0003000004"
  162.  
  163. local function verifySeed(seed)
  164.     if string.len(seed) < 1 then return false end
  165.     for i=1, string.len(seed) do
  166.         if type(tonumber(string.sub(seed, i,i))) == "number" then
  167.             if tonumber(string.sub(seed, i,i)) > #maptileprops then
  168.                 return false
  169.             end
  170.         end
  171.     end
  172.     return true
  173. end
  174.  
  175. local function generateWorld(seed)
  176.     local loop = 1
  177.     for i=1, h do -- height
  178.         map[i] = ""
  179.         for ii=1, w do -- width
  180.             map[i] = map[i]..string.sub(seed, loop, loop)
  181.             loop = loop + 1
  182.             if loop > #seed then loop = 1 end
  183.         end
  184.     end
  185.     local rMap = map -- used to reset map if needed.
  186. end
  187.  
  188. -- World Generation --
  189.  
  190. -- Rednet and Connections --
  191.  
  192. local online, clientId, isHost
  193. local player2 = player
  194. local function openRednet()
  195.     local names = peripheral.getNames()
  196.     for i=1, #names do
  197.         if peripheral.getType(names[i]) == "modem" then
  198.             rednet.open(names[i]) return true
  199.         end
  200.     end
  201.     return false
  202. end
  203. online = openRednet()
  204.  
  205. local function console(t)
  206.     print("[Console] "..t)
  207. end
  208.  
  209. local args = {...}
  210. if args[1] == "host" then
  211.     if #args ~= 2 then print("Usage: npac host <room name>") return end
  212.     st(colors.yellow) sb(colors.black) clr() cp(1,1) write("NPAC") st(colors.white) write(" | ") st(colors.gray) print("Just Does Games")
  213.     st(colors.white) print("======================")
  214.     console("Server room id: "..args[2])
  215.     write("World Seed: ") seed = string.gsub(read(), " ", "")
  216.     if not verifySeed(seed) then
  217.         print("Seed is invalid.")
  218.         print("Seed Codes:")
  219.         print("0 = Air")
  220.         for i=1, #maptileprops do
  221.             print(i.." = "..maptileprops[i].name or "Invalid")
  222.         end
  223.         return
  224.     end
  225.     console("Seed valid.")
  226.     if online then
  227.         console("Searching for duplicate room(s)...")
  228.         local id = rednet.lookup("npac", args[2])
  229.         if id then
  230.             console("Room id Already Exists.") return
  231.         end
  232.         console("Room does not exists! Creating one...")
  233.         rednet.host("npac", args[2])
  234.         console("Created room sucessfully!")
  235.         console("Room Id: "..args[2])
  236.         local attempts = 0
  237.         local tx,ty = term.getCursorPos()
  238.         while attempts < 30 do
  239.             cp(tx,ty) print("("..attempts..") Searching for client...")
  240.             local id, m = rednet.receive(args[2], 1)
  241.             if type(m) == "table" then
  242.                 if m[1] ~= w or m[2] ~= h then
  243.                     rednet.unhost("npac", args[2])
  244.                     rednet.send(id, "Width and Height are not the same.", args[2])
  245.                 else
  246.                     clientId = id
  247.                     rednet.send(clientId, {maptileprops, seed}, args[2]) break
  248.                 end
  249.             else
  250.                 attempts = attempts+1
  251.                
  252.             end
  253.         end
  254.         if clientId == 0 then
  255.             rednet.unhost("npac", args[2])
  256.             print("Connection timed out.") return
  257.         end
  258.         console("Connected! Loading map...") sleep(0.5)
  259.     else
  260.         print("Rednet Required.") return
  261.     end
  262.     isHost = true
  263.     player.x, player.y,player.direction = 1,1,2
  264.     player2.x, player2.y, player2.direction = w,h,1
  265. elseif args[1] == "join" then
  266.     if #args ~= 2 then print("Usage: npac join <room name>") return end
  267.     if online then
  268.         console("Searching for host...")
  269.         clientId = rednet.lookup("npac", args[2])
  270.         if clientId then
  271.             console("Found host. Connecting...")
  272.             rednet.send(clientId, {w,h}, args[2])
  273.             local id, m = rednet.receive(args[2], 1)
  274.             if id == clientId and type(m) == "table" then
  275.                 maptileprops, seed = m[1], m[2]
  276.             else
  277.                 if m then
  278.                     console("Error: "..m)
  279.                 else
  280.                     console("Failed to Connect to Host.")
  281.                 end
  282.                 return
  283.             end
  284.         else
  285.             console("Host could not be found.") return
  286.         end
  287.         console("Connected! Loading map...") sleep(0.5)
  288.     else
  289.         print("Rednet Required.") return
  290.     end
  291.     isHost = false
  292.     player.x, player.y,player.direction = w,h,1
  293.     player2.x, player2.y, player2.direction = 1,1,2
  294. else
  295.     online = false
  296. end
  297. console("Generating World...")
  298. generateWorld(seed)
  299. console("World Generated.") sleep(0.1) sb(colors.black) st(colors.white) clr()
  300.  
  301. local function sendData()
  302.     if not online then return end
  303.     rednet.send(clientId, player, args[2])
  304.     --rednet.broadcast(player, args[2]) -- << for debug
  305. end
  306.  
  307. -- Rednet and Connections --
  308.  
  309. -- World Functions --
  310.  
  311. local function modifyMap(x,y, change)
  312.     --local tmp = string:sub(map[y],x,x)
  313.     if x == nil or y == nil or change == nil then return end
  314.     if map[y] == string.sub(map[y], 1,x-1)..change..string.sub(map[y], x+1,-1) then return end
  315.     map[y] = string.sub(map[y], 1,x-1)..change..string.sub(map[y], x+1,-1)
  316.     if online then
  317.         player.worldmod = {x=x,y=y,change=change}
  318.         sendData()
  319.     end
  320. end
  321.  
  322. local function drawMapPixel(x,y)
  323.     if y > #map then y = 1 elseif y < 1 then y = #map end
  324.     if x > w then x = 1 elseif x < 1 then x = w end
  325.     local t = tonumber(string.sub(map[y], x,x))
  326.     cp(x,y)
  327.     if t == 0 then
  328.         sb(colors.black) st(colors.white) write(" ")
  329.     else
  330.         sb(maptileprops[t].bcolor) st(maptileprops[t].tcolor) write(maptileprops[t].sprite)
  331.     end
  332. end
  333.  
  334. local function maptile(x,y)
  335.     return maptileprops[tonumber(string.sub(map[y] or "",x,x))] or {}
  336. end
  337.  
  338. local function maptileByte(x,y)
  339.     return string.sub(map[y], x,x)
  340. end
  341.  
  342. -- World Functions --
  343.  
  344.  
  345. local function drawScreen(t)
  346.    
  347.     if firstDraw then
  348.         firstDraw = false
  349.         sendData() sleep(0.02)
  350.     end
  351.    
  352.     local function drawPlayerBlocks()
  353.         drawMapPixel(player.x,player.y-1) -- up
  354.         drawMapPixel(player.x,player.y+1) -- down
  355.         drawMapPixel(player.x-1,player.y) -- left
  356.         drawMapPixel(player.x+1,player.y) -- right
  357.         if online then
  358.             drawMapPixel(player2.x,player2.y-1) -- up
  359.             drawMapPixel(player2.x,player2.y+1) -- down
  360.             drawMapPixel(player2.x-1,player2.y) -- left
  361.             drawMapPixel(player2.x+1,player2.y) -- right
  362.         end
  363.     end
  364.    
  365.     local function drawUI()
  366.         -- UI --
  367.         cp(1,h+1) st(colors.white) write("Score: "..player.score.." ") if shifting then write("\7") else write(" ") end
  368.         if player.inventory[1] then
  369.             sb(player.inventory[1][2].bcolor) st(player.inventory[1][2].tcolor)
  370.             write(player.inventory[1][2].sprite)
  371.         else
  372.             sb(colors.black)
  373.             write(" ")
  374.         end
  375.        
  376.         if online then
  377.             if player2.inventory[1] then
  378.                 sb(player2.inventory[1][2].bcolor) st(player2.inventory[1][2].tcolor)
  379.                 write(player2.inventory[1][2].sprite)
  380.             else
  381.                 sb(colors.black)
  382.                 write(" ")
  383.             end
  384.         end
  385.        
  386.         sb(colors.black) write("   ")
  387.         -- UI --
  388.     end
  389.    
  390.     -- Draw Types --
  391.     if t == 1 then -- update only blocks around the players
  392.         drawPlayerBlocks()
  393.     elseif t == 2 then -- update whole screen
  394.         for i=1, h do
  395.             cp(1,i)
  396.             for ii=1, w do
  397.                 drawMapPixel(ii,i)
  398.             end
  399.         end
  400.         drawPlayerBlocks()
  401.     elseif t == 3 then -- update only UI (unused for now)
  402.         drawUI() return
  403.     end
  404.     -- Draw Types --
  405.    
  406.     -- Draw Globals --
  407.    
  408.     cp(player.x, player.y) st(colors.white) sb(colors.black) write(sprites.player[player.direction])
  409.     if online then
  410.         cp(player2.x, player2.y) st(colors.red) sb(colors.black) write(sprites.player[player2.direction])
  411.     end
  412.    
  413.     drawUI()
  414.     -- Draw Globals --
  415. end
  416.  
  417. local function moveEnt(ent, direction)
  418.     ent.direction = direction
  419.     if shifting then
  420.         if not ent.isBullet then
  421.             sendData() return
  422.         end
  423.     end
  424.     local prevx, prevy = ent.x, ent.y
  425.     local m = {
  426.         function() ent.y=ent.y-1 end,
  427.         function() ent.y=ent.y+1 end,
  428.         function() ent.x=ent.x-1 end,
  429.         function() ent.x=ent.x+1 end,
  430.     }
  431.     m[direction]()
  432.     if ent.x > w then ent.x = 1 end
  433.     if ent.x < 1 then ent.x = w end
  434.     if ent.y > h then ent.y = 1 end
  435.     if ent.y < 1 then ent.y = h end
  436.     if maptile(ent.x, ent.y).value then player.score = player.score+maptile(ent.x, ent.y).value end
  437.     if maptile(ent.x, ent.y).collision then
  438.         if ent.isBullet then
  439.             if maptile(ent.x, ent.y).disappear then
  440.                 modifyMap(ent.x, ent.y,"0")
  441.                 ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
  442.             else
  443.                 ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
  444.             end
  445.             drawMapPixel(prevx, prevy) -- this is needed
  446.         else
  447.             ent.x, ent.y = prevx, prevy
  448.         end
  449.     else
  450.         if ent.isBullet then
  451.             if prevx == player.x and prevy == player.y then
  452.                
  453.             elseif prevx == player2.x and prevy == player2.y then
  454.                
  455.             else
  456.                 cp(ent.x, ent.y) st(colors.red) sb(colors.black) write(sprites.bullet[ent.direction])
  457.                 cp(prevx, prevy) sb(colors.black) write(" ")
  458.             end
  459.             ent.active = ent.active+1
  460.             if ent.active == 300 then
  461.                 drawMapPixel(ent.x, ent.y)
  462.                 ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
  463.             end
  464.         end
  465.     end
  466.     if maptile(ent.x, ent.y).disappear then
  467.         if ent.isBullet then
  468.             ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
  469.         end
  470.         modifyMap(ent.x, ent.y,"0")
  471.     end
  472.     if not ent.isBullet then sendData() end
  473. end
  474.  
  475. local function kill()
  476.     player.isDead = true
  477. end
  478.  
  479. local function runConnection()
  480.     r,e = pcall(function()
  481.         while not player.isDead and not player2.isDead do
  482.             local id, m = rednet.receive(args[2])
  483.             if id == clientId then
  484.                 if type(m) == "table" then
  485.                     player2 = m
  486.                     if player2.worldmod.change ~= 0 then
  487.                         modifyMap(player2.worldmod.x, player2.worldmod.y, player2.worldmod.change)
  488.                         player2.worldmod = {x=0, y=0, change=0}
  489.                     end
  490.                     drawScreen(1)
  491.                 end
  492.             end
  493.         end
  494.     end)
  495. end
  496.  
  497. local function runBullets()
  498.     r,e = pcall(function()
  499.         while not player.isDead and not player2.isDead do
  500.             --if player.bullet.direction ~= 0 or player2.bullet.direction ~= 0 then
  501.                 if player.bullet.direction ~= 0 then
  502.                     moveEnt(player.bullet, player.bullet.direction)
  503.                     if player.bullet.x == player.x and player.bullet.y == player.y then
  504.                         player.isDead = true
  505.                     elseif player.bullet.x == player2.x and player.bullet.y == player2.y then
  506.                         player2.isDead = true
  507.                     end
  508.                 end
  509.                 if player2.bullet.direction ~= 0 then
  510.                     --error("Bullet Spawn")
  511.                     moveEnt(player2.bullet, player2.bullet.direction)
  512.                     if player2.bullet.x == player2.x and player2.bullet.y == player2.y then
  513.                         player2.isDead = true
  514.                     elseif player2.bullet.x == player.x and player2.bullet.y == player.y then
  515.                         player.isDead = true
  516.                     end
  517.                 end
  518.             --end
  519.             sleep(0.05)
  520.         end
  521.     end)
  522. end
  523.  
  524. local function runPlayer()
  525.     r,e = pcall(function()
  526.         local u = 2
  527.         while not player.isDead and not player2.isDead do
  528.             if u ~= 0 then drawScreen(u) u = 0 end
  529.             a,b = os.pullEvent("key")
  530.            
  531.             local tx,ty = player.x, player.y
  532.             if player.direction == 1 then -- up
  533.                 ty = ty-1 if ty < 1 then ty = h end
  534.             elseif player.direction == 2 then -- down
  535.                 ty = ty+1 if ty > h then ty = 1 end
  536.             elseif player.direction == 3 then -- left
  537.                 tx = tx-1 if tx < 1 then tx = w end
  538.             else -- anything other than the other 3 (right usually)
  539.                 tx = tx+1 if tx > w then tx = 1 end
  540.             end
  541.            
  542.             if b == keys.up or b == keys.w then
  543.                 moveEnt(player, 1) u = 1
  544.             elseif b == keys.down or b == keys.s  then
  545.                 moveEnt(player, 2) u = 1
  546.             elseif b == keys.left or b == keys.a  then
  547.                 moveEnt(player, 3) u = 1
  548.             elseif b == keys.right or b == keys.d  then
  549.                 moveEnt(player, 4) u = 1
  550.             elseif b == keys.leftShift or b == keys.rightShift then
  551.                 shifting = not shifting u = 1
  552.             elseif b == keys.space or b == keys.e then
  553.                 if #player.inventory == 0 then
  554.                     if maptile(tx,ty).grabable then
  555.                         table.insert(player.inventory, {maptileByte(tx,ty), maptile(tx,ty)})
  556.                         modifyMap(tx,ty, "0") u = 1
  557.                     end
  558.                 else
  559.                     if maptileByte(tx,ty) == "0" then
  560.                         modifyMap(tx,ty, player.inventory[1][1])
  561.                         table.remove(player.inventory, 1) u = 1
  562.                     end
  563.                 end
  564.             elseif b == keys.f then
  565.                 if player.bullet.direction == 0 and not maptile(tx,ty).collision then
  566.                     player.bullet.direction = player.direction
  567.                     player.bullet.x, player.bullet.y = player.x, player.y
  568.                     --moveEnt(player.bullet, player.bullet.direction)
  569.                     sendData()
  570.                     u = 1
  571.                 end
  572.             elseif b == keys.q then
  573.                 kill() break
  574.             end
  575.         end
  576.     end)
  577. end
  578.  
  579. for i=1, h do
  580.     cp(1,i)
  581.     for ii=1, w do
  582.         drawMapPixel(ii,i)
  583.     end
  584.     sleep(0.1)
  585. end
  586.  
  587. st(colors.white) sb(colors.black)
  588. for i=1, 5 do
  589.     cp(player.x, player.y) write(5-i+1) sleep(0.2)
  590.     cp(player.x, player.y) write(" ") sleep(0.1)
  591. end
  592.  
  593. local pidname = {"Player", "Bullets", "Connection"}
  594. pid = parallel.waitForAny(runPlayer, runBullets, runConnection)
  595.  
  596. st(colors.white) sb(colors.black) clr() cp(1,1)
  597. if r then
  598.     if online then
  599.         if player.isDead then
  600.             st(colors.red) print("You have Lost. (You Died)")
  601.         elseif player2.isDead then
  602.             st(colors.green) print("You have Won! (Player 2 Died)")
  603.         else
  604.             kill()
  605.             st(colors.yellow) print("Unexpected Disconnect.")
  606.         end
  607.         sendData()
  608.     else
  609.         st(colors.red) print("You have died.")
  610.     end
  611.     print("") st(colors.white)
  612.     write("Your Score: ")
  613.     if online then if player.score > player2.score then st(colors.lime) elseif player.score < player2.score then st(colors.gray) else st(colors.orange) end else st(colors.purple) end
  614.     print(player.score)
  615.     if online then
  616.         st(colors.white)
  617.         write("Player 2 Score: ")
  618.         if online then if player.score > player2.score then st(colors.lime) elseif player.score < player2.score then st(colors.gray) else st(colors.orange) end else st(colors.purple) end
  619.         print(player2.score)
  620.     end
  621.     sleep(2) st(colors.gray)
  622.     cp(1,h+1) write("Press any key to exit.") os.pullEvent("key")
  623. else
  624.     kill() sendData()
  625.     st(colors.red) sb(colors.black) clr() cp(1,1)
  626.     print("Pacman Crashed!")
  627.     write("PID: ") st(colors.white) print(pid.." ("..pidname[pid]..")") st(colors.red)
  628.     write("Error: ") st(colors.white) print(tostring(e)) sleep(2) st(colors.gray)
  629.     cp(1,h) write("Press any key to exit.") os.pullEvent("key")
  630. end
  631.  
  632. if online then rednet.unhost("npac", args[2]) end
  633. st(colors.white) sb(colors.black) clr() cp(1,1) sleep(.5)
Advertisement
Add Comment
Please, Sign In to add comment