Bien, a ver, he vuelto a cambiar las clases de Personajes
La superclase he quitado los setters, no son necesarios e incluso uno estaba mal. El setVida() recibía un String que lo seteaba al atributo nombre. Debería recibir un int para el atributo vida.
Pero en cualquier caso, no son necesarios los setters para este programa.
A cambio le añado un método llamado perderVida(), que recibe un int con la cantidad de daños sufridos en un turno, para que lo reste de la vida.
import java.util.Random;
public class Personaje {
protected String nombre;
protected int ataque;
protected int defensa;
protected int vida;
public Personaje() {
nombre = "Desconocido";
ataque = 0;
defensa = 0;
vida = 0;
}
public Personaje(String nombre, int ataque, int defensa, int vida){
this.nombre = nombre;
this.ataque = ataque;
this.defensa = defensa;
this.vida = vida;
}
public String getNombre (){
return this.nombre;
}
public int getAtaque (){
return this.ataque;
}
public int getDefensa (){
return this.defensa;
}
public int getVida (){
return this.vida;
}
public void perderVida(int danos) {
vida -= danos;
if (vida < 0) //Evitamos valores negativos en nivel de vida
vida = 0;
}
public int atacar() {
System.out.println(nombre + " ataca.");
Random azar = new Random();
//Ataque mínimo: 1, Maximo: según nivel de ataque
int puntosAtaque = azar.nextInt(ataque) + 1;
//Damos un 10% de posibilidad de obtener un "Ataque Crítico"
int valor = azar.nextInt(100);
if (valor%10 == 0) { //Comprobamos si es multiplo de 10
System.out.println("¡" + nombre + " consigue un Ataque Crítico!");
//Un "Ataque Crítico" duplica el daño del ataque
return puntosAtaque * 2;
}
else
return puntosAtaque;
}
public int defender() {
System.out.println(nombre + " se defiende.");
Random azar = new Random();
//Defensa mínima: 1, Maxima: según nivel de defensa
int puntosDefensa = azar.nextInt(defensa) + 1;
return puntosDefensa;
}
}
Caballero y Mago cambian muy ligeramente. Ahora son ellos mismos quienes muestran mensajes en pantalla para cada acción que realizan, mensajes tipo: "Caballero hace un ataque Relámpago"
Al principio había pensado lanzar esos mensajes desde el programa principal, pero mejor que cada método de estas clases se encargue de eso.
public class Caballero extends Personaje{
public Caballero() {
super();
}
public Caballero(String nombre, int ataque, int defensa, int vida) {
super(nombre, ataque, defensa, vida);
}
public int ataqueRelampago() {
/*
* Nivel de ataque máximo, incrementado un 50%
*/
System.out.println(nombre + " usa su ataque RELÁMPAGO");
return (int)(ataque * 1.5);
}
@Override
public String toString() {
return String.format("Caballero: %s / Nivel de Vida: %d", nombre, vida);
}
}
import java.util.Random;
public class Mago extends Personaje{
private final int VIDA_MAX; //Los hechizos de curación no pueden rebasar este límite
public Mago() {
super();
VIDA_MAX = vida; //Vida inicial es el límite máximo
}
public Mago(String nombre, int ataque, int defensa, int vida) {
super(nombre, ataque, defensa, vida);
VIDA_MAX = vida; //Vida inicial es el límite máximo
}
public boolean recuperarVida() {
/*
* 20% posibilidad de recuperar 1 punto de vida
* en cada turno de juego.
* Pero solo sí no se tiene ya la vida al máximo
*/
if (vida < VIDA_MAX) {
Random azar = new Random();
int valor = azar.nextInt(100);
if (valor%5 == 0) {//Comprobamos si es múltiplo de 5
System.out.println("¡" + nombre + " recupera 1 punto de vida!");
vida++;
return true;
}
}
return false;
}
public void curar50puntos() {
System.out.println(nombre + " usa su hechizo de SANACIÓN");
if (vida == VIDA_MAX)//No se puede recuperar vida porque ya está en su límite
System.out.println("El hechizo no tiene ningún efecto");
else {
/*
* Calculamos diferencia entre vica actual y vida maxima
* para saber si podemos recuperar 50 puntos o menos.
*/
int diferencia = VIDA_MAX - vida;
if (diferencia >= 50) {
vida += 50;
System.out.println("¡" + nombre + " recupera 50 puntos de vida!");
}
else {
vida += diferencia;
System.out.println("¡" + nombre + " recupera " + diferencia + " puntos de vida!");
}
}
}
public int hechizoAtaque() {
/*
* Nivel de ataque máximo, incrementado un 50%
*/
System.out.println(nombre + " usa su hechizo de ATAQUE");
return (int)(ataque * 1.5);
}
@Override
public String toString() {
return String.format("Mago: %s / Nivel de Vida: %d", nombre, vida);
}
}
Bien, sobre el programa principal,el main...
Habíamos hecho un método para poder crear un Personaje.
A partir de este Personaje, se autocrea el rival controlado por la CPU.
Ahora había que iniciar el motor del juego, un sistema de combate por turnos donde cada jugador elige una acción para actuar sobre el otro.
Esto básicamente se consigue con un bucle while(), que se repita mientras ambos jugadores sigan con vida.
//Comienza el combate...
while(jugador.getVida() > 0 && CPU.getVida() > 0) {
En cuanto uno de los dos muere (o ambos mueren, puede ocurrir...) el bucle while terminará.
Dentro de este bucle, el usuario ha de poder escoger una acción mediante un menú.
Este menú será algo distinto según si ha creado un Caballero, o un Mago, ya que tienen habilidades distintas.
Esto lo podemos hacer mediante este método. Muestra el menú adecuado según la clase de Jugador y retorna lo que elija el usuario:
private static int menuCombateJugador() {
System.out.println("\nTurno de " + jugador.getNombre());
//Opciones comunes a todos los jugadores
System.out.println("[1] -> Atacar");
System.out.println("[2] -> Defender");
//Opciones según la clase de Jugador
if (jugador instanceof Caballero) {
System.out.println("[3] -> Ataque RELÁMPAGO");
}
else {
System.out.println("[3] -> Hechizo de ATAQUE");
System.out.println("[4] -> Hechizo de SANACIÓN");
}
System.out.print("Elige acción: ");
return Integer.parseInt(teclado.nextLine());
}
Cuando nos retorne esta elección, desde el main la evaluaremos mediante un switch para invocar el método adecuado a lo que haya escogido:
atacar(), defender(), ataqueRelampago(), etc...
Estos métodos nos devuelve un int con una potencia de ataque o de defensa.
¿Y que hacemos con este int?
Se me ha ocurrido usar unas variables para recoger estos int.
Primero recogemos el ataque o defensa del jugador
Luego la CPU elige su acción y también recogeremos su ataque o defensa.
Y cuando lo tengamos, evaluamos daños.
El jugador recibe daños si el ataqueCPU supera a la defensaJugador
Y CPU recibe daños, si ataqueJugador supera defensaCPU.
Ojo, no me refiero al valor de sus atributos, si no a la potencia que los métodos atacar(), defender(), etc... consiguen de forma aleatoria, en base al valor de esos atributos.
Una vez hemos confrontado los ataques de uno con la defensa del otro (que será 0 si no eligió la acción defender() ) podemos mostrar en pantalla cuántos daños ha sufrido cada uno, y si ha muerto o no.
Bien, así que recapitulando, la mecánica es que el Jugador humano elige acción, y en base a su elección obtenemos unos valores de ataque y defensa.
Luego elige la CPU y así obtendremos sus valores de ataque y defensa.
Confrontamos estos valores y sabremos si alguien recibe daños, si alguien ha logrado bloquear por completo el ataque del otro y si alguien muere.
Y este proceso es lo que se repetirá dentro del bucle while.
Bien, hacer que elija el Jugador, es fácil, ya he puesto antes el método que hará eso.
Hacer que elija la CPU, también es fácil. Como la CPU siempre es un Caballero, solo tiene tres posibles acciones: atacar, defender, y relámpago.
Así que basta con que genera un número al azar entre 1 y 3.
Sin embargo, al llegar a este punto, se me ha ocurrido incluir una funcionalidad extra en el programa, un nivel de dificultad seleccionable.
El jugador podrá elegir entre modo normal o difícil. Este modo se controla mediante una variable boolean global
private static void elegirDificultad() {
System.out.println("\n\t\tNIVEL DIFICULTAD");
System.out.println("\n\t\t----- ----------");
System.out.println("[1] -> Modo NORMAL(Por defecto)");
System.out.println("[2] -> Modo DIFICIL(La CPU será más inteligente)");
System.out.print("Elige modo: ");
int modo = Integer.parseInt(teclado.nextLine());
//si teclea 2, pasa a modo dificil
if (modo == 2)
modoDificil = true;
//Si teclea 1 o cualquier otra cosa, no se cambia el modo
}
Normal, es que la CPU hace elecciones al azar, como hemos dicho. Es el modo por defecto.
Difícil, en cambio, consistiría en dotar a la CPU de cierta inteligencia, para depender menos del azar.
En este modo, la CPU analizaría los valores de ataque y defensa que ha conseguido previamente el jugador humano. Y según estos valores, dará más o menos probabilidades a las distintas acciones.
Por ejemplo, si el humano a elegido defender, es decir, no va a atacar en este turno, entonces la CPU no hay riesgo de recibir daños. Así que en este caso, la CPU nunca elegirá defenderse, se centrará en atacar. Y cuanto mayor sea la defensa que ha conseguido el jugador, más favorecerá un ataque Relámpago para intentar causar los mayores daños.
En cambio, si la CPU detecta que el jugador Humano está atacando, y ha conseguido un ataque potente... entonces dará mayor probabilidades a la acción defender, para protegerse de este ataque.
Así pues, el programa tendrá dos métodos para que la CPU escoja acción.
Este sería para el modo normal, simplemente, un número al azar:
private static int CPUmodoNormal() {
/*
* En el modo normal las acciones de la CPU
* se eligen completamente por azar.
*/
Random azar = new Random();
//Al ser un Caballero, solo tiene 3 posibles acciones, entre 1 y 3
//Si retorna 1, atacará
//Si es 2, se defenderá
//3, ¡¡Ataque Relámpago!!
return azar.nextInt(3) + 1;
}
Y a continuación el método para el modo difícil, donde la CPU estará dotada con una sencilla IA, para ser más estratégica en base a las acciones del jugador humano.
private static int CPUmodoDificil(int ataqueJugador, int defensaJugador) {
/*
* En el modo difícil, las acciones van a depender menos del azar.
* La CPU evaluará si el jugador ataca o se defiende,
* y con que intensidad, para favorecer más unas acciones u otras.
*/
int[] arrayAcciones = new int[10];
/*
* Las acciones de la CPU se escogerán al azar desde un array de int.
* Este array tendrá varios valores entre 1 y 3, que son las posibles acciones a escoger.
* (1 = ataque; 2 = defender; 3 = ataque Relámpago)
*
* Este array se fabricará según el analisis de la jugada del Jugador Humano.
* Si por ejemplo el humano está defendiendo, el array no tendrá valor 2(defensa)
* porque la CPU no querrá defenderse, querrá atacar.
* En cambio, si detecta que el humano ha conseguido un ataque muy fuerte, si habrá más
* cantidad de valores 2 en el array, para favorecer que la CPU se defienda en lugar de atacar
*/
//Evaluamos acciones del Jugador
if (defensaJugador > 0) {
/*
* Jugador se está defendiendo, así que la CPU
* NO se va a defender ya que sería desperdiciar este turno.
*
* Atacará al jugador, y además favorecerá un "Ataque Relámpago"
* si detecta que la defensa del Jugador es muy alta.
*/
if (defensaJugador >= 9) //Defensa muy alta, mayor probabilidad de ataque relámpago
arrayAcciones = new int[]{1, 3, 3, 1, 3, 3, 1, 3, 3, 3};
else if (defensaJugador >= 5) //Defensa media, ataque relámpago un poco más probable
arrayAcciones = new int[]{1, 3, 1, 1, 3, 3, 1, 3, 3, 1};
else //Defensa baja, menos probabilidad de ataque Relámpago
arrayAcciones = new int[]{1, 3, 1, 1, 3, 1, 1, 3, 1, 1};
}
else { //Jugador no se defiende, está atacando o usando hechizo curación (si es un Mago)
//Según la fuerza de ataque, se favorecerá más o menos la defensa.
if (ataqueJugador >= 9) //Ataque fuerte, mucha defensa
arrayAcciones = new int[]{1, 2, 2, 3, 2, 2, 3, 2, 2, 3};
else if (ataqueJugador >= 5) //Ataque medio
arrayAcciones = new int[]{1, 1, 2, 3, 2, 2, 1, 2, 2, 3};
else if (ataqueJugador > 0) //Ataque bajo
arrayAcciones = new int[]{1, 1, 2, 3, 2, 3, 1, 1, 2, 3};
else if (ataqueJugador == 0) //Ataque es 0,.. ¡¡y defensa también es 0!!
//Esto solo ocurre cuando el jugador es un Mago usando hechizo de sanacion
//En este caso, CPU no se defiende, y además se favorece el ataque Relámpago
arrayAcciones = new int[]{1, 3, 3, 1, 3, 3, 1, 3, 3, 3};
}
/*
* Ya se ha conformado un array de acciones acorde
* según el análisis de la jugada del humano.
* Ahora retornaremos una accion al azar de este array
*/
Random azar = new Random();
return arrayAcciones[azar.nextInt(10)];
}
Por supuesto este añadido es una idea mía. Si no quieres implementarlo en tu programa, basta con quedarse solo con el modo normal.
(Sigue a continuación..)