mirror of
				https://github.com/LDDestroier/CC/
				synced 2025-11-04 01:13:01 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			902 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			902 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()
 | 
						||
	term.setBackgroundColor(grid.voidvol)
 | 
						||
	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
 |