Autor Tema: Batalla RPG por turnos con Java juego rol Role Playing Game Señor de los Anillos  (Leído 2371 veces)

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Hola a todos, llevo poco tiempo con la programación y estoy con un proyecto, se trata de un juego tipo RPG por turnos basado en el señor de los anillos.

Las características que tiene son las siguientes:

  • Los personajes se dividen en dos categorías: Héroes y Bestias.
  • El ataque de los Heroes se realiza mediante la tirada de dos dados con valores entre 0 y 100 y se elegirá el mayor de las dos tiradas.
  • El ataque de las Bestias será con el lanzamiento de un único dado y los valores irán entre 0 a 90
  • Sólo se producirá un daño al adversario si la potencia ofensiva del atacante es superior al nivel de armadura del defensor. Si es así el daño producido será la diferencia entre la potencia de ataque y el nivel de armadura del oponente.
  • el sistema de batalla, se enfrentarán siempre los personajes situados en la misma posición de cada ejército.
  • En el momento en que un personaje llegue a un nivel de vida igual o inferior a cero se producirá su muerte por lo que se eliminará de su posición y se desplazarán todos sus compañeros en posiciones posteriores para cubrir la baja

Mi problema es que no consigo que el programa finalice, ya que no acumula cuando un personaje ataca a otro, creo que mi función vida() calcula la vida de los héroes y las bestias, pero los héroes y las bestias que están al inicio de la función y los que se atacaron son las copias que tienes en los arreglos por lo tanto en si tus héroes y bestias originales nunca se atacaron y tienen toda la vida, pero, no sé como solucionarlo.

Adjunto el código que tengo:

Clase Ejercito:

Código: [Seleccionar]
public class Ejercito {
    String nombre;
    int puntosVida;
    int nivelResistencia;


    public Ejercito(String nombre, int puntosVida, int nivelResistencia) {
        this.nombre = nombre;
        this.puntosVida = puntosVida;
        this.nivelResistencia = nivelResistencia;

    }

    public String getNombre() {
        return nombre;
    }

    public void setNombre(String nombre) {
        this.nombre = nombre;
    }

    public int getPuntosVida() {
        return puntosVida;
    }

    public void setPuntosVida(int puntosVida) {
        this.puntosVida = puntosVida;
    }

    public int getNivelResistencia() {
        return nivelResistencia;
    }

    public void setNivelResistencia(int nivelResistencia) {
        this.nivelResistencia = nivelResistencia;
    }

Clase Heroes:

Código: [Seleccionar]
package com.tokioschool.Batalla.domain;

import java.util.List;

public class Heroes extends Ejercito {
    int dado1;
    int dado2;

    public Heroes(String nombre, int puntosVida, int nivelResistencia) {
        super(nombre, puntosVida, nivelResistencia);

    }

    public int mayorResultado(){
        dado1=(int) (Math.random()*(100+1));
        dado2=(int) (Math.random()*(100+1));
        System.out.println("primer dado:"+dado1);
        System.out.println("segundo dado:"+dado2);

        return Math.max(dado1, dado2);
    }

    public int atacar(Bestias bestias) {
        int ataque = mayorResultado();
        int resultado = bestias.getPuntosVida() - ataque;
        boolean vid = getPuntosVida() >= 0;
        String men = "Las tiradas no superan la armadura de la bestia, pasa el turno";
        do {

            if (ataque > bestias.getNivelResistencia()) {
                System.out.println(bestias.getPuntosVida() + " - " + ataque + "=" + resultado);
                System.out.println("Datos actualizados de " + bestias.getNombre() + " (Vida=" + resultado + " Armadura=" + bestias.getNivelResistencia() + ")");
                return resultado;
            } else
                System.out.println(men);
            return 0;
        }while (bestias.getPuntosVida() >=0);

    }

    public boolean vida( List<Heroes> heroes){
        boolean vidas = getPuntosVida()<=0;
        if(!vidas);
        System.out.println(getNombre()+" ha sido eliminado");
        return vidas;
    }

    public String toString(){
        return "Lucha entre "+getNombre()+" (Vida="+getPuntosVida()+" Armadura="+getNivelResistencia()+")";
    }

}

Clase Bestias

Código: [Seleccionar]
public class Bestias extends Ejercito {

    int dado;

    public Bestias(String nombre, int puntosVida, int nivelResistencia) {
        super(nombre, puntosVida, nivelResistencia);

    }

    public int getNivelResistencia(){
        return nivelResistencia;
    }

    public int tirarDadosBestias(){
        return dado =(int) (Math.random()*(90+1));
    }

    public int atacarBestias(Heroes heroe) {
        int at = tirarDadosBestias();
        int result = heroe.getPuntosVida() - at;
        String mensaje = "La tirada no ha superado la armadura del heroe, pasa el turno";
        do{
            if (at > heroe.getNivelResistencia()) {
                System.out.println("Turno de las bestias, el resultado del dado es :"+at);
                System.out.println("Datos actualizados de: " + heroe.getNombre() + " (Vida=" + result + " Armadura=" + heroe.getNivelResistencia() + ")");
                return result;
            } else
                System.out.println(mensaje);
            return 0;
        }while(heroe.getPuntosVida()>=0);

    }

    public boolean vida( List<Bestias> bestias){
        boolean vidas = getPuntosVida()<=0;
        if(vidas);
        System.out.println(getNombre()+" ha sido eliminado");
        return vidas;
    }

    public String toString(){
        return " y "+getNombre()+" (Vida="+getPuntosVida()+" Armadura="+getNivelResistencia()+")";
    }

}

Clase Batalla

Código: [Seleccionar]
import java.util.List;

public class Batalla {

    private static Heroes[] ejHero;
    private static Bestias[] ejeBestia;

    public Batalla() {
        ejHero = new Heroes[5];
        ejeBestia = new Bestias[4];
    }

    public static void batalla(List<Heroes> heroes, List<Bestias> bestias) {

        Heroes[] he = new Heroes[heroes.size()];
        he = heroes.toArray(he);

        Bestias[] be = new Bestias[bestias.size()];
        be = bestias.toArray(be);

        do {

            System.out.println(he[0].toString() + be[0].toString());
            System.out.println(he[0].atacar(be[0]));
            System.out.println(be[0].atacarBestias(he[0]));


            System.out.println(he[1].toString() + be[1].toString());
            System.out.println(he[1].atacar(be[1]));
            System.out.println(be[1].atacarBestias(he[1]));

            System.out.println(he[2].toString() + be[2].toString());
            System.out.println(he[2].atacar(be[2]));
            System.out.println(be[2].atacarBestias(he[2]));

            System.out.println(he[3].toString() + be[3].toString());
            System.out.println(he[3].atacar(be[3]));
            System.out.println(be[3].atacarBestias(he[3]));

            System.out.println(he[4].toString() + " se queda en guardia");


        } while (!he[0].vida(heroes) || !be[0].vida(bestias));

    }

}

Clase Principal:

Código: [Seleccionar]
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main (String args[]){
        List<Heroes> ejerHero = new ArrayList<>();
        List<Bestias> ejerBestias = new ArrayList<>();

        ejerHero.add(new Elfos("Legolas", 150, 30));
        ejerHero.add(new Humano("Aragorn", 150, 50));
        ejerHero.add(new Humano("Boromir",100, 60));
        ejerHero.add(new Humano("Gandalf",300, 10));
        ejerHero.add(new Hobbits("Frodo", 20, 10));

        ejerBestias.add(new Orcos("Lurtz", 200, 60));
        ejerBestias.add(new Orcos("Shagrat", 220, 50));
        ejerBestias.add(new Trasgo("Uglúk", 120, 30));
        ejerBestias.add(new Trasgo("Mauhúr", 100, 30));

        Batalla.batalla(ejerHero, ejerBestias);

    }
}

Agradecería cualquier ayuda, gracias de antemano!
« Última modificación: 12 de Marzo 2022, 18:29 por Ogramar »

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #1 en: 28 de Agosto 2021, 14:15 »
Hola.
Veo varias inconsistencias:

Aquí al método vida(), le pasa un List<Heroes>, pero si te fijas no estás haciendo nada con él.
Citar
    public boolean vida(List<Heroes> heroes){
        boolean vidas = getPuntosVida()<=0;
        if(!vidas);
        System.out.println(getNombre()+" ha sido eliminado");
        return vidas;
    }

Si lo quitas, verás que todo funciona exactamente igual, por tanto es innecesario.
Código: [Seleccionar]
    public boolean vida(){
        boolean vidas = getPuntosVida()<=0;
        if(vidas);
        System.out.println(getNombre()+" ha sido eliminado");
        return vidas;
    }

En la clase Batalla ocurre algo parecido.
Declaras como atributos dos Ejercitos
Citar
public class Batalla {

    private static Heroes[] ejHero;
    private static Bestias[] ejeBestia;


