Code viewer for World: Dungeon Cloner
/*
Ian Kelly - 13480138

This is the world I would like to me graded on.

This game involves placing the hero in a random location in a randomized maze.
The cloner is also randomly placed and after a certain amount of time will 
appear at another location, leaving a clone behind.
The goal of the game is for the hero to move within 1 square of the real cloner.

The hero moves using the arrow keys and can dash upwards using the spacebar.
The cloner moves randomly away from the hero.

Gravity was attempted but it conflicts with the movement inputs.


I/We declare that this material, which I/We now submit for assessment, is entirely my/our
own work and has not been taken from the work of others, save and to the extent that such
work has been cited and acknowledged within the text of my/our work. I/We understand
that plagiarism, collusion, and copying are grave and serious offences in the university and
accept the penalties that would be imposed should I engage in plagiarism, collusion or
copying. I/We have read and understood the Assignment Regulations. I/We have identified
and included the source of all facts, ideas, opinions, and viewpoints of others in the
assignment references. Direct quotations from books, journal articles, internet sources,
module text, or any other source whatsoever are acknowledged and the source cited are
identified in the assignment references. This assignment, or any part of it, has not been
previously submitted by me/us or any other person for assessment on this or any other
course of study.

*/

const	 	CLOCKTICK 	= 100;				
const		MAXSTEPS 	= 500;					

const gridsize = 20;						   

const NOBOXES =  Math.trunc ( (gridsize * gridsize) / 10 );

const squaresize = 100;					
const MAXPOS = gridsize * squaresize;	
	
const SKYCOLOR 	= 0x000000;				
const BLANKCOLOR 	= SKYCOLOR ;			



const startRadiusConst	 	= MAXPOS * 0.63;		
const skyboxConst			= MAXPOS * 3 ;		
const maxRadiusConst 		= MAXPOS * 3  ;		  

const ACTION_LEFT 		= 0;		   
const ACTION_RIGHT 		= 1;
const ACTION_UP 			= 2;		 
const ACTION_DOWN 		= 3;
const ACTION_STAYSTILL 		= 4;
const ACTION_JUMP       =5;


const GRID_BLANK 	= 0;
const GRID_WALL 	= 1;
const GRID_MAZE 	= 2;
 

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

function randomintAtoB ( A, B )			 
{
 return  ( Math.round ( randomfloatAtoB ( A, B ) ) );
}
  

function World() { 


var GRID 	= new Array(gridsize);			   
var WALLS 	= new Array ( 4 * gridsize );		
var MAZE 	= new Array ( NOBOXES );
var theHero, theenemy;
  
var ei, ej, ai, aj;
var  step;

var self = this;					


function initGrid()
{
 for (var i = 0; i < gridsize ; i++) 
 {
  GRID[i] = new Array(gridsize);		

  for (var j = 0; j < gridsize ; j++) 
  {
   GRID[i][j] = GRID_BLANK ;
  }
 }
}

function occupied ( i, j )		
{
 if ( ( ei == i ) && ( ej == j ) ) return true;		 
 if ( ( ai == i ) && ( aj == j ) ) return true;

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


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


function initSkybox() 
{

  var materialArray = [
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/kellyi7/dungeon2.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/kellyi7/dungeon2.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/kellyi7/dungeon2.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/kellyi7/dungeon2.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/kellyi7/dungeon2.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/kellyi7/dungeon2.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 loadTextures()
{

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

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

 var loader3 = new THREE.TextureLoader();
 loader3.load ( '/uploads/kellyi7/hero.png',	function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		theHero.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} ); 

 var loader4 = new THREE.TextureLoader();
 loader4.load ( '/uploads/kellyi7/enemy.png',	function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		theenemy.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} ); 

}


function initLogicalWalls()			
{
 for (var i = 0; i < gridsize ; i++) 
  for (var j = 0; j < gridsize ; j++) 
   if (( j==0 ) || ( j==gridsize-1 ) )
   {
    	GRID[i][j] = GRID_WALL ;		 
   }
}


function initThreeWalls()		
{
 var t = 0;
 for (var i = 0; i < gridsize ; i++) 
  for (var j = 0; j < gridsize ; j++)  
    if ( GRID[i][j] == true )
    {
 	  var shape    = new THREE.BoxGeometry( squaresize*1.5, squaresize*2, 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 ( 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-2);
    	GRID[i][j] = GRID_MAZE ;		 
 }
}


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, squaresize, 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;		// save it for later
	t++; 
   }
}


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


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;
 threeworld.scene.add(theenemy);

 threeworld.lookat.copy ( theenemy.position );		// if camera moving, look back at where the enemy is  
}


function initLogicalEnemy()
{
 var i, j;
 do
 {
  i = randomintAtoB(1,gridsize-2);
  j = randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square 

 ei = i;
 ej = j;
}


function initThreeEnemy()
{
 var shape    = new THREE.BoxGeometry( squaresize, squaresize, 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) )  	// if no obstacle then move, else just miss a turn
 {
  ei = i;
  ej = j;
 }
}


