Logica_Tablero.js

import Celda from "./Celda.js";
import { Eventos } from "../Events.js";
import { EventBus } from "../EventBus.js";

/**
 * Clase que representa el tablero de juego.
 * @class Tablero
 * @memberof Logica
 */
class Tablero {
    /**
     * Constructor del tablero.
     * @param {number} _filas - número de filas del tablero (default: 8)
     * @param {number} _columnas - número de columnas del tablero (default: 10)
     * @param {Phaser.Scene} escena - escena asociada al tablero
     * @constructor
     */
    constructor(_filas = 8, _columnas = 10, escena) {
        this.filas = _filas;
        this.columnas = _columnas;
        this.tablero = this.crearTablero(); //creamos el tablero lleno de celdas
        this.piezaActiva = null;
        this.escena = escena;
        this.turnoActivo = null;

        this.celdasJ1 = 0;
        this.celdasJ2 = 0;
    }

    /**
     * Crea una matriz bidimensional de celdas que representan el tablero.
     * @returns {Array<Array<Celda>>} matriz de celdas
     * @private
     */
    crearTablero() {
        let tab = [];
        for (let i = 0; i < this.filas; i++) {
            tab[i] = [];
            for (let j = 0; j < this.columnas; j++) {
                tab[i][j] = new Celda(i, j);
            }
        }
        return tab;
    }

    /**
     * Obtiene la celda en una posición específica del tablero.
     * @param {number} fila - fila de la celda
     * @param {number} columna - columna de la celda
     * @returns {Celda} celda solicitada
     */
    getCelda(fila, columna) {
        return this.tablero[fila][columna];
    }

    // Selecciona las casillas de movimiento/ataque de la pieza
    /**
     * Calcula las casillas disponibles para mover o atacar cuando se selecciona una pieza.
     * Devuelve las casillas según el tipo de pieza y su alcance.
     * @param {number} fil - fila de la pieza seleccionada
     * @param {number} col - columna de la pieza seleccionada
     * @returns {Array<Object>} array de objetos con coordenadas y tipo de acción (vacia/enemigo)
     */
    piezaSeleccionada(fil, col) {

        let celda = this.tablero[fil][col];
        let celdasSeleccionadas = [];

        let pieza = celda.getPieza();

        // Si la pieza ya no puede actuar, no devuelvas casillas
        if (!pieza || pieza.getMovida()) return [];

        if (pieza != this.piezaActiva) {
            //Lanzamos el evento de pieza seleccionada
            EventBus.emit(Eventos.PIECE_SELECTED, pieza);
            this.piezaActiva = pieza;
        }

        if (pieza.getTipo() == "Artilleria" && !pieza.puedeDisparar()) return [];

        // Direcciones cardinales
        const direcciones = [
            { df: -1, dc: 0 },  // arriba
            { df: 1, dc: 0 },   // abajo
            { df: 0, dc: -1 },  // izquierda
            { df: 0, dc: 1 }    // derecha
        ];

        if (pieza.getTipo() == "Artilleria") {
            const jugador = pieza.getJugador();

            let iniCol;
            let maxCol;
            if (jugador === "J1") {
                iniCol = pieza.getPosicion().col + 1; // La siguiente a la artilleria
                maxCol = iniCol + 4;
            }
            else {
                iniCol = pieza.getPosicion().col - 4; // La siguiente a la artilleria
                maxCol = pieza.getPosicion().col;
            }

            for (let col = iniCol; col < maxCol; col++) {
                for (let fil = 0; fil < this.filas; fil++) {
                    let celda = this.tablero[fil][col];

                    let esRival;
                    if (!celda.estaVacia()) esRival = jugador !== celda.getPieza().getJugador();

                    if (esRival) {
                        celdasSeleccionadas.push({ fil: fil, col: col, tipo: "enemigo" });
                    }
                    else {
                        celdasSeleccionadas.push({ fil: fil, col: col, tipo: "vacia" });
                    }
                }
            }

        }
        else if (pieza.getTipo() != "Comandante") { //solo el comandante puede moverse en diagonal
            for (let dir of direcciones) {
                const f = fil + dir.df;
                const c = col + dir.dc;
                // fuera de tablero → deja de mirar en esta dirección
                if (f < 0 || c < 0 || f >= this.filas || c >= this.columnas) continue;

                const cel = this.tablero[f][c];

                if (cel.estaVacia()) {
                    // casilla libre: se puede mover; sigue mirando más lejos
                    celdasSeleccionadas.push({ fil: f, col: c, tipo: "vacia" });
                } else {
                    // hay pieza: si es rival, puedes atacar esa casilla; en ambos casos paras
                    const esRival = cel.getPieza().getJugador() !== celda.getPieza().getJugador();
                    if (esRival) celdasSeleccionadas.push({ fil: f, col: c, tipo: "enemigo" });
                    if (pieza.getTipo() == "Caballeria" && pieza.getSaltoCaballeria()) {

                        const f2 = f + dir.df;
                        const c2 = c + dir.dc;

                        // Comprobar límites correctamente
                        if (f2 >= 0 && f2 < this.filas && c2 >= 0 && c2 < this.columnas) {

                            // Puede saltar solo si la casilla destino está vacía
                            if (this.tablero[f2][c2].estaVacia()) {
                                celdasSeleccionadas.push({ fil: f2, col: c2, tipo: "vacia" });
                            }
                        }
                    }


                }
            }
        }
        else {
            for (let i = col - 1; i <= col + 1; i++) {
                for (let j = fil - 1; j <= fil + 1; j++) {
                    // fuera de tablero → deja de mirar en esta dirección
                    if (j < 0 || i < 0 || j >= this.filas || i >= this.columnas) continue;
                    if (j == fil && i == col) continue; // saltar la casilla central
                    const cel = this.tablero[j][i];

                    if (cel.estaVacia()) {
                        // casilla libre: se puede mover; sigue mirando más lejos
                        celdasSeleccionadas.push({ fil: j, col: i, tipo: "vacia" });
                    } else {
                        // hay pieza: si es rival, puedes atacar esa casilla; en ambos casos paras
                        const esRival = cel.getPieza().getJugador() !== celda.getPieza().getJugador();
                        if (esRival) celdasSeleccionadas.push({ fil: j, col: i, tipo: "enemigo" });
                    }
                }
            }
        }
        return celdasSeleccionadas;
    }

