De todo un poco

This commit is contained in:
Marklogo 2024-04-08 02:28:46 +02:00
parent 3444bc59ed
commit 68177c2ffa
15 changed files with 924 additions and 153 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 197 KiB

View File

@ -10,17 +10,24 @@
</head>
<body>
<div id="container">
<div id="sub-container">
<canvas id="mapa" tabindex="0"></canvas>
<div class="parent">
<div class="div1">
<div class="logo" style="width: fit-content;">
<h1 style="font-size: 5rem;display: inline;">BattleShip</h1>
<i style="font-size: 2rem;" class="ti ti-medal-2"></i>
<i style="font-size: 2rem;" class="ti ti-medal-2"></i>
</div>
</div>
<div id="sub-container">
<canvas id="minimapa" tabindex="1"></canvas>
<textarea id="chat" readonly>Battleship</textarea>
<input type="text" name="" id="">
<div class="div2"> <canvas id="mapa" tabindex="-1"></canvas></div>
<div class="div3"> <canvas id="minimapa" tabindex="-1"></canvas></div>
<div class="div4" tabindex="-1"> </div>
<div class="div5" tabindex="-1">
<div id="logchat"> </div>
<input id="chatinput" type="text">
</div>
</div>
</body>
<script src="socket.io/socket.io.js"></script>
<script type="module" src="./js_game/battleship.js"></script>
</html>

63
public/js_game/barco.js Normal file
View File

@ -0,0 +1,63 @@
export const BarcoTipo = Object.freeze({
FRAGATA: 1,
DESTRUCTOR: 2,
ACORAZADO: 3,
PORTAAVIONES: 4,
});
class Barco {
constructor(x, y, longitud, orientacion, barcoImg, tipoNave) {
this.barcoImg = barcoImg;
this.tipoNave = tipoNave;
this.xIni = this.x = x;
this.yIni = this.y = y;
this.longitud = longitud;
this.orientacionIni = this.orientacion = orientacion;
this.seleccionado = false;
this.posIncorrecta = true;
}
clickado(clickX, clickY) {
if (this.orientacion === 'VERTICAL') {
return (
clickX === this.x && clickY >= this.y && clickY < this.y + this.longitud
);
} else {
return (
clickY === this.y && clickX >= this.x && clickX < this.x + this.longitud
);
}
}
saveIniPos() {
this.orientacionIni = this.orientacion;
this.xIni = this.x;
this.yIni = this.y;
}
restoreIniPos() {
this.orientacion = this.orientacionIni;
this.x = this.xIni;
this.y = this.yIni;
}
setXY(x, y) {
this.x = x;
this.y = y;
}
giraBarco() {
this.orientacion === 'VERTICAL'
? (this.orientacion = 'HORIZONTAL')
: (this.orientacion = 'VERTICAL');
}
draw(ctx) {
this.orientacion === 'VERTICAL'
? ctx.drawImage(this.barcoImg[0], this.x * 64, this.y * 64)
: ctx.drawImage(this.barcoImg[1], this.x * 64, this.y * 64);
if (this.seleccionado) {
ctx.fillStyle = this.posIncorrecta
? 'rgba(255, 0, 0, 0.2)'
: 'rgba(187, 187, 0, 0.2)';
this.orientacion === 'VERTICAL'
? ctx.fillRect(this.x * 64, this.y * 64, 64, 64 * this.longitud)
: ctx.fillRect(this.x * 64, this.y * 64, 64 * this.longitud, 64);
}
}
}
export default Barco;

View File