    public Batalla() {
        ejHero = new Heroes[5];
        ejeBestia = new Bestias[4];
    }

Pero luego no se usan para nada, porque el único método que tiene esta clase, trabaja con unos List<> que recibe por argumentos. No hace nada con estos atributos declarados en la clase:
Citar
    public static void batalla(List<Heroes> heroes, List<Bestias> bestias) {

        Heroes[] he = new Heroes[heroes.size()];
        he = heroes.toArray(he);

        Bestias[] be = new Bestias[bestias.size()];
        be = bestias.toArray(be);

        do {

            System.out.println(he[0].toString() + be[0].toString());
            System.out.println(he[0].atacar(be[0]));
            System.out.println(be[0].atacarBestias(he[0]));


            System.out.println(he[1].toString() + be[1].toString());
            System.out.println(he[1].atacar(be[1]));
            System.out.println(be[1].atacarBestias(he[1]));

            System.out.println(he[2].toString() + be[2].toString());
            System.out.println(he[2].atacar(be[2]));
            System.out.println(be[2].atacarBestias(he[2]));

            System.out.println(he[3].toString() + be[3].toString());
            System.out.println(he[3].atacar(be[3]));
            System.out.println(be[3].atacarBestias(he[3]));

            System.out.println(he[4].toString() + " se queda en guardia");


        } while (!he[0].vida() || !be[0].vida());

    }
Luego, en ese método, la condición del while no me queda clara:
Código: [Seleccionar]
while (!he[0].vida() || !be[0].vida())
Citar
Hacer Mientras el primer Heroe no tenga vida o la primera Bestia no tenga vida

¿No sería mientras tengan vida?
¿Y por qué solo primer Heroe o primera Bestia?(los que ocupan posición
  • )

Supongo que esto lo haces por este punto del enuciado:
Citar
En el momento en que un personaje llegue a un nivel de vida igual o inferior a cero se producirá su muerte por lo que se eliminará de su posición y se desplazarán todos sus compañeros en posiciones posteriores para cubrir la baja
En teoría, mientras van muriendo combatientes, se van desplazando de forma que la posición
  • siempre está ocupada, hasta que ya no quede nadie.

Pero en tu código este desplazamiento no está contemplado, así que esa condición para el bucle no se cumplirá correctamente.
Además, tu código está pensado para ejércitos de un tamaño fijo.
La "batalla" ha de adaptarse para poder recibir desde el principio ejércitos de diferentes tamaños y tener en cuenta que los ejércitos verán alterado este tamaño durante el combate.

Creo que hay que volver al principio y reformular todo el concepto.
De hecho, lo primero que he visto y me ha chirriado ha sido esto:
Código: [Seleccionar]
public class Heroes extends Ejercito¿Heroes y Bestias han de ser hijas(herederas) de Ejercito?
¿Eso tiene sentido?

En POO es muy habitual cometer el error de usar herencia cuando no corresponde. Y en realidad es fácil evitar este error. Basta con hacerse la pregunta adecuada.

Cuando decimos que una clase hereda de otra, estamos diciendo que esa clase "es una"... lo que sea la otra clase.

Ejemplos.
Si yo digo:
Código: [Seleccionar]
public class Alumno extends PersonaLo que estoy diciendo, es que un Alumno es una Persona
¿Tiene sentido esa afirmación? Sí la tiene, por tanto la herencia es correcta.

Si yo digo:
Código: [Seleccionar]
public class Perro extends AnimalLo que estoy diciendo, es que un Perro es un Animal
¿Tiene sentido esa afirmación? Totalmente, así que la herencia está justificada.

Si yo digo:
Código: [Seleccionar]
public class Heroes extends EjercitoLo que estoy diciendo es que un Heroe (o Bestia), es un Ejército
¿Tiene sentido esa afirmación?
Pues no, un Héroe no es un Ejército.
Un Ejército está compuesto de Héroes, eso sí es cierto. Pero un Héroe, no es un Ejército.

Por lo tanto, aquí una "relación de herencia" no es aplicable.
Lo que se puede aplicar es una "relación de composición".
Un Ejército se compone de uno o varios Héroes o Bestias.
Eso suena mucho mejor, ¿verdad?
¿Y cómo se hace una "relación de composición"?

Pues en este caso hay que reformular la clase Ejército para que sea ella quien tenga como atributo una colección de Héroes o Bestias.
Que sea ella quien controle si en sus filas todavía quedan guerreros con vida o no.
Que sea ella quien se encargue de hacer que los guerreros desplacen sus posiciones para ocupar el puesto de sus compañeros caídos (esto es automático si usamos un ArrayList).

Yo lo haría de este modo:
Código: [Seleccionar]
import java.util.ArrayList;

public class Ejercito {

public ArrayList<Soldado> soldados;

public Ejercito() {
soldados = new ArrayList<Soldado>();
}

public void reclutarSoldado(Soldado soldado) {
soldados.add(soldado);
}

public Soldado getSoldado(int pos) {
try {
return soldados.get(pos);
}
catch(Exception e) {
return null; //No hay soldado en esa posición
}
}

public void comprobarEjercito() {
int soldadoMuerto = -1; //Posicion de posible soldado muerto
//Comprobamos si hay alguna baja
for (int pos = 0; pos < soldados.size(); pos++)
if (soldados.get(pos).estaMuerto()) //Baja confirmada
soldadoMuerto = pos; //Guardamos posicion
/*
* Para evitar inconsistencias, hay que eliminar al soldado
* muerto DESPUÉS del bucle for.
* Si lo eliminasemos DENTRO del bucle for, mientras aún se está
* recorriendo, se podría producir una excepcion porque el indice
* que usa el bucle FOR podría ser inconsistente con el nuevo
* tamaño del ArrayList tras eliminar al soldado.
*/
if (soldadoMuerto != -1) //Se encontró una baja, hay que eliminar
soldados.remove(soldadoMuerto);
//Al eliminar del ArrayList, automáticamente suben una posición los soldados posteriores
}

public boolean esDerrotado() {
//Si ya no quedan soldado, el ejercito ha sido derrotado
return soldados.size() == 0;
}
}

Un Ejercito tiene un grupo de Soldados en un ArrayList.
Necesitamos una clase común entre Heroes y Bestias, para que Ejercito pueda trabajar tanto con unos como con otros..., porque en realidad trabajará con Soldados.

Así que Ejercito gestionará un ArrayList de Soldados. Métodos
- para reclutar Soldados, es decir, insertar Soldados en el ArrayList
- otro para retornar un Soldado de una posición en concreto, esto se usará en los combates por turnos. Como es posible que nos soliciten una posición donde no queda un soldado vivo, controlamos esta excepción y retornaremos null
- otro método comprobará si hay algún soldado muerto y lo eliminará de la lista
- por último, un método que indica si el ejército ha sido derrotado, es decir, ya no quedan soldados en sus filas.

Continuo en el siguiente mensaje.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #2 en: 28 de Agosto 2021, 14:38 »
Bien, tenemos la clase Ejercito.

Veamos la clase Soldado.
Esta será una superclase, que hará de nexo común entre Heroes y Bestias.
Será abstracta, es decir, no podremos crear un Soldado. Se podrán crear Heroes y Bestias (que son Soldados), pero no un Soldado que no pertenezca a un bando o al otro.

Tendrá los atributos comunes: nombre, vida y resistencia.
Tendrá los métodos comunes: getNombre(), getResistencia(), toString()....

Tendrá el método que indica como gestionar recibir el daño de un ataque, porque esto es igual tanto para Heroes como para Bestias.
Pero NO tendrá el método que gestiona como producir el daño de ataque, ya que esto es distinto para Heroes(mejor de dos dados) y para Bestias(un solo dado)

Si tendrá la "firma abstracta" de dicho método, es decir, no tendrá el código. Pero si "obligará" a que sus clases hijas, Heroes y Bestias, tengan que implementar y sobreescribir este método

Código: [Seleccionar]
public abstract class Soldado {

private String nombre;
private int puntosVida;
private int nivelResistencia;

public Soldado(String nombre, int puntosVida, int nivelResistencia) {
this.nombre = nombre;
this.puntosVida = puntosVida;
this.nivelResistencia = nivelResistencia;
}

public String getNombre() {
return nombre;
}

public boolean estaMuerto() {
return puntosVida <= 0;
}

public int getNivelResistencia() {
return nivelResistencia;
}

public void recibirAtaque(int ataque) {
if (ataque > nivelResistencia)
puntosVida -= ataque - nivelResistencia;

if (puntosVida < 0)
puntosVida = 0; //Evitamos valores negativos
}

@Override
public String toString() {
return String.format("%s (Vida=%d Armadura=%d)", nombre, puntosVida, nivelResistencia);
}

//Este método lo han de sobreescribir Heroe y Bestia, porque será distinto para cada uno
public abstract int atacar();

}

Ahora vamos a ver la clase Heroe.
Esta clase hereda de Soldado.
Un Heroe es un Soldado. ¿Tiene sentido decir eso? Sí lo tiene, así que aprobamos la "relación de herencia"  ;)

Al heredar de Soldado, esta clase estará obligada a sobreescribir el método atacar(), siendo esto lo único que le va a distinguir de la clase Bestia
Código: [Seleccionar]
import java.util.Random;

public class Heroe extends Soldado {

public Heroe(String nombre, int puntosVida, int nivelResistencia) {
super(nombre, puntosVida, nivelResistencia);
}

@Override
public int atacar() {
// El ataque será el mejor lanzamiento entre dos dados de 0 a 100
Random dado = new Random();
int tirada1 = dado.nextInt(101);
int tirada2 = dado.nextInt(101);
System.out.println("Primer dado: " + tirada1);
System.out.println("Segundo dado: " + tirada2);
return Math.max(tirada1, tirada2);
}

}

Ahora la clase Bestia, que viene a ser lo mismo que Heroe, solo varía el código del método que se usa para atacar()
Código: [Seleccionar]
import java.util.Random;

