Code viewer for World: minecraft chase (clone by ...

// Cloned by Cbum on 5 Dec 2022 from World "minecraft chase (clone by David L)" by David L 
// Please leave this clone trail here.
 
// Cloned by Liam on 30 Nov 2022 from World "User-controlled Model World" by Starter user 
// Please leave this clone trail here.
// ==== Starter World =================================================================================================
// This code is designed for use on the Ancient Brain site.
// This code may be freely copied and edited by anyone on the Ancient Brain site.
// To include a working run of this program on another site, see the "Embed code" links provided on Ancient Brain.
// ====================================================================================================================
// User controlled (not Mind controlled) 3D model World with various features:
// - User controlled UP/LEFT/RIGHT arrows move model 
// - "First Person View" - Camera rotates with direction you are facing
//   Best effect is with "Move with" camera
// - Can have one or multiple ZOMBIEs chasing you 
// Smooth movement
// UP moves forward in whatever angle you are at
// LEFT/RIGHT rotate by small angle
// Uses x,y,z rather than grid of squares 
// Has collision detection for ZOMBIE moves
// Things to do: 
// - Initialise ZOMBIEs so they are not already colliding 
// - Collision detection for agent moves
// ===================================================================================================================
// === Start of tweaker's box ======================================================================================== 
// ===================================================================================================================
// The easiest things to modify are in this box.
// You should be able to change things in this box without being a JavaScript programmer.
// Go ahead and change some of these. What's the worst that could happen?

var hitCount = 0;
AB.clockTick       = 100;     	// Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps        = 10000;    	// Length of run: Maximum length of run in steps. Default 1000.
AB.screenshotStep  = 100;   	// Take screenshot on this step. (All resources should have finished loading.) Default 50.
AB.drawRunControls = true;	// Scrap the Run/Step/Pause controls

//---- global constants: -------------------------------------------------------
var zombieAmount = 3; // number of ZOMBIEs: 
const OBJ_ZOMBIE 	= '/uploads/goobert/zombie.obj' ;
const OBJPATH = "/uploads/starter/";
const OBJNAME = "Peter_Parker.obj";
const MTLNAME = "Peter_Parker.mtl";
const floorTextureFile = "/uploads/goobert/grass2.png"; // floor texture
const steveScale = 70; // multiply model sizes by some amount:
const TEXTURE_ZOMBIE = '/uploads/goobert/zombie.png';
var SCALE_ZOMBIE 	= AB.randomFloatAtoB ( 0, 0 );
const MUSIC_BACK  	= '/uploads/liam/mc.mp3';
const MAXPOS 		= 3500;			// length of one side of the arena 
const SKYCOLOR 		= 0xffffcc;				// a number, not a string 
const LIGHTCOLOR 	= 0xffffff ;
const startRadiusConst	 	= MAXPOS ;			// distance from centre to start the camera at
const maxRadiusConst 		= MAXPOS * 10 ;		// maximum distance from camera we will render things  
var steveSpeed = 0; // how much agent moves each step 
var zombieSpeed = 0; // how much enemy moves each step 
const CLOSE_LIMIT = 25; // this is as close as models come to other models
const mixers = [];
const clock = new THREE.Clock();
var WorldSize = 10000;
const time_elapsed = 0;
//--- lookat and follow -----------------------------------------------------------------

	// camera on "Move With" should move up/down in y axis as we re-scale objects
	// to place the follow camera just above the agent, something like: 
	
	
	// to point the camera at ZOMBIE's face, sssomething like: 
	const FOLLOW_Y = steveScale * 4;	// going high up (or forward/back) means we get it out of agent's hair (makes it first person)
	const LOOKAT_Y = SCALE_ZOMBIE * 40;
	const CAMERASHIFT 	= - steveScale * 40 ;		// shift camera behind agent, looking past agent along line of sight  

	
	//		const CAMERASHIFT 	=   0 ;						// put camera exactly inside agent's head
	//		const CAMERASHIFT 	=   steveScale * 2 ;		// shift camera ahead of agent along line of sight 
				 	 
function mainMenu(){
    AB.newSplash();
    let splash = document.getElementById("splash-inner");
    // var h1 = document.createElement("p");
    // h1.innerHTML = "How To Play!";
    // splash.appendChild(h1);
    let play = document.getElementById("splashbutton");
    document.getElementById("splash").style.backgroundImage = "url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMQAAAAPCAAAAACe4j/AAAAEuElEQVRIx1WWS5Ibuw5EtddrdRRJJE4iSKrb4c2/AUuy30iK+jEBHCTwAICMCgAKiGhKVQUwKzJHQo4mQGBy7AIvk1E2AQKhFt6bUGD1gILy0DkD4dmzACkGyzCScgFgQmLZr4LpQpC56QB2wgSYE8i1fd6xd1yPHIAKwAs14Undx2LIHK0X6vdFbzTa6CNBLQE8Y5rapn7oAl3KSDKC6cLfL6sLnIVG7ffnyUgvQ6rwLjUZdIVqWz0iEXXCZ+TJsV8feUdoPHDk+a/IeAbq0ldLT59jtHweHwmKS6gFSUYCqKcVsPami1pEnqolkCMBiAS87FXTICp03geYUr8TV8un3KuyC0BfTUzihOCdwjUya1bkT9VWe+AaFOoJzHdtERXRe/eulyEDhC6pjYwr+JQuIDU9/0wQBnJEAkd+9i6IRTJrV7YWnGfwLA6ESphkBoCu88M0YH1CTTOiRUpDdwpGkkP5yL9phhwn+QVxDarAXq513y9bLKgxBGR/CjDyMl4ECd6lO82CaMLbEHGHp1R8ePDERj392ssaCOigNjJ+dXXl6OLUFfVx6dA1BFJLlHpkUHPam095C3QJLIjn6OLIOB3xe0JS85Zd+govg1/z/UhtG3RdVW98yxNp5I0Vtf/U+6y/dJ/yHtSK4q7G9ARscC3/Df/kXTwiT3d6ovjEISLJIVEvBwWeCXlALZTbpy85xqWuf3sNiBBUmYjI+BqN6aNQqDWpipSYf3W1nOV/WALoykjIiCPreSn97UigsjZdPA5s3AmCDIxaVO0Fa1fV/qkaiijPPz/bEeRyLSBDI8K8j37TO8aH7vmOqUUQ1DboYhvqd+FlFGEo+2T75MbLGnjvPeu7Pl521Ap14dcR3h7p75qv4xieqnNDoyCPLt/yKkf5gOUXl+6S3d9Syq/982fz+fhpsm28C5EhJcxsWWQke6KoPMA7tqm7kKcrn6FLhWcoqCNCxDPyQ8wEKR73nIlEX8GpARSJX7/rzeNtWPD/Dl2IwtOYMwV7osGsHYJanzeU7KqIZVCTcrT0IEbyD4d1e1dkBtW2qLhLXK7a1ogDqsjI1DR4PY7nHEqfF9tn8HUJL+eQ/lGOXdwI1++57CqgmDaReM9CZYLAk4D4ymqti4KZI9Htz9OnrcuOhCQEqJfXaxHPAFG/l/ee1GFJPXmPryYUiUGPutnRGXkagmnjHk5RNpm7xxiclm6CjJEQ4+MD4xPmuT9Ef4b3YT3B51a7d4Np76PF0cavuC+SoaH6PlilipEakoJBJETWT3k7Hf+1REPj2fIx95t5ct6u9S5v3KM2fv2Ve29ax9yC98zT2IZot61MHyYtdPF92nguZ29xfcVxSu9Nj5Gl8G3wcVoW8n1Q/f6BHFEAyy5R2aRIdYH8XfV4453vHKcnqarXPD6mdrD0rlVV5xBPF2xuJM5Oo0SdaaqGMjJva7ii6bYv74IzPMo5pGebpu72ghypwTR+WWDuhQOkFijGyAzF3GXh11lEH0hkPINZivpTqLEtVm2j1hVcn/S0//q0es5dbyxOErxKivQ+bHotvzcT3/XztPf20XqG6fJZMePT2X6ts+nlm9LoSYHMiHuJjGXniB7v2fo/5qT4nuxYg6AAAAAASUVORK5CYII=)";
    document.getElementById("splashbutton").style.backgroundSize = "60px 120px";

    play.addEventListener("click", beginGame);
	splash.appendChild(play); // adds the button start button to the splash page.\
	
  if (AB.socket) {
    if (AB.socket.connected) {
        console.log("SOCKET CONNECTED");
        // AB.socketOut("TESTING SOCKETS!!!");
        }
    }
    
    document.getElementById("splash").style.backgroundImage = "url('https://picfiles.alphacoders.com/478/478314.png')"; // adds background image with entire splash death screen
    document.getElementById("splash").style.backgroundSize = "cover"; // sets the background image to cover the entire splash.
    var p = document.createElement("p");
    var p1 = document.createElement("p");
    var h2 = document.createElement("p");
    
}

function splashScreen(text) {
    // let text = "Welcome to Tic Tac Toe. The aim of the game is to fill a row, diagonal or column with your symbol, X or O. First to do so is the winner.";        
    return (text); // display welcome message on splash screen 
}

function death(){ // this function is called when the player has no more lives.
 	steve.position.y = 0;
	steve.position.x = 0;
	steve.position.z = 0;
    steveSpeed = 0;
    AB.newSplash();
    let splash = document.getElementById("splash-inner");
    let span = document.getElementById("splashbutton");
    span.innerHTML = "Restart";
    document.getElementById("splash").style.backgroundImage = "url('https://picfiles.alphacoders.com/478/478314.png')"; // adds background image with entire splash death screen
    document.getElementById("splash").style.backgroundSize = "cover"; // sets the background image to cover the entire splash.
    span.addEventListener("click", beginGame); // when the restart button is clicked the beginGame function is executed.
    var winmessage = document.createElement("p");
    winmessage.innerHTML = "<br><b>You Died! <b>";
    splash.append(winmessage);
    winmessage.style.fontSize = "25px";
    zombieSpeed = 0; // freezes the zombies in place until updated when game is restarted.
    steve.rotation.z = Math.PI / 1; // faces steve model in the default direction (forward).
    for ( var i = 0; i < zombieAmount ; i++ ) // loops for all zombies and changes their scale.
    {
      zombieArray[i].scale.set(0, 0, 0); // sets the zombies to 0 so invisible.
    }
}

ABHandler.MAXCAMERAPOS 	= maxRadiusConst ;
ABHandler.GROUNDZERO		= true;						// "ground" exists at altitude zero

 const SKYBOX_ARRAY = [										 
                "/uploads/goobert/pz.jpg", // left
                "/uploads/goobert/nz.jpg", // right
                "/uploads/goobert/py.jpg", // top
                "/uploads/goobert/ny.jpg", // bottom
                "/uploads/goobert/nx.jpg", // back
                "/uploads/goobert/px.jpg", // front
                ];

var zombieArray = new Array ( zombieAmount );
var steve, zombie;		  
var steveRotation = 0 ; 
var enemyRotation = 0 ;    
var ZOMBIE_texture; 

function loadResources() // asynchronous file loads - call initScene() when all finished 
{
	var loader = new THREE.OBJLoader ( new THREE.LoadingManager() ); // load ZOMBIE - OBJ that we will paint with a texture 
	loader.load ( OBJ_ZOMBIE, function ( object )
	{
		zombie = object; // canonical enemy object - may be copied multiple times 
		if ( asynchFinished() )	initScene();		 
	});

THREE.DefaultLoadingManager.addHandler ( /\.tga$/i, new THREE.TGALoader() );
 // load textures 
	var loader2 = new THREE.TextureLoader();
	loader2.load ( TEXTURE_ZOMBIE, function ( thetexture )  
	{
		thetexture.minFilter  = THREE.LinearFilter;
		ZOMBIE_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});
}

var difficulty = "<p align=\"center\"><button id='easyButton', style=\"height:50px;width:100px;\"><b>Easy</b></button><button id='normalButton', style=\"height:50px;width:100px\"><b>Normal</b></button><button id='hardButton', style=\"height:50px;width:100px\"><b>Hard</b></button></p>";  
function setDifficulty()
{
    $("#user_span5").html( difficulty );
    easyButton.style.background = '#4CAF63';
    normalButton.style.background = '#FA9E2A';
    hardButton.style.background = '#C43737';
    document.getElementById("easyButton").style.font = "20px MinecraftiaRegular";
    document.getElementById("easyButton").addEventListener("click", easyMode); // checks if the button with the ID of easyButton is pressed, if so the easyMode is set (function is called)
    document.getElementById("normalButton").addEventListener("click", normalMode); // checks if the button with the ID of normalButton is pressed, if so the normalMode is set (function is called)
    document.getElementById("hardButton").addEventListener("click", hardMode); // checks if the button with the ID of hardButton is pressed, if so the hardMode is set (function is called)
}

function loadModels() 
{
  const loader = new THREE.GLTFLoader();
  const onLoad = ( gltf, position ) => 
  {
    var model = gltf.scene.children[ 0 ]; // the model is the first child of the scene

    model.position.copy( position ); // set the position of the model
    
    
    var animation = gltf.animations[ 1 ];
    var animationIdle = gltf.animations[ 0 ]; // get the animation from the gltf object
    
    
    const mixer = new THREE.AnimationMixer( model );
    mixers.push( mixer );

    const action = mixer.clipAction( animation );
    action.play();

    ABWorld.scene.add( model );
    steve = model;
    model.scale.set(5, 5, 5);
    initScene();
    lighting();
  };

  var stevePosition = new THREE.Vector3 ( 0, 0, 0 );
  const mutantPosition = new THREE.Vector3 ( 10, 0, -60 );

  // can load 3D models of other user:
  loader.load ( '/uploads/liam/steve.glb',   glb => onLoad ( glb, stevePosition )   );
  // loader.load ( '/uploads/liam/mutant.glb',   glb => onLoad ( glb, mutantPosition )   );

}

function asynchFinished()		 // all file loads returned 
{
	if ( ZOMBIE_texture && zombie && steve )   return true;  
	else return false;
}	
	
function initScene() // all file loads have returned 
{
 // add agent model start at center
// 	ABWorld.scene.add( steve ); 
	setLookatFollow();	  // set up lookat and follow in direction agent is pointing in 
 // add enemies
 // first paint and size the canonical enemy object 
 
	zombie.traverse ( function ( child ) 
	{
		if ( child instanceof THREE.Mesh ) 
			child.material.map = ZOMBIE_texture ;
	});
		
	zombie.scale.multiplyScalar ( SCALE_ZOMBIE );    	// scale it   
 // add perhaps multiple enemy models, starting near outside of arena
 
 for ( var i = 0; i < zombieAmount ; i++ ) 
 {
    var object = zombie.clone();         // copy the object multiple times 

	object.position.y = 0;
	object.position.x = AB.randomPick (		AB.randomIntAtoB ( -MAXPOS/2, -MAXPOS/3 ), 		AB.randomIntAtoB ( MAXPOS/3, MAXPOS/2 ));
	object.position.z = AB.randomPick (		AB.randomIntAtoB ( -MAXPOS/2, -MAXPOS/3 ), 		AB.randomIntAtoB ( MAXPOS/3, MAXPOS/2 ));
 
	ABWorld.scene.add( object ); // adds the ZOMBIE enemies
	zombieArray[i] = object; 	// save in array for later
 }
}

function lighting() // this function is called on the first new run of the game.
{
	steve.scale.multiplyScalar ( steveScale ); // sets steve's scale
    ABWorld.scene.background = new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY,	function() // skybox
    { 
    ABWorld.render();  // skybox
    AB.removeLoading(); // removes loading splash
    AB.runReady = true; 
    });
}

