//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( " <font color=red> <B> You have died. Better luck next time! </B> </font> " );
else
$("#user_span6").html( " <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 );
}