public class Bestia extends Soldado{

public Bestia(String nombre, int puntosVida, int nivelResistencia) {
super(nombre, puntosVida, nivelResistencia);
}

@Override
public int atacar() {
//Único lanzamiento de un dado entre 0 y 90
Random dado = new Random();
int tirada = dado.nextInt(91);
System.out.println("Resultado del dado es: " + tirada);
return tirada;
}

}

Luego vendrían las subclases Humano, Elfo, Orco.... y todas las que queramos imaginar.
No vale la pena que las ponga aquí todas, simplemente lo que han de hacer es heredar de Heroe, o de Bestia, según corresponda:
Código: [Seleccionar]
public class Trasgo extends Bestia {

public Trasgo(String nombre, int puntosVida, int nivelResistencia) {
super(nombre, puntosVida, nivelResistencia);
}

}

Bien, pasamos a lo importante.
La clase Batalla.
Siguiendo un poco el enfoque que tú le diste, este clase tendrá un método estático que recibirá los dos Ejercitos que se van a enfrentar.
Cada Ejercito tiene su propio ArrayList de Soldados Heroes o Soldados Bestia, así que no se necesita declarar ningún otro ArrayList, ni array primitivo, ni nada...

El proceso de batalla sería que, mientras ningún ejercito haya sido derrotado...
... pues hacemos una ronda de turnos.
La duración de esta ronda depende del tamaño del ejército más grande. Los ejércitos cambiarán de tamaño durante la ejecución del programa, así que tras cada ronda de turnos, se vuelve a calcular su duración.

En cada turno, se le pide a cada Ejercito un Soldado que ocupen la misma posición.
Puede ocurrir que algún Ejercito no tenga un Soldado disponible en esa posición, entonces no hay combate y el Soldado que sí está disponible "queda en guardia".

Puede ocurrir que no haya soldado disponible en ninguno de los dos ejércitos, en ese caso, simplemente se pone fin a esta ronda de turnos.

Y si tenemos la suerte de dispone de ambos soldados, pues comienza el combate.
Primero ataca el Heroe. Si la Bestia sobrevive, pues hace su turno de ataque.

Tras esto, cada ejercito actualiza sus filas, es decir, comprueba si hay algún muerto que eliminar de su ArrayList.
Y tras esto, se pasa al siguiente turno con los siguientes soldados

Al terminar los turnos, se inicia una nueva ronda, cuya duración se calcula de nuevo según el ejército con más soldados.

Todo esto terminará cuando alguno de los Ejercitos, haya sido derrotado

Para poder ver paso a paso que ocurre en pantalla, uso un Scanner para pausar la ejecución del programa hasta que se pulse la tecla INTRO.

Código: [Seleccionar]
import java.util.Scanner;

public class Batalla {

private static Scanner sc = new Scanner(System.in);

public static void batallar(Ejercito heroes, Ejercito bestias) {

//Mientras ningun ejército haya sido derrotado....
while(!heroes.esDerrotado() && !bestias.esDerrotado()) {

//Calculamos el total de turnos, según el ejército más grande
int turnosTotal;
if (heroes.soldados.size() >= bestias.soldados.size())
turnosTotal = heroes.soldados.size();
else
turnosTotal = bestias.soldados.size();

//Comienza una ronda de turnos
for (int turno = 0; turno < turnosTotal; turno++) {
//Seleccionamos combatientes
Heroe heroe = (Heroe) heroes.getSoldado(turno);
Bestia bestia = (Bestia) bestias.getSoldado(turno);
//Comprobamos que ninguno sea null
if (heroe == null && bestia == null)
//¿Ambos son null?Entonces esta ronda de turnos ha terminado
break;
else if (heroe == null)
//No hay Heroe, Bestia queda en guardia
System.out.println(bestia.getNombre() + " queda en guardia");
else if (bestia == null)
//No hay Bestia, Heroe queda en guardia
System.out.println(heroe.getNombre() + " queda en guardia");
else {
//Ninguno es null, comienza el combate
System.out.println("Lucha entre " + heroe + " y " + bestia);
//Turno heroe
System.out.println("Turno de " + heroe.getNombre());
int ataqueH = heroe.atacar();
bestia.recibirAtaque(ataqueH);
System.out.println("Datos Actualizados de " + bestia);
if (bestia.estaMuerto())
System.out.println(bestia.getNombre() + " ha muerto.");
else {
//Turno bestia
System.out.println("Turno de " + bestia.getNombre());
int ataqueB = bestia.atacar();
heroe.recibirAtaque(ataqueB);
System.out.println("Datos Actualizados de " + heroe);
if (heroe.estaMuerto())
System.out.println(heroe.getNombre() + " ha muerto.");
}
}
//Turno combate finalizado, ejercitos actualizan sus filas
heroes.comprobarEjercito();
bestias.comprobarEjercito();
pausa();
//Y se inicia el siguiente turno
}
}

//Las rondas de turnos han finalizado porque algún ejército ha sido derrotado. Comprobamos
if (heroes.esDerrotado())
System.out.println("Han ganado las Bestias. Soldados restantes: " + bestias.soldados.size());
else
System.out.println("Han ganado los Heroes. Soldados restantes: " + heroes.soldados.size());
}

private static void pausa() {
System.out.println("\n\t\tPulse INTRO para continuar...\n");
sc.nextLine();
}

}


Y listo.
Ahora solo queda hacer una clase Main, declarar dos Ejercitos, reclutar algunos Soldados.., y hacer que se maten entre ellos.. ::)
Código: [Seleccionar]
public class Main {

    public static void main (String args[]){
   
        Ejercito heroes = new Ejercito();
        Ejercito bestias = new Ejercito();

        heroes.reclutarSoldado(new Elfos("Legolas", 150, 30));
        heroes.reclutarSoldado(new Humano("Aragorn", 150, 50));
        heroes.reclutarSoldado(new Humano("Boromir",100, 60));
        heroes.reclutarSoldado(new Humano("Gandalf",300, 10));
        heroes.reclutarSoldado(new Hobbits("Frodo", 20, 10));

        bestias.reclutarSoldado(new Orcos("Lurtz", 200, 60));
        bestias.reclutarSoldado(new Orcos("Shagrat", 220, 50));
        bestias.reclutarSoldado(new Trasgo("Uglúk", 120, 30));
        bestias.reclutarSoldado(new Trasgo("Mauhúr", 100, 30));

        Batalla.batallar(heroes, bestias);

    }
}

Si algo no ha quedado claro, por favor, no dudes en preguntar, y repreguntar.

Un saludo.
« Última modificación: 28 de Agosto 2021, 14:41 por Kabuto »
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #3 en: 28 de Agosto 2021, 20:57 »
!Muchísimas gracias de verdad, porque estaba muy perdida!

La verdad pensaba que la herencia lo había comprendido mejor pero me ha resultado mucho más útil y sencillo de entender con la manera que indicas.

Una última pregunta si quisiera hacer por ejemplo que un Elfo incrementara su ataque en X puntos si se enfrenta a un Orco, ¿se podría realizar llamando de nuevo al método atacar() dentro de un método específico para esa clase Elfo o es mejor de otra manera?

Muchas gracias de nuevo  ;D

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #4 en: 28 de Agosto 2021, 23:51 »
Mmhhh.. para hacer lo que dices...

El problema es que tal y como yo he diseñado la batalla, los Soldados no pueden saber a quién se están enfrentando.
El que ataca genera un valor de daño, y este valor se lo enviamos al que defiende para que evalúe si es suficiente para producirle daño o no.
Código: [Seleccionar]
int ataqueH = heroe.atacar();
bestia.recibirAtaque(ataqueH);
Pero no hay comunicación directa entre ellos, no saben si están atacando a un Orco, a un Goblin, o que...

Pero bueno, podría modificarse para que el método atacar(), exija recibir entre paréntesis al enemigo que se está atacando.
Y ahora, en lugar de retornar el valor de ataque en un int, dicho valor se aplicará directamente al enemigo
Comenzamos indicando esta exigencia en la declaración abstracta de este método, en la clase Soldado.
Marco los cambios en negrita
Citar
//Este método lo han de sobreescribir Heroe y Bestia, porque será distinto para cada uno
   public abstract void atacar(Soldado enemigo);

Al hacer este cambio, ahora Java nos obliga a que, al menos, las clases Heroe y Bestia se adapten a esta exigencia.
Lo que haremos será cambiar el código para que ya no se retorne ningún valor, si no que el daño se aplicará al "enemigo" recibido entre paréntesis.

La clase Heroe queda con estos cambios:

Citar
import java.util.Random;

public class Heroe extends Soldado {

   public Heroe(String nombre, int puntosVida, int nivelResistencia) {
      super(nombre, puntosVida, nivelResistencia);
   }

   @Override
   public void atacar(Soldado enemigo) {
      // El ataque será el mejor lanzamiento entre dos dados de 0 a 100
      Random dado = new Random();
      int tirada1 = dado.nextInt(101);
      int tirada2 = dado.nextInt(101);
      System.out.println("Primer dado: " + tirada1);
      System.out.println("Segundo dado: " + tirada2);
      int maximo =  Math.max(tirada1, tirada2);
      enemigo.recibirAtaque(maximo);

   }

}

Y la clase Bestia queda así:

Citar
import java.util.Random;

public class Bestia extends Soldado{

   public Bestia(String nombre, int puntosVida, int nivelResistencia) {
      super(nombre, puntosVida, nivelResistencia);
   }

