<repo_name>dshartnett/gravity<file_sep>/server.js
(function(){

"use strict";

function Timer()
{
	this.then = Date.now();
	this.int_count = 0;

	this.time_array = [];
	this.time_array_index = 0;
	this.frame_count = 0;
	this.time_count = 0;
}

Timer.prototype = {
	get interval(){
		var i = Date.now() - this.then;
		this.then = Date.now();

		this.time_array_index++;
		if (this.time_array_index >= CONST.FRAME_MAX) this.time_array_index = 0;

		if (this.frame_count >= CONST.FRAME_MAX) this.time_count -= this.time_array[this.time_array_index];
		else this.frame_count++;

		this.time_array[this.time_array_index] = i;

		this.time_count += i;
		this.int_count++;
		return i;
	},
	get i_count(){return this.int_count;},
	get frame_rate(){return 1000*this.frame_count/this.time_count;}
};

var CONST = require('./site/constants.js').getConstants();
var app = require('http').createServer(handler);
var io = require('socket.io').listen(app);
var staticReq = require('node-static'); // for serving files

// This will make all the files in the current folder accessible
var fileServer = new staticReq.Server('./site/');

// http://localhost:8080
app.listen(8080);

// If the URL of the socket server is opened in a browser
function handler(request, response) {request.addListener('end', function () {fileServer.serve(request, response);}); }
//function handler(request, response) {response.writeHeader(200, {'Content-Type': 'text/plain'});  response.end('Hello World\n');}

// Delete this row if you want to see debug messages
io.set('log level', 1);

var PLAYER_ID = 0;
var player_list = {};

var PAR_ID = 0;
var par_col = {};
var par_ids_to_del = [];

var OBJ_ID = 0;
var obj_col = {};
var obj_ids_to_del = [];

var power_up_count = {};
power_up_count[CONST.POWER_UP_CLOAK] = power_up_count[CONST.POWER_UP_INVINCIBLE] = power_up_count[CONST.POWER_UP_G_OBJECT] = power_up_count[CONST.POWER_UP_BOMB] = 0;

var team_count = [];

team_count[CONST.TEAM1] = team_count[CONST.TEAM2] = 0;

var main_timer = new Timer();

setInterval(function () {
	//console.log(Date.now() + "    " + Math.random()*Math.pow(2,32));
	var server_interval = main_timer.interval;
//	if(100*Math.random() < 1) obj_col[++OBJ_ID] = new G_Object(Math.random()*CONST.MAP_WIDTH, Math.random()*CONST.MAP_HEIGHT, 0, 0, 0, OBJ_ID, 0);
	for (var u in player_list) player_list[u].player.refresh(server_interval, par_col, obj_col, par_ids_to_del);
	for (var u in par_col) par_col[u].refresh(server_interval);
	for (var i = 0, len = par_ids_to_del.length; i < len; i++) {
		console.log("deleting particle " + par_ids_to_del[0] + " and emitting delete");
		io.sockets.emit("par_delete", par_ids_to_del[0]);
		delete par_col[par_ids_to_del.shift()];
	}
	power_up_count[CONST.POWER_UP_CLOAK] = power_up_count[CONST.POWER_UP_INVINCIBLE] = power_up_count[CONST.POWER_UP_G_OBJECT] = power_up_count[CONST.POWER_UP_BOMB] = 0;
	for (var u in obj_col){
		obj_col[u].refresh(server_interval, par_col, obj_col, player_list, obj_ids_to_del);
		if(obj_col[u].object_type == CONST.OBJ_POWER_UP) power_up_count[obj_col[u].power_up_type]++;
	}
	//console.log(power_up_count);
	for (var u in power_up_count)
	{
		//console.log(u);
		if (power_up_count[u] < 1)
			obj_col[++OBJ_ID] = new Power_Up_Object(OBJ_ID, Math.random()*CONST.MAP_WIDTH, Math.random()*CONST.MAP_HEIGHT, (Math.random()-0.5)*0.1, (Math.random()-0.5)*0.1, Number(u));
	}
	for (var i = 0, len = obj_ids_to_del.length; i < len; i++) {
		console.log("deleting object " + obj_ids_to_del[0] + " and emitting delete");
		io.sockets.emit("obj_delete", obj_ids_to_del[0]);
		delete obj_col[obj_ids_to_del.shift()];
	}
//	console.log(server_interval);
},1000/CONST.UPS);

// Listen for incoming connections from clients
io.sockets.on('connection', function (socket) {
	++PLAYER_ID;

	player_list[socket.id] = {
		start_interval: Date.now(),
		end_interval: null,
		player:new Player(PLAYER_ID, team_count[CONST.TEAM1]>team_count[CONST.TEAM2]?CONST.TEAM2:CONST.TEAM1)
	};
	team_count[player_list[socket.id].player.team]++;

	console.log("player " + player_list[socket.id].player.player_id + " connected on socket " + socket.id + ". ponging now...");
	
	socket.emit("player_id",{p_id:player_list[socket.id].player.player_id, team:player_list[socket.id].player.team});
	//socket.broadcast.emit('player_added', player_list[socket.id].player.player_id);
	socket.emit("pong",player_list[socket.id].player.data());

	socket.on("request_player_list", function(data) {
		var p_list = {};
		for (var u in player_list) p_list[player_list[u].player.player_id] = player_list[u].player.team;
		socket.emit("player_list", p_list);
	});
	
	socket.on("ping", function(data) {
		player_list[socket.id].end_interval = Date.now();
		var interval = player_list[socket.id].end_interval - player_list[socket.id].start_interval;
		//console.log("pinged with: " + data + " player id: " + player_list[socket.id].player.player_id + "  socket id: " + socket.id + " interval: " + interval);

		player_list[socket.id].player.move_command_state = data;
		//player_list[socket.id].player.refresh(interval);

		player_list[socket.id].start_interval = Date.now();

		//socket.volatile.emit("pong", player_list[socket.id].player.data());
		for (var u in player_list) socket.volatile.emit("pong", player_list[u].player.data(player_list[socket.id].player.player_id));
		for (var u in par_col) socket.volatile.emit("par", par_col[u].data());
		for (var u in obj_col) socket.volatile.emit("obj", obj_col[u].data());
		//setTimeout(function(){socket.emit("pong", player_list[socket.id].player.data());}, 200);
	});

	socket.on('disconnect', function(){
		console.log("player " + player_list[socket.id].player.player_id + " disconnected");
		socket.broadcast.emit('player_removed', player_list[socket.id].player.player_id);
		player_list[socket.id].player.disconnect(par_ids_to_del);
		team_count[player_list[socket.id].player.team]--;
		delete player_list[socket.id];
	});
});

function Particle(x,y,v_x,v_y,team,particle_id,player_id){
        this.pos_x = x;
        this.pos_y = y;
		this.last_pos_x = x;
		this.last_pos_y = y;
        this.v_x = v_x;
        this.v_y = v_y;
        this.mass = CONST.PARTICLE_MASS;
        this.charge = CONST.PARTICLE_CHARGE;
        this.size = CONST.PARTICLE_SIZE;
        this.half_size = this.size/2;
		
        this.team = team;
		this.particle_id = particle_id;
		this.player_id = player_id;

		this.data = function() {return {id:this.particle_id, x:this.pos_x, y:this.pos_y, v_x:this.v_x, v_y:this.v_y};};
		
        this.refresh = function(interval) {
				this.last_pos_x = this.pos_x;
				this.last_pos_y = this.pos_y;
                this.pos_x += this.v_x*interval;
                this.pos_y += this.v_y*interval;

                if (this.pos_x >= CONST.MAP_WIDTH) {this.pos_x = CONST.MAP_WIDTH - 1; this.v_x = -this.v_x*CONST.PARTICLE_WALL_LOSS;}
                else if (this.pos_x <= 0) {this.pos_x = 1; this.v_x = -this.v_x*CONST.PARTICLE_WALL_LOSS;}

                if (this.pos_y >= CONST.MAP_HEIGHT) {this.pos_y = CONST.MAP_HEIGHT - 1; this.v_y = -this.v_y*CONST.PARTICLE_WALL_LOSS;}
                else if (this.pos_y <= 0) {this.pos_y = 1; this.v_y = -this.v_y*CONST.PARTICLE_WALL_LOSS;}
        }
        return this;
}

function Player(player_id, team){
	this.player_id = player_id;
	this.team = team;
	
	this.pos_x = (0.1 + Math.random()*0.8)*CONST.MAP_WIDTH;
	this.pos_y = (0.1 + Math.random()*0.8)*CONST.MAP_HEIGHT;
	this.angle = Math.random()*2*Math.PI;
	this.v_x = 0;
	this.v_y = 0;
	
	this.radius = CONST.PLAYER_RADIUS;
	this.wing_angle = CONST.PLAYER_WING_ANGLE;
	
	this.health = CONST.PLAYER_MAX_HEALTH;
	this.mass = CONST.PLAYER_MASS;
	
	this.dying_timer = 0;
	this.cloak_timer = 0;
	this.invincible_timer = 0;
	this.fire_battery = 0;
	this.stat = 0;
	
	this.move_command_state = 0;
	
	this.server_set = false;
	
	var particle_ids = [];

	this.disconnect = function(par_ids_to_del)
	{
		for (var i in particle_ids) par_ids_to_del.push(particle_ids[i]);
	};
	
	this.spawn = function()
	{
		this.health = CONST.PLAYER_MAX_HEALTH;
		this.v_x = 0;
		this.v_y = 0;
		this.angle = Math.random()*2*Math.PI;
		this.pos_x = (0.1 + Math.random()*0.8)*CONST.MAP_WIDTH;
		this.pos_y = (0.1 + Math.random()*0.8)*CONST.MAP_HEIGHT;
		this.dying_timer = 0;
		this.fire_battery = 0;
		this.stat = 0;
	};

	this.data = function(other_id){
		if (this.stat & CONST.PLAYER_STATUS_CLOAKED && this.player_id != other_id)
			return {p_id:this.player_id, x:0, y:0, v_x:0, v_y:0, angle:0, health:this.health, stat:this.stat};
		else 
			return {p_id:this.player_id, x:this.pos_x, y:this.pos_y, v_x:this.v_x, v_y:this.v_y, angle:this.angle, health:this.health, stat:this.stat};
	};

	this.refresh = function (interval, par_col, obj_col, par_ids_to_del) {
		this.v_x -= CONST.PLAYER_FRICTION*this.v_x*interval;
		this.v_y -= CONST.PLAYER_FRICTION*this.v_y*interval;

		if (this.stat & CONST.PLAYER_STATUS_DEAD)
		{
			this.cloak_timer = 0;
			this.stat &= ~CONST.PLAYER_STATUS_CLOAKED;
			this.dying_timer -= interval;
			this.move_command_state = 0;
			this.angle -= CONST.PLAYER_ROTATE_SPEED*interval;
		}
		if (this.dying_timer < 0) this.spawn();
		
		if (this.stat & CONST.PLAYER_STATUS_CLOAKED) this.cloak_timer -= interval;
		if (this.cloak_timer < 0) this.stat &= ~CONST.PLAYER_STATUS_CLOAKED;
		
		if (this.stat & CONST.PLAYER_STATUS_INVINCIBLE) this.invincible_timer -= interval;
		if (this.invincible_timer < 0) this.stat &= ~CONST.PLAYER_STATUS_INVINCIBLE;
		
		//if (this.shield_fade > 0) this.shield_fade -= interval;
		if (this.fire_battery > 0) this.fire_battery -= interval;

		if (this.move_command_state & CONST.COMMAND_ROTATE_CC) this.angle -= CONST.PLAYER_ROTATE_SPEED*interval;
		if (this.move_command_state & CONST.COMMAND_MOVE_FORWARD)
		{
			this.v_x += CONST.PLAYER_ACCELERATION*interval*Math.cos(this.angle);
			this.v_y += CONST.PLAYER_ACCELERATION*interval*Math.sin(this.angle);
		}
		if (this.move_command_state & CONST.COMMAND_ROTATE_CW) this.angle += CONST.PLAYER_ROTATE_SPEED*interval;
		if (this.move_command_state & CONST.COMMAND_MOVE_BACKWARD)
		{
			this.v_x -= CONST.PLAYER_ACCELERATION*interval*Math.cos(this.angle);
			this.v_y -= CONST.PLAYER_ACCELERATION*interval*Math.sin(this.angle);
		}
		if (this.move_command_state & CONST.COMMAND_STRAFE_LEFT)
		{
			this.v_x += CONST.PLAYER_ACCELERATION*interval*Math.sin(this.angle);
			this.v_y -= CONST.PLAYER_ACCELERATION*interval*Math.cos(this.angle);
		}
		if (this.move_command_state & CONST.COMMAND_STRAFE_RIGHT)
		{
			this.v_x -= CONST.PLAYER_ACCELERATION*interval*Math.sin(this.angle);
			this.v_y += CONST.PLAYER_ACCELERATION*interval*Math.cos(this.angle);
		}
		
		if (this.fire_battery <= 0 && this.move_command_state & CONST.COMMAND_FIRE)
		{
			//this.request_state += CONST.COMMAND_FIRE;
			//this.fire_request = true;
			var temp_v_x = this.v_x;
			var temp_v_y = this.v_y;
			this.fire_battery = CONST.PLAYER_FIRE_BATTERY;
			this.v_x -= CONST.PARTICLE_MASS*CONST.PARTICLE_INITIAL_VELOCITY*Math.cos(this.angle)/this.mass;
			this.v_y -= CONST.PARTICLE_MASS*CONST.PARTICLE_INITIAL_VELOCITY*Math.sin(this.angle)/this.mass;
			par_col[++PAR_ID] = new Particle(
				this.pos_x,// + CONST.PLAYER_RADIUS*Math.cos(this.angle),
				this.pos_y,// + CONST.PLAYER_RADIUS*Math.sin(this.angle),
				temp_v_x + CONST.PARTICLE_INITIAL_VELOCITY*Math.cos(this.angle),
				temp_v_y + CONST.PARTICLE_INITIAL_VELOCITY*Math.sin(this.angle),
				this.team, PAR_ID, this.player_id);
				
			console.log("player " + this.player_id + " fired particle " + PAR_ID);
			particle_ids.push(PAR_ID);
			if (particle_ids.length > CONST.PLAYER_MAX_BULLETS){
				var last_p = particle_ids.shift();
				console.log("deleting particle " + last_p + " from player " + this.player_id);
				par_ids_to_del.push(last_p);
				//delete par_col[last_p];
			}
		}
		//else this.fire_request = false;
		if (this.stat & CONST.PLAYER_STATUS_HAS_G_OBJECT && this.move_command_state & CONST.COMMAND_G_OBJECT)
		{
			var temp_v_x = this.v_x;
			var temp_v_y = this.v_y;
			this.v_x -= CONST.G_OBJECT_LAUNCH_MASS*CONST.G_OBJECT_INITIAL_VELOCITY*Math.cos(this.angle)/this.mass;
			this.v_y -= CONST.G_OBJECT_LAUNCH_MASS*CONST.G_OBJECT_INITIAL_VELOCITY*Math.sin(this.angle)/this.mass;
			obj_col[++OBJ_ID] = new G_Object(
				this.pos_x,// + CONST.PLAYER_RADIUS*Math.cos(this.angle),
				this.pos_y,// + CONST.PLAYER_RADIUS*Math.sin(this.angle),
				temp_v_x + CONST.G_OBJECT_INITIAL_VELOCITY*Math.cos(this.angle),
				temp_v_y + CONST.G_OBJECT_INITIAL_VELOCITY*Math.sin(this.angle),
				this.team, OBJ_ID, this.player_id);
			console.log("player " + this.player_id + " launched G_Object " + OBJ_ID);
			this.stat -= CONST.PLAYER_STATUS_HAS_G_OBJECT;
		}
		if (this.stat & CONST.PLAYER_STATUS_HAS_BOMB && this.move_command_state & CONST.COMMAND_BOMB)
		{
			var temp_v_x = this.v_x;
			var temp_v_y = this.v_y;
			this.v_x -= CONST.BOMB_LAUNCH_MASS*CONST.BOMB_INITIAL_VELOCITY*Math.cos(this.angle)/this.mass;
			this.v_y -= CONST.BOMB_LAUNCH_MASS*CONST.BOMB_INITIAL_VELOCITY*Math.sin(this.angle)/this.mass;
			obj_col[++OBJ_ID] = new Bomb(
				this.pos_x,// + CONST.PLAYER_RADIUS*Math.cos(this.angle),
				this.pos_y,// + CONST.PLAYER_RADIUS*Math.sin(this.angle),
				temp_v_x + CONST.G_OBJECT_INITIAL_VELOCITY*Math.cos(this.angle),
				temp_v_y + CONST.G_OBJECT_INITIAL_VELOCITY*Math.sin(this.angle),
				this.team, OBJ_ID, this.player_id);
			console.log("player " + this.player_id + " launched bomb " + OBJ_ID);
			this.stat -= CONST.PLAYER_STATUS_HAS_BOMB;
		}

		this.pos_x += this.v_x*interval;
		this.pos_y += this.v_y*interval;
		
		this.stat &= ~CONST.PLAYER_STATUS_MOVING;
		this.stat |= this.move_command_state & CONST.PLAYER_MOVING?CONST.PLAYER_STATUS_MOVING:0;
		
		if (this.health < CONST.PLAYER_MAX_HEALTH && !(this.stat & CONST.PLAYER_STATUS_DEAD)) this.health += CONST.PLAYER_HEALTH_REGEN*interval;
		if (this.health > CONST.PLAYER_MAX_HEALTH) this.health = CONST.PLAYER_MAX_HEALTH;
		
		var invincible_health_save = this.health;
		
		for (var i in par_col)
		{
			if (par_col[i].team != this.team)
			{
				var diff_x = par_col[i].pos_x - this.pos_x;
				var diff_y = par_col[i].pos_y - this.pos_y;
				if (diff_x*diff_x + diff_y*diff_y < this.radius*this.radius)
				{
					this.health -= CONST.PARTICLE_DAMAGE;
					par_ids_to_del.push(i);
					console.log("collision");
				}
			}
		}
		
		for (var u in obj_col)
		{
			if (obj_col[u].object_type == CONST.OBJ_POWER_UP)
			{
				var diff_x = obj_col[u].pos_x - this.pos_x;
				var diff_y = obj_col[u].pos_y - this.pos_y;
				if (diff_x*diff_x + diff_y*diff_y < this.radius*this.radius)
				{
					switch(obj_col[u].power_up_type)
					{
						case CONST.POWER_UP_CLOAK:
							if (!(this.stat & CONST.PLAYER_STATUS_CLOAKED))
							{
								this.stat += CONST.PLAYER_STATUS_CLOAKED*obj_col[u].power_up();
								this.cloak_timer = CONST.PLAYER_CLOAK_TIMER_MAX;
							}
						break;
						case CONST.POWER_UP_INVINCIBLE:
							if (!(this.stat & CONST.PLAYER_STATUS_INVINCIBLE))
							{
								this.stat += CONST.PLAYER_STATUS_INVINCIBLE*obj_col[u].power_up();
								this.invincible_timer = CONST.PLAYER_INVINCIBLE_TIMER_MAX;
							}
						break;
						case CONST.POWER_UP_G_OBJECT:
							if (!(this.stat & CONST.PLAYER_STATUS_HAS_G_OBJECT))
							{
								this.stat += CONST.PLAYER_STATUS_HAS_G_OBJECT*obj_col[u].power_up();
							}
						break;
						case CONST.POWER_UP_BOMB:
							if (!(this.stat & CONST.PLAYER_STATUS_HAS_BOMB))
							{
								this.stat += CONST.PLAYER_STATUS_HAS_BOMB*obj_col[u].power_up();
							}
						break;
					}
				}
			}
		}

		if (this.pos_x - this.radius <= 0)
			{this.v_x = -CONST.PLAYER_WALL_LOSS*this.v_x; this.pos_x = this.radius; this.health -= CONST.WALL_DAMAGE_MULTIPLIER*this.v_x + CONST.WALL_DAMAGE_MINIMUM;}
		else if (this.pos_x + this.radius >= CONST.MAP_WIDTH)
			{this.v_x = -CONST.PLAYER_WALL_LOSS*this.v_x; this.pos_x = CONST.MAP_WIDTH-this.radius; this.health -= -CONST.WALL_DAMAGE_MULTIPLIER*this.v_x + CONST.WALL_DAMAGE_MINIMUM;}
		if (this.pos_y - this.radius <= 0)
			{this.v_y = -CONST.PLAYER_WALL_LOSS*this.v_y; this.pos_y = this.radius; this.health -= CONST.WALL_DAMAGE_MULTIPLIER*this.v_y + CONST.WALL_DAMAGE_MINIMUM;}
		else if (this.pos_y + this.radius >= CONST.MAP_HEIGHT)
			{this.v_y = -CONST.PLAYER_WALL_LOSS*this.v_y; this.pos_y = CONST.MAP_HEIGHT-this.radius; this.health -= -CONST.WALL_DAMAGE_MULTIPLIER*this.v_y + CONST.WALL_DAMAGE_MINIMUM;}

		if (this.stat & CONST.PLAYER_STATUS_INVINCIBLE) this.health = invincible_health_save;
		if (this.health < 0 && !(this.stat & CONST.PLAYER_STATUS_DEAD)) {this.stat |= CONST.PLAYER_STATUS_DEAD; this.dying_timer = CONST.PLAYER_DEAD_COUNTER_MAX;}
	};
	return this;
}

function Power_Up_Object(obj_id, x, y, v_x, v_y, power_up_type)
{
	this.obj_id = obj_id;
	this.pos_x = x;
	this.pos_y = y;
	this.v_x = v_x;
	this.v_y = v_y;
	this.radius = CONST.POWER_UP_RADIUS;
	this.object_type = CONST.OBJ_POWER_UP;
	this.power_up_type = power_up_type;
	
	this.item = 1;
	
	this.power_up = function()
	{
		var to_ret = this.item;
		this.item = 0;
		return to_ret;
	}
	
	this.data = function(){
		return {id:this.obj_id,
			type:this.object_type,
			p_t: this.power_up_type,
			x:this.pos_x,
			y:this.pos_y,
			v_x:this.v_x,
			v_y:this.v_y};
	};
	
	this.refresh = function(interval, par_col, obj_col, player_list, obj_ids_to_del)
	{		
		if (this.pos_x - this.radius <= 0)
			{this.v_x = -CONST.G_OBJECT_WALL_LOSS*this.v_x; this.pos_x = this.radius;}
		else if (this.pos_x + this.radius >= CONST.MAP_WIDTH)
			{this.v_x = -CONST.G_OBJECT_WALL_LOSS*this.v_x; this.pos_x = CONST.MAP_WIDTH-this.radius;}
		if (this.pos_y - this.radius <= 0)
			{this.v_y = -CONST.G_OBJECT_WALL_LOSS*this.v_y; this.pos_y = this.radius;}
		else if (this.pos_y + this.radius >= CONST.MAP_HEIGHT)
			{this.v_y = -CONST.G_OBJECT_WALL_LOSS*this.v_y; this.pos_y = CONST.MAP_HEIGHT-this.radius;}
			
		this.pos_x += this.v_x*interval;
		this.pos_y += this.v_y*interval;
		
		if (this.item == 0) obj_ids_to_del.push(this.obj_id);
	};
};

// Gravity object class
function G_Object(x, y, v_x, v_y, team, obj_id, player_id)
{
	this.pos_x = x;
	this.pos_y = y;
	this.v_x = v_x;
	this.v_y = v_y;
	this.radius = CONST.G_OBJECT_MIN_RADIUS;
	this.mass = CONST.G_OBJECT_MASS;

	this.object_type = CONST.OBJ_G_OBJECT;
	this.team = team;
	this.obj_id = obj_id;
	this.player_id = player_id;

	this.timer = CONST.G_OBJECT_TIME_TO_DET;
	this.stat = 0;

	this.data = function(){
		return {id:this.obj_id,
			type:this.object_type,
			x:this.pos_x,
			y:this.pos_y,
			v_x:this.v_x,
			v_y:this.v_y,
			team:this.team,
			stat:this.stat,
			timer:this.timer};
	};

	this.refresh = function(interval, par_col, obj_col, player_list, obj_ids_to_del)
	{		
		if (this.pos_x - this.radius <= 0)
			{this.v_x = -CONST.G_OBJECT_WALL_LOSS*this.v_x; this.pos_x = this.radius;}
		else if (this.pos_x + this.radius >= CONST.MAP_WIDTH)
			{this.v_x = -CONST.G_OBJECT_WALL_LOSS*this.v_x; this.pos_x = CONST.MAP_WIDTH-this.radius;}
		if (this.pos_y - this.radius <= 0)
			{this.v_y = -CONST.G_OBJECT_WALL_LOSS*this.v_y; this.pos_y = this.radius;}
		else if (this.pos_y + this.radius >= CONST.MAP_HEIGHT)
			{this.v_y = -CONST.G_OBJECT_WALL_LOSS*this.v_y; this.pos_y = CONST.MAP_HEIGHT-this.radius;}
			
		this.pos_x += this.v_x*interval;
		this.pos_y += this.v_y*interval;

		this.timer -= interval;
		if (this.timer < 0){
			if (this.stat == 0){
				this.stat |= CONST.G_OBJECT_STATUS_DETONATED;
				this.timer = CONST.G_OBJECT_TIME_TO_LAST;
				this.radius = CONST.G_OBJECT_MAX_RADIUS;
				console.log("boom!");
			}
			else if (this.stat & CONST.G_OBJECT_STATUS_DETONATED)//add to objects to delete
			{
			//	this.pos_x = this.pos_y = 100;
			//	this.v_x = this.v_y = 0.01;
			//	this.timer = CONST.G_OBJECT_TIME_TO_DET;
			//	this.stat = 0;
				obj_ids_to_del.push(this.obj_id);
				console.log("object added to delete array");
			}
		}

		if (this.stat & CONST.G_OBJECT_STATUS_DETONATED)
		{
			var disX, disY, distance2, distance, force;
			for (var i in par_col)
			{
					disX = this.pos_x - par_col[i].pos_x;
					disY = this.pos_y - par_col[i].pos_y;
					distance2 = disX*disX + disY*disY;
					if (distance2 > this.radius*this.radius)
					{
						distance = Math.sqrt(distance2);
						force = this.mass/distance2;
						par_col[i].v_x += interval*disX*force/distance;
						par_col[i].v_y += interval*disY*force/distance;
					}
					else
					{
						par_col[i].v_x -= CONST.G_OBJECT_FRICTION*par_col[i].v_x*interval;
						par_col[i].v_y -= CONST.G_OBJECT_FRICTION*par_col[i].v_y*interval;
					}
			}
			for (var i in obj_col)
			{
				if (obj_col[i].object_type == CONST.OBJ_BOMB)
				{
					disX = this.pos_x - obj_col[i].pos_x;
					disY = this.pos_y - obj_col[i].pos_y;
					distance2 = disX*disX + disY*disY;
					if (distance2 > this.radius*this.radius){
						distance = Math.sqrt(distance2);
						force = this.mass/distance2;
						obj_col[i].v_x += interval*disX*force/distance;
						obj_col[i].v_y += interval*disY*force/distance;
					}
					else
					{
						obj_col[i].v_x -= CONST.G_OBJECT_FRICTION*obj_col[i].v_x*interval;
						obj_col[i].v_y -= CONST.G_OBJECT_FRICTION*obj_col[i].v_y*interval;
					}
				}
			}
			for (var i in player_list){
				if (this.team != player_list[i].player.team){
					disX = this.pos_x - player_list[i].player.pos_x;
					disY = this.pos_y - player_list[i].player.pos_y;
					distance2 = disX*disX + disY*disY;
					if (distance2 > this.radius*this.radius){
						distance = Math.sqrt(distance2);
						force = this.mass/distance2;
						player_list[i].player.v_x += interval*disX*force/distance;
						player_list[i].player.v_y += interval*disY*force/distance;
					}
				}
			}
		}
	};
	return this;
}

function Bomb(x, y, v_x, v_y, team, obj_id, player_id)
{
	this.pos_x = x;
	this.pos_y = y;
	this.v_x = v_x;
	this.v_y = v_y;
	this.radius = CONST.BOMB_MIN_RADIUS;
	this.mass = CONST.BOMB_MASS;

	this.object_type = CONST.OBJ_BOMB;
	this.team = team;
	this.obj_id = obj_id;
	this.player_id = player_id;

	this.timer = CONST.BOMB_TIME_TO_DET;
	this.stat = 0;

	this.data = function(){
		return {id:this.obj_id,
			type:this.object_type,
			x:this.pos_x,
			y:this.pos_y,
			v_x:this.v_x,
			v_y:this.v_y,
			team:this.team,
			stat:this.stat,
			timer:this.timer};
	};

	this.refresh = function(interval, par_col, obj_col, player_list, obj_ids_to_del)
	{		
		if (this.pos_x - this.radius <= 0)
			{this.v_x = -CONST.BOMB_WALL_LOSS*this.v_x; this.pos_x = this.radius;}
		else if (this.pos_x + this.radius >= CONST.MAP_WIDTH)
			{this.v_x = -CONST.BOMB_WALL_LOSS*this.v_x; this.pos_x = CONST.MAP_WIDTH-this.radius;}
		if (this.pos_y - this.radius <= 0)
			{this.v_y = -CONST.BOMB_WALL_LOSS*this.v_y; this.pos_y = this.radius;}
		else if (this.pos_y + this.radius >= CONST.MAP_HEIGHT)
			{this.v_y = -CONST.BOMB_WALL_LOSS*this.v_y; this.pos_y = CONST.MAP_HEIGHT-this.radius;}
		
		if (this.stat & CONST.BOMB_STATUS_DETONATED) this.v_x = this.v_y = 0;
		
		this.pos_x += this.v_x*interval;
		this.pos_y += this.v_y*interval;

		this.timer -= interval;
		if (this.timer < 0){
			if (this.stat == 0){
				this.stat |= CONST.BOMB_STATUS_DETONATED;
				this.timer = CONST.BOMB_TIME_TO_LAST;
				//this.radius = CONST.BOMB_BLAST_RADIUS;
				console.log("boom!");
			}
			else if (this.stat & CONST.BOMB_STATUS_DETONATED)//add to objects to delete
			{
				obj_ids_to_del.push(this.obj_id);
				console.log("object added to delete array");
			}
		}

		if (this.stat & CONST.BOMB_STATUS_DETONATED)
		{
			var disX, disY, distance2, distance, force;
			/*for (var i in par_col)
			{
					disX = this.pos_x - par_col[i].pos_x;
					disY = this.pos_y - par_col[i].pos_y;
					distance2 = disX*disX + disY*disY;
					if (distance2 > this.radius*this.radius)
					{
						distance = Math.sqrt(distance2);
						force = this.mass/distance2;
						par_col[i].v_x += interval*disX*force/distance;
						par_col[i].v_y += interval*disY*force/distance;
					}
					else
					{
						par_col[i].v_x -= CONST.G_OBJECT_FRICTION*par_col[i].v_x*interval;
						par_col[i].v_y -= CONST.G_OBJECT_FRICTION*par_col[i].v_y*interval;
					}
			}*/
			for (var i in player_list){
				disX = this.pos_x - player_list[i].player.pos_x;
				disY = this.pos_y - player_list[i].player.pos_y;
				distance2 = disX*disX + disY*disY;
				if (distance2 < CONST.BOMB_MIN_RADIUS) distance2 = CONST.BOMB_MIN_RADIUS; // distance will be sqrt(CONST.BOMB_MIN_RADIUS) - lazy
				if (distance2 < CONST.BOMB_BLAST_RADIUS*CONST.BOMB_BLAST_RADIUS){
					distance = Math.sqrt(distance2);
					force = this.mass/distance2;
					player_list[i].player.v_x -= interval*disX*force/distance;
					player_list[i].player.v_y -= interval*disY*force/distance;
					if (!(player_list[i].player.stat & CONST.PLAYER_STATUS_INVINCIBLE) && this.team != player_list[i].player.team) player_list[i].player.health -= CONST.BOMB_DAMAGE_FACTOR*interval/distance;
				}
			}
		}
	};
	return this;
}
})();
<file_sep>/site/main.js