@ -1,6 +1,178 @@
const urlParams = new URLSearchParams(window.location.search);
const gameId = urlParams.get('game_id');
const userId = localStorage.getItem('userId');
import { TableroEditor, TableroVisor } from './tablero.js';
const barcosImg = new Image();
barcosImg.src = '/assets/barcos.png';
const socket = io('/batalla', { closeOnBeforeunload: true, auth: { userId, gameId } });
barcosImg.onload = function () {
iniciaJuego();
};
function getSprites(spriteSheet) {
const sprites = {
PORTAAVIONES: [
{ x: 0, y: 0, width: 64, height: 256 },
{ x: 64, y: 192, width: 256, height: 64 },
],
ACORAZADO: [
{ x: 64, y: 0, width: 64, height: 192 },
{ x: 128, y: 128, width: 192, height: 64 },
],
DESTRUCTOR: [
{ x: 128, y: 0, width: 64, height: 128 },
{ x: 192, y: 64, width: 128, height: 64 },
],
FRAGATA: [
{ x: 192, y: 0, width: 64, height: 64 },
{ x: 256, y: 0, width: 64, height: 64 },
],
OCEANO: [
{ x: 320, y: 0, width: 32, height: 32 },
{ x: 384, y: 0, width: 32, height: 32 },
{ x: 416, y: 0, width: 32, height: 32 },
{ x: 320, y: 32, width: 32, height: 32 },
{ x: 352, y: 64, width: 32, height: 32 },
{ x: 416, y: 64, width: 32, height: 32 },
{ x: 320, y: 96, width: 32, height: 32 },
{ x: 352, y: 96, width: 32, height: 32 },
{ x: 416, y: 96, width: 32, height: 32 },
{ x: 448, y: 0, width: 32, height: 32 },
{ x: 512, y: 0, width: 32, height: 32 },
{ x: 544, y: 0, width: 32, height: 32 },
{ x: 448, y: 32, width: 32, height: 32 },
{ x: 480, y: 64, width: 32, height: 32 },
{ x: 544, y: 64, width: 32, height: 32 },
{ x: 448, y: 96, width: 32, height: 32 },
{ x: 480, y: 96, width: 32, height: 32 },
{ x: 544, y: 96, width: 32, height: 32 },
{ x: 576, y: 0, width: 32, height: 32 },
{ x: 640, y: 0, width: 32, height: 32 },
{ x: 672, y: 0, width: 32, height: 32 },
{ x: 576, y: 32, width: 32, height: 32 },
{ x: 608, y: 64, width: 32, height: 32 },
{ x: 672, y: 64, width: 32, height: 32 },
{ x: 576, y: 96, width: 32, height: 32 },
{ x: 608, y: 96, width: 32, height: 32 },
{ x: 672, y: 96, width: 32, height: 32 },
],
};
const spritesObj = {};
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
for (const key in sprites) {
if (Object.hasOwnProperty.call(sprites, key)) {
const spriteArray = sprites[key];
spritesObj[key] = spriteArray.map((sprite) => {
canvas.width = sprite.width;
canvas.height = sprite.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(
spriteSheet,
sprite.x,
sprite.y,
sprite.width,
sprite.height,
0,
0,
sprite.width,
sprite.height,
);
const spriteImage = new Image();
spriteImage.src = canvas.toDataURL();
return spriteImage;
});
}
}
return spritesObj;
}
function iniciaJuego() {
const canvasMapa = document.getElementById('mapa');
const canvasMiniMapa = document.getElementById('minimapa');
const sprites = getSprites(barcosImg);
const urlParams = new URLSearchParams(window.location.search);
const gameId = urlParams.get('game_id');
const userId = localStorage.getItem('userId');
const socket = io('/batalla', {
closeOnBeforeunload: true,
auth: { userId, gameId },
});
let tablero = null;
const OnChangeState = (snapshotJugador) => {
switch (snapshotJugador.estado) {
case 'ENPREPARACION':
ENPREPARACION(snapshotJugador);
break;
case 'ENCURSO':
ENCURSO(snapshotJugador);
break;
}
};
const handlers = {
connect: () => {
gameLoop();
},
disconnect: () => {
window.close();
},
OnChangeState,
};
Object.entries(handlers).forEach(([event, handler]) => {
socket.on(event, handler);
});
const ENPREPARACION = (snapshotJugador) => {
muestraBoton('Fijar Flota',()=>{
console.log(JSON.stringify(tablero.mapaFlota.barcos));
socket.emit('sendFlota', tablero.mapaFlota.barcos,handlers.OnChangeState);
});
addMsgChat('Situa tu flota en el mapa y pulsa el boton para continuar');
tablero = new TableroEditor(canvasMapa, canvasMiniMapa, sprites, [
snapshotJugador.mapaFlota,
snapshotJugador.mapaDeAtaques,
]);
};
function addMsgChat(msg) {
const divChat = document.querySelector('#logchat');
const pMsg = document.createElement('p');
pMsg.textContent = msg;
divChat.appendChild(pMsg);
divChat.scrollTop = divChat.scrollHeight;
}
function muestraBoton(msg, callback) {
const divContent = document.querySelector('.div4');
divContent.innerHTML = `<button class="button button-orange prepasradoBtn"> --${msg}-- </button>`;
const preparadoBtn = divContent.querySelector('.prepasradoBtn');
preparadoBtn.addEventListener('click', callback);
}
function muestraAviso(msg) {
const divContent = document.querySelector('.div4');
divContent.innerHTML = `<span class="avisoMsg parpadea"> -- ${msg} -- </span>`;
}
function limpiaContenedorAvisos() {
const divContent = document.querySelector('.div4');
divContent.innerHTML = '';
}
function gameLoop() {
if (tablero) tablero.draw();
window.requestAnimationFrame(gameLoop);
}
}

266
public/js_game/mapa.js Normal file
View File

