/*
 * (C) Copyright Chris B. Kaynor 2006
 * All Rights Reserved.
 *
 * CONTACT: chris@kaynor.net
*/

#define _CRT_SECURE_NO_DEPRECATE

bool KeyDown[255];

//The current game state
//0 = loading screen
//1 = Playing
//2 = Player Lost
//3 = Player Won
int GameState = 0;
int PlayerLives = 3;

#include <windows.h>
#include "Settings.h"
#include "Texture.h"
#include "Macros.h"
#include "vector.h"
#include "data.h"
#include "particle.h"

#ifdef WIN32
	#define glutGetProcAddress wglGetProcAddress
#endif

//Store FPS
char FPS[10];
GLfloat LastFPSUpdate = -1000;

//Data for all tanks
cbk_object_data_tank Tanks[Maximum_Tanks + 1];
long Number_Tanks = 0;

//Data for all projectiles
cbk_object_data_projectile Projectiles[Maximum_Projectiles];
long Number_Projectiles = -1;

//Data for all static objects
cbk_object_data_static Objects[Maximum_Objects];
long Number_Objects = -1;

//Data for all particle emitters
idr_particle_system ParticleEmitters[Maximum_Tanks + 1];
long Number_Particles = -1;

//Buffer Object Items
GLuint GroundPlaneArray = 1;
GLuint SkyPlaneArray    = 2;

long GroundDataSize;

unsigned long cLevelID = 0;

GLfloat *SkyPlaneData;

static PFNGLGENBUFFERSARBPROC gen_buffers = NULL;
static PFNGLDELETEBUFFERSARBPROC delete_buffers = NULL;
static PFNGLISBUFFERARBPROC is_buffer = NULL;
static PFNGLBINDBUFFERARBPROC bind_buffer = NULL;
static PFNGLBUFFERDATAARBPROC buffer_data = NULL;
static PFNGLMAPBUFFERARBPROC map_buffer = NULL;
static PFNGLUNMAPBUFFERARBPROC unmap_buffer = NULL;
static PFNGLGETBUFFERSUBDATAARBPROC get_buffer_subdata = NULL;
static PFNGLGETBUFFERPARAMETERIVARBPROC get_buffer_parameteriv = NULL;
static PFNGLGETBUFFERPOINTERVARBPROC get_buffer_pointerv = NULL;

//Varibles to hold texture IDs
UINT GroundTexture        =  1; //            Procedual Texture

UINT SkyTexture           =  2; //            "texture_sky.jpg"
UINT TankTexture          =  3; //            "texture_tank.jpg"
UINT BulletTexture        = 26; //            "texture_bullet.jpg"
UINT ObjectTexture        = 27; //            "texture_object.jpg"

UINT BeginScreenTexture   =  4; //            "screen_begin.jpg"
UINT DeathScreenTexture   =  5; //            "screen_death.jpg"
UINT VictoryScreenTexture =  6; //            "screen_victory.jpg"

UINT NumberTexture[]     = { 7, // '0'        "number_0.jpg"
                             8, // '1'        "number_1.jpg"
							 9, // '2'        "number_2.jpg"
						    10, // '3'        "number_3.jpg"
							11, // '4'        "number_4.jpg"
							12, // '5'        "number_5.jpg"
							13, // '6'        "number_6.jpg"
							14, // '7'        "number_7.jpg"
							15, // '8'        "number_8.jpg"
							16, // '9'        "number_9.jpg"
                            17, // '-'        "number_-.jpg"
                            18, // '/'        "number_Slash.jpg"
                            19, // '%'        "number_%.jpg"
                            20};// '.'        "number_Decimal.jpg"

UINT ScoreTexture         = 21; // 'Score:'   "text_Score.jpg"
UINT LivesTexture         = 22; // 'Lives:'   "text_Lives.jpg"
UINT TanksAliveTexture    = 23; // 'Tanks:'   "text_Tanks.jpg"
UINT LevelTexture         = 24; // 'Level:'   "text_Level.jpg"
UINT AccuracyTexture      = 25; // 'Accu.:'   "text_Accuracy.jpg"
UINT FPSTexture           = 28; // '  FPS:'   "text_FPS.jpg"
UINT TurboCDTexture       = 29; // 'Turbo:'   "text_Turbo.jpg"

GLfloat DetectCollision(cbk_object_data l_Object1, cbk_object_data l_Object2, GLfloat time_slice);

int FireProjectile(long TankID) {
	Number_Projectiles++;

	if (Number_Projectiles >= Maximum_Projectiles) {
		Number_Projectiles--;
		return 1;
	}

	if (Tanks[TankID].FireCoolDownTime > 0) {
		Number_Projectiles--;
		return 2;
	}

	Tanks[TankID].FireCoolDownTime = RefireRate;

	Projectiles[Number_Projectiles].Firer = TankID;
	Projectiles[Number_Projectiles].move(Tanks[TankID].get_position());
	Projectiles[Number_Projectiles].rotate(Tanks[TankID].get_rotation());
	Projectiles[Number_Projectiles].change_movement_speed(1);
	Projectiles[Number_Projectiles].update(1);
	Projectiles[Number_Projectiles].change_movement_speed(Tanks[TankID].get_movement_speed() + BaseProjectileSpeed);
	Projectiles[Number_Projectiles].SetLifeTime(ProjectileLife);
	Projectiles[Number_Projectiles].SetCollsionSize(0.1);

	Tanks[TankID].ShotsFired++;
	Tanks[TankID].Score -= BulletPointCost;

	return 0;
}

void RunAI(long AITankID, GLfloat Dt) { 
	unsigned long FireChance = min(cLevelID * 100 + 50, 970);

	bool WillCollide = false;
	bool TankCollide = false;
	float CollideTime = 2;

	const long RandomNumber = GetRandom(0, 1000);

	//Tanks[AITankID].change_movement_speed(MaxMovementSpeed);
	Tanks[AITankID].Accelerate(Dt);

	for (long X = 0; X <= Number_Objects; X++) {
		GLfloat dc_time = DetectCollision(Tanks[AITankID], Objects[X], 4);
		if ((dc_time < -1) || (dc_time > 1)) {
		} else {
			if (CollideTime > dc_time) {
				WillCollide = true;
				TankCollide = false;
				CollideTime = dc_time;
			}
		}
	}

	for (long X = 0; X <= Number_Tanks; X++) {
		GLfloat dc_time = DetectCollision(Tanks[AITankID], Tanks[X], 2);
		if ((dc_time < -1) || (dc_time > 1)) {
		} else {
			if (CollideTime > (dc_time / 2)) {
				WillCollide = true;
				TankCollide = true;
				CollideTime = (dc_time / 2);
			}
		}
	}

	if (WillCollide) {
		if (!TankCollide) {
			if (Tanks[AITankID].Turning == 0) {
				if (RandomNumber <= (500)) {
					Tanks[AITankID].TurnRight();
					Tanks[AITankID].Turning = 1;
				} else {
					Tanks[AITankID].TurnLeft();
					Tanks[AITankID].Turning = -1;
				}
			} else if (Tanks[AITankID].Turning == 1) {
				Tanks[AITankID].TurnRight();
			} else if (Tanks[AITankID].Turning == -1) {
				Tanks[AITankID].TurnLeft();
			}
		} else {
			if (RandomNumber <= (FireChance * Dt)) {
				FireProjectile(AITankID);
				return;
			}
			if (Tanks[AITankID].Turning == 0) {
				if (RandomNumber <= (1000 - (FireChance * 0.5))) {
					Tanks[AITankID].TurnRight();
					Tanks[AITankID].Turning = 1;
				} else {
					Tanks[AITankID].TurnLeft();
					Tanks[AITankID].Turning = -1;
				}
			} else if (Tanks[AITankID].Turning == 1) {
				Tanks[AITankID].TurnRight();
			} else if (Tanks[AITankID].Turning == -1) {
				Tanks[AITankID].TurnLeft();
			}
		}
	} else {
		if (RandomNumber <= 200) {
			Tanks[AITankID].TurnRight();
			Tanks[AITankID].TurnRight();
		} else if (RandomNumber <= 400) {
			Tanks[AITankID].TurnLeft();
			Tanks[AITankID].TurnLeft();
		} else if (RandomNumber <= (400 + (200 * Dt))) {
			FireProjectile(AITankID);
		}
		Tanks[AITankID].Turning = 0;
	}
}