// --- lookat and follow -----------------------------------
// we do NOT automatically look at enemy / enemies 
// instead we look in direction we are facing
 
 
function setLookatFollow()		// set up lookat and follow 
{	
 // follow - camera position 
 
	 // start with agent centre, then adjust it by small amount
	 console.log(steve.position)
    
	 ABWorld.follow.copy ( steve.position );		 
	 
	 ABWorld.follow.y = FOLLOW_Y ;

	 // shifted forward/back along line linking agent with direction it is facing 
	 ABWorld.follow.x = ABWorld.follow.x + ( CAMERASHIFT * Math.sin(steveRotation) );
	 ABWorld.follow.z = ABWorld.follow.z + ( CAMERASHIFT * Math.cos(steveRotation) );
	 

 // lookat - look at point in distance along line we are facing
	 
	 // start with agent centre, then adjust it along line by huge amount 
	 ABWorld.lookat.copy ( steve.position );

	 ABWorld.lookat.y = LOOKAT_Y ;     	
	 
	 ABWorld.lookat.x = ABWorld.lookat.x + ( (startRadiusConst * 3) * Math.sin(steveRotation) );
	 ABWorld.lookat.z = ABWorld.lookat.z + ( (startRadiusConst * 3) * Math.cos(steveRotation) );
}

