From b956120312d4e2c67b9a13059a7defbb07fc4d6f Mon Sep 17 00:00:00 2001 From: MaciejkaG Date: Sat, 6 Apr 2024 21:18:38 +0200 Subject: [PATCH] Major update - UI/UX improvements - Multiple bug fixes and improvements --- index.js | 94 ++++++++++--- lang/en.json | 13 +- lang/pl.json | 13 +- public/assets/css/board.css | 32 ++++- public/assets/js/main.js | 10 +- public/assets/js/socket-game.js | 92 +++++++++++- test.js | 241 ++++++++++++++++++++++++++++++++ utils/battleships.js | 67 ++++++--- utils/localisation.js | 21 +-- views/board.handlebars | 12 ++ 10 files changed, 528 insertions(+), 67 deletions(-) create mode 100644 test.js diff --git a/index.js b/index.js index 90b041b..55c8d43 100644 --- a/index.js +++ b/index.js @@ -447,6 +447,7 @@ io.on('connection', async (socket) => { hostId: opp.request.session.id, state: "pregame", startTs: (new Date()).getTime() / 1000, + ready: [false, false], boards: [ { // typ 2 to trójmasztowiec pozycja i obrót na planszy które pola zostały trafione ships: [], // zawiera np. {type: 2, posX: 3, posY: 4, rot: 2, hits: [false, false, true]} @@ -528,29 +529,10 @@ io.on('connection', async (socket) => { } } - let UTCTs = Math.floor((new Date()).getTime() / 1000 + 90); + let UTCTs = Math.floor((new Date()).getTime() / 1000 + 180); io.to(playerGame.id).emit('turn update', { turn: 0, phase: "preparation", timerToUTC: UTCTs }); - GInfo.timer(playerGame.id, 90, async () => { - const members = [...roomMemberIterator(playerGame.id)]; - for (let i = 0; i < members.length; i++) { - const sid = members[i][0]; - const socket = io.sockets.sockets.get(sid); - - let placedShips = await GInfo.depleteShips(socket); - placedShips.forEach(shipData => { - socket.emit("placed ship", shipData) - }); - - if (placedShips.length > 0) { - const locale = new Lang(session.langs); - socket.emit("toast", locale.t("board.Your remaining ships have been randomly placed")) - } - } - - GInfo.endPrepPhase(socket); - GInfo.timer(playerGame.id, 30, () => { - AFKEnd(playerGame.id); - }); + GInfo.timer(playerGame.id, 180, async () => { + finishPrepPhase(socket, playerGame); }); await redis.json.set(`game:${playerGame.id}`, '$.state', "preparation"); @@ -563,6 +545,50 @@ io.on('connection', async (socket) => { } } + socket.on('ready', async (callback) => { + const playerGame = await GInfo.getPlayerGameData(socket); + let timeLeft = await GInfo.timerLeft(playerGame.id); + + if (timeLeft > 170) { + const locale = new Lang(session.langs); + socket.emit('toast', locale.t("board.You cannot ready up so early")); + return; + } + + if (playerGame && playerGame.data.state === 'preparation') { + await GInfo.setReady(socket); + const playerGame = await GInfo.getPlayerGameData(socket); + + if (playerGame.data.ready[0] && playerGame.data.ready[1]) { + // Both set ready + await GInfo.resetTimer(playerGame.id); + + await finishPrepPhase(socket, playerGame); + } else if (playerGame.data.ready[0] || playerGame.data.ready[1]) { + // One player set ready + + const members = [...roomMemberIterator(playerGame.id)]; + for (let i = 0; i < members.length; i++) { + const sid = members[i][0]; + const pSocket = io.sockets.sockets.get(sid); + if (pSocket.session.id !== socket.session.id) { + const locale = new Lang(pSocket.session.langs); + + pSocket.emit("toast", locale.t("board.Your opponent is ready")) + } + } + + let UTCTs = Math.floor((new Date()).getTime() / 1000 + Math.max(timeLeft / 2.5, 15)); + io.to(playerGame.id).emit('turn update', { turn: 0, phase: "preparation", timerToUTC: UTCTs }); + await GInfo.timer(playerGame.id, Math.max(timeLeft / 2.5, 15), async () => { + await finishPrepPhase(socket, playerGame); + }); + } // else if (playerGame.data.ready[1]) { + // // Guest set ready + // } + } + }); + socket.on('place ship', async (type, posX, posY, rot) => { const playerGame = await GInfo.getPlayerGameData(socket); @@ -735,4 +761,28 @@ function checkFlag(key) { } else { return false; } +} + +async function finishPrepPhase(socket, playerGame) { + await GInfo.endPrepPhase(socket); + + const members = [...roomMemberIterator(playerGame.id)]; + for (let i = 0; i < members.length; i++) { + const sid = members[i][0]; + const socket = io.sockets.sockets.get(sid); + + let placedShips = await GInfo.depleteShips(socket); + placedShips.forEach(shipData => { + socket.emit("placed ship", shipData) + }); + + if (placedShips.length > 0) { + const locale = new Lang(socket.session.langs); + socket.emit("toast", locale.t("board.Your remaining ships have been randomly placed")) + } + } + + GInfo.timer(playerGame.id, 30, () => { + AFKEnd(playerGame.id); + }); } \ No newline at end of file diff --git a/lang/en.json b/lang/en.json index b7f7a2f..1a4959d 100644 --- a/lang/en.json +++ b/lang/en.json @@ -103,6 +103,15 @@ "Four-masted": "Four-masted", "Available:": "Available:", + "Sunk ships": "Sunk ships", + "Single-mastedPlu": "Single-masted:", + "Two-mastedPlu": "Two-masted:", + "Three-mastedPlu": "Three-masted:", + "Four-mastedPlu": "Four-masted:", + "Your accuracy": "Your accuracy", + + "Ready up": "Ready up", + "Controls": "Controls", "Change ship": "Change ship", "Rotate ship": "Rotate ship", @@ -116,7 +125,9 @@ "You have ran out of ships of that type": "You have ran out of ships of that type", "You have already shot at this field": "You have already shot at this field", "Your remaining ships have been randomly placed": "Your remaining ships have been automatically placed", - + "Your opponent is ready": "Your opponent is ready.\nTime for preparation has been reduced", + "You cannot ready up so early": "You cannot ready up so early!", + "Victory": "Victory", "Defeat": "Defeat" } diff --git a/lang/pl.json b/lang/pl.json index 4d46cb3..e09c755 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -104,6 +104,15 @@ "Four-masted": "Czteromasztowiec", "Available:": "Dostępne:", + "Sunk ships": "Zatopione statki", + "Single-mastedPlu": "Jednomasztowce:", + "Two-mastedPlu": "Dwumasztowce:", + "Three-mastedPlu": "Trójmasztowce:", + "Four-mastedPlu": "Czteromasztowce:", + "Your accuracy": "Twoja celność", + + "Ready up": "Gotowy", + "Controls": "Sterowanie", "Change ship": "Zmień statek", "Rotate ship": "Obróć statek", @@ -117,7 +126,9 @@ "You have ran out of ships of that type": "Skończyły ci się statki tego typu", "You have already shot at this field": "Już strzelałeś w to pole", "Your remaining ships have been randomly placed": "Twoje pozostałe statki zostały automatycznie rozstawione", - + "Your opponent is ready": "Twój przeciwnik jest gotowy.\nCzas na przygotowania został skrócony", + "You cannot ready up so early": "Nie możesz zgłosić gotowości tak wcześnie!", + "Victory": "Zwycięstwo", "Defeat": "Porażka" } diff --git a/public/assets/css/board.css b/public/assets/css/board.css index ea4d36a..f86bf56 100644 --- a/public/assets/css/board.css +++ b/public/assets/css/board.css @@ -176,6 +176,7 @@ h1,h2,h3,h4,h5,h6 { } .ownBoardInfo { + height: 11rem; transition: opacity 0.3s; } @@ -208,7 +209,7 @@ h1,h2,h3,h4,h5,h6 { to {transform: translateX(0);opacity:1;} } -#selectedShip.changing { +#selectedShip.changing, .ownBoardInfo.changing { opacity: 0; animation: changingOut 1 0.2s ease; } @@ -309,7 +310,7 @@ h1,h2,h3,h4,h5,h6 { display: none; } -.mobileControls button { +.mobileControls button, .readyButton { padding: 0.5rem 2rem; font-size: 1rem; background-color: black; @@ -321,4 +322,31 @@ h1,h2,h3,h4,h5,h6 { transition: all 0.3s; margin-bottom: 1.5rem; +} + +.readyButton:hover { + color: black; + background-color: white; +} + +.lateBoardInfo { + display: none; +} + +.ownBoardInfo #accuracy { + transition: all 0.3s; +} + +.ownBoardInfo #accuracy.animatingDown { + color: var(--ship-invalid); + transform: scale(1.2); +} + +.ownBoardInfo #accuracy.animatingUp { + color: var(--ship-valid); + transform: scale(1.2); +} + +.shipnote { + font-weight: bold; } \ No newline at end of file diff --git a/public/assets/js/main.js b/public/assets/js/main.js index 64939af..47443fe 100644 --- a/public/assets/js/main.js +++ b/public/assets/js/main.js @@ -125,16 +125,12 @@ $(".controlsOwnBoard").css("opacity", 1); function switchBoards() { if (postPrep) { - if (ownBoardIsActive) { // Aktywna jest plansza użytkownika + if (ownBoardIsActive) { // Aktywna jest plansza przeciwnika $("#secondaryBoard").removeClass("secondary"); $("#board").addClass("secondary"); - $(".ownBoardInfo").css("opacity", 0); - $(".controlsOwnBoard").css("opacity", 0.3); - } else { // Aktywna jest plansza przeciwnika + } else { // Aktywna jest plansza gracza $("#board").removeClass("secondary"); $("#secondaryBoard").addClass("secondary"); - $(".ownBoardInfo").css("opacity", 1); - $(".controlsOwnBoard").css("opacity", 1); } ownBoardIsActive = !ownBoardIsActive; @@ -152,7 +148,7 @@ function switchShips() { refreshBoardView(); $("#selectedShip").addClass("changing"); - + setTimeout(() => { switch (selectedShip) { case 0: diff --git a/public/assets/js/socket-game.js b/public/assets/js/socket-game.js index 949da8a..316c4e0 100644 --- a/public/assets/js/socket-game.js +++ b/public/assets/js/socket-game.js @@ -5,6 +5,11 @@ var timerDestination = null; var gamePhase = 'pregame'; var occupiedFields = []; +var shipsSunk = [0, 0, 0, 0]; + +var hits = 0; +var misses = 0; + var lastTimeClick = 0; if ($(window).width() <= 820) { @@ -48,8 +53,6 @@ $('#board .field').on('click', function () { if (new Date().getTime() / 1000 - lastTimeClick > 0.3) { if ($(window).width() > 820) { socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation); - - navigator.vibrate(200); lastTimeClick = new Date().getTime() / 1000; } } @@ -66,8 +69,6 @@ $('#secondaryBoard .field').on('click', function () { if (new Date().getTime() / 1000 - lastTimeClick > 0.3) { if ($(window).width() > 820) { socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y')); - - navigator.vibrate(200); lastTimeClick = new Date().getTime() / 1000; } } @@ -143,6 +144,8 @@ socket.on("shot hit", (victimIdx, posX, posY) => { bsc.setField(posX, posY, "hit"); } else { bsc.setFieldEnemy(posX, posY, "hit"); + hits++; + updateAccuracy(getAccuracy()); } }); @@ -151,6 +154,8 @@ socket.on("shot missed", (victimIdx, posX, posY) => { bsc.setField(posX, posY, "miss"); } else { bsc.setFieldEnemy(posX, posY, "miss"); + misses++; + updateAccuracy(getAccuracy()); } }); @@ -186,6 +191,9 @@ socket.on("ship sunk", (victimIdx, ship) => { bsc.setFieldEnemy(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken"); }, i * 150); } + + shipsSunk[ship.type]++; + updateShipsSunk(); } }); @@ -246,6 +254,17 @@ socket.on('turn update', (turnData) => { $("#whosTurn").html(window.locale["Preparation phase"]); $(".boardSwitch").css("opacity", 0.3); } else { + if (!postPrep) { + $(".controlsOwnBoard").css("opacity", 0.3); + + $(".ownBoardInfo").addClass("changing"); + setTimeout(() => { + $(".ownBoardInfo").html($(".lateBoardInfo").html()); + + $(".ownBoardInfo").removeClass("changing"); + }, 200); + } + postPrep = true; myTurn = turnData.turn === playerIdx; turnData.turn === playerIdx ? $("#whosTurn").html(window.locale["Your turn"]) : $("#whosTurn").html(window.locale["Opponents turn"]); @@ -256,6 +275,71 @@ socket.on('turn update', (turnData) => { gamePhase = turnData.phase; }); +function updateLateInfo() { + if (postPrep) { + + } +} + +var currentAccuracy = 0; + +function updateAccuracy(val) { + var obj = $(".ownBoardInfo #accuracy").get(0); + + const start = currentAccuracy !== null ? currentAccuracy : val; + + const range = val - start; + var minTimer = 50; + var stepTime = Math.abs(Math.floor(1000 / range)); + + stepTime = Math.max(stepTime, minTimer); + + var startTime = new Date().getTime(); + var endTime = startTime + 1000; + var timer; + + if (val < currentAccuracy) { + $(".ownBoardInfo #accuracy").addClass("animatingDown"); + } else { + $(".ownBoardInfo #accuracy").addClass("animatingUp"); + } + + currentAccuracy = val; + + const run = () => { + var now = new Date().getTime(); + var remaining = Math.max((endTime - now) / 1000, 0); + var value = Math.round(val - (remaining * range)); + obj.innerHTML = value + "%"; + if (value == val) { + obj.innerHTML = Math.round(value) + "%"; + $(".ownBoardInfo #accuracy").removeClass("animatingDown animatingUp"); + + clearInterval(timer); + } + }; + + timer = setInterval(run, stepTime); + run(); +} + +function getAccuracy() { + return hits / (misses + hits) * 100; +} + +function updateShipsSunk() { + $("#singlemasted").html(shipsSunk[0]); + $("#twomasted").html(shipsSunk[1]); + $("#threemasted").html(shipsSunk[2]); + $("#fourmasted").html(shipsSunk[3]); +} + +function readyUp() { + socket.emit("ready", () => { + $(".readyButton").css({ pointerEvents: 'none', opacity: 0.3 }); + }); +} + socket.on('player left', () => { window.location.replace("/"); }); \ No newline at end of file diff --git a/test.js b/test.js new file mode 100644 index 0000000..b6a32ec --- /dev/null +++ b/test.js @@ -0,0 +1,241 @@ +function findEmptyFields(grid, len) { + const rowPlacements = []; + + // Helper function to check if a row can be placed horizontally at a given position + function canPlaceHorizontally(x, y) { + console.log(x, y); + // console.log(x + len) + // console.log(x + len >= grid.length) + if (x + len >= grid[0].length) { + return false; // Ship exceeds board boundaries + } + for (let i = x; i < x + len; i++) { + if (grid[i][y]) { + return false; // One of ship's fields is already occupied + } + } + return true; + } + + // Helper function to check if a row can be placed vertically at a given position + function canPlaceVertically(x, y) { + // console.log(y + len) + // console.log(y + len >= grid.length) + if (y + len >= grid.length) { + return false; // Ship exceeds board boundaries + } + for (let i = y; i < y + len; i++) { + if (grid[x][i]) { + return false; // One of ship's fields is already occupied + } + } + return true; + } + + for (let i = 0; i < grid.length; i++) { + for (let j = 0; j < grid[0].length; j++) { + if (grid[j][i] === false) { + if (canPlaceHorizontally(j, i)) { + rowPlacements.push({ posX: j, posY: i, rot: 0 }); + } + + if (canPlaceVertically(j, i)) { + rowPlacements.push({ posX: j, posY: i, rot: 1 }); + } + } + } + } + + return rowPlacements; +} + +let data = { + hostId: "123456", + state: "action", + boards: [ + { + ships: [ + { type: 3, posX: 3, posY: 4, rot: 0, hits: [false, false, false] }, + ], + shots: [], + }, + { + ships: [], + shots: [], + } + ], + nextPlayer: 0, +} + +// checkHit(data, 1, 0, 0); + +// console.log(validateShipPosition(type, posX, posY, rot)); + +let boardRender = []; + +for (let i = 0; i < 10; i++) { + var array = []; + for (let i = 0; i < 10; i++) { + array.push(false); + } + boardRender.push(array); +} + +data.boards[0].ships.forEach(ship => { + let multips; + + 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; + } + + for (let i = 0; i <= ship.type; i++) { + boardRender[ship.posX + multips[0] * i][ship.posY + multips[1] * i] = true; + } +}); + +// const rot = 0; +const type = 3; + +// let multips; + +// switch (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; +// } + +boardRender = [ + [ + true, true, true, + true, true, true, + true, false, true, + true + ], + [ + true, true, true, + true, true, true, + true, false, true, + true + ], + [ + false, true, true, + true, true, true, + true, true, true, + true + ], + [ + false, true, true, + true, false, false, + true, true, true, + false + ], + [ + false, false, false, + true, true, true, + true, true, true, + false + ], + [ + false, false, false, + true, true, true, + true, true, false, + false + ], + [ + true, true, true, + true, true, true, + true, true, true, + true + ], + [ + true, true, true, + true, false, false, + false, true, true, + true + ], + [ + true, true, true, + true, true, true, + true, true, true, + true + ], + [ + false, false, false, + true, true, true, + true, false, false, + false + ] +]; + +let search = findEmptyFields(boardRender, 4); +for (let y = 0; y < 10; y++) { + let row = ""; + for (let x = 0; x < 10; x++) { + row += `${boardRender[x][y] ? "\x1b[31m" : "\x1b[32m"}${boardRender[x][y]}\x1b[0m\t`; + } + console.log(row); +} +console.log(search); + +const rPos = search[Math.floor(Math.random() * search.length)]; + +switch (rPos.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; +} + +for (let i = 0; i <= type; i++) { + console.log(`boardRender[${rPos.posX + multips[0] * i}][${rPos.posY + multips[1] * i}]`) + boardRender[rPos.posX + multips[0] * i][rPos.posY + multips[1] * i] = true; +} + +for (let y = 0; y < 10; y++) { + let row = ""; + for (let x = 0; x < 10; x++) { + row += `${boardRender[x][y] ? "\x1b[31m" : "\x1b[32m"}${boardRender[x][y]}\x1b[0m\t`; + } + console.log(row); +} + +// console.log(); +// console.log(findAllRowsOfXTrueValues(matrix, 3, 1)); +// console.log(findAllRowsOfXTrueValues(matrix, 3, 2)); \ No newline at end of file diff --git a/utils/battleships.js b/utils/battleships.js index 1bb89db..3812376 100644 --- a/utils/battleships.js +++ b/utils/battleships.js @@ -5,8 +5,8 @@ export class GameInfo { } async timer(tId, time, callback) { - await this.redis.set(`timer:${tId}`, new Date().getTime() / 1000); - let localLastUpdate = await this.redis.get(`timer:${tId}`); + await this.redis.json.set(`timer:${tId}`, '$', { lastUpdate: new Date().getTime() / 1000, end: new Date().getTime() / 1000 + time }); + let localLastUpdate = await this.redis.json.get(`timer:${tId}`, { path: ".lastUpdate" }); let timeout = setTimeout(callback, time * 1000); @@ -17,7 +17,7 @@ export class GameInfo { return; } - let lastUpdate = await this.redis.get(`timer:${tId}`); + let lastUpdate = await this.redis.json.get(`timer:${tId}`, { path: ".lastUpdate" }); if (localLastUpdate != lastUpdate) { // timer has been reset clearTimeout(timeout); @@ -27,9 +27,15 @@ export class GameInfo { }, 200); } + async timerLeft(tId) { + let end = await this.redis.json.get(`timer:${tId}`, { path: ".end" }); + let left = end - new Date().getTime() / 1000; + return left; + } + async resetTimer(tId) { - let lastUpdate = await this.redis.get(`timer:${tId}`); - await this.redis.set(`timer:${tId}`, -lastUpdate); + let lastUpdate = await this.redis.json.get(`timer:${tId}`, { path: ".end" }); + await this.redis.json.set(`timer:${tId}`, '.lastUpdate', -lastUpdate); } async isPlayerInGame(socket) { @@ -159,13 +165,17 @@ export class GameInfo { let availableShipsOfType = availableShips[i]; for (let j = 0; j < availableShipsOfType; j++) { playerShips = (await this.redis.json.get(key, { path: `.boards[${playerIdx}].ships` })); + + let print = ""; for (let y = 0; y < 10; y++) { let row = ""; for (let x = 0; x < 10; x++) { row += `${boardRender[x][y] ? "\x1b[31m" : "\x1b[32m"}${boardRender[x][y]}\x1b[0m\t`; } + print += row+"\n"; } - const search = findEmptyFields(boardRender, i+1); + + const search = findEmptyFields(boardRender, i + 1); const rPos = search[Math.floor(Math.random() * search.length)]; @@ -275,6 +285,16 @@ export class GameInfo { await this.redis.json.set(key, `.boards[${enemyIdx}]`, playerBoard); return { status: 1, ship: shotShip }; } + + async setReady(socket) { + const gameId = socket.session.activeGame; + const key = `game:${gameId}`; + const hostId = (await this.redis.json.get(key, { path: '.hostId' })); + + const playerIdx = socket.request.session.id === hostId ? 0 : 1; + + await this.redis.json.set(key, `.ready[${playerIdx}]`, true); + } } export function isPlayerInRoom(socket) { @@ -423,16 +443,18 @@ export function checkTurn(data, playerId) { } function findEmptyFields(grid, len) { - const rowPlacements = []; + const shipPlacements = []; // Helper function to check if a row can be placed horizontally at a given position function canPlaceHorizontally(x, y) { - if (x + len >= grid[0].length) { - return false; // Ship exceeds board boundaries + // Check if the ship exceeds the board boundaries horizontally + if (x + len > grid.length) { + return false; } - for (let i = x; i <= x + len; i++) { + // Check if any field within the ship's length is already occupied + for (let i = x; i < x + len; i++) { if (grid[i][y]) { - return false; // One of ship's fields is already occupied + return false; } } return true; @@ -440,32 +462,35 @@ function findEmptyFields(grid, len) { // Helper function to check if a row can be placed vertically at a given position function canPlaceVertically(x, y) { - if (y + len >= grid.length) { - return false; // Ship exceeds board boundaries + // Check if the ship exceeds the board boundaries vertically + if (y + len > grid[0].length) { + return false; } - for (let i = y; i <= y + len; i++) { + // Check if any field within the ship's length is already occupied + for (let i = y; i < y + len; i++) { if (grid[x][i]) { - return false; // One of ship's fields is already occupied + return false; } } return true; } + // Loop through the grid to find empty places for (let i = 0; i < grid.length; i++) { for (let j = 0; j < grid[0].length; j++) { - if (grid[j][i] === false) { - if (canPlaceHorizontally(j, i)) { - rowPlacements.push({ posX: j, posY: i, rot: 0 }); + if (!grid[i][j]) { // Check if the current position is empty + if (canPlaceHorizontally(i, j)) { + shipPlacements.push({ posX: i, posY: j, rot: 0 }); } - if (canPlaceVertically(j, i)) { - rowPlacements.push({ posX: j, posY: i, rot: 1 }); + if (canPlaceVertically(i, j)) { + shipPlacements.push({ posX: i, posY: j, rot: 1 }); } } } } - return rowPlacements; + return shipPlacements; } function clamp(n, min, max) { diff --git a/utils/localisation.js b/utils/localisation.js index 3371bb1..03d891e 100644 --- a/utils/localisation.js +++ b/utils/localisation.js @@ -9,16 +9,19 @@ export class Lang { constructor(langs) { const languagesPath = path.join(__dirname, '../lang'); this.allText = null; - for (let i = 0; i < langs.length; i++) { - const lang = langs[i]; - if (fs.readdirSync(languagesPath).includes(`${lang}.json`)) { - try { - this.allText = JSON.parse(fs.readFileSync(path.join(languagesPath, `${lang}.json`), 'utf8')); - this.lang = lang; - return; - } catch (e) { - console.log(e); + if (langs && langs.length > 0) { + for (let i = 0; i < langs.length; i++) { + const lang = langs[i]; + + if (fs.readdirSync(languagesPath).includes(`${lang}.json`)) { + try { + this.allText = JSON.parse(fs.readFileSync(path.join(languagesPath, `${lang}.json`), 'utf8')); + this.lang = lang; + return; + } catch (e) { + console.log(e); + } } } } diff --git a/views/board.handlebars b/views/board.handlebars index d335253..eaefe36 100644 --- a/views/board.handlebars +++ b/views/board.handlebars @@ -17,6 +17,17 @@

{{ t 'board.Single-masted' }}

{{ t 'board.Available:' }} -

+
+

{{ t 'board.Sunk ships' }}

+

+ {{ t 'board.Single-mastedPlu' }} 0
+ {{ t 'board.Two-mastedPlu' }} 0
+ {{ t 'board.Three-mastedPlu' }} 0
+ {{ t 'board.Four-mastedPlu' }} 0
+

+

{{ t 'board.Your accuracy' }}

+

-

+

{{ t 'board.Controls' }}

@@ -29,6 +40,7 @@
+