Code viewer for World: Pig Shooter (clone by Prze...

// Cloned by Przemyslaw Majda on 5 Dec 2022 from World "Pig Shooter (clone by Przemyslaw Majda) (clone by Thomas)" by Thomas 
// Please leave this clone trail here.
 


// Cloned by Thomas on 5 Dec 2022 from World "Pig Shooter (clone by Przemyslaw Majda)" by Przemyslaw Majda 
// Please leave this clone trail here.
 


// Cloned by Przemyslaw Majda on 29 Nov 2022 from World "Pig Shooter" by Przemyslaw Majda 
// Please leave this clone trail here.
 


// Cloned by Przemyslaw Majda on 28 Nov 2022 from World "Password Websockets boxes  " 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.
// ====================================================================================================================


// Demo of Websockets functionality added to a 3D World
// Run with two or more users
// You have to tell the users the password you will be using

// Click button to change a random box's texture on all clients running this World
// Pick a side and compete against an opponent! 


AB.clockTick       = 40;    

	// Speed of run: Step every n milliseconds. Default 100.
	
AB.maxSteps        = 1200;    // run for ~40 seconds

	// Length of run: Maximum length of run in steps. Default 1000.

AB.screenshotStep  = 100;   
// ABHandler.GROUNDZERO		= true;	
	// Take screenshot on this step. (All resources should have finished loading.) Default 50.
// this isnt used
	const OBJPATH = "/uploads/przemyslawmajda/";	// path of OBJ and MTL (Peter Parker model)
	const OBJNAME = "puss_in_boots.obj";
	const MTLNAME = "puss_in_boots.mtl";
	// Peter Parker credit
// formerly here:
// https://free3d.com/3d-model/spider-man-4998.html

 const FILE_ARRAY = [
		"/uploads/przemyslawmajda/pig.png",  // array 0 to 4 are the earths
		"/uploads/przemyslawmajda/grassHD.jpg",
		"/uploads/przemyslawmajda/fence_texture2.png"
		];
	
	
const SKYCOLOR 	= 0x7dd6ff;       // very light blue 

// a number, not a string
// 0xFFB6C1;    // light pink   
// 0xccffcc;	// light green 			 

const ARMYSIZE = 1000;       // an "army" of objects 

const objectsize = 700 ;

const WALKSTEP = 20;       // bounds of the random move per timestep 
                            // try 50 (vibrating sheet) versus 1000 (cloud)
         
const MAXPOS                = 25000 ;            // start things within these bounds                    
const startRadiusConst	 	= MAXPOS ;	// distance from centre to start the camera at
const maxRadiusConst 		= MAXPOS * 5 ;		// maximum distance from camera we will render things  
const skyboxConst			= MAXPOS * 2 ;

ABHandler.MAXCAMERAPOS 		= MAXPOS * 3 ;			// allow camera go far away 

ABWorld.drawCameraControls 	= true; 

AB.drawRunControls 			= false;


    var THEARMY 	= new Array( ARMYSIZE );	

	var textureArray = new Array ( FILE_ARRAY.length );
	
var puss;

var CONFIRMED_KILLS = [];

var score = 0;

// player movement
// this code was taken from "User-controlled Model World" by Starter User
// https://ancientbrain.com/world.php?world=4606438712

var pussRotation = 0;
const PUSS_STEP = 350;
const rotateConstant = Math.PI / 20;
const KEY_UP = "ArrowUp";
const KEY_LEFT = "ArrowLeft";
const KEY_RIGHT = "ArrowRight";
const KEY_DOWN = "ArrowDown";
var OURKEYS = [KEY_UP, KEY_LEFT, KEY_RIGHT, KEY_DOWN];

function ourKeys(event) {
    console.log(event.key);
    return OURKEYS.includes(event.key);
}


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.key == KEY_UP) {
    // move a bit along angle we are facing
    puss.position.x =
      puss.position.x + PUSS_STEP * Math.sin(pussRotation);
    puss.position.z =
      puss.position.z + PUSS_STEP * Math.cos(pussRotation);
  }
  if (event.key == KEY_DOWN) {
    // move a bit along angle we are facing
    puss.position.x =
      puss.position.x - PUSS_STEP * Math.sin(pussRotation);
    puss.position.z =
      puss.position.z - PUSS_STEP * Math.cos(pussRotation);
  }

  if (event.key == KEY_LEFT) {
    // rotate in place
    pussRotation = pussRotation + rotateConstant;
    puss.rotation.set(0, pussRotation, 0);
  }

  if (event.key == KEY_RIGHT) {
    pussRotation = pussRotation - rotateConstant;
    puss.rotation.set(0, pussRotation, 0);
  }

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

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


