Autor Tema: Problema Batalla RPG por turnos con java  (Leído 291 veces)

MirMiriam

  • Sin experiencia
  • *
  • APR2.COM
  • Mensajes: 3
    • Ver Perfil
Problema Batalla RPG por turnos con java
« en: 27 de Agosto 2021, 21:01 »
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!

Kabuto

  • Moderador Global
  • Experto
  • *******
  • Mensajes: 724
    • 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: 724
    • 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: 3
    • 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: 724
    • 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: 3
    • 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

 

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".