function drawHero()	// given ai, aj, draw it 
{
  var x = translate ( ai * squaresize );   	
  var z = translate ( aj * squaresize );   	
  var y =  0;	

 theHero.position.x = x;
 theHero.position.y = y;
 theHero.position.z = z;
 threeworld.scene.add(theHero);

 threeworld.follow.copy ( theHero.position );	
}


function initLogicalHero()
{
// 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 

 ai = i;
 aj = j;
}

function initThreeHero()
{
 var shape    = new THREE.BoxGeometry( squaresize, squaresize, squaresize );			 
 theHero = new THREE.Mesh( shape );
 theHero.material.color.setHex( BLANKCOLOR );	
 drawHero(); 		  
}


function moveLogicalHero( a )		
{ 
 var i = ai;
 var j = aj;
 var q = aj-1;

      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_UP ) 		j++;
 else if ( a == ACTION_DOWN ) 	j--;
 else if ( a == ACTION_JUMP )
 {
    if ( ! occupied(i,j-3) && ! occupied(i,j-2) && ! occupied(i,q))
    {
        j--;
        j--;
        j--;
    }
    else if ( ! occupied(i,j-1) && ! occupied(i,q))
    {
        j--;
        j--;
    }
    else if ( ! occupied(i,q))
    {
        j--;
    }

 }
/*
 if ( ! occupied(i,q)) // Gravity attempt.
 {
    j++;
 }
*/

  
 if ( ! occupied(i,j) ) 
 {
  ai = i;
  aj = j;
 }
}


function keyHandler(e)		

{
    if (e.keyCode == 37)  moveLogicalHero ( ACTION_LEFT 	);
    if (e.keyCode == 38)  moveLogicalHero ( ACTION_DOWN  	);
    if (e.keyCode == 39)  moveLogicalHero ( ACTION_RIGHT 	);
    if (e.keyCode == 40)  moveLogicalHero ( ACTION_UP	    );
    if (e.keyCode == 32)  moveLogicalHero ( ACTION_JUMP	);
}



function enemyCaught()	//Caught if the hero moves within 1 square of the enemy.
{
 return ( ((ai - ei) == 1 && (aj - ej) == 1) || ((ei - ai) == 1 && (ej - aj) == 1) || ((ai - ei) == -1 && (aj - ej) == -1) || ((ei - ai) == 1 && (ej - aj) == 1) || (ai == ei && (aj - ej) == 1) || ((ai - ei) == 1 && aj == ej) || (ei == ai && (aj - ej) == -1) || ((ai - ei) == -1 && ej == aj)  )		
} 


function updateStatusBefore(a)

{
 var x = self.getState();
 $("#user_span3").html( status );
}


function   updateStatusAfter()		
{
 var y = self.getState();

 var status = "Find The Real Cloner Before He Escapes! Be Quick Or He'll Move To A New Body. Use The Arrow Keys To Move And Space To Dash Upwards. "; 
 
 $("#user_span5").html( status );
}

this.endCondition;		 

this.newRun = function() 
{

  this.endCondition = false;

	step = 0;

 	initGrid();
	initLogicalWalls(); 
	initLogicalMaze();
	initLogicalHero();
	initLogicalEnemy();
	

  if ( true  )
  {
	 threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 		     


	initSkybox();
 	initMusic();

	initThreeWalls(); 
	initThreeMaze();
	initThreeHero();
	initThreeEnemy();

	loadTextures();	

	document.onkeydown = keyHandler;	 
  }

};

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


this.takeAction = function ( a )
{
  step++;

  if ( true  )
 	 

  moveLogicalHero(a);

  if ( ( step % 2 ) == 0 )	
    moveLogicalEnemy();
  if ( step == 2 || step == 4  || ( step % 15) == 0) 
  {
      initLogicalEnemy();
      initThreeEnemy();
      loadTextures();
  }

  if ( true  )
  {
   drawHero();
   drawEnemy();
   updateStatusAfter();		 
  }

  if ( enemyCaught() )			
  {
	this.endCondition = true;
  	if ( true  )
  	{
	 musicPause();
	 enemyDeath();
	}
  }

};



this.endRun = function()
{
 if ( true  )
 {
  musicPause(); 
  if ( this.endCondition )
    $("#user_span6").html( " &nbsp; <font color=red> <B> You Killed The Cloner! </B> </font>   "  );
  else
    $("#user_span6").html( " &nbsp; <font color=red> <B> The Cloner Has Escaped. </B> </font>   "  );
 }
};


}

function initMusic()
{
	var x = "<audio  id=theaudio  src=/uploads/kellyi7/dungeonmusic.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 enemyDeath()
{
 	var x = "<audio    src=/uploads/kellyi7/evilyelling-soundbible.com-1774362373.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}