@ -0,0 +1,266 @@
import Barco from './barco.js';
import Oceano from './oceano.js';
class MapaBase {
constructor(canvas, size, scale, sprites) {
this.canvas = canvas;
this.canvas.width = size;
this.canvas.height = size;
this.width = size / scale;
this.height = size / scale;
this.ctx = this.canvas.getContext('2d');
this.scale = scale;
this.ctx.scale(this.scale, this.scale);
this.sprites = sprites;
this.oceano = new Oceano(this.width, this.height, 32, sprites['OCEANO']);
this.casillaSize = 64;
this.numFilas = 10;
this.numColumnas = 10;
}
draw() {
this.ctx.clearRect(0, 0, this.width, this.height);
this.oceano.draw(this.ctx);
}
}
class MapaVisor extends MapaBase {
celdas = [];
barcos = [];
constructor(canvas, size, scale, sprites, barcos, celdas) {
super(canvas, size, scale, sprites);
this.celdas = celdas;
//this.inicializarCeldas();
this.setBarcos(barcos);
}
inicializarCeldas() {
for (let i = 0; i < this.numFilas; i++) {
this.celdas[i] = new Array(this.numColumnas).fill(0);
}
}
setBarcos(barcos) {
const barcosArray = Array.isArray(barcos) ? barcos : [barcos];
for (const barco of barcosArray) {
this.barcos.push(
new Barco(
barco.x,
barco.y,
barco.longitud,
barco.orientacion,
this.sprites[barco.tipoNave],
barco.tipoNave,
),
);
}
}
draw() {
super.draw();
this.barcos.forEach((barco) => {
barco.draw(this.ctx);
});
this.ctx.beginPath();
for (let i = 1; i < this.numFilas; i++) {
const y = i * this.casillaSize;
this.ctx.moveTo(5, y);
this.ctx.lineTo(this.casillaSize * this.numColumnas - 5, y);
}
for (let i = 1; i < this.numColumnas; i++) {
const x = i * this.casillaSize;
this.ctx.moveTo(x, 5);
this.ctx.lineTo(x, this.casillaSize * this.numFilas - 5);
}
this.ctx.strokeStyle = 'rgba(0, 255, 0, 0.6)';
this.ctx.stroke();
this.ctx.fillStyle = 'red';
this.ctx.font = 'bold 24px Arial';
for (let fila = 0; fila < this.numFilas; fila++) {
for (let columna = 0; columna < this.numColumnas; columna++) {
const valor = this.celdas[fila][columna];
const posX = columna * this.casillaSize + this.casillaSize / 2.3;
const posY = fila * this.casillaSize + this.casillaSize / 1.5;
this.ctx.fillText(valor, posX, posY);
}
}
}
}
class MapaAtaque extends MapaVisor {
constructor(canvas, size, scale, sprites, barcos, celdas) {
super(canvas, size, scale, sprites, barcos, celdas);
canvas.addEventListener('click', this.getPosClick);
}
calcularCoordenadas(event) {
const rect = this.canvas.getBoundingClientRect();
const x = Math.floor(
(event.clientX - rect.left) / (this.casillaSize * this.scale),
);
const y = Math.floor(
(event.clientY - rect.top) / (this.casillaSize * this.scale),
);
return { x, y };
}
getPosClick = (event) => {
const { x, y } = this.calcularCoordenadas(event);
console.log(x, y);
};
draw() {
super.draw();
}
}
class MapaEditor extends MapaVisor {
barcoSeleccionado;
constructor(canvas, size, scale, sprites, barcos, celdas) {
super(canvas, size, scale, sprites, barcos, celdas);
canvas.addEventListener('click', this.getPosClick);
canvas.addEventListener('contextmenu', this.giraBarcoSeleccionado);
canvas.addEventListener('mousemove', this.mueveBarcoSeleccionado);
document.addEventListener('keydown', this.handleKeyDown);
}
fijaBarco(barco) {
barco.seleccionado = false;
const incFila = barco.orientacion === 'VERTICAL' ? 1 : 0;
const incColumna = barco.orientacion === 'HORIZONTAL' ? 1 : 0;
for (let i = 0; i < barco.longitud; i++) {
const fila = barco.y + i * incFila;
const columna = barco.x + i * incColumna;
this.celdas[fila][columna] = barco.longitud;
}
this.barcos.push(barco);
}
calcularCoordenadas(event) {
const rect = this.canvas.getBoundingClientRect();
const x = Math.floor(
(event.clientX - rect.left) / (this.casillaSize * this.scale),
);
const y = Math.floor(
(event.clientY - rect.top) / (this.casillaSize * this.scale),
);
return { x, y };
}
//Acuerdate!!!! funcion de flecha en los callBack para no tener problemas con el contexto this
handleKeyDown = (event) => {
const canvasFocused = document.activeElement === this.canvas;
if (canvasFocused && event.key === 'Escape' && this.barcoSeleccionado) {
this.barcoSeleccionado.restoreIniPos();
this.posicionaBarcoSeleccionado();
}
};
//Acuerdate!!!! funcion de flecha en los callBack para no tener problemas con el contexto this
giraBarcoSeleccionado = (event) => {
event.preventDefault();
if (this.barcoSeleccionado) {
if (
this.barcoSeleccionado.x + this.barcoSeleccionado.longitud <=
this.numColumnas &&
this.barcoSeleccionado.y + this.barcoSeleccionado.longitud <=
this.numFilas
) {
this.barcoSeleccionado.giraBarco();
this.barcoSeleccionado.posIncorrecta = this.sePuedeColocar();
}
}
};
//Acuerdate!!!! funcion de flecha en los callBack para no tener problemas con el contexto this
getPosClick = (event) => {
const { x, y } = this.calcularCoordenadas(event);
if (!this.barcoSeleccionado) {
const index = this.barcos.findIndex((barco) => barco.clickado(x, y));
if (index != -1) this.seleccionaBarco(index);
} else {
this.posicionaBarcoSeleccionado();
}
};
seleccionaBarco(index_barco) {
this.barcoSeleccionado = this.barcos[index_barco];
this.barcoSeleccionado.seleccionado = true;
this.barcoSeleccionado.saveIniPos();
this.barcos.splice(index_barco, 1);
const incFila = this.barcoSeleccionado.orientacion === 'VERTICAL' ? 1 : 0;
const incColumna =
this.barcoSeleccionado.orientacion === 'HORIZONTAL' ? 1 : 0;
for (let i = 0; i < this.barcoSeleccionado.longitud; i++) {
const fila = this.barcoSeleccionado.y + i * incFila;
const columna = this.barcoSeleccionado.x + i * incColumna;
this.celdas[fila][columna] = 0;
}
}
//Acuerdate!!!! funcion de flecha en los callBack para no tener problemas con el contexto this
mueveBarcoSeleccionado = (event) => {
if (this.barcoSeleccionado) {
const { x, y } = this.calcularCoordenadas(event);
if (!this.colisionBorde(x, y)) this.barcoSeleccionado.setXY(x, y);
this.barcoSeleccionado.posIncorrecta = this.sePuedeColocar();
}
};
posicionaBarcoSeleccionado() {
if (!this.barcoSeleccionado.posIncorrecta) {
this.fijaBarco(this.barcoSeleccionado);
this.barcoSeleccionado = null;
}
}
colisionBorde(x, y) {
const barco = this.barcoSeleccionado;
const longitud = barco.longitud;
const orientacion = barco.orientacion;
return orientacion === 'VERTICAL'
? x < 0 || x >= this.numColumnas || y < 0 || y + longitud > this.numFilas
: x < 0 || x + longitud > this.numColumnas || y < 0 || y > this.numFilas;
}
sePuedeColocar() {
const x = this.barcoSeleccionado.x;
const y = this.barcoSeleccionado.y;
const longitud = this.barcoSeleccionado.longitud;
const orientacion = this.barcoSeleccionado.orientacion;
let suma = 0;
if (orientacion === 'VERTICAL') {
for (let f = -1; f <= longitud; f++) {
for (let c = -1; c < 2; c++) {
const fila = y + f;
const columna = x + c;
if (
fila >= 0 &&
fila < this.celdas.length &&
columna >= 0 &&
columna < this.celdas[0].length
) {
suma += this.celdas[fila][columna];
}
}
}
} else {
for (let f = -1; f < 2; f++) {
for (let c = -1; c <= longitud; c++) {
const fila = y + f;
const columna = x + c;
if (
fila >= 0 &&
fila < this.celdas.length &&
columna >= 0 &&
columna < this.celdas[0].length
) {
suma += this.celdas[fila][columna];
}
}
}
}
return suma;
}
esFlotaValida() {
return true;
}
draw() {
super.draw();
if (this.barcoSeleccionado) this.barcoSeleccionado.draw(this.ctx);
}
}
export { MapaBase, MapaVisor, MapaAtaque, MapaEditor };