static void init_textures( void )
{
	char FileNameChar[20];
	LPSTR *FileName;

	//Ground Texture
	#define CUBE_TEX_SIZE 32
	#define CUBE_TEX_REPEAT 16

	GLubyte image[CUBE_TEX_SIZE][CUBE_TEX_SIZE][3];

	GLint Value;
	GLint i, j;

	glGenTextures(1, &GroundTexture);

	glBindTexture(GL_TEXTURE_2D, GroundTexture);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

	/* make colored checkerboard cube faces */
	for (i = 0; i < CUBE_TEX_SIZE; i++) {
		for (j = 0; j < CUBE_TEX_SIZE; j++) {
			Value = (((i / CUBE_TEX_REPEAT) + (j / CUBE_TEX_REPEAT)) % 2);
			if (Value == 1) {
				image[i][j][0] = 0;
				image[i][j][1] = 0;
				image[i][j][2] = 0;
			} else {
				image[i][j][0] = 255;
				image[i][j][1] = 255;
				image[i][j][2] = 255;
			}
		}
	}

	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, true);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, CUBE_TEX_SIZE, CUBE_TEX_SIZE, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, 1);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//Sky Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(SkyTexture, "texture_sky.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); //GL_MIRRORED_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


	//Tank Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, true);
	Load_Texture(TankTexture, "texture_tank.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);


	//Begin Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(BeginScreenTexture, "screen_begin.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//Death Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(DeathScreenTexture, "screen_death.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//Victory Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(VictoryScreenTexture, "screen_victory.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//Number Textures
	for (long x = 0; x <= 9; x++) {
		sprintf(FileNameChar, "number_%i.jpg", x);
		FileName = new LPSTR(FileNameChar);

		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
		Load_Texture(NumberTexture[x], *FileName);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	}


	//'-' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(NumberTexture[10], "number_-.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//'/' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(NumberTexture[11], "number_Slash.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	//'%' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(NumberTexture[12], "number_%.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	//'.' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(NumberTexture[13], "number_Decimal.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//'Score:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(ScoreTexture, "text_Score.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//'Lives:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(LivesTexture, "text_Lives.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

		
	//'Tanks:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(TanksAliveTexture, "text_Tanks.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//'Level:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(LevelTexture, "text_Level.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);


	//'Accu.:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(AccuracyTexture, "text_Accuracy.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	//'  FPS:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(FPSTexture, "text_FPS.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	//'Turbo:' Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, false);
	Load_Texture(TurboCDTexture, "text_Turbo.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	//Bullet Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, true);
	Load_Texture(BulletTexture, "texture_bullet.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);

	//Object Texture
	glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, true);
	Load_Texture(ObjectTexture, "texture_object.jpg");
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
}

void DrawScorePlane(GLfloat X, GLfloat Y, UINT TextTexture, float Number) {
	glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	static const GLfloat Divisor = 30;

	if (Number >  99999999) {Number =  99999999;}
	if (Number < -99999999) {Number = -99999999;}

	long NumberIndex = 0;
	char Numbers[10] = {'\0'};
	sprintf(Numbers, "%-5.1f", Number);
	if (Number == 0) {Numbers[0] = '0';}

	glDisable(GL_LIGHTING);
	glDisable(GL_FOG);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_GEN_S); //X
	glDisable(GL_TEXTURE_GEN_T); //Z

	X -= 810;
	Y += 770;

	glLoadIdentity();
	gluLookAt( 0.0,  0.0,  40.0,    // eye position
			   0.0,  0.0,   0.0,    // point the eye is looking at
			   0.0,  1.0,   0.0);   // "up" direction

	glBindTexture(GL_TEXTURE_2D, TextTexture);
	glBegin(GL_QUADS);
		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  0);
		glVertex3f  ( X / Divisor,  Y / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  0);
		glVertex3f  ( (X + 200)  / Divisor,  Y  / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  1);
		glVertex3f  ( (X + 200) / Divisor,  (Y + 50)  / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  1);
		glVertex3f  ( X / Divisor,  (Y + 50)  / Divisor,  0);
	glEnd();
	X += 200;
	for (long x = 0; x <= 5; x++) {
		NumberIndex = 0;
		switch (Numbers[x]) {
			case '.':
				NumberIndex++;
			case '%':
				NumberIndex++;
			case '/':
				NumberIndex++;
			case '-':
				NumberIndex++;
			case '9':
				NumberIndex++;
			case '8':
				NumberIndex++;
			case '7':
				NumberIndex++;
			case '6':
				NumberIndex++;
			case '5':
				NumberIndex++;
			case '4':
				NumberIndex++;
			case '3':
				NumberIndex++;
			case '2':
				NumberIndex++;
			case '1':
				NumberIndex++;
			case '0': //Run if the value is a number or an '-'
				glBindTexture(GL_TEXTURE_2D, NumberTexture[NumberIndex]);
				glBegin(GL_QUADS);
				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 0,  0);
				glVertex3f  ( X / Divisor,  Y / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 1,  0);
				glVertex3f  ( (X + 50)  / Divisor,  Y  / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 1,  1);
				glVertex3f  ( (X + 50) / Divisor,  (Y + 50)  / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 0,  1);
				glVertex3f  ( X / Divisor,  (Y + 50)  / Divisor,  0);
				glEnd();
				X += 50;
				break;
			case ' ':
				X += 50;
				break;
		}
	}

	glEnable(GL_TEXTURE_GEN_S); //X
	glEnable(GL_TEXTURE_GEN_T); //Z
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_FOG);
}