   @Override
   public void atacar(Soldado enemigo) {
      //Único lanzamiento de un dado entre 0 y 90
      Random dado = new Random();
      int tirada = dado.nextInt(91);
      System.out.println("Resultado del dado es: " + tirada);
      enemigo.recibirAtaque(tirada);
   }

}

Y por último modificamos ligeramente el método de la clase Batalla.
Ahora ya no capturamos el daño generado para pasárselo al enemigo a ver si le hemos hecho pupa, todo esto ya ocurre dentro del método atacar() de cada Soldado.
Es decir, las líneas que indiqué al principio:
Código: [Seleccionar]
int ataqueH = heroe.atacar();
bestia.recibirAtaque(ataqueH);
Ahora se reducen a esto:
Código: [Seleccionar]
heroe.atacar(bestia);
Así que la clase Batalla queda así:

Citar
public class Batalla {
   
   private static Scanner sc = new Scanner(System.in);
   
   public static void batallar(Ejercito heroes, Ejercito bestias) {
      
      //Mientras ningun ejército haya sido derrotado....
      while(!heroes.esDerrotado() && !bestias.esDerrotado()) {

         //Calculamos el total de turnos, según el ejército más grande
         int turnosTotal;
         if (heroes.soldados.size() >= bestias.soldados.size())
            turnosTotal = heroes.soldados.size();
         else
            turnosTotal = bestias.soldados.size();
         
         //Comienza una ronda de turnos
         for (int turno = 0; turno < turnosTotal; turno++) {
            //Seleccionamos combatientes
            Heroe heroe = (Heroe) heroes.getSoldado(turno);
            Bestia bestia = (Bestia) bestias.getSoldado(turno);
            //Comprobamos que ninguno sea null
            if (heroe == null && bestia == null)
               //¿Ambos son null?Entonces esta ronda de turnos ha terminado
               break;
            else if (heroe == null)
               //No hay Heroe, Bestia queda en guardia
               System.out.println(bestia.getNombre() + " queda en guardia");
            else if (bestia == null)
               //No hay Bestia, Heroe queda en guardia
               System.out.println(heroe.getNombre() + " queda en guardia");
            else {
               //Ninguno es null, comienza el combate
               System.out.println("Lucha entre " + heroe + " y " + bestia);
               //Turno heroe
               System.out.println("Turno de " + heroe.getNombre());
               heroe.atacar(bestia);
               System.out.println("Datos Actualizados de " + bestia);
               if (bestia.estaMuerto())
                  System.out.println(bestia.getNombre() + " ha muerto.");
               else {
                  //Turno bestia
                  System.out.println("Turno de " + bestia.getNombre());
                  bestia.atacar(heroe);
                  System.out.println("Datos Actualizados de " + heroe);
                  if (heroe.estaMuerto())
                     System.out.println(heroe.getNombre() + " ha muerto.");
               }
            }
            //Turno combate finalizado, ejercitos actualizan sus filas
            heroes.comprobarEjercito();
            bestias.comprobarEjercito();
            pausa();
            //Y se inicia el siguiente turno
         }
      }
      
      //Las rondas de turnos han finalizado porque algún ejército ha sido derrotado. Comprobamos
      if (heroes.esDerrotado())
         System.out.println("Han ganado las Bestias. Soldados restantes: " + bestias.soldados.size());
      else
         System.out.println("Han ganado los Heroes. Soldados restantes: " + heroes.soldados.size());
   }

   private static void pausa() {
      System.out.println("\n\t\tPulse INTRO para continuar...\n");
      sc.nextLine();
   }
   
}


Si ahora, con estos cambios, ejecutamos el programa. Va a funcionar exactamente igual que antes.
Pero ahora sí hemos conseguido que el Soldado que ataca, pueda saber a quién está atacando.
Y esto abre la puerta a conseguir lo que tu pedías, poder añadir reglas específicas de combate según la subclase de cada Soldado.

Por ejemplo: Queremos que un Elfo aumente su ataque al enfrentarse un Orco

Bien, ahora mismo, las clases Elfo, Orco, Humano, etc... son muy simples. No aportan nada.

Código: [Seleccionar]
public class Elfos extends Heroe {

public Elfos(String nombre, int puntosVida, int nivelResistencia) {
super(nombre, puntosVida, nivelResistencia);
}

}

Pero podemos pedirle que sobreescriban el método atacar() para cambiar las reglas de ataque.

Por ejemplo, podemos hacer que un Elfo incremente su ataque un 50% (1.5 veces) si se enfrenta a un Orco.


Citar
public class Elfos extends Heroe {

   public Elfos(String nombre, int puntosVida, int nivelResistencia) {
      super(nombre, puntosVida, nivelResistencia);
   }

   @Override
   public void atacar(Soldado enemigo) {
      if (enemigo instanceof Orcos) {
         //Regla específica cuando un Elfo ataca un Orco
         Random dado = new Random();
         int tirada1 = dado.nextInt(101);
         int tirada2 = dado.nextInt(101);
         System.out.println("Primer dado: " + tirada1);
         System.out.println("Segundo dado: " + tirada2);
         int maximo =  Math.max(tirada1, tirada2);
         System.out.println("*****¡¡El odio élfico hacia los Orcos incrementa el ataque en 1.5x!!*****");
         maximo *= 1.5;
         System.out.println("Valor de ataque resultante: " + maximo);
         enemigo.recibirAtaque(maximo);
         
      }
      else //Si no es Orco, se aplica la regla general de los Heroes
         super.atacar(enemigo);
   }

}

Entonces ahora, al combatir, veremos como Légolas puede hacer ataques letales al enfrentarse con Orcos:
Citar
Lucha entre Legolas (Vida=53 Armadura=30) y Shagrat (Vida=31 Armadura=50)
Turno de Legolas
Primer dado: 17
Segundo dado: 95
*****¡¡El odio élfico hacia los Orcos incrementa el ataque en 1.5x!!*****
Valor de ataque resultante: 142
Datos Actualizados de Shagrat (Vida=0 Armadura=50)
Shagrat ha muerto.

En cambio si se enfrenta a un Goblin, atacará como cualquier otro Heroe
Citar
Lucha entre Legolas (Vida=150 Armadura=30) y Uglúk (Vida=120 Armadura=30)
Turno de Legolas
Primer dado: 21
Segundo dado: 74
Datos Actualizados de Uglúk (Vida=76 Armadura=30)
Turno de Uglúk
Resultado del dado es: 30
Datos Actualizados de Legolas (Vida=150 Armadura=30)

Y de esta manera, podemos personalizar las reglas de ataque según cada Heroe y cada Bestia  ;)
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #5 en: 29 de Agosto 2021, 00:11 »
!!Muchísimas gracias de verdad!! No sé como agradecerte todo lo que me has ayudado.

 ;D ;D ;D ;D

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #6 en: 14 de Enero 2022, 21:16 »
Buenas noches de nuevo, Estoy intentando aplicar la librería swing al código con el que me ayudaron y no lo consigo, ya que al ir a empezar no si si es mejor hacer varias clases JPanel una para los Heroes y otra para las Bestias y dentro de ellas aplicar una estructura sencilla donde elegir el nombre, el tipo de héroe o bestia (hobbit, humano, Orco, etc), la vida y la armadura para luego dar un botón de lucha donde aparecerá el texto que ya teníamos.

Os Agradecería mucho si me pudieras ayudar de nuevo.

Un saludo.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #7 en: 15 de Enero 2022, 00:38 »
Mmmmhh.. Recomiendo primero hacer un dibujo, ya sea con Paint o con papel y lápiz, haciendo un boceto aproximado de lo que se quiere conseguir tener en pantalla.
No tiene que ser una obra de arte, bastan unos garabatos que ayuden a visualizar lo que se tiene en mente.

A partir de ahí es más fácil identificar donde será necesario un panel, un botón,...
Que luego sobre la marcha, se van tomando decisiones y quizá se acaba diseñando algo muy distinto a lo dibujado, pero el boceto al menos ya de un punto de partida.

No se cómo tienes pensado diseñarlo, pero es muy posible que puedas escribir una única clase JPanel, de la que crear dos objetos (dos paneles) y que sirva por igual para Heroes y Bestias en lo que respecta a la "Vista".
Lo que cambiará serán los datos (el "Modelo") que le pases a cada panel, es decir, a uno le pasarás Heroes y al otro Bestias
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #8 en: 15 de Enero 2022, 14:13 »
Pues en un principio he pensado en algo como la imagen que adjunto y una vez que le pinches en el botón de lucha que aparezca un cuadre con el desglose de la batalla.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #9 en: 15 de Enero 2022, 23:53 »
OK.

Pues como dije, para los dos paneles superiores en principio bastaría con escribir una única clase JPanel.
Solo cambiará el texto de algún JLabel y que uno será para Heroes y otro para Bestias, que en realidad todos son clase Soldado.

Lo mismo con los paneles inferiores, escribiendo una sola clase podrías crear ambos paneles.

En la parte inferior, quizás se podría añadir otro panel con un JTextArea donde se irían mostrando los textos del combate
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #10 en: 16 de Enero 2022, 00:52 »
Muchas gracias, pero perdona que te pregunte, otra vez, porque no lo entiendo del todo y por confirmar, en la clase por ejemplo PanelArriba, ¿en el constructor llamo a la clase soldado y como de ella se desglosa Heroes y bestias ya podría aplicar un JList?