52
public/js_game/oceano.js Normal file
View File

@ -0,0 +1,52 @@
class Oceano {
constructor(width, height, celdaSize, sprites) {
this.sprites = sprites;
this.celdaSize = celdaSize;
this.filas = Math.floor(height / celdaSize);
this.columnas = Math.floor(width / celdaSize);
this.nFramesAnim = 3;
this.animFrame = 0;
this.gameFrame = 0;
this.lastColumnIndex = this.columnas - 1;
this.lastRowIndex = this.filas - 1;
this.lastColumnX = this.lastColumnIndex * this.celdaSize;
this.lastRowY = this.lastRowIndex * this.celdaSize;
}
update() {}
draw(ctx) {
this.animFrame = Math.floor(this.gameFrame / 10) % this.nFramesAnim;
this.gameFrame++;
const frame = this.animFrame * 9;
// Dibujar los bordes superior e inferior
ctx.drawImage(this.sprites[0 + frame], 0, 0);
ctx.drawImage(this.sprites[2 + frame], this.lastColumnX, 0);
ctx.drawImage(this.sprites[6 + frame], 0, this.lastRowY);
ctx.drawImage(this.sprites[8 + frame], this.lastColumnX, this.lastRowY);
// Dibujar los bordes laterales
for (let y = 1; y < this.lastRowIndex; y++) {
ctx.drawImage(this.sprites[3 + frame], 0, y * this.celdaSize);
ctx.drawImage(
this.sprites[5 + frame],
this.lastColumnX,
y * this.celdaSize
);
}
for (let x = 1; x < this.lastColumnIndex; x++) {
ctx.drawImage(this.sprites[1 + frame], x * this.celdaSize, 0);
ctx.drawImage(this.sprites[7 + frame], x * this.celdaSize, this.lastRowY);
}
// Dibujar el contenido interno
for (let y = 1; y < this.lastRowIndex; y++) {
for (let x = 1; x < this.lastColumnIndex; x++) {
ctx.drawImage(this.sprites[4], x * this.celdaSize, y * this.celdaSize);
}
}
}
}
export default Oceano;