window['Gravity']={};

(function(){

"use strict";

var CONST = CONSTANTS['getConstants']();


var tick = new Howl({'urls': ['./files/tick1.mp3', './files/tick1.ogg']});
var blast = new Howl({'urls': ['./files/bomb_blast1.mp3', './files/bomb_blast1.ogg']});
var g_obj = new Howl({'urls': ['./files/g_obj1.mp3', './files/g_obj1.ogg']});


var key_arr = {
	r_cc:CONST.KEY_CODE_ROTATE_CC,
	f:CONST.KEY_CODE_MOVE_FORWARD,
	r_cw:CONST.KEY_CODE_ROTATE_CW,
	bk:CONST.KEY_CODE_MOVE_BACKWARD,
	s_l:CONST.KEY_CODE_STRAFE_LEFT,
	s_r:CONST.KEY_CODE_STRAFE_RIGHT,
	fr:CONST.KEY_CODE_FIRE,
	g:CONST.KEY_CODE_G_OBJECT,
	b:CONST.KEY_CODE_BOMB};

Gravity['options'] = function()
{
	var temp = key_arr.r_cc;
	key_arr.r_cc = key_arr.r_cw;
	key_arr.r_cw = temp;
}

Gravity['about'] = function()
{
	alert(CONST.ABOUT);
}

window.requestAnimFrame = (function () {
 return window.requestAnimationFrame
 					|| window.webkitRequestAnimationFrame
 					|| window.mozRequestAnimationFrame
 					|| window.oRequestAnimationFrame
 					|| window.msRequestAnimationFrame
 					|| function(callback){window.setTimeout(callback, 1000 / CONST.FPS);};
}
)();

function Timer()
{
	this.then = Date.now();
	this.int_count = 0;

	this.time_array = [];
	this.time_array_index = 0;
	this.frame_count = 0;
	this.time_count = 0;
}

Timer.prototype = {
	get interval(){
		var i = Date.now() - this.then;
		this.then = Date.now();

		this.time_array_index++;
		if (this.time_array_index >= CONST.FRAME_MAX) this.time_array_index = 0;

		if (this.frame_count >= CONST.FRAME_MAX) this.time_count -= this.time_array[this.time_array_index];
		else this.frame_count++;

		this.time_array[this.time_array_index] = i;

		this.time_count += i;
		this.int_count++;
		return i;
	},
	get i_count(){return this.int_count;},
	get frame_rate(){return 1000*this.frame_count/this.time_count;}
};//*/

function Background ()
{
	//var background_canvas = $("<canvas id='background_canvas' width='" + CONST.MAP_WIDTH + "' height='" + CONST.MAP_HEIGHT + "'></canvas>");
	//var background_context = background_canvas.get(0).getContext('2d');

	var sf_0 = [];
	var pf_0 = [];

	this.initialize = function () {
		//console.log("Generating background...");
		/*var gradient = background_context.createLinearGradient(0,0,CONST.MAP_WIDTH,CONST.MAP_HEIGHT);
		gradient.addColorStop(0.0,"black");
		gradient.addColorStop(0.3,"black");
		gradient.addColorStop(0.5,"purple");
		gradient.addColorStop(0.7,"black");
		gradient.addColorStop(1.0,"black");
		background_context.fillStyle = gradient;
		background_context.fillRect(0,0,CONST.MAP_WIDTH,CONST.MAP_HEIGHT);//*/
		
		//console.log("Generating star field...");
		//background_context.fillStyle = 'white';
		for (var i = 0; i < CONST.BACKGROUND_STARS0; i++){
			var star = {x:Math.random()*CONST.MAP_WIDTH, y: Math.random()*CONST.MAP_HEIGHT, star_size:Math.floor(1 + 2*Math.random())};
			sf_0.push(star);
			//background_context.fillRect(sf_0[i].x, sf_0[i].y, sf_0[i].star_size, sf_0[i].star_size);
		}
		/*
		for (var i = 0; i < CONST.BACKGROUND_PLANETS0; i++){
			var planet = {x:Math.random()*CONST.MAP_WIDTH, y: Math.random()*CONST.MAP_HEIGHT, planet_radius:CONST.BACKGROUND_PLANET_SIZE_MIN + CONST.BACKGROUND_PLANET_SIZE_RANGE*Math.random()};
			pf_0.push(planet);
		}
		*/
	}

	this.draw = function (context, map_pos_x, map_pos_y) {
		var gradient = context.createLinearGradient(-map_pos_x,-map_pos_y,CONST.MAP_WIDTH-map_pos_x,CONST.MAP_HEIGHT-map_pos_y);
		gradient.addColorStop(0.0,"black");
		gradient.addColorStop(0.3,"black");
		gradient.addColorStop(0.5,"purple");
		gradient.addColorStop(0.7,"black");
		gradient.addColorStop(1.0,"black");
		context.fillStyle = gradient;//'black';
		context.fillRect(0,0,CONST.CANVAS_WIDTH,CONST.CANVAS_HEIGHT);
		context.fillStyle = 'white';
		for (var i = 0; i < CONST.BACKGROUND_STARS0; i++)
			if (sf_0[i].x >= map_pos_x && sf_0[i].x <= map_pos_x + CONST.CANVAS_WIDTH && sf_0[i].y >= map_pos_y && sf_0[i].y <= map_pos_y + CONST.CANVAS_HEIGHT)
				context.fillRect(sf_0[i].x - map_pos_x, sf_0[i].y - map_pos_y, sf_0[i].star_size, sf_0[i].star_size);
		//*/
	}
	return this;
}

function Player(team){
	var pos_x = 0,
		pos_y = 0,
		angle = 0,
		v_x = 0,
		v_y = 0,
		radius = CONST.PLAYER_RADIUS,
		team = team,
		
		canvas_pos_x = CONST.CANVAS_WIDTH/2,
		canvas_pos_y = CONST.CANVAS_HEIGHT/2,
		
		mass = CONST.PLAYER_MASS,
		
		health = CONST.PLAYER_MAX_HEALTH,
		health_then = CONST.PLAYER_MAX_HEALTH,
		shield_fade = 0,
		fire_battery = 0,
		
		stat = 0,
		
		has_power_up_array = [0,0,0,0];
	
	this.map_pos_x = pos_x - CONST.CANVAS_WIDTH/2;
	this.map_pos_y = pos_y - CONST.CANVAS_HEIGHT/2;
	var move_command_state = 0;
	this.server_set = true;
	
	this.set_command_state = function(key_down)
	{
		if (key_down[key_arr.r_cc]) move_command_state |= CONST.COMMAND_ROTATE_CC;
		if (key_down[key_arr.f]) move_command_state |= CONST.COMMAND_MOVE_FORWARD;
		if (key_down[key_arr.r_cw]) move_command_state |= CONST.COMMAND_ROTATE_CW;
		if (key_down[key_arr.bk]) move_command_state |= CONST.COMMAND_MOVE_BACKWARD;
		if (key_down[key_arr.s_l]) move_command_state |= CONST.COMMAND_STRAFE_LEFT;
		if (key_down[key_arr.s_r]) move_command_state |= CONST.COMMAND_STRAFE_RIGHT;
		if (key_down[key_arr.fr]) move_command_state |= CONST.COMMAND_FIRE;
		if (key_down[key_arr.g]) move_command_state |= CONST.COMMAND_G_OBJECT;
		if (key_down[key_arr.b]) move_command_state |= CONST.COMMAND_BOMB;
	};
	
	this.move_command_state = function(){return move_command_state;};
	
	this.server_update = function(data)
	{
		var diff_x = data['x'] - pos_x;
		var diff_y = data['y'] - pos_y;
		var diff_a = data['angle'] - angle;
		
		if (Math.abs(diff_x) > CONST.POSITION_CORRECT_X)
			pos_x = data['x'];
		else pos_x += diff_x*CONST.POSITION_CORRECTION_PERCENT;
		
		if (Math.abs(diff_y) > CONST.POSITION_CORRECT_Y)
			pos_y = data['y'];
		else pos_y += diff_y*CONST.POSITION_CORRECTION_PERCENT;

		angle = data['angle'];
		v_x = data['v_x'];
		v_y = data['v_y'];
		health = data['health'];
		stat = data['stat'];
		this.server_set = true;
	};
	
	this.refresh = function (interval) {
		has_power_up_array[CONST.POWER_UP_CLOAK] = stat & CONST.PLAYER_STATUS_CLOAKED;
		has_power_up_array[CONST.POWER_UP_INVINCIBLE] = stat & CONST.PLAYER_STATUS_INVINCIBLE;
		has_power_up_array[CONST.POWER_UP_G_OBJECT] = stat & CONST.PLAYER_STATUS_HAS_G_OBJECT;
		has_power_up_array[CONST.POWER_UP_BOMB] = stat & CONST.PLAYER_STATUS_HAS_BOMB;

		v_x -= CONST.PLAYER_FRICTION*v_x*interval;
		v_y -= CONST.PLAYER_FRICTION*v_y*interval;
		
		if (stat & CONST.PLAYER_STATUS_DEAD)
		{
			move_command_state = 0;
			angle -= CONST.PLAYER_ROTATE_SPEED*interval;
		}
		
		if (shield_fade > 0) shield_fade -= interval;
		if (fire_battery > 0) fire_battery -= interval;
		
		if (move_command_state & CONST.COMMAND_ROTATE_CC) angle -= CONST.PLAYER_ROTATE_SPEED*interval;
		if (move_command_state & CONST.COMMAND_MOVE_FORWARD)
		{
			v_x += CONST.PLAYER_ACCELERATION*interval*Math.cos(angle);
			v_y += CONST.PLAYER_ACCELERATION*interval*Math.sin(angle);
		}
		if (move_command_state & CONST.COMMAND_ROTATE_CW) angle += CONST.PLAYER_ROTATE_SPEED*interval;
		if (move_command_state & CONST.COMMAND_MOVE_BACKWARD)
		{
			v_x -= CONST.PLAYER_ACCELERATION*interval*Math.cos(angle);
			v_y -= CONST.PLAYER_ACCELERATION*interval*Math.sin(angle);
		}
		if (move_command_state & CONST.COMMAND_STRAFE_LEFT)
		{
			v_x += CONST.PLAYER_ACCELERATION*interval*Math.sin(angle);
			v_y -= CONST.PLAYER_ACCELERATION*interval*Math.cos(angle);
		}
		if (move_command_state & CONST.COMMAND_STRAFE_RIGHT)
		{
			v_x -= CONST.PLAYER_ACCELERATION*interval*Math.sin(angle);
			v_y += CONST.PLAYER_ACCELERATION*interval*Math.cos(angle);
		}
		
		if (fire_battery <= 0 && move_command_state & CONST.COMMAND_FIRE)
		{
			fire_battery = CONST.PLAYER_FIRE_BATTERY;
			v_x -= CONST.PARTICLE_MASS*CONST.PARTICLE_INITIAL_VELOCITY*Math.cos(angle)/mass;
			v_y -= CONST.PARTICLE_MASS*CONST.PARTICLE_INITIAL_VELOCITY*Math.sin(angle)/mass;
		}
		if (stat & CONST.PLAYER_STATUS_HAS_G_OBJECT && move_command_state & CONST.COMMAND_G_OBJECT)
		{
			v_x -= CONST.G_OBJECT_LAUNCH_MASS*CONST.G_OBJECT_INITIAL_VELOCITY*Math.cos(angle)/mass;
			v_y -= CONST.G_OBJECT_LAUNCH_MASS*CONST.G_OBJECT_INITIAL_VELOCITY*Math.sin(angle)/mass;	
		}
		if (stat & CONST.PLAYER_STATUS_HAS_BOMB && move_command_state & CONST.COMMAND_BOMB)
		{
			v_x -= CONST.BOMB_LAUNCH_MASS*CONST.BOMB_INITIAL_VELOCITY*Math.cos(angle)/mass;
			v_y -= CONST.BOMB_LAUNCH_MASS*CONST.BOMB_INITIAL_VELOCITY*Math.sin(angle)/mass;	
		}

		pos_x += v_x*interval;
		pos_y += v_y*interval;

		if (health < health_then) shield_fade = CONST.PLAYER_SHIELD_FADE_MAX;
		health_then = health;

		// Handles wall bounce
		if (pos_x - radius <= 0)
			{v_x = -CONST.PLAYER_WALL_LOSS*v_x; pos_x = radius;}
		else if (pos_x + radius >= CONST.MAP_WIDTH)
			{v_x = -CONST.PLAYER_WALL_LOSS*v_x; pos_x = CONST.MAP_WIDTH-radius;}
		if (pos_y - radius <= 0)
			{v_y = -CONST.PLAYER_WALL_LOSS*v_y; pos_y = radius;}
		else if (pos_y + radius >= CONST.MAP_HEIGHT)
			{v_y = -CONST.PLAYER_WALL_LOSS*v_y; pos_y = CONST.MAP_HEIGHT-radius;}

		// Handles map location for drawing
		if (pos_x <= CONST.CANVAS_WIDTH/2)
			{this.map_pos_x = 0; canvas_pos_x = pos_x;}
		else if (pos_x >= CONST.MAP_WIDTH - CONST.CANVAS_WIDTH/2)
			{this.map_pos_x = CONST.MAP_WIDTH - CONST.CANVAS_WIDTH; canvas_pos_x = CONST.CANVAS_WIDTH - CONST.MAP_WIDTH + pos_x;}
		else {this.map_pos_x = pos_x - CONST.CANVAS_WIDTH/2; canvas_pos_x = CONST.CANVAS_WIDTH/2;}

		if (pos_y <= CONST.CANVAS_HEIGHT/2)
			{this.map_pos_y = 0; canvas_pos_y = pos_y;}
		else if (pos_y >= CONST.MAP_HEIGHT - CONST.CANVAS_HEIGHT/2)
			{this.map_pos_y = CONST.MAP_HEIGHT - CONST.CANVAS_HEIGHT; canvas_pos_y = CONST.CANVAS_HEIGHT - CONST.MAP_HEIGHT + pos_y;}
		else {this.map_pos_y = pos_y - CONST.CANVAS_HEIGHT/2; canvas_pos_y = CONST.CANVAS_HEIGHT/2;}

		move_command_state = 0;
	};
	
	function draw_player(context)
	{
		var gradient = context.createRadialGradient(0, 0, 0, 0, 0, 1*radius);
		gradient.addColorStop(0, CONST.TEAM_DARK[team]);
		gradient.addColorStop(1, "black");
		
		context.beginPath();
		context.moveTo(radius,0);
		context.lineTo(CONST.PLAYER_WING_ANGLE_COS,CONST.PLAYER_WING_ANGLE_SIN);
		context.lineTo(0,0);
		context.lineTo(CONST.PLAYER_WING_ANGLE_COS,-CONST.PLAYER_WING_ANGLE_SIN);
		context.lineTo(radius,0);
		context.closePath();
	
		context.fillStyle = gradient;
		context.fill();
		context.lineWidth = 1;
		context.strokeStyle = "gray";
		context.stroke();
	}
	
	function draw_moving(context)
	{
		var gradient;
		context.beginPath();
		context.moveTo(0,0);
		context.lineTo(CONST.PLAYER_WING_ANGLE_COS,CONST.PLAYER_WING_ANGLE_SIN);
		context.arc(0,0,radius,-CONST.PLAYER_WING_ANGLE,CONST.PLAYER_WING_ANGLE,true);
		context.lineTo(CONST.PLAYER_WING_ANGLE_COS,-CONST.PLAYER_WING_ANGLE_SIN);
		context.moveTo(0,0);
		context.closePath();
		gradient = context.createRadialGradient(0, 0, 0, 0, 0, radius);
		gradient.addColorStop(0, CONST.TEAM_LIGHT[team]);
		gradient.addColorStop(0.4+0.3*Math.random(), CONST.TEAM_LIGHT[team]);
		gradient.addColorStop(1, "transparent");
		context.strokeStyle = "transparent";
		context.fillStyle = gradient;
		context.fill();
		context.stroke();
	}
	
	function draw_cloaked(context)
	{
		var gradient;
		context.beginPath();
		context.arc(0,0,CONST.PLAYER_SHIELD_RADIUS,0,2*Math.PI,false);
		gradient = context.createRadialGradient(0, 0, 0, 0, 0, radius*2);
		gradient.addColorStop(0, "transparent");
		gradient.addColorStop(1, "violet");
		context.fillStyle = gradient;
		context.fill();
		context.stroke();
	}
	
	function draw_invincible(context)
	{
		var gradient;
		context.beginPath();
		context.arc(0,0,CONST.PLAYER_SHIELD_RADIUS,0,2*Math.PI,false);
		gradient = context.createRadialGradient(0, 0, 0, 0, 0, radius*2);
		gradient.addColorStop(0, "transparent");
		gradient.addColorStop(0.5+0.1*Math.random(), "white");
		gradient.addColorStop(1, "transparent");
		context.fillStyle = gradient;
		context.fill();
		context.stroke();
	}
	
	function draw_shield_fade(context)
	{
		var gradient;
		context.beginPath();
		context.arc(0,0,CONST.PLAYER_SHIELD_RADIUS,0,2*Math.PI,false);
		gradient = context.createRadialGradient(0, 0, 0, 0, 0, radius*2);
		gradient.addColorStop(0, "transparent");
		gradient.addColorStop(1-shield_fade/CONST.PLAYER_SHIELD_FADE_MAX, CONST.TEAM_LIGHT[team]);
		gradient.addColorStop(1, "transparent");
		context.fillStyle = gradient;
		context.fill();
		context.stroke();
	}
	
	this.draw = function (context) {
		context.save();
		context.translate(canvas_pos_x, canvas_pos_y);
		context.rotate(angle);

		var gradient;
		
		draw_player(context);
		if (stat & CONST.PLAYER_STATUS_MOVING) draw_moving(context);
		if (has_power_up_array[CONST.POWER_UP_CLOAK])draw_cloaked(context);
		if (has_power_up_array[CONST.POWER_UP_INVINCIBLE]) draw_invincible(context);
		if (shield_fade > 0) draw_shield_fade(context);
		
		context.restore();

		if (health > 0){
		for (var i = -1; i <= 1; i += 2){
			context.save();
			context.translate(CONST.HEALTH_LOCATION_X,CONST.HEALTH_LOCATION_Y);
			context.scale(i,1)
			context.beginPath();
			gradient = context.createLinearGradient(0,0,CONST.HEALTH_WIDTH,0);
			gradient.addColorStop(0, CONST.TEAM_DARK[team]);
			gradient.addColorStop(1, CONST.TEAM_LIGHT[team]);
			context.rect(0,0,CONST.HEALTH_WIDTH*health/CONST.PLAYER_MAX_HEALTH, CONST.HEALTH_HEIGHT);
			context.fillStyle = gradient;
			context.fill();
			context.lineWidth = 1;
			context.strokeStyle = CONST.TEAM_DARK[team];
			context.stroke();
			context.restore();
		}}
		
		for (var u in has_power_up_array)
		{
			if (has_power_up_array[u])
			{
				context.save();
				context.translate(CONST.HAS_LOC_X[u], CONST.HAS_LOC_Y[u]);
				gradient = context.createRadialGradient(CONST.INFO_BOX_SIDE/2, CONST.INFO_BOX_SIDE/2, 0, CONST.INFO_BOX_SIDE/2, CONST.INFO_BOX_SIDE/2, CONST.INFO_BOX_SIDE);
				gradient.addColorStop(0, CONST.TEAM_LIGHT[CONST.TEAM0]);
				gradient.addColorStop(1, CONST.TEAM_DARK[CONST.TEAM0]);
				context.fillStyle = gradient;
				context.lineWidth = 1;
				context.strokeStyle = CONST.TEAM_DARK[CONST.TEAM0];
				context.fillRect(0, 0, CONST.INFO_BOX_SIDE, CONST.INFO_BOX_SIDE);
				
				context.fillStyle = CONST.POWER_UP_TEXT_COLOR;
				context.font = CONST.POWER_UP_FONT;
				context.fillText(CONST.POWER_UP_CHAR[u],3,CONST.INFO_BOX_SIDE-5);
				
				context.restore();
			}
		}
	};
	
	this.net_draw = function(context, map_pos_x, map_pos_y){
		if (stat & CONST.PLAYER_STATUS_CLOAKED) return;
		var x = pos_x - map_pos_x;
		var y = pos_y - map_pos_y;
		if (x >= (-radius) && x <= (CONST.CANVAS_WIDTH + radius) && y >= (-radius) && y <= (CONST.CANVAS_HEIGHT + radius))
		{
			context.save();
			
			context.translate(x, y);
			context.rotate(angle);
			
			draw_player(context);
			if (stat & CONST.PLAYER_STATUS_MOVING) draw_moving(context);
			if (shield_fade > 0) draw_shield_fade(context);
			if (has_power_up_array[CONST.POWER_UP_INVINCIBLE]) draw_invincible(context);
			
			context.restore();
		}
	};
	return this;
}

function Particle(p_id,x,y,v_x,v_y,p_color){
	var id = p_id,
		pos_x = x,
		pos_y = y,
		v_x = v_x,
		v_y = v_y,
		mass = CONST.PARTICLE_MASS,
		charge = CONST.PARTICLE_CHARGE,
		p_color = p_color,
		size = CONST.PARTICLE_SIZE,
		half_size = size/2;
	
	this.server_update = function(data)
	{
		var diff_x = data['x'] - pos_x;
		var diff_y = data['y'] - pos_y;
		
		if (Math.abs(diff_x) > CONST.PARTICLE_CORRECT_X)
			pos_x = data['x'];
		else pos_x += diff_x*CONST.PARTICLE_CORRECTION_PERCENT;
		
		if (Math.abs(diff_y) > CONST.PARTICLE_CORRECT_Y)
			pos_y = data['y'];
		else pos_y += diff_y*CONST.PARTICLE_CORRECTION_PERCENT;
		
		v_x = data['v_x'];
		v_y = data['v_y'];
	};
	
	this.refresh = function(interval) {
		pos_x += v_x*interval;
		pos_y += v_y*interval;
		
		if (pos_x >= CONST.MAP_WIDTH) {pos_x = CONST.MAP_WIDTH - 1; v_x = -v_x*CONST.PARTICLE_WALL_LOSS;}
		else if (pos_x <= 0) {pos_x = 1; v_x = -v_x*CONST.PARTICLE_WALL_LOSS;}
		
		if (pos_y >= CONST.MAP_HEIGHT) {pos_y = CONST.MAP_HEIGHT - 1; v_y = -v_y*CONST.PARTICLE_WALL_LOSS;}
		else if (pos_y <= 0) {pos_y = 1; v_y = -v_y*CONST.PARTICLE_WALL_LOSS;}
	};

	this.draw = function(context, map_pos_x, map_pos_y) {
		var gradient;
		var x = pos_x - map_pos_x;
		var y = pos_y - map_pos_y;
		if (x >= 0 && x <= CONST.CANVAS_WIDTH && y >= 0 && y <= CONST.CANVAS_HEIGHT)
		{
			context.save();
			context.translate(x, y);
			context.beginPath();
			context.arc(0,0,size,0,2*Math.PI,false);
			gradient = context.createRadialGradient(0, 0, 0, 0, 0, size);
			gradient.addColorStop(0, "black");
			gradient.addColorStop(1, p_color);
			context.fillStyle = gradient;
			context.fill();
			context.lineWidth = 1;
			context.strokeStyle = p_color;
			context.stroke();
			context.restore();
		}
	};
	return this;
}

function G_Object(x, y, v_x, v_y, team)
{
	var pos_x = x,
		pos_y = y,
		v_x = v_x,
		v_y = v_y,
		radius = CONST.G_OBJECT_MIN_RADIUS,
		mass = CONST.G_OBJECT_MASS,

		object_type = CONST.OBJ_G_OBJECT,
		team = team,

		timer = CONST.G_OBJECT_TIME_TO_DET,
		stat = 0,

		timer_ceil = Math.ceil(timer/1000),
		blast_played = false;

	this.server_update = function(data)
	{
		var diff_x = data['x'] - pos_x;
		var diff_y = data['y'] - pos_y;

		if (Math.abs(diff_x) > CONST.OBJECT_POSITION_CORRECT_X)
			pos_x = data['x'];
		else pos_x += diff_x*CONST.POSITION_CORRECTION_PERCENT;

		if (Math.abs(diff_y) > CONST.OBJECT_POSITION_CORRECT_Y)
			pos_y = data['y'];
		else pos_y += diff_y*CONST.POSITION_CORRECTION_PERCENT;

		v_x = data['v_x'];
		v_y = data['v_y'];
		stat = data['stat'];
		timer = data['timer'];
	};
	
	this.refresh = function(interval, par_col, player_list, obj_ids_to_del)
	{		
		if (pos_x - radius <= 0)
			{v_x = -CONST.G_OBJECT_WALL_LOSS*v_x; pos_x = radius;}
		else if (pos_x + radius >= CONST.MAP_WIDTH)
			{v_x = -CONST.G_OBJECT_WALL_LOSS*v_x; pos_x = CONST.MAP_WIDTH-radius;}
		if (pos_y - radius <= 0)
			{v_y = -CONST.G_OBJECT_WALL_LOSS*v_y; pos_y = radius;}
		else if (pos_y + radius >= CONST.MAP_HEIGHT)
			{v_y = -CONST.G_OBJECT_WALL_LOSS*v_y; pos_y = CONST.MAP_HEIGHT-radius;}
			
		pos_x += v_x*interval;
		pos_y += v_y*interval;

		if (stat & CONST.G_OBJECT_STATUS_DETONATED) radius = CONST.G_OBJECT_MAX_RADIUS;

		timer -= interval;
		if (timer < 0) timer = 0;
	}
	
	this.draw = function (context, map_pos_x, map_pos_y)
	{
		var x = pos_x - map_pos_x;
		var y = pos_y - map_pos_y;

		if (x >= (-radius) && x <= (CONST.CANVAS_WIDTH + radius) && y >= (-radius) && y <= (CONST.CANVAS_HEIGHT + radius))
		{
			if (Math.ceil(timer/1000) < timer_ceil && !(stat & CONST.G_OBJECT_STATUS_DETONATED))
			{
				tick.play();
				timer_ceil = Math.ceil(timer/1000);
			}
			else if (stat & CONST.G_OBJECT_STATUS_DETONATED && !blast_played)
			{
				g_obj.play();
				blast_played = true;
			}
			
			context.save();
			context.translate(x, y);

			var gradient = context.createRadialGradient(0, 0, 0, 0, 0, 1*radius);
			if (stat == 0){
				gradient.addColorStop(0, CONST.TEAM_LIGHT[team]);
				gradient.addColorStop(1, CONST.TEAM_DARK[team]);
			} else if (stat & CONST.G_OBJECT_STATUS_DETONATED){
				gradient.addColorStop(0, "transparent");
				gradient.addColorStop(1 - timer/CONST.G_OBJECT_TIME_TO_LAST, CONST.TEAM_DARK[team]);
				gradient.addColorStop(1, "transparent");
			}
			context.beginPath();
			context.arc(0,0,radius,0,2*Math.PI,false);
			context.fillStyle = gradient;
			context.fill();
			context.stroke();
			if (stat == 0){
				context.fillStyle = CONST.POWER_UP_TEXT_COLOR;
				context.font = CONST.POWER_UP_FONT;
				context.fillText(timer_ceil,-6,5);
			}
			context.restore();
		}
	}
	
	return this;
};

function Bomb(x, y, v_x, v_y, team, obj_id, player_id)
{
	var pos_x = x,
		pos_y = y,
		v_x = v_x,
		v_y = v_y,
		radius = CONST.BOMB_MIN_RADIUS,
		mass = CONST.BOMB_MASS,

		object_type = CONST.OBJ_BOMB,
		team = team,
		obj_id = obj_id,
		player_id = player_id,

		timer = CONST.BOMB_TIME_TO_DET,
		stat = 0,
		
		timer_ceil = Math.ceil(timer/1000),
		blast_played = false;
	
	this.server_update = function(data)
	{
		var diff_x = data['x'] - pos_x;
		var diff_y = data['y'] - pos_y;

		if (Math.abs(diff_x) > CONST.OBJECT_POSITION_CORRECT_X)
			pos_x = data['x'];
		else pos_x += diff_x*CONST.POSITION_CORRECTION_PERCENT;

		if (Math.abs(diff_y) > CONST.OBJECT_POSITION_CORRECT_Y)
			pos_y = data['y'];
		else pos_y += diff_y*CONST.POSITION_CORRECTION_PERCENT;

		v_x = data['v_x'];
		v_y = data['v_y'];
		stat = data['stat'];
		timer = data['timer'];
	};
	
	this.refresh = function(interval, par_col, player_list, obj_ids_to_del)
	{		
		if (pos_x - CONST.BOMB_MIN_RADIUS <= 0)
			{v_x = -CONST.BOMB_WALL_LOSS*v_x; pos_x = radius;}
		else if (pos_x + CONST.BOMB_MIN_RADIUS >= CONST.MAP_WIDTH)
			{v_x = -CONST.BOMB_WALL_LOSS*v_x; pos_x = CONST.MAP_WIDTH-radius;}
		if (pos_y - CONST.BOMB_MIN_RADIUS <= 0)
			{v_y = -CONST.BOMB_WALL_LOSS*v_y; pos_y = radius;}
		else if (pos_y + CONST.BOMB_MIN_RADIUS >= CONST.MAP_HEIGHT)
			{v_y = -CONST.BOMB_WALL_LOSS*v_y; pos_y = CONST.MAP_HEIGHT-radius;}

		if (stat & CONST.BOMB_STATUS_DETONATED)
		{
			radius = CONST.BOMB_BLAST_RADIUS;
			v_x = v_y = 0;
		}
		pos_x += v_x*interval;
		pos_y += v_y*interval;
		
		timer -= interval;
		if (timer < 0) timer = 0;
	};
	
	this.draw = function (context, map_pos_x, map_pos_y)
	{
		var x = pos_x - map_pos_x;
		var y = pos_y - map_pos_y;

		if (x >= (-radius) && x <= (CONST.CANVAS_WIDTH + radius) && y >= (-radius) && y <= (CONST.CANVAS_HEIGHT + radius))
		{
			if (Math.ceil(timer/1000) < timer_ceil && !(stat & CONST.G_OBJECT_STATUS_DETONATED))
			{
				tick.play();
				timer_ceil = Math.ceil(timer/1000);
			}
			else if (stat & CONST.G_OBJECT_STATUS_DETONATED && !blast_played)
			{
				blast.play();
				blast_played = true;
			}
			context.save();
			context.translate(x, y);

			var gradient = context.createRadialGradient(0, 0, 0, 0, 0, 1*radius);
			if (stat == 0){
				gradient.addColorStop(0, CONST.TEAM_LIGHT[team]);
				gradient.addColorStop(1, CONST.TEAM_DARK[team]);
			} else if (stat & CONST.BOMB_STATUS_DETONATED){
				gradient.addColorStop(0, "transparent");
				gradient.addColorStop(1 - timer/CONST.BOMB_TIME_TO_LAST, CONST.TEAM_LIGHT[team]);
				gradient.addColorStop(1, "transparent");
			}
			context.beginPath();
			context.arc(0,0,radius,0,2*Math.PI,false);
			context.fillStyle = gradient;
			context.fill();
			context.lineWidth = 0;
			context.strokeStyle = "transparent";
			context.stroke();
			if (stat == 0){
				context.fillStyle = CONST.POWER_UP_TEXT_COLOR;
				context.font = CONST.POWER_UP_FONT;
				context.fillText(timer_ceil,-6,5);
			}
			context.restore();
		}
	}
	
	return this;
}

function Power_Up_Object(x, y, v_x, v_y, power_up_type)
{
	var pos_x = x,
		pos_y = y,
		v_x = v_x,
		v_y = v_y,
		radius = CONST.POWER_UP_RADIUS,
		type = CONST.OBJ_POWER_UP,
		power_up_type = power_up_type,
		text = CONST.POWER_UP_CHAR[power_up_type];
	
	this.server_update = function(data)
	{
		var diff_x = data['x'] - pos_x;
		var diff_y = data['y'] - pos_y;

		if (Math.abs(diff_x) > CONST.OBJECT_POSITION_CORRECT_X)
			pos_x = data['x'];
		else pos_x += diff_x*CONST.POSITION_CORRECTION_PERCENT;

		if (Math.abs(diff_y) > CONST.OBJECT_POSITION_CORRECT_Y)
			pos_y = data['y'];
		else pos_y += diff_y*CONST.POSITION_CORRECTION_PERCENT;

		v_x = data['v_x'];
		v_y = data['v_y'];
	};
	
	this.refresh = function(interval)
	{		
		if (pos_x - radius <= 0)
			{v_x = -CONST.G_OBJECT_WALL_LOSS*v_x; pos_x = radius;}
		else if (pos_x + radius >= CONST.MAP_WIDTH)
			{v_x = -CONST.G_OBJECT_WALL_LOSS*v_x; pos_x = CONST.MAP_WIDTH-radius;}
		if (pos_y - radius <= 0)
			{v_y = -CONST.G_OBJECT_WALL_LOSS*v_y; pos_y = radius;}
		else if (pos_y + radius >= CONST.MAP_HEIGHT)
			{v_y = -CONST.G_OBJECT_WALL_LOSS*v_y; pos_y = CONST.MAP_HEIGHT-radius;}
			
		pos_x += v_x*interval;
		pos_y += v_y*interval;
	};
	
	this.draw = function (context, map_pos_x, map_pos_y)
	{
		var x = pos_x - map_pos_x;
		var y = pos_y - map_pos_y;

		if (x >= (-radius) && x <= (CONST.CANVAS_WIDTH + radius) && y >= (-radius) && y <= (CONST.CANVAS_HEIGHT + radius))
		{
			context.save();
			context.translate(x, y);

			var gradient = context.createRadialGradient(0, 0, 0, 0, 0, 1*radius);
			gradient.addColorStop(0, CONST.TEAM_LIGHT[CONST.TEAM0]);
			gradient.addColorStop(1, CONST.TEAM_DARK[CONST.TEAM0]);
				
			context.beginPath();
			context.arc(0,0,radius,0,2*Math.PI,false);
			context.fillStyle = gradient;
			context.fill();
			context.stroke();
			
			context.fillStyle = CONST.POWER_UP_TEXT_COLOR;
			context.font = CONST.POWER_UP_FONT;
			context.fillText(text,-6,5);
		
			context.restore();
		}
	}
};

function Game()
{
	var request_id;
	var interval_id;
	
	var main_canvas = $("<canvas id='main_canvas' width='" + CONST.CANVAS_WIDTH + "' height='" + CONST.CANVAS_HEIGHT + "'>Update your browser</canvas>");
	var main_context = main_canvas.get(0).getContext('2d');
	
	var server_url = 'http://' + (window.location.hostname || "localhost") + ':8080';
	var socket;
	var socket_worker;

	var key_down = {};

	var background = new Background();
	
	var draw_timer = new Timer();
	var refresh_timer = new Timer();
	var ping_timer = new Timer();
	var ping = 0;
	var ping_arr = [];
	
	for (var i=0; i<200;i++) ping_arr.push(0);
	
	var debug = false;
	var frame_rates = false;
	var quit = false;
	
	var player_col = {};
	var player_id = 0;

	var par_col = {};
	var obj_col = {};
	
	function draw_ping (context)
	{
		var ping_max = 200;
		context.save();
		context.fillStyle = "lime";
		context.fillText(ping_max, 10, 10);
		context.fillText((1000/ping_timer.frame_rate).toFixed(1), 10, 30);
		context.fillText("0", 10, 50);
		for (var i = 0, len = ping_arr.length; i < len; i++)
		{
			context.beginPath();
			context.strokeStyle = "lime";
			context.lineWidth = 1;
			context.moveTo(40+i,ping_max-ping_arr[i]>0?50*(ping_max-ping_arr[i])/ping_max:0);
			context.lineTo(40+i,50);
			context.stroke();
		}
		context.restore();
	}
	
	this.initialize = function () {
	
		background.initialize();
		
		$(window)['keydown'](function (key_code) {
			if (debug) console.log("Key down: " + key_code.keyCode);
			key_down[key_code.keyCode]=true;
			});
		$(window)['keyup'](function (key_code) {
			if (key_code.keyCode==CONST.KEY_CODE_DEBUG) { debug = !debug; console.log("Debug " + (debug?"on.":"off.")); }
			if (key_code.keyCode==CONST.KEY_CODE_FRAMERATE) frame_rates = !frame_rates;
			if (debug) console.log("Key up: " + key_code.keyCode);
			key_down[key_code.keyCode]=false;
			});
		
		console.log("Attempting to connect to " + server_url);
		
		socket = io['connect'](server_url);
		//if (socket) console.log("Server is down");
		socket['on']("connect", function(){console.log("connected...");});
		socket['on']("error", function(){console.log("connection error...");});
		
		socket['on']("player_id", function(data){ player_id = data['p_id']; player_col[player_id] = new Player(data['team']); });
		
		socket['on']("player_list", function(data){
			for (var u in data) if (typeof player_col[u] === 'undefined') player_col[u] = new Player(data[u]);
		});
		socket['on']("player_removed", function(data){delete player_col[data]; });
		
		socket['on']("par", function(data){
			if (typeof par_col[data['id']] === 'undefined') par_col[data['id']] = new Particle(data['id'],data['x'],data['y'],data['v_x'],data['v_y'],CONST.PARTICLE_COLOR);
			par_col[data['id']].server_update(data);
		});
		socket['on']("par_delete", function(data) { delete par_col[data]; });

		socket['on']("obj", function(data){
			if (typeof obj_col[data['id']] === 'undefined'){
				switch (data['type']){
					case CONST.OBJ_POWER_UP:
						obj_col[data['id']] = new Power_Up_Object(data['x'], data['y'], data['v_x'], data['v_y'], data['p_t']);
					break;
					case CONST.OBJ_G_OBJECT:
						obj_col[data['id']] = new G_Object(data['x'], data['y'], data['v_x'], data['v_y'], data['team']);
					break;
					case CONST.OBJ_BOMB:
						obj_col[data['id']] = new Bomb(data['x'], data['y'], data['v_x'], data['v_y'], data['team']);
					break;
				}
			}
			
			obj_col[data['id']].server_update(data);
		});
		socket['on']("obj_delete", function(data) { delete obj_col[data]; });
		
		var init_wait = function(){
			if (player_id != 0){
				socket['on']("pong", function(data){
					if (player_id == 0) return;
					if (typeof player_col[data['p_id']] === 'undefined')
					{
						socket['emit']("request_player_list", player_id);
						return;
					}
					if (data['p_id']==player_id)
					{
						ping = ping_timer.interval;
						ping_arr.shift();
						ping_arr.push(ping);
						if (debug) console.log(ping);
					}
					
					player_col[data['p_id']].server_update(data);
				});
				
				main_canvas['appendTo']("body");
				refresh();
				interval_id = setInterval(refresh, 1000/CONST.UPS);
				draw();
			} else setTimeout(init_wait, 500);
		};
		
		init_wait();
	};

	function refresh() {
		var refresh_interval = refresh_timer.interval;
		
		player_col[player_id].set_command_state(key_down);

		if (player_col[player_id].server_set)
		{
			socket['emit']("ping", player_col[player_id].move_command_state());
			player_col[player_id].server_set = false;
		}
		
		for (var u in player_col) player_col[u].refresh(refresh_interval);
		for (var u in par_col) par_col[u].refresh(refresh_interval);
		for (var u in obj_col) obj_col[u].refresh(refresh_interval);

		if (frame_rates) console.log("refresh interval: " + refresh_interval + " frame rate: " + refresh_timer.frame_rate.toFixed(2));
		if (key_down[81]) {quit = true; clearInterval(interval_id); console.log("Quit command sent");}
		//if (!socket.socket.connected) {console.log("socket not connected, quitting"); quit = true;clearInterval(interval_id);}
	};

	function draw() {
		if (!quit) request_id = window.requestAnimFrame(draw);//self.draw);
		
		var draw_interval = draw_timer.interval;
		background.draw(main_context, player_col[player_id].map_pos_x, player_col[player_id].map_pos_y);
		for (var u in obj_col) obj_col[u].draw(main_context, player_col[player_id].map_pos_x, player_col[player_id].map_pos_y);
		for (var u in par_col) par_col[u].draw(main_context, player_col[player_id].map_pos_x, player_col[player_id].map_pos_y);
		for (var u in player_col) if (u != player_id) player_col[u].net_draw(main_context, player_col[player_id].map_pos_x, player_col[player_id].map_pos_y);
		player_col[player_id].draw(main_context);
		
		if (debug) draw_ping(main_context);
		if (frame_rates) console.log("draw interval: " + draw_interval + " frame rate: " + draw_timer.frame_rate.toFixed(2));
	};

	return this;
}

var main_game = new Game();

main_game.initialize();

})();
<file_sep>/site/constants.js
(function (exports){

var constant_refs = 
{
CANVAS_WIDTH: 1024,
CANVAS_HEIGHT: 640,
MAP_SIZE: 11,
PLAYER_WING_ANGLE: 5*Math.PI/6
};

var CONST =
{
ABOUT: "Gravity is a multiplayer 2D space shooter written in javascript using HTML5 Canvas on the client and node.js on the server side.\n\nMovement: Arrow keys to manuever, keys 'E' and 'R' to strafe left and right respectively\n\nBullets (default key 'S'): Supply is limitless but after ten have been fired the earliest bullet fired will disappear.\n\nPower-Ups:\n\nBomb (B) (default key 'B'): A five second count down. The bomb will not harm players on the same team but will still cause a blast effect. Damage to enemy players drops off with distance.\n\nG_Object (G) (default key 'G'): The G_Object is a gravity well with a three second countdown. It will attract enemy team players, all bullets, and all bombs for fifteen seconds.\n\nCloak (C): Your ship will be invisible to all players for 15 seconds.\n\nInvincibility (I): Your ship will take no damage for 10 seconds.",

FPS: 35,
UPS: 35,
TIME_INTERVAL: 1000,
FRAME_MAX: 30,

KEY_CODE_DEBUG: 68,
KEY_CODE_FRAMERATE: 70,
KEY_CODE_ROTATE_CC: 37,
KEY_CODE_ROTATE_CW: 39,
KEY_CODE_MOVE_FORWARD: 38,
KEY_CODE_MOVE_BACKWARD: 40,
KEY_CODE_STRAFE_LEFT: 69,
KEY_CODE_STRAFE_RIGHT: 82,
KEY_CODE_FIRE: 83,
KEY_CODE_G_OBJECT: 71,
KEY_CODE_BOMB: 66,

POSITION_CORRECT_X: 40,
POSITION_CORRECT_Y: 40,
POSITION_CORRECT_ANGLE: Math.PI/3,
POSITION_CORRECTION_PERCENT: 0.1,

CANVAS_WIDTH: constant_refs.CANVAS_WIDTH,
CANVAS_HEIGHT: constant_refs.CANVAS_HEIGHT,

MAP_WIDTH: 2*Math.pow(2, constant_refs.MAP_SIZE),
MAP_HEIGHT: Math.pow(2, constant_refs.MAP_SIZE),

BACKGROUND_STARS0: Math.pow(2,constant_refs.MAP_SIZE + 2),
BACKGROUND_STARS1: 1000,
BACKGROUND_PLANETS0: 10,
BACKGROUND_PLANET_SIZE_MIN: 30,
BACKGROUND_PLANET_SIZE_RANGE: 20,

PARTICLE_SIZE: 4,
PARTICLE_COLOR: "lime",
PARTICLE_WALL_LOSS: 0.5,
PARTICLE_MASS: 100,
PARTICLE_CHARGE: 100,
PARTICLE_INITIAL_VELOCITY: 0.6,// pixels per millisecond
PARTICLE_CORRECT_X: 4,
PARTICLE_CORRECT_Y: 4,
PARTICLE_CORRECTION_PERCENT: 0.2,
PARTICLE_DAMAGE: 120,

OBJECT_POSITION_CORRECT_X: 20,
OBJECT_POSITION_CORRECT_Y: 20,

G_OBJECT_STATUS_DETONATED: 1,
G_OBJECT_TIME_TO_DET: 3000,
G_OBJECT_TIME_TO_LAST: 15000,
G_OBJECT_MIN_RADIUS: 10,
G_OBJECT_MAX_RADIUS: 100,
G_OBJECT_WALL_LOSS: 0.5,
G_OBJECT_LAUNCH_MASS: 2000,
G_OBJECT_INITIAL_VELOCITY: 0.1,
G_OBJECT_MASS: 100,
G_OBJECT_FRICTION: 0.002,

BOMB_STATUS_DETONATED: 1,
BOMB_TIME_TO_DET: 5000,
BOMB_TIME_TO_LAST: 1000,
BOMB_MIN_RADIUS: 10,
BOMB_BLAST_RADIUS: 400,
BOMB_DAMAGE_FACTOR: 1000,
BOMB_WALL_LOSS: 0.5,
BOMB_LAUNCH_MASS: 2000,
BOMB_MASS: 200,
BOMB_INITIAL_VELOCITY: 0.1,

OBJ_POWER_UP: 0,
OBJ_G_OBJECT: 1,
OBJ_BOMB: 2,

HEALTH_LOCATION_X: constant_refs.CANVAS_WIDTH/2,
HEALTH_LOCATION_Y: 10,
HEALTH_WIDTH: 100,
HEALTH_HEIGHT: 20,

POWER_UP_TEXT_COLOR: "black",
POWER_UP_FONT: "bold 20px Courier",
POWER_UP_RADIUS: 10,

POWER_UP_CLOAK: 0,
POWER_UP_INVINCIBLE: 1,
POWER_UP_G_OBJECT: 2,
POWER_UP_BOMB: 3,

POWER_UP_CHAR:["C","I","G","B"],

INFO_BOX_SIDE: 20,
HAS_LOC_X:[constant_refs.CANVAS_WIDTH/2 - 180, constant_refs.CANVAS_WIDTH/2 - 140, constant_refs.CANVAS_WIDTH/2 + 120,constant_refs.CANVAS_WIDTH/2 + 160],
HAS_LOC_Y:[10,10,10,10],

HAS_G_LOCATION_X: constant_refs.CANVAS_WIDTH/2 + 120,
HAS_G_LOCATION_Y: 10,

HAS_CLOAK_LOCATION_X: constant_refs.CANVAS_WIDTH/2 - 140,
HAS_CLOAK_LOCATION_Y: 10,

TEAM0: 0,
TEAM1: 1,
TEAM2: 2,
TEAM3: 3,
TEAM4: 4,
TEAM_DARK: ["black","red","blue","gray","orange"],
TEAM_LIGHT: ["white","pink","cyan","white","yellow"],

WALL_DAMAGE_MINIMUM: 30,
WALL_DAMAGE_MULTIPLIER: 900,

PLAYER_MAX_HEALTH: 1000,
PLAYER_HEALTH_REGEN: 0.1,
PLAYER_MAX_ENERGY: 1000,
PLAYER_MAX_BULLETS: 10,
PLAYER_RADIUS: 20,
PLAYER_SHIELD_RADIUS: 26,
PLAYER_SHIELD_FADE_MAX: 700,
PLAYER_DEAD_COUNTER_MAX: 3000,
PLAYER_CLOAK_TIMER_MAX: 15000,
PLAYER_INVINCIBLE_TIMER_MAX: 10000,
PLAYER_WING_ANGLE: constant_refs.PLAYER_WING_ANGLE,
PLAYER_WING_ANGLE_SIN: 20*Math.sin(constant_refs.PLAYER_WING_ANGLE),
PLAYER_WING_ANGLE_COS: 20*Math.cos(constant_refs.PLAYER_WING_ANGLE),
PLAYER_ACCELERATION: 0.0007,
PLAYER_ROTATE_SPEED: 1/360,
PLAYER_FRICTION: 0.001,
PLAYER_WALL_LOSS: 0.5,
PLAYER_FIRE_BATTERY: 120,
PLAYER_MASS: 2000,

PLAYER_STATUS_MOVING: 1,
PLAYER_STATUS_HIT: 2,
PLAYER_STATUS_INVINCIBLE: 4,
PLAYER_STATUS_CLOAKED: 8,
PLAYER_STATUS_DEAD: 16,
PLAYER_STATUS_HAS_G_OBJECT: 32,
PLAYER_STATUS_HAS_BOMB: 64,

COMMAND_ROTATE_CC: 1,
COMMAND_MOVE_FORWARD: 2,
COMMAND_ROTATE_CW: 4,
COMMAND_MOVE_BACKWARD: 8,
COMMAND_STRAFE_RIGHT: 16,
COMMAND_STRAFE_LEFT: 32,
PLAYER_MOVING: 63,

COMMAND_FIRE: 1024,
COMMAND_G_OBJECT: 2048,
COMMAND_BOMB: 4096
};

if (typeof exports ==='undefined') var exports = {};
exports.getConstants = function () {return CONST;}

})(typeof exports === 'undefined' ? CONSTANTS={}: exports);
