Code viewer for World: Space Plane Shooter
//All models are taken from tf3dm
//All sounds are taken from youtube and converted to mp3

/*
This game was created by Brian Kilduff for CA318.

The aim of this game is to shoot enemy planes flying in space. As well as doing this the player must avoid crashing 
into other objects such as the meteors, enemy missiles and enemy planes. 
Once the player reaches the end of the map they are teleported back to the beginning to go through the map again.

The music and sound effects really help to create an intense and attention grabbing game. Everyone who played the game
gave positive feedback and found it a challenging game. This is a clone of the original and for that reason the number of runs
are lower than they technically should be.


To play the game the user controls their airplane by using the arrow keys.
Up = jump
Right = right
Left = left

To shoot their missile the user must press spacebar.

This game was orignally being modelled after the popular game "Race the Sun" which is an endless game where a player
completes different random levels of flying a plane around obstacles.
This game is different as it allows players to shoot missiles at other flying planes as well as requiring players to avoid
enemy missiles.

This game has a lot of 3d objects which can slow down the loading so I have put a little wait of 60 steps at the beginning of 
the game to help with this.


 The main difficulties in creating this game was 
1 -Creating a way for the plane to "jump"
2 -Allowing the user plane to shoot missiles
3 -Allowing AI plane to shoot missiles
4 -Having the camera follow behind the user plane
5 -Integrating 3d models
6 -Discovering the right rotations for objects.
7 -AI for one enemy
 
1-Creating a way for the plane to "jump"
I had to create a few functions and booleams to first move the plane in the air, move the plane a few steps in the air and then bring the plane back down.
I believe this was the best method to take as it made the jump look more natural than other approaches I tried.

2-Allowing the user plane to shoot missiles
I wanted a way that a plane could shoot missiles to destroy other planes flying in the sky. This was quite hard to do as it needed to know
where the plane which fired it was and then fire from that plane and move along until it met a square that occupied and be able to 
destory a plane if the plane occupied the square. 

3-Allowing AI plane to shoot missiles
Similar to above but I needed the AI plane to know when to shoot.


4-Having the camera follow behind the user plane
I was not able to find a way to use the camera to naturally follow behind the players plane. To deal with this I created an object which
follows behind the plane and focuses on the plane acting as a camera.

5-Integrating 3d models
I tried numerous ways to use 3d models and believe it has made my game look well. The main problem I had with this was using the .mtl file
and .pngs. I discovered that once I uploaded files to this website they were all renamed to lowercase. It took me numerous days to realise
that the names of the .png's in the .mtl file I upoloaded to the website were different as they remained uppercase in the .mtl file.
This is the only problem I had with the website. My suggestion would be to either change the naming system when uploaded or at least notify
users to stop them spending days as I did.

6-Discovering the right rotations for objects
Every 3d object I uploaded and used required different rotations and it was quite time consuming to figure out which was the best. 
I know this is not a problem with the website but I thought I would just mention it.
It was also a difficulty to try and integrate rotation changes into moving objects like the plane. I succesfully managed to do this
as when the player plane moves left and right the plane rotates slightly.

7-AI for one enemy
I created some simple AI code that moved one plane towards the player.
This code could and should be improved as I believe it is not up to standard. This is something I will improve in my spare time outside
of the module.
*/






// World must define these:
 
const	 	CLOCKTICK 	= 100;					// speed of run - move things every n milliseconds
const		MAXSTEPS 	= 1500;					// length of a run before final score
 





//---- global constants: -------------------------------------------------------

const gridsize = 80;						// number of squares along side of world	   

//reduced number of rocks in the players way to help users play
const NOBOXES =  Math.trunc ( (gridsize * gridsize) / 20 );
 

const squaresize = 100;					// size of square in pixels
const MAXPOS = gridsize * squaresize;		// length of one side in pixels 
	
const SKYCOLOR 	= 0xddffdd;				// a number, not a string 
const BLANKCOLOR 	= SKYCOLOR ;			// make objects this color until texture arrives (from asynchronous file read)
const  LIGHTCOLOR 	= 0xffffff ;  //Used with 3d objects


//True because my game is 3d.
const show3d = true;						// Switch between 3d and 2d view (both using Three.js) 
 
const startRadiusConst	 	= MAXPOS * 0.8 ;		// distance from centre to start the camera at
const skyboxConst			= MAXPOS * 3 ;		// where to put skybox 
const maxRadiusConst 		= MAXPOS * 10  ;		// maximum distance from camera we will render things  





//--- Mind can pick one of these actions -----------------
const ACTION_LEFT 		= 0;		   
const ACTION_RIGHT 		= 1;
const ACTION_UP 			= 2;		 
const ACTION_DOWN 		= 3;
const ACTION_STAYSTILL 		= 4;


// contents of a grid square

const GRID_BLANK 	= 0;
const GRID_WALL 	= 1; //Surrounding walls and teleports
const GRID_MAZE 	= 2; //Rocks in the sky
 
 




 
// --- some useful random functions  -------------------------------------------


