Major changes

- Fully working ship placement system
- Validating ship positions works too
- Client side ship data display works as well
- Changes to field colors to dark grey instead of light. This improves comfort of use and ensures proper contrast on lower quality displays and ease of use.
This commit is contained in:
MaciejkaG 2024-03-03 16:55:38 +01:00
parent 7c4801db2d
commit bb17dc47ba
7 changed files with 116 additions and 50 deletions

View File

@ -238,11 +238,15 @@ io.on('connection', async (socket) => {
if (playerGame.data.state === 'preparation') { if (playerGame.data.state === 'preparation') {
const playerShips = await GInfo.getPlayerShips(socket); const playerShips = await GInfo.getPlayerShips(socket);
let canPlace = bships.validateShipPosition(playerShips, type, posX, posY, rot); let canPlace = bships.validateShipPosition(playerShips, type, posX, posY, rot);
let shipAvailable = bships.getShipsAvailable(playerShips)[type] > 0;
if (canPlace) { if (!canPlace) {
console.log("placed"); socket.emit("toast", "Nie możesz postawić tak statku");
} else if (!shipAvailable) {
socket.emit("toast", "Nie masz już statków tego typu");
} else { } else {
socket.emit("toast", "Nie możesz postawić tak statku") await GInfo.placeShip(socket, { type: type, posX: posX, posY: posY, rot: rot });
socket.emit("placed ship", { type: type, posX: posY, posY: posX, rot: rot });
} }
} }
}); });

View File