// --- enemy move -----------------------------------
 
// angle between two points (x1,y1) and (x2,y2)

function angleTwoPoints ( x1, y1, x2, y2 ) 
{
      return  Math.atan2 ( x2 - x1, y2 - y1  );
}

function dist ( a, b )		    // distance between them when a,b are Three vectors  
{
    // console.log(a.distanceTo ( b ))
 return ( a.distanceTo ( b ) );
}

var liveCount = 0;
var lives = 5;
var lives2 = 0;

function collision ( proposedMove, k )  		 // proposed move FOR ZOMBIE k
{
	if ( dist ( proposedMove, steve.position ) < CLOSE_LIMIT )	
	hitCount += 1; // updates every tick the enemey is inside the range of the close limit
// 	console.log(hitCount);
	var scoreDisplay = "<p align=\"center\">Health: " + lives + "</p>";

	$("#user_span2").html( scoreDisplay );
	if ( hitCount == 4 ) // if the player has been hit 4 times, the game is over (around being hit for 0.5 second)
	{
		lives -= 1;
		hitCount = 0; // hit count is resit back to zero
    	var deathSound = new Audio('/uploads/liam/damage.mp3'); // play damage sound everytime hitcounter is 4.
    	deathSound.play();
	}
	
	if ( lives === 0 ) // if the player has no lives left, the death menu is displayed.
	{
	    death(); // calling the death function
        lives = 1000;
	}

  for ( var i=0 ; i < zombieAmount ; i++ )
  if ( i != k )
    if ( dist ( proposedMove, zombieArray[i].position ) < CLOSE_LIMIT )			 
 	 return true;

 // else 
 return false;
}