function randomfloatAtoB ( A, B )			 
{
 return ( A + ( Math.random() * (B-A) ) );
}

function randomintAtoB ( A, B )			 
{
 return  ( Math.round ( randomfloatAtoB ( A, B ) ) );
}
  
function randomBoolean()			 
{
 if ( Math.random() < 0.5 ) { return false; }
 else { return true; }
}







//---- start of World class -------------------------------------------------------
 
function World() { 


// most of World can be private 
// regular "var" syntax means private variables:


var BOXHEIGHT;		// 3d or 2d box height 


var GRID 	= new Array(gridsize);			// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array   
var WALLS 	= new Array ( 4 * gridsize );		// need to keep handles to wall and maze objects so can find them later to paint them 
var MAZE 	= new Array ( NOBOXES );
var theagent, theenemy, follower, bullet, n, n2, enmiss;
//theagent is the players plane 
//theenemy, n and n2 are all other planes
//follower is the object that acts as a camera
//bullet is the players missile
//enmiss is the enemys missile 

// enemy and agent position on squares
var ei, ej, ai, aj, ay, fi, fj, bi, bj, ni, nj, n2i, n2j, mi, qi, qj;
//ei & ej belong to theenemy, ai & aj belong to theagent, fi & fj belong to follower
//bi & bj belong to bullet, ni & nj belong to n, n2i & n2j belong to n2
//qi & qj belong to enmiss

var u = false; //Used for moving players missile 
var u2 = false; //Used for moving enemys missile
var crash = false; //To tell if the player has crashed or has been hit by missile
var jump1 = false; //Used for player jumping
var jump2 = false;//Used for player jumping
var jumpCount = 0; //Used for jumping
var badsteps; //Here badsteps is hitting enemy planes with missiles
var goodsteps;
var  step; //Step
 	var self = this;						// needed for private fn to call public fn - see below  

 


// regular "function" syntax means private functions:


function initGrid()
{
 for (var i = 0; i < gridsize ; i++) 
 {
  GRID[i] = new Array(gridsize);		// each element is an array 

  for (var j = 0; j < gridsize ; j++) 
  {
   GRID[i][j] = GRID_BLANK ;
  }
 }
}
//Added new objects to occupied
function occupied ( i, j )		// is this square occupied
{
 if ( ( ei == i ) && ( ej == j ) ) return true;		// variable objects 
 if ( ( ai == i ) && ( aj == j ) ) return true;
if ( ( ni == i ) && ( nj == j ) ) return true;
if ( ( n2i == i ) && ( n2j == j ) ) return true;
if ( ( qi == i ) && ( qj == j ) ) return true;

 if ( GRID[i][j] == GRID_WALL ) return true;		// fixed objects	 
 if ( GRID[i][j] == GRID_MAZE ) return true;		 
	 
 return false;
}

 
// logically, coordinates are: y=0, x and z all positive (no negative)    
// logically my dimensions are all positive 0 to MAXPOS
// to centre everything on origin, subtract (MAXPOS/2) from all dimensions 

function translate ( x ) 
{
 return ( x - ( MAXPOS/2 ) );
}





//--- skybox ----------------------------------------------------------------------------------------------


function initSkybox() 
{ 


  

//Used the space skybox as I felt it fit well.

// --- alternative skyboxes: ------------------------------

// space skybox, credit:
// http://en.spaceengine.org/forum/21-514-1
// x,y,z labelled differently

   var materialArray = [
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_z.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_z.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_y.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_y.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_x.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_x.jpg" ), side: THREE.BackSide } ) ),
 	];
 	
 	 var skyGeometry = new THREE.CubeGeometry ( skyboxConst, skyboxConst, skyboxConst );	
  var skyMaterial = new THREE.MeshFaceMaterial ( materialArray );
  var theskybox = new THREE.Mesh ( skyGeometry, skyMaterial );
  threeworld.scene.add( theskybox );						// We are inside a giant cube
}







// loader return can call private function


function loadTextures()
{
 
 var manager = new THREE.LoadingManager();
 var loader = new THREE.OBJLoader( manager );
 //Loading user plane
var m = new THREE.MTLLoader();
	m.setTexturePath ( "/uploads/kildufb3/" );
	m.setPath        ( "/uploads/kildufb3/" );
	m.load( "mig-29_fulcrum.mtl", function( materials ) {
 
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( "/uploads/kildufb3/" );
		o.load( "mig-29_fulcrum.obj", function ( object ) {
			addplane ( object );
		} );
	} );
	//Loading AI plane
	var v = new THREE.MTLLoader();
	v.setTexturePath ( "/uploads/kildufb3/" );
	v.setPath        ( "/uploads/kildufb3/" );
	v.load( "fa-18e_superhornet.mtl", function( materials ) {
 
		materials.preload();
		var p = new THREE.OBJLoader();
		p.setMaterials ( materials );
		p.setPath ( "/uploads/kildufb3/" );
		p.load( "fa-18e_superhornet.obj", function ( object ) {
			adden ( object );
		} );
	} );
	//Loading AI plane
	var q = new THREE.MTLLoader();
	q.setTexturePath ( "/uploads/kildufb3/" );
	q.setPath        ( "/uploads/kildufb3/" );
	q.load( "fa-18e_superhornet.mtl", function( materials ) {
 
		materials.preload();
		var l = new THREE.OBJLoader();
		l.setMaterials ( materials );
		l.setPath ( "/uploads/kildufb3/" );
		l.load( "fa-18e_superhornet.obj", function ( object ) {
			addenn ( object );
		} );
	} );
	//Loading AI plane
	var k = new THREE.MTLLoader();
	k.setTexturePath ( "/uploads/kildufb3/" );
	k.setPath        ( "/uploads/kildufb3/" );
	k.load( "fa-18e_superhornet.mtl", function( materials ) {
 
		materials.preload();
		var l = new THREE.OBJLoader();
		l.setMaterials ( materials );
		l.setPath ( "/uploads/kildufb3/" );
		l.load( "fa-18e_superhornet.obj", function ( object ) {
			adden2 ( object );
		} );
	} );
 //Surrounding walls 
 //credit: https://www.google.ie/search?q=wormhole&espv=2&biw=1366&bih=662&source=lnms&tbm=isch&sa=X&ved=0ahUKEwioqvS1gujQAhXrAsAKHVFLAjEQ_AUIBigB#tbm=isch&q=teleport&imgrc=Gq_ogp3cOrNUQM%3A
 var loader1 = new THREE.TextureLoader();
 loader1.load ( '/uploads/kildufb3/wh.jpg',		function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
	} ); 
//Rocks in the users way  
//credit: https://www.google.ie/search?q=rocks&source=lnms&tbm=isch&sa=X&ved=0ahUKEwiYi67qgujQAhXHAMAKHZKLDzwQ_AUICCgB&biw=1366&bih=662#imgrc=v2g5w88gaXYh_M%3A
 var loader2 = new THREE.TextureLoader();
 loader2.load ( '/uploads/kildufb3/rock.jpg',		function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		paintMaze ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
 	} ); 

