Code viewer for World: Websockets boxes lab (clon...

// Cloned by Fionn Gallahar Hall on 5 Dec 2022 from World "Websockets boxes lab (clone by Okikiola Sanni)" by Okikiola Sanni 
// Please leave this clone trail here.
 

// Cloned by Okikiola Sanni on 13 Nov 2022 from World "Websockets boxes" by Starter user 
// Please leave this clone trail here.
 

AB.clockTick       = 100;    

	// Speed of run: Step every n milliseconds. Default 100.
	
AB.maxSteps        = 1000;    

	// 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.


const MUSIC_BACK = "/uploads/kikisanni/arcade.mp3"


const FILE_ARRAY = [
		"/uploads/kikisanni/white.png",  // array 1-5 are default boxes 
		"/uploads/kikisanni/white.png",
		"/uploads/kikisanni/white.png",
		"/uploads/kikisanni/white.png",
		"/uploads/kikisanni/white.png",
		"/uploads/kikisanni/red.jpg",
		"/uploads/kikisanni/yellow.jpeg", 
		"/uploads/kikisanni/blue.png",
		"/uploads/kikisanni/green.png",
		];


const SKYBOX_ARRAY = [
                "/uploads/kikisanni/flame_bk.jpg",
                "/uploads/kikisanni/flame_ft.jpg",
                "/uploads/kikisanni/flame_up.jpg",
                "/uploads/kikisanni/flame_dn.jpg",
                "/uploads/kikisanni/flame_lf.jpg",
                "/uploads/kikisanni/flame_rt.jpg"
                ];



var timer = new THREE.Clock();


const SKYCOLOR 	= 0xd3d3d3 ;

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

const objectsize = 500 ;

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


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

ABWorld.drawCameraControls 	= false; 

AB.drawRunControls 			= false;


    var THEARMY 	= new Array( ARMYSIZE );	

	var textureArray = new Array ( FILE_ARRAY.length );





function initLights()
{
  const ambientLight = new THREE.AmbientLight( 0xffffff, 1 );
  ABWorld.scene.add( ambientLight );

  const frontLight = new THREE.DirectionalLight( 0xffffff, 1 );
  frontLight.position.set( 10, 10, 10 );

  const backLight = new THREE.DirectionalLight( 0xffffff, 1 );
  backLight.position.set( -10, 10, -10 );

  ABWorld.scene.add( frontLight, backLight );
}




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



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[r] } );   

 	ABWorld.scene.add(theobject);
	THEARMY[t] = theobject;	            	// save it for later
	t++; 
 }
 
 
  
	ABWorld.render();		
  
	AB.removeLoading();
	
  	AB.runReady = true; 
  	
  	let listElement = document.createElement("li");
  	listElement.innerHTML
  	
  	
  	
  	AB.msg (" <hr> <p> Colour Rush. Each user will pick a colour. Spam your button to change the boxes to your colour, most boxes filled wins.</hr> <p>" +
  	  	    " <button onclick='red();'  class=ab-largenormbutton > Red </button> <p>" +
            "<button onclick='yellow();'  class=ab-largenormbutton > Yellow </button><p>" +
            "<button onclick='blue();'  class=ab-largenormbutton > Blue </button><p>" +
            "<button onclick='green();'  class=ab-largenormbutton > Green </button><p>" + 
  	        "<br> Timer: " + Math.round(timer.getElapsedTime()) + "<h1> Chat Opponent </h1>" + "If you are logged in, chat is tagged with your name." +
  	        "<div style=width:60vw; background-color:#add8e6;  border: 1px solid black; margin:5; padding: 10px>" +
            "<h3> Me </h3>" +
            "<INPUT style=width:15vw; id=me>" +
            "<button onclick=sendchat();     class=ab-normbutton > Send </button>" +
            "</div>" +
         
            "<div style=width:60vw; background-color:darkgrey;  border: 1px solid black; margin:5; padding: 10px;>" +
            "<h3> Them </h3>" +
            "<div id=them > </div>" +
            "</div>" +
         
            "<div style=width:60vw; background-color:blue;  border: 1px solid black; margin:5; padding: 10px;>" +
            "<h3> Players online </h3>" +
            "<div id=themlist > </div>" +
            "</div>"
  	);
}



function startClock()
{
    timer.start();
}

function stopClock()
{
    timer.stop();
}

function initScene()
{
    initArmy();
    ABWorld.scene.background = new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY,	function()
	{
	ABWorld.render();
	AB.removeLoading();
	AB.runReady = true;
	});
}


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,WALKSTEP) ;        
        THEARMY[i].position.z =  THEARMY[i].position.z + AB.randomIntAtoB(-WALKSTEP,WALKSTEP) ;
        THEARMY[i].position.y =  THEARMY[i].position.y + AB.randomIntAtoB(-WALKSTEP,WALKSTEP) ;
        ABWorld.scene.add( THEARMY[i] );
   }
 }
}



AB.world.newRun = function() 
{
	AB.loadingScreen();

	AB.runReady = false;  

	ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 

	loadResources();		// aynch file loads		
	
	initLights();
				
};



AB.world.nextStep = function()
{
 	moveArmy();
};




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

// start socket
AB.socketStart();

// functions called by buttons
function red()  {    changeBox(5);    AB.socketOut (5); }
function yellow() {    changeBox(6);    AB.socketOut (6); }
function blue()  {    changeBox(7);    AB.socketOut (7); }
function green()  {    changeBox(8);    AB.socketOut (8); }


function changeBox(n)   // change a random box to texture n 
{
    var i = AB.randomIntAtoB ( 0, THEARMY.length - 1 );     // pick a random box to change 
    THEARMY[i].material =  new THREE.MeshBasicMaterial ( { map: textureArray[n] } );   
}



// --- music ----------------------------------------


var backmusic = AB.backgroundMusic ( MUSIC_BACK );
	
function musicPlay()   { backmusic.play();  }
function musicPause()  { backmusic.pause(); }


// // --- Send my line of text to server --------------

function sendchat()
{
  var theline = $("#me").val();
  
  var data = 
  {
    userid:     AB.myuserid,
    username:   AB.myusername,
    line:       theline
  };
  
  AB.socketOut ( data );        // server gets this, and sends the data to all clients running this World
}


function userlink ( userid, username )
{
  if ( userid == "none" )
        return ( "Anon user" );
  else
        return ( "<a target='_blank' href='https://ancientbrain.com/user.php?userid=" + userid + "'>" + username + "</a>" );
}


AB.socketIn = function(n)       // incoming data from other users 
{
    changeBox(n);
};


AB.socketIn = function(data)
{
  var userhtml = userlink ( data.userid, data.username );
  $("#them").html ( userhtml + ": " + data.line );
};


// // At startup, and when list of users changes, server will trigger AB.socketUserlist.  
// // Here I define what to do for it.

AB.socketUserlist = function ( a ) 
{ 
    console.log ( "Got user list: " );
    console.log ( JSON.stringify ( a, null, ' ' ) );

    if ( a.length < 2 )  
    { 
        $("#themlist").html ( "<h3 style='color:red'> You are alone. Get someone else to run this World. </h3>"); 
        return; 
    }
    
    // else  
    var str = " <ol> ";
    for (var i = 0; i < a.length; i++) 
    {
        var userhtml = userlink ( a[i][0], a[i][1] );
        str = str + " <li> " + userhtml ;
    }
    $("#themlist").html ( str + " </ol> " );
};