// ====================================================================================================================
//
// Football World
//
// ====================================================================================================================
// ====================================================================================================================
//
// Scoring: Player is given a point each time they score a goal.
// The arrow keys are used to move the player.
// The PgUp key taps the ball and PgDn key kicks the ball.
// The player can hit the ball straight or at an angle and can bounce it off the walls.
// Each time the ball is kicked into the goal; the ball, the player and the maze are reset.
//
// ====================================================================================================================
const CLOCKTICK = 100; // speed of run in milliseconds
const MAXSTEPS = 1000; // length of each run before final score
// --- GLOBAL CONSTANTS: ----------------------------------------------------------------------------------------------
const gridsize = 20; // number of squares along side of world
const NOBOXES = 8; // density of maze - number of internal boxes
const squaresize = 100; // size of square in pixels
const MAXPOS = gridsize * squaresize; // length of one side in pixels
const ballrad = Math.trunc( squaresize / 2 ); // size of ball radius in pixels
const postrad = Math.trunc( squaresize / 3 ); // size of goal post radius in pixels
const postheight = 800; // height of goal post in pixels
const TAP = 2; // Number of squares ball moves when tapped (PgUp key)
const KICK = 8; // Number of squares ball moves when kicked (PgDn key)
const SKYCOLOR = 0x885D5D; // a number, not a string
const BLANKCOLOR = SKYCOLOR; // default colour until texture applied
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'S ACTIONS -------------------------------------------------------------------------------------------------
// in initial view, (smaller-larger) on i axis is aligned with (left-right)
// in initial view, (smaller-larger) on j axis is aligned with (away from you - towards you)
const ACTION_LEFT = 0;
const ACTION_RIGHT = 1;
const ACTION_UP = 2;
const ACTION_DOWN = 3;
const ACTION_STAYSTILL = 4;
const ACTION_TAP = 5;
const ACTION_KICK = 6;
// contents of a grid square
const GRID_BLANK = 0;
const GRID_WALL = 1;
const GRID_MAZE = 2;
const GOAL_POST = 3;
const GOAL_LINE = 4;
// --- FUNCTIONS ------------------------------------------------------------------------------------------------------
function randomfloatAtoB( A, B )
{
return ( A + ( Math.random() * ( B - A ) ) );
}
function randomintAtoB( A, B )
{
return ( Math.round ( randomfloatAtoB( A, B ) ) );
}
// --- WORLD CLASS ----------------------------------------------------------------------------------------------------
function World() {
var BOXHEIGHT; // 3d or 2d box height
var GRID = new Array( gridsize ); // initialises fixed objects
var WALLS = new Array( 4 * gridsize );
var MAZE = new Array( NOBOXES );
var GROUND = new Array( 1 );
var AUDIENCE = new Array( 4 );
var theplayer, theball; // initialise variable objects
var bi, bj, pi, pj; // ball and player position on squares
var goals;
var step;
var sideway = 0; // no of squares that ball moves sideways : + is right; - is left
var tofrom = 0; // no of squares that ball moves forward/back : + is nearer; - is away
var pos = 0; // Position of player relative to ball when adjacent to it
var rolling = false; // State of the ball : FALSE = stationary; TRUE = rolling after being kicked
var dist = 0; // number of squares ball has to move
var self = this; // needed for private fn to call public fn
function initGrid() // initialise the grid
{
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 ) // checks if a square is occupied
{
if ( ( bi == i ) && ( bj == j ) ) return true; // variable objects
if ( ( pi == i ) && ( pj == j ) ) return true;
if ( GRID[i][j] == GRID_WALL ) return true; // fixed objects
if ( GRID[i][j] == GRID_MAZE ) return true;
if ( GRID[i][j] == GOAL_POST ) return true;
return false;
}
function translate ( x ) // centre objects on origin
{
return ( x - ( MAXPOS / 2 ) );
}
// --- SKYBOX ---------------------------------------------------------------------------------------------------------
function initSkybox()
{
// skybox, credit:
// https://www.dreamstime.com/stock-photo-green-grass-blue-clear-sky-spring-nature-panorama-theme-banner-super-high-resolution-premium-quality-image45126910
var materialArray = [
( new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/uploads/macdonc5/right.png" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/uploads/macdonc5/left.png" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/uploads/macdonc5/top.png" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/uploads/macdonc5/bottom.png" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/uploads/macdonc5/back.png" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( "/uploads/macdonc5/front.png" ), 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 );
}
// ====================================================================================================================
// ====================================================================================================================
// ====================================================================================================================
// This does the file read the old way using loadTexture.
// (todo) Change to asynchronous TextureLoader. A bit complex:
// Make blank skybox. Start 6 asynch file loads to call 6 return functions.
// Each return function checks if all 6 loaded yet. Once all 6 loaded, paint the skybox.
// ====================================================================================================================
// ====================================================================================================================
// ====================================================================================================================
// --- LOAD TEXTURES --------------------------------------------------------------------------------------------------
// credits:
// football: http:http://hd-wallpapers.us/wp-content/uploads/2014/06/golden-football.jpg
function loadTextures()
{
var loader1 = new THREE.TextureLoader(); // the walls texture
loader1.load ( '/uploads/macdonc5/walls.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader2 = new THREE.TextureLoader(); // the maze texture
loader2.load ( '/uploads/macdonc5/walls.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintMaze ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader4 = new THREE.TextureLoader(); // the ball texture
loader4.load ( '/uploads/macdonc5/football.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
theball.material = new THREE.MeshBasicMaterial( { map: thetexture } );
} );
var loader5 = new THREE.TextureLoader(); // the audience texture
loader5.load ( '/uploads/macdonc5/crowd.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintAudience ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
var loader6 = new THREE.TextureLoader(); // the ground texture
loader6.load ( '/uploads/macdonc5/pitch.jpg', function ( thetexture ) {
thetexture.minFilter = THREE.LinearFilter;
paintGround ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
// load OBJ plus MTL (plus TGA files)
// THREE.Loader.Handlers.add ( /.tga$/i, new THREE.TGALoader() );
var m = new THREE.MTLLoader();
m.setTexturePath( "/uploads/macdonc5/" );
m.setPath( "/uploads/macdonc5/" );
m.load( "lego_man.mtl", function ( materials ){
materials.preload();
var o = new THREE.OBJLoader();
o.setMaterials( materials );
o.setPath( "/uploads/macdonc5/" );
o.load( "lego_man.obj", function ( object ) {
initThreePlayer( object );
} );
} );
}
// --- FIXED OBJECTS --------------------------------------------------------------------------------------------------
// --- walls ----------------------------------------------------------------------------------------------------------
function initLogicalWalls() // set up walls in data structure around the edge of the grid
{
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() // build blank boxes
{
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 ); // cube geometry
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize ); // translate (i,j) block-numbering 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 ) // paint the blank boxes
{
for ( var i = 0; i < WALLS.length; i++ )
{
if ( WALLS[i] ) WALLS[i].material = material;
}
}
// --- audience -------------------------------------------------------------------------------------------------------
function addAudience() // set up audience
{
var i, j, t;
// leftwall audience centre point
t = 0;
i = 0;
j = Math.trunc( gridsize / 2 );
initThreeAudience( i, j, t);
// backwall audience centre point
t = 1;
i = Math.trunc( gridsize / 2 );
j = 0;
initThreeAudience( i, j, t);
// rightwall audience centre point
t = 2;
i = Math.trunc( gridsize );
j = Math.trunc( gridsize / 2 );
initThreeAudience( i, j, t);
}
function initThreeAudience( i, j, t) // build audience
{
var shape = new THREE.PlaneGeometry( squaresize*gridsize, squaresize * gridsize / 2 ); // plane geometry
var thecrowd = new THREE.Mesh( shape );
thecrowd.material.color.setHex( BLANKCOLOR );
thecrowd.position.x = translate ( i * squaresize ); // translate (i,j) block-numbering to three.js (x,y,z) coordinates
thecrowd.position.z = translate ( j * squaresize );
thecrowd.position.y = 450;
// rotate the side crowds by 90 degrees
if ( i == gridsize )
{
thecrowd.rotation.y = ( -Math.PI / 2); // right hand audience
}
if ( i == 0 )
{
thecrowd.rotation.y = ( Math.PI / 2 ); // left hand audience
}
AUDIENCE[t] = thecrowd; // save it for later
threeworld.scene.add( thecrowd );
}
function paintAudience ( material ) // paint the audience
{
for ( var i = 0; i < AUDIENCE.length; i++ )
{
if ( AUDIENCE[i] ) AUDIENCE[i].material = material;
}
}
// --- ground ---------------------------------------------------------------------------------------------------------
function initThreeGround() // set up the ground
{
var i, j;
i = gridsize / 2;
j = gridsize / 2;
var shape = new THREE.PlaneGeometry( squaresize * ( gridsize - 2 ), squaresize * ( gridsize - 2 ) );
var theground = new THREE.Mesh( shape );
theground.material.color.setHex( BLANKCOLOR );
theground.position.x = translate ( i * squaresize ); // translate (i,j) block-numbering to three.js (x,y,z) coordinates
theground.position.z = translate ( j * squaresize );
theground.position.y = 0;
theground.rotation.x = (-Math.PI / 2);
threeworld.scene.add( theground );
GROUND[0] = theground; // save it for later
}
function paintGround ( material ) // paint the ground
{
if ( GROUND[0] ) GROUND[0].material = material;
}
// --- maze -----------------------------------------------------------------------------------------------------------
function initLogicalMaze() // set up the maze
{
for ( var c = 1; c <= NOBOXES; c++ )
{
var i = randomintAtoB( 2, gridsize - 3 ); // inner squares are 1 to gridsize-2 so leave at least 1 clear row on left and right
var j = randomintAtoB( 5, gridsize - 6 ); // leave at least 4 clear rows at goal end and and 5 clear rows at near end
GRID[i][j] = GRID_MAZE;
}
}
function initThreeMaze() // build blank boxes
{
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 ); // cube geometry
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize ); // translate (i,j) block-numbering to three.js (x,y,z) coordinates
thecube.position.z = translate ( j * squaresize );
thecube.position.y = 0;
threeworld.scene.add( thecube );
MAZE[t] = thecube; // save it for later
t++;
}
}
function paintMaze( material ) // paint the maze
{
for ( var i = 0; i < MAZE.length; i++ )
{
if ( MAZE[i] ) MAZE[i].material = material;
}
}
function redrawMaze() // generate a new maze layout
{
var i, j, t;
for ( i = 0; i < gridsize ; i++ ) // clear out existing logical maze
{
for ( j = 0; j < gridsize ; j++ )
{
if ( GRID[i][j] == GRID_MAZE ) GRID[i][j] = GRID_BLANK;
}
}
initLogicalMaze(); // generate a new random logical maze
// update the positions of the existing graphical maze elements
t = 0;
for ( i = 0; i < gridsize; i++ )
for ( j = 0; j < gridsize; j++ )
if ( GRID[i][j] == GRID_MAZE )
{
MAZE[t].position.x = translate ( i * squaresize ); // translate (i,j) block-numbering to three.js (x,y,z) coordinates
MAZE[t].position.z = translate ( j * squaresize );
MAZE[t].position.y = 0;
threeworld.scene.add( MAZE[t] );
t++;
}
}
// --- goal -----------------------------------------------------------------------------------------------------------
function initLogicalGoal() // set up the goal
{
var l = Math.trunc( gridsize / 2 ) - 3; // Left goal post position
var j = 1 ; // Goal is just in front of back wall
var r = Math.trunc( gridsize / 2 ) + 3; // Right goal post position
GRID[l][j] = GOAL_POST;
GRID[r][j] = GOAL_POST;
for ( var i = l + 1; i < r; i++ )
{
GRID[i][j] = GOAL_LINE;
}
}
function initThreeGoal()
{
for ( var i = 0; i < gridsize; i++ )
for ( var j = 0; j < gridsize; j++ )
if ( GRID[i][j] == GOAL_POST )
{
var shape = new THREE.CylinderGeometry( postrad, postrad, postheight );
var thepost = new THREE.Mesh( shape );
thepost.material.color.setHex( {color: 0xffff00} );
thepost.position.x = translate ( i * squaresize );
thepost.position.z = translate ( j * squaresize );
thepost.position.y = 400;
threeworld.scene.add( thepost );
}
}
// --- BALL FUNCTIONS -------------------------------------------------------------------------------------------------
function initLogicalBall() // set up the ball
{
// start in fixed location:
var i, j;
i = Math.trunc( gridsize / 2 );
j = gridsize - 4;
bi = i;
bj = j;
}
function initThreeBall() // build the ball sphere
{
var shape = new THREE.SphereGeometry( ballrad, 32, 32 ); // sphere geometry
theball = new THREE.Mesh( shape );
theball.material.color.setHex( BLANKCOLOR );
drawBall();
}
function drawBall() // draw the ball given bi, bj
{
var x = translate ( bi * squaresize );
var z = translate ( bj * squaresize );
var y = 50;
theball.position.x = x;
theball.position.y = y;
theball.position.z = z;
// roll the ball
if ( sideway < 0 ) theball.rotation.x += squaresize / ballrad;
if ( sideway > 0 ) theball.rotation.x -= squaresize / ballrad;
if ( tofrom < 0 ) theball.rotation.z -= squaresize / ballrad;
if ( tofrom > 0 ) theball.rotation.z += squaresize / ballrad;
threeworld.scene.add( theball );
threeworld.lookat.copy( theball.position ); // if camera moving, look back at where the ball is
}
function moveLogicalBall(leftright,forwardback,position) // the ball movement in the grid and off the walls
{
var i, j;
i = bi + leftright;
j = bj + forwardback;
if ( !occupied( i, j ) ) // if no obstacle then move the ball
{
sideway = bi - i;
tofrom = bj - j;
bi = i;
bj = j;
}
else
{
if ( GRID[bi][bj] == GOAL_LINE ) // at wall between goal posts - increment score and reset ball, player and maze positions
{
goals++;
initLogicalPlayer();
initLogicalBall();
redrawMaze();
// reset all of the initial variables
rolling = false;
dist = 0;
pos = 0;
sideway = 0;
tofrom = 0;
return;
}
if ( GRID[i][j] == GRID_WALL ) // handle reflection off walls by reversing pos position
{
// ball hits wall straight on
if ( position == 2 ) pos = 6;
else if ( position == 4 ) pos = 8;
else if ( position == 6 ) pos = 2;
else if ( position == 8 ) pos = 4;
// ball hits wall diagonally
if ( i == 0 ) //left wall
{
if ( position == 1 ) pos = 3;
else if ( position == 7 ) pos = 5;
}
else if ( i == gridsize - 1 ) // right wall
{
if ( position == 3 ) pos = 1;
else if ( position == 5 ) pos = 7;
}
if ( j == 0 ) //back wall
{
if ( position == 5 ) pos = 3;
else if ( position == 7 ) pos = 1;
}
else if ( j == gridsize - 1 ) // front wall
{
if ( position == 3 ) pos = 5;
else if ( position == 1 ) pos = 7;
}
return;
}
}
}
function getPos () // calculate relative position of player if adjacent to ball
{
if (rolling) return; // don't recalculate position if ball is rolling
pos = 0;
if(( bi == pi - 1 ) && ( bj == pj + 1 )) pos = 1;
else if (( bi == pi ) && ( bj == pj + 1 )) pos = 2;
else if (( bi == pi + 1 ) && ( bj == pj + 1 )) pos = 3;
else if (( bi == pi + 1 ) && ( bj == pj )) pos = 4;
else if (( bi == pi + 1 ) && ( bj == pj - 1 )) pos = 5;
else if (( bi == pi ) && ( bj == pj - 1 )) pos = 6;
else if (( bi == pi - 1 ) && ( bj == pj - 1 )) pos = 7;
else if (( bi == pi - 1 ) && ( bj == pj )) pos = 8;
}
function kickBall (a) // move ball one square at a time based on position of player relative to ball when ball is kicked
{
if ( pos == 0 ) return; // don't move ball if player is not beside it
if ( !rolling ) // only calculate new distance to move ball if it is stationary
{
if ( a == ACTION_TAP )
{
dist = TAP;
rolling = true;
}
else if ( a == ACTION_KICK )
{
dist = KICK;
rolling = true;
}
}
if ( dist > 0 ) // move the ball one square at a time through dist squares and decrement dist each time
{
if ( pos == 1 ) moveLogicalBall( -1, 1, pos );
else if ( pos == 2 ) moveLogicalBall( 0, 1, pos );
else if ( pos == 3 ) moveLogicalBall( 1, 1, pos );
else if ( pos == 4 ) moveLogicalBall( 1, 0, pos );
else if ( pos == 5 ) moveLogicalBall( 1, -1, pos );
else if ( pos == 6 ) moveLogicalBall( 0, -1, pos );
else if ( pos == 7 ) moveLogicalBall( -1, -1, pos );
else if ( pos == 8 ) moveLogicalBall( -1, 0, pos );
dist--;
if (dist == 0) rolling = false; // set rolling to false when ball has moved the required distance
}
}
// --- PLAYER FUNCTIONS -----------------------------------------------------------------------------------------------
function initLogicalPlayer() // set up player in fixed location behind ball
{
var i, j;
i = Math.round( gridsize / 2 );
j = gridsize - 2;
pi = i;
pj = j;
}
function initThreePlayer ( object ) // build player
{
object.scale.multiplyScalar ( 50 );
theplayer = object;
rotatePlayerTowards( Math.PI );
drawPlayer();
}
function drawPlayer() // draw the player given pi, pj
{
var x = translate ( pi * squaresize );
var z = translate ( pj * squaresize );
var y = 0;
theplayer.position.x = x;
theplayer.position.y = y;
theplayer.position.z = z;
threeworld.scene.add( theplayer );
threeworld.follow.copy( theplayer.position ); // follow vector = player position (for camera following player)
}
function moveLogicalPlayer( a ) // this is called by the infrastructure that gets action a from the Mind
{ // change players position and rotate them
var i = pi;
var j = pj;
// keyboard controls change the player's position
if ( a == ACTION_LEFT ) i--;
else if ( a == ACTION_RIGHT ) i++;
else if ( a == ACTION_UP ) j--;
else if ( a == ACTION_DOWN ) j++;
if ( true )
{
if (( Math.abs( bi - i ) > 1 ) || ( Math.abs( bj - j ) > 1 )) // rotation while player isn't beside the ball
{
if ( a == ACTION_LEFT ) rotatePlayerTowards ( 3 * ( Math.PI / 2 ) );
else if ( a == ACTION_RIGHT ) rotatePlayerTowards ( 1 * ( Math.PI / 2 ) );
else if ( a == ACTION_UP ) rotatePlayerTowards ( 2 * ( Math.PI / 2 ) );
else if ( a == ACTION_DOWN ) rotatePlayerTowards ( 0 * ( Math.PI / 2 ) );
}
else // rotation of player while beside the ball
{
if (( bi == pi-1 ) && ( bj == pj + 1 )) rotatePlayerTowards ( 0 * ( Math.PI / 2 ) );
else if (( bi == pi ) && ( bj == pj + 1 )) rotatePlayerTowards ( 0 * ( Math.PI / 2 ) );
else if (( bi == pi + 1 ) && ( bj == pj + 1 )) rotatePlayerTowards ( 0 * ( Math.PI / 2 ) );
else if (( bi == pi + 1 ) && ( bj == pj )) rotatePlayerTowards ( 1 * ( Math.PI / 2 ) );
else if (( bi == pi + 1 ) && ( bj == pj - 1 )) rotatePlayerTowards ( 2 * ( Math.PI / 2 ) );
else if (( bi == pi ) && ( bj == pj - 1 )) rotatePlayerTowards ( 2 * ( Math.PI / 2 ) );
else if (( bi == pi - 1 ) && ( bj == pj - 1 )) rotatePlayerTowards ( 2 * ( Math.PI / 2 ) );
else if (( bi == pi - 1 ) && (bj == pj)) rotatePlayerTowards ( 3 * ( Math.PI / 2 ) );
}
}
if ( ! occupied(i,j) )
{
pi = i;
pj = j;
}
}
function rotatePlayerTowards ( newRotation ) // rotate the player
{
var x = ( newRotation );
theplayer.rotation.set ( 0, x, 0);
}
// --- KEYBOARD CONTROLS ----------------------------------------------------------------------------------------------
function keyHandler(e) // user control
{ // Note that this.takeAction(a) is constantly running at same time, redrawing the screen.
if ( e.keyCode == 37 ) moveLogicalPlayer ( ACTION_LEFT );
if ( e.keyCode == 38 ) moveLogicalPlayer ( ACTION_UP );
if ( e.keyCode == 39 ) moveLogicalPlayer ( ACTION_RIGHT );
if ( e.keyCode == 40 ) moveLogicalPlayer ( ACTION_DOWN );
if ( e.keyCode == 33 )
{
getPos();
kickBall ( ACTION_TAP ); // PgUp key to move ball TAP squares
}
if ( e.keyCode == 34 )
{
getPos();
kickBall ( ACTION_KICK ); // PgDn key to move ball KICK squares
}
}
// --- SCORE: ---------------------------------------------------------------------------------------------------------
function updateStatusBefore( a ) // this is called before anyone has moved on this step, user 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() + ") ";
$("#user_span3").html( status );
}
function updateStatusAfter() // player and ball have moved, can calculate score
{
// new state after both have moved
var instr = " Move Player with Arrow Keys ( Up, Dn, L & R); PgUp to Tap ball; PgDn to Kick it. Enjoy!<BR> "
var y = self.getState();
var status = " y = (" + y.toString() + ") ";
status = status + instr;
$("#user_span4").html( status );
var status = " Goals: " + goals;
$("#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;
step = 0;
goals = 0;
pos = 0;
sideway = 0;
tofrom = 0;
rolling = false;
// for all runs:
initGrid();
initLogicalWalls();
initLogicalMaze();
initLogicalGoal();
initLogicalPlayer();
initLogicalBall();
// for graphical runs only:
if ( true )
{
if ( show3d )
{
BOXHEIGHT = squaresize;
threeworld.init3d( startRadiusConst, maxRadiusConst, SKYCOLOR );
}
else
{
BOXHEIGHT = 1;
threeworld.init2d( startRadiusConst, maxRadiusConst, SKYCOLOR );
}
initSkybox();
initMusic();
// Set up objects first:
initThreeWalls();
initThreeMaze();
initThreeGoal();
// initThreePlayer();
initThreeBall();
addAudience();
initThreeGround();
// Then paint them with textures
loadTextures();
document.onkeydown = keyHandler;
}
};
this.getState = function()
{
var x = [ pi, pj, bi, bj ];
return ( x );
};
this.takeAction = function( a )
{
step++;
if ( true )
updateStatusBefore( a ); // show status line before moves
moveLogicalPlayer( a );
// move the ball if it has been kicked
getPos(); // get the position of the player relative to ball
kickBall( a ); // move the ball
if ( true )
{
drawPlayer();
drawBall();
// stop ball spinning
sideway = 0;
tofrom = 0;
updateStatusAfter(); // show status line after moves
}
};
this.endRun = function()
{
if ( true )
{
musicPause();
$("#user_span6").html( " <font color=red> <B> Run over. </B> </font> " );
}
};
}
// --- 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/starter/Defense.Line.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/starter/air.horn.mp3 autoplay > </audio>";
$("#user_span2").html( x );
}