53
public/js_game/tablero.js Normal file
View File

@ -0,0 +1,53 @@
import { MapaVisor,MapaEditor, MapaAtaque } from './mapa.js';
class TableroBase {
constructor(canvasMapa, canvasMiniMapa,sprites){
this.canvasMapa = canvasMapa;
this.canvasMiniMapa = canvasMiniMapa;
this.sprites = sprites;
}
}
class TableroEditor extends TableroBase{
constructor(canvasMapa, canvasMiniMapa,sprites, [mapaFlota, mapaAtaques] ){
super(canvasMapa, canvasMiniMapa,sprites)
const { barcos: barcosMapaFlota, celdas: celdasMapaFlota } = mapaFlota;
const { barcos: barcosMapaAtaques, celdas: celdasMapaAtaques } = mapaAtaques;
this.mapaFlota = new MapaEditor(canvasMapa, 640, 1,sprites,barcosMapaFlota,celdasMapaFlota);
this.mapaDeAtaques = new MapaVisor(canvasMiniMapa, 320, 0.5, sprites,barcosMapaAtaques,celdasMapaAtaques);
}
draw() {
this.mapaFlota.draw();
this.mapaDeAtaques.draw();
}
}
class TableroVisor extends TableroBase{
constructor(canvasMapa, canvasMiniMapa,sprites, [mapaFlota, mapaAtaques] ){
super(canvasMapa, canvasMiniMapa,sprites)
const { barcos: barcosMapaFlota, celdas: celdasMapaFlota } = mapaFlota;
const { barcos: barcosMapaAtaques, celdas: celdasMapaAtaques } = mapaAtaques;
this.mapaFlota = new MapaVisor(canvasMiniMapa, 320, 0.5,sprites,barcosMapaFlota,celdasMapaFlota);
this.mapaDeAtaques = new MapaVisor(canvasMapa, 640, 1, sprites,barcosMapaAtaques,celdasMapaAtaques);
}
draw() {
this.mapaFlota.draw();
this.mapaDeAtaques.draw();
}
}
class TableroAtaque extends TableroBase{
constructor(canvasMapa, canvasMiniMapa,sprites, [mapaFlota, mapaAtaques] ){
super(canvasMapa, canvasMiniMapa,sprites)
const { barcos: barcosMapaFlota, celdas: celdasMapaFlota } = mapaFlota;
const { barcos: barcosMapaAtaques, celdas: celdasMapaAtaques } = mapaAtaques;
this.mapaFlota = new MapaVisor(canvasMiniMapa, 320, 0.5,sprites,barcosMapaFlota,celdasMapaFlota);
this.mapaDeAtaques = new MapaAtaque(canvasMapa, 640, 1, sprites,barcosMapaAtaques,celdasMapaAtaques);
}
draw() {
this.mapaFlota.draw();
this.mapaDeAtaques.draw();
}
}
export {TableroBase,TableroEditor,TableroVisor}

View File