Perdona de nuevo, porque aunque veo que swing no es difícil al tener un código ya hecho me es más complicado pasarlo a una interfaz gráfica y me está costando más de lo que esperaba.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #11 en: 16 de Enero 2022, 02:49 »
Coger un código escrito para el "modo consola" y adaptarlo a una GUI, sí, es más complicado de lo que parece, porque el flujo del programa es muy distinto, así como la forma de organizarlo.

Además, no se que herramientas vas a usar para crearlo.
Es decir, en los centros de enseñanza es habitual enseñar Swing creando interfaces directamente con el asistente de Netbeans (o el equivalente para otro IDE), con la opción JFrame Form, donde indicas donde quieres colocar un componente swing y Netbeans te escribe todo el código necesario (y también el innecesario).
Esto permite crear de forma rápida interfaces, pero implica que el programador se desentienda de la estructura del código y en mi modesta opinión (no soy ni maestro, ni profesional ni nada...) luego cuesta más encontrar una forma coherente de combinar y relacionar la Vista (lo que se ve en pantalla, botones, campos, etiquetas...) con el Modelo (lo que no se ve, los ArrayList, variables, métodos, clases, etc..)

Yo las interfaces las hago escribiendo código a mano y es cierto que se requiere más tiempo de aprendizaje conseguir crear interfaces medio decentes..., pero al tener total control sobre el código escrito, luego es más fácil relacionar Vista y Modelo.

No se cuál de estos dos métodos vas a emplear tú, pero en cualquier caso...

...el panel superior, o los paneles superiores (yo uniría dos paneles creados a partir de una misma clase) su función es crear Soldados para los Ejercitos.

El código debería tener en algún sitio dos objetos Ejercitos ya declarados e inicializados al comenzar el programa.
Estos Ejercitos comienza sin Soldados, los cuáles se irán creando con el panel superior y añadiendo al Ejercito correspondiente según si son Heroes o Bestias.

Si el panel superior va a ser creado como clase independiente del resto del programa, necesitamos de algún modo que tenga acceso a esos Ejercitos para que cuando se pulse el botón Añadir pueda agregar Soldados a los Ejercitos.

Una opción es pasar por constructor una referencia a esos Ejercitos, los cuales seguramente vamos a querer tener declarados en la clase JFrame principal.
Así, los cambios que hagamos en los Ejercitos desde la clase del panel superior, se verán reflejados en los Ejercitos declarados en la clase principal.


Otra opción, es escribir el código del ActionListener del botón "Añadir" como una subclase escrita en la clase JFrame principal, donde se tendría acceso a los Ejercitops ahí declarados.
Luego, este ActionListener se lo transferimos al botón del panel superior. Así, aunque este panel superior no tenga referencia o acceso a los Ejercitos, no importará, porque su ActionListener, que es quien recogerá los datos introducidos y creará el nuevo Soldado, si tiene acceso a los Ejercitos.



No se si me estoy explicando bien, a ver si mañana puedo escribir algún ejemplo.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #12 en: 16 de Enero 2022, 08:21 »
Pues voy a utilizar el IDE de IntellJ IDEA, pero no aplicando un interfaz directamente sino escribiendo código ya que también soy de la opinión que es mejor escribir el código desde cero, pero estoy con el problema de que cuando voy a empezar me surgen tantas dudas  que me bloqueo y no consigo escribir ni una sola línea de código.

Te agradecería mucho un pequeño ejemplo de lo que comentas porque no lo entiendo bien, muchísimas gracias por la ayuda.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #13 en: 16 de Enero 2022, 13:45 »
Es normal bloquearse  al principio ;)
Y también seguir caminos que llevan a callejones sin salida, pero no te preocupes, forma parte del proceso de aprendizaje.

Lo ideal es centrarte primero en crear la Vista, sin preocuparte demasiado por el Modelo.
Es decir, haz una interfaz con sus campos y botones, que de momento, no van a tener ninguna función.

Luego ya irás integrando el Modelo. Y sí, puede que durante ese proceso veas la necesidad de reescribir o desechar parte del código escrito para la Vista. Pero así va esto.

A ver, voy a empezar un proyecto de cero.
En el diseño que propones, tanto para la parte de arriba como para la de abajo, yo crearía dos paneles a partir de una única clase.
Lo señalado en rojo serían dos paneles, pero de una misma clase.
Y lo mismo para lo señalado en verde.


Veamos la clase para los paneles superiores.
En lo que respecta a la Vista, entre ambos paneles solo cambia el titulo del borde del panel y las opciones del JComboBox. Así que estos datos se pueden indicar por el constructor

Para maquetar los componentes, puedes usar el/los layouts que tú prefieras.

Yo (de momento) creo que voy a usar un BoxLayout vertical, donde apilar paneles unos sobre otros.
Estos paneles los haré con una subclase que reciba por constructor el texto para un JLabel y el componente que acompañará

Clase PanelCrearSoldado
Código: [Seleccionar]
public class PanelCrearSoldado extends JPanel {

private JTextField jtNombre;
private JComboBox<String> jcTipo;
private JTextField jtVida;
private JTextField jtArmadura;
private JButton btAnadir;

public PanelCrearSoldado(String titulo, String[] tipos) {
//Inicializamos componentes
jtNombre = new JTextField();
jcTipo = new JComboBox<String>(tipos);
jtVida = new JTextField();
jtArmadura = new JTextField();
btAnadir = new JButton("Añadir");

//Layout de "cajas" verticales
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

/*
* Cada "caja" apilada verticalmente será un panel
* de clase PanelConLabel
*/
add(new PanelConLabel("Nombre:", jtNombre));
add(new PanelConLabel("Tipo:", jcTipo));
add(new PanelConLabel("Vida:", jtVida));
add(new PanelConLabel("Armadura:", jtArmadura));

//Boton añadir
add(btAnadir);

//Combinamos dos bordes, uno titulado y otro vacío para crear algo de relleno
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(15, 15, 15, 15),
BorderFactory.createTitledBorder(titulo)));

}

private class PanelConLabel extends JPanel {

public PanelConLabel(String texto, JComponent componente) {
//Layout tipo grilla, será una fila con dos columnas
setLayout(new GridLayout(1,2));
//En la primera columna, la etiqueta
add(new JLabel(texto));
//En la segunda columna, el componente que acompaña la etiqueta
add(componente);
}
}

}

Estaría bien poder ver cómo ha quedado, así que vamos a crear ahora la clase principal, que será el JFrame que contenga toda la interfaz.
De momento mostraremos solo el panel que hemos desarrollado, a ver que aspecto tiene.
Código: [Seleccionar]
public class BatallaRPG extends JFrame {

private PanelCrearSoldado crearHeroes;

public BatallaRPG() {

crearHeroes = new PanelCrearSoldado("Heroes", new String[] {"Elfo", "Humano", "Hobbit"});

setContentPane(crearHeroes);

setTitle("Batalla RPG");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new BatallaRPG();
}
});
}
}

Veamos qué se nos dibuja en pantalla:


Bueno, no es una belleza, pero básicamente es el diseño que buscábamos. Necesita arreglillos.
Más separación entre las "cajas" apiladas, podemos hacerlo inflándolas un poco añadiendo algo de borde vacío en su interior.
Y el botón "Añadir" quedará mejor centrado si lo colocamos dentro de su propio panel...

Citar
public class PanelCrearSoldado extends JPanel {
   
   private JTextField jtNombre;
   private JComboBox<String> jcTipo;
   private JTextField jtVida;
   private JTextField jtArmadura;
   private JButton btAnadir;
   
   public PanelCrearSoldado(String titulo, String[] tipos) {
      //Inicializamos componentes
      jtNombre = new JTextField();
      jcTipo = new JComboBox<String>(tipos);
      jtVida = new JTextField();
      jtArmadura = new JTextField();
      btAnadir = new JButton("Añadir");
      
      //Layout de "cajas" verticales
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      
      /*
       * Cada "caja" apilada verticalmente será un panel
       * de clase PanelConLabel
       */
      add(new PanelConLabel("Nombre:", jtNombre));
      add(new PanelConLabel("Tipo:", jcTipo));
      add(new PanelConLabel("Vida:", jtVida));
      add(new PanelConLabel("Armadura:", jtArmadura));
      
      //Boton añadir, le damos un panel propio para que quede mejor centrado
      JPanel pnAnadir = new JPanel();
      pnAnadir.add(btAnadir);
      add(pnAnadir);

      
      //Combinamos dos bordes, uno titulado y otro vacío para crear algo de relleno
      setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createEmptyBorder(15, 15, 15, 15),
            BorderFactory.createTitledBorder(titulo)));
      
   }
   
   private class PanelConLabel extends JPanel {
      
      public PanelConLabel(String texto, JComponent componente) {
         //Layout tipo grilla, será una fila con dos columnas
         setLayout(new GridLayout(1,2));
         //En la primera columna, la etiqueta
         add(new JLabel(texto));
         //En la segunda columna, el componente que acompaña la etiqueta
         add(componente);
         //"Inflamos" el panel con algo de borde vacío
         setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

      }
   }

}

A ver...



Bueno, ya tiene mejor aspecto. Por ahora puede servir para seguir avanzando.
Lo que haremos será poner a su lado un segundo panel, esta vez para crear Bestias.

Para ponerlos juntos, en el JFrame crearemos un "panel superior" donde insertaremos nuestros dos paneles de creación de soldados.

Citar
public class BatallaRPG extends JFrame {
   