function moveEnemy()
{ 
  for ( var i = 0; i < zombieAmount ; i++ ) 
  {
		var e = zombieArray[i];

		enemyRotation =  angleTwoPoints ( e.position.x, e.position.z, steve.position.x, steve.position.z  ); // rotate enemy to face agent
			
		e.rotation.set ( 0, enemyRotation + 85, 0 );

		// move along that line, if no collision with any other model  
		  
		var  proposedMove = new THREE.Vector3 
		( 		 
			e.position.x +  ( zombieSpeed *  Math.sin(enemyRotation) ),
			e.position.y,
			e.position.z + ( zombieSpeed *  Math.cos(enemyRotation) )
		);				

		if ( ! collision ( proposedMove, i ) )
			e.position.copy ( proposedMove );
		// else enemy i just misses a turn
  }
}


//--- key control of agent -------------------------------------------------------------

const KEY_UP = 38;
const KEY_LEFT = 37;
const KEY_RIGHT = 39;
const KEY_DOWN = 40;
const SPACEBAR = 32;
const KEY_W = 87;
const KEY_A = 65;
const KEY_S = 83;
const KEY_D = 68;
var pressedcount = 0; // counts how long the key is pressed for.

var OURKEYS = [ KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_W, KEY_A, KEY_S, KEY_D ];