void DrawScorePlane(GLfloat X, GLfloat Y, UINT TextTexture, long Number) {
	glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	static const GLfloat Divisor = 30;

	if (Number >  99999999) {Number =  99999999;}
	if (Number < -99999999) {Number = -99999999;}

	long NumberIndex = 0;
	char Numbers[10];
	sprintf(Numbers, "%-+5.0i", Number);
	if (Number == 0) {Numbers[0] = '0';}

	glDisable(GL_LIGHTING);
	glDisable(GL_FOG);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_GEN_S); //X
	glDisable(GL_TEXTURE_GEN_T); //Z

	X -= 810;
	Y += 770;

	glLoadIdentity();
	gluLookAt( 0.0,  0.0,  40.0,    // eye position
			   0.0,  0.0,   0.0,    // point the eye is looking at
			   0.0,  1.0,   0.0);   // "up" direction

	glBindTexture(GL_TEXTURE_2D, TextTexture);
	glBegin(GL_QUADS);
		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  0);
		glVertex3f  ( X / Divisor,  Y / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  0);
		glVertex3f  ( (X + 200)  / Divisor,  Y  / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  1);
		glVertex3f  ( (X + 200) / Divisor,  (Y + 50)  / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  1);
		glVertex3f  ( X / Divisor,  (Y + 50)  / Divisor,  0);
	glEnd();
	X += 200;
	for (long x = 0; x <= 5; x++) {
		NumberIndex = 0;
		switch (Numbers[x]) {
			case '.':
				NumberIndex++;
			case '%':
				NumberIndex++;
			case '/':
				NumberIndex++;
			case '-':
				NumberIndex++;
			case '9':
				NumberIndex++;
			case '8':
				NumberIndex++;
			case '7':
				NumberIndex++;
			case '6':
				NumberIndex++;
			case '5':
				NumberIndex++;
			case '4':
				NumberIndex++;
			case '3':
				NumberIndex++;
			case '2':
				NumberIndex++;
			case '1':
				NumberIndex++;
			case '0': //Run if the value is a number or an '-'
				glBindTexture(GL_TEXTURE_2D, NumberTexture[NumberIndex]);
				glBegin(GL_QUADS);
				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 0,  0);
				glVertex3f  ( X / Divisor,  Y / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 1,  0);
				glVertex3f  ( (X + 50)  / Divisor,  Y  / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 1,  1);
				glVertex3f  ( (X + 50) / Divisor,  (Y + 50)  / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 0,  1);
				glVertex3f  ( X / Divisor,  (Y + 50)  / Divisor,  0);
				glEnd();
				X += 50;
				break;
			case ' ':
				X += 50;
				break;
		}
	}

	glEnable(GL_TEXTURE_GEN_S); //X
	glEnable(GL_TEXTURE_GEN_T); //Z
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_FOG);
}

void DrawScorePlane(GLfloat X, GLfloat Y, UINT TextTexture, char *String, long StringLength) {
	glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	static const GLfloat Divisor = 30;

	long NumberIndex = 0;

	glDisable(GL_LIGHTING);
	glDisable(GL_FOG);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_TEXTURE_GEN_S); //X
	glDisable(GL_TEXTURE_GEN_T); //Z

	X -= 810;
	Y += 770;

	glLoadIdentity();
	gluLookAt( 0.0,  0.0,  40.0,    // eye position
			   0.0,  0.0,   0.0,    // point the eye is looking at
			   0.0,  1.0,   0.0);   // "up" direction

	glBindTexture(GL_TEXTURE_2D, TextTexture);
	glBegin(GL_QUADS);
		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  0);
		glVertex3f  ( X / Divisor,  Y / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  0);
		glVertex3f  ( (X + 200)  / Divisor,  Y  / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  1);
		glVertex3f  ( (X + 200) / Divisor,  (Y + 50)  / Divisor,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  1);
		glVertex3f  ( X / Divisor,  (Y + 50)  / Divisor,  0);
	glEnd();
	X += 200;
	for (long x = 0; x <= StringLength; x++) {
		NumberIndex = 0;
		switch (String[x]) {
			case '.':
				NumberIndex++;
			case '%':
				NumberIndex++;
			case '/':
				NumberIndex++;
			case '-':
				NumberIndex++;
			case '9':
				NumberIndex++;
			case '8':
				NumberIndex++;
			case '7':
				NumberIndex++;
			case '6':
				NumberIndex++;
			case '5':
				NumberIndex++;
			case '4':
				NumberIndex++;
			case '3':
				NumberIndex++;
			case '2':
				NumberIndex++;
			case '1':
				NumberIndex++;
			case '0': //Run if the value is a number or an '-'
				glBindTexture(GL_TEXTURE_2D, NumberTexture[NumberIndex]);
				glBegin(GL_QUADS);
				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 0,  0);
				glVertex3f  ( X / Divisor,  Y / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 1,  0);
				glVertex3f  ( (X + 50)  / Divisor,  Y  / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 1,  1);
				glVertex3f  ( (X + 50) / Divisor,  (Y + 50)  / Divisor,  0);

				glNormal3f  ( 0,  0,  1);
				glTexCoord2f( 0,  1);
				glVertex3f  ( X / Divisor,  (Y + 50)  / Divisor,  0);
				glEnd();
				X += 50;
				break;
			case ' ':
				X += 50;
				break;
		}
	}

	glEnable(GL_TEXTURE_GEN_S); //X
	glEnable(GL_TEXTURE_GEN_T); //Z
	glEnable(GL_LIGHTING);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_FOG);
}

void CreateGroundPlane() {
	#define GroundfStep   1.0f
	#define GroundYValue  0.0f
	
	GLfloat iStrip, iRun;

	long X = 0;

	const long DataSize = (long) ((((HorizonDistance * 2) / GroundfStep) * 2 + 1) * (((HorizonDistance * 2) / GroundfStep) * 2 + 1)); 
	GroundDataSize = ((HorizonDistance * 2) / GroundfStep) * 2;

	GLfloat VertexData[DataSize][3] = {0};

	for(iStrip = -HorizonDistance; iStrip <= HorizonDistance; iStrip += GroundfStep)
	{
		for(iRun = -HorizonDistance; iRun <= HorizonDistance; iRun += GroundfStep)
		{
			VertexData[X][0] = iStrip;
			VertexData[X][1] = GroundYValue;
			VertexData[X][2] = iRun;
			X++;

			VertexData[X][0] = iStrip + GroundfStep;
			VertexData[X][1] = GroundYValue;
			VertexData[X][2] = iRun;
			X++;
		}
	}
	(*gen_buffers)(1, &GroundPlaneArray);
	(*bind_buffer)(GL_ARRAY_BUFFER, GroundPlaneArray);
	(*buffer_data)(GL_ARRAY_BUFFER, (sizeof(VertexData)), VertexData, GL_STATIC_DRAW);
}

void DrawGround(GLfloat CameraAngle) {
	const GLfloat Object_Plane_T[] = {0, 0, 1};

	glPushMatrix();
	glLoadIdentity();
    gluLookAt( 0.0, 2.0,  -5.0,    // eye position
	           0.0, 1.25,  5.0,    // point the eye is looking at
	           0.0, 1.0,   0.0);   // "up" direction

	glBindTexture(GL_TEXTURE_2D, GroundTexture);
	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
	glTexGenfv(GL_T, GL_OBJECT_PLANE, Object_Plane_T);

	glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glLoadIdentity();
		glScalef(0.5, 0.5, 0.5);
		glTranslatef(Tanks[0].get_position().GetValue(1), Tanks[0].get_position().GetValue(3), 0);
	glMatrixMode(GL_MODELVIEW);

	glRotatef(-CameraAngle, 0, 1, 0);

	(*bind_buffer)(GL_ARRAY_BUFFER, GroundPlaneArray);
	glVertexPointer(3, GL_FLOAT, 0, (GLvoid *) 0);

	glNormal3f(0.0f, 1.0f, 0.0f);
	for (long X = 0; X <= GroundDataSize; X++) {
		glDrawArrays(GL_TRIANGLE_STRIP, X * GroundDataSize, GroundDataSize);
	}

	glMatrixMode(GL_TEXTURE);
		glPopMatrix();
	glMatrixMode(GL_MODELVIEW);

	glPopMatrix();
}

