/* Get over the wall, created by Daniel Holton
This game is a fairly basic concept, to navigate a maze and end to the endzone while avoiding projectiles shot by an enemy.
Due to the recent election of trump, I thought it would be a bit of a laugh to create to create a game where a wee mexican chap had to scale over the proposed wall trump is building.
The map is partitioned into different sections in order to create a basic maze, a random maze is then also created to make navigation more challenging. Unfortunately this sometimes results in no valid path for the mexican chap
to move to the end. I tried to remedy this by allowing a gap of 1 square on the far side of the maze. This did not completely fix the problem but I found that a blockage would only occur every 10 runs or so.
Navigation is made possible by the arrow keys, and the game will terminate whenever we reach the end or we get hit by one of trumps bricks.
*/
const CLOCKTICK = 30; // I sped up the run a bit, this was to make the projectiles move faster. Decreasing this number will result in a more challanging game
const MAXSTEPS = 10000000; // length of a run before final score
const LIGHTCOLOR = 0xffffff ;
//---- global constants: -------------------------------------------------------
const gridsize = 50 ; // number of squares along side of world
const NOBOXES = Math.trunc ( (gridsize * gridsize)/10 );
const squaresize = 30;
const MAXPOS = gridsize * squaresize;
const SKYCOLOR = 0xf4a460;
const BLANKCOLOR = 0x00ff00 ;
const grey = 0x808080;
const red = SKYCOLOR
const show3d = false; // Personally, I believe it's far easier to navigate the maze in 2D
const startRadiusConst = MAXPOS * 0.8 ;
const skyboxConst = MAXPOS * 3 ;
const maxRadiusConst = MAXPOS * 10 ;
//--- 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;
const GRID_MAZE = 2;
// --- 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() {
var BOXHEIGHT;
var GRID = new Array(gridsize);
var WALLS = new Array ( 4 * gridsize );
var MAZE = new Array ( NOBOXES);
var theagent, brick, theenemy;
var ei, ej, ai, aj, si, sj,bi,bj; // e= enemy coords---- a = agent coords----- b = brick coords
var step;
var dBrick = false; //boolean to represent if a brick is thrown or not
var isShot = false; // boolean to represent if the agent is shot
var isEnd = false; // boolean to represent if the agent has reached the end of the maze
var self = this;
// 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 ;
}
}
}
function occupied ( i, j ) // is this square occupied
{
if ( ( ei == i +1 ) && ( ej == j+1 ) ) return true; // variable objects
if ( ( ai == i -1 ) && ( aj == j+1 ) ) return true;
if ( ( si == i +1 ) && ( sj == j-1 ) ) return true;
if ( GRID[i][j] == GRID_WALL) return true;
if ( GRID[i][j] == GRID_MAZE) return true;// fixed objects
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()
{
var materialArray = [
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh6fixed.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh2.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh5.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/holtond2/border.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh4.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh1.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
}
//These are the brick textures, the program wouldn't draw the brick texture if it was called at the same time and place as the other textures
function loadtextures2()
{
var loader4 = new THREE.TextureLoader();
loader4.load ( '/uploads/holtond2/projectile.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
brick.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
}
function loadTextures()
{
var loader1 = new THREE.TextureLoader();
loader1.load ( '/uploads/holtond2/brick.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader2 = new THREE.TextureLoader();
loader2.load ( '/uploads/holtond2/brick.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintMaze ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader3 = new THREE.TextureLoader();
loader3.load ( '/uploads/holtond2/mehico.png', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
theagent.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
var loader5 = new THREE.TextureLoader();
loader5.load ( '/uploads/holtond2/trump.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
theenemy.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
}
// --- add fixed objects ----------------------------------------
function initLogicalWalls()
{
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 ;
}
}
//These are all the mazes that have been drawn in the program
function initRightMaze()
{
for(var i = 3; i <gridsize-1 ;i++)
for(var j = 0; j < gridsize-1 ; j++)
{
j = j+10;
if((i != 0) || (i != gridsize-1) || (j != 0) || (j != gridsize -1))
{
GRID[i][j] = GRID_MAZE;
}
}
}
function initLeftMaze()
{
for(var i = 1; i <gridsize-4 ;i++)
for(var j = 6; j < gridsize-1 ; j++)
{
j = j+10;
if((i != 0) || (i != gridsize-1) || (j != 0) || (j != gridsize -1))
{
GRID[i][j] = GRID_MAZE;
}
}
}
function initShooterPartition()
{
for(var i = 1; i < gridsize-1; i++)
{
GRID[i][47] = GRID_MAZE;
}
}
function initLogicalMaze()
{
for ( var c=1 ; c <= NOBOXES ; c++ )
{
var i = randomintAtoB(2,gridsize-4);
var j = randomintAtoB(1,gridsize-3);
GRID[i][j] = GRID_MAZE ;
}
}
function initThreeWalls()
{
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 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;
threeworld.scene.add(thecube);
WALLS[t] = thecube;
t++;
}
}
function paintWalls ( material )
{
for ( var i = 0; i < WALLS.length; i++ )
{
if ( WALLS[i] ) WALLS[i].material = material;
}
}
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.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
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;
threeworld.scene.add(thecube);
MAZE[t] = thecube;
t++;
}
}
function paintMaze ( material )
{
for ( var i = 0; i < MAZE.length; i++ )
{
if ( MAZE[i] ) MAZE[i].material = material;
}
}
// --- brick functions -----------------------------------
function drawBrick()
{
var x = translate ( bi * squaresize );
var z = translate ( bj * squaresize );
var y = 0;
brick.position.x = x;
brick.position.y = y;
brick.position.z = z;
threeworld.scene.add(brick);
threeworld.lookat.copy ( brick.position );
}
function initLogicalBrick()
{
var i, j;
do
{
i = ei;
j = ej;
}
while ( occupied(i,j) );
bi = i;
bj = j;
}
function initThreeBrick()
{
var shape = new THREE.BoxGeometry( 15, 15, 15 );
brick = new THREE.Mesh( shape );
brick.material.color.setHex( BLANKCOLOR );
drawBrick();
}
function moveLogicalBrick()
{
var i, j;
if(bj != 0) //The bullet keeps moving until it reaches the end of the course
{
i = bi;
j = bj -1; //The bullet only has to move through the j-axis
bi = i;
bj = j;
}
}
// --- agent functions -----------------------------------
function drawAgent()
{
if ( theagent )
{
var x = translate ( ai * squaresize );
var z = translate ( aj * squaresize );
var y = ( 0 );
theagent.position.x = x;
theagent.position.y = y;
theagent.position.z = z;
threeworld.scene.add(theagent);
threeworld.follow.copy ( theagent.position );
}
}
function initLogicalAgent()
{
ai = 48;
aj = 2;
}
function initThreeAgent()
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
theagent = new THREE.Mesh( shape );
theagent.material.color.setHex( BLANKCOLOR );
drawAgent();
}
function moveLogicalAgent( a )
{
var i = ai;
var j = aj;
if ( a == ACTION_LEFT ) i--;
else if ( a == ACTION_RIGHT ) i++;
else if ( a == ACTION_UP ) j++;
else if ( a == ACTION_DOWN ) j--;
if ( ! occupied(i,j) )
{
ai = i;
aj = j;
}
}
// ------Enemy functions -----------------------------------------
function drawEnemy()
{
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;
threeworld.scene.add(theenemy);
threeworld.lookat.copy ( theenemy.position );
}
function initLogicalEnemy()
{
ei = 48;
ej = 48;
}
function initThreeEnemy()
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
theenemy = new THREE.Mesh( shape );
theenemy.material.color.setHex( BLANKCOLOR );
drawEnemy();
}
function moveLogicalEnemy()
{
var i, j;
if ( ei < ai ) i = randomintAtoB(ei, ei+1);
if ( ei == ai ) i = ei;
if ( ei > ai ) i = randomintAtoB(ei-1, ei);
if ( ej < aj ) j = randomintAtoB(ej, ej+1);
if ( ej == aj ) j = ej;
if ( ej > aj ) j = randomintAtoB(ej-1, ej);
if ( ! occupied(i,j) )
{
ei = i;
ej = j;
}
}
function keyHandler(e)
{
if (e.keyCode == 37) moveLogicalAgent ( ACTION_LEFT );
if (e.keyCode == 38) moveLogicalAgent ( ACTION_DOWN );
if (e.keyCode == 39) moveLogicalAgent ( ACTION_RIGHT );
if (e.keyCode == 40) moveLogicalAgent ( ACTION_UP );
}
//----- Checks if the user has been shot --------------------------------
function gotShot()
{
if(bi == ai && bj == aj)
{
isShot = true;
}
}
//------- Checks if the user has reached the end of the maze ---------
function gotEnd()
{
if(ai == 46 &&(aj == 46 || aj == 45 || aj == 44))
{
isEnd = true;
}
}
// --- score: -----------------------------------
function badstep() // is the enemy within one square of the agent
{
if ( ( Math.abs(ei - ai) < 2 ) && ( Math.abs(ej - aj) < 2 ) ) return true;
else return false;
}
function count()
{
var x = x + 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> x = (" + x.toString() + ") a = (" + a + ") ";
$("#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 = " y = (" + y.toString() + ") <BR> ";
$("#user_span4").html( status );
var score = self.getScore();
var status = " Bad steps: " + badsteps +
" Good steps: " + goodsteps +
" Score: " + score.toFixed(2) + "% ";
$("#user_span5").html( status );
}
//--- public functions / interface / API ----------------------------------------------------------
this.endCondition; // If set to true, run will end.
this.newRun = function()
{
this.endCondition = false;
badsteps = 0;
goodsteps = 0;
step = 0;
// for all runs:
initGrid();
initLogicalWalls();
initRightMaze();
initLeftMaze();
initShooterPartition();
initLogicalMaze();
initLogicalAgent();
initLogicalEnemy();
// for graphical runs only:
if ( true )
{
// var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
// thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
// threeworld.scene.add(thelight);
if ( show3d )
{
BOXHEIGHT = squaresize;
threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
// thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
threeworld.scene.add(thelight);
}
else
{
BOXHEIGHT = 1;
threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
}
initSkybox();
initMusic();
// Set up objects first:
initThreeWalls();
initThreeMaze();
initThreeAgent();
initThreeEnemy();
// Then paint them with textures - asynchronous load of textures from files.
// The texture file loads return at some unknown future time in some unknown order.
// Because of the unknown order, it is probably best to make objects first and later paint them, rather than have the objects made when the file reads return.
// It is safe to paint objects in random order, but might not be safe to create objects in random order.
loadTextures();
document.onkeydown = keyHandler;
}
};
this.getState = function()
{
var x = [ ai, aj, ei, ej, si ,sj ];
return ( x );
};
this.takeAction = function ( a )
{
step++;
if ( true )
updateStatusBefore(a); // show status line before moves
moveLogicalEnemy();
moveLogicalEnemy();
if(step % 50 ==0)
{
dBrick = true;
initThreeBrick();
initLogicalBrick();
loadtextures2();
}
moveLogicalBrick();
gotShot();
gotEnd();
if ( badstep() )
badsteps++;
else
goodsteps++;
if ( true )
{
drawAgent();
drawEnemy();
if (dBrick == true)
drawBrick();
if(isEnd == true)
{
this.endCondition = true;
musicPause();
mexicanHat();
}
if(isShot == true)
{
this.endCondition = true;
musicPause();
sadMusic();
}
updateStatusAfter(); // show status line after moves
}
};
this.endRun = function()
{
if ( true )
{
if ( this.endCondition )
{
if(isShot == true)
$("#user_span6").html( " <font color=red> <B>You've been BRICKED</B> </font> " );
if(isEnd == true)
$("#user_span6").html( " <font color=red> <B> You Made it over the Wall, Congratulations! </B> </font> " );
}
}
};
this.getScore = function()
{
return ( ( goodsteps / step ) * 100 );
};
}
//---- 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=/uploads/holtond2/stupidquote-donaldtrumpremixofficialmusicvideo.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 sadMusic()
{
var x = "<audio src=/uploads/holtond2/sadpianoitshardtosaygoodbyebymichaelortegamp3cut.net.mp3 autoplay > </audio>";
$("#user_span2").html( x );
}
function mexicanHat()
{
var x = "<audio src=/uploads/holtond2/mexicanhat.mp3 autoplay > </audio>";
$("#user_span3").html( x );
}