mirror of
				https://github.com/LDDestroier/CC/
				synced 2025-10-31 15:32:59 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			901 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			901 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| --[[
 | ||
| 	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.1 -- lower value = faster game. I'd reccommend 0.1 for SMP play.
 | ||
| local player
 | ||
| 
 | ||
| local resetPlayers = function()
 | ||
| 	player = {
 | ||
| 		[1] = {
 | ||
| 			num = 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] = {
 | ||
| 			num = 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 = {
 | ||
| 		{
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"
",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 		},
 | ||
| 		{
 | ||
| 			"55ffffff55f555555f5ffffff5f55",
 | ||
| 			"55ffffff5555f55f5f55f5fff5f55",
 | ||
| 			"55fff5ff55fff55fff5555fff5f55",
 | ||
| 			"55ff55ff55fff55fff55f5fff5f55",
 | ||
| 			"5f55f5ff55f5f55fff55fff555ff5",
 | ||
| 			"555ffff555f555555f55fffff5f55",
 | ||
| 		},
 | ||
| 		{
 | ||
| 			"5fffffff5f5555555f55ffff55f5f",
 | ||
| 			"5fffffff5ffff5ffff555fff55f5f",
 | ||
| 			"5fff5fff5ffff5ffff5ff55f55f5f",
 | ||
| 			"5f55f55f5ffff5ffff5fff5555f5f",
 | ||
| 			"555fff555f5ff5ff5f5fffff55f5f",
 | ||
| 			"5fffffff5f5555555f5fffff55f5f",
 | ||
| 		}
 | ||
| 	},
 | ||
| 	lose = {
 | ||
| 		{
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"
",
 | ||
| 			"",
 | ||
| 		},
 | ||
| 		{
 | ||
| 			"eeffffffffeeefeffeeeeeffeeeeeee",
 | ||
| 			"eeffffffeeefefefeefffeefeefffee",
 | ||
| 			"eeffffffeeffffefeeffffffeffffef",
 | ||
| 			"eeffffffeeffffefeeeeefefeeeeeef",
 | ||
| 			"eeffffffefefffeffeffffefeefffff",
 | ||
| 			"eeeeeeefefeeeeeffeeeeeefeeeeeee",
 | ||
| 		},
 | ||
| 		{
 | ||
| 			"efffffffeeeeeeffeeeeeeefeeeeeee",
 | ||
| 			"efffffffeffffeefefffffffeffffff",
 | ||
| 			"efffffffeffffeefeeeeeeefeeeeeff",
 | ||
| 			"efffffffeffffeeffffffeefeffffff",
 | ||
| 			"efffffffeeffeeefeffffeefeffffee",
 | ||
| 			"eeeeeeeffeeeefffeeeeeeffeeeeeee",
 | ||
| 		}
 | ||
| 	},
 | ||
| 	tie = {
 | ||
| 		{
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 			"",
 | ||
| 		},
 | ||
| 		{
 | ||
| 			"77888800f0000000f0888877",
 | ||
| 			"fff88fff00ff0ff0f08ffff7",
 | ||
| 			"fff88fffffff0ffff0ffff7f",
 | ||
| 			"fff88fffffff0ffff088887f",
 | ||
| 			"fff88ffff0ff0ffff08fffff",
 | ||
| 			"fff88ffff0000000f0888877",
 | ||
| 		},
 | ||
| 		{
 | ||
| 			"7788880f00000000f0888877",
 | ||
| 			"fff8fffffff00ffff0ffffff",
 | ||
| 			"fff8fffffff00ffff08888ff",
 | ||
| 			"fff8fffffff00ffff0ffffff",
 | ||
| 			"fff8ffff0ff00ff0f0fffff7",
 | ||
| 			"fff8ffff00000000f0888877",
 | ||
| 		},
 | ||
| 	}
 | ||
| }
 | ||
| for k,v in pairs(images) do
 | ||
| 	v.x = #v[1][1]
 | ||
| 	v.y = #v[1]
 | ||
| 	for i = 2, #v do
 | ||
| 		for line = 1, #v[i] do
 | ||
| 			images[k][i][line] = v[i][line]:gsub("f", " ")
 | ||
| 		end
 | ||
| 	end
 | ||
| end
 | ||
| 
 | ||
| local drawImage = function(im, x, y)
 | ||
| 	local cx, cy = term.getCursorPos()
 | ||
| 	for iy = 1, #im[1] do
 | ||
| 		for ix = 1, #im[1][iy] do
 | ||
| 			term.setCursorPos(x+(ix-1),y+(iy-1))
 | ||
| 			if not (im[2][iy]:sub(ix,ix) == " " and im[3][iy]:sub(ix,ix) == " ") then
 | ||
| 				term.blit(im[1][iy]:sub(ix,ix), im[2][iy]:sub(ix,ix), im[3][iy]:sub(ix,ix))
 | ||
| 			end
 | ||
| 		end
 | ||
| 	end
 | ||
| 	term.setCursorPos(cx,cy)
 | ||
| end
 | ||
| 
 | ||
| local deadGuys = {}
 | ||
| local trail = {}
 | ||
| local lastTrails = {}
 | ||
| 
 | ||
| local putTrailXY = function(x, y, p)
 | ||
| 	trail[y] = trail[y] or {}
 | ||
| 	trail[y][x] = {
 | ||
| 		player = player[p],
 | ||
| 		age = 0
 | ||
| 	}
 | ||
| end
 | ||
| 
 | ||
| local putTrail = function(p)
 | ||
| 	putTrailXY(p.x, p.y, p.num)
 | ||
| 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', along with trails and players
 | ||
| local drawGrid = function(x, y, onlyDrawGrid)
 | ||
| 	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
 | ||
| 			if not onlyDrawGrid then
 | ||
| 				for i = 1, #player do
 | ||
| 					if player[i].x == adjX and player[i].y == adjY then
 | ||
| 						isPlayer = i
 | ||
| 						break
 | ||
| 					end
 | ||
| 				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 (not onlyDrawGrid) and 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 (not onlyDrawGrid) and (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 (not onlyDrawGrid) and 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
 | ||
| 			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 "
 | ||
| 	scrollX = player[you].x - math.floor(scr_x / 2)
 | ||
| 	scrollY = player[you].y - math.floor(scr_y / 2)
 | ||
| 	for i = 3, 1, -1 do
 | ||
| 		render()
 | ||
| 		term.setCursorPos(math.floor(scr_x / 2 - (#cMessage + #cName) / 2), math.floor(scr_y / 2) + 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) + 4)
 | ||
| 		term.write(i .. "...")
 | ||
| 		sleep(1)
 | ||
| 	end
 | ||
| end
 | ||
| 
 | ||
| local makeMenu = function(x, y, options, doAnimate)
 | ||
| 	local cpos = 1
 | ||
| 	local cursor = "> "
 | ||
| 	local gsX, gsY = 0, 0
 | ||
| 	local step = 0
 | ||
| 	local lastPos = cpos
 | ||
| 	if not doAnimate then
 | ||
| 		drawImage(images.logo, math.ceil(scr_x / 2 - images.logo.x / 2), 2)
 | ||
| 	end
 | ||
| 	local rend = function()
 | ||
| 		if doAnimate then
 | ||
| 			drawImage(images.logo, math.ceil(scr_x / 2 - images.logo.x / 2), 2)
 | ||
| 		end
 | ||
| 		for i = 1, #options do
 | ||
| 			if i == cpos then
 | ||
| 				term.setCursorPos(x, y + (i - 1))
 | ||
| 				term.setTextColor(colors.white)
 | ||
| 				term.write(cursor .. options[i])
 | ||
| 			else
 | ||
| 				if i == lastPos then
 | ||
| 					term.setCursorPos(x, y + (i - 1))
 | ||
| 					term.write((" "):rep(#cursor))
 | ||
| 					lastPos = nil
 | ||
| 				else
 | ||
| 					term.setCursorPos(x + #cursor, y + (i - 1))
 | ||
| 				end
 | ||
| 				term.setTextColor(colors.gray)
 | ||
| 				term.write(options[i])
 | ||
| 			end
 | ||
| 		end
 | ||
| 	end
 | ||
| 	local gstID, evt = math.random(1,65535)
 | ||
| 	if doAnimate then
 | ||
| 		os.queueEvent("timer", gstID)
 | ||
| 	end
 | ||
| 	while true do
 | ||
| 		rend()
 | ||
| 		evt = {os.pullEvent()}
 | ||
| 		if evt[1] == "key" then
 | ||
| 			if evt[2] == keys.up then
 | ||
| 				lastPos = cpos
 | ||
| 				cpos = (cpos - 2) % #options + 1
 | ||
| 			elseif evt[2] == keys.down then
 | ||
| 				lastPos = cpos
 | ||
| 				cpos = (cpos % #options) + 1
 | ||
| 			elseif evt[2] == keys.home then
 | ||
| 				lastPos = cpos
 | ||
| 				cpos = 1
 | ||
| 			elseif evt[2] == keys["end"] then
 | ||
| 				lastPos = cpos
 | ||
| 				cpos = #options
 | ||
| 			elseif evt[2] == keys.enter then
 | ||
| 				return cpos
 | ||
| 			end
 | ||
| 		elseif evt[1] == "timer" and evt[2] == gstID then
 | ||
| 			gstID = os.startTimer(0.05)
 | ||
| 			drawGrid(gsX, gsY, true)
 | ||
| 			step = step + 1
 | ||
| 			if math.ceil(step / 100) % 2 == 1 then
 | ||
| 				gsX = gsX + 1
 | ||
| 			else
 | ||
| 				gsY = gsY - 1
 | ||
| 			end
 | ||
| 		end
 | ||
| 	end
 | ||
| end
 | ||
| 
 | ||
| local titleScreen = function()
 | ||
| 	term.clear()
 | ||
| 	
 | ||
| 	local choice = makeMenu(2, scr_y - 4, {
 | ||
| 		"Start Game",
 | ||
| 		"How to Play",
 | ||
| 		"Grid Demo",
 | ||
| 		"Exit"
 | ||
| 	}, true)
 | ||
| 	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 lastTrails or {},
 | ||
| 		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.ceil(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.ceil(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.ceil(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)))
 | ||
| 				if doesIntersectBorder(p.x, p.y) or getTrail(p.x, p.y) then
 | ||
| 					p.dead = true
 | ||
| 					deadGuys[i] = true
 | ||
| 				elseif p.putTrail then
 | ||
| 					putTrail(p)
 | ||
| 					lastTrails[#lastTrails+1] = {p.x, p.y, p.num}
 | ||
| 					if #lastTrails > #player then
 | ||
| 						table.remove(lastTrails, 1)
 | ||
| 					end
 | ||
| 				end
 | ||
| 			end
 | ||
| 			for a = 1, #player do
 | ||
| 				if (a ~= i) and (player[a].x == p.x and player[a].y == p.y) then
 | ||
| 					p.dead = true
 | ||
| 					deadGuys[i] = true
 | ||
| 					if (p.direction + 2) % 4 == player[a].direction % 4 then
 | ||
| 						player[a].dead = true
 | ||
| 						deadGuys[a] = true
 | ||
| 					end
 | ||
| 					break
 | ||
| 				end
 | ||
| 			end
 | ||
| 		end
 | ||
| 	end
 | ||
| 	return deadAnimation(doSend)
 | ||
| end
 | ||
| 
 | ||
| local setDirection = function(keylist, p, checkDir)
 | ||
| 	p.putTrail = not keylist[control.release]
 | ||
| 	if keylist[control.left] and (checkDir or p.direction) ~= 0 then
 | ||
| 		p.direction = 2
 | ||
| 	elseif keylist[control.right] and (checkDir or p.direction) ~= 2 then
 | ||
| 		p.direction = 0
 | ||
| 	elseif keylist[control.up] and (checkDir or p.direction) ~= 1 then
 | ||
| 		p.direction = -1
 | ||
| 	elseif keylist[control.down] and (checkDir or p.direction) ~= -1 then
 | ||
| 		p.direction = 1
 | ||
| 	end
 | ||
| end
 | ||
| 
 | ||
| local game = function()
 | ||
| 	local outcome
 | ||
| 	local p, np
 | ||
| 	while true do
 | ||
| 		if not isHost then
 | ||
| 			os.pullEvent("move_tick")
 | ||
| 		end
 | ||
| 		p = player[you]
 | ||
| 		np = player[nou]
 | ||
| 		
 | ||
| 		if isHost then
 | ||
| 			setDirection(keysDown, p)
 | ||
| 			setDirection(netKeysDown, np)
 | ||
| 		else
 | ||
| 			setDirection(keysDown, p)
 | ||
| 		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
 | ||
| 		ageTrails()
 | ||
| 		if outcome == "end" then
 | ||
| 			return
 | ||
| 		else
 | ||
| 			scrollX = p.x - math.floor(scr_x / 2)
 | ||
| 			scrollY = p.y - math.floor(scr_y / 2)
 | ||
| 
 | ||
| 			render()
 | ||
| 			if isHost then
 | ||
| 				sleep(gameDelay)
 | ||
| 			end
 | ||
| 		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
 | ||
| 						for i = 1, #msg.trail do
 | ||
| 							putTrailXY(unpack(msg.trail[i]))
 | ||
| 						end
 | ||
| 						deadGuys = msg.deadGuys
 | ||
| 						os.queueEvent("move_tick")
 | ||
| 					elseif type(msg.keysDown) == "table" then
 | ||
| 						netKeysDown = msg.keysDown
 | ||
| 					end
 | ||
| 				end
 | ||
| 				
 | ||
| 			end
 | ||
| 		end
 | ||
| 	end
 | ||
| end
 | ||
| 
 | ||
| local 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
 | ||
| 		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)
 | ||
| 		sleep(0.1)
 | ||
| 		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
 | 