void DrawSky(GLfloat CameraAngle) {
	const GLfloat fStep = 2.0f;
	const GLfloat z = HorizonDistance * 0.9;
	GLfloat iStrip, iRun;

    glDisable(GL_DEPTH_TEST);
	glDisable(GL_FOG);
	glDisable(GL_LIGHTING);

	glBindTexture(GL_TEXTURE_2D, SkyTexture);

	glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); //GL_OBJECT_LINEAR);
	glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); //GL_OBJECT_LINEAR);

	glMatrixMode(GL_TEXTURE);
		glPushMatrix();
		glLoadIdentity();
		glTranslatef(CameraAngle / -60, 0, 0);
		glScalef(5, 5, 5);
	glMatrixMode(GL_MODELVIEW);

	glLineWidth(10);
	glPushMatrix();
	glLoadIdentity();
    gluLookAt( 0.0, 2.0,  -5.0,    // eye position
	           0.0, 1.25,  5.0,    // point the eye is looking at
	           0.0, 1.0,   0.0);   // "up" direction
	glRotatef(90, 1, 0, 0);

	for(iStrip = 10; iStrip >= -10; iStrip -= fStep)
	{
		glBegin(GL_TRIANGLE_STRIP);
		glNormal3f(0.0f, 1.0f, 0.0f);

		for(iRun = -10; iRun <= 0; iRun += fStep)
		{
			glVertex3f(iStrip,         5, iRun);
			glVertex3f(iStrip + fStep, 5, iRun);
		}

		glEnd();
	}

	glPopMatrix();	

	glMatrixMode(GL_TEXTURE);
		glPopMatrix();
	glMatrixMode(GL_MODELVIEW);

	glEnable(GL_FOG);
    glEnable(GL_DEPTH_TEST);
	glEnable(GL_LIGHTING);

}

void SetupLighting(void) {
	glEnable(GL_LIGHT0);
	glPushMatrix();
	//glLoadIdentity();
	glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientColorLight0);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseColorLight0);
	glLightfv(GL_LIGHT0, GL_POSITION,    PositionLight0);
	glPopMatrix();
}


void DrawFullScreenPlane(void) {
	glMatrixMode(GL_TEXTURE);
		glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	glDisable(GL_LIGHTING);
	glDisable(GL_FOG);
	glDisable(GL_TEXTURE_GEN_S); //X
	glDisable(GL_TEXTURE_GEN_T); //Z

	glLoadIdentity();
	gluLookAt( 0.0,  0.0,  1.5,    // eye position
			   0.0,  0.0,  0.0,    // point the eye is looking at
			   0.0,  1.0,  0.0);   // "up" direction

	glBegin(GL_QUADS);
		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  0);
		glVertex3f  (-1, -1,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  0);
		glVertex3f  ( 1, -1,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 1,  1);
		glVertex3f  ( 1,  1,  0);

		glNormal3f  ( 0,  0,  1);
		glTexCoord2f( 0,  1);
		glVertex3f  (-1,  1,  0);
	glEnd();

	glEnable(GL_TEXTURE_GEN_S); //X
	glEnable(GL_TEXTURE_GEN_T); //Z
	glEnable(GL_LIGHTING);
	glEnable(GL_FOG);
}

GLfloat DetectCollision(cbk_object_data l_Object1, cbk_object_data l_Object2, GLfloat time_slice) {
	if (time_slice == 0) {return -2;}

	cbk_vector q(l_Object1.get_position() - l_Object2.get_position()); //Combined Position Vector
	cbk_vector r((l_Object1.get_velocity() * time_slice) - (l_Object2.get_velocity() * time_slice)); //Combined Velocity Vector

	const GLfloat d = (l_Object1.GetCollsionSize() + l_Object2.GetCollsionSize()); //combined radius of collision spheres

	const GLfloat q_dot_r = (q * r);
	const GLfloat r_dot_r = (~r);
	const GLfloat q_dot_q = (~q);
	const GLfloat d_dot_d = (d * d);
	const GLfloat two_qr = (2 * q_dot_r);
	const GLfloat two_qr_squared = (two_qr * two_qr);
	const GLfloat D = (two_qr_squared - (4 * r_dot_r * (q_dot_q - d_dot_d)));

	if (D <= 0) {return -2;}

	const GLfloat sqrt_D = sqrt(D);
	const GLfloat t1 = (-two_qr + sqrt_D) / (2 * r_dot_r);
	const GLfloat t2 = (-two_qr - sqrt_D) / (2 * r_dot_r);
	GLfloat t;

    if ((t1 > 0) && (t1 < t2)) {
		t = t1;
    } else {
		t = t2;
    }

	return ((t > 1.0) || (t < 0.0)) ? -2 : t;
}

void SetupLevel(unsigned long LevelID) {
	GLfloat X_Pos, Y_Pos, Z_Pos;

	srand((unsigned)time(0));

	cLevelID = LevelID;

	Number_Tanks = (LevelID * 4) + 10;
	if (Number_Tanks > Maximum_Tanks) {Number_Tanks = Maximum_Tanks;}

	Tanks[0].move(WorldSize / 2, 0.4, WorldSize / 2);
	Tanks[0].SetCollsionSize(2.1 - max((41 - pow((float)LevelID, 0.99f)) / 40, 0.7));
	Tanks[0].change_movement_speed(0);
	Tanks[0].change_rotation_speed(0);
	Tanks[0].rotate(0, 0, 0);
	Tanks[0].set_texture(TankTexture);
	Tanks[0].change_movement_decay(0.4);
	Tanks[0].change_rotation_decay(0.2);

	for (long x = 1; x <= Number_Tanks; x++) {
		Tanks[x].move(GetRandom(0, WorldSize), 0.4, GetRandom(0, WorldSize));
		Tanks[x].SetCollsionSize(max((41 - pow((float)LevelID, 0.99f)) / 40, 0.7));
		Tanks[x].change_movement_speed(0);
		Tanks[x].change_rotation_speed(0);
		Tanks[x].rotate(GetRandom(0, 360), 0, 0);
		Tanks[x].set_texture(TankTexture);
		Tanks[x].change_movement_decay(0.4);
		Tanks[x].change_rotation_decay(0.2);
	}

	Number_Objects = Maximum_Objects;
	for (long x = 0; x <= Number_Objects; x++) {
		Objects[x].move(GetRandom(0, WorldSize), 0, GetRandom(0, WorldSize));
		Objects[x].set_texture(ObjectTexture);
		Objects[x].SetCollsionSize(2);
	}
}