    // Mueve la pieza a fil, col
    /**
     * Mueve la pieza activa a una nueva posición en el tablero.
     * @param {number} fil - fila de destino
     * @param {number} col - columna de destino
     */
    moverPieza(fil, col) {

        //Limpia la celda de origen
        let origen = this.piezaActiva.getPosicion()
        this.tablero[origen.fila][origen.col].limpiar();

        //Añade la pieza a la celda de destino
        this.piezaActiva.moverse(fil, col);
        this.tablero[fil][col].setContenido(this.piezaActiva);

        EventBus.emit(Eventos.PIECE_MOVED, this.piezaActiva);
    }

    // Mueve la pieza a fil, col cuando gana un combate
    /**
     * Mueve una pieza a una nueva posición después de ganar un combate.
     * @param {number} fil - fila de destino
     * @param {number} col - columna de destino
     * @param {Pieza} pieza - pieza a mover
     */
    moverPiezaCombate(fil, col, pieza) {

        //Limpia la celda de origen
        let origen = pieza.getPosicion();
        this.tablero[origen.fila][origen.col].limpiar();

        //Añade la pieza a la celda de destino
        pieza.moverse(fil, col);
        this.tablero[fil][col].setContenido(pieza);
        EventBus.emit(Eventos.PIECE_MOVED, pieza);
    }

    /**
     * Inicia un ataque contra una pieza enemiga en la posición especificada.
     * @param {number} fil - fila del enemigo objetivo
     * @param {number} col - columna del enemigo objetivo
     */
    ataque(fil, col) {
        if (!this.piezaActiva) {
            console.error('Error: No hay pieza activa para atacar');
            return;
        }
        
        let defensa = this.getCelda(fil, col);
        let origen = this.piezaActiva.getPosicion();
        let ataque = this.getCelda(origen.fila, origen.col);
        EventBus.emit(Eventos.ENEMY_SELECTED, ataque, defensa);
    }

    /**
     * Obtiene la pieza actualmente seleccionada.
     * @returns {Pieza|null} pieza activa o null si ninguna está seleccionada
     */
    getPiezaActiva() {
        return this.piezaActiva;
    }

    /**
     * Reinicia la pieza activa (deselecciona).
     */
    resetPiezaActiva() {
        this.piezaActiva = null;
    }

    /**
     * Registra la conquista de una celda por un jugador.
     * Actualiza los contadores de territorio y verifica condición de victoria.
     * @param {string} jugador - identificador del jugador ('J1' o 'J2')
     * @param {boolean} ocupada - indica si la celda estaba previamente ocupada por el enemigo
     */
    conquistarCelda(jugador, ocupada) {
        if (jugador == "J1") {
            this.celdasJ1++;
            if (ocupada) this.celdasJ2--;
        }
        else {
            this.celdasJ2++;
            if (ocupada) this.celdasJ1--;
        }
        let j1Porcentaje = this.celdasJ1 * 100 / 80;
        let j2Porcentaje = this.celdasJ2 * 100 / 80;

        EventBus.emit(Eventos.CONQUER_CELL, j1Porcentaje, j2Porcentaje)

        if (this.celdasJ1 >= 64 || this.celdasJ2 >= 64) EventBus.emit(Eventos.END_GAME, {
            jugador: this.piezaActiva.getJugador(),
            tipo: "TERRITORIO"
        });
    }

    /**
     * Elimina una celda conquistada por el jugador especificado (por lluvia).
     * @param {string} jugadorAnterior - identificador del jugador anterior ('J1' o 'J2')
     */
    borrarCelda(jugadorAnterior) {
        if (jugadorAnterior === 'J1') {
            this.celdasJ1--;
        } else if (jugadorAnterior === 'J2') {
            this.celdasJ2--;
        }

        let j1Porcentaje = this.celdasJ1 * 100 / 80;
        let j2Porcentaje = this.celdasJ2 * 100 / 80;
        EventBus.emit(Eventos.UPDATE_PERCENTAGES, j1Porcentaje, j2Porcentaje);
    }


    /**
     * Obtiene el tamaño del tablero.
     * @returns {Object} objeto con propiedades fila y col
     */
    size() {
        return { fila: this.filas, col: this.columnas };
    }

    /**
     * Devuelve la escena asociada al tablero.
     * @returns {Phaser.Scene} escena asociada al tablero
     */
    getEscena() {
        return this.escena;
    }

    /**
     * Resetea la pieza activa (deselecciona).
     */
    resetPiezaActiva() {
        console.log("Reset pieza activa en Tablero");
        this.piezaActiva = null;
    }
}

export default Tablero;