//this isn't actually used, just for the follower
	var loader5 = new THREE.TextureLoader();
 loader5.load ( '/uploads/starter/ghost.3.png',	function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		follower.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} ); 
	
	//User missile
	var loader6 = new THREE.OBJLoader( manager );
loader6.load( "/uploads/kildufb3/missileaim-120damraam.obj", buildbullet );

//Enemys missile
var loader9 = new THREE.OBJLoader( manager );
loader9.load( "/uploads/kildufb3/missileaim-120damraam.obj", buildenmiss );
}

//Adding plane theagent
function addplane ( object )
{
	object.scale.multiplyScalar (7 );    	   
	theagent = object;
	threeworld.scene.add( theagent ); 
}
//Adding plane thenenmy
function adden ( object )
{
	object.scale.multiplyScalar (7 );    	   
	theenemy = object;
	threeworld.scene.add( theenemy ); 
}
//Adding plane n2
function adden2 ( object )
{
	object.scale.multiplyScalar (7 );    	   
	n2 = object;
	threeworld.scene.add( n2 ); 
}
//Adding plane n
function addenn ( object )
{
	object.scale.multiplyScalar (7 );    	   
	n = object;
	threeworld.scene.add( n ); 
}
//Building bullet
function buildbullet ( object ) 
{ 
    object.scale.multiplyScalar (0.5);    	
    object.traverse( paintBullet );//Calling paintBullet
	bullet = object;
	threeworld.scene.add( bullet ); 
}
//buildng enmiss
function buildenmiss ( object ) 
{ 
    object.scale.multiplyScalar (0.5);    	
    object.traverse( paintBullet ); //Calling paintBullet
	enmiss = object;
	threeworld.scene.add( enmiss ); 
}
//painting missiles red
//credit: https://www.google.ie/search?q=redtiger&espv=2&biw=1366&bih=662&source=lnms&tbm=isch&sa=X&ved=0ahUKEwigzpXGhOjQAhVmJ8AKHeVgA4AQ_AUIBigB#tbm=isch&q=red+tiger+camo&imgrc=F3cos4SoVRXveM%3A
function paintBullet ( child ) 
{
	if ( child instanceof THREE.Mesh ) 
	{
    child.material.map = THREE.ImageUtils.loadTexture( "/uploads/kildufb3/redtiger.png" ); 
	}
}
// --- add fixed objects ---------------------------------------- 
   
 
function initLogicalWalls()		// set up logical walls in data structure, whether doing graphical run or not	
{
 for (var i = 0; i < gridsize ; i++) 
  for (var j = 0; j < gridsize ; j++) 
   if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) )
   {
    	GRID[i][j] = GRID_WALL ;		 
   }
}


