(function($) {
"use strict"

var canv // canvas
var gl // webgl context
var programInfo // vector and fragment shaders compiled into program
var hedge_program // vector and fragment shaders compiled into program

var arrays = {
	position: [-1, -1, 0, 1, -1, 0, -1, 1, 0, -1, 1, 0, 1, -1, 0, 1, 1, 0]
}

var cell = null // array of cells representing the map internally
var mincellside = 60 // minimum cell size so that a finger can fit into it
var cellside = 60 // side of one cell in pixels
var hwratio = 0.15 // halfwall ratio
var halfwall = 0 // half of wall's width in pixels
var mazemap = { tex: null, res: [7, 7] } // map of the maze
var playercell = 0 // index of the cell the player is currently in right now
var prevcell = 0 // index of the previous playercell
var entrycell = 0 // index of the entry cell
var exitcell = 0 // index of exit cell
var difficulty = 0 // don't limit difficulty
var d2side = [0, 7, 10, 12] // difficulty level to square maze's side
var playersprite = $("#playersprite")
var exitsprite = $("#exitsprite")
var scs = 1 // scene scale (a.k.a global font-size)
var textures // textures to be used by webgl
var hedges // map data to be displayed
var hedges_program // compiled webgl shaders
var bufferInfo // buffer to be displayed
var socket // websocket connection
var myid = null // id that will be assigned by the server
var mycpid = null // truncated id players id based on shirt and pants colors
var players = {} // players information
var last_render_time = Date.now() // previous render time
var last_update_time = 0 // when was the last update from server received
var winw // window width
var winh // window height
var off = [0, 0] // player offset
var goff = [0, 0] // ground offset 1 = cellside
var level = 0
var updates_per_second = 10 // how many updates server sends per second
var marks = [] // chalk marks on the ground
var iswaiting = false // is server waiting for 's' command to continue
var looped = false // has this player looped yet? Needed to reset goff

function clearmaze() {
	var ncells = mazemap.res[0] * mazemap.res[1];
	for(var i = 0; i < ncells; ++i) {
		cell[i] &= ~240;
	}
	prevcell = entrycell;
	playercell = entrycell;
	cell[playercell] |= 224;
};

function nextmaze() {
	mkmap();
};

// set cellside and automatically adjust halfwall:
function setcside(npixels) {
	cellside = npixels
	halfwall = Math.floor(cellside * hwratio);
}

// make map and init the game
function mkmap() {
	cell = new Uint8Array(49);
}


function clearhedges() {
	hedges = { position: {
				numComponents: 2,
				data: []
		}, texcoord: {
				numComponents: 2,
				data: []
		}, aspritesel: {
				numComponents: 2,
				data: []
		}, acolorsel: {
				numComponents: 2,
				data: []
		}
	}
}

function addground(pos) {
	var b = [500000 * scs, 500000 * scs] // brush halfwidth and halfheight
	pos[0] = pos[0] - (gl.canvas.width + b[0]) * 0.5;
	pos[1] = pos[1] - (gl.canvas.height + b[0]) * 0.5;
	var ratio = [2 / gl.canvas.width, 2 / gl.canvas.height];
	hedges.position.data.push(
		(pos[0] - b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],

		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1]
	);
	hedges.texcoord.data.push(0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0)
	hedges.aspritesel.data.push(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0)
	hedges.acolorsel.data.push(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

function addmark(pos, c) {
	c = c == null? -1: c / 256 // null means main character: no color
	var b = [25 * scs, 25 * scs] // brush halfwidth and halfheight
	pos[0] = pos[0] - (gl.canvas.width + b[0]) * 0.5;
	pos[1] = pos[1] - (gl.canvas.height + b[0]) * 0.5;
	var ratio = [2 / gl.canvas.width, 2 / gl.canvas.height];
	hedges.position.data.push(
		(pos[0] - b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],

		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1]
	);
	hedges.texcoord.data.push(0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0)
	hedges.aspritesel.data.push(3, 0, 3, 0, 3, 0, 3, 0, 3, 0, 3, 0)
	hedges.acolorsel.data.push(c, 0, c, 0, c, 0, c, 0, c, 0, c, 0);
}

function addhedge(pos) {
	var b = [25 * scs, 38 * scs] // brush halfwidth and halfheight
	pos[0] = pos[0] - gl.canvas.width * 0.5 + b[0];// offset to get -1 to +1
	pos[1] = pos[1] + b[1] - gl.canvas.height * 0.5;
	var ratio = [2 / gl.canvas.width, 2 / gl.canvas.height];
	hedges.position.data.push(
		(pos[0] - b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],

		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1]
	);
	hedges.texcoord.data.push(0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0);
	hedges.aspritesel.data.push(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
	hedges.acolorsel.data.push(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

// position, pose from 0 to 8, shirt and pants colors from 0 to 1.0
function addchar(pos, pose, scol, pcol) {
	var b = [25 * scs, 50 * scs] // brush halfwidth and halfheight
	pos[0] = pos[0] - gl.canvas.width * 0.5 + b[0];// offset to get -1 to +1
	pos[1] = pos[1] + b[1] - gl.canvas.height * 0.5;
	var ratio = [2 / gl.canvas.width, 2 / gl.canvas.height];
	hedges.position.data.push(
		(pos[0] - b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],

		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1]

	);
	hedges.texcoord.data.push(0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0);
	hedges.aspritesel.data.push(1, pose, 1, pose, 1, pose, 1, pose,
				1, pose, 1, pose);
	hedges.acolorsel.data.push(scol, pcol, scol, pcol, scol, pcol,
			scol, pcol, scol, pcol, scol, pcol);
}

function addportal(pos) {
	var b = [100 * scs, 100 * scs] // brush halfwidth and halfheight
	pos[0] = pos[0] - gl.canvas.width * 0.5 + b[0];// offset to get -1 to +1
	pos[1] = pos[1] + b[1] - gl.canvas.height * 0.5;
	var ratio = [2 / gl.canvas.width, 2 / gl.canvas.height];
	hedges.position.data.push(
		(pos[0] - b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],

		(pos[0] - b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] - b[1]) * ratio[1],
		(pos[0] + b[0]) * ratio[0],
		(pos[1] + b[1]) * ratio[1]

	);
	hedges.texcoord.data.push(0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0);
	hedges.aspritesel.data.push(2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0);
	hedges.acolorsel.data.push(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

// add hedge or character: [0=hedge | 1=character, pos, pose, scol, pcol]
function addsprite(params) {
	switch(params[0]) {
	case 0:	addhedge(params[1]); break;
	case 1: addchar(params[1], params[2], params[3], params[4]); break;
	case 2: addportal(params[1]); break;
	}
}

function process_player(p, sp, tnow, off) {
	// if offset is given we use it, if not we calculate and return it
	var pose = 0
	if(!p) {
		console.log("Players: ", players)
		return
	}
	if(p.server_vel[0] > 0) {
		if(p.server_vel[1] > 0) {
			pose = 3
		} else if(p.server_vel[1] < 0) {
			pose = 1
		} else {
			pose = 2
		}
	} else if(p.server_vel[0] < 0) {
		if(p.server_vel[1] > 0) {
			pose = 5
		} else if(p.server_vel[1] < 0) {
			pose = 7
		} else {
			pose = 6
		}
	} else {
		if(p.server_vel[1] > 0) {
			pose = 4
		}
	}

	var dt = tnow - p.client_ts
	p.client_ts = tnow
	// client_ve is in units per second, dt is in ms, so we / 1000

	p.client_pos[0] += p.client_vel[0] * dt * 0.001
	p.client_pos[1] += p.client_vel[1] * dt * 0.001

	if(!off) {
		off = [-(p.client_pos[0] * 0.001 - 3.5),
			-(p.client_pos[1] * 0.001 - 3.5)]
	}

	sp.push([1,
		[(p.client_pos[0] * 0.001 - 1.05 + off[0]) * cellside,
		(p.client_pos[1] * 0.001 - 1 + off[1]) * cellside],
		pose, p.glowing? -1: p.scolor / 256, p.pcolor / 256])
	// above is a little trick: we do not allocate a separate
	// attribute variable for the glowing effect, as we have in
	// the network protocol. when p[6] is 1 it means this player
	// has passed this maze and doing it second time. so in
	// fragment shader we have a special value -1 for colorsel.x
	// that enables the glow special effect when p[6] is not zero

	return off
}

function mkscene() {
	var sp = [] // we need to sort sprites by Y coordinate
	clearhedges()
	off = [0, 0] // screen offset depending on current player's position
	// display all players
	var tnow = Date.now() // current timestamp
	// we must process the client's player first to determine screen offset
	if(players[mycpid]) {
		// position of main character affects the global offset
		off = process_player(players[mycpid], sp, tnow, null)
		// once player has looped, it will be respawned, so we need
		// to reset the ground offset
		if(!looped && players[mycpid].glowing) {
			looped = true
			goff = [0, 0]
		}
		for(var cpid in players) {
			if(cpid != mycpid) {
				process_player(players[cpid], sp, tnow, off)
			}
		}
	}

	for(var i = 0; i < cell.length; ++i) {
		var x = ((i % mazemap.res[0]) - 1 + off[0]) * cellside
		var y = (Math.floor(i / mazemap.res[0]) - 1 + off[1]) * cellside
		// check if this cell has right wall
		if(!(cell[i] & 2)) {
			sp.push([0, [(x + cellside), y]])
			sp.push([0, [(x + cellside), y + cellside * 0.25]])
			sp.push([0, [(x + cellside), y + cellside * 0.5]])
			sp.push([0, [(x + cellside), y + cellside * 0.75]])
			sp.push([0, [(x + cellside), y + cellside * 1.0]])
		}
		if(!(cell[i] & 1)) {
			sp.push([0, [x, (y + cellside)]])
			sp.push([0, [x + cellside * 0.2, (y + cellside)]])
			sp.push([0, [x + cellside * 0.4, (y + cellside)]])
			sp.push([0, [x + cellside * 0.6, (y + cellside)]])
			sp.push([0, [x + cellside * 0.8, (y + cellside)]])
			sp.push([0, [x + cellside * 1.0, (y + cellside)]])
		}
		if(cell[i] & 16) {
			// this cell has a portal
			sp.push([2, [x + cellside * 0.2, y + cellside * 0.2]])
		}
	}


	function compare(a, b) {
		// always show character in front of portal
		if(a[0] == 1 && b[0] == 2) {
			return 1
		}
		if(a[0] == 2 && b[0] == 1) {
			return -1
		}
		// y-coordinate front-back sorting
		if(a[1][1] > b[1][1]) {
			return -1
		}
		if(a[1][1] < b[1][1]) {
			return 1
		}
		// x-direction left-right sorting
		if(a[1][0] < b[1][0]) {
			return -1
		}
		return 1
	}

	sp.sort(compare)

	addground([(off[0] + goff[0]) * cellside,
			(off[1] + goff[1]) * cellside])


	for(var i = 0; i < marks.length; ++i) {
		addmark([(marks[i][0] + goff[0] + off[0]) * cellside,
			(marks[i][1] + goff[1] + off[1]) * cellside],
			marks[i][2])
	}

	for(var i = 0; i < sp.length; ++i) {
		addsprite(sp[i])
	}
}

function render(time) {
	var render_time = Date.now()
	var dt = (render_time - last_render_time) * 0.001
	last_render_time = render_time
	mkscene()

	var bufinfo = twgl.createBufferInfoFromArrays(gl, hedges);

	gl.enable(gl.BLEND);
	gl.disable(gl.DEPTH_TEST);
	gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);

	var uniforms = {};

	uniforms = {
		dres: [scs * 1000, scs * 1000],
		fgcolor: [1.0, 1.0, 1.0, 1.0],
		groundtex: textures.ground,
		hedgetex: textures.hedge,
		coribtex: textures.corib,
		coristex: textures.coris,
		coriptex: textures.corip,
		portaltex: textures.portal,
		marktex: textures.mark,
	}
	gl.useProgram(hedge_program.program);
	twgl.setBuffersAndAttributes(gl, hedge_program, bufinfo);
	twgl.setUniforms(hedge_program, uniforms);

	twgl.drawBufferInfo(gl, gl.TRIANGLES, bufinfo);

	requestAnimationFrame(render)
}

function resize() {
	winw = $(window).width()
	winh = $(window).height()
	var ratio = winh / winw
	var scene_ratio = 1.0 // square scene
	var scw = winw
	var sch = winh
	var scx = 0
	var scy = 0
	if(ratio < scene_ratio) {
		// window is too wide
		scw = winh / scene_ratio
		scx = (winw - scw) * 0.5
		scs = sch / 1000 // height of screen square in "virtual pixels"
	} else {
		sch = winw * scene_ratio
		scy = (winh - sch) * 0.5
		scs = scw / 1000
	}
	cellside = (1000 / 5) * scs
	halfwall = Math.floor(cellside * hwratio);

	$("html").css("font-size", scs)

	twgl.resizeCanvasToDisplaySize(gl.canvas);
	gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

//	requestAnimationFrame(render)
}

function onsocketopen(e) {
//	socket.send("Hello, Server!")

}

// server sends info about surrounding landscape and all the visible players
// we cache players info locally in the format:
//	players[cpid] = {
//		server_pos, // position vector received from server
//		server_vel, // velocity vector received from server
//		server_ts, // when position from server was received
//		client_pos, // position vector interpolated by the client
//		client_vel, // client velocity, adjusted on server's update
//		client_ts, // when client position was last interpolated
//		scolor, // shirt color
//		pcolor, // pants color
//		glowing // whether this player is glowing
//	}
function onsocketmessage(e) {
	var tnow = Date.now() // current timstamp
	// warp happens when current player's character crosses maze cell's
	// boundary. we need to adjust local player states every time it happens
	var hwarp = 0
	var vwarp = 0

	// check if we are in waiting state, in that case the server expects
	// from us a special command to continue playing
	if(e.data.length == 1 && e.data[0] == 'w') {
		// waiting state initiated. show level up congrats screen
		$("#levelup").show()
		iswaiting = true
	}

	// load the visible area information
	cell = new Uint8Array(49);
	for(var i = 0; i < 49; ++i) {
		cell[i] = e.data.charCodeAt(i) - 0x20
	}

	// we don't know other players ids so we identify them by colors
	// and store them in the players object. if a player update did not
	// come in recent server message, we delete that user from players obj.
	var charinfo = e.data.substring(49)
	var pstr = charinfo.split(';')
	for(var i = 0; i < pstr.length; ++i) {
		if(!pstr[i].length) {
			break
		}
		// server's player data array:
		// [posx, posy, velx, vely, scolor=0...255, pcolor, glowing=0|1]
		var splayer = pstr[i].split(',')
		// make sure all parameters are numbers
		for(var k = 0; k < splayer.length; ++k) {
			splayer[k] *= 1
		}
		// cpid - color-based (incomplete) player id
		var cpid = splayer[4] * 256 + 1 * splayer[5]
		// check if player is already in our player obj
		if(!players[cpid]) {
			players[cpid] = players[cpid]? players[cpid]: {}
		}
		var p = players[cpid]
		// detect if warp just happened (happens when crossing
		// maze cell's boundary
		if(i == 0) {
			if(p.server_pos &&
				Math.sign(p.server_pos[0] - splayer[0]) &&
				Math.sign(p.server_pos[0] - splayer[0]) !=
					-Math.sign(splayer[2])) {
				// horizontal warp happened
				hwarp = -Math.sign(splayer[2])
				goff[0] += hwarp
			}
			if(p.server_pos &&
				Math.sign(p.server_pos[1] - splayer[1]) &&
				Math.sign(p.server_pos[1] - splayer[1]) !=
					-Math.sign(splayer[3])) {
				// vertical warp happened
				vwarp = -Math.sign(splayer[3])
				goff[1] += vwarp
			}
		}
		p.server_pos = [splayer[0], splayer[1]]
		p.server_vel = [splayer[2], splayer[3]]
		p.server_ts = tnow
		p.scolor = splayer[4]
		p.pcolor = splayer[5]
		p.glowing = (splayer[6] & 1)
		// other player left a mark
		if(splayer[6] & 2) {
			marks.push([p.server_pos[0] * 0.001 - 0.87 - goff[0],
				p.server_pos[1] * 0.001 - 1 - goff[1],
				p.scolor])
		}
		// adjust client's player velocity if necessary
		if(!p.client_pos) {
			p.client_pos = [p.server_pos[0], p.server_pos[1]]
			p.client_vel = [0, 0]
			p.client_ts = tnow
		} else {
			// adjust for warp if any has just happened
			p.client_pos[0] += hwarp * 1000
			p.client_pos[1] += vwarp * 1000
			// our client velocity is delta distance times
			// updates per second
			p.client_vel = [(p.server_pos[0] - p.client_pos[0]) *
					updates_per_second,
					(p.server_pos[1] - p.client_pos[1]) *
					updates_per_second]
		}
	}
	// we now remove all players that went out of sight
	for(var cpid in players) {
		if(!players[cpid].server_ts) {
			console.log(players)
		}
		if(players[cpid].server_ts != tnow) {
			delete players[cpid]
		}
	}
}

function onsocketclose(e) {
	console.log("Socket closed")
	$("#gameover").show()
}

function onsocketerror(e) {
	console.log("Socket error")
	$("#gameover").show()
}

function ws_connect() {
//	document.cookie = "groundhog_gardens_pid=" + myid + "; path=/"
	socket = new WebSocket("ws://wizstaff.com/ghog/eserv/?pid=" + myid)
	socket.onopen = onsocketopen
	socket.onclose = onsocketclose
	socket.onmessage = onsocketmessage
	socket.onerror = onsocketerror
}

var keyAllowed = {}

function mkmark() {
	marks.push([2.63 - goff[0] - off[0], 2.5 - goff[1] - off[1], null])
	socket.send('m') // serialize the mark so other visible players have it
}

function onkeydown(e) {
	if(keyAllowed[e.which] === false) return
	keyAllowed[e.which] = false
	switch(e.keyCode) {
	case 65: case 37: socket.send('l'); break
	case 87: case 38: socket.send('u'); break
	case 68: case 39: socket.send('r'); break
	case 83: case 40: socket.send('d'); break
	case 32: if(!start()) mkmark(); break;
	case 13: start(); break;
	}
}

function onkeyup(e) {
	keyAllowed[e.which] = true
	switch(e.keyCode) {
	case 65: case 37: socket.send('L'); break
	case 87: case 38: socket.send('U'); break
	case 68: case 39: socket.send('R'); break
	case 83: case 40: socket.send('D'); break
	}
}

function start() {
	if(!iswaiting) {
		return false
	}
	if(socket) {
		socket.send('s')
		iswaiting = false
	}
	$(".modal").hide()
	if(!level++) {
		setInterval(update_hud, 5000)
		update_hud()
		requestAnimationFrame(render)
	}
	goff = [0, 0] // reset the ground offset
	marks = [] // remove all markings from the previous level
	looped = false // reset the main character's looped state tracking
	return true
}

var direction = 0



/*
function ontap(e) {
	if(!started) return
	console.log(e.px_start_x, e.px_start_y)
	if(e.px_start_x > winw * 0.52) {
		socket.send('L')
		socket.send('r')
	} else if(e.px_start_x < winw * 0.48) {
		socket.send('R')
		socket.send('l')
	} else {
		socket.send('R')
		socket.send('L')
	}

	if(e.px_start_y > winh * 0.52) {
		socket.send('U')
		socket.send('d')
	} else if(e.px_start_y < winh * 0.48) {
		socket.send('D')
		socket.send('u')
	} else {
		socket.send('U')
		socket.send('D')
	}
}
*/

function getdir(x, y) {
	var mag = x * x + y * y
	if(mag < 300) return 0
	var theta = Math.atan2(y, x) * 180 / Math.PI
	if(theta < 0) theta = 360 + theta
	theta += 45;
	if(theta > 360) theta = theta - 360
	return Math.floor((theta * 0.0222222) + 1)
}

function onheldstart(e) {
}

function onheldmove(e) {
	var dir = getdir(e.px_tdelta_x, e.px_tdelta_y)
	if(dir == direction) return
	direction = dir
	switch(dir) {
	case 1: socket.send('d')
				 socket.send('l'); break;
	case 2: socket.send('l');
				 break;
	case 3: socket.send('u');
				 socket.send('l'); break;
	case 4: socket.send('u');
			 break;
	case 5: socket.send('u');
				socket.send('r'); break;
	case 6:
				socket.send('r');  break;
	case 7:  socket.send('d')
				socket.send('r'); break;
	case 8:  socket.send('d')
				 break;

	}
}

function onheldend(e) {
	direction = 0
	socket.send('U')
	socket.send('D')
	socket.send('L')
	socket.send('R')
}

function update_hud() {
	$.get("http://wizstaff.com/ghog/eserv?stats=1", function(data) {
		$("#hud").html("Level " + level + " of " + data.nrooms +
			" | Players Online: " + data.nonline)
	}, "json")
}

var double_click_last_ts = 0
function doubleclick() {
	var tnow = Date.now()
	if(tnow - double_click_last_ts < 500) {
		mkmark()
		double_click_last_ts = 0
	} else {
		double_click_last_ts = tnow
	}
}

$(function() {
	canv = $("#c")
	gl = twgl.getWebGLContext(document.getElementById("c"))
	hedge_program = twgl.createProgramInfo(gl, ["vs_hedge", "fs_hedge"])

	textures = twgl.createTextures(gl, {
		ground: { src: "img/ground.jpg" },
		hedge: { src: "img/hedge1.png" },
		corib: { src: "img/corib.png" },
		coris: { src: "img/coris.png" },
		corip: { src: "img/corip.png" },
		portal: { src: "img/portal.png" },
		mark: { src: "img/mark.png" }
	})


	$(window).resize(resize)
	resize()

	$.ajax({
		url: "http://wizstaff.com/ghog/eserv?canijoinpls=" +
			Math.floor(Math.random() * 56535)
	}).done(function(data) {
		if(data.startsWith("Hello, ")) {
			var newid = parseInt(data.substring(7))
			if(newid > 0) {
				myid = newid
				mycpid = myid & 0xffff
				ws_connect()
			}
		}
	})

	$(document).keydown(onkeydown)
	$(document).keyup(onkeyup)
	$(document).focus(function(e) {
		keyAllowed = {};
	})

	$("#welcome").click(start)
	$("#levelup").click(start).hide()
	$("#gameover").hide()
	iswaiting = true

	$(document).bind('uheldstart', onheldstart)
	$(document).bind('uheldmove', onheldmove)
	$(document).bind('uheldend', onheldend)
	$(document).bind('utap', doubleclick)

	mkmap()
})

})(jQuery)
