Code viewer for World: 3D Platform Game
ABWorld;
AB.clockTick       = 100;    
AB.maxSteps        = 10000;    
AB.screenshotStep  = 50;   
AB.drawRunControls = true;
// default AB paramters
    const show3d = true;						
    const OBJPATH = "/uploads/linr2/";
	const PLAYER1OBJ = "Quigon.obj";
	const PLAYER1MTL = "Quigon.mtl";
	const WALLOBJ = "farm_type_wall.obj";
	const WALLMTL = "farm_type_wall.mtl";
	const WALL_TEXTURE = '/uploads/linr2/farm_type_wall_wall_Height.png' ;          // constants for uploads
    var cam;
    var meshFloor;
    var keyboard = {};
    var updatedPosX;
    var updatedPosZ;
    var play;
    var player = { height:2, speed:0.3, turnSpeed:0.09};
    var playerspeed=0;
    var scene = new THREE.Scene();
    var sky_texture = new THREE.TextureLoader().load( 'uploads/linr2/milky.jpg' );
    scene.background = sky_texture;
    var startTime = performance.now();
    var renderer = new THREE.WebGLRenderer();           //inialise WEBGL
    
    startTime=performance.now();                    // begins timing the user
    var wall_texture; 
    
function render()
{
    renderer.setSize(window.innerWidth, window.innerHeight);            // sets how big to render the game in browser window 
    document.body.appendChild(renderer.domElement);
}
    
function camera()
{
    cam = new THREE.PerspectiveCamera(45, window.innerWidth/window.innerHeight, 0.1, 1000);
    cam.position.set(0, 3.5, -90); // sets camera positioning (x,y,z)
    cam.lookAt(scene.position);
}

function create_player()
{
    THREE.DefaultLoadingManager.addHandler ( /\.tga$/i, new THREE.TGALoader() );
    var m = new THREE.MTLLoader();
    m.setResourcePath ( OBJPATH );
    m.setPath         ( OBJPATH );
	m.load ( PLAYER1MTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();                      // imports the player uploaded to AB, 
		o.setMaterials ( materials );                       // credit to tomi210 here: https://free3d.com/3d-model/lego-qui-gon-18960.html
		o.setPath ( OBJPATH );
		o.load ( PLAYER1OBJ, function ( object ) 
		{
			play = object;
			play.scale.multiplyScalar(0.05);                // sets the scale and location of where to place player model
            play.position.set(0,player.height,-160);
            scene.add(play);
		}
		);
	}
	);
}

function loadtextures()
{
    var loader2 = new THREE.TextureLoader();
	loader2.load ( WALL_TEXTURE, function ( thetexture )  
	{                                                                       // loads textures of the wall
		thetexture.minFilter  = THREE.LinearFilter;
		wall_texture = thetexture;
	}
	);
}
    
function generateWall()
{
    THREE.DefaultLoadingManager.addHandler ( /\.tga$/i, new THREE.TGALoader() );
	var m = new THREE.MTLLoader();
	m.setResourcePath ( OBJPATH );
	m.setPath         ( OBJPATH );
	m.load ( WALLMTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );                                           // generates the wall obstacles using a 3D model
		o.setPath ( OBJPATH );                                                  // Credit to g3ordie, link here : https://free3d.com/3d-model/farm-type-wall-and-gate-47186.html
		o.load ( WALLOBJ, function ( object ) 
		{
			Wall = object;
			Wall.scale.multiplyScalar(0.1);
            Wall.position.set(-10,0,100);
            scene.add(Wall);
		}
		);
	}
	);
	m.load ( WALLMTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		o.load ( WALLOBJ, function ( object ) 
		{
			Wall2 = object;
			Wall2.scale.multiplyScalar(0.1);
            Wall2.position.set(10,0,50);
            Wall2.rotation.y = 330;
            scene.add(Wall2);
		}
		);
	}
	);
	m.load ( WALLMTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		o.load ( WALLOBJ, function ( object ) 
		{
			Wall3 = object;
			Wall3.scale.multiplyScalar(0.1);
            Wall3.position.set(-10,0,30);
            Wall3.rotation.y = 0;
            scene.add(Wall3);
		}
		);
	}
	);
	m.load ( WALLMTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		o.load ( WALLOBJ, function ( object ) 
		{
			Wall4 = object;
			Wall4.scale.multiplyScalar(0.15);
            Wall4.position.set(10,0,-15);
            Wall4.rotation.y = 330;
            scene.add(Wall4);
		}
		);
	}
	);
	m.load ( WALLMTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		o.load ( WALLOBJ, function ( object ) 
		{
			Wall5 = object;
			Wall5.scale.multiplyScalar(0.1);
            Wall5.position.set(-10,0,-60);
            Wall5.rotation.y = 0;
            scene.add(Wall5);
            var light = new THREE.DirectionalLight( "#3fe293", 5 );
            light.position.set( 0, 10, -180 );
            scene.add( light );
		}
		);
	}
	);
	m.load ( WALLMTL, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		o.load ( WALLOBJ, function ( object ) 
		{
			Wall6 = object;
			Wall6.scale.multiplyScalar(0.1);
            Wall6.position.set(10,0,-125);
            Wall6.rotation.y = 330;
            scene.add(Wall6);
		}
		);
	}
	);
}
    
