Compare commits

..

5 Commits

Author SHA1 Message Date
MaciejkaG
955171be36 Minor fixes 2024-04-06 21:49:27 +02:00
MaciejkaG
322b932dbd Minor bug fixes 2024-04-06 21:44:18 +02:00
MaciejkaG
7e3db33ae8 Major bug fixes 2024-04-06 21:38:46 +02:00
MaciejkaG
2e8f8952f3 Minor bug fixes 2024-04-06 21:27:46 +02:00
MaciejkaG
b956120312 Major update
- UI/UX improvements
- Multiple bug fixes and improvements
2024-04-06 21:18:38 +02:00
11 changed files with 556 additions and 80 deletions

104
index.js
View File

@ -447,6 +447,7 @@ io.on('connection', async (socket) => {
hostId: opp.request.session.id, hostId: opp.request.session.id,
state: "pregame", state: "pregame",
startTs: (new Date()).getTime() / 1000, startTs: (new Date()).getTime() / 1000,
ready: [false, false],
boards: [ boards: [
{ // typ 2 to trójmasztowiec pozycja i obrót na planszy które pola zostały trafione { // 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]} 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 }); io.to(playerGame.id).emit('turn update', { turn: 0, phase: "preparation", timerToUTC: UTCTs });
GInfo.timer(playerGame.id, 90, async () => { GInfo.timer(playerGame.id, 180, async () => {
const members = [...roomMemberIterator(playerGame.id)]; finishPrepPhase(socket, playerGame);
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);
});
}); });
await redis.json.set(`game:${playerGame.id}`, '$.state', "preparation"); await redis.json.set(`game:${playerGame.id}`, '$.state', "preparation");
@ -563,6 +545,60 @@ io.on('connection', async (socket) => {
} }
} }
socket.on('ready', async (callback) => {
if (!(callback && typeof callback === 'function')) {
return;
}
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;
}
const playerIdx = playerGame.data.hostId === session.userId ? 0 : 1;
const userNotReady = !playerGame.data.ready[playerIdx];
if (playerGame && playerGame.data.state === 'preparation' && userNotReady) {
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);
callback();
await finishPrepPhase(socket, playerGame);
} else if (playerGame.data.ready[0] || playerGame.data.ready[1]) {
// One player set ready
callback();
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) => { socket.on('place ship', async (type, posX, posY, rot) => {
const playerGame = await GInfo.getPlayerGameData(socket); const playerGame = await GInfo.getPlayerGameData(socket);
@ -736,3 +772,27 @@ function checkFlag(key) {
return false; 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);
});
}

View File

@ -103,6 +103,15 @@
"Four-masted": "Four-masted", "Four-masted": "Four-masted",
"Available:": "Available:", "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", "Controls": "Controls",
"Change ship": "Change ship", "Change ship": "Change ship",
"Rotate ship": "Rotate ship", "Rotate ship": "Rotate ship",
@ -116,6 +125,8 @@
"You have ran out of ships of that type": "You have ran out of ships of that type", "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", "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 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", "Victory": "Victory",
"Defeat": "Defeat" "Defeat": "Defeat"

View File

@ -104,6 +104,15 @@
"Four-masted": "Czteromasztowiec", "Four-masted": "Czteromasztowiec",
"Available:": "Dostępne:", "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", "Controls": "Sterowanie",
"Change ship": "Zmień statek", "Change ship": "Zmień statek",
"Rotate ship": "Obróć statek", "Rotate ship": "Obróć statek",
@ -117,6 +126,8 @@
"You have ran out of ships of that type": "Skończyły ci się statki tego typu", "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", "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 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", "Victory": "Zwycięstwo",
"Defeat": "Porażka" "Defeat": "Porażka"

View File

