Code viewer for World: Dragon Ball Mengte Zhu 201...
// ==== Code Description =================================================================================================
// This code template is based on CA318 Ancient Brain Websockets boxes.
// I used the framework of the overall code, and its Websocket structure. 
// I added textures of the background scene, object textures, modified the important parameters of the leading scene, and imported character and object models, and added more optional items based on the original code. 
// The specific additions and changes will be marked in detail in the following code.
// ====================================================================================================================

 

AB.clockTick       = 100;    

	// 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 FILE_ARRAY = [                      // Add the corresponding object texture
		"/uploads/chris/dragonball1.png",  // array 0 to 6 are the Dragon balls 
		"uploads/chris/dragonball2n.png",
		"/uploads/chris/dragonball3.png",
		"/uploads/chris/dragonball4.png",
		"/uploads/chris/dragonball5.png",
		"/uploads/chris/dragonball6.png",
		"/uploads/chris/dragonball7.png",
		"/uploads/chris/goku.png",        // array 7
		"/uploads/chris/Vegeta.png",     // array 8
		"/uploads/chris/Gohan.png",      // array 9
		"/uploads/chris/Piccolo.png",    // array 10
		"/uploads/chris/Frieza.png",     // array 11
		"/uploads/chris/Cell.png",       // array 12
		"/uploads/chris/Buu.png",        // array 13
		"/uploads/chris/Broly.png"       // array 14
		];
	
	
const SKYCOLOR = 0xccffcc;				// a number, not a string 

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

const objectsize = 0.5;

const WALKSTEP = 0.7;       // bounds of the random move per timestep 
                            // try 50 (vibrating sheet) versus 1000 (cloud)

// According to the scale of the model, set the parameters in line with your own scene construction
const MAXPOS                = 11;                 // 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  


const SKYBOX_ARRAY = [                         // Add corresponding scene textures

                "/uploads/chris/skyxpos.png",
                "/uploads/chris/skyxneg.png",
                "/uploads/chris/skyzpos.png",
                "/uploads/chris/skyzneg.png",
                "/uploads/chris/skyyneg.png",
                "/uploads/chris/skyypos.png"
                ];
                
 const mixers = [];
 const clock = new THREE.Clock();

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 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, 30, 30 ); 
  
  	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, 6 );    			            // random one of the balls
    theobject.material =  new THREE.MeshBasicMaterial ( { map: textureArray[r] } );   

 	ABWorld.scene.add(theobject);
	THEARMY[t] = theobject;	            	// save it for later
	t++; 
 }
 
  // can start the run loop
  
	ABWorld.render();		
  
	AB.removeLoading();
	
  	AB.runReady = true;
  	
  	// Use the character function defined in Websocket
  	AB.msg ( ` <hr> <p> Multi-user game. Pick a character. Click buttons to change Dragon balls on all users' machines. Drag the camera. <p>
  	        <button onclick='Goku();'  class=ab-largenormbutton > Goku </button>
            <button onclick='Vegeta();'  class=ab-largenormbutton > Vegeta </button> <p>
            <button onclick='Gohan();'  class=ab-largenormbutton > Gohan </button>
            <button onclick='Piccolo();'  class=ab-largenormbutton > Piccolo </button> <p>
            <button onclick='Frieza();'  class=ab-largenormbutton > Frieza </button> 
            <button onclick='Cell();'  class=ab-largenormbutton > Cell </button> <p>
            <button onclick='Buu();'  class=ab-largenormbutton > Buu </button>
            <button onclick='Broly();'  class=ab-largenormbutton > Broly </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].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		
							// calls initArmy() when it returns 
							
	initLights();						
	loadModels();
	loadStayModels();
							
	ABWorld.scene.background = new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY );
};


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

function loadModels()                                // Added function to import animated characters and object models
{
  const loader = new THREE.GLTFLoader();

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

      const animation = gltf.animations[ 0 ];

      const mixer = new THREE.AnimationMixer( model );
      mixers.push( mixer );

      const action = mixer.clipAction( animation );
      action.play();

    ABWorld.scene.add( model );
  };
     
     const gokuPosition  = new THREE.Vector3 ( 12,     0,   0);
     const buuPosition    = new THREE.Vector3 ( 9,   0, 6 );
     const vegetaPosition    = new THREE.Vector3 ( -8,   0, 4 );
 // can load 3D models of other user:
  
  loader.load ( '/uploads/chris/goku_rigged__animated.glb', gltf => onLoad ( gltf, gokuPosition ) );  // Import animated character and object models and adjust their positions according to the overall layout
  loader.load ( '/uploads/chris/kid_buu.glb',   gltf => onLoad ( gltf, buuPosition )   );
  loader.load ( '/uploads/chris/kaioh_-_dragon_ball.glb',   gltf => onLoad ( gltf, vegetaPosition )   );
  
}

function loadStayModels()                            // Added function to import non-animated character and object models
{
  const Sloader = new THREE.GLTFLoader();

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

    ABWorld.scene.add( Smodel );
  };
     const vegettoPosition    = new THREE.Vector3 ( 0,   0,  0);
     const brulmaPosition    = 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 );
     const brolyPosition    = new THREE.Vector3 ( 3, 0, 10 );
     
  Sloader.load ( '/uploads/chris/bulma_-_dragon_ball.glb', gltf => SonLoad ( gltf, brulmaPosition ) );                 // Import non-animated character and object models and adjust their positions according to the overall layout
  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 ) );
  Sloader.load ( '/uploads/chris/dragon_ball_zbroly.glb', gltf => SonLoad ( gltf, brolyPosition ) );
}

// Add the light function so that the character object model can present colors
function initLights() 
// the lights are needed to show up models
{
  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 );
}

 const MUSICFILE = '/uploads/chris/VEGETA-royalblue.mp3';   // Added background music
 AB.backgroundMusic ( MUSICFILE );


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

// start socket

AB.socketStart();


// functions called by buttons
// characters are textures 7 and 14 in the array:
// Added multiple functions to change the Dragon Ball pattern

function Goku()  {    changeBall(7);    AB.socketOut (7); }
function Vegeta() {    changeBall(8);    AB.socketOut (8); }
function Gohan() {    changeBall(9);    AB.socketOut (9); }
function Piccolo() {    changeBall(10);    AB.socketOut (10); }
function Frieza() {    changeBall(11);    AB.socketOut (11); }
function Cell() {    changeBall(12);    AB.socketOut (12); }
function Buu() {    changeBall(13);    AB.socketOut (13); }
function Broly() {    changeBall(14);    AB.socketOut (14); }


function changeBall(n)   // change a random ball to texture n (7 or 14) 
{
    var i = AB.randomIntAtoB ( 0, THEARMY.length - 1 );     // pick a random ball 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;
    changeBall(n);
};