function ourKeys ( event ) { return ( OURKEYS.includes ( event.keyCode ) ); }

function keyHandler ( event )		
{
	if ( ! AB.runReady ) return true; 		// not ready yet 

	// if not handling this key, send it to default: 
	
	if ( ! ourKeys ( event ) ) return true;
	
	// else handle it and prevent default:

	 if ( event.keyCode == KEY_UP || event.keyCode == KEY_W) // if the Up arrow or W key is pressed.
	 {

        // pressedcount = pressedcount + 1; // updates the pressed count each time the W key is pressed
        // console.log(pressedcount) // logs the pressing to console.
        if ( pressedcount > 8 ) // only play the sound every 8 key presses to avoid the sound effect overlapping.
        {
        pressedcount = 0; // resets the pressedcount
		var forwardSound = new Audio('/uploads/liam/walk_forward.mp3'); // play the walking forward sound effect
		forwardSound.setLoop = ( false );
		forwardSound.setVolume = ( 0.1 );
		forwardSound.play(); // audio is played
        }
	    if ( steve.position.z > -(WorldSize / 2)){
	       // console.log("up", (WorldSize / 2) );
    	    steve.rotation.z = 0; // face the agent backwards
    	    steveRotation = steve.rotation.z;
    	    steve.position.z = steve.position.z - ( steveSpeed *  Math.cos(steveRotation) ); // moves the agent in the direction its facing (backwards)
	    }
	 }

	 if ( event.keyCode == KEY_DOWN || event.keyCode == KEY_S) // if the down arrow or S key is pressed.
	 {

        // pressedcount = pressedcount + 1; // updates the pressed count each time the W key is pressed
        // console.log(pressedcount) // logs the pressing to console.
        if ( pressedcount > 8 ) // only play the sound every 8 key presses to avoid the sound effect overlapping.
        {
        pressedcount = 0; // resets the pressedcount
		var backwardsSound = new Audio('/uploads/liam/walk_backward.mp3'); // play the walking forward sound effect
		backwardsSound.setLoop = ( false );
		backwardsSound.setVolume = ( 0.1 );
		backwardsSound.play(); // audio is played
        }
	    if ( steve.position.z < (WorldSize / 2)){
    	   // console.log("down", steve.position.z);
            steve.rotation.z = Math.PI / 1; // face the agent forward
    		steveRotation = steve.rotation.z;
    		steve.position.z = steve.position.z - ( steveSpeed *  Math.cos(steveRotation) ); // moves the agent in the direction its facing (forward)
	    }
	 }

	 if ( event.keyCode == KEY_LEFT || event.keyCode == KEY_A) // if the left arrow or A key is pressed.
	 {

        // pressedcount = pressedcount + 1; // updates the pressed count each time the W key is pressed
        // console.log(pressedcount) // logs the pressing to console.
        if ( pressedcount > 8 ) // only play the sound every 8 key presses to avoid the sound effect overlapping.
        {
        pressedcount = 0; // resets the pressedcount
		var leftSound = new Audio('/uploads/liam/walk_left.mp3'); // play the walking forward sound effect
		leftSound.setLoop = ( false );
		leftSound.setVolume = ( 0.1 );
		leftSound.play(); // audio is played
        }
        if (steve.position.x > -(WorldSize / 2) ){
    	   // console.log("left", steve.position.x);
            steve.rotation.z = Math.PI / 2; // face the agent left
    		steveRotation = steve.rotation.z;
    		steve.position.x = steve.position.x - ( steveSpeed *  Math.sin(steveRotation) ); // moves the agent in the direction its facing (left)
        }
	 }

	 if ( event.keyCode == KEY_RIGHT || event.keyCode == KEY_D) // if the right arrow or D key is pressed.
	 {
        // pressedcount = pressedcount + 1; // updates the pressed count each time the W key is pressed
        // console.log(pressedcount) // logs the pressing to console.
        if ( pressedcount > 8 ) // only play the sound every 8 key presses to avoid the sound effect overlapping.
        {
        pressedcount = 0; // resets the pressedcount
		var rightSound = new Audio('/uploads/liam/walk_right.mp3'); // play the walking forward sound effect
		rightSound.setLoop = ( false );
		rightSound.setVolume = ( 0.1 );
		rightSound.play(); // audio is played
        }
        if (steve.position.x < (WorldSize / 2) ){
            // console.log("right", steve.position.x);
            steve.rotation.z = Math.PI / -2; // face the agent right
    		steveRotation = steve.rotation.z;
    		steve.position.x = steve.position.x - ( steveSpeed *  Math.sin(steveRotation) ); // moves the agent in the direction its facing (left)   
        }
	 }

// 	 if ( event.keyCode == SPACEBAR ) 
// 	 {
//         steve.rotation.z = Math.PI / -2; // face the agent right
// 		steveRotation = steve.rotation.z;
// 		steve.position.y = steve.position.y - ( steveSpeed *  Math.sin(steveRotation) ); // moves the agent in the direction its facing (left)
// 		steve.position.y = steve.position.y + ( steveSpeed *  Math.sin(steveRotation) ); // moves the agent in the direction its facing (left)
// 	 }

	setLookatFollow(); 	// lookat/follow depend on change in agent position/rotation: M.H

	event.stopPropagation(); event.preventDefault(); return false;
}

