// Cloned by Okikiola Sanni on 3 Dec 2022 from World "Port of flying birds " by Discover three.js
// Please leave this clone trail here.
// Port of flying birds from discoverthreejs.com
// "Final code from Ch 1.7 of Discover three.js"
// https://codesandbox.io/s/github/looeee/discoverthree.com-examples/tree/master/1-first-steps/7-load-models?from-embed
// const startRadius = 150; // distance from centre we start the camera at
// const maxRadius = startRadius * 20; // maximum distance from camera we render things
// // can load skybox of other user:
// const SKYBOX_ARRAY = [
// "/uploads/starter/dawnmountain-xpos.png",
// "/uploads/starter/dawnmountain-xneg.png",
// "/uploads/starter/dawnmountain-ypos.png",
// "/uploads/starter/dawnmountain-yneg.png",
// "/uploads/starter/dawnmountain-zpos.png",
// "/uploads/starter/dawnmountain-zneg.png"
// ];
// Cloned by Okikiola Sanni on 13 Nov 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 = 150;
// 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/happy.mp3"
const FILE_ARRAY = [
"/uploads/starter/earth.1.jpg", // array 0 to 4 are the earths
"/uploads/starter/earth.2.jpg",
"/uploads/starter/earth.3.jpg",
"/uploads/starter/earth.4.jpg",
"/uploads/starter/earth.5.jpg",
"/uploads/starter/baby.1.png", // array 5
"/uploads/starter/ghost.3.png" // array 6
];
// const SKYCOLOR = 0xccffcc; // a number, not a string
// can load skybox of other user:
const SKYBOX_ARRAY = [
"/uploads/starter/dawnmountain-xpos.png",
"/uploads/starter/dawnmountain-xneg.png",
"/uploads/starter/dawnmountain-ypos.png",
"/uploads/starter/dawnmountain-yneg.png",
"/uploads/starter/dawnmountain-zpos.png",
"/uploads/starter/dawnmountain-zneg.png"
];
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 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++;
}
// can start the run loop
// ABWorld.scene.background = new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY, function()
// {
// ABWorld.render();
// AB.removeLoading();
// AB.runReady = true;
// });
ABWorld.render();
AB.removeLoading();
AB.runReady = true;
let listElement = document.createElement("li");
listElement.innerHTML
AB.msg ( ` <hr> <p> Multiplayer game. Pick a side. Click buttons to change boxes on all users' machines. Drag the camera. <p>
<button onclick='baby();' class=ab-largenormbutton > Baby </button>
<button onclick='skull();' class=ab-largenormbutton > Skull </button>
` );
AB.msg (` <p>
<h1> Chat Opponent </h1>
If you are logged in, chat is tagged with your name.
<p>
<div style="width:60vw; background-color:#add8e6; border: 1px solid black; margin:5; padding: 10px;">
<h3> Me </h3>
<INPUT style="width:30vw;" 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> Users online </h3>
<div id=themlist > </div>
</div>
`)
}
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
// calls initArmy() when it returns
// var ambient = new THREE.AmbientLight(); // light
// ABWorld.scene.add( ambient );
// var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, -1 );
// thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
// ABWorld.scene.add(thelight);
};
AB.world.nextStep = function()
{
moveArmy();
};
//--- Socket functionality -----------------------------------------------------
// start socket
AB.socketStart();
// functions called by buttons
// baby and skull are textures 5 and 6 in the array:
function baby() { changeBox(5); AB.socketOut (5); }
function skull() { changeBox(6); AB.socketOut (6); }
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
{
changeBox(n);
};
// --- music and sound effects ----------------------------------------
// var backmusic = AB.backgroundMusic ( MUSIC_BACK );
// function musicPlay() { backmusic.play(); }
// function musicPause() { backmusic.pause(); }
// Enter will also send chat:
document.getElementById('me').onkeydown = function(event) { if (event.keyCode == 13) sendchat(); };
// // --- 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
}
// 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 ( "Anon user" );
else return ( "<a target='_blank' href='https://ancientbrain.com/user.php?userid=" + userid + "'>" + username + "</a>" );
}
// --- re-define AB socket handler functions -------------------------------------
// re-define these handler functions from default (which is do nothing)
// When someone else enters text, server will trigger AB.socketIn.
// Here I define what to do for it.
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> " );
};