@ -176,6 +176,7 @@ h1,h2,h3,h4,h5,h6 {
} }
.ownBoardInfo { .ownBoardInfo {
height: 11rem;
transition: opacity 0.3s; transition: opacity 0.3s;
} }
@ -208,7 +209,7 @@ h1,h2,h3,h4,h5,h6 {
to {transform: translateX(0);opacity:1;} to {transform: translateX(0);opacity:1;}
} }
#selectedShip.changing { #selectedShip.changing, .ownBoardInfo.changing {
opacity: 0; opacity: 0;
animation: changingOut 1 0.2s ease; animation: changingOut 1 0.2s ease;
} }
@ -309,7 +310,7 @@ h1,h2,h3,h4,h5,h6 {
display: none; display: none;
} }
.mobileControls button { .mobileControls button, .readyButton {
padding: 0.5rem 2rem; padding: 0.5rem 2rem;
font-size: 1rem; font-size: 1rem;
background-color: black; background-color: black;
@ -322,3 +323,30 @@ h1,h2,h3,h4,h5,h6 {
margin-bottom: 1.5rem; 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;
}

View File

@ -125,16 +125,12 @@ $(".controlsOwnBoard").css("opacity", 1);
function switchBoards() { function switchBoards() {
if (postPrep) { if (postPrep) {
if (ownBoardIsActive) { // Aktywna jest plansza użytkownika if (ownBoardIsActive) { // Aktywna jest plansza przeciwnika
$("#secondaryBoard").removeClass("secondary"); $("#secondaryBoard").removeClass("secondary");
$("#board").addClass("secondary"); $("#board").addClass("secondary");
$(".ownBoardInfo").css("opacity", 0); } else { // Aktywna jest plansza gracza
$(".controlsOwnBoard").css("opacity", 0.3);
} else { // Aktywna jest plansza przeciwnika
$("#board").removeClass("secondary"); $("#board").removeClass("secondary");
$("#secondaryBoard").addClass("secondary"); $("#secondaryBoard").addClass("secondary");
$(".ownBoardInfo").css("opacity", 1);
$(".controlsOwnBoard").css("opacity", 1);
} }
ownBoardIsActive = !ownBoardIsActive; ownBoardIsActive = !ownBoardIsActive;

View File

@ -5,6 +5,11 @@ var timerDestination = null;
var gamePhase = 'pregame'; var gamePhase = 'pregame';
var occupiedFields = []; var occupiedFields = [];
var shipsSunk = [0, 0, 0, 0];
var hits = 0;
var misses = 0;
var lastTimeClick = 0; var lastTimeClick = 0;
if ($(window).width() <= 820) { if ($(window).width() <= 820) {
@ -48,8 +53,6 @@ $('#board .field').on('click', function () {
if (new Date().getTime() / 1000 - lastTimeClick > 0.3) { if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
if ($(window).width() > 820) { if ($(window).width() > 820) {
socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation); socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation);
navigator.vibrate(200);
lastTimeClick = new Date().getTime() / 1000; lastTimeClick = new Date().getTime() / 1000;
} }
} }
@ -66,8 +69,6 @@ $('#secondaryBoard .field').on('click', function () {
if (new Date().getTime() / 1000 - lastTimeClick > 0.3) { if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
if ($(window).width() > 820) { if ($(window).width() > 820) {
socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y')); socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y'));
navigator.vibrate(200);
lastTimeClick = new Date().getTime() / 1000; lastTimeClick = new Date().getTime() / 1000;
} }
} }
@ -143,6 +144,8 @@ socket.on("shot hit", (victimIdx, posX, posY) => {
bsc.setField(posX, posY, "hit"); bsc.setField(posX, posY, "hit");
} else { } else {
bsc.setFieldEnemy(posX, posY, "hit"); bsc.setFieldEnemy(posX, posY, "hit");
hits++;
updateAccuracy(getAccuracy());
} }
}); });
@ -151,6 +154,8 @@ socket.on("shot missed", (victimIdx, posX, posY) => {
bsc.setField(posX, posY, "miss"); bsc.setField(posX, posY, "miss");
} else { } else {
bsc.setFieldEnemy(posX, posY, "miss"); 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"); bsc.setFieldEnemy(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken");
}, i * 150); }, i * 150);
} }
shipsSunk[ship.type]++;
updateShipsSunk();
} }
}); });
@ -246,6 +254,19 @@ socket.on('turn update', (turnData) => {
$("#whosTurn").html(window.locale["Preparation phase"]); $("#whosTurn").html(window.locale["Preparation phase"]);
$(".boardSwitch").css("opacity", 0.3); $(".boardSwitch").css("opacity", 0.3);
} else { } else {
if (!postPrep) {
$(".readyButton").css({ pointerEvents: 'none', opacity: 0.3 });
$(".controlsOwnBoard").css("opacity", 0.3);
$(".ownBoardInfo").addClass("changing");
setTimeout(() => {
$(".ownBoardInfo").html($(".lateBoardInfo").html());
$(".ownBoardInfo").removeClass("changing");
}, 200);
}
postPrep = true; postPrep = true;
myTurn = turnData.turn === playerIdx; myTurn = turnData.turn === playerIdx;
turnData.turn === playerIdx ? $("#whosTurn").html(window.locale["Your turn"]) : $("#whosTurn").html(window.locale["Opponents turn"]); turnData.turn === playerIdx ? $("#whosTurn").html(window.locale["Your turn"]) : $("#whosTurn").html(window.locale["Opponents turn"]);
@ -256,6 +277,73 @@ socket.on('turn update', (turnData) => {
gamePhase = turnData.phase; gamePhase = turnData.phase;
}); });
function updateLateInfo() {
if (postPrep) {
}
}
var currentAccuracy = 0;
function updateAccuracy(val) {
val = Math.round(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', () => { socket.on('player left', () => {
window.location.replace("/"); window.location.replace("/");
}); });