function easyMode() // this function is called when Easy difficulty is selected.
{
    steveSpeed = 100;
    zombieSpeed = 50; // makes the enemy speed 50% slower.
    for ( var i = 0; i < zombieAmount ; i++ ) // loops for all zombies and increases their size to 700x700x700
    {
        zombieArray[i].scale.set(300, 300, 300);
    }
}

function normalMode() // this function is called when Normal difficulty is selected.
{
    steveSpeed = 100;
    zombieSpeed = 80; // increased zombie movement speed.
    for ( var i = 0; i < zombieAmount ; i++ ) // loops for all zombies and increases their size to 700x700x700
    {
      zombieArray[i].scale.set(700, 700, 700);
    }
}

function hardMode() // this function is called when Easy difficulty is selected.
{
    steveSpeed = 100;
    zombieSpeed = 200 ; // makes the enemy speed much faster
    for ( var i = 0; i < zombieAmount ; i++ )
    {
      zombieArray[i].scale.set(1000, 1000, 1000); // zombie is larger then normal mode.
    }
}

const gameClock = new THREE.Clock();

AB.world.nextStep = function()
{
    var time_elapsed = Math.trunc(gameClock.getElapsedTime());
    var timelapsed = "<p align=\"center\">Time Elapsed: " + time_elapsed + "s</p>";
    $("#user_span4").html( timelapsed );
	moveEnemy();   	// enemies moves on their own clock
	const delta = clock.getDelta();
    mixers.forEach( ( mixer ) => { mixer.update( delta ); } );
    if(AB.socket)
    {
        if(AB.socket.connected)
            {
                AB.socketOut(lives);
                AB.msg("P1 Score = " + lives + " P2 score = " + lives2 + "\n Time remaining: ");
            }
    }
};

