// Cloned by Effa Al Bulushi on 1 Dec 2022 from World "Websockets boxes" by Starter user // Please leave this clone trail here.//This world is Created by Effa Issa Faqir Al Bulushi. The code is Obfuscated and you are not allowed to use it as it is part of a CA.//Any changes you make without permission from the author will be complained immediatly to the lecturer.//These are some of the extra features I have added to the world after cloning it from starter://1->Music //2->password//3 ->SkyBox//4 ->added new models//5-> multi-world//6-> chatting feature//7-> make a 3d model move// ==== 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!
$('body').css("margin",'auto');
$('body').css("padding",'auto');
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.
AB.drawRunControls =falseconst FILE_ARRAY =["/uploads/issafae2/logo2.jpeg",// array 0 "/uploads/issafae2/logo3.png",//array 1"/uploads/issafae2/cj.jpeg",//array 2"/uploads/issafae2/bigsmoke.jpeg",// array 3"/uploads/issafae2/ryder.jpeg",// array 4"/uploads/issafae2/sweet.jpeg",// array 5];const FILE_ARRAY2 =["/uploads/issafae2/carl_jonhson_cj.glb"];const SKYCOLOR =0xa3ccd6;// a number, not a string const LIGHTCOLOR =0xffffff;const ARMYSIZE =150;// an "army" of objects const objectsize =1;const WALKSTEP =0.7;// bounds of the random move per timestep // try 50 (vibrating sheet) versus 1000 (cloud)const MAXPOS =11;// start things within these bounds const startRadiusConst = MAXPOS *3;// distance from centre to start the camera atconst maxRadiusConst = MAXPOS *10;// maximum distance from camera we will render things ABHandler.MAXCAMERAPOS = MAXPOS *10;// allow camera go far away ABWorld.drawCameraControls =false;var THEARMY =newArray( ARMYSIZE );var textureArray =newArray( FILE_ARRAY.length );var characterArray =newArray( FILE_ARRAY2.length );const mixers =[];const clock =new THREE.Clock();ABHandler.MAXCAMERAPOS = maxRadiusConst;ABHandler.GROUNDZERO =true;const ROTATE_AMOUNT =Math.PI /20;const CHAR_STEP =0.5;const SCALE_HERO =1;const CAMERASHIFT =- SCALE_HERO *2;const FOLLOW_Y = SCALE_HERO *4;var character;var characterRotation =0;//======SkyBox=======//Sources used to implement this part: https://threejs.org/docs/#api/en/loaders/CubeTextureLoader and https://ancientbrain.com/world.php?world=complexconst SKYBOX_ARRAY =["/uploads/issafae2/right1.png","/uploads/issafae2/left1.png","/uploads/issafae2/top1.png","/uploads/issafae2/bottom1.png","/uploads/issafae2/front1.png","/uploads/issafae2/back1.png"];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();});
THREE.DefaultLoadingManager.addHandler (/\.tga$/i,new THREE.TGALoader());var m =new THREE.MTLLoader();
m.setResourcePath ("/uploads/issafae2/");
m.setPath ("/uploads/issafae2/");
m.load ("carljonhson.mtl",function( materials ){
materials.preload();var o =new THREE.OBJLoader();
o.setMaterials ( materials );
o.setPath ("/uploads/issafae2/");
o.load ("carljonhson.obj",function( object ){
character = object;if( asynchFinished()) initArmy();//HERE});});}function loadothermodels (){const loader =new THREE.GLTFLoader();const onLoad =( gltf, position )=>{const model = gltf.scene.children[0];
model.position.copy( position );ABWorld.scene.add( model );};const balles =new THREE.Vector3(-6.5,2.2,-6.7);const ogloc =new THREE.Vector3(-15.5,2.2,0);const cj =new THREE.Vector3(-15.5,2.2,21);const family =new THREE.Vector3(-10.5,2.2,15);const car =new THREE.Vector3(-17.5,2.0,0);const car2 =new THREE.Vector3(-17.5,2.0,20);const pizza =new THREE.Vector3(0,8,0);
loader.load ('/uploads/issafae2/ballas1.glb', gltf => onLoad ( gltf, balles ));
loader.load ('/uploads/issafae2/ogloc.glb', gltf => onLoad ( gltf, ogloc ));
loader.load ('/uploads/issafae2/carl_jonhson_cj.glb', gltf => onLoad ( gltf, cj ));
loader.load ('/uploads/issafae2/grove_families_gang.glb', gltf => onLoad ( gltf, family ));
loader.load ('/uploads/issafae2/the_well_stacked_pizza_co.glb', gltf => onLoad ( gltf, pizza ));
loader.load ('/uploads/issafae2/greenwood_gta_sa.glb', gltf => onLoad ( gltf, car ));
loader.load ('/uploads/issafae2/blade_gta_sa.glb', gltf => onLoad ( gltf, car2 ));}function asynchFinished()// all file loads returned {if( character )returntrue;elsereturnfalse;for(var i =0; i < FILE_ARRAY.length; i++)if(! textureArray[i])returnfalse;returntrue;}function initArmy()// called when all textures ready {
character.position.y =2.2;
character.position.x =0;
character.position.z =-20;
character.scale.multiplyScalar ( SCALE_HERO );// scale it ABWorld.scene.add( character );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,1);// first and second pictures are used
theobject.material =new THREE.MeshBasicMaterial({ map: textureArray[r]});ABWorld.scene.add(theobject);
THEARMY[t]= theobject;// save it for later
t++;}// can start the run loopABWorld.render();
AB.runReady =true;
AB.msg (`<hr><p>Multi-user world.Click buttons to change boxes on all users' machines.Drag the camera.<p><button onclick='cj();'class=ab-largenormbutton > CJ </button><button onclick='bigsmoke();'class=ab-largenormbutton >BigSmoke</button><p><button onclick='ryder();'class=ab-largenormbutton >Ryder</button><button onclick='sweet();'class=ab-largenormbutton >Sweet</button><p><hr><h1>ChatHere!</h1><div style="width:20vw; margin: 'auto'; padding:'auto';"><h3>Me</h3><p><INPUT style="width:13vw;" id=me ><button onclick="sendchat();"class=ab-normbutton >Send</button><p><br><hr><h3>Them</h3><div id=them ></div><br><hr><h3>Who's online:</h3><div id=themlist ></div><br></div><p>`);const me = document.getElementById('me').onkeydown =function(event){if(event.keyCode ==13) sendchat();};
console.log('me');
AB.removeLoading();}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]);}}}// UP moves forward in whatever angle you are at// LEFT/RIGHT rotate by small angle//const KEY_UP =38;const KEY_LEFT =37;const KEY_RIGHT =39;var KEYS =[ KEY_UP, KEY_LEFT, KEY_RIGHT ];function ourKeys ( event ){return( KEYS.includes ( event.keyCode ));}function keyHandler ( event ){if(! AB.runReady )returntrue;// not ready yet if(! ourKeys ( event ))returntrue;// else handle it and prevent default:if( event.keyCode == KEY_UP )// move a bit along angle we are facing{
character.position.x = character.position.x +( CHAR_STEP *Math.sin(characterRotation));
character.position.z = character.position.z +( CHAR_STEP *Math.cos(characterRotation));}if( event.keyCode == KEY_LEFT )// rotate in place {
characterRotation = characterRotation + ROTATE_AMOUNT;
character.rotation.set(0, characterRotation,0);}if( event.keyCode == KEY_RIGHT ){
characterRotation = characterRotation - ROTATE_AMOUNT;
character.rotation.set(0, characterRotation,0);}
event.stopPropagation(); event.preventDefault();returnfalse;}
AB.world.newRun =function(){
AB.loadingScreen();
AB.newSplash();
AB.splashHtml (`<h1>EnterPassword</h1>Only certain users can join.<br>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 // calls initArmy() when it returns
loadothermodels();
initLights();/*var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
ABWorld.scene.add(thelight);*/ABWorld.scene.background =new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY,function(){ABWorld.render();
AB.removeLoading();
AB.runReady =true;// start the run loop});
document.onkeydown = keyHandler;};
AB.world.nextStep =function(){const delta = clock.getDelta();
mixers.forEach(( mixer )=>{ mixer.update( delta );});
moveArmy();};function initLights(){const ambientLight =new THREE.AmbientLight(0xffffff,1);ABWorld.scene.add( ambientLight );const frontLight =new THREE.DirectionalLight(0xffffff,1);
frontLight.position.set(100,100,100);const backLight =new THREE.DirectionalLight(0xffffff,1);
backLight.position.set(-50,50,-50);ABWorld.scene.add( frontLight, backLight );}//========= Socket functionality=============//=========start Socket============function alphanumeric ( str )//check if the password is alphanumeric{var pass = str.match(/^[0-9a-zA-Z]+$/);// start of line "[0-9a-zA-Z]+" end of line if( pass )returntrue;elsereturnfalse;}function start()// start the game if the user have entered a password{var password = jQuery("input#p").val();
password = password.trim();//If the password is not alphanumeric, show an error statment to the userif(! 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();}//=======Buttons work==========// functions called by buttonsfunction cj(){ changeBox(2); AB.socketOut (2);}function bigsmoke(){ changeBox(3); AB.socketOut (3);}function ryder(){ changeBox(4); AB.socketOut (4);}function sweet(){ changeBox(5); AB.socketOut (5);}function changeBox(n)// change a random box to texture n (5 or 6) {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);//moveCharacter(n);};//=====Chatting Feature=======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}// given AB userid, username, construct a link to that user // if not run logged in, userid = "none"function userlink ( userid, username ){if( userid =="none")return("Unknown user");elsereturn("<a target='_blank' href='https://ancientbrain.com/user.php?userid="+ userid +"'>"+ username +"</a>");}// When someone else enters text, server will trigger AB.socketIn.
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> ");};//======Music=======const music ='/uploads/issafae2/GTAV-SanAndreasIntroRemake.mp3';// adding music in the background
AB.backgroundMusic ( music );