function loadSkybox()
{
    var geometry = new THREE.BoxGeometry(maxRadiusConst, maxRadiusConst, maxRadiusConst);
    var cubeMaterials = 
    [
        new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/posz.jpg"), side: THREE.DoubleSide } ),
        new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/negz.jpg"), side: THREE.DoubleSide } ),
        new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/posy.jpg"), side: THREE.DoubleSide } ),
        new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/negy.jpg"), side: THREE.DoubleSide } ),
        new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/posx.jpg"), side: THREE.DoubleSide } ),
        new THREE.MeshBasicMaterial( { map: new THREE.TextureLoader( ).load("/uploads/kellyt46/negx.jpg"), side: THREE.DoubleSide } )
    ];

    var cubeMaterial = new THREE.MeshFaceMaterial( cubeMaterials );
    var cube = new THREE.Mesh( geometry, cubeMaterial );
    ABWorld.scene.add(cube);
}

function loadPlayer()
{
    var manager = new THREE.LoadingManager();
    var loader = new THREE.OBJLoader( manager );
    loader.load("/uploads/victoriacrabbe/puss_in_boots.obj", buildpuss);
    
    // THIS DOESNT WORK IDK WHY LOL
    // var tloader = new THREE.TextureLoader();
    // tloader.load("/uploads/przemyslawmajda/puss_in_boots.mtl", function (materials) {
    //     materials.preload();
        
    //     var objLoader = new THREE.OBJLoader();
    //     objLoader.setMaterials(materials);
    //     objLoader.setPath( OBJPATH );
    //     objLoader.load(OBJNAME, function(object){
    //         object.traverse(function(child) {
    //             child.material.map = texture;
    //         })
    //     })
    // })

}







function buildpuss( object )
{
    object.scale.multiplyScalar(1000);
    console.log(object.children);
    // object.traverse(paintPuss);
    puss = object;
    puss.position.x = AB.randomIntAtoB ( -MAXPOS, MAXPOS );   	
  	puss.position.z = AB.randomIntAtoB ( -MAXPOS, MAXPOS );   	
  	puss.position.y =  350;
    threeworld.scene.add(puss);
    // ABWorld.cameraTrack(puss);
}

// function paintPuss( obj )
// {
//     var ttloader = new THREE.TextureLoader();
//     var material = new THREE.MeshBasicMaterial( {
//         color: 0xFF8844
//     })
//     for (let i = 0; i < obj.children.length; i++) {
//         console.log(obj.children[i]);
//         obj.children[i].material.color = 0xFF8844;
//     }
// }



function loadResources()		// asynchronous file loads - call initScene() when all finished 
{
	for ( var i = 0; i < FILE_ARRAY.length; i++ ) 
	  startFileLoad ( i );						// launch n asynchronous file loads
}

	
function startFileLoad ( n )				// asynchronous file load of texture n 
{
	var loader = new THREE.TextureLoader();

	loader.load ( FILE_ARRAY[n], function ( thetexture )  	 
	{
		thetexture.minFilter  = THREE.LinearFilter;
		textureArray[n] = thetexture;
		if ( asynchFinished() ) {
		    setFloor();
		    initArmy();
		    loadSkybox();
		    loadPlayer();
		    buildFence();
		}
	});	
}
 
 
function asynchFinished()		// all file loads returned 
{
	for ( var i = 0; i < FILE_ARRAY.length; i++ ) 
		if ( ! textureArray[i] ) 
			return false;
		
	  return true;
}

// credit to https://ancientbrain.com/world.php?world=1038845430 on ancient brain
// function buildpuss( object )
// {
//     object.scale.multiplyScalar(4);
//     object.traverse( paintEnemy );
//     puss = object;
//     ABWorld.scene.add(puss);
// }

// function loadModel()
// {
//     var manager = new THREE.LoadingManager();
//     var loader = new THREE.OBJLoader( manager );
//     loader.load(FILE_ARRAY[2], buildpuss);
// }


