Code viewer for World: First Person Controls (clo...

// Cloned by Liam on 29 Nov 2022 from World "First Person Controls (clone by David L)" by David L 
// Please leave this clone trail here.
 


// Customise AB run parameters (optional).
// The following parameters can be customised. (They have default values.)

AB.clockTick       = 1; // Speed of run: Step every n milliseconds. Default 100.
	
AB.maxSteps        = 65545; // Length of run: Maximum length of run in steps. Default 1000.

AB.screenshotStep  = 50;   

AB.drawRunControls = false;
threeworld.drawCameraControls = false;

const floorTextureFile = "/uploads/goobert/grass2.png" // const floorTextureFile = "/uploads/mathias/grass.jpg"

var MOVESPEED = 3;
var GRAVITY = 15;
var FogDistance = 3000;
var FOV = 120;

//  Defines the THREE.PointerLockControls class, source at https://threejs.org/

THREE.PointerLockControls = function ( camera ) {

	var scope = this;

// 	camera.rotation.set( 0, 0, 0 );

	var pitchObject = new THREE.Object3D();
	pitchObject.add( camera );

	var yawObject = new THREE.Object3D();
	yawObject.position.y = 10;
	yawObject.add( pitchObject );

	var PI_2 = Math.PI / 2;

	var onMouseMove = function ( event ) {

		if ( scope.enabled === false ) return;

		var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
		var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;

		yawObject.rotation.y -= movementX * 0.002;
		pitchObject.rotation.x -= movementY * 0.002;

// 		pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) );
	};

	this.dispose = function () {

		document.removeEventListener( 'mousemove', onMouseMove, false );
	};

	document.addEventListener( 'mousemove', onMouseMove, false );

	this.enabled = false;

	this.getObject = function () {

		return yawObject;

	};

	this.getDirection = function () {

		// assumes the camera itself is not rotated

		var direction = new THREE.Vector3( 0, 0, 0 );
		var rotation = new THREE.Euler( 0, 0, 0 );

// 		return function ( v ) {

// 			rotation.set( pitchObject.rotation.x, yawObject.rotation.y, 0 );

// 			v.copy( direction ).applyEuler( rotation );

// 			return v;

// 		};

	}();

};
//==============================================================================

function loadResources()		// asynchronous file loads - call initScene() when all finished 
{

// load skeleton - OBJ that we will paint with a texture 
	
	var loader = new THREE.OBJLoader ( new THREE.LoadingManager() );
	
	loader.load ( OBJ_SKELETON, function ( object )
	{
		theenemy = object;						// canonical enemy object - may be copied multiple times 
		if ( asynchFinished() )	initScene();		 
	});

THREE.DefaultLoadingManager.addHandler ( /\.tga$/i, new THREE.TGALoader() );


	var m = new THREE.MTLLoader();
	m.setResourcePath ( OBJPATH );
	m.setPath         ( OBJPATH );
	
	m.load ( MTLNAME, function ( materials ) 
	{
		materials.preload();
		var o = new THREE.OBJLoader();
		o.setMaterials ( materials );
		o.setPath ( OBJPATH );
		
		o.load ( OBJNAME, function ( object ) 
		{
			theagent = object;
			if ( asynchFinished() )	initScene();		 
		});
	});
	
	
 // load textures 
	
	var loader2 = new THREE.TextureLoader();

	loader2.load ( TEXTURE_SKELETON, function ( thetexture )  
	{
		thetexture.minFilter  = THREE.LinearFilter;
		skeleton_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});

}


function asynchFinished()		 // all file loads returned 
{
	if ( skeleton_texture && theenemy && theagent )   return true;  
	else return false;
}	

function initScene()		// all file loads have returned 
{
	
 // add agent model 	
 // start at center
 
 	theagent.position.y = 0;
	theagent.position.x = 0;
	theagent.position.z = 0;
	
	theagent.scale.multiplyScalar ( SCALE_HERO );    	  	// scale it 
	ABWorld.scene.add( theagent ); 

 // set up lookat and follow in direction agent is pointing in 
	
	setLookatFollow();	 

 // add enemies
 // first paint and size the canonical enemy object 
 
	theenemy.traverse ( function ( child ) 
	{
		if ( child instanceof THREE.Mesh ) 
			child.material.map = skeleton_texture ;
	});
		
	theenemy.scale.multiplyScalar ( SCALE_SKELETON );    	// scale it   

	
 // add perhaps multiple enemy models, starting near outside of arena
 
 for ( var i = 0; i < NO_SKELETONS ; i++ ) 
 {
    var object = theenemy.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 ); 
	    
	// save in array for later
    THESKELETONS[i] = object;
 }
}

