diff --git a/index.js b/index.js
index c2380b4..abb9b51 100644
--- a/index.js
+++ b/index.js
@@ -281,10 +281,18 @@ io.on('connection', async (socket) => {
if (bships.checkTurn(playerGame.data, socket.request.session.id)) {
const enemyIdx = socket.request.session.id === playerGame.data.hostId ? 1 : 0;
- if (await GInfo.shootShip(socket, posX, posY)) {
- io.to(playerGame.id).emit("shot hit", enemyIdx, posX, posY);
- } else {
+ let hit = await GInfo.shootShip(socket, posX, posY);
+ if (!hit.status) {
io.to(playerGame.id).emit("shot missed", enemyIdx, posX, posY);
+ } else if (hit.status === 1) {
+ io.to(playerGame.id).emit("shot hit", enemyIdx, posX, posY);
+ } else if (hit.status === 2) {
+ io.to(playerGame.id).emit("shot hit", enemyIdx, posX, posY);
+ io.to(playerGame.id).emit("ship sunk", enemyIdx, hit.ship);
+
+ if (hit.gameFinished) {
+ io.to(playerGame.id).emit("game finished", !enemyIdx ? 1 : 0);
+ }
}
await GInfo.passTurn(socket);
diff --git a/public/assets/css/board.css b/public/assets/css/board.css
index 535027a..5fed73d 100644
--- a/public/assets/css/board.css
+++ b/public/assets/css/board.css
@@ -14,6 +14,9 @@
--ship-invalid: hsl(0, 70%, 55%);
--ship-miss: hsl(0, 0%, 18%);
+ --ship-ally-sunken: hsl(120, 30%, 40%);
+ --ship-enemy-sunken: hsl(0, 30%, 40%);
+
--dynamic: rgb(83, 83, 245);
--danger: rgb(243, 56, 56);
--important: rgb(203, 50, 241);
@@ -83,7 +86,7 @@ h1,h2,h3,h4,h5,h6 {
pointer-events: none;
opacity: 0;
transform: scale(0);
- transition: opacity 0.25s, transform 0.25s 0.05s;
+ transition: opacity 0.25s, transform 0.25s 0.05s, background-color 0.25s;
}
#secondaryBoard .field .shipField {
@@ -110,6 +113,14 @@ h1,h2,h3,h4,h5,h6 {
opacity: 1;
}
+#board .field.sunken .shipField {
+ background-color: var(--ship-ally-sunken);
+}
+
+#secondaryBoard .field.sunken .shipField {
+ background-color: var(--ship-enemy-sunken);
+}
+
.dynamic {
color: var(--dynamic);
}
diff --git a/public/assets/js/battleships-lib.js b/public/assets/js/battleships-lib.js
index 3c6ead3..54594f1 100644
--- a/public/assets/js/battleships-lib.js
+++ b/public/assets/js/battleships-lib.js
@@ -59,7 +59,7 @@ class Battleships {
}
}
- setField(x, y, state, primary = false) {
+ setField(x, y, state) {
if (state==="hit") {
this.getField(x, y).children().children("svg").html("");
this.getField(x, y).addClass("hit");
@@ -67,12 +67,12 @@ class Battleships {
this.getField(x, y).children(".shipField").css("background-color", "var(--ship-miss)");
this.getField(x, y).addClass("active hit");
this.getField(x, y).children().children("svg").html("");
+ } else if (state === "sunken") {
+ this.getField(x, y).addClass("sunken");
}
-
- this.getFieldSecondary(x, y).addClass("hit");
}
- setFieldEnemy(x, y, state, primary = false) {
+ setFieldEnemy(x, y, state) {
if (state === "hit") {
this.getFieldSecondary(x, y).children().children("svg").html("");
this.getFieldSecondary(x, y).addClass("active hit");
@@ -80,9 +80,9 @@ class Battleships {
this.getFieldSecondary(x, y).children(".shipField").css("background-color", "var(--ship-miss)");
this.getFieldSecondary(x, y).addClass("active hit");
this.getFieldSecondary(x, y).children().children("svg").html("");
+ } else if (state === "sunken") {
+ this.getFieldSecondary(x, y).addClass("sunken");
}
-
- this.getFieldSecondary(x, y).addClass("hit");
}
placeShip(data) {
diff --git a/public/assets/js/socket-err-handler.js b/public/assets/js/socket-err-handler.js
new file mode 100644
index 0000000..d8a5d29
--- /dev/null
+++ b/public/assets/js/socket-err-handler.js
@@ -0,0 +1,72 @@
+// Handling connection errors
+socket.on("reconnecting", (number) => {
+ Toastify({
+ text: `Ponowne łączenie... ${number}`,
+ duration: 5000,
+ newWindow: true,
+ gravity: "bottom",
+ position: "right",
+ stopOnFocus: true,
+ className: "bshipstoast",
+ }).showToast();
+});
+
+socket.on("reconnect", () => {
+ Toastify({
+ text: "Połączono ponownie",
+ duration: 5000,
+ newWindow: true,
+ gravity: "bottom",
+ position: "right",
+ stopOnFocus: true,
+ className: "bshipstoast",
+ }).showToast();
+});
+
+socket.on("reconnect_error", () => {
+ Toastify({
+ text: "Wystąpił problem w trakcie ponownego łączenia",
+ duration: 5000,
+ newWindow: true,
+ gravity: "bottom",
+ position: "right",
+ stopOnFocus: true,
+ className: "bshipstoast",
+ }).showToast();
+});
+
+socket.on("reconnect_failed", () => {
+ Toastify({
+ text: "Nie udało się połączyć ponownie",
+ duration: 5000,
+ newWindow: true,
+ gravity: "bottom",
+ position: "right",
+ stopOnFocus: true,
+ className: "bshipstoast",
+ }).showToast();
+});
+
+socket.on("disconnect", () => {
+ Toastify({
+ text: "Rozłączono z serwerem\nSpróbuj odświeżyć stronę jeżeli błąd będzie się powtarzał",
+ duration: 5000,
+ newWindow: true,
+ gravity: "bottom",
+ position: "right",
+ stopOnFocus: true,
+ className: "bshipstoast",
+ }).showToast();
+});
+
+socket.on("error", () => {
+ Toastify({
+ text: "Błąd połączenia",
+ duration: 5000,
+ newWindow: true,
+ gravity: "bottom",
+ position: "right",
+ stopOnFocus: true,
+ className: "bshipstoast",
+ }).showToast();
+});
\ No newline at end of file
diff --git a/public/assets/js/socket-game.js b/public/assets/js/socket-game.js
index 3b56b6f..8bf73c7 100644
--- a/public/assets/js/socket-game.js
+++ b/public/assets/js/socket-game.js
@@ -5,20 +5,29 @@ var timerDestination = null;
var gamePhase = 'pregame';
var occupiedFields = [];
+var lastTimeClick = 0;
+
$('#board .field').on('click', function () {
- socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation);
+ console.log(new Date().getTime() / 1000 - lastTimeClick);
+ if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
+ socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation);
+ lastTimeClick = new Date().getTime() / 1000;
+ }
});
$('#secondaryBoard .field').on('click', function () {
- socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y'));
+ if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
+ socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y'));
+ lastTimeClick = new Date().getTime() / 1000;
+ }
});
-
$('.field').on('contextmenu', function () {
- if ($(this).hasClass('active')) {
+ if ($(this).hasClass('active') && new Date().getTime() / 1000 - lastTimeClick > 0.3) {
let originPos = occupiedFields.find((elem) => elem.pos[0] == $(this).data('pos-x') && elem.pos[1] == $(this).data('pos-y')).origin;
socket.emit("remove ship", originPos[0], originPos[1]);
+ lastTimeClick = new Date().getTime() / 1000;
}
});
@@ -48,19 +57,20 @@ socket.on("removed ship", (data) => {
return elem.origin[0] == data.posX && elem.origin[1] == data.posY;
});
- shipFields.forEach(field => {
- bsc.getField(field.pos[0], field.pos[1]).removeClass("active");
- });
+ for (let i = 0; i < shipFields.length; i++) {
+ const field = shipFields[i];
+ setTimeout(() => {
+ bsc.getField(field.pos[0], field.pos[1]).removeClass("active");
+ }, i * 150);
+ }
occupiedFields = occupiedFields.filter(n => !shipFields.includes(n));
- console.log(`shipsLeft[${data.type}] = ${shipsLeft[data.type]}`)
shipsLeft[data.type]++;
refreshBoardView();
});
socket.on("shot hit", (victimIdx, posX, posY) => {
- console.log("hit");
if (victimIdx === playerIdx) {
bsc.setField(posX, posY, "hit");
} else {
@@ -69,7 +79,6 @@ socket.on("shot hit", (victimIdx, posX, posY) => {
});
socket.on("shot missed", (victimIdx, posX, posY) => {
- console.log("missed");
if (victimIdx === playerIdx) {
bsc.setField(posX, posY, "miss");
} else {
@@ -77,6 +86,51 @@ socket.on("shot missed", (victimIdx, posX, posY) => {
}
});
+socket.on("ship sunk", (victimIdx, ship) => {
+ switch (ship.rot) {
+ case 0:
+ multips = [1, 0];
+ break;
+
+ case 1:
+ multips = [0, 1];
+ break;
+
+ case 2:
+ multips = [-1, 0];
+ break;
+
+ case 3:
+ multips = [0, -1];
+ break;
+ }
+
+ let l = !ship.type ? ship.type + 1 : ship.type + 2;
+ if (victimIdx === playerIdx) {
+ for (let i = 0; i < l; i++) {
+ console.log("ourship");
+ setTimeout(() => {
+ bsc.setField(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken");
+ }, i * 150);
+ }
+ } else {
+ for (let i = 0; i < l; i++) {
+ console.log("theirship");
+ setTimeout(() => {
+ bsc.setFieldEnemy(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken");
+ }, i * 150);
+ }
+ }
+});
+
+socket.on("game finished", (winnerIdx) => {
+ if (winnerIdx === playerIdx) {
+ alert("Wygrałeś!");
+ } else {
+ alert("Przegrałeś!");
+ }
+});
+
socket.on('connect', () => {
$(".cover h1").html("Oczekiwanie na serwer...");
});
diff --git a/public/assets/js/socket.js b/public/assets/js/socket.js
index 0baa791..f4ea422 100644
--- a/public/assets/js/socket.js
+++ b/public/assets/js/socket.js
@@ -1,5 +1,6 @@
const socket = io();
+// Handling server-sent events
socket.on("joined", (nick) => {
lockUI(true);
$("#oppNameField").html(nick);
diff --git a/utils/battleships.js b/utils/battleships.js
index a295b77..7fdfb23 100644
--- a/utils/battleships.js
+++ b/utils/battleships.js
@@ -37,7 +37,7 @@ export class GameInfo {
await this.redis.json.set(key, '.state', 'action');
let nextPlayer = await this.redis.json.get(key, { path:'.nextPlayer' });
- nextPlayer = nextPlayer === 0 ? 1 : 0;
+ nextPlayer = !nextPlayer ? 1 : 0;
await this.redis.json.set(key, '.nextPlayer', nextPlayer);
const UTCTs = Math.floor((new Date()).getTime() / 1000 + 30);
@@ -80,14 +80,14 @@ export class GameInfo {
const hostId = (await this.redis.json.get(key, { path: '.hostId' }));
const enemyIdx = socket.request.session.id === hostId ? 1 : 0;
- const playerIdx = enemyIdx ? 0 : 1;
+ // const playerIdx = enemyIdx ? 0 : 1;
let playerShips = await this.redis.json.get(key, { path: `.boards[${enemyIdx}].ships` });
var check = checkHit(playerShips, posX, posY);
if (!check) {
- return false;
+ return { status: 0 };
}
var shotShip;
@@ -97,12 +97,26 @@ export class GameInfo {
if (ship.posX === check.originPosX & ship.posY === check.originPosY) {
shotShip = ship;
playerShips[i].hits[check.fieldIdx] = true;
+ if (!playerShips[i].hits.includes(false)) {
+ console.log(playerShips);
+ let gameFinished = true;
+ await this.redis.json.set(key, `.boards[${enemyIdx}].ships`, playerShips);
+ playerShips.every(ship => {
+ if (ship.hits.includes(false)) {
+ gameFinished = false;
+ return false;
+ } else {
+ return true;
+ }
+ });
+
+ return { status: 2, ship: ship, gameFinished: gameFinished };
+ }
}
}
await this.redis.json.set(key, `.boards[${enemyIdx}].ships`, playerShips);
-
- return true;
+ return { status: 1, ship: shotShip };
}
}
@@ -198,9 +212,13 @@ export function checkHit(ships, posX, posY) {
break;
}
- for (let i = 0; i < ship.type + 2; i++) {
- console.log(`boardRender[${ship.posX + multips[1] * i}][${ship.posY + multips[0] * i}]`)
- boardRender[ship.posX + multips[1] * i][ship.posY + multips[0] * i] = {fieldIdx: i, originPosX: ship.posX, originPosY: ship.posY};
+ let l = !ship.type ? ship.type + 1 : ship.type + 2;
+ for (let i = 0; i < l; i++) {
+ // console.log("a");
+ let x = clamp(ship.posX + multips[0] * i, 0, 9);
+ let y = clamp(ship.posY + multips[1] * i, 0, 9);
+
+ boardRender[x][y] = {fieldIdx: i, originPosX: ship.posX, originPosY: ship.posY};
}
});
@@ -295,29 +313,6 @@ export function checkTurn(data, playerId) {
}
}
-// let type = 3;
-// let posX = 3;
-// let posY = 0;
-// let rot = 2;
-
-// let data = {
-// hostId: "123456",
-// state: "action",
-// boards: [
-// {
-// ships: [
-// { type: type, posX: posX, posY: posY, rot: rot, hits: [false, false, false] },
-// ],
-// shots: [],
-// },
-// {
-// ships: [],
-// shots: [],
-// }
-// ],
-// nextPlayer: 0,
-// }
-
-// checkHit(data, 1, 0, 0);
-
-// console.log(validateShipPosition(type, posX, posY, rot));
\ No newline at end of file
+function clamp(n, min, max) {
+ return Math.min(Math.max(n, min), max);
+}
\ No newline at end of file
diff --git a/views/board.handlebars b/views/board.handlebars
index 8c3bde7..0a2525c 100644
--- a/views/board.handlebars
+++ b/views/board.handlebars
@@ -33,4 +33,5 @@
-
\ No newline at end of file
+
+
\ No newline at end of file
diff --git a/views/index.handlebars b/views/index.handlebars
index 3fa608f..d28fbe2 100644
--- a/views/index.handlebars
+++ b/views/index.handlebars
@@ -75,4 +75,5 @@
+
\ No newline at end of file