function initThreeWalls()		// graphical run only, set up blank boxes, painted later 	
{
 var t = 0;
 for (var i = 0; i < gridsize ; i++) 
  for (var j = 0; j < gridsize ; j++) 
   if ( GRID[i][j] == GRID_WALL )
   {
 	 var shape    = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );			 
 	 //var shape = new THREE.SphereGeometry ( 33, 10, 10 );
 	 var thecube  = new THREE.Mesh( shape );
	 thecube.material.color.setHex( BLANKCOLOR  );			  
 
    	 thecube.position.x = translate ( i * squaresize );   		// translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates 
    	 thecube.position.z = translate ( j * squaresize );   	
    	 thecube.position.y =  0;	
 	 threeworld.scene.add(thecube);
	 WALLS[t] = thecube;				// save it for later
	 t++; 
   }
}


function paintWalls ( material )		 
{
 for ( var i = 0; i < WALLS.length; i++ )
 { 
   if ( WALLS[i] )  WALLS[i].material = material;
 }
}





function initLogicalMaze()		 
{
 for ( var c=1 ; c <= NOBOXES ; c++ )
 {
  	var i = randomintAtoB(1,gridsize-2);	// inner squares are 1 to gridsize-2
  	var j = randomintAtoB(1,gridsize-10);
    	GRID[i][j] = GRID_MAZE ;		 
 }
}

//Rocks/Maze are spheres to be more like rocks in space
function initThreeMaze()		  	
{
 var t = 0;
 for (var i = 0; i < gridsize ; i++) 
  for (var j = 0; j < gridsize ; j++) 
   if ( GRID[i][j] == GRID_MAZE )
   {
    var shape = new THREE.SphereGeometry ( 33, 10, 10 ); 
  	var thecube  = new THREE.Mesh( shape );
	thecube.material.color.setHex( BLANKCOLOR  );			  

  	thecube.position.x = translate ( i * squaresize );   	
  	thecube.position.z = translate ( j * squaresize );   	
  	thecube.position.y =  0;	
    thecube.rotation.set(0, -90, 0);

 	threeworld.scene.add(thecube);
	MAZE[t] = thecube;		// save it for later
	t++; 
   }
}


function paintMaze ( material )		 
{
 for ( var i = 0; i < MAZE.length; i++ )
 { 
   if ( MAZE[i] )  MAZE[i].material = material;
 }
}







// --- enemy functions -----------------------------------

//Draw the 3 enemies
//enemy 1 theenemy
function drawEnemy()	// given ei, ej, draw it 
{
  var x = translate ( ei * squaresize );   	
  var z = translate ( ej * squaresize );   	
  var y = 0;	
  
 theenemy.position.x = x;
 theenemy.position.y = y;
 theenemy.position.z = z;

}
//enemy 2 n
function drawn()	// given ni, nj, draw it 
{
  var x = translate ( ni * squaresize );   	
  var z = translate ( nj * squaresize );   	
  var y = 0;	
  
 n.position.x = x;
 n.position.y = y;
 n.position.z = z;
 
}
//enemy 3 n2
function drawn2()	// given n2i, n2j, draw it 
{
  var x = translate ( n2i * squaresize );   	
  var z = translate ( n2j * squaresize );   	
  var y = 0;	
  
 n2.position.x = x;
 n2.position.y = y;
 n2.position.z = z;
  
}

