Code viewer for World: Space Odyssey
// Cloned by Yiming Fu on 2 Dec 2022 from World "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
// Click button to change a random box's texture on all clients running this World
// Pick a side and compete against an opponent! 

// You can annoy the other player by reloading the page!

 

AB.clockTick       = 200;    

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

	// 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 mixers = [];
const clock = new THREE.Clock();


const FILE_ARRAY = [
		"/uploads/fuy3/solar4.jpeg",  // the white solar
		"/uploads/fuy3/solar1.jpeg",  // array 5
		"/uploads/fuy3/solar8.png",  // array 6 
		];
		
		
SKYBOX_ARRAY=["/uploads/fuy3/img3.png",
    "/uploads/fuy3/img4.png",
    "/uploads/fuy3/img5.png",
    "/uploads/fuy3/img1.png",
    "/uploads/fuy3/img.png",
    "/uploads/fuy3/img2.png"];


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

const objectsize = 450 ; 

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


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

ABWorld.drawCameraControls 	= false; 

AB.drawRunControls 			= false;

    var THEARMY 	= new Array( ARMYSIZE );	

	var textureArray = new Array ( FILE_ARRAY.length );

function loadModels() 
{
  const loader = new THREE.GLTFLoader();

  const onLoad = ( gltf, position ) => 
  {
    const model = gltf.scene.children[ 0 ];
    
    model.position.copy( position );
    
    model.rotation.z = 100;

    ABWorld.scene.add( model );
  };

  const rockPosition      = new THREE.Vector3 ( 0,     -300,   0 );

 // can load 3D models of other user:
  loader.load ( '/uploads/fuy3/rock.glb',   gltf => onLoad ( gltf, rockPosition )   );
  
}

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() ) initArmy();
	});	
}
 
 
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, 10, 10 ); 
  
  	var theobject  = new THREE.Mesh( shape );

  	theobject.position.x = AB.randomIntAtoB ( -9000, 9000 );   	
  	theobject.position.z = AB.randomIntAtoB ( -9000, 9000 );   	
  	theobject.position.y = AB.randomIntAtoB ( -9000, 9000 );  		
	
    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.removeLoading();
	
  	AB.runReady = true; 
  	
  	AB.msg ( ` <hr> <p> This is a multi-user game.<br><br>Try dragging the camera to en-<br>joy  the cosmic landscape.<br><br>Would you rather the planet be <br> pink or blue? <br><br>Click here to pick your side.<p>
  	        <button onclick='pink();'  class=ab-largenormbutton > Pink </button>  
            <button onclick='blue();'  class=ab-largenormbutton > Blue </button> <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].rotation.x += AB.randomIntAtoB(0,1);
        THEARMY[i].rotation.y += AB.randomIntAtoB(0, 1);
        ABWorld.scene.add( THEARMY[i] );
   }
 }
 
}


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

	AB.runReady = false;  

	ABWorld.init3d ( startRadiusConst, maxRadiusConst); 
	ABWorld.scene.background=(new THREE.CubeTextureLoader).load(SKYBOX_ARRAY)

	loadResources();		// aynch file loads		
							// calls initArmy() when it returns
	initLights();
	loadModels();
	
	
};


AB.world.nextStep = function()
{
 	moveArmy();
 	const delta = clock.getDelta();
        mixers.forEach( ( mixer ) => { mixer.update( delta ); } );
};


const MUSICFILE = '/uploads/fuy3/space.mp3';
AB.backgroundMusic ( MUSICFILE );


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

// start socket

AB.socketStart();


// functions called by buttons
// baby and skull are textures 5 and 6 in the array:

function pink()  {    changeBox(1);    AB.socketOut (1); }
function blue() {    changeBox(2);    AB.socketOut (2); }


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

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