   private PanelCrearSoldado crearHeroes;
   private PanelCrearSoldado crearBestias;
   
   public BatallaRPG() {
      
      crearHeroes = new PanelCrearSoldado("Heroes", new String[] {"Elfo", "Humano", "Hobbit"});
      crearBestias = new PanelCrearSoldado("Bestias", new String[] {"Trasgo", "Orco"});
      
      JPanel pnSuperior = new JPanel();
      pnSuperior.add(crearHeroes);
      pnSuperior.add(crearBestias);
      
      setContentPane(pnSuperior);

      
      setTitle("Batalla RPG");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      pack();
      setLocationRelativeTo(null);
      setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            new BatallaRPG();   
         }
      });
   }
}

Y de esta manera, con una única clase JPanel, ya tenemos dos paneles idénticos para crear un tipo de soldado u otro.




Bueno, y ahora viene el quid de la cuestión.
¿Dónde se guardan los soldados? ¿Cómo sabrán estos paneles que soldado crear y donde ubicarlos?

Bien, tendremos dos Ejercitos, y estos conviene que se encuentren en la clase principal JFrame, para que su "ámbito" sea lo más amplio posible y los distintos paneles que finalmente compongan la interfaz tengan posibilidad de acceder a ellos.

Así que los añadimos como atributos del JFrame, y los inicializamos en el constructor.
Citar
public class BatallaRPG extends JFrame {
   
   //Modelo
   private Ejercito bestias;
   private Ejercito heroes;

   
   //Vista
   private PanelCrearSoldado crearHeroes;
   private PanelCrearSoldado crearBestias;
   
   public BatallaRPG() {
      bestias = new Ejercito();
      heroes = new Ejercito();

      crearHeroes = new PanelCrearSoldado("Heroes", new String[] {"Elfo", "Humano", "Hobbit"});
      crearBestias = new PanelCrearSoldado("Bestias", new String[] {"Trasgo", "Orco"});
      
      JPanel pnSuperior = new JPanel();
      pnSuperior.add(crearHeroes);
      pnSuperior.add(crearBestias);
      
      setContentPane(pnSuperior);
      
      setTitle("Batalla RPG");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      pack();
      setLocationRelativeTo(null);
      setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         @Override
         public void run() {
            new BatallaRPG();   
         }
      });
   }
}

Ahora necesitamos que los paneles de creación de soldados, tengan algún tipo de conexión con estos ejércitos para poder agregarles soldados.

Una forma, sería dotar a estos paneles de una referencia a estos ejércitos. Como cada panel va a interactuar con un único ejercito, le añadimos a su clase un ejercito como atributo, y recibirá por constructor la referencia de uno de los ejércitos declarados en la clase principal.

Ya de paso, desarrollamos un ActionListener para que el botón "Añadir" comience a funcionar.
Esta acción ha de recuperar los datos de los campos, evaluar que tipo de soldado se está creando y añadirlo al ejercito que tenga referenciado.
De momento, podemos informar por consola de que hay un nuevo recluta.

Citar
public class PanelCrearSoldado extends JPanel {
   
   private JTextField jtNombre;
   private JComboBox<String> jcTipo;
   private JTextField jtVida;
   private JTextField jtArmadura;
   private JButton btAnadir;
   
   //Atributo para referenciar alguno de los ejercitos de la clase main
   private Ejercito ejercito;

   
   public PanelCrearSoldado(String titulo, String[] tipos, Ejercito ejercito) {
      //Inicializamos componentes
      jtNombre = new JTextField();
      jcTipo = new JComboBox<String>(tipos);
      jtVida = new JTextField();
      jtArmadura = new JTextField();
      btAnadir = new JButton("Añadir");
      btAnadir.addActionListener(new AccionCrearSoldado());
      
      this.ejercito = ejercito; //Ejercito referenciado
      
      //Layout de "cajas" verticales
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      
      /*
       * Cada "caja" apilada verticalmente será un panel
       * de clase PanelConLabel
       */
      add(new PanelConLabel("Nombre:", jtNombre));
      add(new PanelConLabel("Tipo:", jcTipo));
      add(new PanelConLabel("Vida:", jtVida));
      add(new PanelConLabel("Armadura:", jtArmadura));
      
      //Boton añadir, le damos un panel propio para que quede mejor centrado
      JPanel pnAnadir = new JPanel();
      pnAnadir.add(btAnadir);
      add(pnAnadir);
      
      //Combinamos dos bordes, uno titulado y otro vacío para crear algo de relleno
      setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createEmptyBorder(15, 15, 15, 15),
            BorderFactory.createTitledBorder(titulo)));
      
   }
   
   private class PanelConLabel extends JPanel {
      
      public PanelConLabel(String texto, JComponent componente) {
         //Layout tipo grilla, será una fila con dos columnas
         setLayout(new GridLayout(1,2));
         //En la primera columna, la etiqueta
         add(new JLabel(texto));
         //En la segunda columna, el componente que acompaña la etiqueta
         add(componente);
         //"Inflamos" el panel con algo de borde vacío
         setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
      }
   }
   
   private class AccionCrearSoldado implements ActionListener {

      @Override
      public void actionPerformed(ActionEvent e) {
         //Recogemos datos
         String nombre = jtNombre.getText();
         String tipo = (String) jcTipo.getSelectedItem();
         int vida = Integer.parseInt(jtVida.getText());
         int armadura = Integer.parseInt(jtArmadura.getText());
         
         //Creamos nuevo soldado según tipo
         switch(tipo) {
         case "Elfo":
            ejercito.reclutarSoldado(new Elfos(nombre, vida, armadura));
            break;
         case "Humano":
            ejercito.reclutarSoldado(new Humano(nombre, vida, armadura));
            break;
         case "Hobbit":
            ejercito.reclutarSoldado(new Hobbits(nombre, vida, armadura));
            break;
         case "Trasgo":
            ejercito.reclutarSoldado(new Trasgo(nombre, vida, armadura));
            break;
         case "Orco":
            ejercito.reclutarSoldado(new Orcos(nombre, vida, armadura));
            break;
         }
         //Limpiamos campos
         jtNombre.setText(null);
         jcTipo.setSelectedIndex(0);
         jtVida.setText(null);
         jtArmadura.setText(null);
         
         //Confirmamos en consola la creación
         System.out.println("Nuevo soldado reclutado");         
      }

   }

}

Ahora en la clase JFrame, ya podemos pasarles las referencias a estos paneles
Citar
public class BatallaRPG extends JFrame {
   
   //Modelo
   private Ejercito bestias;
   private Ejercito heroes;
   
   //Vista
   private PanelCrearSoldado crearHeroes;
   private PanelCrearSoldado crearBestias;
   