const zombiePOS  = new THREE.Vector3 ( 12, 0, 0);
    // const buuPosition    = new THREE.Vector3 ( 9,   0, 6 );
    // can load 3D models of other user:

loader.load ( '/uploads/goobert/zombie.glb', gltf => onLoad ( gltf, zombiePOS ) );
    // loader.load ( '/uploads/chris/kid_buu.glb',   gltf => onLoad ( gltf, buuPosition )   );

function loadStayModels(){
    const Sloader = new THREE.GLTFLoader();

    const SonLoad = ( gltf, Sposition ) => {
    const Smodel = gltf.scene.children[ 0 ];
    Smodel.position.copy( Sposition );

    ABWorld.scene.add( Smodel );
    };

    const zombiePOS    = new THREE.Vector3 ( 12,   0,  0);
    //  const brolyPosition    = new THREE.Vector3 ( 0,   1, -8 );
    //  const smallgokuPosition = new THREE.Vector3(-4, 5, -16);
    //  const dragonPosition = new THREE.Vector3( 0, 0, 0 );
    //  const androidPosition    = new THREE.Vector3 ( -3, 0, 10 );
             
    Sloader.load ( '/uploads/goobert/zombie.glb', gltf => SonLoad ( gltf, zombiePOS ) );
//   Sloader.load ( '/uploads/chris/bulma_-_dragon_ball.glb', gltf => SonLoad ( gltf, brolyPosition ) );
//   Sloader.load ( '/uploads/chris/son_goku_and_kintoun_nimbus.glb', gltf => SonLoad ( gltf, smallgokuPosition ) );
//   Sloader.load ( '/uploads/chris/kame_house.glb', gltf => SonLoad ( gltf, dragonPosition ) );
//   Sloader.load ( '/uploads/chris/dragon_ball_z_-_android_18.glb', gltf => SonLoad ( gltf, androidPosition ) );
          
}