241
test.js Normal file
View File

@ -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));

View File

@ -75,6 +75,7 @@ export class MailAuth {
if (error) reject(error); if (error) reject(error);
const row = response[0]; const row = response[0];
conn.end();
resolve({ status: 1, uid: row.user_id }); resolve({ status: 1, uid: row.user_id });
}); });
@ -83,10 +84,9 @@ export class MailAuth {
const row = response[0]; const row = response[0];
conn.end();
resolve({ status: 1, uid: row.user_id }); resolve({ status: 1, uid: row.user_id });
}); });
conn.end();
}); });
} }
@ -101,20 +101,21 @@ export class MailAuth {
} else { } else {
resolve(null); resolve(null);
} }
});
conn.end(); conn.end();
}); });
});
} }
startVerification(email, ip, agent) { startVerification(email, ip, agent) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const conn = mysql.createConnection(this.mysqlOptions); const conn = mysql.createConnection(this.mysqlOptions);
conn.query(`SELECT user_id, nickname FROM accounts WHERE email = ${conn.escape(email)}`, async (error, response) => { conn.query(`SELECT user_id, nickname FROM accounts WHERE email = ${conn.escape(email)}`, async (error, response) => {
if (error) { reject(error); return; } if (error) { reject(error); conn.end(); return; }
if (response.length !== 0) { if (response.length !== 0) {
let timer = await this.redis.get(`loginTimer:${response[0].user_id}`); let timer = await this.redis.get(`loginTimer:${response[0].user_id}`);
if (timer && timer > 0) { if (timer && timer > 0) {
conn.end();
resolve({ status: -1, uid: response[0].user_id, }); resolve({ status: -1, uid: response[0].user_id, });
return; return;
} }
@ -155,6 +156,7 @@ export class MailAuth {
reject(e); reject(e);
} }
conn.end();
resolve({ status: 1, uid: row.user_id, code: authCode }); resolve({ status: 1, uid: row.user_id, code: authCode });
}); });
@ -190,10 +192,9 @@ export class MailAuth {
} }
conn.end();
resolve({ status: 1, uid: row.user_id }); resolve({ status: 1, uid: row.user_id });
}); });
conn.end();
}); });
} }
@ -206,10 +207,10 @@ export class MailAuth {
if (error) reject(error); if (error) reject(error);
else resolve(); else resolve();
}); });
});
conn.end(); conn.end();
}); });
});
} }
getProfile(userId) { getProfile(userId) {
@ -227,10 +228,10 @@ export class MailAuth {
resolve({ profile, stats, matchHistory }); resolve({ profile, stats, matchHistory });
} }
});
conn.end(); conn.end();
}); });
});
} }
async finishVerification(uid, authCode) { async finishVerification(uid, authCode) {
@ -251,10 +252,10 @@ export class MailAuth {
conn.query(`UPDATE accounts SET nickname = ${conn.escape(nickname)} WHERE user_id = ${conn.escape(uid)}`, (error) => { conn.query(`UPDATE accounts SET nickname = ${conn.escape(nickname)} WHERE user_id = ${conn.escape(uid)}`, (error) => {
if (error) reject(error); if (error) reject(error);
resolve(); resolve();
});
conn.end(); conn.end();
}); });
});
} }
getNickname(uid) { getNickname(uid) {

View File

@ -5,8 +5,8 @@ export class GameInfo {
} }
async timer(tId, time, callback) { async timer(tId, time, callback) {
await this.redis.set(`timer:${tId}`, new Date().getTime() / 1000); await this.redis.json.set(`timer:${tId}`, '$', { lastUpdate: new Date().getTime() / 1000, end: new Date().getTime() / 1000 + time });
let localLastUpdate = await this.redis.get(`timer:${tId}`); let localLastUpdate = await this.redis.json.get(`timer:${tId}`, { path: ".lastUpdate" });
let timeout = setTimeout(callback, time * 1000); let timeout = setTimeout(callback, time * 1000);
@ -17,7 +17,7 @@ export class GameInfo {
return; return;
} }
let lastUpdate = await this.redis.get(`timer:${tId}`); let lastUpdate = await this.redis.json.get(`timer:${tId}`, { path: ".lastUpdate" });
if (localLastUpdate != lastUpdate) { if (localLastUpdate != lastUpdate) {
// timer has been reset // timer has been reset
clearTimeout(timeout); clearTimeout(timeout);
@ -27,9 +27,15 @@ export class GameInfo {
}, 200); }, 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) { async resetTimer(tId) {
let lastUpdate = await this.redis.get(`timer:${tId}`); let lastUpdate = await this.redis.json.get(`timer:${tId}`, { path: ".end" });
await this.redis.set(`timer:${tId}`, -lastUpdate); await this.redis.json.set(`timer:${tId}`, '.lastUpdate', -lastUpdate);
} }
async isPlayerInGame(socket) { async isPlayerInGame(socket) {
@ -159,13 +165,17 @@ export class GameInfo {
let availableShipsOfType = availableShips[i]; let availableShipsOfType = availableShips[i];
for (let j = 0; j < availableShipsOfType; j++) { for (let j = 0; j < availableShipsOfType; j++) {
playerShips = (await this.redis.json.get(key, { path: `.boards[${playerIdx}].ships` })); playerShips = (await this.redis.json.get(key, { path: `.boards[${playerIdx}].ships` }));
let print = "";
for (let y = 0; y < 10; y++) { for (let y = 0; y < 10; y++) {
let row = ""; let row = "";
for (let x = 0; x < 10; x++) { for (let x = 0; x < 10; x++) {
row += `${boardRender[x][y] ? "\x1b[31m" : "\x1b[32m"}${boardRender[x][y]}\x1b[0m\t`; 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)]; 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); await this.redis.json.set(key, `.boards[${enemyIdx}]`, playerBoard);
return { status: 1, ship: shotShip }; 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) { export function isPlayerInRoom(socket) {
@ -423,16 +443,18 @@ export function checkTurn(data, playerId) {
} }
function findEmptyFields(grid, len) { function findEmptyFields(grid, len) {
const rowPlacements = []; const shipPlacements = [];
// Helper function to check if a row can be placed horizontally at a given position // Helper function to check if a row can be placed horizontally at a given position
function canPlaceHorizontally(x, y) { function canPlaceHorizontally(x, y) {
if (x + len >= grid[0].length) { // Check if the ship exceeds the board boundaries horizontally
return false; // Ship exceeds board boundaries 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]) { if (grid[i][y]) {
return false; // One of ship's fields is already occupied return false;
} }
} }
return true; 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 // Helper function to check if a row can be placed vertically at a given position
function canPlaceVertically(x, y) { function canPlaceVertically(x, y) {
if (y + len >= grid.length) { // Check if the ship exceeds the board boundaries vertically
return false; // Ship exceeds board boundaries 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]) { if (grid[x][i]) {
return false; // One of ship's fields is already occupied return false;
} }
} }
return true; return true;
} }
// Loop through the grid to find empty places
for (let i = 0; i < grid.length; i++) { for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid[0].length; j++) { for (let j = 0; j < grid[0].length; j++) {
if (grid[j][i] === false) { if (!grid[i][j]) { // Check if the current position is empty
if (canPlaceHorizontally(j, i)) { if (canPlaceHorizontally(i, j)) {
rowPlacements.push({ posX: j, posY: i, rot: 0 }); shipPlacements.push({ posX: i, posY: j, rot: 0 });
} }
if (canPlaceVertically(j, i)) { if (canPlaceVertically(i, j)) {
rowPlacements.push({ posX: j, posY: i, rot: 1 }); shipPlacements.push({ posX: i, posY: j, rot: 1 });
} }
} }
} }
} }
return rowPlacements; return shipPlacements;
} }
function clamp(n, min, max) { function clamp(n, min, max) {

View File

@ -9,6 +9,8 @@ export class Lang {
constructor(langs) { constructor(langs) {
const languagesPath = path.join(__dirname, '../lang'); const languagesPath = path.join(__dirname, '../lang');
this.allText = null; this.allText = null;
if (langs && langs.length > 0) {
for (let i = 0; i < langs.length; i++) { for (let i = 0; i < langs.length; i++) {
const lang = langs[i]; const lang = langs[i];
@ -22,6 +24,7 @@ export class Lang {
} }
} }
} }
}
this.allText = JSON.parse(fs.readFileSync(path.join(languagesPath, 'en.json'), 'utf8')); this.allText = JSON.parse(fs.readFileSync(path.join(languagesPath, 'en.json'), 'utf8'));
this.lang = 'en'; this.lang = 'en';

View File

@ -17,6 +17,17 @@
<h2 class="dynamic" id="selectedShip">{{ t 'board.Single-masted' }}</h2> <h2 class="dynamic" id="selectedShip">{{ t 'board.Single-masted' }}</h2>
<h3>{{ t 'board.Available:' }} <span class="dynamic danger" id="shipsLeft">-</span></h3> <h3>{{ t 'board.Available:' }} <span class="dynamic danger" id="shipsLeft">-</span></h3>
</div> </div>
<div class="lateBoardInfo">
<h3>{{ t 'board.Sunk ships' }}</h3>
<p>
{{ t 'board.Single-mastedPlu' }} <span class="dynamic shipnote" id="singlemasted">0</span><br>
{{ t 'board.Two-mastedPlu' }} <span class="dynamic shipnote" id="twomasted">0</span><br>
{{ t 'board.Three-mastedPlu' }} <span class="dynamic shipnote" id="threemasted">0</span><br>
{{ t 'board.Four-mastedPlu' }} <span class="dynamic shipnote" id="fourmasted">0</span><br>
</p>
<h3>{{ t 'board.Your accuracy' }}</h3>
<h2 id="accuracy">-</h2>
</div>
<span class="break"></span> <span class="break"></span>
<div class="controls"> <div class="controls">
<h2>{{ t 'board.Controls' }}</h2> <h2>{{ t 'board.Controls' }}</h2>
@ -29,6 +40,7 @@
<button class="boardSwitch" onclick="switchBoards()">{{ t 'board.Change boards' }}</button> <button class="boardSwitch" onclick="switchBoards()">{{ t 'board.Change boards' }}</button>
</div> </div>
<span class="break"></span> <span class="break"></span>
<button class="readyButton" onclick="readyUp()">{{ t 'board.Ready up' }}</button>
<h3><span class="dynamic" id="whosTurn"></span></h3> <h3><span class="dynamic" id="whosTurn"></span></h3>
<h2 class="important" id="timer">∞</h2> <h2 class="important" id="timer">∞</h2>
</div> </div>