function setFloor()
{
    var floor = new THREE.BoxGeometry( 60000, 1, 60000 );
    var material = new THREE.MeshBasicMaterial( {color: 0x00ff00} );
    var mesh = new THREE.Mesh(floor);
    mesh.position.x = 0;
    mesh.position.y = -350;
    mesh.position.z = 0;
    mesh.material = new THREE.MeshBasicMaterial({map: textureArray[1]});
    
    ABWorld.scene.add(mesh);
}


function buildFence() {
    // z axis fence
    for (let i = 0; i < 30; i++) {
        let fence = new THREE.BoxGeometry(2000, 2000, 1);
        let fence_mesh = new THREE.Mesh(fence);
        fence_mesh.position.x = (-29000 + (2000 * i));
        fence_mesh.position.y = 650;
        fence_mesh.position.z = 30000;
        fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
        ABWorld.scene.add(fence_mesh);
    }
    for (let i = 0; i < 30; i++) {
        let fence = new THREE.BoxGeometry(2000, 2000, 1);
        let fence_mesh = new THREE.Mesh(fence);
        fence_mesh.position.x = (-29000 + (2000 * i));
        fence_mesh.position.y = 650;
        fence_mesh.position.z = -30000;
        fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
        ABWorld.scene.add(fence_mesh);
    }
    for (let i = 0; i < 30; i++) {
        let fence = new THREE.BoxGeometry(1, 2000, 2000);
        let fence_mesh = new THREE.Mesh(fence);
        fence_mesh.position.x = 30000;
        fence_mesh.position.y = 650;
        fence_mesh.position.z = (-29000 + (2000 * i));
        fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
        ABWorld.scene.add(fence_mesh);

    }
    for (var i = 0; i < 30; i++) {
        var fence = new THREE.BoxGeometry(1, 2000, 2000);
        var fence_mesh = new THREE.Mesh(fence);
        fence_mesh.position.x = -30000;
        fence_mesh.position.y = 650;
        fence_mesh.position.z = (-29000 + (2000 * i));
        fence_mesh.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
        ABWorld.scene.add(fence_mesh);
    }
    
}
function initArmy()		 // called when all textures ready 
{
 var t = 0;
 
 for ( var c=1 ; c <= ARMYSIZE ; c++ )
 {
    //    var shape = new THREE.SphereGeometry ( objectsize, 30, 30 ); 
   var shape = new THREE.BoxGeometry( objectsize, objectsize, objectsize );
  
  	var theobject  = new THREE.Mesh( shape );

  	theobject.position.x = AB.randomIntAtoB ( -MAXPOS, MAXPOS );   	
  	theobject.position.z = AB.randomIntAtoB ( -MAXPOS, MAXPOS );   	
  	theobject.position.y =  0;	
	
// 	var r = AB.randomIntAtoB ( 0, textureArray.length - 1 );    // random texture 
 	var r = AB.randomIntAtoB ( 0, 4 );    			            // random one of the earths
    theobject.material =  new THREE.MeshBasicMaterial ( { map: textureArray[0] } );   

 	ABWorld.scene.add(theobject);
	THEARMY[t] = theobject;	            	// save it for later
	t++; 
 }
 
  // can start the run loop
  
	ABWorld.render();		
  
  	AB.runReady = true; 
  	
  	AB.msg ( ` <hr> <p> Multi-user game. Kill as many pigs as you can before they escape! Player with the most pig kills in 40 seconds wins. <p id="score">Score: 0<p>
  	<p id="highest_score">Highest Score (All players): 0</p>
  	<p id="final_score"></p>` );						
}


function moveArmy()		    // move all the objects 
{
 for ( var i = 0; i < THEARMY.length; i++ )
 { 
   if ( THEARMY[i] )		// in case initArmy() not called yet 
   { 
        THEARMY[i].position.x =  THEARMY[i].position.x + AB.randomIntAtoB(-WALKSTEP *10,WALKSTEP*10) ;        
        THEARMY[i].position.z =  THEARMY[i].position.z + AB.randomIntAtoB(-WALKSTEP*10,WALKSTEP*10) ;
        // dont allow pigs outside the zone
        
    ABWorld.scene.add( THEARMY[i] );
        
   }
 }
}


function checkArmy() {
    for ( var i = 0; i < THEARMY.length; i++ ) { 
        if (THEARMY[i]){
            if ( THEARMY[i].position.x > 29000 || THEARMY[i].position.x < -29000 || THEARMY[i].position.z > 29000 || THEARMY[i].position.z < -29000){
                console.log(THEARMY[i].position.x);
                console.log(THEARMY[i].position.z);
                
                // experimental: 
                ABWorld.scene.remove(THEARMY[i]);
                delete THEARMY[i];
                console.log("BOUNDARY CROSSED: ELIMINATE PIG");
                
            }
        }
    }
     
}



