// Cloned by Tara on 15 Nov 2023 from World "Mighty Spread" by Gareth Hogan
// Please leave this clone trail here.
// Cloned by Jack on 17 Nov 2022 from World "Websockets Project" by Gareth Hogan
// Please leave this clone trail here.
// Cloned by Gareth Hogan on 12 Nov 2022 from World "Websockets boxes" by Starter user
// Please leave this clone trail here.
//--------------------------------------------------------------------------------------------------------------
// CONSTS AND VARS
//--------------------------------------------------------------------------------------------------------------
AB.clockTick = 100; // Speed of run: Step every n milliseconds. Default 100.
AB.maxSteps = 300; // 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.
// Source: https://minecraft.fandom.com/wiki/List_of_block_textures
// '/uploads/gareth22/example.jpg'
const FILE_ARRAY = [
"/uploads/gareth22/dirt.jpg",
"/uploads/gareth22/diamond.jpg",
"/uploads/gareth22/redstone.jpg",
"/uploads/gareth22/redglass.jpg",
"/uploads/gareth22/blueglass.jpg",
"/uploads/gareth22/blorb.jpg",
"/uploads/gareth22/rorb.jpg"
];
//skybox array
// http://stemkoski.github.io/Three.js/#skybox
// https://jaxry.github.io/panorama-to-cubemap/
const SKYBOX_ARRAY = [
"/uploads/gareth22/px-min.png",
"/uploads/gareth22/nx-min.png",
"/uploads/gareth22/py-min.png",
"/uploads/gareth22/ny-min.png",
"/uploads/gareth22/pz-min.png",
"/uploads/gareth22/nz-min.png"
];
const SKYCOLOR = 0x000000; // colour of the sky
const gridsize = 10; // number of squares along side of world
const objectsize = 100; // size of the objects
const NumBoxes = gridsize*gridsize; // calculate the number of boxes that will be in the grid
const MAXPOS = gridsize * objectsize; // start things within these bounds
const startRadiusConst = MAXPOS; // distance from centre to start the camera at
const maxRadiusConst = MAXPOS * 5 ; // maximum distance from camera we will render things
// constants used in the grid, each square has one number to show state, you can see what each number means here
const GRID_BLANK = 0;
const GRID_USER1 = 1;
const GRID_USER2 = 4;
const GRID_COLOUR1 = 2;
const GRID_COLOUR2 = 3;
// each action has an associated number
const ACTION_LEFT = 0;
const ACTION_RIGHT = 1;
const ACTION_UP = 2;
const ACTION_DOWN = 3;
// setup camera
ABHandler.MAXCAMERAPOS = MAXPOS;
ABWorld.drawCameraControls = false;
AB.drawRunControls = false;
ABHandler.GROUNDZERO = true;
// Variables to be used
var step; // timer
var textureArray = new Array(FILE_ARRAY.length);
var GRID = new Array(gridsize);
var ai, aj; //player 1
var bi, bj; //player 2
var player1, player2; // the orb objects
//--------------------------------------------------------------------------------------------------------------
// PRELOAD RESOURCES
//--------------------------------------------------------------------------------------------------------------
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(); // initialize the scene when finished loading
});
}
function asynchFinished() // all file loads returned
{
for ( var i = 0; i < FILE_ARRAY.length; i++ )
{
if(!textureArray[i])
return false;
}
return true;
}
function splashHTML() // HTML string to be shown in the splash
{
string = "Colour the floor, QUICKLY! <br><font color=red><b>Red is the arrow keys</b></font> <br><font color=blue><b>Blue is WASD</b></font><br> Colour the most squares before time is up to win!";
return(string);
}
AB.newSplash(splashHTML()); // show the splash
AB.splashClick(function() // when the start is pressed
{
AB.removeSplash(); // remove splash screen
// ready to start run loop?
splashClicked = true;
step = 0;
ABWorld.render();
AB.runReady = true;
AB.socketOut("Start"); // send out start command to all worlds so they remove the splash
if(asynchFinished())
AB.runReady = true; // start run loop
});
//--------------------------------------------------------------------------------------------------------------
// SETUP GRID
//--------------------------------------------------------------------------------------------------------------
function initScene() // called when all textures ready
{
var i, j, shape, theobject, borb, rorb; // various variables used in the grid
for (i = 0; i < gridsize ; i++) // setting up the lists inside the grid list
GRID[i] = new Array(gridsize);
// MAKING CUBES
for (i = 0; i < gridsize ; i++){
for (j = 0; j < gridsize ; j++){
GRID[i][j] = GRID_BLANK; // default cube is blank
shape = new THREE.BoxGeometry(objectsize, objectsize, objectsize);
theobject = new THREE.Mesh(shape);
theobject.material = new THREE.MeshBasicMaterial({map: textureArray[0]}); // textureArray[0] is for blank
theobject.position.copy(translate(i,j)); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(theobject);
}
}
// PLAYER ORBS
borb = new THREE.SphereGeometry(50,32,16); // blue orb
rorb = new THREE.SphereGeometry(50,32,16); // red orb
player1 = new THREE.Mesh(borb);
player2 = new THREE.Mesh(rorb);
player1.material = new THREE.MeshBasicMaterial({map: textureArray[5]});
player2.material = new THREE.MeshBasicMaterial({map: textureArray[6]});
position1 = translateOrb(0,0) // placing it top left
player1.position.copy(position1)
position2 = translateOrb(9,9) // placing it bottom right
player2.position.copy(position2)
ABWorld.scene.add(player1);
ABWorld.scene.add(player2);
//positions of the players
ai = 0;
aj = 0;
bi = 9;
bj = 9;
GRID[ai][aj] = GRID_USER1;
GRID[bi][bj] = GRID_USER2;
ABWorld.scene.background = new THREE.CubeTextureLoader().load(SKYBOX_ARRAY, function()
{
ABWorld.render();
});
AB.removeLoading(); // remove loading screen
}
function translate(i, j) // translte x y coord into a 3D vector
{
var v = new THREE.Vector3();
v.y = 0;
v.x = (i * objectsize) - (MAXPOS/2);
v.z = (j * objectsize) - (MAXPOS/2);
return v;
}
function translateOrb(i, j) // translate for the ORB as they need to be one layer above the rest
{
var v = new THREE.Vector3();
v.y = 100;
v.x = (i * objectsize) - (MAXPOS/2);
v.z = (j * objectsize) - (MAXPOS/2);
return v;
}
function occupied(i, j) // is this square occupied
{
return(GRID[i][j] == GRID_USER1 || GRID[i][j] == GRID_USER2);
}
//--------------------------------------------------------------------------------------------------------------
// MOVEMENT
//--------------------------------------------------------------------------------------------------------------
// arrow keys : LEFT, UP, RIGHT, DOWN, A, W, D, S
var OURKEYS = [ 37, 38, 39, 40, 65, 87, 68, 83];
function ourKeys(event) { // checks if a key pressed is either WASD or arrow keys
return(OURKEYS.includes(event.keyCode));
}
function keyHandler(event) // what to do when a key is pressed
{
if(!AB.runReady)return true;
if(!ourKeys(event)) return true;
// cases for each different key
// socketsender first sends to other worlds what to do, then does it locally using moveUser
if(event.keyCode == 65) socketSender(ACTION_LEFT, 1);
if(event.keyCode == 87) socketSender(ACTION_DOWN, 1);
if(event.keyCode == 68) socketSender(ACTION_RIGHT, 1);
if(event.keyCode == 83) socketSender(ACTION_UP, 1);
if(event.keyCode == 37) socketSender(ACTION_LEFT, 2);
if(event.keyCode == 38) socketSender(ACTION_DOWN, 2);
if(event.keyCode == 39) socketSender(ACTION_RIGHT, 2);
if(event.keyCode == 40) socketSender(ACTION_UP, 2);
event.stopPropagation(); event.preventDefault(); return false;
}
function moveUser(action, player) // moving the player, takes in the action to do, and which player to do it to
{
if(player == 1){ // ai aj is where the player was, i j will be where they move to
var i = ai;
var j = aj;
}
else if(player == 2){
var j = bj;
var i = bi;
}
if (action == ACTION_LEFT ){
if(i > 0) i--;
else return;
}
else if (action == ACTION_RIGHT ){
if(i < 9) i++;
else return;
}
else if (action == ACTION_UP ){
if(j < 9) j++;
else return;
}
else if (action == ACTION_DOWN){
if(j > 0) j--;
else return;
}
if (!occupied(i,j)) // else just miss a turn cause you can't move into that space
{
if(player == 1){ //diamond
GRID[ai][aj] = GRID_COLOUR1; // change colour of square you are moving off of
shape = new THREE.BoxGeometry(objectsize, objectsize, objectsize);
theobject = new THREE.Mesh(shape);
theobject.material = new THREE.MeshBasicMaterial({map: textureArray[1]});
theobject.position.copy(translate(ai,aj)); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(theobject);
position = translateOrb(i,j) // move their orb
player1.position.copy(position)
GRID[i][j] = GRID_USER1; // new square the player is standing on
shape = new THREE.BoxGeometry(objectsize, objectsize, objectsize);
theobject = new THREE.Mesh(shape);
theobject.material = new THREE.MeshBasicMaterial({map: textureArray[4]}); // where user is
theobject.position.copy(translate(i,j)); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(theobject);
}
else if(player == 2){ //redstone
GRID[bi][bj] = GRID_COLOUR2;
shape = new THREE.BoxGeometry(objectsize, objectsize, objectsize);
theobject = new THREE.Mesh(shape);
theobject.material = new THREE.MeshBasicMaterial({map: textureArray[2]});
theobject.position.copy(translate(bi,bj)); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(theobject);
position = translateOrb(i,j)
player2.position.copy(position)
GRID[i][j] = GRID_USER2;
shape = new THREE.BoxGeometry(objectsize, objectsize, objectsize);
theobject = new THREE.Mesh(shape);
theobject.material = new THREE.MeshBasicMaterial({map: textureArray[3]}); // where user is
theobject.position.copy(translate(i,j)); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(theobject);
}
if(player == 1){ // now they are moved, update the coords where they are
ai = i;
aj = j;
}
else if(player == 2){
bi = i;
bj = j;
}
}
}
//--------------------------------------------------------------------------------------------------------------
// AB WORLD FUNCTIONS
//--------------------------------------------------------------------------------------------------------------
AB.world.newRun = function()
{
AB.loadingScreen();
AB.runReady = false;
ABWorld.init3d(startRadiusConst, maxRadiusConst, SKYCOLOR );
loadResources(); // aynch file loads, calls initScene() when it returns
document.onkeydown = keyHandler;
step = 0;
};
function updatestatus(){ // this shows the timer
var status = "<font size=5 face=verdana> Time: <br>" + step + "</font>" ;
$("#user_span3").html(status);
};
AB.world.nextStep = function()
{
step++; // each step the timer goes up and is shown
updatestatus();
};
function countScore(){ // function to calculate score at the end of the game
scoreP1 = 1;
scoreP2 = 1;
for (i = 0; i < gridsize ; i++){
for (j = 0; j < gridsize ; j++){
if (GRID[i][j] == 2){
scoreP1 += 1;
}
else if(GRID[i][j] == 3){
scoreP2 += 1;
}
}
}
console.log(scoreP1)
console.log(scoreP2)
if(scoreP1 > scoreP2){
winner = "<br> <font color=blue> <B> The winner is ... PLAYER 1 </B> </font>"
}
else if (scoreP2 > scoreP1){
winner = "<br> <font color=red> <B> The winner is ... PLAYER 2 </B> </font>"
}
else{
winner = "<br> <font color=green> <B> The game was a Draw</B> </font>"
}
return(winner);
}
AB.world.endRun = function() // at the end of the run
{
if(step == 300)
{
$("#user_span4").html("<font size=5 face=verdana> <B>TIME UP</B> </font>");
}
winner = countScore();
AB.msg(winner, 7) // shows the winner
};
//--------------------------------------------------------------------------------------------------------------
// SOCKETS
//--------------------------------------------------------------------------------------------------------------
AB.socketStart(); // start sockets
AB.socketIn = function(data) // incoming data on socket, i.e. clicks of other player
{
if(typeof data === "string") // a string signals to take down the splash for all screens
{
AB.removeSplash();
splashClicked = true;
step = 0;
ABWorld.render();
AB.runReady = true;
}
else // it will be a list containing the player and move to do
{
action = data[0]
player = data[1]
moveUser(action, player);
}
};
function socketSender(action, player) // function to send out the move to do, then do the move locally
{
data = [action, player];
AB.socketOut(data);
moveUser(action, player);
};