Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Npac
- Just Does Games
- ===== Beta v1.0 =====
- Init Release
- ===== Beta v1.0 =====
- ===== Beta v1.1 =====
- +Added Lazers
- +Increased the amount of blocks being updated to those around the player
- +Level seed can now be changed by specifying in the args (type '<program> help')
- +Removed 'air' from block list and labeled it as 0
- +Added red, green, and blue blocks for demo
- +Removed help on startup
- ===== Beta v1.1 =====
- ===== Beta v1.2 =====
- --+ General +--
- +Fully rewrote the entire code to decrease code length
- +Restyled patch notes
- +Redid how seeds are read and used
- +Seeds can now be set before game starts
- +Added death screen for appropriate gamemodes
- +Added white block for default seed
- +World generation happens after establishing a connection (if specified)
- --+ Multiplayer v.b.1.0 +--
- FULLY KEEP IN MIND THAT THE MULTIPLAYER ASPECT IS IN IT'S OWN BETA
- +Added Multiplayer functionality (Client to Client also in beta so expect bugs)
- +Added usages for both 'host' and 'join'
- +Both playesr and blocks move on both users screens
- +Host and Client bullets move on the same frame regardless of when they shot it
- +The client is given 5 seconds to look at the mapdata and the seed to plot out what to do
- +Rednet was choosen because of how insecure and pointless this game is
- +Rednet opens a protocol of 'npac' and a hostname specified by the user
- +UI now displays what the other user is currently holding
- ===== Beta v1.2 =====
- ===== Beta v1.3 =====
- --+ General +-
- +Death/error screens now wait for 2 seconds instead of 5
- +Lazers now have a cycle limit of 300 instead of inf.
- +Fixed mapdata for white block
- +Added new animation for starting the game for both singleplayer and multiplayer
- +Added color to both the death and crash screens for both singleplayer and multiplayer
- --+ Multiplayer v.b.1.1 +--
- +Rednet will now open the top port automatically if on ccemux
- +Bullets can now kill players standing next to each other
- +Rednet will now send instead of broadcast (had it to broadcast for debug stuff and forgot to change it lol)
- +Host will now search for games that are already existing. this means it will take slightly longer to start
- +Changed the style of text for connecting (more on the host than the client)
- +Bullets can no longer crash the game due to nil values (still possible most likely. just report them to me on discord)
- ===== Beta v1.3 =====
- --]]
- -- Error Processing and init --
- if ccemux then
- ccemux.attach("top", "wireless_modem")
- end
- local w,h = term.getSize() h=h-1 term.setCursorBlink(false)
- local r,e,pid
- -- Error Processing and init --
- -- Init Functions and Variables --
- local clr, cp, st, sb = term.clear, term.setCursorPos, term.setTextColor, term.setBackgroundColor
- local shifting = false
- local sprites = {
- player = {"\30","\31","\17","\16"},
- bullet = {"|","|","-","-"}
- }
- local player = {
- x=1, y=1, direction=1, isDead=false, score=0, isBullet=false,
- bullet={x=0,y=0,direction=0,isBullet=true,active=0},
- inventory={},
- worldmod={x=0,y=0,change=0}
- }
- local firstDraw = true -- WHYYYYYYY
- -- Init Functions and Variables --
- -- World Generation --
- local map = {}
- local maptileprops = {
- { -- wall
- name = "Wall (White)", -- Used for Usage (Host)
- bcolor = colors.white, -- background color
- tcolor = colors.white, -- text color
- sprite = " ", -- sprite
- collision = true, -- collision
- disappear = false, -- disappear on collision
- deadly = false, -- kill player on collision
- grabable = true, -- grabable
- value = 0, -- value on collection
- },
- { -- wall
- name = "Wall (Red)", -- Used for Usage (Host)
- bcolor = colors.red, -- background color
- tcolor = colors.white, -- text color
- sprite = " ", -- sprite
- collision = true, -- collision
- disappear = false, -- disappear on collision
- deadly = false, -- kill player on collision
- grabable = true, -- grabable
- value = 0, -- value on collection
- },
- { -- wall
- name = "Wall (Green)", -- Used for Usage (Host)
- bcolor = colors.green, -- background color
- tcolor = colors.white, -- text color
- sprite = " ", -- sprite
- collision = true, -- collision
- disappear = false, -- disappear on collision
- deadly = false, -- kill player on collision
- grabable = true, -- grabable
- value = 0, -- value on collection
- },
- { -- wall
- name = "Wall (Blue)", -- Used for Usage (Host)
- bcolor = colors.blue, -- background color
- tcolor = colors.white, -- text color
- sprite = " ", -- sprite
- collision = true, -- collision
- disappear = false, -- disappear on collision
- deadly = false, -- kill player on collision
- grabable = true, -- grabable
- value = 0, -- value on collection
- },
- { -- coin
- name = "Coin", -- Used for Usage (Host)
- bcolor = colors.black, -- background color
- tcolor = colors.orange, -- text color
- sprite = "\4", -- sprite
- collision = false, -- collision
- disappear = true, -- disappear on collision
- deadly = false, -- kill player on collision
- grabable = false, -- grabable
- value = 10, -- value on collection
- }
- }
- local seed = "0003000004"
- local function verifySeed(seed)
- if string.len(seed) < 1 then return false end
- for i=1, string.len(seed) do
- if type(tonumber(string.sub(seed, i,i))) == "number" then
- if tonumber(string.sub(seed, i,i)) > #maptileprops then
- return false
- end
- end
- end
- return true
- end
- local function generateWorld(seed)
- local loop = 1
- for i=1, h do -- height
- map[i] = ""
- for ii=1, w do -- width
- map[i] = map[i]..string.sub(seed, loop, loop)
- loop = loop + 1
- if loop > #seed then loop = 1 end
- end
- end
- local rMap = map -- used to reset map if needed.
- end
- -- World Generation --
- -- Rednet and Connections --
- local online, clientId, isHost
- local player2 = player
- local function openRednet()
- local names = peripheral.getNames()
- for i=1, #names do
- if peripheral.getType(names[i]) == "modem" then
- rednet.open(names[i]) return true
- end
- end
- return false
- end
- online = openRednet()
- local function console(t)
- print("[Console] "..t)
- end
- local args = {...}
- if args[1] == "host" then
- if #args ~= 2 then print("Usage: npac host <room name>") return end
- st(colors.yellow) sb(colors.black) clr() cp(1,1) write("NPAC") st(colors.white) write(" | ") st(colors.gray) print("Just Does Games")
- st(colors.white) print("======================")
- console("Server room id: "..args[2])
- write("World Seed: ") seed = string.gsub(read(), " ", "")
- if not verifySeed(seed) then
- print("Seed is invalid.")
- print("Seed Codes:")
- print("0 = Air")
- for i=1, #maptileprops do
- print(i.." = "..maptileprops[i].name or "Invalid")
- end
- return
- end
- console("Seed valid.")
- if online then
- console("Searching for duplicate room(s)...")
- local id = rednet.lookup("npac", args[2])
- if id then
- console("Room id Already Exists.") return
- end
- console("Room does not exists! Creating one...")
- rednet.host("npac", args[2])
- console("Created room sucessfully!")
- console("Room Id: "..args[2])
- local attempts = 0
- local tx,ty = term.getCursorPos()
- while attempts < 30 do
- cp(tx,ty) print("("..attempts..") Searching for client...")
- local id, m = rednet.receive(args[2], 1)
- if type(m) == "table" then
- if m[1] ~= w or m[2] ~= h then
- rednet.unhost("npac", args[2])
- rednet.send(id, "Width and Height are not the same.", args[2])
- else
- clientId = id
- rednet.send(clientId, {maptileprops, seed}, args[2]) break
- end
- else
- attempts = attempts+1
- end
- end
- if clientId == 0 then
- rednet.unhost("npac", args[2])
- print("Connection timed out.") return
- end
- console("Connected! Loading map...") sleep(0.5)
- else
- print("Rednet Required.") return
- end
- isHost = true
- player.x, player.y,player.direction = 1,1,2
- player2.x, player2.y, player2.direction = w,h,1
- elseif args[1] == "join" then
- if #args ~= 2 then print("Usage: npac join <room name>") return end
- if online then
- console("Searching for host...")
- clientId = rednet.lookup("npac", args[2])
- if clientId then
- console("Found host. Connecting...")
- rednet.send(clientId, {w,h}, args[2])
- local id, m = rednet.receive(args[2], 1)
- if id == clientId and type(m) == "table" then
- maptileprops, seed = m[1], m[2]
- else
- if m then
- console("Error: "..m)
- else
- console("Failed to Connect to Host.")
- end
- return
- end
- else
- console("Host could not be found.") return
- end
- console("Connected! Loading map...") sleep(0.5)
- else
- print("Rednet Required.") return
- end
- isHost = false
- player.x, player.y,player.direction = w,h,1
- player2.x, player2.y, player2.direction = 1,1,2
- else
- online = false
- end
- console("Generating World...")
- generateWorld(seed)
- console("World Generated.") sleep(0.1) sb(colors.black) st(colors.white) clr()
- local function sendData()
- if not online then return end
- rednet.send(clientId, player, args[2])
- --rednet.broadcast(player, args[2]) -- << for debug
- end
- -- Rednet and Connections --
- -- World Functions --
- local function modifyMap(x,y, change)
- --local tmp = string:sub(map[y],x,x)
- if x == nil or y == nil or change == nil then return end
- if map[y] == string.sub(map[y], 1,x-1)..change..string.sub(map[y], x+1,-1) then return end
- map[y] = string.sub(map[y], 1,x-1)..change..string.sub(map[y], x+1,-1)
- if online then
- player.worldmod = {x=x,y=y,change=change}
- sendData()
- end
- end
- local function drawMapPixel(x,y)
- if y > #map then y = 1 elseif y < 1 then y = #map end
- if x > w then x = 1 elseif x < 1 then x = w end
- local t = tonumber(string.sub(map[y], x,x))
- cp(x,y)
- if t == 0 then
- sb(colors.black) st(colors.white) write(" ")
- else
- sb(maptileprops[t].bcolor) st(maptileprops[t].tcolor) write(maptileprops[t].sprite)
- end
- end
- local function maptile(x,y)
- return maptileprops[tonumber(string.sub(map[y] or "",x,x))] or {}
- end
- local function maptileByte(x,y)
- return string.sub(map[y], x,x)
- end
- -- World Functions --
- local function drawScreen(t)
- if firstDraw then
- firstDraw = false
- sendData() sleep(0.02)
- end
- local function drawPlayerBlocks()
- drawMapPixel(player.x,player.y-1) -- up
- drawMapPixel(player.x,player.y+1) -- down
- drawMapPixel(player.x-1,player.y) -- left
- drawMapPixel(player.x+1,player.y) -- right
- if online then
- drawMapPixel(player2.x,player2.y-1) -- up
- drawMapPixel(player2.x,player2.y+1) -- down
- drawMapPixel(player2.x-1,player2.y) -- left
- drawMapPixel(player2.x+1,player2.y) -- right
- end
- end
- local function drawUI()
- -- UI --
- cp(1,h+1) st(colors.white) write("Score: "..player.score.." ") if shifting then write("\7") else write(" ") end
- if player.inventory[1] then
- sb(player.inventory[1][2].bcolor) st(player.inventory[1][2].tcolor)
- write(player.inventory[1][2].sprite)
- else
- sb(colors.black)
- write(" ")
- end
- if online then
- if player2.inventory[1] then
- sb(player2.inventory[1][2].bcolor) st(player2.inventory[1][2].tcolor)
- write(player2.inventory[1][2].sprite)
- else
- sb(colors.black)
- write(" ")
- end
- end
- sb(colors.black) write(" ")
- -- UI --
- end
- -- Draw Types --
- if t == 1 then -- update only blocks around the players
- drawPlayerBlocks()
- elseif t == 2 then -- update whole screen
- for i=1, h do
- cp(1,i)
- for ii=1, w do
- drawMapPixel(ii,i)
- end
- end
- drawPlayerBlocks()
- elseif t == 3 then -- update only UI (unused for now)
- drawUI() return
- end
- -- Draw Types --
- -- Draw Globals --
- cp(player.x, player.y) st(colors.white) sb(colors.black) write(sprites.player[player.direction])
- if online then
- cp(player2.x, player2.y) st(colors.red) sb(colors.black) write(sprites.player[player2.direction])
- end
- drawUI()
- -- Draw Globals --
- end
- local function moveEnt(ent, direction)
- ent.direction = direction
- if shifting then
- if not ent.isBullet then
- sendData() return
- end
- end
- local prevx, prevy = ent.x, ent.y
- local m = {
- function() ent.y=ent.y-1 end,
- function() ent.y=ent.y+1 end,
- function() ent.x=ent.x-1 end,
- function() ent.x=ent.x+1 end,
- }
- m[direction]()
- if ent.x > w then ent.x = 1 end
- if ent.x < 1 then ent.x = w end
- if ent.y > h then ent.y = 1 end
- if ent.y < 1 then ent.y = h end
- if maptile(ent.x, ent.y).value then player.score = player.score+maptile(ent.x, ent.y).value end
- if maptile(ent.x, ent.y).collision then
- if ent.isBullet then
- if maptile(ent.x, ent.y).disappear then
- modifyMap(ent.x, ent.y,"0")
- ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
- else
- ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
- end
- drawMapPixel(prevx, prevy) -- this is needed
- else
- ent.x, ent.y = prevx, prevy
- end
- else
- if ent.isBullet then
- if prevx == player.x and prevy == player.y then
- elseif prevx == player2.x and prevy == player2.y then
- else
- cp(ent.x, ent.y) st(colors.red) sb(colors.black) write(sprites.bullet[ent.direction])
- cp(prevx, prevy) sb(colors.black) write(" ")
- end
- ent.active = ent.active+1
- if ent.active == 300 then
- drawMapPixel(ent.x, ent.y)
- ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
- end
- end
- end
- if maptile(ent.x, ent.y).disappear then
- if ent.isBullet then
- ent.x, ent.y, ent.direction, ent.active = 0,0,0,0
- end
- modifyMap(ent.x, ent.y,"0")
- end
- if not ent.isBullet then sendData() end
- end
- local function kill()
- player.isDead = true
- end
- local function runConnection()
- r,e = pcall(function()
- while not player.isDead and not player2.isDead do
- local id, m = rednet.receive(args[2])
- if id == clientId then
- if type(m) == "table" then
- player2 = m
- if player2.worldmod.change ~= 0 then
- modifyMap(player2.worldmod.x, player2.worldmod.y, player2.worldmod.change)
- player2.worldmod = {x=0, y=0, change=0}
- end
- drawScreen(1)
- end
- end
- end
- end)
- end
- local function runBullets()
- r,e = pcall(function()
- while not player.isDead and not player2.isDead do
- --if player.bullet.direction ~= 0 or player2.bullet.direction ~= 0 then
- if player.bullet.direction ~= 0 then
- moveEnt(player.bullet, player.bullet.direction)
- if player.bullet.x == player.x and player.bullet.y == player.y then
- player.isDead = true
- elseif player.bullet.x == player2.x and player.bullet.y == player2.y then
- player2.isDead = true
- end
- end
- if player2.bullet.direction ~= 0 then
- --error("Bullet Spawn")
- moveEnt(player2.bullet, player2.bullet.direction)
- if player2.bullet.x == player2.x and player2.bullet.y == player2.y then
- player2.isDead = true
- elseif player2.bullet.x == player.x and player2.bullet.y == player.y then
- player.isDead = true
- end
- end
- --end
- sleep(0.05)
- end
- end)
- end
- local function runPlayer()
- r,e = pcall(function()
- local u = 2
- while not player.isDead and not player2.isDead do
- if u ~= 0 then drawScreen(u) u = 0 end
- a,b = os.pullEvent("key")
- local tx,ty = player.x, player.y
- if player.direction == 1 then -- up
- ty = ty-1 if ty < 1 then ty = h end
- elseif player.direction == 2 then -- down
- ty = ty+1 if ty > h then ty = 1 end
- elseif player.direction == 3 then -- left
- tx = tx-1 if tx < 1 then tx = w end
- else -- anything other than the other 3 (right usually)
- tx = tx+1 if tx > w then tx = 1 end
- end
- if b == keys.up or b == keys.w then
- moveEnt(player, 1) u = 1
- elseif b == keys.down or b == keys.s then
- moveEnt(player, 2) u = 1
- elseif b == keys.left or b == keys.a then
- moveEnt(player, 3) u = 1
- elseif b == keys.right or b == keys.d then
- moveEnt(player, 4) u = 1
- elseif b == keys.leftShift or b == keys.rightShift then
- shifting = not shifting u = 1
- elseif b == keys.space or b == keys.e then
- if #player.inventory == 0 then
- if maptile(tx,ty).grabable then
- table.insert(player.inventory, {maptileByte(tx,ty), maptile(tx,ty)})
- modifyMap(tx,ty, "0") u = 1
- end
- else
- if maptileByte(tx,ty) == "0" then
- modifyMap(tx,ty, player.inventory[1][1])
- table.remove(player.inventory, 1) u = 1
- end
- end
- elseif b == keys.f then
- if player.bullet.direction == 0 and not maptile(tx,ty).collision then
- player.bullet.direction = player.direction
- player.bullet.x, player.bullet.y = player.x, player.y
- --moveEnt(player.bullet, player.bullet.direction)
- sendData()
- u = 1
- end
- elseif b == keys.q then
- kill() break
- end
- end
- end)
- end
- for i=1, h do
- cp(1,i)
- for ii=1, w do
- drawMapPixel(ii,i)
- end
- sleep(0.1)
- end
- st(colors.white) sb(colors.black)
- for i=1, 5 do
- cp(player.x, player.y) write(5-i+1) sleep(0.2)
- cp(player.x, player.y) write(" ") sleep(0.1)
- end
- local pidname = {"Player", "Bullets", "Connection"}
- pid = parallel.waitForAny(runPlayer, runBullets, runConnection)
- st(colors.white) sb(colors.black) clr() cp(1,1)
- if r then
- if online then
- if player.isDead then
- st(colors.red) print("You have Lost. (You Died)")
- elseif player2.isDead then
- st(colors.green) print("You have Won! (Player 2 Died)")
- else
- kill()
- st(colors.yellow) print("Unexpected Disconnect.")
- end
- sendData()
- else
- st(colors.red) print("You have died.")
- end
- print("") st(colors.white)
- write("Your Score: ")
- 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
- print(player.score)
- if online then
- st(colors.white)
- write("Player 2 Score: ")
- 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
- print(player2.score)
- end
- sleep(2) st(colors.gray)
- cp(1,h+1) write("Press any key to exit.") os.pullEvent("key")
- else
- kill() sendData()
- st(colors.red) sb(colors.black) clr() cp(1,1)
- print("Pacman Crashed!")
- write("PID: ") st(colors.white) print(pid.." ("..pidname[pid]..")") st(colors.red)
- write("Error: ") st(colors.white) print(tostring(e)) sleep(2) st(colors.gray)
- cp(1,h) write("Press any key to exit.") os.pullEvent("key")
- end
- if online then rednet.unhost("npac", args[2]) end
- st(colors.white) sb(colors.black) clr() cp(1,1) sleep(.5)
Advertisement
Add Comment
Please, Sign In to add comment