//Initialise the 3 enemies at the different places
//enemy 1 thenenemy
function initLogicalEnemy()
{
// start in random location on the right of the grid:
 var i, j;
 do
 {
  i = gridsize-3;
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 ei = i;
 ej = j;
 
}

//enemy 2 n
function initLogicaln()
{
// start in random location on the left:
 var i, j;
 do
 {
  i = 2;
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 ni = i;
 nj = j;
 
}

//enemy 3 n2
function initLogicaln2()
{
// start in random location at the border:
 var i, j;
 do
 {
  i = randomintAtoB(1,gridsize-2);
  j = 2;
 }
 while ( occupied(i,j) );  	  // search for empty square 

 n2i = i;
 n2j = j;
 
}



//enemy 1 theenemy
// move from right to left
function moveLogicalEnemy()
{ 

//Set rotations
 var h = 3 * (Math.PI / 2);
 var w = 1 * (Math.PI / 2);
 theenemy.rotation.set(h, 0, w);

 // move from right to left
 var i, j;
 j = ej;
 i = ei - 1;
 
 //teleport if at the edge
 if(i==2)
  {
    i = gridsize-3;
      
  }

  ei = i;
  ej = j;
 
}
//enemy 2 n
// move from left to right
function moveLogicaln()
{ 
//Set rotations
 var h = 3 * (Math.PI / 2);
 var w = 1 * (Math.PI / 2);
 n.rotation.set(h, 0, -w);
 
 // move from left to right
 var i, j;
 j = nj;
 i = ni + 1;
  //teleport if at the edge
 if(i==gridsize-3)
  {
      i = 2;
      
  }
 
  ni = i;
  nj = j;

}
//enemy 3 n2
// move from top to bottom
function moveLogicaln2()
{ 
//Set rotations
 var h = 3 * (Math.PI / 2);
 var w = 2 * (Math.PI / 2);
 n2.rotation.set(h, 0, w);
 // move from top to bottom
 var i, j;
 j = n2j;
 i = n2i;
 
 //Teleport back 
 if(j==gridsize-3)
  {
      j = 2;
      
  }
  if(i < ai) //Get to the same I co-ordinate as the player plane
  {
      i++;
  }
  else if(i>ai) //Get to the same I co-ordinate as the player plane
  {
      i--;
  }
  else //Move towards the player
  {
      j++;
  }
  

  n2i = i;
  n2j = j;

}

//Functions to reset the enemy planes after they're hit by missiles
//enemy 1 theenemy
//Move back to the right side of the grid
function resetEnemy()
{
 var i, j;
 do
 {
  i = gridsize-3;
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 ei = i;
 ej = j;
}
//enemy 2 n
//Move back to the left side of the grid
function resetn()
{
 var i, j;
 do
 {
  i = 2;
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 ni = i;
 nj = j;
}
//enemy 3 n2
//Move back to the top side of the grid
function resetn2()
{
 var i, j;
 do
 {
  i = 2;
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 n2i = i;
 n2j = j;
}
// --- follower functions -----------------------------------

//Draw the follower which is the camera to the grid
function drawFollower()	// given fi, fj, draw it 
{
  var x = translate ( fi * squaresize );   	
  var z = translate ( fj * squaresize );   	
  var y = 100;   //Raise up to get a better view
  
 follower.position.x = x;
 follower.position.y = y;
 follower.position.z = z;
 threeworld.scene.add(follower);
 threeworld.follow.copy ( follower.position ); 
 //threeworld.lookat.copy ( theenemy.position );		// if camera moving, look back at where the enemy is after in move function 
}

//Initialise the follower, start in random location, quickly moves to behind the player
function initLogicalFollower()
{
// start in random location:
 var i, j;
 do
 {
  i = randomintAtoB(1,gridsize-2);
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 fi = i;
 fj = j;
}



//Create 0 sized box
function initThreeFollower()
{
 var shape    = new THREE.BoxGeometry( 0, 0, 0);			 
 follower = new THREE.Mesh( shape );
 follower.material.color.setHex( BLANKCOLOR  );	
 drawFollower();		  
}


//Follow the player, 2 steps behind
function moveLogicalFollower()
{ 


 var i, j;
 i = ai;
 j = aj+2;

 fi = i;
 fj = j;
}


// --- Player Missile functions (bullet) -----------------------------------

//Draws bullet 
function drawBullet()	// given bi, bj, draw it 
{
  var x = translate ( bi * squaresize );   	
  var z = translate ( bj * squaresize );   	
  var y = -50;
  var h = 3 * (Math.PI / 2);
  
 bullet.position.x = x;
 bullet.position.y = y;
 bullet.position.z = z;
 
 bullet.rotation.set(h, 0, 0);
 threeworld.scene.add(bullet);
 
}
//Creates bullet outside the grid, not the best way but is effective
function initLogicalBullet()
{
 var i, j;
 do
 {
  i = 1;
  j = -1;
 }
 while ( occupied(i,j) );  	   

 bi = i;
 bj = j;
}

//First move bullet function
function moveLogicalBulletOne()
{ 
// move to in front of the players plan and begin moving straight ahead 

 var i, j;
 //get co-ordinates of theagent and move to in front of it
 i = ai;
 j = aj;
 //j-- is the position ahead of theagent 
 j--;
 //Move bullet here if it is not occupied
 if ( ! occupied(i,j) )
 {
  bi = i;
  bj = j;
 }
 //if the position is occupied by enemy make u false.
 else if(i==ei && j==ej+1)
 {
     u=false; //u is used to tell the main to move bullet, false means don't move it
     bi = 0;
     bj = 0;
 }
 //otherwise it has hit an object, move to off the map
 else
 {
     u = false; //u is used to tell the main to move bullet, false means don't move it
     bi = 0;
     bj = 0;
 }
}
//Moving missile after first movement
function moveLogicalBulletTwo()
{ 

//i and j are the coordinates of bullet now
 var i, j;
 i = bi;
 j = bj;
 //next space
 j--;
 
 //Move bullet here if it is not occupied
if ( ! occupied(i,j) )  	
 {
 
  bi = i;
  bj = j;
 }
 //if the position is occupied by enemy make u false.
 else if(i==ei && j==ej+1)
 {
     u=false; //u is used to tell the main to move bullet, false means don't move it
     bi = 0;
     bj = 0;
 }
 //otherwise it has hit an object, move to off the map
 else
 {
     u = false; //u is used to tell the main to move bullet, false means don't move it
     bi = 0;
     bj = 0;
 }
 
}

 // --- Enemys Missile functions (enmiss) -----------------------------------

//Draws enmiss
function drawEnmiss()	// given qi, qj, draw it 
{
  var x = translate ( qi * squaresize );   	
  var z = translate ( qj * squaresize );   	
  var y = -50;
  var h = 3 * (Math.PI / 2);
  
 enmiss.position.x = x;
 enmiss.position.y = y;
 enmiss.position.z = z;
 
 enmiss.rotation.set(h, 0, 0);

 
}

//Funtion to intialise enmiss outside the grid
function initLogicalEnmiss()
{
// start in outside the grid:
 var i, j;
 do
 {
  i = 2;
  j = -1;
 }
 while ( occupied(i,j) );  	  

 qi = i;
 qj = j;
}



//Function to move from n2 position towards theagent
function moveLogicalEnmissOne()
{ 
//Get the co-ordinates of n2, start enmiss there
 var i, j;
 i = n2i;
 j = n2j;
 //Move towards theagent
 j++;
 
 //Move ahead if the space isnt occupied
 if ( ! occupied(i,j) )
 {
  qi = i;
  qj = j;
 }
 //if theagent is in this location end the game 
 else if(i==ai && j==aj)
 {
     u2=false;
     qi = 0;
     qj = 0;
     crash = true; //player has been hit by missile, end game
 }
 //enmiss hit another object, move away from the grid
 else
 {
     u2 = false;
     qi = 0;
     qj = 0;
 }
}



//Function to keep moving enmiss towards theagent
function moveLogicalEnmissTwo()
{ 
//Gets the current co-ordinates of enmiss

 var i, j;
 i = qi;
 j = qj;
 //towards theagent
 j++;
 //Move ahead if the space isnt occupied
if ( ! occupied(i,j) )  	
 {
 
  qi = i;
  qj = j;
 }
 //if theagent is in this location end the game 
 else if(i==ai && j==aj-1)
 {
     u2=false;
     qi = 0;
     qj = 0;
     crash=true; //player has been hit by missile, end game
 }
 //enmiss hit another object, move away from the grid
 else
 {
     u2 = false;
     qi = 0;
     qj = 0;
 }
 
 
}


// --- agent functions -----------------------------------

//Draw theagent
function drawAgent()	// given ai, aj, draw it 
{
  var x = translate ( ai * squaresize );   	
  var z = translate ( aj * squaresize );   	
  var y = ay * squaresize;	
  //The right roation
  var h = 3 * (Math.PI / 2);
 theagent.position.x = x;
 theagent.position.y = y;
 theagent.position.z = z;


 threeworld.lookat.copy ( theagent.position );		// follow vector = agent position (for camera following agent)
}

//Start off the agent at the bottom of the grid with a random j
function initLogicalAgent()
{
// start in random location:
 var i, j;
 do
 {
  i = 30;
  j = gridsize-3;
 }
 while ( occupied(i,j) );  	  // search for empty square 
//Set ai, aj and ay
 ai = i;
 aj = j;
 ay = 0;
}


//Move the agent forward or to the side if the user requests
function moveLogicalAgent( a )			
{ 
//Get the co-ordinates
 var i = ai;
 var j = aj;	
 var y = ay;
var h = 3 * (Math.PI / 2);
      //Move left and change roation
      if ( a == ACTION_LEFT )
      {
          theagent.rotation.set(h, -h/8, 0);
          i--;
      }
      //Move right and change roation
      else if ( a == ACTION_RIGHT )
      {
          theagent.rotation.set(h, h/8, 0);
          i++;
          //turnSound();
      }
    //Boost by pressing "b", used for testing mainly
    else if ( a == ACTION_DOWN ) 
    {
        j--;
         theagent.rotation.set(h, 0, 0);
    }    
    //else reset rotation
    else
    {
         theagent.rotation.set(h, 0, 0);
    }

//teleport of the plane reaches the top
var teleport = gridsize-3;
if(j==1)
{
  j=teleport;
}
//End the game if the plane crashes
 if(occupied(i,j) ) 
 {
     crash = true;
     
 }
 //Else move the plane and the follower(camera) forward
 else
 {
  ai = i;
  aj = j;
  fi = i;
  fj = j+2;
 }
 ay = y;
}

//Move forward and to the sides while jumping
function moveLogicalAgentWhileJump( a )			
{ 
//Get co-ordinates of theagent
 var i = ai;
 var j = aj;	
 var y = ay;
 //move to the sides if the user presses keys
      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_DOWN ) 	j--;
 
 //Move forward, dont need to check occupied because plane is above
 j--;

 //teleport if need to
 var teleport = gridsize-3;
if(j==1)
{
  j=teleport;
}

//Update co-ordinates
  ai = i;
  aj = j;
  fi = i;
  fj = j+2;
 ay = y;
 
 //Keep track of spaces moved while jump
 jumpCount++; //Increment 
 
 //If it is 3, that means the jumping must stop and return to normal height
 if(jumpCount == 3)
 {
     jump2 = false; //notify main to tell it it is finshed main jumping part
     jumpCount = 0;
 }
}

//First intial jump, raises the plane up and makes jump1 and jump2 to allow to move in the air
function jumpLogicalAgentOne()			
{ 
//get co-ordinates
 var i = ai;
 var j = aj;	
 var y = ay;
 var h = 3 * (Math.PI / 2);
//next position
 j--;

 //Teleport if need to
 var teleport = gridsize-3;
if(j==1)
{
  j=teleport;
}
//Update co-ordinates
  ai = i;
  aj = j;
  fi = i;
  fj = j+2;
  ay = y + 1; //Raise up
  jump1 = true; //Used for next jumping functions
  jump2 = true;
}

//Last jump function, returns to normal height
function jumpLogicalAgentTwo()			
{ 
//get co-ordinates
 var i = ai;
 var j = aj;	
 var y = ay;
//Next space
 j--;
 //Teleport if need to
var teleport = gridsize-3;
if(j==1)
{
  j=teleport;
  //i = randomintAtoB(1, 20);
}
//Update co-ordinates
  ai = i;
  aj = j;
  fi = i;
  fj = j+2;
  ay = y - 1;
  jump1 = false; //notify the main that the jumping is finished
  jump2 = false;
}







//-------key handler-----------
function keyHandler(e)		
// user control 
{
    if (e.keyCode == 37)  moveLogicalAgent ( ACTION_LEFT 	); //left arrow key
    if (e.keyCode == 38 && jump1 == false) //up arrow key
    {
        jumpLogicalAgentOne();
    }
    if (e.keyCode == 39)  moveLogicalAgent ( ACTION_RIGHT 	); //right arrow key
    if (e.keyCode == 66)  moveLogicalAgent ( ACTION_DOWN	); //"b"key
    if (e.keyCode == 32) //speace bar key fires bullets
    {
        u = true;
        moveLogicalBulletOne();
        fireMissile();
    }
}





// --- score: -----------------------------------


function badstep()			//Counts enemies shot (thenenemy)
{
  //if the bullet equals the same co-ordinates
 if(bi==ei && bj==(ej+1))
    {
     return true;
     
    }
    else
        return false;
}


function badstep2()			//Counts enemies shot (n)
{
    //if the bullet equals the same co-ordinates
 if(bi==ni && bj==(nj+1))
    {
     return true;
     
    }
    else
        return false;
}

function badstep3()			//Counts enemies shot (n2)
{
    //if the bullet equals the same co-ordinates
 if(bi==n2i && bj==(n2j+1))
    {
     return true;
     
    }
    else
        return false;
}

function agentBlocked()			// agent is blocked on all sides, run over
{
 return ( 	occupied (ai-1,aj) 		&& 
		occupied (ai+1,aj)		&&
		occupied (  ai,aj+1)		&&
		occupied (  ai,aj-1) 	);		
} 



function updateStatusBefore(a)
// this is called before anyone has moved on this step, agent has just proposed an action
// update status to show old state and proposed move 
{
 var x = self.getState();
 var status = " Step: <b> " + step + " </b> "; 

 $("#user_span3").html( status );
}


function   updateStatusAfter()		// agent and enemy have moved, can calculate score
{
 // new state after both have moved
 var y = self.getState();
 var status = "Use arrow keys and spacebar to play. Please click move with <BR> "; 
 $("#user_span4").html( status );

 var score = self.getScore();

 var status = "   Enemys Shot: " + badsteps; 

 $("#user_span5").html( status );
}






//--- public functions / interface / API ----------------------------------------------------------


	this.endCondition;			// If set to true, run will end. 



this.newRun = function() 
{

// (subtle bug) must reset variables like these inside newRun (in case do multiple runs)

  this.endCondition = false;
  badsteps = 0;	
  goodsteps = 0;
	step = 0;


 // for all runs:

 	initGrid();
	initLogicalWalls(); 
	initLogicalMaze();
	initLogicalAgent();
	initLogicalEnemy();
	initLogicaln();
	initLogicaln2();
    initLogicalFollower();
    initLogicalBullet();
    initLogicalEnmiss();
 // for graphical runs only:

  if ( true  )
  {
	if ( show3d )
	{
	 BOXHEIGHT = squaresize;
	 threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 
	 // light
    		var ambient = new THREE.AmbientLight();
    		threeworld.scene.add( ambient );

		
	   
		
	}	     
	else
	{
	 BOXHEIGHT = 1;
	 threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 		     
	}

	initSkybox();
 	initMusic();

	// Set up objects first:

	initThreeWalls(); 
	initThreeMaze();
    initThreeFollower();
 

	loadTextures();	

	document.onkeydown = keyHandler;	 
  }

};




this.getState = function()
{
 var x = [ ai, aj, ei, ej ];
  return ( x );  
};



this.takeAction = function ( a )
{
//Let the game load 
 if(step<60)
 {
   
    step++;
 }
 else
 {
    step++;
  if ( true  )
   updateStatusBefore(a);			// show status line before moves 
//player move
 if ( ( step % 2 ) == 0 )
{
  //Check if the player is jumped
  if(jump1 == true)
  {
      //Check if the player is jumped
      if(jump2 == false)
      {
        jumpLogicalAgentTwo(); //return to normal height
        
      }
      else
      {
          moveLogicalAgentWhileJump(); //Move normally raised up
      }
  }
  //otherwise move normally
  else{  
    moveLogicalAgent(a);
  moveLogicalFollower();
    }
}  
  //Move the enemies
  if ( ( step % 5 ) == 0 )	
  {// slow the enemy down to every 5th step
    moveLogicalEnemy();
    moveLogicaln();
    moveLogicaln2();
  }
  //Move enmiss missile
  if(u2==true)
  {
      moveLogicalEnmissTwo();
      
  }
  //Move bullet missile
  if(u == true)
  {
     moveLogicalBulletTwo();
  }
  //AI to shoot enmiss missile if the agent is close enough to n2
  if(u2==false && ai==n2i)
  {
      var distance = aj - n2j;
      if(distance < 8 && distance >0)
      {
        u2=true;
        fireMissile(); //Sound
        moveLogicalEnmissOne(); //Start movement
      }
  } 
  //if theenemy is hit by missile
  if ( badstep() )
  {
    badsteps++; //Score goes up
    resetEnemy(); //reset enemy
    u=false;
    bi = 0;
    bj = 0;
    soundAlarm();
    enemyShotSound();
    
  }
//if n is hit by missile
if ( badstep2() )
  {
    badsteps++; //Score goes up
    resetn(); //reset enemy
    u=false;
    bi = 0;
    bj = 0;
    soundAlarm();
    enemyShotSound();
  }
  //if n2 is hit by missile
  if ( badstep3() )
  {
    badsteps++; //Score goes up
    resetn2(); //reset n2
    u=false;
    bi = 0;
    bj = 0;
    enemyShotSound();
    soundAlarm();

  }
  if ( true  )
  {
   drawAgent();
   drawEnemy();
   drawn();
   drawn2();
   drawFollower();
   drawBullet();
   drawEnmiss();
   
   updateStatusAfter();			// show status line after moves  
  }
  //End game if the agent crashes or is hit by missile
  if(crash == true)
  {
    soundAlarm();
    this.endCondition = true;
    
      
  }

  if ( agentBlocked() )			// if agent blocked in, run over 
  {
	this.endCondition = true;
	goodsteps = 0;			// you score zero as far as database is concerned 			 
  	if ( true  )
  	{
	 musicPause();
	 soundAlarm();
	}
  }
}
};



this.endRun = function()
{
 if ( true  )
 {
  musicPause(); 
  if ( this.endCondition )
    $("#user_span6").html( " &nbsp; <font color=red> <B> You have died. Better luck next time! </B> </font>   "  );
  else
    $("#user_span6").html( " &nbsp; <font color=red> <B> Run over. </B> </font>   "  );
 }
};


this.getScore = function()
{
 return badsteps;
};


}

//---- end of World class -------------------------------------------------------








// --- music and sound effects ----------------------------------------
// credits:
// http://www.dl-sounds.com/royalty-free/defense-line/
// http://soundbible.com/1542-Air-Horn.html 



function initMusic()
{
	// put music element in one of the spans
  	var x = "<audio  id=theaudio  src=http://www.student.computing.dcu.ie/~kildufb3/song2.mp3  autoplay loop> </audio>" ;
  	$("#user_span1").html( x );
} 
 

function musicPlay()  
{
	// jQuery does not seem to parse pause() etc. so find the element the old way:
 	document.getElementById('theaudio').play();
}


function musicPause() 
{
 	document.getElementById('theaudio').pause();
}


function soundAlarm()
{
 	var x = "<audio    src=/uploads/kildufb3/soundeffectsexplosion.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}

function fireMissile()
{
 	var x = "<audio    src=/uploads/kildufb3/soundeffect-missilelaunch.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}

function teleportSound()
{
 	var x = "<audio    src=/uploads/kildufb3/timewarpsoundeffect.mp3   autoplay  > </audio>";
 	//var x = "<audio    src=http://www.student.computing.dcu.ie/~kildufb3/Hans Zimmer - Cornfield Chase - (Interstellar OST) HQ Audio!.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}

function turnSound()
{
 	var x = "<audio    src=/uploads/kildufb3/jetflyby-soundeffect.mp3   autoplay  > </audio>";
 	//var x = "<audio    src=http://www.student.computing.dcu.ie/~kildufb3/Hans Zimmer - Cornfield Chase - (Interstellar OST) HQ Audio!.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}

function enemyShotSound()
{
 	var x = "<audio    src=/uploads/kildufb3/soundeffects-tangodownmw2-rangers.mp3   autoplay  > </audio>";
 	//var x = "<audio    src=http://www.student.computing.dcu.ie/~kildufb3/Hans Zimmer - Cornfield Chase - (Interstellar OST) HQ Audio!.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}