diff --git a/app.js b/app.js index 86229e4..7efd951 100644 --- a/app.js +++ b/app.js @@ -3,7 +3,7 @@ var app = express(); var serv = require('http').Server(app); var colors = require('colors/safe'); var middleware = require('socketio-wildcard')(); - +var exports = module.exports={countActivePlayers: countActivePlayers}; var debug = typeof v8debug === 'object' || /--debug/.test(process.execArgv.join(' ')); console.log(colors.green("[jsShooter] Starting server...")); @@ -37,6 +37,8 @@ var ATTACKER_LIST = {}; var NPCSHOOTER_LIST = {}; var POWERUP_LIST = {}; +const loseVal = 20; //set max value of points where if you lose the game you will die to 20 + // ---------- Entities ---------- // Npc shooter object var NPCShooter = function(id, x, y) { @@ -148,7 +150,7 @@ var NPCAttacker = function(id, x, y) { self.x += Math.cos(dir/180*Math.PI) * 2; self.y += Math.sin(dir/180*Math.PI) * 2; } - + } } catch(err) { if(debug) { @@ -256,7 +258,7 @@ var Bullet = function(id, ownerID, x, y, angle, size) { owner.score += 10; if(at.hp <= 0) { owner.score += 50; - } + } } self.lifetime = 0; } @@ -274,7 +276,7 @@ var Bullet = function(id, ownerID, x, y, angle, size) { if(sh.hp <= 0) { owner.score += 50; } - } + } self.lifetime = 0; } } @@ -418,7 +420,10 @@ var Player = function(id) { return self; } + //Powerup object +//Description: This object takes three parameters to make an object that deals with the powerups of each player +//Parameters: x is the x coordinate, y is the y coordinate, id designates which player var PowerUp = function(x, y, id) { var self = { x:x, @@ -426,6 +431,7 @@ var PowerUp = function(x, y, id) { id:id }; +//the update function updates the player's power up time and score if they have died self.update = function() { for(let p in PLAYER_LIST) { let player = PLAYER_LIST[p]; @@ -437,6 +443,7 @@ var PowerUp = function(x, y, id) { } } +//this function finds the player by the id and deletes them when they die self.destroy = function() { delete POWERUP_LIST[self.id]; delete self; @@ -446,6 +453,12 @@ var PowerUp = function(x, y, id) { } // ---------- Functions ---------- + +//Function name: getPlayerByID +//Parameters: id - the id of the player +//Description: this function returns the player from the list of players based +//on the unique player id. +//Return: player the player that we found by id function getPlayerByID(id) { for(let p in PLAYER_LIST) { let player = PLAYER_LIST[p]; @@ -455,6 +468,10 @@ function getPlayerByID(id) { } } +//Function name: getDistance +//Parameters: x1 & x2 - the x coordinates for distance, y1 & y2 - the y coordinates for distance +//Description: finds the distance between two points +//Return: the distance from the two points function getDistance(x1, y1, x2, y2) { let a = x1 - x2; let b = y1 - y2; @@ -462,6 +479,10 @@ function getDistance(x1, y1, x2, y2) { return Math.sqrt( a*a + b*b ); } +//Function name: getSmallest +//Parameters: obj - a list of obj that are the distances, so we can compare them +//Description: find the min distances from our list of distances and return the one with the smallest distance +//Return: the id of the obj with mininmum distance function getSmallest(obj) { let min,key; for(let k in obj) @@ -474,13 +495,19 @@ function getSmallest(obj) { } if(obj[k] 3) { + if(power > 3) { //if they has over three powerups they are over power return true; } else { return false; } } +//Name: countOPPlayers +//Description: count the players who are overpower +//Return: the number of players who are overpower function countOPPlayers() { let result = 0; for(let p in PLAYER_LIST) { @@ -531,34 +565,50 @@ function countOPPlayers() { return result; } +//Name: spawnBlock +//Description: Create a new block object, assign it a random id and random position to start life +//Return: id of block that was created function spawnBlock() { let id = (Math.random() * 10); - BLOCK_LIST[id] = NPCBlock(id); + BLOCK_LIST[id] = NPCBlock(id); //randomly assign new position return id; } +//Name: spawnAttacker +//Description: Create an object that will attack the players and assign it a random position and id +//Return id of new attacker that was created function spawnAttacker() { - let id = (Math.random() * 10); - let x = Math.floor(Math.random() * 1180) + 10; - let y = Math.floor(Math.random() * 580) + 10; - ATTACKER_LIST[id] = NPCAttacker(id, x, y); + let id = (Math.random() * 10); //assign random id + let x = Math.floor(Math.random() * 1180) + 10; //assign random position + let y = Math.floor(Math.random() * 580) + 10; //assign random position + ATTACKER_LIST[id] = NPCAttacker(id, x, y); //add it to the list return id; } +//Name: spawnShooter +//Description: Create an shooter object with random id and position +//Return: id of new shooter that was created function spawnShooter() { - let id = (Math.random() * 10); - let x = Math.floor(Math.random() * 1180) + 10; - let y = Math.floor(Math.random() * 580) + 10; - NPCSHOOTER_LIST[id] = NPCShooter(id, x, y); + let id = (Math.random() * 10); //assign random id + let x = Math.floor(Math.random() * 1180) + 10; //assign random position + let y = Math.floor(Math.random() * 580) + 10; //assign random position + NPCSHOOTER_LIST[id] = NPCShooter(id, x, y); //add it to the list return id; } +//Name: disconnectSocket +//Parameters: id - the id of the connection +//Description: disconnects from the socket connection function disconnectSocket(id) { SOCKET_LIST[id].disconnect(); delete SOCKET_LIST[id]; delete SOCKET_ACTIVITY[id]; } +//Name: getCommand +//Parameters: text - the word that is being modified so it can be used for checking what to do +//Description: parse text to the correct format +//return: the modified text in the correct format function getCommand(text) { let command = ""; for(let i = 0; i < text.length; i++) { @@ -571,6 +621,10 @@ function getCommand(text) { return command.toLowerCase(); } +//Name: getArgs +//Parameters: text - the words that need to be parsed to get the args +//Description: parse text to find the arguments given +//Return: the arguments in the correct format for checking later function getArgs(text) { let args = []; let arg = ""; @@ -594,6 +648,7 @@ function getArgs(text) { // ---------- Socket Connections ---------- io.sockets.on("connection", function(socket) { + //connect the id with a socket socket.id = Math.random(); if(SOCKET_ACTIVITY[socket.id] == undefined) { SOCKET_ACTIVITY[socket.id] = 0; @@ -605,7 +660,9 @@ io.sockets.on("connection", function(socket) { socket.emit("id", { id:socket.id }); - + + //disconect the player with the socket + socket.on("disconnect", function() { try { for(let b in BULLET_LIST) { @@ -624,6 +681,7 @@ io.sockets.on("connection", function(socket) { } }); + //move the player based on which arrow key the user pressed socket.on('keyPress',function(data){ try { if(data.inputId === 'left') @@ -641,6 +699,7 @@ io.sockets.on("connection", function(socket) { } }); + //change the player name based on the input provided socket.on('changeName', function(data) { try { if(data.name.length > 64) { // Name is way too long. Kick the player for sending too much data @@ -655,7 +714,7 @@ io.sockets.on("connection", function(socket) { return; } - let player = getPlayerByID(socket.id); + let player = getPlayerByID(socket.id); //find player who wants to change the name if(player.name != data.name ) { console.log(colors.cyan("[jsShooter] Player with id " + socket.id + " changed name to " + data.name)); player.name = data.name; @@ -667,6 +726,7 @@ io.sockets.on("connection", function(socket) { } }); + //find if we need to kick out player because of afk socket.on('not afk', function(data) { try { let player = getPlayerByID(socket.id); @@ -678,6 +738,7 @@ io.sockets.on("connection", function(socket) { } }); + //verify player based on id in game socket.on('kthx',function(data){ try { let player = getPlayerByID(socket.id); @@ -692,6 +753,7 @@ io.sockets.on("connection", function(socket) { } }); + //add a connection to the list of socket connections there are socket.on("*", function(data) { try { SOCKET_ACTIVITY[socket.id]++; @@ -704,6 +766,8 @@ io.sockets.on("connection", function(socket) { }); // HP Upgrade + //verify that connection is still good when they want to upgrade their hp and make sure + //that all the correct information was updated socket.on('upgHPClicked',function(data){ try { let player = getPlayerByID(socket.id); @@ -725,6 +789,8 @@ io.sockets.on("connection", function(socket) { }); // Fire speed upgrade + //verify that connection is still good when they want to upgrade their fire speed and make sure + //that all the correct information was updated socket.on('upgFSpeedClicked',function(data){ try { let player = getPlayerByID(socket.id); @@ -749,6 +815,8 @@ io.sockets.on("connection", function(socket) { }); // Bullet size upgrade + //verify that connection is still good when they want to upgrade their bullet size and make sure + //that all the correct information was updated socket.on('upgBulletSize',function(data){ try { let player = getPlayerByID(socket.id); @@ -765,9 +833,11 @@ io.sockets.on("connection", function(socket) { throw err; } } - }); + }); // Dual bullet upgrade + //verify that connection is still good when they want to upgrade to dual bullets and make sure + //that all the correct information was updated socket.on('upgDualBullets', function() { try { let player = getPlayerByID(socket.id); @@ -791,6 +861,7 @@ io.sockets.on("connection", function(socket) { } }); + //this updates the movement of the player based on mouse movement by the user socket.on('mouseMove',function(data){ try { let player = getPlayerByID(socket.id); @@ -805,7 +876,7 @@ io.sockets.on("connection", function(socket) { } }); }); - +//****************************************************************** // ---------- Loops ---------- // Bullet fire loop setInterval(function() { @@ -883,7 +954,9 @@ setInterval(function() { for(let p in PLAYER_LIST) { let player = PLAYER_LIST[p]; if(!(player.spawnCooldown < 0)) { - player.spawnCooldown--; + if(player.score>loseVal){ + player.spawnCooldown--; + } } if(player.powerupTime > 0) { player.powerupTime--; @@ -929,7 +1002,7 @@ setInterval(function() { } }, 500); } - + // AFK Test loop for(let i in SOCKET_LIST) { let socket = SOCKET_LIST[i]; @@ -1205,4 +1278,4 @@ for(let spBlock = 0; spBlock < 20; spBlock++) { console.log(colors.green("[jsShooter] Server started ")); if(debug) { console.log("Running in debug mode"); -} \ No newline at end of file +} diff --git a/client/game.js b/client/game.js index ee3874d..34d8e8b 100644 --- a/client/game.js +++ b/client/game.js @@ -36,10 +36,12 @@ var lmx = -1; var lmy = -1; var upgHP = 500; +const loseVal = 20;//set max value of points where you will lose the game if you die to 20 var uiVisible = false; var shooter_blink_state = true; var dead = false; +var lost = false; var respawnCooldown = 0; var colorBlink = 0; @@ -232,6 +234,11 @@ socket.on("newPositions", function(data) { } if (data.players[i].spawnCooldown > -1) { dead = true; + if(data.players[i].score<=loseVal){ + lost=true;//if the player doesn't have more than the losing value of points, set lost to true + }else{ + lost=false;//set to false otherwise because this is a loop that goes through all players + } respawnCooldown = data.players[i].spawnCooldown; } else { dead = false; @@ -329,10 +336,15 @@ socket.on("newPositions", function(data) { } if (dead) { - $("#death").show(); - countdownDiv.innerHTML = "Respawn in " + respawnCooldown; + if(lost){ + $("#loseGame").show();//if the players score is low enough that they lost, show the lost screen + }else{ + $("#death").show(); + countdownDiv.innerHTML = "Respawn in " + respawnCooldown;//otherwise, show normal death screen and respawn countdown + } } else { $("#death").hide(); + $("#loseGame").hide();//if the player did not die, keep death screens hidden } }); diff --git a/client/index.html b/client/index.html index bf16218..928c643 100644 --- a/client/index.html +++ b/client/index.html @@ -21,6 +21,11 @@
0
+
+
+ You lost the game :( Reload the page to play again +
+
Powerup time left:
diff --git a/client/style.css b/client/style.css index 1eacd63..a902433 100644 --- a/client/style.css +++ b/client/style.css @@ -105,6 +105,21 @@ html { display: none; } +#loseGame { + background-color: rgba(0,0,0,0.0); + text-align: center; + z-index: 2; + position: absolute; + margin: auto; + top: 70%; + right: 0; + bottom: 0; + left: 0; + width: 100%; + height: 100%; + display: none; +} + #dead { text-align: center; font-size: 500%; diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json index 5ab2290..34179ad 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,6 +38,12 @@ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, "base64-arraybuffer": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz", @@ -78,6 +84,16 @@ "type-is": "~1.6.15" } }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "bytes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", @@ -108,6 +124,12 @@ "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, "content-disposition": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", @@ -310,6 +332,26 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, "has-binary2": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has-binary2/-/has-binary2-1.0.3.tgz", @@ -344,6 +386,16 @@ "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=" }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", @@ -359,6 +411,22 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.1.tgz", "integrity": "sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=" }, + "jasmine": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-3.3.1.tgz", + "integrity": "sha512-/vU3/H7U56XsxIXHwgEuWpCgQ0bRi2iiZeUpx7Nqo8n1TpoDHfZhkPIc7CO8I4pnMzYsi3XaSZEiy8cnTfujng==", + "dev": true, + "requires": { + "glob": "^7.0.6", + "jasmine-core": "~3.3.0" + } + }, + "jasmine-core": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-3.3.0.tgz", + "integrity": "sha512-3/xSmG/d35hf80BEN66Y6g9Ca5l/Isdeg/j6zvbTYlTzeKinzmaTM4p9am5kYqOmE05D7s1t8FGjzdSnbUbceA==", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -392,6 +460,15 @@ "mime-db": "~1.36.0" } }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -415,6 +492,15 @@ "ee-first": "1.1.1" } }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, "parseqs": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/parseqs/-/parseqs-0.0.5.tgz", @@ -436,6 +522,12 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -658,6 +750,12 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, "ws": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/ws/-/ws-6.0.0.tgz", diff --git a/package.json b/package.json index 984c630..4e73bc6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Nodejs and html5 game", "main": "app.js", "scripts": { - "test": "node --debug --use_strict app.js", + "test": "jasmine --config=spec/support/jasmine.json", "start": "node app.js" }, "repository": { @@ -24,5 +24,8 @@ "bugs": { "url": "https://github.com/Zeeraa/jsShooter/issues" }, - "homepage": "https://github.com/Zeeraa/jsShooter#readme" + "homepage": "https://github.com/Zeeraa/jsShooter#readme", + "devDependencies": { + "jasmine": "^3.3.1" + } } diff --git a/run.sh b/run.sh old mode 100644 new mode 100755 diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json new file mode 100644 index 0000000..3ea3166 --- /dev/null +++ b/spec/support/jasmine.json @@ -0,0 +1,11 @@ +{ + "spec_dir": "spec", + "spec_files": [ + "**/*[sS]pec.js" + ], + "helpers": [ + "helpers/**/*.js" + ], + "stopSpecOnExpectationFailure": false, + "random": false +} diff --git a/spec/test/appSpec.js b/spec/test/appSpec.js new file mode 100644 index 0000000..387c200 --- /dev/null +++ b/spec/test/appSpec.js @@ -0,0 +1,32 @@ +var app = require("../../app.js"); +//getDistance +describe("getDistance", function(){ + + it("getDistance calculates correct values for positive number input", function() { + //arrange + expect(app.getDistance(2, 2, 2, 2)).toEqual(0); + + }); +}); + +//getSmallest +describe("getSmallest", function(){ + + it("getSmallest finds the smallest object from array given as input", function() { + //arrange + var array = [1, 2, 3, 4, 5]; + expect(app.getSmallest(array)).toEqual(1); + + }); +}); + +//getSmallest +describe("countActivePlayers", function(){ + + it("countActivePlayers counts the number of players playing", function() { + //arrange + + expect(app.getSmallest(array)).toEqual(1); + + }); +});