void DrawCompass(void) {
	const static GLfloat MapX    = 20.0f;
	const static GLfloat MapY    = 20.0f;
	const static GLfloat MapSize = (4.0f / (WorldSize * 0.5f));
	const static GLfloat MapHorizonDistance = HorizonDistance * (4.0f / (WorldSize * 0.5f));

	const static GLfloat RadarX    =  0.0f;
	const static GLfloat RadarY    = 20.0f;
	const static GLfloat RadarSize = (4.0f / (HorizonDistance));

	const float Facing = Degrees2Radians(Tanks[0].get_rotation().GetValue(1));
	const float SinFacing = sin(Facing);
	const float CosFacing = cos(Facing);

	cbk_vector RealitivePosition;
	cbk_vector PlayerPositionMap(WorldSize / 2, 0, WorldSize / 2);
	cbk_vector PlayerPositionRadar = Tanks[0].get_position();

	glPushMatrix();
	glLoadIdentity();
	glDisable(GL_LIGHTING);
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_FOG);
	glDisable(GL_DEPTH_TEST);
	gluLookAt( 0.0,  0.0,  40.0,    // eye position
			   0.0,  0.0,   0.0,    // point the eye is looking at
			   0.0,  1.0,   0.0);   // "up" direction
	glLineWidth(2);

	//Full Map
		//Draw Backcolor
	glColor3f(0.0f, 0.0f, 0.0f);
	glBegin(GL_QUADS);
		glVertex2f(MapX - 4.0f, MapY + 4.0f);
		glVertex2f(MapX - 4.0f, MapY - 4.0f);
		glVertex2f(MapX + 4.0f, MapY - 4.0f);
		glVertex2f(MapX + 4.0f, MapY + 4.0f);
	glEnd();

		//Draw Border
	glColor3f(0.0f, 0.0f, 1.0f);
	glBegin(GL_LINE_STRIP);
		glVertex2f(MapX - 4.0f, MapY + 4.0f);
		glVertex2f(MapX - 4.0f, MapY - 4.0f);
		glVertex2f(MapX + 4.0f, MapY - 4.0f);
		glVertex2f(MapX + 4.0f, MapY + 4.0f);
		glVertex2f(MapX - 4.0f, MapY + 4.0f);
	glEnd();
	
		//Draw Objects Tanks
	glPointSize(1);
	glColor3f(0.0f, 1.0f, 1.0f);
	glBegin(GL_POINTS);
		for (long X = 0; X <= Number_Objects; X++) {
			RealitivePosition = ((PlayerPositionMap - Objects[X].get_position()) * MapSize);
			glVertex2f(MapX - RealitivePosition.GetValue(1), RealitivePosition.GetValue(3) + MapY);
		}
	glEnd();

		//Draw Self
	RealitivePosition = ((PlayerPositionMap - Tanks[0].get_position()) * MapSize);
	glBegin(GL_LINE_STRIP);
		glColor3f(0.0f, 1.0f, 0.0f);
		glVertex2f(MapX - RealitivePosition.GetValue(1)                     , RealitivePosition.GetValue(3) + MapY);
		glColor3f(0.0f, 1.0f, 0.0f);
		glVertex2f(MapX - RealitivePosition.GetValue(1) - (SinFacing * 0.625 * MapHorizonDistance), RealitivePosition.GetValue(3) + MapY + (CosFacing * 0.625 * MapHorizonDistance));
		glColor3f(1.0f, 1.0f, 0.0f);
		glVertex2f(MapX - RealitivePosition.GetValue(1) - (SinFacing * 0.75 * MapHorizonDistance), RealitivePosition.GetValue(3) + MapY + (CosFacing * 0.75 * MapHorizonDistance));
		glColor3f(1.0f, 1.0f, 0.0f);
		glVertex2f(MapX - RealitivePosition.GetValue(1) - (SinFacing * MapHorizonDistance), RealitivePosition.GetValue(3) + MapY + (CosFacing * MapHorizonDistance));
	glEnd();

		//Draw Horizon Distance
	glColor3f(0.0f, 0.0f, 1.0f);
	glBegin(GL_LINE_STRIP);
		for (long X = 0; X < 360; X += 15) {
			glVertex2f(MapX - RealitivePosition.GetValue(1) - (sin(Degrees2Radians(X)) * MapHorizonDistance), (cos(Degrees2Radians(X)) * MapHorizonDistance) + RealitivePosition.GetValue(3) + MapY);
		}
		glVertex2f(MapX - RealitivePosition.GetValue(1), MapHorizonDistance + RealitivePosition.GetValue(3) + MapY);
	glEnd();

		//Draw Enemy Tanks
	glPointSize(2);
	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POINTS);
		for (long X = 1; X <= Number_Tanks; X++) {
			RealitivePosition = ((PlayerPositionMap - Tanks[X].get_position()) * MapSize);
			glVertex2f(MapX - RealitivePosition.GetValue(1), RealitivePosition.GetValue(3) + MapY);
		}
	glEnd();

	//Near Radar
		//Draw Border
	glColor3f(0.0f, 0.0f, 1.0f);
	glBegin(GL_LINE_STRIP);
		for (long X = 0; X < 360; X += 15) {
			glVertex2f(RadarX - (sin(Degrees2Radians(X)) * 4), (cos(Degrees2Radians(X)) * 4) + RadarY);
		}
		glVertex2f(RadarX, 4 + RadarY);
	glEnd();

		//Draw Objects
	glPointSize(2);
	glColor3f(0.0f, 1.0f, 1.0f);
	glBegin(GL_POINTS);
		for (long X = 0; X <= Number_Objects; X++) {
			RealitivePosition = (PlayerPositionRadar - Objects[X].get_position());
			if (RealitivePosition.GetMagitude() <= HorizonDistance) {
				RealitivePosition *= RadarSize;
				glVertex2f(RadarX - RealitivePosition.GetValue(1), RealitivePosition.GetValue(3) + RadarY);
			}
		}
	glEnd();

		//Draw Self
	glBegin(GL_LINE_STRIP);
		glColor3f(0.0f, 1.0f, 0.0f);
		glVertex2f(RadarX                     , RadarY);
		glColor3f(0.0f, 1.0f, 0.0f);
		glVertex2f(RadarX - (SinFacing * 2.5f), RadarY + (CosFacing * 2.5f));
		glColor3f(1.0f, 1.0f, 0.0f);
		glVertex2f(RadarX - (SinFacing * 3.0f), RadarY + (CosFacing * 3.0f));
		glColor3f(1.0f, 1.0f, 0.0f);
		glVertex2f(RadarX - (SinFacing * 4.0f), RadarY + (CosFacing * 4.0f));
	glEnd();

		//Draw Enemy Tanks
	glPointSize(2);
	glColor3f(1.0f, 0.0f, 0.0f);
	glBegin(GL_POINTS);
		for (long X = 1; X <= Number_Tanks; X++) {
			RealitivePosition = (PlayerPositionRadar - Tanks[X].get_position());
			if (RealitivePosition.GetMagitude() <= HorizonDistance) {
				RealitivePosition *= RadarSize;
				glVertex2f(RadarX - RealitivePosition.GetValue(1), RealitivePosition.GetValue(3) + RadarY);
			}
		}
	glEnd();

	//Reset Settings
	glColor3fv(AmbientColor);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_FOG);
	glEnable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glPopMatrix();
}