   public BatallaRPG() {
      bestias = new Ejercito();
      heroes = new Ejercito();
      //Este panel referenciará los Heroes
      crearHeroes = new PanelCrearSoldado("Heroes", new String[] {"Elfo", "Humano", "Hobbit"}, heroes);
      //Este refenciará a la Bestias
      crearBestias = new PanelCrearSoldado("Bestias", new String[] {"Trasgo", "Orco"}, bestias);


Y ahora ya podemos crear soldados en los ejércitos.
Parece que todo va por buen camino, pero luego va a venir una dificultad.... :o

Los soldados creados, han de mostrarse en un JList que aún no hemos creado. Este JList, se escribirá en otra clase JPanel.
La idea es que, al pulsar el botón "Añadir" en el panel de creación, se muestre el nuevo soldado en el JList.

Pero, y he aquí la dificultad, el botón "Añadir" y el JList pertenecen a clases diferentes. Desde el ActionListener que hemos escrito en el panel de crear personajes, no podemos actuar sobre el JList. Habrá que buscar, de nuevo, alguna solución para que haya comunicación entre esos paneles.
Pero eso ya lo veremos luego.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #14 en: 16 de Enero 2022, 17:28 »
Vamos a crear los paneles inferiores.
Como antes, mediante una única clase, se pueden crear ambos paneles.

Código: [Seleccionar]
public class PanelLista extends JPanel {

private JList<Soldado> lista;
private DefaultListModel<Soldado> modeloLista;
private JButton btSubir;
private JButton btBajar;
private JButton btEliminar;

public PanelLista(String titulo) {
lista = new JList<Soldado>();
lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
modeloLista = new DefaultListModel<Soldado>();
lista.setModel(modeloLista);
btSubir = new JButton("Subir");
btBajar = new JButton("Bajar");
btEliminar = new JButton("Subir");

setLayout(new BorderLayout());
JScrollPane scrollLista = new JScrollPane();
scrollLista.setViewportView(lista);
scrollLista.setBorder(BorderFactory.createTitledBorder(null, titulo));
add(scrollLista, BorderLayout.CENTER);

JPanel pnBotones = new JPanel();
pnBotones.add(btSubir);
pnBotones.add(btBajar);
pnBotones.add(btEliminar);
add(pnBotones, BorderLayout.SOUTH);
}

}

Bien, ya tendríamos el panel con la lista. Ahora toca ver donde lo colocamos.

Pensando en el problema de cómo hacer que el panel con JList también tenga acceso a los ejercitos..., me han pasado por la cabeza varias formas de hacerlo, todas ellas correctas, pero a ver cuál es la que requiere menos código y/o resulta menos difícil de entender.

Y entonces se me ha ocurrido que este panel con la lista, aunque sea una clase escrita a parte, la podemos integrar directamente como un componente más del panel de creación de soldados.
Así de forma sencilla, podemos pasarle copia de la referencia de los ejercitos, y no habrá que añadir nuevo código en la clase JFrame.


Así que a PanelLista, la clase que acabamos de escribir, le incluimos atributo y referencia a un ejercito

Citar
public class PanelLista extends JPanel {
   
   private JList<Soldado> lista;
   private DefaultListModel<Soldado> modeloLista;
   private JButton btSubir;
   private JButton btBajar;
   private JButton btEliminar;
   
   private Ejercito ejercito;
   
   public PanelLista(String titulo, Ejercito ejercito) {
      this.ejercito = ejercito;
      lista = new JList<Soldado>();
      lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      modeloLista = new DefaultListModel<Soldado>();
      lista.setModel(modeloLista);
      btSubir = new JButton("Subir");
      btBajar = new JButton("Bajar");
      btEliminar = new JButton("Eliminar");
      
      setLayout(new BorderLayout());
      JScrollPane scrollLista = new JScrollPane();
      scrollLista.setViewportView(lista);
      scrollLista.setBorder(BorderFactory.createTitledBorder(null, titulo));
      add(scrollLista, BorderLayout.CENTER);
      
      JPanel pnBotones = new JPanel();
      pnBotones.add(btSubir);
      pnBotones.add(btBajar);
      pnBotones.add(btEliminar);
      add(pnBotones, BorderLayout.SOUTH);
   }

}

Y en la clase PanelCrearSoldado, añadimos el panel de lista a los atributos como un componente más, le pasamos la referencia por constructor y lo colocamos debajo de las "cajas" apiladas en la interfaz.

Citar
public class PanelCrearSoldado extends JPanel {
   
   private JTextField jtNombre;
   private JComboBox<String> jcTipo;
   private JTextField jtVida;
   private JTextField jtArmadura;
   private JButton btAnadir;
   
   //Atributo para referenciar alguno de los ejercitos de la clase main
   private Ejercito ejercito;
   
   //Panel con la lista de soldados
   private PanelLista lista;

   
   public PanelCrearSoldado(String titulo, String[] tipos, Ejercito ejercito) {
      //Inicializamos componentes
      jtNombre = new JTextField();
      jcTipo = new JComboBox<String>(tipos);
      jtVida = new JTextField();
      jtArmadura = new JTextField();
      btAnadir = new JButton("Añadir");
      btAnadir.addActionListener(new AccionCrearSoldado());
      
      this.ejercito = ejercito; //Ejercito referenciado
      
      lista = new PanelLista(titulo, ejercito); //Pasamos titulo de borde y referencia a ejercito
      
      //Layout de "cajas" verticales
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
      
      /*
       * Cada "caja" apilada verticalmente será un panel
       * de clase PanelConLabel
       */
      add(new PanelConLabel("Nombre:", jtNombre));
      add(new PanelConLabel("Tipo:", jcTipo));
      add(new PanelConLabel("Vida:", jtVida));
      add(new PanelConLabel("Armadura:", jtArmadura));
      
      //Boton añadir, le damos un panel propio para que quede mejor centrado
      JPanel pnAnadir = new JPanel();
      pnAnadir.add(btAnadir);
      add(pnAnadir);
      
      //Colocamos el panel lista debajo de todo
      add(lista);

      
      //Combinamos dos bordes, uno titulado y otro vacío para crear algo de relleno
      setBorder(BorderFactory.createCompoundBorder(
            BorderFactory.createEmptyBorder(15, 15, 15, 15),
            BorderFactory.createTitledBorder(titulo)));
      
   }
   
   private class PanelConLabel extends JPanel {
      
      public PanelConLabel(String texto, JComponent componente) {
         //Layout tipo grilla, será una fila con dos columnas
         setLayout(new GridLayout(1,2));
         //En la primera columna, la etiqueta
         add(new JLabel(texto));
         //En la segunda columna, el componente que acompaña la etiqueta
         add(componente);
         //"Inflamos" el panel con algo de borde vacío
         setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
      }
   }
   
   private class AccionCrearSoldado implements ActionListener {

      @Override
      public void actionPerformed(ActionEvent e) {
         //Recogemos datos
         String nombre = jtNombre.getText();
         String tipo = (String) jcTipo.getSelectedItem();
         int vida = Integer.parseInt(jtVida.getText());
         int armadura = Integer.parseInt(jtArmadura.getText());
         
         //Creamos nuevo soldado según tipo
         switch(tipo) {
         case "Elfo":
            ejercito.reclutarSoldado(new Elfos(nombre, vida, armadura));
            break;
         case "Humano":
            ejercito.reclutarSoldado(new Humano(nombre, vida, armadura));
            break;
         case "Hobbit":
            ejercito.reclutarSoldado(new Hobbits(nombre, vida, armadura));
            break;
         case "Trasgo":
            ejercito.reclutarSoldado(new Trasgo(nombre, vida, armadura));
            break;
         case "Orco":
            ejercito.reclutarSoldado(new Orcos(nombre, vida, armadura));
            break;
         }
         //Limpiamos campos
         jtNombre.setText(null);
         jcTipo.setSelectedIndex(0);
         jtVida.setText(null);
         jtArmadura.setText(null);
         
         //Confirmamos en consola la creación
         System.out.println("Nuevo soldado reclutado");         
      }
   }

}


Y si ahora ejecutamos el programa, voilá, ya tenemos la interfaz prácticamente terminada. Falta el botón de "¡Lucha!", que aún no lo necesitamos (y el botón "Eliminar" tiene mal el texto, luego lo corrijo)


Tal vez no queda guay que el borde titulado que antes rodeaba el panel de creación, ahora también está rodeando el panel de la lista...., pero esto es un detalle estético del que yo me preocuparía al final, cuando el programa ya estuviera funcionando bien.

Porque ahora viene eso, lo difícil, hacer que todo funcione...

A ver, necesitamos que al agregar soldados, aparezcan en la lista.

Podemos hacer un método público en la clase PanelLista que renueve los elementos de la lista según lo que contenga el ArrayList del ejercito que tengamos referenciado.

Código: [Seleccionar]
public void actualizarLista() {
modeloLista.clear();
for (Soldado sold: ejercito.soldados) //Ejercito nos proporciona el ArrayList de Soldados
modeloLista.addElement(sold);

lista.setModel(modeloLista);
}

Y en la clase PanelCrearSoldado, en el ActionListener de su botón "Añadir", en lugar de lanzar un mensaje en consola, ahora lo que hacemos es invocar al método actualizarLista()
Citar
   private class AccionCrearSoldado implements ActionListener {

      @Override
      public void actionPerformed(ActionEvent e) {
         //Recogemos datos
         String nombre = jtNombre.getText();
         String tipo = (String) jcTipo.getSelectedItem();
         int vida = Integer.parseInt(jtVida.getText());
         int armadura = Integer.parseInt(jtArmadura.getText());
         
         //Creamos nuevo soldado según tipo
         switch(tipo) {
         case "Elfo":
            ejercito.reclutarSoldado(new Elfos(nombre, vida, armadura));
            break;
         case "Humano":
            ejercito.reclutarSoldado(new Humano(nombre, vida, armadura));
            break;
         case "Hobbit":
            ejercito.reclutarSoldado(new Hobbits(nombre, vida, armadura));
            break;
         case "Trasgo":
            ejercito.reclutarSoldado(new Trasgo(nombre, vida, armadura));
            break;
         case "Orco":
            ejercito.reclutarSoldado(new Orcos(nombre, vida, armadura));
            break;
         }
         //Limpiamos campos
         jtNombre.setText(null);
         jcTipo.setSelectedIndex(0);
         jtVida.setText(null);
         jtArmadura.setText(null);
         
         //Actualizamos lista
         lista.actualizarLista();   
      
      }
   }

Y ahora, cada vez que creamos un soldado, la lista los va mostrando.


En el método actualizarLista(), puede parecer un poco bruto que cada vez que se añada un elemento al ArrayList, me cargo el contenido del JList por completo y vuelvo a añadirlos todos, en lugar de simplemente añadir el último soldado creado, que sería más óptimo.

Lo he hecho así porque posiblemente, si luego queremos eliminar elementos de la lista, y cambiar el orden (que por lo tanto, habrá que cambiarlo también en el ArrayList de los ejercitos) en esos casos será más cómodo actualizar la lista por "las bravas", la borramos y la escribimos de nuevo, siempre después de haber hecho los cambios previamente en el ArrayList.

Así que ya tenemos ese método disponible para esas funcionalidades.

Curiosamente, para agregar nuevos soldados es precisamente cuándo no hace falta hacerlo por "las bravas" je je. Luego más adelante podemos crear otro método menos "agresivo" para ese caso, cuando el programa este terminado y queramos hacer algunas optimizaciones.

Pregunta si algo no te ha quedado claro de estos dos últimos mensajes. Si decides continuar con este código que he ido escribiendo, intenta tú ahora hacer los ActionListener para los botones "Subir", "Bajar" y "Eliminar"

Recuerda que los cambios que hagamos en el JList, han de reflejarse también en los ArrayList de los ejercitos.
Porque luego, cuando comience la "batalla", los Soldados que pelearan serán los que estén en los ArrayList, no en los JList, así que han de estar sincronizadas ambas listas: mismos soldados y en el mismo orden.
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #15 en: 16 de Enero 2022, 21:00 »
Muchísimas gracias de verdad porque no veía la luz, con tus explicaciones, la verdad que son muy claras y sencillas, ahora me veo un poco más capaz de terminarlo  ;D Así que me atrevo con los botones que faltan junto con sus métodos y a cruzar los dedos  ;)

De nuevo muchísimas gracias.

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #16 en: 18 de Enero 2022, 20:58 »
Hola de nuevo, estoy con el botón "Eliminar" y me gustaría, si es posible que me volvieras a ayudar ya que hay algo que no llego a entender de todo, a lo mejor es que lo estoy entendiendo mal tu explicación, con respecto a como indicar que el dato que tiene que eliminar es el del array, lo que he hecho es esto pero está claro que estoy equivocada porque no funciona (estoy en la clase PanelLista).

De nuevo gracias

Código: [Seleccionar]
    private void eliminar(){
        for (Soldado sold: ejercito.soldados)
            modeloLista.removeElement(sold);

    }

