Code viewer for World: Mid Air Pong

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

const  SCREENSHOT_STEP = 50;    


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

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

const NOBOXES =  Math.trunc ( (gridsize * gridsize) / 10 );
	// density of maze - number of internal boxes
	// (bug) use trunc or can get a non-integer 

const squaresize = 95;         // 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 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;

// 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)


// contents of a grid square
const GRID_BLANK  = 0;
const GRID_WALL   = 1;
const GRID_MAZE   = 2;
 
// scene object variables
var renderer, scene;

// field variables
var tableWidth = 400, tableHeight = 200;

// paddle variables
var paddleWidth, paddleHeight, paddleDepth, pad1YDirection = 0, pad2YDirection = 0, paddleSpeed = 3;

// ball variables
var ball, paddlePlayer, paddleOpponent, ballXDirection = 1, ballYDirection = 1, ballSpeed = 10;

// game-related variables
var scorePlayer= 0, scoreOpponent = 0, scoreMax = 5;

//---- 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;
  
// enemy and agent position on squares
var ei, ej, ai, aj;

var  step;

var self = this;            // needed for private fn to call public fn - see below  

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

// 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 ) && ( ej == j ) ) return true;   // variable objects 
 if ( ( ai == i ) && ( aj == 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() 
{
// x,y,z positive and negative faces have to be in certain order in the array 
 
// mountain skybox, credit:
// http://stemkoski.github.io/Three.js/Skybox.html

  var materialArray = [
  ( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/ishfaqt2/building3.jpg" ), side: THREE.BackSide } ) ),
  ( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/ishfaqt2/building3.jpg" ), side: THREE.BackSide } ) ),
  ( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/ishfaqt2/blue-1.jpg" ), side: THREE.BackSide } ) ),
  ( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/ishfaqt2/road1.jpg" ), side: THREE.BackSide } ) ),
  ( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/ishfaqt2/building2.jpg" ), side: THREE.BackSide } ) ),
  ( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/ishfaqt2/building2.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
}

function addPong() 
{
  var radius = 5, segments = 6, rings = 6;

  // Create a ball using a sphere 
  ball = new THREE.Mesh(new THREE.SphereGeometry(radius, segments, rings));
  threeworld.scene.add(ball); //add ball to the world
}

function addPaddles() 
{
	// set up the paddle vars
	paddleWidth = 10;
	paddleHeight = 50;
	paddleDepth = 10;
	
	// set up the player paddle 
	paddlePlayer = new THREE.Mesh(
	  new THREE.CubeGeometry(
	  paddleWidth,
	  paddleHeight,
	  paddleDepth));
	
	threeworld.scene.add(paddlePlayer); //add paddle to the world
	
	// Set up the opponent paddle
	paddleOpponent = new THREE.Mesh(
	  new THREE.CubeGeometry(
	  paddleWidth,
	  paddleHeight,
	  paddleDepth));
	
	threeworld.scene.add(paddleOpponent);
	
	//put the paddles on the edje of the 'table'
	paddlePlayer.position.x = -tableWidth/2 + paddleWidth;
	paddleOpponent.position.x = tableWidth/2 - paddleWidth;
}

// --- asynchronous load textures from file ----------------------------------------
// credits:
// http://commons.wikimedia.org/wiki/File:Old_door_handles.jpg?uselang=en-gb
// https://commons.wikimedia.org/wiki/Category:Pac-Man_icons
// https://commons.wikimedia.org/wiki/Category:Skull_and_crossbone_icons
// http://en.wikipedia.org/wiki/File:Inscription_displaying_apices_(from_the_shrine_of_the_Augustales_at_Herculaneum).jpg
  
// loader return can call private function
function loadTextures()
{

 var loader1 = new THREE.TextureLoader();
 loader1.load ( '/uploads/starter/door.jpg',   function ( thetexture ) {      
	thetexture.minFilter = THREE.LinearFilter;
	paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
  } ); 

 var loader2 = new THREE.TextureLoader();
 loader2.load ( '/uploads/starter/latin.jpg',    function ( thetexture ) {      
	thetexture.minFilter = THREE.LinearFilter;
	paintMaze ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
  } ); 
}


// --- add fixed objects ---------------------------------------- 

/*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 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 ( i/8 * squaresize );
	   thecube.position.y =  translate ( j * squaresize ); 
 
	   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 paintMaze ( material )    
{
	for ( var i = 0; i < MAZE.length; i++ )
	{ 
		if ( MAZE[i] )  MAZE[i].material = material;
	}
}

function moveUp()
{
	//check if the paddle is not at the edge of the board
	  if (paddlePlayer.position.y < tableHeight * 0.45)
	  {
			paddlePlayer.position.y += 10; //move the paddle up
	  }
}

function moveDown()
{
	//check if the paddle is not at the edge of the board
  if(paddlePlayer.position.y > -tableHeight * 0.45)
  {
		paddlePlayer.position.y -= 10; //move the paddle down
  }
}

function keyHandler(e)    
// user control 
// Note that this.takeAction(a) is constantly running at same time, redrawing the screen.
{    
	if (e.keyCode == 38) //if the up button is pressed
	{ 
	  moveUp();
	}
	
	if(e.keyCode == 40) //if the down button is pressed
	{ 
		moveDown();
	}
}




// --- score: -----------------------------------
function   updateStatusAfter()    // agent and enemy have moved, can calculate score
{
 var status = "   Player Score: " + scorePlayer + 
	" &nbsp; Opponent Score: " + scoreOpponent;
	
 $("#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;


  //Call all the methods to make the program run 
  initGrid();

  if ( true  )
  {
  if ( show3d )
  {
   BOXHEIGHT = squaresize;
   threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  );   
  }      
  else
  {
   BOXHEIGHT = 1;
   threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR  );          
  }

  initSkybox();
  initMusic();

  addPong();
  addPaddles();

  // 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.nextStep = function()		 
{
 var a = 4;

	//if the player or opponent wins end the game
	if(scoreOpponent == scoreMax || scorePlayer == scoreMax)
	{
	  this.endCondition = true;
	}
	
	//Update the score if the game is still runnning
	if ( true  )
	{
		updateStatusAfter();
	}

};

this.getScore = function()
{  
	// return the score of the winner
	if(scorePlayer == scoreMax)
	{
		return scorePlayer;
	}
	else
	{
		return scoreOpponent;
	}
};



this.endRun = function()
{
 if ( true  )
 {
	  musicPause(); 
	  if ( this.endCondition ) //if the game is finished
	  {
			if(scoreOpponent == scoreMax)
			{
			  $("#user_span6").html( " &nbsp; <font color=red> <B> Opponent Wins. Final score zero. </B> </font>   "  );
			}
			else if(scorePlayer == scoreMax)
			{
			  $("#user_span6").html( " &nbsp; <font color=red> <B> Player Wins. Final score zero. </B> </font>   "  );
			}
	  }
	  else if(step = MAXSTEPS) //if the max steps is reached
	  {
			$("#user_span6").html( " &nbsp; <font color=red> <B> Game Over. Maximum Time has reached</B> </font>   "  );
	  }
	  else
	  {
			$("#user_span6").html( " &nbsp; <font color=red> <B> Game 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 );
}