void init(void) {
	double version = 0;

	const char *const ver_string = (const char *) glGetString(GL_VERSION);

	if (ver_string != NULL) {
		version = strtod(ver_string, NULL);
 	} else {
		printf("An error occured while trying to determine if the current version of OpenGL.\n");
		exit(1);
	}

	if (!glutExtensionSupported("GL_ARB_vertex_buffer_object")) {
		printf("GL_ARB_vertex_buffer_object not found!\n");
		exit(0);
	} else {
		printf("Using Buffer Objects!\n");
		gen_buffers = (PFNGLGENBUFFERSARBPROC) glutGetProcAddress("glGenBuffersARB");
		delete_buffers = (PFNGLDELETEBUFFERSARBPROC) glutGetProcAddress("glDeleteBuffersARB");
		is_buffer = (PFNGLISBUFFERARBPROC) glutGetProcAddress("glIsBufferARB");
		bind_buffer = (PFNGLBINDBUFFERARBPROC) glutGetProcAddress("glBindBufferARB");
		buffer_data = (PFNGLBUFFERDATAARBPROC) glutGetProcAddress("glBufferDataARB");
		map_buffer = (PFNGLMAPBUFFERARBPROC) glutGetProcAddress("glMapBufferARB");
		unmap_buffer = (PFNGLUNMAPBUFFERARBPROC) glutGetProcAddress("glUnmapBufferARB");
		get_buffer_subdata = (PFNGLGETBUFFERSUBDATAARBPROC) glutGetProcAddress("glGetBufferSubDataARB");
		get_buffer_parameteriv = (PFNGLGETBUFFERPARAMETERIVARBPROC) glutGetProcAddress("glGetBufferParameterivARB");
		get_buffer_pointerv = (PFNGLGETBUFFERPOINTERVARBPROC) glutGetProcAddress("glGetBufferPointervARB");
	}

	printf("GL_RENDERER = %s\n", (char *) glGetString(GL_RENDERER));

	CreateGroundPlane();

	glutSetCursor(GLUT_CURSOR_NONE);

    glClearColor(0.0, 1.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);

	glFogi(GL_FOG_MODE, GL_EXP2);
	glFogfv(GL_FOG_COLOR, Color_Fog);
	glFogf(GL_FOG_DENSITY, FogDensity);
	glHint(GL_FOG_HINT, GL_DONT_CARE);

	glEnable(GL_RESCALE_NORMAL);

	glEnableClientState(GL_VERTEX_ARRAY);

	glPointSize(5);

	init_textures();

	glEnable(GL_TEXTURE_2D);
	glEnable(GL_CULL_FACE);

	glColor3fv(AmbientColor);
}

void display(void)
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	if (GameState == 1) {
		cbk_vector PlayerPosition = Tanks[0].get_position();

		//Get Camera Angle
		GLfloat CameraAngle = 0;
		if (KeyDown['1']) { //Look Backwards
			CameraAngle = Tanks[0].get_rotation().GetValue(1) + 180;
		} else if (KeyDown['2']) { //Look Left
			CameraAngle = Tanks[0].get_rotation().GetValue(1) +  90;
		} else if (KeyDown['3']) { //Look Right
			CameraAngle = Tanks[0].get_rotation().GetValue(1) -  90;
		} else { //Look Forward
			CameraAngle = Tanks[0].get_rotation().GetValue(1)      ;
		}
		if (CameraAngle >= 360) {CameraAngle -= 360;}
		if (CameraAngle <    0) {CameraAngle +=   0;}

		//Camera Setup
		glLoadIdentity();
		gluLookAt( 0.0, 2.0,  -5.0,    // eye position
				   0.0, 1.25,  5.0,    // point the eye is looking at
				   0.0, 1.0,   0.0);   // "up" direction
			//Move and Rotate Camera
		glRotatef(-CameraAngle, 0, 1, 0);
		glTranslatef(-PlayerPosition.GetValue(1), -PlayerPosition.GetValue(2), -PlayerPosition.GetValue(3));

		//Setup Global Lighting
		SetupLighting();

		//Draw Enviroment
		DrawSky(CameraAngle);
		DrawGround(CameraAngle);

		//Draw Tanks
		#ifdef FIRST_PERSON_VIEW
			for (long x = 1; x <= Number_Tanks; x++) {
		#else
			for (long x = 0; x <= Number_Tanks; x++) {
		#endif
				Tanks[x].draw(Tanks[0]);
			}

		//Draw Static Objects
		for (long x = 0; x <= Number_Objects; x++) {
			Objects[x].draw(Tanks[0]);
		}

		//Draw Projectiles
		for (long x = 0; x <= Number_Projectiles; x++) {
			Projectiles[x].draw(Tanks[0]);
		}

		//Disable all lights, as many may not be needed
		for (long X = 0; X <= 15; X++) {
			glDisable(GL_LIGHT1 + X);
		}

		//Draw Particles  (may add additional lights)
		for (long x = 0; x <= Number_Particles; x++) {
			ParticleEmitters[x].draw_particles(Tanks[0], bind_buffer);
		}

		//Draw Compass
		DrawCompass();

		//Draw Score Plane
		DrawScorePlane(75, -1560, ScoreTexture, Tanks[0].Score);
		//Draw Lives Plane
		DrawScorePlane(75, -1500, LivesTexture, (long) PlayerLives);
		//Draw Turbo Plane
		DrawScorePlane(75, -1440, TurboCDTexture, Tanks[0].TurboCoolDownTime);
		//Draw Tank Count Plane
		DrawScorePlane(75, -1380, TanksAliveTexture, Number_Tanks);
		//Draw Level Plane
		DrawScorePlane(75, -1320, LevelTexture, (long) (cLevelID + 1));
		//Draw Accuracy Plane
		char AccurString[30];
		if (Tanks[0].ShotsFired > 0) {
			sprintf(AccurString, "%i/%i - %03.1f%%", Tanks[0].Kills, Tanks[0].ShotsFired, ((float) Tanks[0].Kills / (float) Tanks[0].ShotsFired) * 100);
		} else {
			sprintf(AccurString, "%i/%i - %03.1f%%", Tanks[0].Kills, Tanks[0].ShotsFired, 0.0f);
		}
		DrawScorePlane(75, -1260, AccuracyTexture, AccurString, 30);

		//Draw FPS Plane
		DrawScorePlane(75, -100, FPSTexture, FPS, 10);
	} else if (GameState == 0) { //Loading Screen
		glBindTexture(GL_TEXTURE_2D, BeginScreenTexture);

		DrawFullScreenPlane();
	} else if (GameState == 2) { //Player Lost
		glBindTexture(GL_TEXTURE_2D, DeathScreenTexture);

		DrawFullScreenPlane();
		//Draw Score Plane
		DrawScorePlane(75, -1560, ScoreTexture, Tanks[0].Score);
		//Draw Lives Plane
		DrawScorePlane(75, -1500, LivesTexture, (long) PlayerLives);
		//Draw Tank Count Plane
		DrawScorePlane(75, -1440, TanksAliveTexture, Number_Tanks);
		//Draw Level Plane
		DrawScorePlane(75, -1380, LevelTexture, (long) (cLevelID + 1));
		//Draw Accuracy Plane
		char AccurString[30];
		if (Tanks[0].ShotsFired > 0) {
			sprintf(AccurString, "%i/%i - %03.1f%%", Tanks[0].Kills, Tanks[0].ShotsFired, ((float) Tanks[0].Kills / (float) Tanks[0].ShotsFired) * 100);
		} else {
			sprintf(AccurString, "%i/%i - %03.1f%%", Tanks[0].Kills, Tanks[0].ShotsFired, 0.0f);
		}
		DrawScorePlane(75, -1320, AccuracyTexture, AccurString, 30);
	} else if (GameState == 3) { //Player Won
		glBindTexture(GL_TEXTURE_2D, VictoryScreenTexture);

		DrawFullScreenPlane();
		//Draw Score Plane
		DrawScorePlane(75, -1560, ScoreTexture, Tanks[0].Score);
		//Draw Lives Plane
		DrawScorePlane(75, -1500, LivesTexture, (long) PlayerLives);
		//Draw Tank Count Plane
		DrawScorePlane(75, -1440, TanksAliveTexture, Number_Tanks);
		//Draw Level Plane
		DrawScorePlane(75, -1380, LevelTexture, (long) (cLevelID + 1));
		//Draw Accuracy Plane
		char AccurString[30];
		if (Tanks[0].ShotsFired > 0) {
			sprintf(AccurString, "%i/%i - %03.1f%%", Tanks[0].Kills, Tanks[0].ShotsFired, ((float) Tanks[0].Kills / (float) Tanks[0].ShotsFired) * 100);
		} else {
			sprintf(AccurString, "%i/%i - %03.1f%%", Tanks[0].Kills, Tanks[0].ShotsFired, 0.0f);
		}
		DrawScorePlane(75, -1320, AccuracyTexture, AccurString, 30);
	}

	//Display
	glutSwapBuffers();
}