function World() {

    var camera, controls;

	var objects = [];

	var raycaster;

    var moveForward = false;
	var moveBackward = false;
	var moveLeft = false;
	var moveRight = false;
	var canJump = false;

	var prevTime = performance.now();
	var velocity = new THREE.Vector3();
	var direction = new THREE.Vector3();
	var vertex = new THREE.Vector3();
	var color = new THREE.Color();

	this.newRun = function()
	{
	   // loadModels();
	   // loadStayModels();
        // this handles key presses
        var onKeyDown = function ( event ) {

	    	switch ( event.keyCode ) {

				case 38: // up
				case 87: // w
					moveForward = true;
					break;
                
                case 38: // up
                case 16: // shift
                    MOVESPEED = 5; // 
                    break;

                // case 38: // up
                case 80: // P
                    FOV = 500;
                    break;

				case 37: // left
				case 65: // a
					moveLeft = true; break;

				case 40: // down
				case 83: // s
					moveBackward = true;
					break;

				case 39: // right
				case 68: // d
					moveRight = true;
					break;

				case 32: // space
					if ( canJump === true ) velocity.y += 350;
					canJump = false;
					break;

			}

		};

		var onKeyUp = function ( event ) {

			switch( event.keyCode ) {

				case 38: // up
				case 87: // w
					moveForward = false;
					break;
                
                case 38: // up
                case 16: // shift
                    MOVESPEED = 10; // 
                    break;
                
				case 37: // left
				case 65: // a
					moveLeft = false;
					break;

				case 40: // down
				case 83: // s
					moveBackward = false;
                    break;

				case 39: // right
				case 68: // d
					moveRight = false;
					break;

			}

		};

	    camera = new THREE.PerspectiveCamera( FOV );
        threeworld.camera = camera;
	    threeworld.init3d ( 0,0, 0x7ec0ee  );

	    var light = new THREE.HemisphereLight( 0xeeeeff, 0x777788, 0.75 );
		light.position.set( 0.5, 1, 0.75 );
		threeworld.scene.add( light );

		threeworld.scene.fog = new THREE.Fog( 0xffffff, 0, FogDistance );

        controls = new THREE.PointerLockControls( camera );
        threeworld.scene.add( controls.getObject() );
    
        // event listener for key presses
		document.addEventListener( 'keyup', onKeyUp, false );
		document.addEventListener( 'keydown', onKeyDown, false );
		
		raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 );


    
    
	    $("#user_span1").html("Use <b>WASD</b> or <b>Arrows</b> to move and <b>shift</b> to run any direction.");
	    
	   // var blocker = $("#user_span2");
	    $("#user_span2").html("<p><b>Click screen to enable mouse controls</b></p>");

    //The following handles pointer locking when clicking the window
    // handles the pointer locking (game return feature) when clicking back onto the game.
	    var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
        console.log(havePointerLock);
        if ( havePointerLock ) {

			var element = document.body;

			var pointerlockchange = function ( event ) {

				if ( document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element ) {

					controls.enabled = true;
					$("#user_span2").html("");

				} else
				{
					controls.enabled = false;
					$("#user_span2").html("<p><b>Click onto the game to return playing.</b></p>");

				}

			};

			var pointerlockerror = function ( event ) {

				console.error("pointerlockerror");

			};

			// Hook pointer lock state change events
			document.addEventListener( 'pointerlockchange', pointerlockchange, false );
			document.addEventListener( 'mozpointerlockchange', pointerlockchange, false );
			document.addEventListener( 'webkitpointerlockchange', pointerlockchange, false );

			document.addEventListener( 'pointerlockerror', pointerlockerror, false );
			document.addEventListener( 'mozpointerlockerror', pointerlockerror, false );
			document.addEventListener( 'webkitpointerlockerror', pointerlockerror, false );

			document.addEventListener( 'click', function ( event ) {

				// Ask the browser to lock the pointer
				element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
				element.requestPointerLock();

			}, false );

		} else {

			$("#user_span1").html('<p>Your browser doesn\'t seem to support Pointer Lock API</p>');

		}


	//The following draws a simple scene
	
		//floor
	    var WorldSize = 20000;
	    var BlockSize = 50;

	    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,-20,0);
        threeworld.scene.add(floor);
	};


	this.nextStep = function()		 
	{

        
        //======================================================================
        //This will handle moving the player and the camera
        //======================================================================
        
        
			raycaster.ray.origin.copy( controls.getObject().position );
			raycaster.ray.origin.y -= 10;

			var intersections = raycaster.intersectObjects( objects );

			var onObject = intersections.length > 0;

			var time = performance.now();
			var delta = ( time - prevTime ) / 1000;

			velocity.x -= velocity.x * 10.0 * delta;
	    	velocity.z -= velocity.z * 10.0 * delta;

			velocity.y -= GRAVITY;


			direction.z = Number( moveForward ) - Number( moveBackward );
			direction.x = Number( moveLeft ) - Number( moveRight );
			direction.normalize(); // this ensures consistent movements in all directions

			if ( moveForward || moveBackward ) velocity.z -= direction.z * 400.0 * MOVESPEED * delta;
			
			if ( moveLeft || moveRight ) velocity.x -= direction.x * 400.0 * MOVESPEED * delta;

			if ( onObject === true ) {

				velocity.y = Math.max( 0, velocity.y );
				canJump = true;

			}

			controls.getObject().translateX( velocity.x * delta );
			controls.getObject().translateY( velocity.y * delta );
			controls.getObject().translateZ( velocity.z * delta );

			if ( controls.getObject().position.y < 10 ) {

				velocity.y = 0;
				controls.getObject().position.y = 10;

				canJump = true;

			}

			prevTime = time;

	};

}