// Creates a new Scenevar scene =new THREE.Scene();// =============================================================================================// =============================================================================================// AUDIO// =============================================================================================// =============================================================================================const SPEED1 ="/uploads/duarte/swingx1.mp3";const SPEED2 ="/uploads/duarte/swingx1.25.mp3";const SPEED3 ="/uploads/duarte/swingx1.5.mp3";const HONK ="/uploads/duarte/car-honk.mp3";const SPEEDUP ="/uploads/duarte/speed-up.mp3";var speed1 =newAudio(SPEED1);var speed2 =newAudio(SPEED2);var speed3 =newAudio(SPEED3);var honk =newAudio(HONK);var speedUp =newAudio(SPEEDUP);var isMuted =false;// Function to sets volume of each soundfunction setVolume(){if(isMuted){// Mutes all sounds
speed1.volume =0;
speed2.volume =0;
speed3.volume =0;
honk.volume =0;
speedUp.volume =0;}else{// Unmutes all sounds
speed1.volume =0.1;
speed2.volume =0.1;
speed3.volume =0.1;
honk.volume =0.1;
speedUp.volume =0.1;}}// Calls function to set volumes
setVolume();// =============================================================================================// =============================================================================================// VARIABLES// =============================================================================================// =============================================================================================
let password ="753";// Scoreboard Variablesvar score1 =0;var hscore1 =0;var score2 =0;var hscore2 =0;// Lanes variablesconst laneTypes =['car','forest'];const vechicleColors =[0xFFFFFF,0x000000,0xFF0000,0xFFFF00,0x0000FF,0x9400D3,0xFFA500];const laneSpeeds =[2,3,5,6,7];const bushHeights =[15,25,35];const rocksHeights =[5,7,12];
let lanes;
let currentLane;
let currentColumn;// Movement Variableconst positionWidth =42;const columns =17;const boardWidth = positionWidth * columns;const stepTime =50;const zoom =2;
let previousTimestamp;
let startMoving;
let moves;
let stepStartTimestamp;// Player settingsvar player;const playerSize =15;// Lights Settingsconst initialDirLightPositionX =-100;const initialDirLightPositionY =-100;var d =500;// Websockets Variablesvar host;var playerJoined =false;var playerLeft =false;var lockControls =false;var clock;// Manages Sound Speed UPsvar onSpeedUp1 =true;var onSpeedUp2 =true;// =============================================================================================// =============================================================================================// CAMERA// =============================================================================================// =============================================================================================const distance =500;// Distance for the camera// Creates camera and assigns positionconst camera =new THREE.OrthographicCamera(window.innerWidth /-2, window.innerWidth /2, window.innerHeight /2, window.innerHeight /-2,0.1,10000);
camera.rotation.x =50*Math.PI /180;
camera.rotation.y =20*Math.PI /180;
camera.rotation.z =10*Math.PI /180;const initialCameraPositionY =-Math.tan(camera.rotation.x)* distance;const initialCameraPositionX =Math.tan(camera.rotation.y)*Math.sqrt(distance **2+ initialCameraPositionY **2);
camera.position.y = initialCameraPositionY;
camera.position.x = initialCameraPositionX;
camera.position.z = distance;// =============================================================================================// =============================================================================================// INITIALISE VALUES// =============================================================================================// =============================================================================================const initaliseValues =()=>{// Function to initialise values
player =newPlayer();// Creates player
scene.add(player);// Adds the player to the scene
hemiLight =new THREE.HemisphereLight(0xffffff,0xffffff,0.6);// Creates a new Hemisphere Light
scene.add(hemiLight);// Adds the Hemisphere Light to the scene
dirLight =new THREE.DirectionalLight(0xffffff,0.6);// Creates a new Directional Light
dirLight.position.set(initialDirLightPositionX, initialDirLightPositionY,200);// Sets the Directional Light position
dirLight.target = player;// Sets the Directional Light target
scene.add(dirLight);// Adds the Directional Light to the scene
backLight =new THREE.DirectionalLight(0x000000,.4);// Creates a new Directional Light
backLight.position.set(200,200,50);// Sets the Directional Light position
scene.add(backLight);// Adds the Directional Light to the scene
lanes = generateLanes();// Generates the lanes
currentLane =0;// Sets the current lane to 0
currentColumn =Math.floor(columns /2);// Sets the current column to the middle of the board
previousTimestamp =null;// Sets the previous timestamp to null
startMoving =false;// Sets start moving to false
moves =[];// Sets moves to an empty array
stepStartTimestamp;// Sets the step start timestamp to null
player.position.x =0;// Sets the player position to 0
player.position.y =0;// Sets the player position to 0
camera.position.y = initialCameraPositionY;// Sets the camera position to the initial camera position
camera.position.x = initialCameraPositionX;// Sets the camera position to the initial camera position
dirLight.position.x = initialDirLightPositionX;// Sets the Directional Light position to the initial Directional Light position
dirLight.position.y = initialDirLightPositionY;// reset lighting to follow player
requestAnimationFrame(animate);// Calls the animate function
updateHtml();// Calls the update html function
speed1.play();// Plays the speed 1 sound}// =============================================================================================// =============================================================================================// LOAD RESOURCES // =============================================================================================// =============================================================================================function loadResources(){// Function to load resources
AB.socketStart();// Calls the socket start function
AB.removeSplash();// Removes the splash screenif(playerJoined){// If the player has joined
initaliseValues();// Calls the initialise values function}else{// If the player has not joined
waitingForPlayers();// Calls the waiting for players function}}// =============================================================================================// =============================================================================================// GAME // =============================================================================================// ============================================================================================= const renderer =new THREE.WebGLRenderer({// Creates renderer
alpha:true,
antialias:true});
renderer.shadowMap.enabled =true;// Enables shadows
renderer.shadowMap.type = THREE.PCFSoftShadowMap;// Soft shadows
renderer.setSize(window.innerWidth, window.innerHeight);// Sets size of renderer
document.body.appendChild(renderer.domElement);// Adds renderer to the body of the document// =============================================================================================// =============================================================================================// SOCKETS // =============================================================================================// =============================================================================================// function to handle socket out to the other userfunction socketOut(_name, _joined, _closed){
AB.socketOut({
name: _name,
data:{
joined: _joined,
closed: _closed,
host: host,
clock: clock,
score2: score1,
hscore2: hscore1,}});}// =============================================================================================// =============================================================================================// ON USER LEAVE // =============================================================================================// =============================================================================================
window.onbeforeunload =function(){
socketOut("playerLeft",false,true);// Remove player from game when leaving the page}// =============================================================================================// =============================================================================================// SOCKET USER LIST // =============================================================================================// =============================================================================================
AB.socketUserlist =function(e){// Function to handle socket user list// console.log(e); // Logs the eventif(e.length %2===0&&!playerJoined && host ===undefined){// If there are an even number of players and the player has not joined and the host is undefined// Join game
host =Math.floor(Math.random()*999999999);// Generate random host number// console.log(host); // Set host
playerJoined =true;// Sets player joined to true
initaliseValues();// Calls the initialise values function
AB.removeSplash();// Removes the splash screen
clock =newClock();// Creates a new clock
clock.start();// Starts the clock
socketOut("triggerJoin",true,false);// Send socket to other player// console.log("Join game");}elseif(e.length %2===1&&!playerJoined && host ===undefined){// If there is an odd number of players and the player has not joined and the host is undefined// Create game
host = AB.socket.id;// Sets the host to the socket id// console.log("Create game"); // Creates a new}switch(e.length){// Switch statement to handle the number of playersdefault:// Default caseif(void0!== host){// If the host is not undefined
socketOut("start",false,true);// Calls the socket out function}}
AB.msg("<p>Current number of players = "+ e.length +"\n</p>",2);// Display number of players}// =============================================================================================// =============================================================================================// SOCKET IN // =============================================================================================// =============================================================================================
AB.socketIn =function(element){// console.log("TRIGGER SOCKET IN ", element.name, element.data);if(element.name ==="playerLeft"&& element.data.host === host && playerJoined){
lockControls =true;// Lock controls
playerLeft =true;// Player left
AB.socket.destroy();// Destroy socket
endGame();// End game
resetPlayer();// Reset player}elseif(element.name ==="update"&& element.data.host === host && playerJoined){
score2 = element.data.score2;// Updates score
hscore2 = element.data.hscore2;// Updates highscore
updateHtml();// update HTML side screen}elseif(element.name ==="triggerJoin"&& element.data.joined ===true&&!playerJoined){
playerJoined =true;// sets playerJoined to true
clock = element.data.clock;// gets clock from host
host = element.data.host;// gets host id
initaliseValues();// starts game
AB.removeSplash();// removes splash screen}elseif(element.name ==="clockUpdate"&& element.data.host === host && playerJoined){
clock = element.data.clock;// Update clock
AB.msg("<p>Time left: <strong>"+ clock.currTime +"</strong> seconds</p>",5);// Update time left screen}elseif(element.name ==="endGame"&& element.data.host === host && playerJoined){// End game
score2 = element.data.score2;// Update score
hscore2 = element.data.hscore2;// Update highscore
lockControls =true;// Lock controls
AB.socket.destroy();// Destroy socket
endGame();// End game
resetPlayer();// Reset player}};// =============================================================================================// =============================================================================================// CLOCK // =============================================================================================// =============================================================================================classClock{// Clock class
constructor(){// Constructor this.currTime =60// Current time}
start(){// Start clockvar clock =this;// Set clock to thisfunction _time(){// Function to update time
clock.currTime--;if(clock.currTime <0){
socketOut("endGame",false,false);// Send end game to other player
lockControls =true;// Lock controls
AB.socket.destroy();// Destroy socket
endGame();// End game
resetPlayer();// Reset playerreturn;// Stop clock}
AB.msg("<p>Time left: <strong>"+ clock.currTime +"</strong> seconds</p>",5);// Update time
socketOut("clockUpdate",false,false);// Send clock update to other player}
setInterval(_time,1000);// Set interval to 1 second}}// =============================================================================================// =============================================================================================// PLAYER // =============================================================================================// =============================================================================================functionPlayer(){// Player classconst player =new THREE.Group();// Create a group to hold the playerconst body =new THREE.Mesh(// Create the body new THREE.BoxBufferGeometry((playerSize -1)* zoom,(playerSize -1)* zoom,(playerSize -1)* zoom),new THREE.MeshPhongMaterial({ color:0xF0B8A0}));
body.position.z =10* zoom;// Move the body up
body.position.z +=15;// Move the body up
player.add(body);// Adds the body to the groupconst hair =new THREE.Mesh(// Create the hairnew THREE.BoxBufferGeometry((playerSize -0.8)* zoom,(playerSize -0.8)* zoom,2* zoom),new THREE.MeshLambertMaterial({ color:0x000000}));
hair.position.z =21* zoom;// Move the hair up
hair.position.z +=6;// Move the hair up
player.add(hair);// Add the hair to the groupconst pants =new THREE.Mesh(// Create the pantsnew THREE.BoxBufferGeometry(playerSize * zoom, playerSize * zoom,20* zoom),new THREE.MeshLambertMaterial({ color:0x435F9A}));
pants.position.z =10* zoom /2;// Move the pants up
player.add(pants);// Add the pants to the groupreturn player;// Returns the group}// =============================================================================================// =============================================================================================// CAR // =============================================================================================// =============================================================================================functionWheel(){// Wheelconst wheel =new THREE.Mesh(// Creates a new mesh for the wheelnew THREE.BoxBufferGeometry(12* zoom,33* zoom,12* zoom),// Creates a new box geometry for the wheelnew THREE.MeshLambertMaterial({ color:0x333333})// Creates a new material for the wheel);
wheel.position.z =6* zoom;// Sets the position of the wheelreturn wheel;// Returns the wheel}functionCar(){const car =new THREE.Group();// Creates a new groupconst carFrontTexture =newTexture(40,80,[{ x:0, y:10, w:30, h:60}]);// Creates a new textureconst carBackTexture =newTexture(40,80,[{ x:10, y:10, w:30, h:60}]);// Creates a new textureconst carRightSideTexture =newTexture(110,40,[{ x:10, y:0, w:50, h:30},{ x:70, y:0, w:30, h:30}]);// Creates a new textureconst carLeftSideTexture =newTexture(110,40,[{ x:10, y:10, w:50, h:30},{ x:70, y:10, w:30, h:30}]);// Creates a new textureconst color = vechicleColors[Math.floor(Math.random()* vechicleColors.length)];// Randomly selects a color from the arrayconst main =new THREE.Mesh(// Creates a new meshnew THREE.BoxBufferGeometry(60* zoom,30* zoom,15* zoom),// Creates a new box buffer geometrynew THREE.MeshPhongMaterial({ color })// Creates a new material);// Creates a new mesh
main.position.z =12* zoom;// Sets the position of the mesh
car.add(main)// Adds the mesh to the groupconst cabin =new THREE.Mesh(// Creates a new meshnew THREE.BoxBufferGeometry(33* zoom,24* zoom,12* zoom),// Creates a new box[new THREE.MeshPhongMaterial({ color:0xcccccc, map: carBackTexture }),// Creates a new materialnew THREE.MeshPhongMaterial({ color:0xcccccc, map: carFrontTexture }),// Creates a new materialnew THREE.MeshPhongMaterial({ color:0xcccccc, map: carRightSideTexture }),// Creates a new materialnew THREE.MeshPhongMaterial({ color:0xcccccc, map: carLeftSideTexture }),// Creates a new materialnew THREE.MeshPhongMaterial({ color:0xcccccc,}),// topnew THREE.MeshPhongMaterial({ color:0xcccccc,})// bottom]);
cabin.position.x =6* zoom;// Sets the position of the mesh
cabin.position.z =25.5* zoom;// Sets the position of the mesh
car.add(cabin);// Adds the mesh to the groupconst frontWheel =newWheel();// Creates a new wheel
frontWheel.position.x =-18* zoom;// Sets the position of the wheel
car.add(frontWheel);// Adds the wheel to the groupconst backWheel =newWheel();// Creates a new wheel
backWheel.position.x =18* zoom;// Sets the position of the wheel
car.add(backWheel);// Adds the wheel to the groupreturn car;// Returns the group}// =============================================================================================// =============================================================================================// BUSH// =============================================================================================// =============================================================================================functionBush(){// Creates a new bush objectconst bush =new THREE.Group();// Creates a new group
height = bushHeights[Math.floor(Math.random()* bushHeights.length)];// Randomly selects a height for the bushconst leaf =new THREE.Mesh(// Creates a new mesh for the leavesnew THREE.BoxBufferGeometry(30* zoom,30* zoom, height * zoom),// Creates a box with the size of the bushnew THREE.MeshLambertMaterial({ color:0x37AE0F,})// Creates a material for the leaves);
leaf.position.z =(height /2)* zoom;// Sets the position of the leaves
bush.add(leaf);// Adds the leaves to the bushreturn bush;// Returns the bush}// =============================================================================================// =============================================================================================// ROCK// =============================================================================================// =============================================================================================functionRock(){// Create a new rock objectconst rocks =new THREE.Group();// Create a new group for the rock
height = rocksHeights[Math.floor(Math.random()* bushHeights.length)];// Get a random height for the rockconst rock =new THREE.Mesh(// Create the rocknew THREE.BoxBufferGeometry(12* zoom,12* zoom, height * zoom),// Create a box with the random heightnew THREE.MeshLambertMaterial({ color:0x666a6c,})// Set the color of the rock);
rock.position.z =(height /2)* zoom;// Set the position of the rock
rocks.add(rock);// Add the rock to the groupreturn rocks;// Return the group}// =============================================================================================// =============================================================================================// ROAD// =============================================================================================// =============================================================================================functionRoad(){// Creates a new road object const road =new THREE.Group();// Creates a new group for the roadconst createSection = color =>new THREE.Mesh(// Creates a new section of the roadnew THREE.PlaneBufferGeometry(boardWidth * zoom, positionWidth * zoom),// Creates a new plane with the width of the board and the height of the positionnew THREE.MeshPhongMaterial({ color })// Creates a new material with the color of the road);const middle = createSection(0x454A59);// Creates the middle section of the road
road.add(middle);// Adds the middle section to the roadconst left = createSection(0x393D49);// Creates a new section
left.position.x =- boardWidth * zoom;// Moves the section to the left
road.add(left);// Adds the section to the roadconst right = createSection(0x393D49);// Creates a new section
right.position.x = boardWidth * zoom;// Moves the section to the right
road.add(right);// Adds the section to the roadreturn road;// Returns the road}// =============================================================================================// =============================================================================================// GRASS// =============================================================================================// =============================================================================================functionGrass(){// Creates a new grass objectconst grass =new THREE.Group();// Creates a new groupconst createSection = color =>new THREE.Mesh(// Creates a new sectionnew THREE.BoxBufferGeometry(boardWidth * zoom, positionWidth * zoom,3* zoom),// Creates a new box geometrynew THREE.MeshPhongMaterial({ color })// Creates a new material);// Creates a new sectionconst middle = createSection(0xbaf455);// Creates a new section
middle.receiveShadow =true;// Sets the shadow to true
grass.add(middle);// Adds the section to the grass groupconst left = createSection(0x99C846);// Creates a new section
left.position.x =- boardWidth * zoom;// Sets the position of the section
grass.add(left);// Adds the section to the grass groupconst right = createSection(0x99C846);// Creates a new section
right.position.x = boardWidth * zoom;// Sets the position of the section
grass.add(right);// Adds the section to the grass group
grass.position.z =1.5* zoom;// Moves the grass up a bitreturn grass;// Returns the grass object}// =============================================================================================// =============================================================================================// LANE// =============================================================================================// =============================================================================================// Generates inital lanes const generateLanes =()=>[-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8,9].map((index)=>{const lane =newLane(index);// Creates a new Lane as an object
lane.mesh.position.y = index * positionWidth * zoom;// Edit lane y position
scene.add(lane.mesh);// Adds it to the scenereturn lane;}).filter((lane)=> lane.index >=0);const addLane =()=>{// Adds a new lane to the sceneconst index = lanes.length;// Gets next Index to add laneconst lane =newLane(index);// Creates a new Lane as an object
lane.mesh.position.y = index * positionWidth * zoom;// Edit lane y position
scene.add(lane.mesh);// Adds lane to the scene
lanes.push(lane);// Appends new Lane to current Lanes}functionLane(index){this.index = index;// Index of the lanethis.type = index <=0?'field': laneTypes[Math.floor(Math.random()* laneTypes.length)];// randomly chooses to either create a field or a forest or a car laneswitch(this.type){case'field':{// Makes a new field lanethis.type ='field';this.mesh =newGrass();// Makes a grass layerbreak;}case'forest':{// Makes a new forest lanethis.mesh =newGrass();// Makes a grass layerthis.occupiedPositions =newSet();// Saves the ocuppied spots this.threes =[1,2,3,4].map(()=>{// Creates 4 bushes or rocks at random positionsvar obstacle;if(Math.random()>0.7){// Random to add either a bush or a rock
obstacle =newBush();// Creates a new bush}else{
obstacle =newRock();// Creates a new rock}
let position;// Saves the position of the obstacledo{
position =Math.floor(Math.random()* columns);// Randomly chooses a position}while(this.occupiedPositions.has(position))// Checks if the position is already occupiedthis.occupiedPositions.add(position);// Adds the position to the occupied positions
obstacle.position.x =(position * positionWidth + positionWidth /2)* zoom - boardWidth * zoom /2;// Sets the x position of the obstaclethis.mesh.add(obstacle);// Adds the obstacle to the meshreturn obstacle;// Returns the new made obstacle})break;}case'car':{// Makes a new car lanethis.mesh =newRoad();// Makes a road layerthis.direction =Math.random()>=0.5;// Randomly chooses the direction of the car laneconst occupiedPositions =newSet();// Saves the ocuppied spotsthis.vechicles =[1,2,3].map(()=>{// Creates 3 carsconst vechicle =newCar();// Creates a new car
let position;// Creates a new positiondo{
position =Math.floor(Math.random()* columns /2);// Randomly chooses a position}while(occupiedPositions.has(position))// Checks if the position is already taken
occupiedPositions.add(position);// Adds the position to the taken positions
vechicle.position.x =(position * positionWidth *2+ positionWidth /2)* zoom - boardWidth * zoom /2;// Sets the position of the carif(!this.direction) vechicle.rotation.z =Math.PI;// Rotates the car if the direction is falsethis.mesh.add(vechicle);// Adds the car to the scenereturn vechicle;// Returns the new made obstacle})this.speed = laneSpeeds[Math.floor(Math.random()* laneSpeeds.length)];// Randomly chooses a speed for the laneif(score1 >=30){this.speed *=1.7;// Increases speed of cars if score is greater than 30}elseif(score1 >=10){this.speed *=1.3;// Increases the speed of the cars}break;}}}// =============================================================================================// =============================================================================================// TEXTURE// =============================================================================================// =============================================================================================functionTexture(width, height, rects){// Creates a new textureconst canvas = document.createElement("canvas");// Creates a new canvas
canvas.width = width; canvas.height = height;// Sets the width and height of the canvasconst context = canvas.getContext("2d");// Gets the context of the canvas
context.fillStyle ="#FFFFFF";// Sets the fill color to white
context.fillRect(0,0, width, height);// Fills the canvas with white
context.fillStyle ="rgba(0,0,0,0.6)";// Sets the fill color to black with 60% opacity
rects.forEach(rect =>{// Loops through all the rectangles
context.fillRect(rect.x, rect.y, rect.w, rect.h);// Fills the rectangle with the black color});returnnew THREE.CanvasTexture(canvas);// Returns the canvas texture}// =============================================================================================// =============================================================================================// MOVES// =============================================================================================// =============================================================================================function move(direction){const finalPositions = moves.reduce((position, move)=>{// Gets the final position of the playerif(move ==='forward')return{ lane: position.lane +1, column: position.column };// If the move is forward, then the lane increases by 1if(move ==='backward')return{ lane: position.lane -1, column: position.column };// If the move is backward, then the lane decreases by 1if(move ==='left')return{ lane: position.lane, column: position.column -1};// If the move is left, then the column decreases by 1if(move ==='right')return{ lane: position.lane, column: position.column +1};// If the move is right, then the column increases by 1},{ lane: currentLane, column: currentColumn })// The initial position is the current position of the player if(direction ==='forward'){// If the player is trying to move into a bush, then don't moveif(lanes[finalPositions.lane +1].type ==='forest'&& lanes[finalPositions.lane +1].occupiedPositions.has(finalPositions.column))return;if(!stepStartTimestamp) startMoving =true;
addLane();}elseif(direction ==='backward'){if(finalPositions.lane ===0)return;// If the player is trying to move into a bush, then don't moveif(lanes[finalPositions.lane -1].type ==='forest'&& lanes[finalPositions.lane -1].occupiedPositions.has(finalPositions.column))return;if(!stepStartTimestamp) startMoving =true;}elseif(direction ==='left'){if(finalPositions.column ===0)return;// If the player is at the left most column, then don't move // If the player is trying to move into a bush, then don't moveif(lanes[finalPositions.lane].type ==='forest'&& lanes[finalPositions.lane].occupiedPositions.has(finalPositions.column -1))return;if(!stepStartTimestamp) startMoving =true;}elseif(direction ==='right'){if(finalPositions.column === columns -1)return;// If the player is at the right most column, then don't move// If the player is trying to move into a bush, then don't moveif(lanes[finalPositions.lane].type ==='forest'&& lanes[finalPositions.lane].occupiedPositions.has(finalPositions.column +1))return;if(!stepStartTimestamp) startMoving =true;}
moves.push(direction);// Adds the direction to the moves array}// =============================================================================================// =============================================================================================// ANIMATION// =============================================================================================// =============================================================================================function animate(timestamp){
requestAnimationFrame(animate);if(!previousTimestamp) previousTimestamp = timestamp;// Checks timestampconst delta = timestamp - previousTimestamp;// edits timestamp with the new delta
previousTimestamp = timestamp;// sets previous timestamp to current timestamp// Animate cars moving on the lane
lanes.forEach(lane =>{// Loops through each lane and animates all carsif(lane.type ==='car'){// if lane is a car animate itconst aBitBeforeTheBeginingOfLane =-boardWidth * zoom /2- positionWidth *2* zoom;// Sets the position of the carconst aBitAfterTheEndOFLane = boardWidth * zoom /2+ positionWidth *2* zoom;// Sets the end of the lane
lane.vechicles.forEach(vechicle =>{// Loops through each car and animates itif(lane.direction){
vechicle.position.x = vechicle.position.x < aBitBeforeTheBeginingOfLane ? aBitAfterTheEndOFLane : vechicle.position.x -= lane.speed /16* delta;}else{
vechicle.position.x = vechicle.position.x > aBitAfterTheEndOFLane ? aBitBeforeTheBeginingOfLane : vechicle.position.x += lane.speed /16* delta;}});}});if(startMoving){// If start moving is true
stepStartTimestamp = timestamp;// sets the timestamp to the start of the step
startMoving =false;// sets start moving to false}if(stepStartTimestamp){// If step start timestamp is true// Moves playerconst moveDeltaTime = timestamp - stepStartTimestamp;// sets the move delta time to the timestamp - the start of the stepconst moveDeltaDistance =Math.min(moveDeltaTime / stepTime,1)* positionWidth * zoom;// Sets the distance of the playerswitch(moves[0]){case'forward':{// If move is forwardconst positionY = currentLane * positionWidth * zoom + moveDeltaDistance;// Sets the position of the player
camera.position.y = initialCameraPositionY + positionY;// Sets the position of the camera
dirLight.position.y = initialDirLightPositionY + positionY;// Sets the position of the light
player.position.y = positionY;// sets the position of the playerbreak;}case'backward':{// If move is backward
positionY = currentLane * positionWidth * zoom - moveDeltaDistance;// Sets the position of the player
camera.position.y = initialCameraPositionY + positionY;// Sets camera position
dirLight.position.y = initialDirLightPositionY + positionY;// Sets the position of the light
player.position.y = positionY;// Sets the position of the playerbreak;}case'left':{// If move is leftconst positionX =(currentColumn * positionWidth + positionWidth /2)* zoom - boardWidth * zoom /2- moveDeltaDistance;// Sets the position of the player
camera.position.x = initialCameraPositionX + positionX;// Sets camera position
dirLight.position.x = initialDirLightPositionX + positionX;// Sets the position of the light
player.position.x = positionX;// Sets the position of the playerbreak;}case'right':{// If move is rightconst positionX =(currentColumn * positionWidth + positionWidth /2)* zoom - boardWidth * zoom /2+ moveDeltaDistance;// Sets the position of the player
camera.position.x = initialCameraPositionX + positionX;// Sets camera position
dirLight.position.x = initialDirLightPositionX + positionX;// Sets the position of the light
player.position.x = positionX;// Sets the position of the playerbreak;}}// Once a step has ended if(moveDeltaTime > stepTime){switch(moves[0]){// Switches through the movescase'forward':{// If move is forwardif(lanes[currentLane].type ==='car'){// If the lane is a car
score1++;// Add to score}
currentLane++;// Add to current lanebreak;}case'backward':{// If move is backward
currentLane--;// Decreases the current laneif(lanes[currentLane].type ==='car'){// If the lane is a car
score1--;// Decreases the current}break;}case'left':{// If move is left
currentColumn--;// Decreases the current columnbreak;}case'right':{// If move is right
currentColumn++;// Increases the current columnbreak;}}if(hscore1 < score1) hscore1 = score1;// saves highscoreif(score1 >=30&& onSpeedUp2){// Updates sounds speed to level 2
speed2.pause();
speedUp.play();
onSpeedUp2 =false;
speed3.play();}elseif(score1 >=10&& onSpeedUp1){// Updates sounds speed to level 1
speed1.pause();
speedUp.play();
speed2.play();
onSpeedUp1 =false;}
socketOut("update",false,false);// calls websockets
updateHtml();// updates HTML sidebar// removes the first element in moves
moves.shift();// If more steps are to be taken then restart counter otherwise stop stepping
stepStartTimestamp = moves.length ===0?null: timestamp;}}// Hit test for lanesif(lanes[currentLane].type ==='car'){const playerMinX = player.position.x - playerSize * zoom /2;// sets player min xconst playerMaxX = player.position.x + playerSize * zoom /2;// sets player min yconst vechicleLength =60;
lanes[currentLane].vechicles.forEach(vechicle =>{// Loops through each vechicle in the laneconst carMinX = vechicle.position.x - vechicleLength * zoom /2;// sets car min xconst carMaxX = vechicle.position.x + vechicleLength * zoom /2;// sets car max xif(playerMaxX > carMinX && playerMinX < carMaxX){// if player is in the same x as the carif(hscore1 < score1) hscore1 = score1;// saves highscore
speed1.pause();// pauses sound
speed2.pause();// pauses sound
speed3.pause();// pauses sound
honk.play();// plays honk sound
onSpeedUp1 =true;// resets speed up
onSpeedUp2 =true;// resets speed up
score1 =0;// resets score
resetPlayer();// resets player
socketOut("update",false,false);// calls websockets}});}
renderer.render(scene, camera);// Updates scene and camera}// =============================================================================================// =============================================================================================// RESET PLAYERS// =============================================================================================// =============================================================================================function resetPlayer(){
currentLane =0;// sets the current lane
currentColumn =Math.floor(columns /2);// sets the current collumn
previousTimestamp =null;// reset timestamp
startMoving =false;// reset startMoving
moves =[];// reset moves
stepStartTimestamp;// reset stepStartTimestamp
player.position.x =0;// reset players x position
player.position.y =0;// reset players y position
camera.position.y = initialCameraPositionY;// reset cameras y position
camera.position.x = initialCameraPositionX;// reset cameras y position
dirLight.position.x = initialDirLightPositionX;// reset lighting to follow player
dirLight.position.y = initialDirLightPositionY;// reset lighting to follow player
updateHtml();// update HTML
speed1.play();// play speed1 song}// =============================================================================================// =============================================================================================// HANDLE KEY PRESSES// =============================================================================================// =============================================================================================
window.addEventListener("keyup", event =>{// Handles key upsif(lockControls)return;if(event.keyCode =='38'|| event.keyCode =='87'){// up arrow OR W
move('forward');// moves foward}elseif(event.keyCode =='40'|| event.keyCode =='83'){// down arrow OR S
move('backward');// moves backward}elseif(event.keyCode =='37'|| event.keyCode =='65'){// left arrow OR A
move('left');// moves to the left}elseif(event.keyCode =='39'|| event.keyCode =='68'){// right arrow OR D
move('right');// moves to the right}elseif(event.keyCode =='77'){// Mute or Unmute
isMuted =!isMuted;// changes to muted or unmuted
setVolume();// changes volume of sounds}});// =============================================================================================// =============================================================================================// SIDE SCREEN HTML INFO // =============================================================================================// =============================================================================================function updateHtml(){// Creates HTML for the side screen
AB.msg(`<h3>Welcome to our game!</h3>`,1);
AB.msg(`<p><strong>Player1</strong> Score: <strong>` + score1 + `</strong>|Highest:<strong>`+ hscore1 +`</strong> </p>`,3);
AB.msg(`<p><strong>Player2</strong> Score: <strong>` + score2 + `</strong>|Highest:<strong>`+ hscore2 +`</strong> </p>`,4);
AB.msg(`<p><strong>Controls</strong><br><strong>ArrowUp/W</strong>-MoveUp<br><strong>ArrowLeft/A</strong>-MoveLeft<br><strong>ArrowDown/S</strong>-MoveDown<br><strong>ArrowRight/D</strong> - Move Right</p>`,7);
AB.msg(`<p>Press<strong>M</strong> to mute </p>`,8);}// =============================================================================================// =============================================================================================// WAITING FOR PLAYERS // =============================================================================================// =============================================================================================function endGame(){
speed1.pause();// Pauses music
speed2.pause();// Pauses music
speed3.pause();// Pauses music
AB.removeSplash();// Removes old splashed
AB.newSplash();// Creates new splashvar txt;var other ="";if(playerLeft){// Other player left
txt ="Other Player has left.<br><br><span style='color: rgb(0, 255, 0);'>You Won!</span>";}elseif(hscore1 > hscore2){// You Win
txt ="<span style='color: rgb(0, 255, 0);'>You Won!</span>";
other =`Your opponent's highscore was:`+ hscore2 +`<br>`;}elseif(hscore2 > hscore1){// Other Player Wins
txt ="<span style='color: rgb(255, 0, 0);'>You Lost!</span>";
other =`Your opponent's highscore was:`+ hscore2 +`<br>`;}else{// Draw
txt ="It's a draw!";
other =`Your opponent's highscore was:`+ hscore2 +`<br>`;}
AB.socket.destroy();// Destroy socket// Creates HTML for the splash screen
AB.splashHtml(`<h1> GAME OVER...</h1><h3>`+ txt +`<br></h3>Your highest score was:`+ hscore1 +`<br>`+ other +`<br><button onclick='location.reload();'class=ab-largenormbutton >PlayAgain</button>`);}// =============================================================================================// =============================================================================================// WAITING FOR PLAYERS // =============================================================================================// =============================================================================================function waitingForPlayers(){
AB.removeSplash();// Removes old splashed
AB.newSplash();// Creates new splash // Creates HTML for the splash screen
AB.splashHtml(`<h1>Waitingfor opponent, please wait...</h1><img src="/uploads/wuk/loading-buffering.gif">`);}// =============================================================================================// =============================================================================================// WELCOME // =============================================================================================// =============================================================================================function welcome(){
AB.removeSplash();// Removes old splashed
AB.newSplash();// Creates new splash // Creates HTML for the splash screen
AB.splashHtml(`<h1 style="font-family:cooper"> RUN RUN RUN!</h1>Run as far as you can in 60 seconds and beat your opponent's highscore to win!<br>Avoid all the cars!If you get hit you will start again.<br><br><b>Controls:</b><br>ArrowUp/W -MoveUp<br>ArrowLeft/A -MoveLeft<br>ArrowDown/S -MoveDown<br>ArrowRight/D -MoveRight<br><br><b>GOOD LUCK!<br><p><button onclick='loadResources();'class=ab-largenormbutton >Searchfor match</button><p><div id=errordiv name=errordiv></div>`);}
welcome();