void reshape(int width, int height)
{
    glViewport(0, 0, (GLsizei) width, (GLsizei) height);
	glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, HorizonDistance);
    glMatrixMode(GL_MODELVIEW);
}

void DestroyProjectile(unsigned int ProjectileID, bool HitTank) {
	if (!HitTank) {
		static const GLfloat Accel = (-2 * ProjectileLife - 0.4) / (ProjectileLife * ProjectileLife);
		const GLfloat ProjectileLifeUsed = ProjectileLife - Projectiles[ProjectileID].GetLifeTime();

		ParticleEmitters[Number_Particles + 1].LifeTime = 3.0f;
		ParticleEmitters[Number_Particles + 1].c_light_id = Number_Particles + 1;
		ParticleEmitters[Number_Particles + 1].reset_particles();
		ParticleEmitters[Number_Particles + 1].Position = Projectiles[ProjectileID].get_position();
		ParticleEmitters[Number_Particles + 1].Position.SetValue(2, 
			ParticleEmitters[Number_Particles + 1].Position.GetValue(2) + 
			Projectiles[ProjectileID].GetCollsionSize() + ((Accel * ProjectileLifeUsed * ProjectileLifeUsed) + (2 * ProjectileLifeUsed))
			);
		ParticleEmitters[Number_Particles + 1].Tank = NULL;
		ParticleEmitters[Number_Particles + 1].ParticleColor[0] = 1.0f;
		ParticleEmitters[Number_Particles + 1].ParticleColor[1] = 0.4f;
		ParticleEmitters[Number_Particles + 1].ParticleColor[2] = 0.4f;
		Number_Particles++;
	}

	for (long x = ProjectileID; x <= (Number_Projectiles - 1); x++) {
		Projectiles[x] = Projectiles[x + 1];
	}
	Number_Projectiles--;
}

void DestroyTank(unsigned int TankID) {
	ParticleEmitters[Number_Particles + 1].LifeTime = 5.0f;
	ParticleEmitters[Number_Particles + 1].c_light_id = Number_Particles + 1;
	ParticleEmitters[Number_Particles + 1].reset_particles();
	ParticleEmitters[Number_Particles + 1].Position = Tanks[TankID].get_position();
	ParticleEmitters[Number_Particles + 1].Tank = NULL;
	ParticleEmitters[Number_Particles + 1].ParticleColor[0] = 1.0f;
	ParticleEmitters[Number_Particles + 1].ParticleColor[1] = 1.0f;
	ParticleEmitters[Number_Particles + 1].ParticleColor[2] = 0.4f;
	Number_Particles++;

	if (TankID == 0) {
		ParticleEmitters[Number_Particles].Tank = &Tanks[0];

		PlayerLives--;
		Tanks[0].Score -= TankPointCost;

		if (PlayerLives == 0) {
			ParticleEmitters[Number_Particles].LifeTime = 0;
			glDisable(GL_LIGHT1 + ParticleEmitters[Number_Particles].c_light_id);
			Number_Particles--;
			GameState = 2;
		}
		
		return;
	} 

	for (long x = TankID; x <= (Number_Tanks - 1); x++) {
		Tanks[x] = Tanks[x + 1];
	}
	Number_Tanks--;
	
	if (Number_Tanks == 0) {
		GameState = 3;
	}
}

void keyboard(unsigned char key, int x, int y) {
	switch (key) {
	case 13: //Enter
		if (GameState == 1) {
			break;
		}

		switch (GameState) {
			case 2: //Player lost last game
				PlayerLives = 3;
				Tanks[0].Score = 0;
				Tanks[0].ShotsFired = 0;
				Tanks[0].Kills = 0;
				cLevelID = 0;
				break;
			case 3: //Player won last game
				cLevelID++;
				break;
		}
		SetupLevel(cLevelID);
		GameState = 1;
		break;
	case 27: // 27 is the ASCII code for escape.
		exit(0);
		break;
	case '+':
		SetupLevel(++cLevelID);
		break;
	case '-':
		SetupLevel(--cLevelID);
		break;
	}

	//Ignore case for movement keys
	if (key == 'A') {key = 'a';}
	if (key == 'W') {key = 'w';}
	if (key == 'S') {key = 's';}
	if (key == 'D') {key = 'd';}

	KeyDown[key] = true;
}

void keyboardUp(unsigned char key, int x, int y)
{
	//Ignore case for movement keys
	if (key == 'A') {key = 'a';}
	if (key == 'W') {key = 'w';}
	if (key == 'S') {key = 's';}
	if (key == 'D') {key = 'd';}

	KeyDown[key] = false;
}

void keyboardSpecial(int key, int x, int y)
{
	//Treat arrow keys are movement keys (A for left, S for down, D for right, W for up);
	switch (key) {
	case GLUT_KEY_UP:
		KeyDown['w'] = true;
		break;
	case GLUT_KEY_DOWN:
		KeyDown['s'] = true;
		break;
	case GLUT_KEY_RIGHT:
		KeyDown['d'] = true;
		break;
	case GLUT_KEY_LEFT:
		KeyDown['a'] = true;
		break;
	}
}

void keyboardSpecialUp(int key, int x, int y)
{
	//Treat arrow keys are movement keys (A for left, S for down, D for right, W for up);
	switch (key) {
	case GLUT_KEY_UP:
		KeyDown['w'] = false;
		break;
	case GLUT_KEY_DOWN:
		KeyDown['s'] = false;
		break;
	case GLUT_KEY_RIGHT:
		KeyDown['d'] = false;
		break;
	case GLUT_KEY_LEFT:
		KeyDown['a'] = false;
		break;
	}
}

