From 2ee13bb022244cbbdc4a5c98985e4545935f85f0 Mon Sep 17 00:00:00 2001 From: Arne van Iterson Date: Mon, 30 Nov 2020 22:05:23 +0100 Subject: [PATCH] Fixed bugs and addes several features Room list on login Restart button to prevent socket.io instance from hanging around Disabled contextmenu on entire playing field Added better logging for players who died Changed some gameplay variables after player feedback --- html/index.html | 24 ++++-- package-lock.json | 13 ++++ package.json | 2 + scss/index.scss | 26 ++++++- src/helpers/multiplayer.js | 147 +++++++++++++++++++++++++++++++++---- src/index.js | 4 + src/screens/game.js | 43 ++++++++--- 7 files changed, 228 insertions(+), 31 deletions(-) diff --git a/html/index.html b/html/index.html index 420774f..f3b7b11 100644 --- a/html/index.html +++ b/html/index.html @@ -44,7 +44,6 @@ -/- -
@@ -53,12 +52,21 @@


-
-

+
+
- +

Rooms available:

+ + + +
+ +
-

+ + +
+

@@ -69,6 +77,10 @@
+
+ + +
@@ -76,7 +88,7 @@ diff --git a/package-lock.json b/package-lock.json index ac0e285..6112937 100644 --- a/package-lock.json +++ b/package-lock.json @@ -511,6 +511,14 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, + "axios": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.0.tgz", + "integrity": "sha512-fmkJBknJKoZwem3/IKSSLpkdNXZeBu5Q7GA/aRsr2btgrptmSCxi2oFjZHqGdK9DoTil9PIHlPIZw2EcRJXRvw==", + "requires": { + "follow-redirects": "^1.10.0" + } + }, "babel-runtime": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", @@ -5136,6 +5144,11 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" }, + "striptags": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/striptags/-/striptags-3.1.1.tgz", + "integrity": "sha1-yMPn/db7S7OjKjt1LltePjgJPr0=" + }, "style-loader": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-2.0.0.tgz", diff --git a/package.json b/package.json index eaa911e..98ae9b0 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "license": "GPL-3.0-or-later", "dependencies": { "@fortawesome/fontawesome-free": "^5.15.1", + "axios": "^0.21.0", "cookie-storage": "^6.1.0", "css-loader": "^5.0.1", "eslint": "^7.12.1", @@ -25,6 +26,7 @@ "sass": "^1.29.0", "sass-loader": "^10.0.5", "socket.io-client": "^3.0.0", + "striptags": "^3.1.1", "style-loader": "^2.0.0", "webpack": "^5.4.0", "webpack-cli": "^4.2.0", diff --git a/scss/index.scss b/scss/index.scss index 158b500..9dcbb5f 100644 --- a/scss/index.scss +++ b/scss/index.scss @@ -49,6 +49,10 @@ body { column-gap: 1em; margin: 0 auto; + h2 { + margin-top: 0; + } + div { padding: 1em; margin-bottom: 1em; @@ -85,11 +89,15 @@ body { } &.bomb { - background-color: #FC5130; + background-color: #e85354; + } + + &.added { + background-color: #e89759; } &.flag { - background-color: #51CB20; + background-color: #4be3e3; } } } @@ -135,11 +143,19 @@ body { &#register { display: none; + + span#query { + height: 6rem; + display: block; + overflow: auto; + } } &#log { display: none; font-size: 12px; + position: relative; + min-height: 24rem; div { max-height: calc(14 * 1.25rem); @@ -164,6 +180,12 @@ body { } } } + + div:nth-of-type(2) { + position: absolute; + bottom: 0; + min-width: unset; + } } } } diff --git a/src/helpers/multiplayer.js b/src/helpers/multiplayer.js index 821d49d..8c0d262 100644 --- a/src/helpers/multiplayer.js +++ b/src/helpers/multiplayer.js @@ -3,11 +3,19 @@ import io from "socket.io-client"; import { CookieStorage } from "cookie-storage"; +import striptags from "striptags"; +import axios from "axios"; + import log from "./log.js"; import math from "../utilities/math.js"; const cookie = new CookieStorage(); +// http://localhost:3000 +// http://localhost:2428/tanks +const APIUrl = "https://api.arnweb.nl/tanks"; +const GameID = "ms99"; + class Multiplayer { constructor(game) { // Keep local and external players @@ -15,20 +23,26 @@ class Multiplayer { this.connected = false; this.game = game; + this.lastPlaying; + this.game.addListener("sendBomb", () => { this.sendBomb.call(this); }); + this.game.addListener("cleared", () => { + this.cleared.call(this); + }); + this.game.addListener("gameOver", () => { this.gameOver.call(this); }); log("Game instance initialized"); - var username = cookie.getItem("ms99_user"); - var opponents = sessionStorage.getItem("ms99_settings"); + var username = cookie.getItem(`${GameID}_user`); + var opponents = sessionStorage.getItem(`${GameID}_settings`); if (username != null && opponents != null) { - log(`Using previous username ${username}, delete cookies for this site to reset.`, "alert"); + log(`Using previous username ${username} and settings, close this session to reset this.`, "alert"); this.connect(username, opponents); } else { this.register(username); @@ -41,7 +55,7 @@ class Multiplayer { // Connect to TanksJS-Server instance log("Connecting to server..."); - this.socket = io("http://localhost:3000/", { + this.socket = io(APIUrl, { reconnection: false }); @@ -53,7 +67,7 @@ class Multiplayer { this.socket.emit("identification", { name: username, playersMax: opponents, - gameID: "ms99" + gameID: GameID }); log(`Connected to server as ${username}`, "success"); this.connected = true; @@ -79,33 +93,54 @@ class Multiplayer { delete this.players[id]; if (Object.keys(this.players).length == 0 && this.game.active) { log("No other players left", "alert"); - this.game.gameOver(); + this.game.gameOver(true); } } } + this.opponents = opponents; + this.game.drawStats({ - current: Object.keys(this.players).length + 1, + current: Object.keys(this.players).length, max: opponents }); }); // Handle player updates this.socket.on("update", (data) => { + if (data.player.cleared) { + this.game.gameOver(); + } + if (data.player.target) { if (data.player.target.id == this.socket.id) { this.game.addBomb(); } } else { if (!data.player.active) { - log(`${this.players[data.uuid].username} had died`); + log(`${this.players[data.uuid].username} has died`); + + if (Object.keys(this.players).length == 1) { + this.lastPlaying = this.players[Object.keys(this.players)[0]]; + } + delete this.players[data.uuid]; - if (Object.keys(this.players).length == 0 && this.game.active) { - log("No other players left", "alert"); - this.game.gameOver(); + + if (Object.keys(this.players).length == 0) { + if (this.game.active) { + log("No other players left", "alert"); + this.game.gameOver(true); + } else { + log(`${this.lastPlaying.username} won`); + } } } } + + this.game.drawStats({ + current: Object.keys(this.players).length, + max: opponents + }); }); // Start game @@ -119,18 +154,84 @@ class Multiplayer { var form = document.querySelectorAll("div#register > form")[0]; form.parentElement.style.display = "block"; + axios.get(APIUrl).then((res) => { + var output = document.getElementById("query"); + if (res.status == 200) { + var result = res.data.players; + + var span = document.createElement("span"); + if (Object.keys(result).length == 0) { + span.style.color = "orange"; + span.innerHTML = "No players are online :("; + output.appendChild(span); + } else { + var rooms = []; + var counts = {}; + + for (var player in result) { + var room = result[player].room.split("_"); + if (room[0] == GameID) { + rooms.push(room[1]); + } + } + + for (var i = 0; i < rooms.length; i++) { + var num = rooms[i]; + counts[num] = counts[num] ? counts[num] + 1 : 1; + } + + for (const size in counts) { + span.innerHTML += String(`${counts[size]} player(s) in groups of ${size}.
`); + } + + output.appendChild(span); + } + } else { + span.style.color = "red"; + span.innerHTML = "Server is offline!"; + output.appendChild(span); + form.querySelector("button").disabled = true; + } + }); + + var checks = { + session: form.querySelector("input[name=session]"), + store: form.querySelector("input[name=store]"), + }; + form.querySelector("input[name=username]").value = username; + if (username != "") { + checks.store.checked = true; + } + + checks.session.addEventListener("change", function () { + if (this.checked) { + checks.store.checked = true; + } + }); + + checks.store.addEventListener("change", function () { + if (!this.checked) { + checks.session.checked = false; + } + }); form.addEventListener("submit", (e) => { e.preventDefault(); form.parentElement.style.display = "none"; - var username = String(form.querySelector("input[name=username]").value); + var username = String(striptags(form.querySelector("input[name=username]").value)); var opponents = form.querySelector("input[name=opponents]").value; + if (form.querySelector("input[name=store]").checked) { - cookie.setItem("ms99_user", username); - sessionStorage.setItem("ms99_settings", opponents); + cookie.setItem(`${GameID}_user`, username); } + + if (form.querySelector("input[name=session]").checked) { + cookie.setItem(`${GameID}_user`, username); + sessionStorage.setItem(`${GameID}_settings`, opponents); + } + this.connect(username, opponents); }); } @@ -150,10 +251,28 @@ class Multiplayer { }); } + cleared() { + this.socket.emit("update", { + cleared: true + }); + } + gameOver() { this.socket.emit("update", { active: this.game.active }); + + this.game.drawStats({ + current: Object.keys(this.players).length, + max: this.opponents + }); + + var button = document.getElementById("restart"); + button.disabled = false; + button.addEventListener("click", (e) => { + this.socket.disconnect(); + window.location.reload(); + }); } } diff --git a/src/index.js b/src/index.js index 7328e6d..9680407 100644 --- a/src/index.js +++ b/src/index.js @@ -25,6 +25,10 @@ img.src = bomb; document.querySelector("body > h1").prepend(img); var board = document.getElementById("board"); +board.addEventListener("contextmenu", (e) => { + e.preventDefault(); +}); + var game = new Game(board); var mp = new Multiplayer(game); diff --git a/src/screens/game.js b/src/screens/game.js index 6426139..af84877 100644 --- a/src/screens/game.js +++ b/src/screens/game.js @@ -31,11 +31,13 @@ class Game extends EventEmitter { this.board.style.gridTemplateColumns = `repeat(${this.sides}, auto)`; this.generated = false; - this.bombAmount = 60; + this.bombAmount = 50; this.bombs = []; + this.added = []; this.discovered = []; this.time = 60; + this.penalty = 20; for (let cell = 0; cell < this.size; cell++) { this.bombs.push(0); @@ -55,7 +57,6 @@ class Game extends EventEmitter { this.board.appendChild(child); } - console.log(this.bombs); } update() { @@ -96,6 +97,9 @@ class Game extends EventEmitter { if (players != undefined) { var playersStats = document.getElementById("players").children[1]; + if (this.active || !this.generated) { + players.current++; + } playersStats.innerHTML = String(players.current + "/" + players.max); } } @@ -164,7 +168,7 @@ class Game extends EventEmitter { let index = 0; while (index < this.bombAmount) { var rand = math.rand(0, this.size); - if (rand != cell && !this.bombs[rand]) { + if (rand != cell && !this.bombs[rand] && this.surrounds(cell).indexOf(rand) == -1) { this.bombs[rand] = 1; index++; } @@ -224,12 +228,12 @@ class Game extends EventEmitter { if (this.bombAmount == 0) { log("You have cleared all bombs!", "success"); - this.active = false; + this.emit("cleared"); } } } else { if (!this.discovered[cell]) { - this.time = this.time - 30; + this.time = this.time - this.penalty; this.drawStats(); this.discover(cell); @@ -239,20 +243,38 @@ class Game extends EventEmitter { } addBomb() { - var cell = math.rand(0, this.bombs.length); for (let tries = 0; tries < 10; tries++) { - var sum = this.surrounds(cell).reduce((a, b) => this.discovered[a] + this.discovered[b], 0); - if (sum == 0) { + var sum = 0; + + var cell = math.rand(0, this.bombs.length); + + // var sum = this.surrounds(cell).reduce((a, b) => { + // console.log(this.discovered[a]); + // return this.discovered[a] + this.discovered[b]; + // }, 0); + + this.surrounds(cell).forEach((e) => { + sum = sum + this.discovered[e]; + }); + + if (sum == 0 && !this.bombs[cell] && !this.discovered[cell]) { this.bombs[cell] = 1; this.bombAmount++; + this.added.push(cell); + + // this.children[cell].style.background = "#0000ff"; log("A bomb has been added to your field!", "alert"); return; } } } - gameOver() { + gameOver(win = false) { log("Game over", "error"); + if (win) { + log("Congratulations, you win!", "success"); + } + this.active = false; this.drawStats(); @@ -265,6 +287,9 @@ class Game extends EventEmitter { img.src = bomb; this.children[index].appendChild(img); this.children[index].classList.add("bomb"); + if (this.added.indexOf(index) != -1) { + this.children[index].classList.add("added"); + } } } }