Code viewer for World: Pig Munch

// 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 ~48 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.
// wolf 3D model credit
// https://free3d.com/3d-model/wolf-rigged-and-game-ready-42808.html
// cannot get MTL to laod, getting "m.setTexturePath is not a function" error
// so we are going with an all black lego model

	const OBJPATH = "/uploads/przemyslawmajda/";
	const OBJNAME = "Wolf_obj.obj";
	//const MTLNAME = "legoobj.mtl";


 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 MAXPOS                = 29000 ;            // start things within these bounds  
const ARMYSIZE = 200;       // an "army" of objects 
const skyboxConst = MAXPOS * 2 ;
const objectsize = 700 ;

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


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

ABWorld.drawCameraControls 	= false; 

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;
}

// skybox
function loadSkybox()
{
    var geometry = new THREE.BoxGeometry(maxRadiusConst, maxRadiusConst, maxRadiusConst);
    //image credit for skybox: http://www.humus.name/index.php?page=Textures&ID=138
    var cubeMaterials = 
    [
        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/posz.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(OBJPATH + OBJNAME, buildpuss);
    
}



function buildpuss( object )
{
    object.scale.multiplyScalar(4000);
    console.log(object.children);

    puss = object;
    puss.position.x = AB.randomIntAtoB ( -MAXPOS, MAXPOS );   	
  	puss.position.z = AB.randomIntAtoB ( -MAXPOS, MAXPOS );   	
  	puss.position.y =  -350;
    threeworld.scene.add(puss);

}




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() ) {
		    loadSkybox();
		    setFloor();
		    initArmy();
		    loadPlayer();
		    buildFence();
		}
	});	
}
 
 
function asynchFinished()		// all file loads returned 
{
	for ( var i = 0; i < FILE_ARRAY.length; i++ ) 
		if ( ! textureArray[i] ) 
			return false;
		
	  return true;
}




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);
    }
    
}

// pig sound source https://www.youtube.com/watch?v=Bg2BlPy1I30
var oink = new Audio("/uploads/przemyslawmajda/pig.mp3");





// based on Password Websocket boxes by Starter User
// https://ancientbrain.com/world.php?world=9623346805

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;	
	
	// pig image credit http://clipart-library.com/clipart/344716.htm
 	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 + 900) && pigx >= (pussx - 900)){
                if (pigz <= (pussz + 900) && pigz >= (pussz - 900)) {
                    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");
                    oink.play();
                }
            }
        }
    }
}


// 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 >= 1200) {
        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  );
	   // var ambient = new THREE.AmbientLight();
    //     ABWorld.scene.add(ambient);
    
          var stageLight = new THREE.DirectionalLight(0xa2a2a2, 1.5);
          stageLight.position.set(startRadiusConst, startRadiusConst, startRadiusConst);
          ABWorld.scene.add(stageLight);

	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;
};