function collisionCheck()
{
    var firstObject = play;
    var secondObject = Wall;
    var thirdObject = Wall2;
    var fourthObject = Wall3;
    var fifthObject = Wall4;
    var sixthObject = Wall5;
    var seventhObject = Wall6;
    firstBB = new THREE.Box3().setFromObject(firstObject, true);
    secondBB = new THREE.Box3().setFromObject(secondObject, true);  
    thirdBB = new THREE.Box3().setFromObject(thirdObject, true); 
    fourthBB = new THREE.Box3().setFromObject(fourthObject, true); 
    fifthBB = new THREE.Box3().setFromObject(fifthObject, true); 
    sixthBB = new THREE.Box3().setFromObject(sixthObject, true); 
    seventBB = new THREE.Box3().setFromObject(seventhObject, true);                           //collision check using boundingBox on each wall and checking if their rays intersect
    var collision = secondBB.intersectsBox(firstBB);                                        
    collision = fourthBB.intersectsBox(firstBB);
    collision = fifthBB.intersectsBox(firstBB);
    collision = sixthBB.intersectsBox(firstBB);
    collision = seventBB.intersectsBox(firstBB);
    if (collision)  
    {
        play.position.set(0,player.height,-160);
        collision = false;
    }
}

function floor()
{
    var texture = new THREE.TextureLoader().load( 'uploads/linr2/ground.jpeg' );
    texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
    texture.offset.set( 2, 2 );
    texture.repeat.set( 10, 10 );                       // loads in the jpeg image as floor texture and repeats it so it isn't stretched.
    meshFloor = new THREE.Mesh(
    new THREE.PlaneGeometry(100,400),                   // creates a plane/floor of 100x400
    new THREE.MeshBasicMaterial({map:texture}));
    meshFloor.rotation.x -= Math.PI / 2;                // rotates floor so it's not vertical
    scene.add(meshFloor);
}
    
function checkLocation()
{
    if(play.position.z >= 160)
    {
        const e=Math.floor((performance.now()-startTime)/1e3);              // checks for position of the player, if passes the point of the finish line
        AB.newSplash()                                                      // sends out time to socketout and calls the game to be over.
        AB.splashHtml(`<h1>It took you ${e} seconds to reach the end!</h1>`)
        AB.socketOut({
        data : 
        {
            time: e,
        }
    })
        AB.socket.destroy();
        GameOver();
    }
}
    
function AddFinishLine()
{
    var finishL    = new THREE.BoxGeometry( 100, 70, 25 );			 
  	var FinishLine  = new THREE.Mesh( finishL );
	FinishLine.material.color.setHex( 0xff2d00  );			            // adds finish line mesh that can be clearly seen
  	FinishLine.position.x = 0;   	
  	FinishLine.position.z = 180;   	
  	FinishLine.position.y = 0;	
	scene.add( FinishLine );
	var light = new THREE.DirectionalLight( "#7fe093", 4 );
    light.position.set( 0, 1, 0 );
    scene.add( light );
}
    