@ -4,10 +4,14 @@
}
* {
/* box-sizing: content-box; */
box-sizing: border-box;
min-width: 0;
}
h1,h2,h3,h4 {
h1,
h2,
h3,
h4 {
margin: 0;
padding: 0;
}
@ -20,96 +24,165 @@ body {
}
body {
padding: 1rem;
background: linear-gradient(to bottom right, #0f2027, #203a43, #2c5364);
color: white;
font-family: 'ITC Machine Std Bold', sans-serif;
text-align: center;
}
#container {
height: 100%;
color: #bf360c;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
#sub-container {
height: 648px;
display: flex;
flex-direction: column;
border: 4px solid #2c5364;
border-radius: 10px;
overflow: hidden;
}
#input-container {
display: flex;
padding: 1rem;
gap: 0.4rem;
max-width: fit-content;
flex-direction: column;
border: 4px solid #2c5364;
border-radius: 10px;
overflow: hidden;
}
/* Estilo del botón */
#input-container input[type="button"] {
/* Estilo base para los botones */
.button {
width: 100%;
padding: 0.5rem 1rem;
font-size: 1rem;
font-family: 'ITC Machine Std Bold', sans-serif;
font-size: 2rem;
border: none;
border-radius: 0.4rem;
background-color: #ff5722; /* Color de fondo naranja */
color: white; /* Color del texto blanco */
cursor: pointer; /* Cambia el cursor al pasar sobre el botón */
transition: background-color 0.3s ease; /* Transición suave al cambiar de color */
cursor: pointer;
transition: background-color 0.3s ease;
}
/* Estilo del botón al pasar el cursor */
#input-container input[type="button"]:hover {
background-color: #e64a19; /* Color de fondo naranja más oscuro al pasar el cursor */
/* Estilo para el botón naranja */
.button-orange {
background-color: #ff5722;
}
/* Estilo del botón al hacer clic */
#input-container input[type="button"]:active {
background-color: #bf360c; /* Color de fondo naranja más oscuro al hacer clic */
/* Estilo para el botón naranja claro al pasar el cursor */
.button-orange:hover {
background-color: #e64a19;
}
/* Estilo para el botón naranja oscuro al hacer clic */
.button-orange:active {
background-color: #bf360c;
}
.avisoMsg {
width: 100%;
text-align: center;
margin: auto;
font-size: 3rem;
}
.parpadea {
animation-name: parpadeo;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: infinite;
-webkit-animation-name: parpadeo;
-webkit-animation-duration: 2s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: infinite;
}
.parent {
height: 744px;
display: grid;
grid-template-columns: min-content min-content;
grid-template-rows: auto 328px 55px auto;
grid-column-gap: 4px;
}
.div1 {
grid-area: 1 / 1 / 2 / 3;
}
.div2 {
grid-area: 2 / 1 / 5 / 2;
width: 648px;
height: 648px;
border: 4px solid #2c5364;
border-radius: 10px;
}
.div3 {
grid-area: 2 / 2 / 3 / 3;
width: 328px;
height: 328px;
border: 4px solid #2c5364;
border-radius: 10px;
}
.div4 {
grid-area: 3 / 2 / 4 / 3;
display: flex;
padding: 0.2rem;
}
.div5 {
grid-area: 4 / 2 / 5 / 3;
border: 4px solid #2c5364;
border-radius: 10px;
display: flex;
flex-direction: column;
gap: 0.4rem;
overflow-y: hidden;
}
#mapa {
width: 640px;
height: 640px;
background-color: #2c5364;
}
#minimapa {
width: 320px;
height: 320px;
flex-shrink: 0;
background-color: #2c5364;
}
#chat {
font-family: 'ITC Machine Std Bold', sans-serif;
resize: none;
width: 320px;
#logchat {
width: 100%;
height: 100%;
padding: 0.1rem;
padding-right: 0.4rem;
font-family: 'roboto', sans-serif;
font-size: small;
font-weight: bold;
border-radius: 0.4rem;
background-color: transparent;
color: antiquewhite;
overflow-y: auto;
}
#logchat p {
margin: 0;
}
#chatinput {
font-family: 'roboto', sans-serif;
padding: 0.4rem;
font-weight: bold;
border-radius: 0 0 0.4rem 0.4rem;
background-color: transparent;
color: wheat;
outline: none;
}
.basic-input{
font-family: 'ITC Machine Std Bold', sans-serif;
padding: 0.4rem;
font-size: larger;
border-radius: 0.4rem;
background-color: #2c5364;
color:wheat;
outline: none;
@-moz-keyframes parpadeo {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
}
@-webkit-keyframes parpadeo {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes parpadeo {
0% {
opacity: 1;
}
50% {
opacity: 0;
}
100% {
opacity: 1;
}
}

View File

@ -1,19 +1,41 @@
import { OnGatewayConnection, OnGatewayDisconnect, WebSocketGateway } from '@nestjs/websockets';
import {
OnGatewayConnection,
OnGatewayDisconnect,
SubscribeMessage,
WebSocketGateway,
} from '@nestjs/websockets';
import { BatallaService } from './batalla.service';
import { Socket } from 'socket.io';
import Barco from 'src/battleship/barco';
@WebSocketGateway({ namespace: 'batalla' })
export class BatallaGateway implements OnGatewayConnection, OnGatewayDisconnect {
export class BatallaGateway
implements OnGatewayConnection, OnGatewayDisconnect
{
constructor(private readonly batallaService: BatallaService) {}
handleConnection(client: Socket) {
const {userId,gameId}=client.handshake.auth
const { userId, gameId } = client.handshake.auth;
if (this.batallaService.existePartidaUsuario(gameId,userId)){
client.emit('OnChangeState',this.batallaService.getSnapshotJugador(gameId,userId));
}else{
client.disconnect();
}
}
handleDisconnect(client: Socket) {}
@SubscribeMessage('sendFlota')
handleMsg(client: Socket, barcos:Barco[]) {
const { userId, gameId } = client.handshake.auth;
console.log(barcos);
console.log(barcos[0] instanceof Barco);
const snapshotJugador = this.batallaService.getSnapshotJugador(gameId,userId);
snapshotJugador.prepararFlota(barcos);
}
handleDisconnect(client: Socket) {
}
}

View File

@ -5,7 +5,18 @@ import { UsuariosService } from 'src/shared/usuarios.service';
@Injectable()
export class BatallaService {
constructor(
private readonly usuariosService: UsuariosService,
private readonly partidasService: PartidasService,
) {}
existePartidaUsuario(uuidPartida:string,uuidJugador:string){
return this.partidasService.existePartida(uuidPartida,uuidJugador);
}
getSnapshotJugador(uuidPartida:string,uuidJugador:string){
return this.partidasService.getSnapshotJugador(uuidPartida,uuidJugador);
}
}

View File

@ -13,7 +13,8 @@ export default class Barco {
public longitud: number,
public orientacion: string,
public impactos: number = 0,
public destruido = false,
public destruido: boolean = false,
public tipoNave: string = '',
) {}
recibirImpacto() {
@ -26,4 +27,14 @@ export default class Barco {
haSidoDestruido(): boolean {
return this.destruido;
}
toJSON() {
return {
x: this.x,
y: this.y,
longitud: this.longitud,
orientacion: this.orientacion,
tipoNave: this.tipoNave,
};
}
}

View File

@ -1,55 +1,17 @@
import Barco from './barco';
import Jugador from './jugador';
export default class BattleshipGame {
public jugadorA: Jugador | null = null;
public jugadorB: Jugador | null = null;
public jugadorA: Jugador;
public jugadorB: Jugador;
turnoActual: Jugador | null;
constructor(public idPartida: string) {
constructor() {
this.turnoActual = null;
this.jugadorA=new Jugador();
this.jugadorB=new Jugador();
this.turnoActual=this.jugadorA;
}
setJugadorA(jugador: Jugador) {
this.jugadorA = jugador;
}
setJugadorB(jugador: Jugador) {
this.jugadorB = jugador;
}
iniciaPartida() {
this.turnoActual = this.jugadorA;
// Iniciar el primer turno
this.realizarTurno();
}
realizarTurno() {
// Realizar las acciones correspondientes al turno actual
// Por ejemplo, mostrar el mapa del turno actual, solicitar un disparo, etc.
// Implementa esta parte según sea necesario
// Luego, alternar el turno al otro jugador
this.turnoActual =
this.turnoActual === this.jugadorA ? this.jugadorB : this.jugadorA;
// Verificar si se ha alcanzado el final del juego
if (this.haFinalizado()) {
this.finalizaPartida();
} else {
// Si no ha finalizado, continuar con el próximo turno
this.realizarTurno();
}
}
haFinalizado(): boolean {
// Aquí debes implementar la lógica para verificar si el juego ha finalizado
// Por ejemplo, si todos los barcos de uno de los jugadores han sido destruidos
// Devuelve true si el juego ha finalizado, de lo contrario, devuelve false
// Implementa esta parte según sea necesario
return false;
}
finalizaPartida() {
// Aquí debes implementar la lógica para finalizar el juego
// Por ejemplo, mostrar el resultado (quién ganó, etc.)
// Implementa esta parte según sea necesario
}
}

View File

@ -5,35 +5,72 @@ export default class Jugador {
public mapaFlota: Mapa = new Mapa();
public mapaDeAtaques: Mapa = new Mapa();
public jugadorPreparado: boolean = false;
public estado: string = 'ENPREPARACION';
public barcos: Barco[] = [
new Barco(1, 0, 0, BarcoTipo.PORTAAVIONES, 'VERTICAL'),
new Barco(2, 1, 0, BarcoTipo.ACORAZADO, 'VERTICAL'),
new Barco(3, 2, 0, BarcoTipo.ACORAZADO, 'VERTICAL'),
new Barco(4, 3, 0, BarcoTipo.ACORAZADO, 'VERTICAL'),
new Barco(5, 4, 0, BarcoTipo.DESTRUCTOR, 'VERTICAL'),
new Barco(6, 5, 0, BarcoTipo.DESTRUCTOR, 'VERTICAL'),
new Barco(7, 6, 0, BarcoTipo.DESTRUCTOR, 'VERTICAL'),
new Barco(8, 7, 0, BarcoTipo.FRAGATA, 'VERTICAL'),
new Barco(9, 8, 0, BarcoTipo.FRAGATA, 'VERTICAL'),
new Barco(
1,
0,
0,
BarcoTipo.PORTAAVIONES,
'VERTICAL',
0,
false,
'PORTAAVIONES',
),
new Barco(2, 1, 0, BarcoTipo.ACORAZADO, 'VERTICAL', 0, false, 'ACORAZADO'),
new Barco(3, 2, 0, BarcoTipo.ACORAZADO, 'VERTICAL', 0, false, 'ACORAZADO'),
new Barco(4, 3, 0, BarcoTipo.ACORAZADO, 'VERTICAL', 0, false, 'ACORAZADO'),
new Barco(
5,
4,
0,
BarcoTipo.DESTRUCTOR,
'VERTICAL',
0,
false,
'DESTRUCTOR',
),
new Barco(
6,
5,
0,
BarcoTipo.DESTRUCTOR,
'VERTICAL',
0,
false,
'DESTRUCTOR',
),
new Barco(
7,
6,
0,
BarcoTipo.DESTRUCTOR,
'VERTICAL',
0,
false,
'DESTRUCTOR',
),
new Barco(8, 7, 0, BarcoTipo.FRAGATA, 'VERTICAL', 0, false, 'FRAGATA'),
new Barco(9, 8, 0, BarcoTipo.FRAGATA, 'VERTICAL', 0, false, 'FRAGATA'),
];
constructor(public nickname: string) {}
constructor() {
this.mapaFlota.setBarcos(this.barcos);
}
prepararFlota(barcos: Barco[]) {
this.barcos = barcos;
this.mapaFlota.setBarcos(this.barcos);
this.jugadorPreparado = true;
}
setBarcos(barcos: Barco[]) {
this.mapaFlota.setBarcos(barcos);
}
realizarDisparo(x: number, y: number, rival: Jugador): boolean {
const acierto = rival.mapaFlota.verificaDisparo(x, y);
this.mapaDeAtaques.marcaDisparo(x,y,acierto ? EstadoCelda.Golpe : EstadoCelda.Agua);
this.mapaDeAtaques.marcaDisparo(
x,
y,
acierto ? EstadoCelda.Golpe : EstadoCelda.Agua,
);
return acierto;
}
@ -46,4 +83,11 @@ export default class Jugador {
return true;
}
toJSON() {
return {
mapaFlota: this.mapaFlota,
mapaDeAtaques: this.mapaDeAtaques,
estado: this.estado,
};
}
}

View File

@ -5,10 +5,14 @@ export enum EstadoCelda {
Agua = -1,
Golpe = -2,
}
export default class Mapa {
celdas: number[][] = [];
barcosMapa: Map<number, Barco> = new Map<number, Barco>();
get barcos() {
return Array.from(this.barcosMapa.values());
}
constructor() {
for (let i = 0; i < 10; i++) {
this.celdas[i] = [];
@ -18,6 +22,10 @@ export default class Mapa {
}
}
toJSON() {
return { celdas: this.celdas, barcos: this.barcos };
}
setBarcos(barcos: Barco | Barco[]) {
const barcosArray = Array.isArray(barcos) ? barcos : [barcos];
for (const barco of barcosArray) {
@ -44,8 +52,8 @@ export default class Mapa {
return null;
}
marcaDisparo(x:number,y:number,valor:number){
this.celdas[x][y]=valor;
marcaDisparo(x: number, y: number, valor: number) {
this.celdas[x][y] = valor;
}
getInformacionBarcos(): Barco[] {
@ -54,7 +62,7 @@ export default class Mapa {
verificaDisparo(x: number, y: number): boolean {
if (this.celdas[x][y] > 0) {
const barco = this.getBarcoEnCelda(x,y);
const barco = this.getBarcoEnCelda(x, y);
if (barco) {
barco.recibirImpacto();
this.celdas[x][y] = EstadoCelda.Golpe;

View File

@ -1,16 +1,29 @@
import { v4 as uuidv4 } from 'uuid';
import { Injectable } from '@nestjs/common';
import { UsuariosService } from './usuarios.service';
import BattleshipGame from 'src/battleship/battleship';
export class Partida {
constructor(
public uuid: string,
public uuidJugadorA: string,
public nickJugadorA: string = '',
public nickJugadorA: string = null,
public uuidJugadorB: string = null,
public nickJugadorB: string = null,
public abierta: boolean = true,
public batalla: BattleshipGame = null,
) {}
toJson() {
return {
uuid: this.uuid,
uuidJugadorA: this.uuidJugadorA,
nickJugadorA: this.nickJugadorA,
uuidJugadorB: this.uuidJugadorB,
nickJugadorB: this.nickJugadorB,
abierta: this.abierta,
};
}
}
@Injectable()
@ -18,19 +31,16 @@ export class PartidasService {
private _partidas: Map<string, Partida> = new Map<string, Partida>();
constructor(private readonly usuariosService: UsuariosService) {}
get partidasAbiertas(): Partida[] {
return Array.from(this._partidas.values()).filter(
(partida) => partida.abierta,
);
}
get partidasCerradadas(): Partida[] {
return Array.from(this._partidas.values()).filter(
(partida) => !partida.abierta,
);
}
creaPartida(uuidJugadorA: string) {
const uuid = uuidv4();
const nickJugadorA =
@ -39,16 +49,16 @@ export class PartidasService {
this._partidas.set(uuid, partida);
return partida;
}
unirsePartida(uuidJugadorB: string, uuidPartida: string){
unirsePartida(uuidJugadorB: string, uuidPartida: string) {
const partida = this._partidas.get(uuidPartida);
const nickJugadorB = this.usuariosService.usuarios.get(uuidJugadorB)?.nickname;
const nickJugadorB =
this.usuariosService.usuarios.get(uuidJugadorB)?.nickname;
partida.uuidJugadorB = uuidJugadorB;
partida.nickJugadorB = nickJugadorB;
partida.abierta = false;
partida.batalla = new BattleshipGame();
return partida;
}
eliminarPartida(uuidJugador: string, uuidPartida: string) {
if (!this._partidas.has(uuidPartida)) {
return null;
@ -63,5 +73,22 @@ export class PartidasService {
this._partidas.delete(uuidPartida);
return uuidPartida;
}
existePartida(uuidPartida: string, uuidJugador: string): boolean {
if (!this._partidas.has(uuidPartida)) {
return false;
}
const partida = this._partidas.get(uuidPartida);
return (partida.uuidJugadorA === uuidJugador || partida.uuidJugadorB === uuidJugador)
}
getSnapshotJugador(uuidPartida: string, uuidJugador: string){
if (this.existePartida(uuidPartida,uuidJugador)){
const partida=this._partidas.get(uuidPartida);
return partida.uuidJugadorA === uuidJugador ? partida.batalla.jugadorA : partida.batalla.jugadorB;
}
return null;
}
}