@ -2,9 +2,9 @@
font-size: 20px; font-size: 20px;
--field: rgb(201, 201, 201); --field: rgb(36, 36, 36);
--mark-line: rgb(136, 136, 136); --mark-line: rgb(59, 59, 59);
--mark-spot: rgb(68, 68, 68); --mark-spot: rgb(90, 90, 90);
--mark-ship-valid: hsl(120, 100%, 80%); --mark-ship-valid: hsl(120, 100%, 80%);
--mark-ship-invalid: hsl(0, 100%, 80%); --mark-ship-invalid: hsl(0, 100%, 80%);
--ship-valid: hsl(120, 70%, 55%); --ship-valid: hsl(120, 70%, 55%);
@ -77,13 +77,19 @@ h1,h2,h3,h4,h5,h6 {
background: var(--ship-valid); background: var(--ship-valid);
pointer-events: none; pointer-events: none;
opacity: 0; opacity: 0;
transition: opacity 0.15s; transform: scale(0);
transition: opacity 0.25s, transform 0.25s 0.05s;
} }
#secondaryBoard .field .shipField { #secondaryBoard .field .shipField {
background: var(--ship-invalid); background: var(--ship-invalid);
} }
.field .shipField.active {
transform: scale(1);
opacity: 1;
}
.field svg { .field svg {
opacity: 0; opacity: 0;
width: 100%; width: 100%;

View File

@ -3,7 +3,7 @@ class Battleships {
if (boardSize>0) { if (boardSize>0) {
this.boardSize = boardSize; this.boardSize = boardSize;
} else { } else {
throw new Error("Wrong boardSize for the 'Battleships' class"); throw new Error("Incorrect boardSize for the 'Battleships' class");
} }
} }
@ -22,7 +22,9 @@ class Battleships {
} }
getField(x, y) { getField(x, y) {
if (0 < x && x <= this.boardSize && 0 < y && y <= this.boardSize) { if (0 <= x && x < this.boardSize && 0 <= y && y < this.boardSize) {
x++;
y++;
return $(`#board .row:nth-child(${y}) .field:nth-child(${x})`); return $(`#board .row:nth-child(${y}) .field:nth-child(${x})`);
} else { } else {
throw new RangeError("getField position out of range."); throw new RangeError("getField position out of range.");
@ -30,20 +32,50 @@ class Battleships {
} }
getRow(row) { getRow(row) {
row = parseInt(row)+1 row++;
if (row<=this.boardSize) { if (row<=this.boardSize) {
return $(`#board .row:nth-child(${row}) .field`); return $(`#board .row:nth-child(${row}) .field`);
} else { } else {
throw new RangeError("getColumn position out of range."); throw new RangeError("getRow position out of range.");
} }
} }
getColumn(column) { getColumn(column) {
column = parseInt(column)+1 column++;
if (column<=this.boardSize) { if (column<=this.boardSize) {
return $(`#board .row .field:nth-child(${column})`); return $(`#board .row .field:nth-child(${column})`);
} else { } else {
throw new RangeError("getColumn position out of range."); throw new RangeError("getColumn position out of range.");
} }
} }
placeShip(data) {
let fields = [];
switch (data.rot) {
case 0:
for (let i = 0; i <= data.type; i++) {
fields.push([data.posX + i, data.posY]);
}
break;
case 1:
for (let i = 0; i <= data.type; i++) {
fields.push([data.posX, data.posY + i]);
}
break;
case 2:
for (let i = 0; i <= data.type; i++) {
fields.push([data.posX - i, data.posY]);
}
break;
case 3:
for (let i = 0; i <= data.type; i++) {
fields.push([data.posX, data.posY - i]);
}
break;
}
fields.forEach(field => {
this.getField(field[0], field[1]).children(".shipField").addClass("active");
});
}
} }

View File

@ -10,10 +10,13 @@ var previousRow = $(":not(*)");
var previousColumn = $(":not(*)"); var previousColumn = $(":not(*)");
var selectedShip = 0; var selectedShip = 0;
var shipRotation = 0; var shipRotation = 0;
var shipsLeft = [4, 3, 2, 1];
var changedFields = []; var changedFields = [];
var hoveredField = null; var hoveredField = null;
refreshBoardView();
$("#board .field").hover(function () { $("#board .field").hover(function () {
hoveredField = this; hoveredField = this;
// Pokaż "miarki" // Pokaż "miarki"
@ -24,17 +27,15 @@ $("#board .field").hover(function () {
changedFields.push(row, column, $(this)); changedFields.push(row, column, $(this));
row.css("background", "rgb(136, 136, 136)"); row.css("background", "var(--mark-line)");
column.css("background", "rgb(136, 136, 136)"); column.css("background", "var(--mark-line)");
$(this).css("background", "rgb(68, 68, 68)"); $(this).css("background", "var(--mark-spot)");
previousRow = row; previousRow = row;
previousColumn = column; previousColumn = column;
// Pokaż podgląd statku // Pokaż podgląd statku
posX++;
posY++;
var fields = []; var fields = [];
switch (shipRotation) { switch (shipRotation) {
case 0: case 0:
@ -61,7 +62,7 @@ $("#board .field").hover(function () {
var fieldElem; var fieldElem;
let failed = false; let failed = false;
for (let i = 0; i < selectedShip+1; i++) { for (let i = 0; i <= selectedShip; i++) {
const field = fields[i]; const field = fields[i];
try { try {
@ -74,9 +75,9 @@ $("#board .field").hover(function () {
} }
if (failed) { if (failed) {
fieldElem.css("background", "rgb(255, 163, 163)"); fieldElem.css("background", "var(--mark-ship-invalid)");
} else { } else {
fieldElem.css("background", "rgb(163, 255, 163)"); fieldElem.css("background", "var(--mark-ship-valid)");
} }
changedFields.push(fieldElem); changedFields.push(fieldElem);
} }
@ -84,7 +85,7 @@ $("#board .field").hover(function () {
hoveredField = null; hoveredField = null;
// Wyłącz "miarki" po wyjściu kursora z pola (aby się nie duplikowały w przyszłości) // Wyłącz "miarki" po wyjściu kursora z pola (aby się nie duplikowały w przyszłości)
changedFields.forEach(field => { changedFields.forEach(field => {
field.css("background", "rgb(201, 201, 201)"); field.css("background", "var(--field)");
}); });
changedFields.length = 0; changedFields.length = 0;
}); });
@ -153,9 +154,17 @@ function switchRotation() {
} }
function refreshBoardView() { function refreshBoardView() {
let shipsOfType = shipsLeft[selectedShip];
$("#shipsLeft").html(shipsOfType);
if (!shipsOfType) {
$("#shipsLeft").addClass("danger");
} else {
$("#shipsLeft").removeClass("danger");
}
if (hoveredField) { if (hoveredField) {
changedFields.forEach(field => { changedFields.forEach(field => {
field.css("background", "rgb(201, 201, 201)"); field.css("background", "var(--field)");
}); });
changedFields.length = 0; changedFields.length = 0;
@ -166,16 +175,14 @@ function refreshBoardView() {
changedFields.push(row, column, $(hoveredField)); changedFields.push(row, column, $(hoveredField));
row.css("background", "rgb(136, 136, 136)"); row.css("background", "var(--mark-line)");
column.css("background", "rgb(136, 136, 136)"); column.css("background", "var(--mark-line)");
$(hoveredField).css("background", "rgb(68, 68, 68)"); $(hoveredField).css("background", "var(--mark-field)");
previousRow = row; previousRow = row;
previousColumn = column; previousColumn = column;
posX++;
posY++;
var fields = []; var fields = [];
switch (shipRotation) { switch (shipRotation) {
case 0: case 0:
@ -202,7 +209,7 @@ function refreshBoardView() {
var fieldElem; var fieldElem;
let failed = false; let failed = false;
for (let i = 0; i < selectedShip + 1; i++) { for (let i = 0; i <= selectedShip; i++) {
const field = fields[i]; const field = fields[i];
try { try {
@ -215,9 +222,9 @@ function refreshBoardView() {
} }
if (failed) { if (failed) {
fieldElem.css("background", "rgb(255, 163, 163)"); fieldElem.css("background", "var(--mark-ship-invalid)");
} else { } else {
fieldElem.css("background", "rgb(163, 255, 163)"); fieldElem.css("background", "var(--mark-ship-valid)");
} }
changedFields.push(fieldElem); changedFields.push(fieldElem);
} }

View File

@ -5,7 +5,6 @@ var timerDestination = null;
var gamePhase = 'pregame'; var gamePhase = 'pregame';
$('.field').on('click', function () { $('.field').on('click', function () {
console.log("Clicked");
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);
}); });
@ -21,6 +20,12 @@ socket.on('toast', (msg) => {
}).showToast(); }).showToast();
}); });
socket.on("placed ship", (data) => {
bsc.placeShip(data);
shipsLeft[data.type]--;
refreshBoardView();
});
socket.on('connect', () => { socket.on('connect', () => {
$(".cover h1").html("Oczekiwanie na serwer..."); $(".cover h1").html("Oczekiwanie na serwer...");
}); });
@ -30,9 +35,7 @@ socket.on("players ready", () => {
}); });
socket.on("player idx", (idx) => { socket.on("player idx", (idx) => {
console.log(idx);
playerIdx = idx; playerIdx = idx;
console.log(playerIdx);
}); });
socket.on('turn update', (turnData) => { socket.on('turn update', (turnData) => {

View File

@ -35,14 +35,23 @@ export class GameInfo {
const gameId = socket.session.activeGame; const gameId = socket.session.activeGame;
const key = `game:${gameId}`; const key = `game:${gameId}`;
await this.redis.json.set(key, '$.state', 'action'); await this.redis.json.set(key, '.state', 'action');
let nextPlayer = await this.redis.json.get(key, '$.nextPlayer'); let nextPlayer = await this.redis.json.get(key, '.nextPlayer');
nextPlayer = nextPlayer === 0 ? 1 : 0; nextPlayer = nextPlayer === 0 ? 1 : 0;
await this.redis.json.set(key, '$.nextPlayer', nextPlayer); await this.redis.json.set(key, '.nextPlayer', nextPlayer);
const UTCTs = Math.floor((new Date()).getTime() / 1000 + 30); const UTCTs = Math.floor((new Date()).getTime() / 1000 + 30);
this.io.to(gameId).emit('turn update', { turn: 0, phase: "action", timerToUTC: UTCTs }); this.io.to(gameId).emit('turn update', { turn: 0, phase: "action", timerToUTC: UTCTs });
} }
async placeShip(socket, shipData) {
const gameId = socket.session.activeGame;
const key = `game:${gameId}`;
const hostId = (await this.redis.json.get(key, {path: '$.hostId'}))[0];
const playerIdx = socket.request.session.id === hostId ? 0 : 1;
let res = await this.redis.json.arrAppend(key, `.boards[${playerIdx}].ships`, shipData);
}
} }
export function isPlayerInRoom(socket) { export function isPlayerInRoom(socket) {
@ -95,12 +104,10 @@ export function resetTimers() {
// }); // });
// } // }
export function getShipsAvailable(data, playerIdx) { export function getShipsAvailable(ships) {
let shipsLeft = [4, 3, 2, 1]; let shipsLeft = [4, 3, 2, 1];
const playerShips = shipsLeft.boards[playerIdx].ships; ships.forEach(ship => {
playerShips.forEach(ship => {
shipsLeft[ship.type]--; shipsLeft[ship.type]--;
}); });
@ -152,8 +159,6 @@ export function checkHit(data, playerIdx, posX, posY) {
} }
export function validateShipPosition(ships, type, posX, posY, rot) { export function validateShipPosition(ships, type, posX, posY, rot) {
let multips;
let boardRender = []; let boardRender = [];
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
@ -165,6 +170,7 @@ export function validateShipPosition(ships, type, posX, posY, rot) {
} }
ships.forEach(ship => { ships.forEach(ship => {
let multips; let multips;
switch (ship.rot) { switch (ship.rot) {
@ -181,7 +187,7 @@ export function validateShipPosition(ships, type, posX, posY, rot) {
break; break;
case 3: case 3:
multips = [1, 0]; multips = [-1, 0];
break; break;
} }
@ -190,6 +196,8 @@ export function validateShipPosition(ships, type, posX, posY, rot) {
} }
}); });
let multips;
switch (rot) { switch (rot) {
case 0: case 0:
multips = [0, 1]; multips = [0, 1];
@ -204,18 +212,24 @@ export function validateShipPosition(ships, type, posX, posY, rot) {
break; break;
case 3: case 3:
multips = [1, 0]; multips = [-1, 0];
break; break;
} }
for (let i = 0; i < type + 1; i++) { for (let x = 0; x <= type; x++) {
if (posY + multips[1] * i > 9 || posY + multips[1] * i < 0 || posX + multips[0] * i > 9 || posX + multips[0] * i < 0) { if (posY + multips[1] * x > 9 || posY + multips[1] * x < 0 || posX + multips[0] * x > 9 || posX + multips[0] * x < 0) {
return false; return false;
} }
if (boardRender[posY + multips[1] * i][posX + multips[0] * i]) {
let subtrahents = [[0, 0], [0, 1], [1, 0], [0, -1], [-1, 0], [1, 1], [-1, -1], [1, -1], [-1, 1]]; // Usuń cztery ostatnie elementy jeżeli chcesz by statki mogły się stykać rogami
for (let y = 0; y < subtrahents.length; y++) {
const idxY = posY - subtrahents[y][0] + multips[1] * x;
const idxX = posX - subtrahents[y][1] + multips[0] * x;
if (!(idxY < 0 || idxY > 9 || idxX < 0 || idxX > 9) && boardRender[idxY][idxX]) {
return false; return false;
} }
} }
}
return true; return true;
} }

View File

@ -10,7 +10,7 @@
<div class="ownBoardInfo"> <div class="ownBoardInfo">
<h3>Wybrany statek</h3> <h3>Wybrany statek</h3>
<h2 class="dynamic" id="selectedShip">Jednomasztowiec</h2> <h2 class="dynamic" id="selectedShip">Jednomasztowiec</h2>
<h3>Dostępne: <span class="dynamic danger">1</span></h3> <h3>Dostępne: <span class="dynamic danger" id="shipsLeft">1</span></h3>
</div> </div>
<span class="break"></span> <span class="break"></span>
<h2>Sterowanie</h2> <h2>Sterowanie</h2>