2024-03-01 22:24:30 +01:00
|
|
|
const socket = io();
|
|
|
|
|
2024-03-02 13:56:01 +01:00
|
|
|
var playerIdx;
|
|
|
|
var timerDestination = null;
|
2024-03-03 01:29:11 +01:00
|
|
|
var gamePhase = 'pregame';
|
2024-03-03 22:59:52 +01:00
|
|
|
var occupiedFields = [];
|
2024-03-03 01:29:11 +01:00
|
|
|
|
2024-04-06 21:18:38 +02:00
|
|
|
var shipsSunk = [0, 0, 0, 0];
|
|
|
|
|
|
|
|
var hits = 0;
|
|
|
|
var misses = 0;
|
|
|
|
|
2024-03-08 19:18:53 +01:00
|
|
|
var lastTimeClick = 0;
|
|
|
|
|
2024-03-25 17:40:07 +01:00
|
|
|
if ($(window).width() <= 820) {
|
|
|
|
tippy('#board .field', {
|
|
|
|
allowHTML: true,
|
|
|
|
placement: "top",
|
|
|
|
theme: "translucent",
|
|
|
|
animation: "shift-toward-subtle",
|
|
|
|
interactive: true,
|
|
|
|
content: (reference) => {
|
2024-04-04 16:08:27 +02:00
|
|
|
// Need to fix this
|
|
|
|
|
|
|
|
console.log("a");
|
2024-03-25 17:40:07 +01:00
|
|
|
let fieldData = `${$(reference).data('pos-x')}, ${$(reference).data('pos-y')}`;
|
|
|
|
|
2024-04-04 16:08:27 +02:00
|
|
|
let pos = occupiedFields.find((elem) => elem.pos[0] == $(reference).data('pos-x') && elem.pos[1] == $(reference).data('pos-y'));
|
|
|
|
|
|
|
|
if (pos) {
|
|
|
|
return $('#removeTippyTemplate').html().replaceAll("[[FIELDPOS]]", fieldData);
|
|
|
|
}
|
|
|
|
|
2024-03-25 17:40:07 +01:00
|
|
|
return $('#mainTippyTemplate').html().replaceAll("[[FIELDPOS]]", fieldData);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
tippy('#secondaryBoard .field', {
|
|
|
|
allowHTML: true,
|
|
|
|
placement: "top",
|
|
|
|
theme: "translucent",
|
|
|
|
animation: "shift-toward-subtle",
|
|
|
|
interactive: true,
|
|
|
|
content: (reference) => {
|
|
|
|
let fieldData = `${$(reference).data('pos-x')}, ${$(reference).data('pos-y')}`;
|
|
|
|
|
|
|
|
return $('#secondaryTippyTemplate').html().replaceAll("[[FIELDPOS]]", fieldData);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2024-03-25 12:41:02 +01:00
|
|
|
|
2024-03-07 21:56:44 +01:00
|
|
|
$('#board .field').on('click', function () {
|
2024-03-08 19:18:53 +01:00
|
|
|
if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
|
2024-03-25 12:41:02 +01:00
|
|
|
if ($(window).width() > 820) {
|
|
|
|
socket.emit("place ship", selectedShip, $(this).data('pos-x'), $(this).data('pos-y'), shipRotation);
|
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
2024-03-25 17:40:07 +01:00
|
|
|
}
|
2024-03-08 19:18:53 +01:00
|
|
|
}
|
2024-03-03 01:29:11 +01:00
|
|
|
});
|
|
|
|
|
2024-03-25 12:41:02 +01:00
|
|
|
function manualPlace(posX, posY) {
|
2024-03-31 15:08:31 +02:00
|
|
|
hoveredShip = null;
|
|
|
|
refreshBoardView();
|
2024-03-25 12:41:02 +01:00
|
|
|
socket.emit("place ship", selectedShip, posX, posY, shipRotation);
|
2024-04-04 16:08:27 +02:00
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
2024-03-25 12:41:02 +01:00
|
|
|
}
|
|
|
|
|
2024-03-07 21:56:44 +01:00
|
|
|
$('#secondaryBoard .field').on('click', function () {
|
2024-03-08 19:18:53 +01:00
|
|
|
if (new Date().getTime() / 1000 - lastTimeClick > 0.3) {
|
2024-03-25 17:40:07 +01:00
|
|
|
if ($(window).width() > 820) {
|
|
|
|
socket.emit("shoot", $(this).data('pos-x'), $(this).data('pos-y'));
|
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
|
|
|
}
|
2024-03-08 19:18:53 +01:00
|
|
|
}
|
2024-03-07 21:56:44 +01:00
|
|
|
});
|
|
|
|
|
2024-03-31 15:08:31 +02:00
|
|
|
function manualShoot(posX, posY) {
|
|
|
|
socket.emit("shoot", posX, posY);
|
2024-04-04 16:08:27 +02:00
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
2024-03-31 15:08:31 +02:00
|
|
|
}
|
|
|
|
|
2024-03-03 22:59:52 +01:00
|
|
|
$('.field').on('contextmenu', function () {
|
2024-03-08 19:18:53 +01:00
|
|
|
if ($(this).hasClass('active') && new Date().getTime() / 1000 - lastTimeClick > 0.3) {
|
2024-04-04 16:08:27 +02:00
|
|
|
let pos = occupiedFields.find((elem) => elem.pos[0] == $(this).data('pos-x') && elem.pos[1] == $(this).data('pos-y'));
|
2024-03-03 22:59:52 +01:00
|
|
|
|
2024-04-04 16:08:27 +02:00
|
|
|
if (pos) {
|
|
|
|
socket.emit("remove ship", pos.origin[0], pos.origin[1]);
|
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
|
|
|
}
|
2024-03-03 22:59:52 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-04-04 16:08:27 +02:00
|
|
|
function manualRemove(posX, posY) {
|
|
|
|
let pos = occupiedFields.find((elem) => elem.pos[0] == posX && elem.pos[1] == posY);
|
|
|
|
|
|
|
|
if (pos) {
|
|
|
|
socket.emit("remove ship", pos.origin[0], pos.origin[1]);
|
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-03 01:29:11 +01:00
|
|
|
socket.on('toast', (msg) => {
|
|
|
|
Toastify({
|
|
|
|
text: msg,
|
|
|
|
duration: 5000,
|
|
|
|
newWindow: true,
|
|
|
|
gravity: "bottom",
|
|
|
|
position: "right",
|
|
|
|
stopOnFocus: true,
|
|
|
|
className: "bshipstoast",
|
|
|
|
}).showToast();
|
|
|
|
});
|
2024-03-02 13:56:01 +01:00
|
|
|
|
2024-03-03 16:55:38 +01:00
|
|
|
socket.on("placed ship", (data) => {
|
2024-03-03 22:59:52 +01:00
|
|
|
let shipFields = bsc.placeShip(data);
|
2024-03-10 11:59:18 +01:00
|
|
|
lastTimeClick = new Date().getTime() / 1000;
|
2024-03-03 22:59:52 +01:00
|
|
|
shipFields.forEach(field => {
|
|
|
|
occupiedFields.push({pos: field, origin: [data.posX, data.posY]});
|
|
|
|
});
|
2024-03-03 16:55:38 +01:00
|
|
|
shipsLeft[data.type]--;
|
|
|
|
refreshBoardView();
|
|
|
|
});
|
|
|
|
|
2024-03-03 22:59:52 +01:00
|
|
|
socket.on("removed ship", (data) => {
|
|
|
|
const shipFields = occupiedFields.filter(elem => {
|
|
|
|
return elem.origin[0] == data.posX && elem.origin[1] == data.posY;
|
|
|
|
});
|
|
|
|
|
2024-03-08 19:18:53 +01:00
|
|
|
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);
|
|
|
|
}
|
2024-03-03 22:59:52 +01:00
|
|
|
|
2024-03-04 12:23:47 +01:00
|
|
|
occupiedFields = occupiedFields.filter(n => !shipFields.includes(n));
|
2024-03-03 22:59:52 +01:00
|
|
|
|
|
|
|
shipsLeft[data.type]++;
|
|
|
|
refreshBoardView();
|
|
|
|
});
|
|
|
|
|
2024-03-07 21:56:44 +01:00
|
|
|
socket.on("shot hit", (victimIdx, posX, posY) => {
|
|
|
|
if (victimIdx === playerIdx) {
|
|
|
|
bsc.setField(posX, posY, "hit");
|
|
|
|
} else {
|
|
|
|
bsc.setFieldEnemy(posX, posY, "hit");
|
2024-04-06 21:18:38 +02:00
|
|
|
hits++;
|
|
|
|
updateAccuracy(getAccuracy());
|
2024-03-07 21:56:44 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on("shot missed", (victimIdx, posX, posY) => {
|
|
|
|
if (victimIdx === playerIdx) {
|
|
|
|
bsc.setField(posX, posY, "miss");
|
|
|
|
} else {
|
|
|
|
bsc.setFieldEnemy(posX, posY, "miss");
|
2024-04-06 21:18:38 +02:00
|
|
|
misses++;
|
|
|
|
updateAccuracy(getAccuracy());
|
2024-03-07 21:56:44 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-03-08 19:18:53 +01:00
|
|
|
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++) {
|
|
|
|
setTimeout(() => {
|
|
|
|
bsc.setField(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken");
|
|
|
|
}, i * 150);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (let i = 0; i < l; i++) {
|
|
|
|
setTimeout(() => {
|
|
|
|
bsc.setFieldEnemy(ship.posX + multips[0] * i, ship.posY + multips[1] * i, "sunken");
|
|
|
|
}, i * 150);
|
|
|
|
}
|
2024-04-06 21:18:38 +02:00
|
|
|
|
|
|
|
shipsSunk[ship.type]++;
|
|
|
|
updateShipsSunk();
|
2024-03-08 19:18:53 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2024-03-08 21:35:01 +01:00
|
|
|
// Update timer
|
|
|
|
var updateTimer = setInterval(() => {
|
|
|
|
if (timerDestination == null) {
|
|
|
|
$("#timer").html("");
|
|
|
|
} else {
|
|
|
|
const UTCNow = Math.floor((new Date()).getTime() / 1000);
|
|
|
|
|
2024-04-04 16:08:27 +02:00
|
|
|
const time = Math.max(timerDestination - UTCNow, 0);
|
2024-03-08 21:35:01 +01:00
|
|
|
|
|
|
|
if (time < 10) {
|
|
|
|
$("#timer").addClass("active");
|
|
|
|
} else {
|
|
|
|
$("#timer").removeClass("active");
|
|
|
|
}
|
|
|
|
|
2024-03-27 15:51:33 +01:00
|
|
|
const minutes = Math.floor(time / 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false });
|
|
|
|
const seconds = (time - minutes * 60).toLocaleString(undefined, { minimumIntegerDigits: 2, useGrouping: false });
|
2024-03-08 21:35:01 +01:00
|
|
|
|
|
|
|
$("#timer").html(`${minutes}:${seconds}`);
|
|
|
|
}
|
|
|
|
}, 250);
|
|
|
|
|
|
|
|
socket.on("game finished", (winnerIdx, oppName) => {
|
|
|
|
socket.disconnect();
|
|
|
|
$("#opponent").html(`Vs. <span class="important">${oppName}</span>`);
|
|
|
|
|
2024-03-08 19:18:53 +01:00
|
|
|
if (winnerIdx === playerIdx) {
|
2024-04-04 16:08:27 +02:00
|
|
|
$("#state").html(locale["Victory"]);
|
2024-03-08 21:35:01 +01:00
|
|
|
$("#state").addClass("dynamic");
|
2024-03-08 19:18:53 +01:00
|
|
|
} else {
|
2024-04-04 16:08:27 +02:00
|
|
|
$("#state").html(locale["Defeat"]);
|
2024-03-08 21:35:01 +01:00
|
|
|
$("#state").addClass("danger");
|
2024-03-08 19:18:53 +01:00
|
|
|
}
|
2024-03-08 21:35:01 +01:00
|
|
|
|
|
|
|
$(".cover").addClass("postGame");
|
|
|
|
clearInterval(updateTimer);
|
|
|
|
$("#timer").html(`00:00`);
|
|
|
|
$(".cover").css({ opacity: 1, pointerEvents: "all" });
|
2024-03-08 19:18:53 +01:00
|
|
|
});
|
|
|
|
|
2024-03-02 13:56:01 +01:00
|
|
|
socket.on('connect', () => {
|
2024-03-27 15:51:33 +01:00
|
|
|
$(".cover .title").html(window.locale["Waiting for the server"]);
|
2024-03-02 13:56:01 +01:00
|
|
|
});
|
|
|
|
|
2024-03-01 22:24:30 +01:00
|
|
|
socket.on("players ready", () => {
|
2024-03-08 21:35:01 +01:00
|
|
|
$(".cover").css({ opacity: 0, pointerEvents: "none" });
|
2024-03-02 13:56:01 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
socket.on("player idx", (idx) => {
|
|
|
|
playerIdx = idx;
|
|
|
|
});
|
|
|
|
|
|
|
|
socket.on('turn update', (turnData) => {
|
2024-03-02 18:28:33 +01:00
|
|
|
if (turnData.phase === "preparation") {
|
2024-03-27 15:51:33 +01:00
|
|
|
$("#whosTurn").html(window.locale["Preparation phase"]);
|
2024-03-25 17:40:07 +01:00
|
|
|
$(".boardSwitch").css("opacity", 0.3);
|
2024-03-02 18:28:33 +01:00
|
|
|
} else {
|
2024-04-06 21:18:38 +02:00
|
|
|
if (!postPrep) {
|
2024-04-06 21:44:18 +02:00
|
|
|
$(".readyButton").css({ pointerEvents: 'none', opacity: 0.3 });
|
|
|
|
|
2024-04-06 21:18:38 +02:00
|
|
|
$(".controlsOwnBoard").css("opacity", 0.3);
|
|
|
|
|
|
|
|
$(".ownBoardInfo").addClass("changing");
|
|
|
|
setTimeout(() => {
|
|
|
|
$(".ownBoardInfo").html($(".lateBoardInfo").html());
|
|
|
|
|
|
|
|
$(".ownBoardInfo").removeClass("changing");
|
|
|
|
}, 200);
|
|
|
|
}
|
|
|
|
|
2024-03-04 20:34:45 +01:00
|
|
|
postPrep = true;
|
|
|
|
myTurn = turnData.turn === playerIdx;
|
2024-03-27 15:51:33 +01:00
|
|
|
turnData.turn === playerIdx ? $("#whosTurn").html(window.locale["Your turn"]) : $("#whosTurn").html(window.locale["Opponents turn"]);
|
2024-03-25 17:40:07 +01:00
|
|
|
$(".boardSwitch").css("opacity", 1);
|
2024-03-02 18:28:33 +01:00
|
|
|
}
|
2024-03-02 13:56:01 +01:00
|
|
|
|
|
|
|
timerDestination = turnData.timerToUTC;
|
|
|
|
gamePhase = turnData.phase;
|
|
|
|
});
|
|
|
|
|
2024-04-06 21:18:38 +02:00
|
|
|
function updateLateInfo() {
|
|
|
|
if (postPrep) {
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var currentAccuracy = 0;
|
|
|
|
|
|
|
|
function updateAccuracy(val) {
|
2024-04-06 21:27:46 +02:00
|
|
|
val = Math.round(val);
|
|
|
|
|
2024-04-06 21:18:38 +02:00
|
|
|
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 });
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-03-02 13:56:01 +01:00
|
|
|
socket.on('player left', () => {
|
|
|
|
window.location.replace("/");
|
2024-03-27 15:51:33 +01:00
|
|
|
});
|