function movement()
{
    requestAnimationFrame(movement);
    if(keyboard[87] || keyboard[38])
    {                                                                   // W or up arrow key in order to move forward and to update the position of the player amplified by the player speed
        if (play.position.x<= 180 && play.position.x>= -180)
        {                           
            play.position.x -= Math.sin(play.rotation.y) * player.speed/1.5;
            play.position.z -= -Math.cos(play.rotation.y) * player.speed/1.5;
            collisionCheck();                                                   // sends the user position to be checked for collisions
                        
        }
        else
        {
            play.position.x += Math.sin(play.rotation.y) * player.speed/1.5;
            collisionCheck();
                 
        }
        if(play.position.z<= 180 && play.position.z>= -180)
        {
            play.position.x -= Math.sin(play.rotation.y) * player.speed/1.5;
            play.position.z -= -Math.cos(play.rotation.y) * player.speed/1.5;
            collisionCheck();
                
        }
        else
        {
            play.position.z += -Math.cos(play.rotation.y) * player.speed/1.5;
            collisionCheck();
                
        }
        playerspeed++;
        checkLocation();
        collisionCheck();
            
    }
    if(keyboard[83] || keyboard[40])
    {                                                                   // S or down arrow key, same as above but with S
        if (play.position.x<= 160 && play.position.x>= -160)
        {
            play.position.x += Math.sin(play.rotation.y) * player.speed/1.5;
            play.position.z += -Math.cos(play.rotation.y) * player.speed/1.5;
            collisionCheck();
        }
        else
        {
            play.position.x -= Math.sin(play.rotation.y) * player.speed/1.5;
        }
        if(play.position.z<= 200 && play.position.z>= -160)
        {
            play.position.x += Math.sin(play.rotation.y) * player.speed/1.5;
            play.position.z += -Math.cos(play.rotation.y) * player.speed/1.5;
            collisionCheck();
        }
        else
        {
            play.position.z -= -Math.cos(play.rotation.y) * player.speed/1.5;
            collisionCheck();
        }
        playerspeed++;
        checkLocation();
        collisionCheck();
    }
    
    if(keyboard[65] || keyboard[37])
    {                                                                   // A or left arrow key, same as above however if player goes too far left off the platform, teleports them back to middle.
        if (play.position.x <= 50 && play.position.x >= -50)                // introduces horizonal movement to the left
        {
            play.position.x += Math.sin(play.rotation.y + Math.PI/2) * player.speed/1.5;
            play.position.z += -Math.cos(play.rotation.y + Math.PI/2) * player.speed/1.5;
            collisionCheck();
        }
        else
        {
            play.position.x = Math.sin(play.rotation.y - Math.PI/2) * player.speed/1.5;
            collisionCheck();
        }
        if(play.position.z <= 200 && play.position.z >= -200)
        {
            play.position.x += Math.sin(play.rotation.y + Math.PI/2) * player.speed/1.5;
            play.position.z += -Math.cos(play.rotation.y + Math.PI/2) * player.speed;
            collisionCheck();
        }
        else
        {
            play.position.z = -Math.cos(play.rotation.y - Math.PI/2) * player.speed/1.5;
            collisionCheck();
        }
        playerspeed++;
        checkLocation();                                                                 // collision check and finish line check
        collisionCheck();
    }
    if(keyboard[68] || keyboard[39])
    {                                                                   // D or right arrow key, same as A key and but checks for right horizonal movement
        if (play.position.x<= 50 && play.position.x>= -50)
        {                             // teleports user back to middle if too far right.
            play.position.x += Math.sin(play.rotation.y - Math.PI/2) * player.speed/1.5;
            play.position.z += -Math.cos(play.rotation.y - Math.PI/2) * player.speed/1.5;
            collisionCheck();
        }
        else
        {
            play.position.x = Math.sin(play.rotation.y + Math.PI/2) * player.speed/1.5;
            collisionCheck();
        }
        if(play.position.z<= 200 && play.position.z>= -200)
        {
            play.position.x += Math.sin(play.rotation.y - Math.PI/2) * player.speed/1.5;
            play.position.z += -Math.cos(play.rotation.y - Math.PI/2) * player.speed;
            collisionCheck();
        }
        else
        {
            play.position.z = -Math.cos(play.rotation.y + Math.PI/2) * player.speed/1.5;
            collisionCheck();
        }
        playerspeed++;
        checkLocation();
        collisionCheck();
    }
    cam.position.x = play.position.x ;
    cam.position.z = play.position.z -5;
    cam.position.y = 4;
    renderer.render(scene, cam);
}
    
    
function keyDown(event)
{
    keyboard[event.keyCode] = true;
}

function keyUp(event)
{
    keyboard[event.keyCode] = false;
}

window.addEventListener('keydown', keyDown);        // checks for if key is held down or lifted up in order to implement smooth movement
window.addEventListener('keyup', keyUp);

AB.world.newRun = function() 
{
    AB.socketStart();
    loadtextures();
    render();
	create_player();
    camera();
    floor();
    AddFinishLine();
    generateWall();
    movement();
    AB.showRunHeader(); 	
    welcome();
};

function welcome() 
{
    AB.newSplash();                                                                     // Welcome screen for all users, introducing game and controls.
    AB.runReady = false
    AB.splashHtml (  ` 
    <h1> Welcome to 3D Platformer! </h1>  
    The Goal is to reach the Finish line in the fastest time.  <br>
    Press WASD to move, If you touch any of the obstacles you get reset to the beginning (not working)!
    <p>
    <button onclick='AB.removeSplash();'  class=ab-largenormbutton >Start</button>
    <p>  `)
}

AB.socketIn = function(element) 
{   
    AB.newSplash();                                                     // socket to receive information, if information is received, it means player reached finish line
    AB.splashHtml ( ` <h1> Game Over!<h1> <p>Other player finished in: `+ element.data.time + ` seconds. </p>`);
    if (playonce != 1)
    {
    var congrats = new Audio("/uploads/linr2/congratulations.mp3");
    backgroundM.volume = 0;
    congrats.volume = 0.04;
    congrats.play();
    playonce += 1;
    Object.freeze(play);
    }
}

var playonce = 0;                                                       // variable to make sure congratulation sound is only played once.
function GameOver()
{
    if (playonce != 1)
    {                                                                   // gameover function to play sound and mute background music
    var congrats = new Audio("/uploads/linr2/congratulations.mp3");
    backgroundM.volume = 0;
    congrats.volume = 0.04;
    congrats.play();
    playonce += 1;
    Object.freeze(play);
    }
}

welcome();
var backgroundM = new Audio("/uploads/linr2/background.mp3");
backgroundM.volume = 0.025;
backgroundM.play();