    private class ActionEliminarSoldado implements ActionListener{

        @Override
        public void actionPerformed(ActionEvent actionEvent) {
            if (btEliminar.isSelected()) {
                eliminar();
            }

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #17 en: 18 de Enero 2022, 21:35 »
Que casualidad...
Mientras escribías tu mensaje, estaba yo escribiendo la explicación de mis acciones.

Es genial que lo intentes, y te falle y no funcione... porque así es como se aprende...

Al eliminar, tienes primero que identificar cuál Soldado ha sido seleccionado en el JList.

El JList es capaz de darte el objeto Soldado elegido, este objeto se lo pasas al ArrayList para que lo borre de su lista, pero hay otro pequeño problema.

Y el problema es que el ArrayList, por sí solo, no sabe como comparar el objeto que tú le vas a pasar con los que tiene listados, para determinar cuál coincide.

Es decir, ¿Cuándo dos Soldados son iguales?
¿Si tienen la misma vida?¿El mismo nombre? ¿El mismo nombre, vida y armadura?....

En principio nos va a bastar cuando tenga el mismo nombre.
Si en la lista hemos elegido el soldado Gandalf, pues queremos que del ArrayList se borre también el objeto Soldado llamado Gandalf.

Pero claro, el ArrayList no sabe si lo que importa para decidir si dos objetos coinciden es el nombre, o la vida, o todos los atributos...

Para que el ArrayList pueda saberlo, necesitamos "enseñar" a los objetos de la clase Soldado a distinguirse unos de otros, o dicho de otro modo, a que sepan decir cuándo dos objetos Soldado son "iguales" y cuando no.

Esto lo hacemos sobreescribiendo el método equals() en la clase Soldado, y ahí vamos a establecer que dos objetos Soldado son iguales cuando coincidan sus nombres:
Código: [Seleccionar]
@Override
public boolean equals(Object obj) {
if (obj instanceof Soldado) {
Soldado otroSoldado = (Soldado) obj;
return nombre.equals(otroSoldado.getNombre());//Si los nombres coinciden, retorna true
}
else
return false;
}
Ahora, dos Soldados pueden compararse y dictaminar si son equivalentes o no.


Gracias a este paso previo, ahora veremos que la acción para el botón "Eliminar" va a requerir muy poco código.
Esta acción lo que hará será recoger el Soldado seleccionado en el JList.
Si no hay ninguno seleccionado, se lanzará un mensaje al usuario.
Y si hay uno seleccionado, simplemente se lo pasaremos al método remove() del ArrayList del Ejercito para que lo elimine y luego actualizamos el JList, donde ya no saldrá el soldado eliminado.

Código: [Seleccionar]
private class AccionEliminar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
Soldado seleccionado = lista.getSelectedValue();
if (seleccionado == null)
JOptionPane.showMessageDialog(null, "Ha de seleccionar un Soldado");
else {
ejercito.soldados.remove(seleccionado);//Eliminamos del ArrayList
actualizarLista();
}
}
}

Ese método remove(), utilizará internamente el equals() de la clase Soldado para encontrar en su lista el Soldado que es igual al que le hemos pasado (que tiene el mismo nombre) y lo eliminará.
Si no hubieramos sobreescrito el equals(), no nos habría funcionado el remove(), ya que el ArrayList por si mismo, no sabe discernir cuándo dos Soldados son iguales, ya que esa "igualdad" en realidad será según las reglas que decida el programador.


Ahora quedan los botones "subir" y "bajar". Los explico también, pero de nuevo recomiendo que primero intentes tú resolverlos.
Mi primer impulso era escribir dos acciones, una para cada botón, pero luego pensé que ambas acciones iban a ser prácticamente idénticas, así que mejor buscar la forma de escribir una única acción que pueda adaptarse a ambos botones.

La acción de estos botones va a consistir en que el usuario escoge un soldado y lo mueve arriba o abajo.
Mover arriba supone restar -1 a la posición actual del soldado elegido. Si está en posición 5, pasa a la 4.
Bajar supone sumar +1, si está en la 5, pasa a la 6.

Ese -1 ó +1 es la única diferencia entre ambas acciones, así que podemos hacer que el ActionListener de estos botones tenga un atributo de tipo int, llamado "desplazamiento" y que por constructor le pasemos un -1 para el botón "subir" y un +1 para el botón "bajar".
Así la misma acción, se adapta a ambos botones.

Recordemos que el "desplazamiento" no es en el JList donde realmente importa, se ha de hacer en el ArrayList y ya luego el JList lo reflejará en pantalla.

Para hacer este cambio en el ArrayList, lo que hago es recoger el índice que ocupa en el JList el Soldado seleccionado (que es el mismo indice que ocupa en el ArrayList) y además recojo el objeto Soldado seleccionado.

Luego, aplicando el "desplazamiento" (-1 ó +1) recojo, del ArrayList, el soldado que ha de intercambiarse y su índice.
Y para hacer el intercambio, "seteo" en el ArrayList a ambos soldados de nuevo, pero a cada uno le pongo el indice del otro.
Y así quedan intercambiados.
Luego actualizo el JList, y listo.
Código: [Seleccionar]
private class AccionSubirBajar implements ActionListener {

private int desplazamiento; //-1 para subir, +1 para bajar

public AccionSubirBajar(int desplazamiento) {
this.desplazamiento = desplazamiento;
}

@Override
public void actionPerformed(ActionEvent e) {
//Soldado seleccionado en JList
Soldado seleccionado = lista.getSelectedValue();
if (seleccionado == null)
JOptionPane.showMessageDialog(null, "Ha de seleccionar un Soldado");
else {
//Indice de soldado seleccionado, es igual en ambas listas
int indiceSeleccionado = lista.getSelectedIndex();
//Indice del soldado para intercambiar, puede que sea uno anterior o uno posterior
int indiceIntercambiar = indiceSeleccionado + desplazamiento;
//Buscamos en el ArrayList, el soldado para intercambiar
Soldado intercambiar = ejercito.getSoldado(indiceIntercambiar);

//Si intercambiar fuese null, es que el soldado seleccionado no se puede intercambiar
//Por ejemplo, si intentamos subir el soldado que ya está arriba del todo.
if (intercambiar != null) {
//Seteamos los soldados, intercambiando los indices
ejercito.soldados.set(indiceSeleccionado, intercambiar);
ejercito.soldados.set(indiceIntercambiar, seleccionado);
actualizarLista();
}
}
}

}

Y ya tengo las acciones para los tres botones.
Solo tengo que añadírselas, teniendo cuidado de indicar -1 en el constructor para el botón "subir" y +1 para el botón "bajar"
Todo esto lo haremos en la clase PanelLista

Citar
   public PanelLista(String titulo, Ejercito ejercito) {
      this.ejercito = ejercito;
      lista = new JList<Soldado>();
      lista.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      modeloLista = new DefaultListModel<Soldado>();
      lista.setModel(modeloLista);
      btSubir = new JButton("Subir");
      btSubir.addActionListener(new AccionSubirBajar(-1));
      btBajar = new JButton("Bajar");
      btBajar.addActionListener(new AccionSubirBajar(1));
      btEliminar = new JButton("Eliminar");
      btEliminar.addActionListener(new AccionEliminar());
« Última modificación: 18 de Enero 2022, 21:37 por Kabuto »
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 16
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #18 en: 21 de Enero 2022, 21:07 »
¡Muchas gracias de nuevo ;D ;D! los botones de subir y bajar los tenía planteados con dos métodos asociados a la posición detallándolo como comentabas, restando para subir y sumando para bajar, pero lo veo mucho más claro con un solo método.

Me surgen dudas con el botón de la batalla, este no lo veo nada claro, como ya  está el método batalla ¿se podría plantear instanciándolo en la clase Batalla RPG y asociando a cuando se pinche?

Gracias de nuevo.

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 860
    • Ver Perfil
Re: Problema Batalla RPG por turnos con java
« Respuesta #19 en: 21 de Enero 2022, 22:11 »
Sí.
Debería bastar con crear un botón en BatallaRPG y en su ActionListener dar comienzo a la batalla invocando al método de la clase Batalla:
Código: [Seleccionar]
btLuchar = new JButton("¡Luchar!");
btLuchar.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Batalla.batallar(heroes, bestias);
}
});
NO respondo dudas por mensaje privado
Publicando vuestras dudas en el foro público conseguimos:
- Que más gente aporte respuestas mejores o complementarias.
- Que otras personas puedan aprender de vuestras dudas.

Mejor en PÚBLICO que en privado. Gracias

 

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".