AB.backgroundMusic ( MUSIC_BACK );

function beginGame(){ // removes the splash menu when button is pressed
    steve.position.y = 0;
	steve.position.x = 0;
	steve.position.z = 0;
	initScene();
    AB.removeSplash();
    lives = 50;
}
    
AB.world.newRun = function() 
{
    AB.socketStart();
	mainMenu();
	AB.loadingScreen();

	AB.runReady = false;  

	ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 

	loadResources();		// aynch file loads								// calls initScene() when it returns 

	loadModels();
	setDifficulty();

	// light
    var ambient = new THREE.AmbientLight();
   	ABWorld.scene.add( ambient );

	var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
	thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
	ABWorld.scene.add(thelight);

    var BlockSize = 500;

    var floorGeometry = new THREE.PlaneBufferGeometry( WorldSize, WorldSize );
    floorGeometry.rotateX( - Math.PI / 2 );

    // loading the floor texture
    var floorTexture = new THREE.ImageUtils.loadTexture ( floorTextureFile );
    floorTexture.minFilter = THREE.LinearFilter;
    floorTexture.wrapS = floorTexture.wrapT = THREE.RepeatWrapping;

    // world floor texture size and location
    floorTexture.offset.set( 0, 0 );
    floorTexture.repeat.set( WorldSize / BlockSize, WorldSize / BlockSize );
    var floor = new THREE.Mesh(floorGeometry, new THREE.MeshBasicMaterial({map : floorTexture}));
    floor.position.set(0,0,0);
    threeworld.scene.add(floor);

	document.onkeydown = keyHandler;
};

AB.socketUserlist = function ( array ) {
    console.log(array.length);    
    if (array.length > 2) {
        // there are more than 2 players trying to enter the game, which is 2-player only
        // display an error screen
        AB.newSplash (splashScreen("<h3>The game is full. Please wait a moment, and then try again."));
        // update the button from "start" to "retry"
        document.getElementById("splashbutton").innerHTML = "Retry";
        // reload the page when the button is clicked
        document.getElementById("splashbutton").onclick = function () {
            location.reload();
        };
    } else if (array.length === 1) {
        // there is only one player, so don't start the game until a second
        // player joins and the first player hits 'retry'
        AB.newSplash (splashScreen("<h3>Waiting for a matched player. Please wait a moment and try again.</h3>"));
        // update the button from "start" to "retry"
        document.getElementById("splashbutton").innerHTML = "Retry";
        // reload the page when the button is clicked
        document.getElementById("splashbutton").onclick = function () {
            location.reload();
        };
    }
    // ensure that when this function is called again, the user array is not passed
    // this will avoid the error message displaying for all users currently playing and ensure it only displays for
    // the new user trying to join
    AB.socketUserlist = function() {};

}; 

AB.socketIn = function (score)
{
    lives2 = score;
};