diff --git a/public/assets/barcos.png b/public/assets/barcos.png index f2b1c37..838b503 100644 Binary files a/public/assets/barcos.png and b/public/assets/barcos.png differ diff --git a/public/batalla.html b/public/batalla.html index 2e2b4f6..a563b75 100644 --- a/public/batalla.html +++ b/public/batalla.html @@ -10,17 +10,24 @@ -
-
- +
+
+
-
- - - +
+
+
+
+
+
+ \ No newline at end of file diff --git a/public/js_game/barco.js b/public/js_game/barco.js new file mode 100644 index 0000000..e0ed486 --- /dev/null +++ b/public/js_game/barco.js @@ -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; diff --git a/public/js_game/battleship.js b/public/js_game/battleship.js index e358c39..6278dce 100644 --- a/public/js_game/battleship.js +++ b/public/js_game/battleship.js @@ -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 = ``; + const preparadoBtn = divContent.querySelector('.prepasradoBtn'); + preparadoBtn.addEventListener('click', callback); + } + function muestraAviso(msg) { + const divContent = document.querySelector('.div4'); + divContent.innerHTML = ` -- ${msg} -- `; + } + function limpiaContenedorAvisos() { + const divContent = document.querySelector('.div4'); + divContent.innerHTML = ''; + } + + function gameLoop() { + if (tablero) tablero.draw(); + window.requestAnimationFrame(gameLoop); + } +} diff --git a/public/js_game/mapa.js b/public/js_game/mapa.js new file mode 100644 index 0000000..e6955d8 --- /dev/null +++ b/public/js_game/mapa.js @@ -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 }; diff --git a/public/js_game/oceano.js b/public/js_game/oceano.js new file mode 100644 index 0000000..7a411df --- /dev/null +++ b/public/js_game/oceano.js @@ -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; diff --git a/public/js_game/tablero.js b/public/js_game/tablero.js new file mode 100644 index 0000000..cfe987e --- /dev/null +++ b/public/js_game/tablero.js @@ -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} \ No newline at end of file diff --git a/public/stylegame.css b/public/stylegame.css index c4849cb..a83cd7b 100644 --- a/public/stylegame.css +++ b/public/stylegame.css @@ -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; + } +} -} \ No newline at end of file +@-webkit-keyframes parpadeo { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} + +@keyframes parpadeo { + 0% { + opacity: 1; + } + 50% { + opacity: 0; + } + 100% { + opacity: 1; + } +} diff --git a/src/batalla/batalla.gateway.ts b/src/batalla/batalla.gateway.ts index d6c4d45..349765e 100644 --- a/src/batalla/batalla.gateway.ts +++ b/src/batalla/batalla.gateway.ts @@ -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) { - } + } \ No newline at end of file diff --git a/src/batalla/batalla.service.ts b/src/batalla/batalla.service.ts index 300cbf4..f7c959d 100644 --- a/src/batalla/batalla.service.ts +++ b/src/batalla/batalla.service.ts @@ -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); + } + + + } diff --git a/src/battleship/barco.ts b/src/battleship/barco.ts index 96218d4..b32579b 100644 --- a/src/battleship/barco.ts +++ b/src/battleship/barco.ts @@ -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, + }; + } } diff --git a/src/battleship/battleship.ts b/src/battleship/battleship.ts index c74c83c..60ad77a 100644 --- a/src/battleship/battleship.ts +++ b/src/battleship/battleship.ts @@ -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 - } } diff --git a/src/battleship/jugador.ts b/src/battleship/jugador.ts index 500cf9a..bba86b8 100644 --- a/src/battleship/jugador.ts +++ b/src/battleship/jugador.ts @@ -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, + }; + } } diff --git a/src/battleship/mapa.ts b/src/battleship/mapa.ts index 662e755..3c809f3 100644 --- a/src/battleship/mapa.ts +++ b/src/battleship/mapa.ts @@ -5,10 +5,14 @@ export enum EstadoCelda { Agua = -1, Golpe = -2, } - export default class Mapa { celdas: number[][] = []; barcosMapa: Map = new Map(); + + 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; diff --git a/src/shared/partidas.service.ts b/src/shared/partidas.service.ts index aee84a2..290397a 100644 --- a/src/shared/partidas.service.ts +++ b/src/shared/partidas.service.ts @@ -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 = new Map(); 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; + } + }