--[[ TRON Light Cycle Game programmed by LDDestroier wget https://raw.githubusercontent.com/LDDestroier/CC/master/tron --]] local port = 701 local scr_x, scr_y = term.getSize() local modem = peripheral.find("modem") if (not modem) and ccemux then ccemux.attach("top", "wireless_modem") modem = peripheral.find("modem") end if modem then modem.open(port) else error("You should attach a modem.") end local gamename = "" local isHost local squareGrid = true local waitingForGame = true local deepCopy deepCopy = function(tbl, ...) local output = {} for k,v in pairs(tbl) do if type(v) == "table" then output[k] = deepCopy(v) else output[k] = v end end for i = 1, #arg do output[#output+1] = arg[i] end return output end local initGrid = { x1 = -100, y1 = -100, x2 = 100, y2 = 100, border = "#", voidcol = "f", forecol = "8", backcol = "7", edgecol = "0" } grid = deepCopy(initGrid) local you = 1 local nou = 2 local keysDown = {} local netKeysDown = {} -- the scrolling of the screen local scrollX = 0 local scrollY = 0 -- used when panning with WASD local scrollAdjX = 0 local scrollAdjY = 0 local lockInput = false local gameDelayInit = 0.05 local player local resetPlayers = function() player = { [1] = { x = -2, y = -5, direction = -1, char = "@", color = { colors.blue, colors.blue, colors.blue, colors.cyan, colors.cyan, colors.lightBlue, colors.lightBlue, colors.cyan, colors.cyan }, dead = false, putTrail = true }, [2] = { x = 2, y = -5, direction = -1, char = "@", color = { colors.red, colors.red, colors.red, colors.orange, colors.orange, colors.yellow, colors.yellow, colors.orange, colors.orange }, dead = false, putTrail = true } } end resetPlayers() local images = { logo = { { " •ƒƒƒƒƒƒƒƒƒ•—ƒƒƒƒƒƒƒ‹‹ ‡‡ƒƒƒ‹‹ Ÿ‹ •ƒƒ•", " •ƒƒƒ”€—ƒƒƒ•‚ƒƒƒƒƒ‹€€€Š —€Ÿƒƒƒ€” •‚‚ •€€•", " •€• ‚‚ƒƒ•€€—€€€”€€••€€‹‹ •€€•", " •€• —ƒ”‹“ƒƒ‹€€€•€€•€€€•€€••€•ˆƒ€€•", " •€• •€• ‚‹€€‹€Š€‹‡€Ÿ…•€• ‚‚€•", " •€• •€• ‹€‚‹ ‹‹€€€Ÿ‡‡ •€• ‹‹•", "   Š ‚‹‡  ‚…", }, { " f7777777777777777777f f77777f 7f f777", " f99979999979999999999f 799999799 77f7 f997", " 799 79999f997ffff9977997f f997", " 799 7797777fffff997ffff9977997797997", " 799 799 799977f7797fff7997799 79797", " 799 799 7797f 797999997 799 797", " 777 777 7777 7777777 777 77", }, { " 7999999999f9999999997 7999997 97 799f", " 7777997777f77777779997 997777997 997f 799f", " 997 f7777799ffff799f99997 799f", " 997 997f9997fff799ffff799f997ff7999f", " 997 997 f7999fff999777997f997 f799f", " 997 997 f9997 f7999977f 997 f7f", " fff fff ffff fffffff fff ff", } }, win = { { "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€", "€€•€€€€€€€••€€€€€€€€Š€€€€•€€€•€", "€€•€€€€€€€•‚€€•€ƒ€€€‚€€•€€€•€", "€€•€€‡€€€•€€€€•€€€€•‹€‹€•€€€•€", "€€•ŸŸ€‹€€•€€€€•€€€€•€‚‚…€€€•€", "€€‚€‡€‚‚€•Ÿ€€•€€€•€€€‹€€€€", "€€Ÿ€€€€‹€••€€€€€€€€•€€€€•€€€•€", "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€", }, { "fffffffffffffffffffffffffffffff", "f55ffffff55f555555f5ffffff5f55f", "f55ffffff5555f55f5f55f5fff5f55f", "f55fff5ff55fff55fff5555fff5f55f", "f55ff55ff55fff55fff55f5fff5f55f", "f5f55f5ff55f5f55fff55fff555ff5f", "f555ffff555f555555f55fffff5f55f", "fffffffffffffffffffffffffffffff", }, { " ", " 5fffffff5f5555555f55ffff55f5ff", " 5fffffff5ffff5ffff555fff55f5ff", " 5fff5fff5ffff5ffff5ff55f55f5ff", " 5f55f55f5ffff5ffff5fff5555f5ff", " 555fff555f5ff5ff5f5fffff55f5ff", " 5fffffff5f5555555f5fffff55f5ff", " ", } }, lose = { { "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€", "€€•€€€€€€Ÿ€€€‚€€€€€€‚€€€€€€€€€", "€€•€€€€€€€Ÿ€‚€€€—€€€‚ƒ€€•€€€‚ƒ€", "€€•€€€€€€€•€€€•€€€€ƒƒƒƒ‹€€‚ƒƒƒ”€€", "€€•€€€€€€€•€€€•€€‚ƒƒƒƒ€€€—ƒƒƒ€€", "€€•€€€€€€€‚€Ÿ€€€€€…€€€•€€€Ÿ€", "€€€€€€€€€‚€€€Ÿ€€€€€€Ÿ€€€€€€€€€", "€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€", }, { "fffffffffffffffffffffffffffffffff", "feeffffffffeeefeffeeeeeffeeeeeeef", "feeffffffeeefefefeefffeefeefffeef", "feeffffffeeffffefeeffffffeffffeff", "feeffffffeeffffefeeeeefefeeeeeeff", "feeffffffefefffeffeffffefeeffffff", "feeeeeeefefeeeeeffeeeeeefeeeeeeef", "fffffffffffffffffffffffffffffffff", }, { " ", " efffffffeeeeeeffeeeeeeefeeeeeeef", " efffffffeffffeefefffffffefffffff", " efffffffeffffeefeeeeeeefeeeeefff", " efffffffeffffeeffffffeefefffffff", " efffffffeeffeeefeffffeefeffffeef", " eeeeeeeffeeeefffeeeeeeffeeeeeeef", " ", } }, tie = { { "€€€€€€€€€€€€€€€€€€€€€€€€€€", "€€€€€€€€••€€€€€€€€€€€€€€€€", "€€€€€•€€€‚€•€€€ƒ€€•€€€€ƒ€", "€€€€€•€€€€€€•€€€€€€‚ƒƒƒ”€€", "€€€€€•€€€€€€•€€€€€€—ƒƒƒ€€", "€€€€€•€€€Ÿ€•€€€€€•€€€€€", "€€€€€•€€€•€€€€€€€€€€€€€€€€", "€€€€€€€€€€€€€€€€€€€€€€€€€€", }, { "ffffffffffffffffffffffffff", "f77888800f0000000f0888877f", "ffff88fff00ff0ff0f08ffff7f", "ffff88fffffff0ffff0ffff7ff", "ffff88fffffff0ffff088887ff", "ffff88ffff0ff0ffff08ffffff", "ffff88ffff0000000f0888877f", "ffffffffffffffffffffffffff", }, { " ", " 7788880f00000000f0888877f", " 8fffffff00ffff0fffffff", " 8fffffff00ffff08888fff", " 8fffffff00ffff0fffffff", " 8ffff0ff00ff0f0fffff7f", " 8ffff00000000f0888877f", " ", }, } } for k,v in pairs(images) do v.x = #v[1][1] v.y = #v[1] end local drawImage = function(im, x, y) local cx, cy = term.getCursorPos() for iy = 1, #im[1] do term.setCursorPos(x,y+(iy-1)) term.blit(im[1][iy], im[2][iy], im[3][iy]) end term.setCursorPos(cx,cy) end local deadGuys = {} local trail = {} local putTrail = function(p) trail[p.y] = trail[p.y] or {} trail[p.y][p.x] = { player = p, age = 0 } end local getTrail = function(x, y) if trail[y] then if trail[y][x] then if doAge then trail[y][x].age = trail[y][x].age + 1 end return trail[y][x].player.char, trail[y][x].player.color, trail[y][x].age end end return false end local ageTrails = function() for y,l in pairs(trail) do for x,v in pairs(l) do trail[y][x].age = trail[y][x].age + 1 end end end local toblit = { [0] = " ", [colors.white] = "0", [colors.orange] = "1", [colors.magenta] = "2", [colors.lightBlue] = "3", [colors.yellow] = "4", [colors.lime] = "5", [colors.pink] = "6", [colors.gray] = "7", [colors.lightGray] = "8", [colors.cyan] = "9", [colors.purple] = "a", [colors.blue] = "b", [colors.brown] = "c", [colors.green] = "d", [colors.red] = "e", [colors.black] = "f" } local control = { up = keys.up, down = keys.down, left = keys.left, right = keys.right, lookUp = keys.w, lookDown = keys.s, lookLeft = keys.a, lookRight = keys.d, release = keys.space } -- keeps track of where you are local gamemode = "" local gridFore, gridBack if squareGrid then gridFore = { "+-------", "| ", "| ", "| ", "| " } gridBack = { "+------------", "| ", "| ", "| ", "| ", "| ", "| ", "| " } else gridFore = { " / ", " / ", " / ", " / ", "/__________" } gridBack = { " / ", " / ", " / ", " / ", " / ", " / ", " / ", "/_______________" } end local dirArrow = { [-1] = "^", [0] = ">", [1] = "V", [2] = "<" } local doesIntersectBorder = function(x, y) return x == grid.x1 or x == grid.x2 or y == grid.y1 or y == grid.y2 end --draws grid and background at scroll 'x' and 'y' local drawGrid = function(x, y) x, y = math.floor(x + 0.5), math.floor(y + 0.5) local bg = {{},{},{}} local foreX, foreY local backX, backY local adjX, adjY local trailChar, trailColor, trailAge, isPlayer for sy = 1, scr_y do bg[1][sy] = "" bg[2][sy] = "" bg[3][sy] = "" for sx = 1, scr_x do adjX = (sx + x) adjY = (sy + y) foreX = 1 + (sx + x) % #gridFore[1] foreY = 1 + (sy + y) % #gridFore backX = 1 + math.floor(sx + (x / 2)) % #gridBack[1] backY = 1 + math.floor(sy + (y / 2)) % #gridBack trailChar, trailColor, trailAge = getTrail(adjX, adjY) isPlayer = false for i = 1, #player do if player[i].x == adjX and player[i].y == adjY then isPlayer = i break end end if isPlayer and not (doesIntersectBorder(adjX, adjY)) then bg[1][sy] = bg[1][sy] .. dirArrow[player[isPlayer].direction] bg[2][sy] = bg[2][sy] .. toblit[player[isPlayer].color[1]] bg[3][sy] = bg[3][sy] .. grid.voidcol else if trailChar and trailColor then trailColor = trailColor[1 + ((trailAge - 1) % #trailColor)] bg[1][sy] = bg[1][sy] .. trailChar bg[2][sy] = bg[2][sy] .. toblit[trailColor] bg[3][sy] = bg[3][sy] .. grid.voidcol else if adjX < grid.x1 or adjX > grid.x2 or adjY < grid.y1 or adjY > grid.y2 then bg[1][sy] = bg[1][sy] .. " " bg[2][sy] = bg[2][sy] .. grid.voidcol bg[3][sy] = bg[3][sy] .. grid.voidcol elseif doesIntersectBorder(adjX, adjY) then bg[1][sy] = bg[1][sy] .. grid.border bg[2][sy] = bg[2][sy] .. grid.voidcol bg[3][sy] = bg[3][sy] .. grid.edgecol else if gridFore[foreY]:sub(foreX,foreX) ~= " " then bg[1][sy] = bg[1][sy] .. gridFore[foreY]:sub(foreX,foreX) bg[2][sy] = bg[2][sy] .. grid.forecol bg[3][sy] = bg[3][sy] .. grid.voidcol elseif gridBack[backY]:sub(backX,backX) ~= " " then bg[1][sy] = bg[1][sy] .. gridBack[backY]:sub(backX,backX) bg[2][sy] = bg[2][sy] .. grid.backcol bg[3][sy] = bg[3][sy] .. grid.voidcol else bg[1][sy] = bg[1][sy] .. " " bg[2][sy] = bg[2][sy] .. grid.voidcol bg[3][sy] = bg[3][sy] .. grid.voidcol end end end end end end for sy = 1, scr_y do term.setCursorPos(1,sy) term.blit(bg[1][sy], bg[2][sy], bg[3][sy]) end end local render = function() local p = player[you] drawGrid(scrollX + scrollAdjX, scrollY + scrollAdjY) term.setCursorPos(1,1) term.setTextColor(player[you].color[1]) term.write("P" .. you) end local pleaseWait = function() local periods = 1 local maxPeriods = 5 term.setBackgroundColor(colors.black) term.setTextColor(colors.gray) term.clear() local tID = os.startTimer(0.2) local evt local txt = "Waiting for game" while true do term.setCursorPos(math.floor(scr_x / 2 - (#txt + maxPeriods) / 2), scr_y - 2) term.write(txt .. ("."):rep(periods)) evt = {os.pullEvent()} if evt[1] == "timer" and evt[2] == tID then tID = os.startTimer(0.5) periods = (periods % maxPeriods) + 1 term.clearLine() elseif evt[1] == "new_game" then sleep(0.1) return evt[2] end end end local startCountdown = function() local cName = "PLAYER " .. you local col = colors.white for k,v in pairs(colors) do if player[you].color[1] == v then cName = k:upper() col = v break end end local cMessage = "You are " for i = 3, 1, -1 do render() term.setCursorPos(math.floor(scr_x / 2 - (#cMessage + #cName) / 2), math.floor(scr_y / 2)) term.setTextColor(colors.white) term.write(cMessage) term.setTextColor(col) term.write(cName) term.setTextColor(colors.white) term.setCursorPos(math.floor(scr_x / 2 - 2), math.floor(scr_y / 2) + 2) term.write(i .. "...") sleep(1) end end local makeMenu = function(x, y, options) local cpos = 1 local cursor = "> " local render = function() for i = 1, #options do term.setCursorPos(x, y + (i - 1)) if i == cpos then term.setTextColor(colors.white) term.write(cursor .. options[i]) else term.setTextColor(colors.gray) term.write((" "):rep(#cursor) .. options[i]) end end end local evt while true do render() evt = {os.pullEvent()} if evt[1] == "key" then if evt[2] == keys.up then cpos = (cpos - 2) % #options + 1 elseif evt[2] == keys.down then cpos = (cpos % #options) + 1 elseif evt[2] == keys.enter then return cpos end end end end local titleScreen = function() term.clear() drawImage(images.logo, 3,3) local choice = makeMenu(2, scr_y - 4, { "Start Game", "How to Play", "Grid Demo", "Exit" }) if choice == 1 then return "start" elseif choice == 2 then return "help" elseif choice == 3 then return "demo" elseif choice == 4 then return "exit" end end local cleanExit = function() term.setBackgroundColor(colors.black) term.setTextColor(colors.white) term.clear() term.setCursorPos(1,1) print("Thanks for playing!") end local getInput = function() local evt while true do evt = {os.pullEvent()} if lockInput then keysDown = {} else if evt[1] == "key" then keysDown[evt[2]] = true elseif evt[1] == "key_up" then keysDown[evt[2]] = false end end end end local scrollToPosition = function(x, y) for i = 1, 16 do scrollX = (scrollX + x - (scr_x/2)) / 2 scrollY = (scrollY + y - (scr_y/2)) / 2 render() sleep(0.05) end end local gridDemo = function() keysDown = {} while true do if keysDown[keys.left] then scrollX = scrollX - 1 end if keysDown[keys.right] then scrollX = scrollX + 1 end if keysDown[keys.up] then scrollY = scrollY - 1 end if keysDown[keys.down] then scrollY = scrollY + 1 end if keysDown[keys.q] then return "end" end drawGrid(scrollX, scrollY) ageTrails() sleep(gameDelay) end end local sendInfo = function(gameID) modem.transmit(port, port, { player = player, gameID = gameID, keysDown = keysDown, trail = isHost and trail or nil, deadGuys = isHost and deadGuys or {}, }) end local waitForKey = function(time) sleep(time or 0.1) os.pullEvent("key") end local deadAnimation = function(doSend) for k,v in pairs(deadGuys) do player[k].char = "X" lockInput = true end if doSend then sendInfo(gamename) end if deadGuys[you] or deadGuys[nou] then term.setTextColor(colors.white) if deadGuys[you] and deadGuys[nou] then scrollToPosition(player[nou].x, player[nou].y) scrollToPosition(player[you].x, player[you].y) drawImage(images.tie, math.floor(scr_x / 2 - images.tie.x / 2), math.floor(scr_y / 2 - images.tie.y / 2)) waitForKey(1) return "end" else if deadGuys[you] then scrollX, scrollY = player[nou].x - scr_x / 2, player[nou].y - scr_y / 2 scrollToPosition(player[you].x, player[you].y) drawImage(images.lose, math.floor(scr_x / 2 - images.lose.x / 2), math.floor(scr_y / 2 - images.lose.y / 2)) waitForKey(1) return "end" elseif deadGuys[nou] then scrollToPosition(player[nou].x, player[nou].y) drawImage(images.win, math.floor(scr_x / 2 - images.win.x / 2), math.floor(scr_y / 2 - images.win.y / 2)) waitForKey(1) return "end" end end end end local moveTick = function(doSend) local p for i = 1, #player do p = player[i] if not p.dead then if isHost then p.x = p.x + math.floor(math.cos(math.rad(p.direction * 90))) p.y = p.y + math.floor(math.sin(math.rad(p.direction * 90))) end if getTrail(p.x, p.y) or (p.x == grid.x1 or p.x == grid.x2 or p.y == grid.y1 or p.y == grid.y2) then p.dead = true deadGuys[i] = true elseif isHost then if p.putTrail then putTrail(p) end end end end ageTrails() return deadAnimation(doSend) end local setDirection = function(keylist, p) p.putTrail = not keylist[control.release] if keylist[control.left] and p.direction ~= 0 then p.direction = 2 elseif keylist[control.right] and p.direction ~= 2 then p.direction = 0 end if keylist[control.up] and p.direction ~= 1 then p.direction = -1 elseif keylist[control.down] and p.direction ~= -1 then p.direction = 1 end end local game = function() local outcome local p, np while true do p = player[you] np = player[nou] setDirection(keysDown, p) if isHost then setDirection(netKeysDown, np) end if keysDown[control.lookLeft] then scrollAdjX = scrollAdjX - 2 end if keysDown[control.lookRight] then scrollAdjX = scrollAdjX + 2 end if keysDown[control.lookUp] then scrollAdjY = scrollAdjY - 1.5 end if keysDown[control.lookDown] then scrollAdjY = scrollAdjY + 1.5 end scrollAdjX = scrollAdjX * 0.8 scrollAdjY = scrollAdjY * 0.8 if isHost then outcome = moveTick(true) else outcome = deadAnimation(true) end if outcome == "end" then return else scrollX = p.x - math.floor(scr_x / 2) scrollY = p.y - math.floor(scr_y / 2) render() sleep(gameDelay) end end end local decision local networking = function() local evt, side, channel, repchannel, msg, distance while true do evt, side, channel, repchannel, msg, distance = os.pullEvent("modem_message") if channel == port and repchannel == port and type(msg) == "table" then if type(msg.player) == "table" and type(msg.gameID) == "string" then if waitingForGame and (type(msg.new) == "number") then if msg.new < os.time() then gamename = msg.gameID isHost = false gameDelay = tonumber(msg.gameDelay) or 0.05 grid = msg.grid or deepCopy(initGrid) else you, nou = nou, you isHost = true end you, nou = nou, you modem.transmit(port, port, { player = player, gameID = gamename, new = isHost and (-math.huge) or (math.huge), grid = initGrid }) waitingForGame = false netKeysDown = {} os.queueEvent("new_game", gameID) elseif msg.gameID == gamename then if not isHost then player = msg.player trail = msg.trail deadGuys = msg.deadGuys elseif type(msg.keysDown) == "table" then netKeysDown = msg.keysDown end end end end end end helpScreen = function() term.setBackgroundColor(colors.black) term.setTextColor(colors.white) term.clear() term.setCursorPos(1,2) print([[ Move with arrow keys. Pan the camera with WASD. Hold SPACE to create gaps. That's basically it. Press any key to go back. ]]) waitForKey(0.25) end while true do decision = titleScreen() lockInput = false if decision == "start" then trail = {} deadGuys = {} gameDelay = gameDelayInit grid = deepCopy(initGrid) resetPlayers() you, nou = 1, 2 scrollX, scrollY = player[you].x - math.floor(scr_x / 2), player[you].x - math.floor(scr_y / 2) gamename = "" for i = 1, 32 do gamename = gamename .. string.char(math.random(1,126)) end waitingForGame = true modem.transmit(port, port, { player = player, gameID = gamename, new = os.time(), gameDelay = gameDelayInit, grid = initGrid }) parallel.waitForAny(pleaseWait, networking) startCountdown() parallel.waitForAny(getInput, game, networking) elseif decision == "help" then helpScreen() elseif decision == "demo" then parallel.waitForAny(getInput, gridDemo) elseif decision == "exit" then return cleanExit() end end