function killPig() {
    var pussx = puss.position.x;
    var pussz = puss.position.z;
    for ( var i = 0; i < THEARMY.length; i++ ) {
        
        if (THEARMY[i]){
            let pigx = THEARMY[i].position.x;
            let pigz = THEARMY[i].position.z;
            if (pigx <= (pussx + 700) && pigx >= (pussx - 700)){
                if (pigz <= (pussz + 700) && pigz >= (pussz - 700)) {
                    console.log(THEARMY[i].position.x);
                    console.log(THEARMY[i].position.z);
                    score = score + 1;
                    updateScore(score);
                    
                    ABWorld.scene.remove(THEARMY[i]);
                    delete THEARMY[i];
                    CONFIRMED_KILLS.push(i);
                    console.log("PIG LOCATED AND ELIMINATED");
                }
            }
        }
    }
}

// makign sure our hero cannot escape the RANCH
function checkPussPosition() {
    if (puss.position.x > 29000) {
        puss.position.x = 29000;
    }
    else if (puss.position.x < -29000) {
        puss.position.x = -29000;
    }
    else if (puss.position.z > 29000) {
        puss.position.z = 29000;
    }
    else if (puss.position.z < -29000) {
        puss.position.z = -29000;
    }
}



function updateScore(n) {
    pContent = document.getElementById("score");
    pContent.innerHTML = "Score: " + n;
}


function finalScore() {
    if (AB.step >= 2400) {
        pContent = document.getElementById("final_score");
        if (high <= score) {
            pContent.innerHTML = "You won! Your score was " + score;
        }
        else {
            pContent.innerHTML = "You were NOT the winner :( The highest score was " + high + ". Better luck next time!";
        }
    }
}



AB.world.newRun = function() 
{
  AB.newSplash();                   
  
  AB.splashHtml (  ` 
    <h1> Password only Websocket boxes </h1>  
    A version of Websocket boxes that only certain users can join.  <br>
    You enter a password. You only join with users who enter the same password (not with all users of the World).  <br>
    The first user to run the World can enter any password! But other users must know what password they will use.
    <p>
	Enter password: 
	<input    style='width:25vw;'    maxlength='2000'   NAME="p"    id="p"       VALUE='' >  
	<button onclick='start();'  class=ab-normbutton >Start</button>
	<p>  
	<div id=errordiv name=errordiv> </div> ` );  
	
	AB.runReady = false;  

	ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 

	loadResources();		// aynch file loads
	document.onkeydown = keyHandler;
							// calls initArmy() when it returns
};



AB.world.nextStep = function()
{
 	moveArmy();
 	checkArmy();
 	killPig();
 	checkPussPosition();
 	sendScore();
 	highestScore();
 	finalScore();
};



//--- start Socket -----------------------------------------------------
	
function start()        // user has entered a password 
{
	var  password =  jQuery("input#p").val();
	password = password.trim();
	
	if ( ! alphanumeric ( password ) )
	{
	    $("#errordiv").html( "<font color=red> <B> Error: Password must be alphanumeric. </b></font> " );
	    return;
	}
	
	// else we have a password, start the socket run with this password 
    AB.socketStart ( password );
    
    AB.removeSplash();
}


  
function alphanumeric ( str ) 				// return if string is just alphanumeric   
{
	var rc = str.match(/^[0-9a-zA-Z]+$/); 			// start of line  "[0-9a-zA-Z]+"  end of line 
	
	if ( rc ) return true;
	else      return false; 
}




//--- Socket functionality -----------------------------------------------------

// functions called by buttons
// baby and skull are textures 5 and 6 in the array:
var high = 0;
function highestScore() {
    pContent = document.getElementById("highest_score");
    if (high > score) {
        pContent.innerHTML = "Highest Score (All players): " + high;
    }
    else {
        pContent.innerHTML = "Highest Score (All players): " + score + " (you)";
    }
    
}

function sendScore() {
    AB.socketOut(score);
}




AB.socketIn = function(n)       // incoming data on socket, i.e. clicks of other player 
{
    if ( ! AB.runReady ) return;
    high = n;
    highestScore();
};