void CheckKeyCommands(GLfloat Dt) {
	if (KeyDown[' ']) { //Fire Weapon
		#ifdef WIN32
			if (FireProjectile(0) == 0) {
				PlaySound("shoot.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
			}
		#endif
	}

	if (KeyDown['a']) { //Turn Left
		Tanks[0].TurnLeft();
	}

	if (KeyDown['d']) { //Turn Right
		Tanks[0].TurnRight();
	}

	if (KeyDown['w']) { //Move Forward
		Tanks[0].Accelerate(Dt);
	}

	if (KeyDown['s']) { //Move Backwards
		Tanks[0].Decelerate();
	}

	if (KeyDown['\t']) { //Turbo Forward
		if (Tanks[0].TurboCoolDownTime <= 0) {
			Tanks[0].TurboCoolDownTime = TurboRefireRate;
			Tanks[0].TurboTimeRemaining = TurboLife;
		}
	}
}

void idle(void)
{
	//Do not run code if a game is not in session
	if (GameState != 1) {glutPostRedisplay(); return;}

	//First Collision Time of Tank in Collisions
	GLfloat FirstCollisionT[Maximum_Tanks + 1];
	//First Collision Time [0], and TankID [1] of Projectile in Collisions
	GLfloat FirstCollisionP[Maximum_Projectiles];
	long FirstCollisionP_Tank[Maximum_Projectiles];

	//Determine time sience last call
	static GLfloat t0 = -1.0;
	const GLfloat t = glutGet(GLUT_ELAPSED_TIME) / 1000.0;
	const GLfloat dt = (t0 < 0.0) ? 0.00001 : t - t0;

	//Misc. Varibles
	GLfloat TimeSlice;
	bool Collide;

	//Update last call time
	t0 = t;

	//Store FPS
	if ((t - LastFPSUpdate) >= 0.1) {
		for (long x = 0; x <= 9; x++) {FPS[x] = '\0';}
		sprintf(FPS, "%3.1f", 1.0f / dt);

		LastFPSUpdate = t;
	}

	//Check for Player Interaction
	CheckKeyCommands(dt);

	//Run AI code
	for (long x = 1; x <= Number_Tanks; x++) {
		RunAI(x, dt);
	}

	//Update Particle Systems
	for (long x = 0; x <= Number_Particles; x++) {
		if (ParticleEmitters[x].update_particles(dt)) { 
			glDisable(GL_LIGHT1 + ParticleEmitters[x].c_light_id);
			Number_Particles--;
		}
	}

	//Detect Tank-Tank Collisions
	for (long x = 0; x <= Number_Tanks; x++) {
		FirstCollisionT[x] = 2;
	}
	for (long x = 0; x <= Number_Tanks; x++) {
		for (long y = 0; y <= Number_Tanks; y++) {
			//Find time of first collision
			GLfloat dc_time = DetectCollision(Tanks[x], Tanks[y], dt);

			//Determine if collision is "real", and get the time to update for
			if ((dc_time < -1) || (dc_time > 1)) {
				TimeSlice = dt;
				Collide = false;
			} else {
				TimeSlice = dc_time * dt;
				Collide = true;
			}

			//Store Eariliest Collision
			if (Collide) {
				if (FirstCollisionT[x] > TimeSlice) {
					FirstCollisionT[x] = TimeSlice;
				}
				if (FirstCollisionT[y] > TimeSlice) {
					FirstCollisionT[y] = TimeSlice;
				}
			}
		}
	}
		
	//Detect Tank-Object Collisions
	for (long x = 0; x <= Number_Objects; x++) {
		for (long y = 0; y <= Number_Tanks; y++) {
			//Find time of first collision
			GLfloat dc_time = DetectCollision(Objects[x], Tanks[y], dt);

			//Determine if collision is "real", and get the time to update for
			if ((dc_time < 0) || (dc_time > 1)) {
				TimeSlice = dt;
				Collide = false;
			} else {
				TimeSlice = dc_time * dt;
				Collide = true;
			}

			//Store Eariliest Collision
			if (Collide) {
				if (FirstCollisionT[y] > TimeSlice) {
					FirstCollisionT[y] = TimeSlice;
				}
			}
		}
	}

	//Detect Projectile-Tank Collisions
	for (long x = 0; x <= Number_Projectiles; x++) {
		FirstCollisionP[x] = 2;
		FirstCollisionP_Tank[x] = -1;
	}
	for (long y = 0; y <= Number_Projectiles; y++) {
		for (long x = 0; x <= Number_Tanks; x++) {
			//Find time of first collision
			GLfloat dc_time = DetectCollision(Tanks[x], Projectiles[y], dt);

			//Determine if collision is "real", and get the time to update for
			if ((dc_time < 0) || (dc_time > 1)) {
				TimeSlice = dt;
				Collide = false;
			} else {
				TimeSlice = dc_time * dt;
				Collide = true;
			}

			//Store Eariliest Collision
			if (Collide) {
				if (FirstCollisionT[x] > TimeSlice) {
					FirstCollisionT[x] = TimeSlice;
				}
				if (FirstCollisionP[y] > TimeSlice) {
					FirstCollisionP[y] = TimeSlice;
					FirstCollisionP_Tank[y] = x;
				}
			}
		}
	}

	//Detect Projectile-Object Collisions
	for (long x = 0; x <= Number_Objects; x++) {
		for (long y = 0; y <= Number_Projectiles; y++) {
			//Find time of first collision
			GLfloat dc_time = DetectCollision(Objects[x], Projectiles[y], dt);

			//Determine if collision is "real", and get the time to update for
			if ((dc_time < 0) || (dc_time > 1)) {
				TimeSlice = dt;
				Collide = false;
			} else {
				TimeSlice = dc_time * dt;
				Collide = true;
			}

			//Store Eariliest Collision
			if (Collide) {
				if (FirstCollisionP[y] > TimeSlice) {
					FirstCollisionP[y] = TimeSlice;
					FirstCollisionP_Tank[y] = -1;
				}
			}
		}
	}

	//Move Tanks
	for (long x = 0; x <= Number_Tanks; x++) {
		if (FirstCollisionT[x] == 2) {
			Tanks[x].update(dt);
		} else {
			Tanks[x].change_movement_speed(0);
			Tanks[x].update(dt);
		}

		if (Tanks[x].FireCoolDownTime >= dt) {
			Tanks[x].FireCoolDownTime -= dt;
		} else {
			Tanks[x].FireCoolDownTime = 0;
		}
	}

	//Move Projectiles
	for (long x = 0; x <= Number_Projectiles; x++) {
		if (FirstCollisionP[x] == 2) {
			if (Projectiles[x].update(dt) == ((GLint) true)) {
				DestroyProjectile(x, false);
				x--;
			}
		} else {
			if (FirstCollisionP_Tank[x] != -1) {
				#ifdef WIN32
					if ((Tanks[0].get_position() - Tanks[FirstCollisionP_Tank[x]].get_position()).GetMagitude() < HorizonDistance) {
						PlaySound("explode.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NODEFAULT);
					}
				#endif

				Tanks[Projectiles[x].Firer].Score += (TankPointGain + BulletPointCost);
				Tanks[Projectiles[x].Firer].Kills++;

				DestroyProjectile(x, true);
				DestroyTank(FirstCollisionP_Tank[x]);
			} else {
				DestroyProjectile(x, false);
			}
		}
	}

	glutPostRedisplay();
}

int main(int argc, char ** argv)
{	
	// Initialize GLUT.
	glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGB);

	// Setup Window
	#ifdef FULL_SCREEN
		glutGameModeString(FULL_SCREEN);
		if (glutGameModeGet(GLUT_GAME_MODE_POSSIBLE)) {
			//GameMode available, use it
			printf("The specified game mode is available. Using it.\n");

			glutEnterGameMode();

			printf("Using Game Mode: %ix%ix%i@%i\n", glutGameModeGet(GLUT_GAME_MODE_WIDTH), glutGameModeGet(GLUT_GAME_MODE_HEIGHT), glutGameModeGet(GLUT_GAME_MODE_PIXEL_DEPTH), glutGameModeGet(GLUT_GAME_MODE_REFRESH_RATE));
		} else {
			//GameMode not advailibe, use a full-screen window
			printf("The specified game mode is unavailable. Defaulting to standard window.\n");

			glutInitWindowSize(800, 600);
			glutCreateWindow("Combat Area");
			glutFullScreen();
		}
	#else
		//GameMode disabled
		printf("Full-screen mode is disabled in code.\n");

		glutInitWindowSize(800, 600);
		glutCreateWindow("Combat Area");
	#endif

    // Perform local program initialization.
    init();
	
    // Register our functions as GLUT callbacks.
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutKeyboardFunc(keyboard);
	glutKeyboardUpFunc(keyboardUp);
	glutSpecialFunc(keyboardSpecial);
	glutSpecialUpFunc(keyboardSpecialUp);
	glutIdleFunc(idle);

    // Let 'er rip!
    glutMainLoop();

	// Make complier happy.
    return 0;
}
