Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 ... 21 22 23 24 25 [26] 27 28 29 30 31 ... 50
501
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 21 de Febrero 2021, 01:04 »
El siguiente paso sería crea el PanelEventos, con los botones para crear evento, cancelar, modificar, visualizar y volver al menú principal.
Será prácticamente igual que PanelUsuarios, pero con 5 botones en lugar de 4.



De momento, solo funcionará el botón para regresar al menú principal:

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

private JButton btNuevo;
private JButton btCancelar;
private JButton btEditar;
private JButton btVisualizar;
private JButton btVolver;

public PanelEventos() {
iniciarBotones();

setLayout(new BorderLayout());
add(new PanelTitulo(), BorderLayout.NORTH);

JPanel pnCentro = new JPanel();
pnCentro.setLayout(new BoxLayout(pnCentro, BoxLayout.Y_AXIS));
//Tres botones en la fila superior
JPanel filaSup = new JPanel();
filaSup.setLayout(new GridLayout(1, 3, 10, 10));
filaSup.add(new PanelBoton(btNuevo, "Crear Evento"));
filaSup.add(new PanelBoton(btCancelar, "Cancelar Evento"));
filaSup.add(new PanelBoton(btEditar, "Editar EVento"));
//Dos botones en la fila inferior
JPanel filaInf = new JPanel();
filaInf.setLayout(new GridLayout(1, 2, 10, 10));
filaInf.add(new PanelBoton(btVisualizar, "Visualizar Evento"));
filaInf.add(new PanelBoton(btVolver, "Volver a menú principal"));
pnCentro.add(filaSup);
pnCentro.add(filaInf);
pnCentro.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
add(pnCentro, BorderLayout.CENTER);

}


private void iniciarBotones() {
btNuevo = new JButton();
btNuevo.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/crearTicket.png")));
btNuevo.setFocusPainted(false);

btCancelar = new JButton();
btCancelar.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/cancelarTicket.png")));
btCancelar.setFocusPainted(false);

btEditar = new JButton();
btEditar.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/modificarTicket.png")));
btEditar.setFocusPainted(false);

btVisualizar = new JButton();
btVisualizar.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/visualizarTicket.png")));
btVisualizar.setFocusPainted(false);

btVolver = new JButton();
btVolver.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/volver.png")));
}

public void setAccionVolver(ActionListener accion) {
btVolver.addActionListener(accion);
}

private class PanelTitulo extends JPanel {

public PanelTitulo() {
JLabel titulo = new JLabel("Administrar Eventos");
titulo.setFont(new Font("Verdana", Font.ITALIC, 32));
JPanel pnBienvenido = new JPanel();
pnBienvenido.add(titulo);
pnBienvenido.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(pnBienvenido);
}
}


private class PanelBoton extends JPanel {

public PanelBoton(JButton boton, String texto) {
setLayout(new BorderLayout());
add(boton, BorderLayout.CENTER);
JLabel etTexto = new JLabel(texto);
etTexto.setFont(new Font("Verdana", Font.BOLD ,18));
JPanel pnTexto = new JPanel();
pnTexto.add(etTexto);
add(pnTexto, BorderLayout.SOUTH);
setBorder(
BorderFactory.createCompoundBorder(BorderFactory.createRaisedSoftBevelBorder(),
BorderFactory.createEmptyBorder(15, 15, 15, 15)));
}
}
}

Para poder visualizar este nuevo panel en la interfaz, primero nos vamos a la clase PanelPrincipal y le añadimos un método para poder dotar de un ActionListener al botón "Gestionar Eventos"
Código: [Seleccionar]
public void setAccionGestionEventos(ActionListener accion) {
btEventos.addActionListener(accion);
}

Esta acción que recibirá, ha de ser escrita en la clase principal JavaTicket

Código: [Seleccionar]
private class AccionGestionarEventos implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(panelActivo, "evento");
}
}
Esta acción consiste en pedir al cardLayout, que en el "panel activo" muestre el panel de eventos, que está etiquetado como "evento".
Pero claro, para que esto funcione, primero hay que declarar este panel en la zona de atributos de esta clase:
Citar
public final class JavaTicket extends JFrame{
   
   //Partes del MODELO
   private GestorUsuarios gestorUsuarios;
   private Usuario usuarioLogueado;
   
   //Partes de la VISTA
   private DialogoLogin dialogoLogin;
   private PanelPrincipal pnPrincipal;
   private PanelEventos pnEventos;
   private PanelUsuarios pnUsuarios;
   private CardLayout cardLayout;
   private JPanel panelActivo;

Inicializar este panel, en el método iniciarVista() y de paso hacerle llegar el único ActionListener del que disponemos para él, el de volver al menú principal.
Que ya lo tenemos escrito porque es el mismo que el del botón "volver" del panel de gestión de usuarios.

Además lo agregamos al cardLayout del "panelActivo", etiquetándolo como "evento" para identificarlo

Citar
   private void iniciarVista() {
      //composicion panel principal
      pnPrincipal = new PanelPrincipal();
      pnPrincipal.setAccionGestionEventos(new AccionGestionarEventos());
      pnPrincipal.setAccionGestionUsuario(new AccionGestionarUsuarios());
      //Composicion panel eventos
      pnEventos = new PanelEventos();
      pnEventos.setAccionVolver(new AccionVolver());

      //Composicion panel usuarios
      pnUsuarios = new PanelUsuarios();
      pnUsuarios.setAccionNuevo(new AccionNuevo());
      pnUsuarios.setAccionModificar(new AccionModificar());
      pnUsuarios.setAccionEliminar(new AccionBorrar());
      pnUsuarios.setAccionVolver(new AccionVolver());
      cardLayout = new CardLayout();
      panelActivo = new JPanel();
      panelActivo.setLayout(cardLayout);
      panelActivo.add(pnPrincipal, "principal");
      panelActivo.add(pnEventos, "evento");
      panelActivo.add(pnUsuarios, "usuarios");
      dialogoLogin = new DialogoLogin(this, true, gestorUsuarios);
      dialogoLogin.setAccionEnviar(new AccionEnviarLogin());
      dialogoLogin.setVisible(true);
   }


Bien, ya tenemos panel para gestionar eventos. Ahora necesitamos un Gestor de Eventos, una clase que será casi idéntica a la clase GestionUsuarios.
Un ArrayList para añadir Eventos y poder guardarlos en disco para luego recuperarlos.
Código: [Seleccionar]
public final class GestorEventos {

private final File FICHERO_EVENTOS = new File("C:/JavaTicket/eventos.dat");
private ArrayList<Evento> eventos;

public GestorEventos() {
cargarEventos();
}

@SuppressWarnings("unchecked")
private void cargarEventos() {
if (FICHERO_EVENTOS.exists()) {
ObjectInputStream ois;
try {
ois = new ObjectInputStream(new FileInputStream(FICHERO_EVENTOS));
Object aux = ois.readObject();
if (aux instanceof ArrayList) {
eventos = (ArrayList<Evento>) aux;
}
else {
JOptionPane.showMessageDialog(null, "No se reconocen los tipos de dato del fichero: "
+ FICHERO_EVENTOS.getPath(), "Leer eventos", JOptionPane.WARNING_MESSAGE);
eventos = new ArrayList<Evento>();
}
ois.close();
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "No se encuentra fichero de Eventos en ruta: "
+ FICHERO_EVENTOS.getPath(), "Leer eventos", JOptionPane.WARNING_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "No se puede acceder a fichero de Eventos en ruta: "
+ FICHERO_EVENTOS.getPath(), "Leer eventos", JOptionPane.WARNING_MESSAGE);
} catch (ClassNotFoundException e) {
JOptionPane.showMessageDialog(null, "Objeto de clase desconocida en fichero: "
+ FICHERO_EVENTOS.getPath(), "Leer eventos", JOptionPane.WARNING_MESSAGE);
}
}
else
eventos = new ArrayList<Evento>();
}

public void guardarUsuarios() {

try {
if (!FICHERO_EVENTOS.exists()) {
File carpeta = new File("C:/JavaTicket");
carpeta.mkdirs();

FICHERO_EVENTOS.createNewFile();
}

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FICHERO_EVENTOS));
oos.writeObject(eventos); //Guardamos el ArrayList
oos.close();
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "No se encuentra fichero de Eventos en ruta: "
+ FICHERO_EVENTOS.getPath(), "Guardar Eventos", JOptionPane.WARNING_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "No se puede acceder a fichero de Eventos en ruta: "
+ FICHERO_EVENTOS.getPath(), "Guardar Eventos", JOptionPane.WARNING_MESSAGE);
}
}

public boolean agregarEvento(Evento evento) {
if (eventos.contains(evento)) {
JOptionPane.showMessageDialog(null, "Este Evento ya está registrado",
"Registrar nuevo Evento", JOptionPane.WARNING_MESSAGE);
return false;
}
else
return eventos.add(evento);
}

}

No le he añadido un método para eliminar eventos, porque leyendo el enunciado (cada vez que lo leo descubro algo nuevo) resulta que los eventos no se borran, SE CANCELAN.
Es decir, un evento que no se va a producir, se marca como cancelado, pero ha de seguir existiendo en el ArrayList.

Esto me ha hecho cambiar, otra vez más, la clase Evento a quién le he añadido un boolean para poder marcar como cancelado un evento.
Además del atributo, dos métodos para interactuar con él.

Y no solo eso, puesto que vamos a guardarlos en disco, ha de implementarse la interfaz Serializable

Citar
public abstract class Evento implements Serializable{

   private String codigo;
   private String titulo;
   private String descripcion;
   private LocalDate fecha;
   protected double renta; //Protected porque algunas clases hijas necesitan verla para calcular seguro
   private boolean cancelado;
   
   public Evento(String codigo, String titulo, String descripcion, LocalDate fecha, double renta) {
      this.codigo = codigo;
      this.titulo = titulo;
      this.descripcion = descripcion;
      this.fecha = fecha;
      this.renta = renta;
      cancelado = false;
   }

   public String getCodigo() {
      return codigo;
   }

   public void setCodigo(String codigo) {
      this.codigo = codigo;
   }

   public String getTitulo() {
      return titulo;
   }

   public void setTitulo(String titulo) {
      this.titulo = titulo;
   }

   public String getDescripcion() {
      return descripcion;
   }

   public void setDescripcion(String descripcion) {
      this.descripcion = descripcion;
   }

   public LocalDate getFecha() {
      return fecha;
   }

   public void setFecha(LocalDate fecha) {
      this.fecha = fecha;
   }

   public double getRenta() {
      return renta;
   }

   public void setRenta(double renta) {
      this.renta = renta;
   }
   
   public boolean estaCancelado() {
      return cancelado;
   }

   
   public void setCancelar(boolean cancelar) {
      cancelado = cancelar;
   }

   
   @Override
   public boolean equals(Object obj) {
      
      if (obj instanceof Evento) {
         Evento otroEvento = (Evento)obj;
         return otroEvento.codigo.equals(codigo);
      }
      else
         return false;
   }
   
}

Todos estos cambios están en Google Drive

502
Hola.
Tienes que enfocarlo de forma distinta.

Todo el proceso de pedir pesos, convertir, etc... se ha de realizar mientras se cumpla una determinada condición, para que el usuario pueda hacer todas las transacciones que quiera.

Por ejemplo, podemos usar un bucle while que se repita mientras un char tenga valor 's'.
Al final de cada transacción preguntamos al usuario si quiere hacer otra.
Si responde 's', el proceso se repite. Si responde 'n', entonces el while termina y se mostraría en pantalla el reporte de totales acumulados.

Luego, otro fallo que tienes es que usas "total" tanto para acumular dolares como pesos

Código: [Seleccionar]
  totalpesos=totalpesos+total;
  totaldolares=totaldolares+total;
Eso va a implicar que ambos acumuladores tendrán el mismo valor al final del programa

En vez de una variable llamada "total", mejor usar una llamada "dolares", ya que lo que recibe es la conversión en dolares.
Así, a totalPesos le acumularemos la variable "pesos", que es el valor introducido por teclado.

Y a totalDolares le acumulamos la variable "dolares", que es el valor de conversión.

Prueba este código, a ver si ahora lo ves más claro:

Código: [Seleccionar]
#include<stdio.h>
#include<stdlib.h>

#define dolar 19.94

int main()
{
    int transacciones = 0;
    float totalPesos = 0, totalDolares = 0;
    float dolares = 0, pesos = 0;
    char continuar = 's';

    while (continuar == 's')
    {
        printf("\n\n\t * * Calculadora de casa de cambio * * \n");

        printf("\n\nIngresa la cantidad total de pesos que deseas cambiar: $");
        scanf("%f", &pesos);
        if (pesos > 0)
        {
            //Hacemos la conversion e informamos en pantalla
            dolares = pesos/dolar;
            printf("Dolares obtenidos al cambio: %.2f$", dolares);
            //Acumulamos pesos ingresados
            totalPesos = totalPesos + pesos;
            //Acumulamos dolares entregados
            totalDolares = totalDolares + dolares;
            //Contamos esta transaccion
            transacciones++;

            //Preguntamos si quiere continuar
            printf("\nHacer nueva transaccion?(s/n): ");
            scanf(" %c", &continuar);
        }
        else
            printf("\n\nIngresa un valor valido\n\n");
    }
    //Final del día, presentamos reporte
    system("cls");
    printf("\t * * Resumen de operaciones * * \n");
    printf("Transacciones realizadas: %d\n", transacciones);
    printf("Pesos ingresados: %.2f\n", totalPesos);
    printf("Dolares entregados: %.2f\n", totalDolares);

    printf("\n\t\tFIN DE PROGRAMA\n");
    system("pause");

    return 0;
}

503
Comunidad / Re: Presentación
« en: 20 de Febrero 2021, 11:22 »
Bienvenido.
Siéntete libre para participar, preguntar o responder en el foro.
Estás en tu casa  ;)

504
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 20 de Febrero 2021, 01:39 »
OK.
Me estoy mirando la parte de gestionar eventos y me he dado cuenta de que el enunciado incorpora unos atributos extra para las clases de eventos que no había visto antes.
Atributos que sus valores serían establecidos desde el apartado "modificar evento".

Lo eventos Religiosos pueden anotarse cuántos convertidos han conseguido tras dicho evento.
Lo lógico sería pues que solo una vez el evento haya tenido lugar, según su fecha, se permitiera apuntar estos convertidos.
Pero no se especifica nada sobre esto, así que no nos vamos a complicar con eso.
De momento, a su clase le añado este atributo y sus getter/setter

Citar
public final class Religioso extends Evento{

   //Constante que establece el limite de personas que pueden asistir a este evento
   private final int AFORO_MAXIMO = 30000;
   //Constante que establece el importe fijado como seguro por desgaste de la grama
   public final int CUOTA_SEGURO = 2000; //Public, no será necesario usar getter para acceder a este valor
   //Atributos
   private int aforo;
   //Atributo extra
   private int convertidos;

   public Religioso(String codigo, String titulo, String descripcion, String fecha, double renta,
         int aforo) {
      super(codigo, titulo, descripcion, fecha, renta);
      setAforo(aforo);
   }
   
   public int getAforo() {
      return aforo;
   }

   /**
    * Establece la cantidad de gente que asistirá al evento.<br>
    * Esta cantidad está limitada por la constante <i>AFORO_MAXIMO.</i>
    * @param aforo Cantidad de gente que asistirá al evento.
    */
   public void setAforo(int aforo) {
      if (aforo > AFORO_MAXIMO) {
         JOptionPane.showMessageDialog(null, "El aforo indicado supera el límite establecido."
               + "El aforo será establecido a " + AFORO_MAXIMO, "Crear Evento Derportivo",
               JOptionPane.WARNING_MESSAGE);
         this.aforo = AFORO_MAXIMO;
      }
      else
         this.aforo = aforo;
   }
   
   public void setConvertidos(int cantidad) {
      convertidos = cantidad;
   }
   
   public int getConvertidos() {
      return convertidos;
   }


}

Los Musicales se puede añadir los nombres del equipo de montaje a una lista.
Aún no he decidido como introducirá el usuario estos nombres. Quizás en un area de texto, con los nombres separados por ;  o algo así.
Luego podemos coger ese texto, hacer divisiones donde haya un ; para separar los nombres, componer un array con ellos y hacérselo llegar al ArrayList.
Ya veremos, de momento para su setter, cuento con hacerle llegar un array de nombres.
Para su getter, en cambio, construiríamos un String con los nombres separado por ; , similar a lo que el usuario hubiera escrito para introducirlos.

Citar
public final class Musical extends Evento{

   //Constante que establece el limite de personas que pueden asistir a este evento
   private final int AFORO_MAXIMO = 25000;
   //Constante que establece el % de cuota de seguro que se cobra sobre la renta acordada
   private final double CUOTA_SEGURO = 30d; //30%
   //Atributos
   private int aforo;
   private TipoMusica tipoMusica;
   //Atributo extra
   private ArrayList<String> staff; //Nombres de los miembros del equipo de montaje

   public Musical(String codigo, String titulo, String descripcion, String fecha, double renta,
         int aforo, TipoMusica tipoMusica) {
      super(codigo, titulo, descripcion, fecha, renta);
      setAforo(aforo);
      this.tipoMusica = tipoMusica;
      staff = new ArrayList<String>();
   }
   
   public int getAforo() {
      return aforo;
   }

   /**
    * Establece la cantidad de gente que asistirá al evento.<br>
    * Esta cantidad está limitada por la constante <i>AFORO_MAXIMO.</i>
    * @param aforo Cantidad de gente que asistirá al evento.
    */
   public void setAforo(int aforo) {
      if (aforo > AFORO_MAXIMO) {
         JOptionPane.showMessageDialog(null, "El aforo indicado supera el límite establecido."
               + "El aforo será establecido a " + AFORO_MAXIMO, "Crear Evento Derportivo",
               JOptionPane.WARNING_MESSAGE);
         this.aforo = AFORO_MAXIMO;
      }
      else
         this.aforo = aforo;
   }

   public TipoMusica getTipoMusica() {
      return tipoMusica;
   }

   public void setTipoMusica(TipoMusica tipoMusica) {
      this.tipoMusica = tipoMusica;
   }

   /**
    * Establece el importe del seguro que se cobra
    * por el uso de la grama.<br>Este importe es el
    * 30% del valor de la renta acordada.
    * @return Importe del seguro.
    */
   public double getImporteSeguro() {
      return  renta * CUOTA_SEGURO / 100;
   }
   
   public void setStaff(String[] lista) {
      for (String nombre: lista)
         staff.add(nombre);
   }

   
   public String getStaff() {
      StringBuilder lista = new StringBuilder();
      for (String nombre: staff)
         lista.append(nombre + ";\n");
      
      return lista.toString();
   }

   
}


En los Deportivos tenemos algo similar, listas de nombres con los jugadores de los equipos.
Citar
public final class Deportivo extends Evento{

   //Constante que establece el limite de personas que pueden asistir a este evento
   private final int AFORO_MAXIMO = 20000;
   //Atributos
   private int aforo;
   private String equipo1;
   private String equipo2;
   private TipoDeporte tipoDeporte;
   //Atributos extra
   private ArrayList<String> jugadores1;
   private ArrayList<String> jugadores2;


   public Deportivo(String codigo, String titulo, String descripcion, String fecha, double renta,
         int aforo, String equipo1, String equipo2, TipoDeporte tipoDeporte) {
      super(codigo, titulo, descripcion, fecha, renta);

      setAforo(aforo);
      this.equipo1 = equipo1;
      this.equipo2 = equipo2;
      this.tipoDeporte = tipoDeporte;
      jugadores1 = new ArrayList<String>();
      jugadores2 = new ArrayList<String>();

   }

   public int getAforo() {
      return aforo;
   }

   /**
    * Establece la cantidad de gente que asistirá al evento.<br>
    * Esta cantidad está limitada por la constante <i>AFORO_MAXIMO.</i>
    * @param aforo Cantidad de gente que asistirá al evento.
    */
   public void setAforo(int aforo) {
      if (aforo > AFORO_MAXIMO) {
         JOptionPane.showMessageDialog(null, "El aforo indicado supera el límite establecido."
               + "El aforo será establecido a " + AFORO_MAXIMO, "Crear Evento Derportivo",
               JOptionPane.WARNING_MESSAGE);
         this.aforo = AFORO_MAXIMO;
      }
      else
         this.aforo = aforo;
   }

   public String getEquipo1() {
      return equipo1;
   }

   public void setEquipo1(String equipo1) {
      this.equipo1 = equipo1;
   }

   public String getEquipo2() {
      return equipo2;
   }

   public void setEquipo2(String equipo2) {
      this.equipo2 = equipo2;
   }

   public TipoDeporte getTipoDeporte() {
      return tipoDeporte;
   }

   public void setTipoDeporte(TipoDeporte tipoDeporte) {
      this.tipoDeporte = tipoDeporte;
   }
   
   public void setJugadores1(String[] lista) {
      for (String nombre: lista)
         jugadores1.add(nombre);
   }
   
   public void setJugadores2(String[] lista) {
      for (String nombre: lista)
         jugadores2.add(nombre);
   }

   
   public String getJugadores1() {
      StringBuilder lista = new StringBuilder();
      for (String nombre: jugadores1)
         lista.append(nombre);
      
      return lista.toString();
   }

   
   public String getJugadores2() {
      StringBuilder lista = new StringBuilder();
      for (String nombre: jugadores2)
         lista.append(nombre);
      
      return lista.toString();
   }


}


También he visto que habrá que operar con las fechas de los eventos.
Este atributo, que está en la clase padre, lo puse como String por sencillez.
Pero no sabía que se iba a tener que operar con ellas, así pues, puede que nos convenga más usar la clase LocalDate.
Puede que luego vuelva a cambiar de opinión, pero seguramente cuando haya que escribir código para comparar fechas, será mejor contar con la clase LocalDate

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

private String codigo;
private String titulo;
private String descripcion;
private [b]LocalDate[/b] fecha;
protected double renta; //Protected porque algunas clases hijas necesitan verla para calcular seguro

public Evento(String codigo, String titulo, String descripcion, [b]LocalDate[/b] fecha, double renta) {
this.codigo = codigo;
this.titulo = titulo;
this.descripcion = descripcion;
this.fecha = fecha;
this.renta = renta;
}

public String getCodigo() {
return codigo;
}

public void setCodigo(String codigo) {
this.codigo = codigo;
}

public String getTitulo() {
return titulo;
}

public void setTitulo(String titulo) {
this.titulo = titulo;
}

public String getDescripcion() {
return descripcion;
}

public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}

public [b]LocalDate[/b] getFecha() {
return fecha;
}

public void setFecha([b]LocalDate[/b] fecha) {
this.fecha = fecha;
}

public double getRenta() {
return renta;
}

public void setRenta(double renta) {
this.renta = renta;
}

@Override
public boolean equals(Object obj) {

if (obj instanceof Evento) {
Evento otroEvento = (Evento)obj;
return otroEvento.codigo.equals(codigo);
}
else
return false;
}

}

Y se me acaba el tiempo... a ver si mañana podemos avanzar más.

505
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 20 de Febrero 2021, 00:58 »
Volviendo a la clase DialogoModificarUsuario
En su constructor vemos que gracias al boolean modoBorrar:
Citar
public DialogoModificarUsuario(Frame padre, boolean modal, boolean modoBorrar, GestorUsuarios gestor)

Cambia la construcción del dialogo para adaptarse a una funcionalidad o a otra.
Si el valor de ese boolean es TRUE, entonces estamos en modo borrar, y por ejemplo los campos con los datos queremos que NO se puedan editar.
Así que a sus métodos setEditable() les decimos FALSE. O sea, lo contrario de este boolean. Por lo tanto, nos basta con "negar" este boolean y pasárselo como argumento.
Citar
campoNombre.setEditable(!modoBorrar); //En modo Borrar, no se pueden editar datos
      campoUser = new JTextField(10);
      campoUser.setFont(fuentePlana);
      campoUser.setForeground(Color.BLUE);
      campoUser.setEditable(!modoBorrar);

Así, cuando modoBorrar sea TRUE, poder editar campos será FALSE. Y cuando modoBorrar sea FALSE, poder editar campos será TRUE.

El botón de acción también cambia de nombre, y de acción, según el valor de este boolean

Citar
btAccion = new JButton(modoBorrar?"Borrar Usuario":"Modificar Usuario");
      btAccion.setFont(fuenteNegrita);
      btAccion.setForeground(Color.BLUE);
      if (modoBorrar)
         btAccion.addActionListener(new AccionBorrar());
      else
         btAccion.addActionListener(new AccionModificar());


Sobre las acciones de esta clase.
Tenemos tres acciones, una es para el combobox de Usuarios. Las otras dos son para el botón de acción, pero escogerá una u otra según si estamos en modo borrar o no.

La acción del combobox se ejecuta cada vez que elegimos uno de los items que contiene. Lo que hace es coger el usuario seleccionado y pasar una referencia a un atributo llamado "escogido"

Citar
   //Usuario actualmente seleccionado
   private Usuario escogido;
Este atributo apunta al Usuario seleccionado en el combobox, para saber que usuario ha de mostrar sus atributos en pantalla, o ha de ser modificado, o ha de ser borrado.

Así, esta acción del combobox.
Código: [Seleccionar]
private class AccionEscoger implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
mostrarEscogido();
}
}

Invoca este método para seleccionar al "escogido" y mostrar sus datos.

Código: [Seleccionar]
private void mostrarEscogido() {
escogido = (Usuario) comboUsuarios.getSelectedItem();

campoNombre.setText(escogido.getNombre());
campoUser.setText(escogido.getUserName());
campoPassword1.setText(escogido.getPassword());
campoPassword2.setText(escogido.getPassword());
campoEdad.setValue(escogido.getEdad());
if (escogido instanceof Administrador)
campoTipoUsuario.setSelectedIndex(0);
else if (escogido instanceof DeContenidos)
campoTipoUsuario.setSelectedIndex(1);
else
campoTipoUsuario.setSelectedIndex(2);
}

Luego, el botón de acción.
Para modificar se ejecuta este código, que comprueba que los campos tengan datos, que las contraseñas coincidan y si todo está bien, se modifican los atributos del usuario escogido.

Código: [Seleccionar]
private class AccionModificar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (compruebaCampos()) {
String nombreComp = campoNombre.getText();
String nombreUser = campoUser.getText();
String password = String.valueOf(campoPassword1.getPassword());
String password2 = String.valueOf(campoPassword2.getPassword());
int edad = (int) campoEdad.getValue();
if (password.equals(password2)) {
escogido.setNombre(nombreComp);
escogido.setUserName(nombreUser);
escogido.setEdad(edad);
escogido.setPassword(password);
gestorUsuarios.guardarUsuarios();
JOptionPane.showMessageDialog(null, "Usuario modificado",
"Modificar Usuario", JOptionPane.INFORMATION_MESSAGE);
}
else
advertencia("Las contraseñas no coinciden");
}
}
}

Y luego, esta acción, para cuando estamos en "modo Borrar"
Código: [Seleccionar]
private class AccionBorrar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int respuesta = JOptionPane.showConfirmDialog(null,
"¿Seguro que quiere borrar este Usuario",
"Borrar Usuarios", JOptionPane.YES_NO_OPTION);
if (respuesta == JOptionPane.YES_OPTION) {
//Eliminamos del Gestor de usuarios
gestorUsuarios.borrarUsuario(escogido);
gestorUsuarios.guardarUsuarios();
//Eliminamos del combobox
comboUsuarios.removeItem(escogido);
}
}
}

Por último, para que poder usar este JDialog.
En la clase principal escribimos estas dos acciones, donde ambas llaman a este JDialog pero con un valor distinto para el boolean "modoBorrar"

Código: [Seleccionar]
private class AccionModificar implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
new DialogoModificarUsuario(null, true, false, gestorUsuarios);
}
}

private class AccionBorrar implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
new DialogoModificarUsuario(null, true, true, gestorUsuarios);
}
}
Estas acciones, las agregamos al panel de usuarios:
Código: [Seleccionar]
private void iniciarVista() {
//composicion panel principal
pnPrincipal = new PanelPrincipal();
pnPrincipal.setAccionGestionUsuario(new AccionGestionarUsuarios());
//Composicion panel usuarios
pnUsuarios = new PanelUsuarios();
pnUsuarios.setAccionNuevo(new AccionNuevo());
[b]pnUsuarios.setAccionModificar(new AccionModificar());
pnUsuarios.setAccionEliminar(new AccionBorrar());[/b]
pnUsuarios.setAccionVolver(new AccionVolver());
cardLayout = new CardLayout();
panelActivo = new JPanel();
panelActivo.setLayout(cardLayout);
panelActivo.add(pnPrincipal, "principal");
panelActivo.add(pnUsuarios, "usuarios");
dialogoLogin = new DialogoLogin(this, true, gestorUsuarios);
dialogoLogin.setAccionEnviar(new AccionEnviarLogin());
dialogoLogin.setVisible(true);
}

Para poder hacer esta agregación, a la clase PanelUsuarios hay que escribirle estos dos métodos:
Código: [Seleccionar]
public void setAccionModificar(ActionListener accion) {
btModificar.addActionListener(accion);
}

public void setAccionEliminar(ActionListener accion) {
btEliminar.addActionListener(accion);
}

Y con esto, la parte de la gestión de Usuarios creo que quedaría completa.

Enlace a Google Drive

506
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 20 de Febrero 2021, 00:58 »
Lo de guardar en disco, es porque para poder poner a prueba el programa, sería terrible que cada vez que iniciamos el programa, tuviéramos que registrar nuevos usuarios y registrar nuevos eventos, para comprobar si lo último que hemos añadido funciona bien o no.

Así que, o guardamos en disco los usuarios y eventos que iremos generando...
O bien, generamos por código una serie de usuarios y eventos.

En mi código ya has visto que si no hay datos guardados en disco, genero automáticamente un usuario Administrador.
Pues igual que genero uno, podemos generar mediante código 10, 20.. los que queramos.

Si crees que por añadir lo de guardar en disco te lo van a cuestionar por ser algo que aún no te han explicado, puedes optar por lo de generar usuarios mediante código.

Si mantienes lo de guardar en disco, asegúrate de que entiendes bien como funciona, por si te lo preguntan, pues que puedas explicarlo.

Tu pregunta por aquí cualquier cosa que no entiendas. Todo lo que voy añadiendo intento explicarlo paso a paso, pero puede que algo requiera explicaciones extra.

Dicho esto, he añadido nuevas funcionalidades.
Ya podemos modificar y borrar usuarios. Para ello usamos un nueva clase JDialog, la cuál para ahorrar tiempo y código, la he escrito de forma que nos va a servir para ambas funcionalidades.
En su constructor incluyo un boolean, y según su valor, el JDialog se construye con la funcionalidad de modificar o con la de borrar.

Para ambas cosas nos va a servir el mismo formulario.
El formulario consiste en un ComboBox para seleccionar un Usuario de los que hay registrados en el Gestor de Usuarios.
Debajo de este combobox tenemos los campos que muestran los atributos del usuario que hayamos seleccionado.
Y debajo un botón de acción

Si estamos en "modo modificar", los campos de texto son editables, excepto el selector de tipo de Usuario.
Para simplificarlo, no vamos a permitir cambiar la clase de un Usuario. Se podría hacer, pero implica eliminar el usuario anterior, crearlo de nuevo bajo la nueva clase seleccionada, transferirles los id's de eventos que tuviera asociados....
No vale la pena complicarse con esto.



Cuando pulsamos el botón en "modo modificar", se recuperan los valores de los campos de texto, se setean en el usuario seleccionado y se le pide al gestor que guarde los cambios en disco.


Cuando estamos en "modo borrar", que recuerdo vamos a usar un boolean para elegir un modo u otro, los campos no son editables. Se muestran los datos del usuario pero no se pueden cambiar.
El botón de acción lo que hará será pedir confirmación antes de borrar, y si se confirma, se elimina el usuario del combobox, luego se elimina del Gestor de Usuarios y se guarda en disco el cambio realizado.



Ambas funcionalidades, la conseguimos con una única clase, que se construirá de un modo u otro según el valor del boolean llamado "modoBorrar" que se recibe por el constructor.

Código: [Seleccionar]
public class DialogoModificarUsuario extends JDialog{

private boolean modoBorrar; //Indica si este dialogo ha de ser para borrar usuario, y no modificar
private GestorUsuarios gestorUsuarios;
private Font fuentePlana = new Font("Verdana", Font.PLAIN, 20);
private Font fuenteNegrita = new Font("Verdana", Font.PLAIN, 20);
private JTextField campoNombre;
private JTextField campoUser;
private JPasswordField campoPassword1;
private JPasswordField campoPassword2;
private JSpinner campoEdad;
private JComboBox<String> campoTipoUsuario;
private JButton btAccion; //Su accion será distinta según si el dialogo es para borrar o modificar

//Este ComboBox permitirá seleccionar usuarios registrados
private JComboBox<Usuario> comboUsuarios;

//Usuario actualmente seleccionado
private Usuario escogido;

public DialogoModificarUsuario(Frame padre, boolean modal, boolean modoBorrar, GestorUsuarios gestor) {
super(padre, modal);
this.modoBorrar = modoBorrar;
gestorUsuarios = gestor;
iniciarComponentes();
mostrarEscogido();

setLayout(new BorderLayout());

add(new PanelTitulo(), BorderLayout.NORTH);
JPanel pnCentro = new JPanel();
pnCentro.setLayout(new BoxLayout(pnCentro, BoxLayout.Y_AXIS));
pnCentro.add(new PanelSelectorUsuario());
pnCentro.add(new JSeparator(SwingConstants.HORIZONTAL));
pnCentro.add(new PanelDatos());
add(pnCentro, BorderLayout.CENTER);
add(new PanelBoton(), BorderLayout.SOUTH);

setTitle(modoBorrar?"Borrar Usuario":"Modificar Usuario");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}

private void iniciarComponentes() {
campoNombre = new JTextField(10);
campoNombre.setFont(fuentePlana);
campoNombre.setForeground(Color.BLUE);
campoNombre.setEditable(!modoBorrar); //En modo Borrar, no se pueden editar datos
campoUser = new JTextField(10);
campoUser.setFont(fuentePlana);
campoUser.setForeground(Color.BLUE);
campoUser.setEditable(!modoBorrar);
campoPassword1 = new JPasswordField(10);
campoPassword1.setFont(fuentePlana);
campoPassword1.setForeground(Color.BLUE);
campoPassword1.setEditable(!modoBorrar);
campoPassword2 = new JPasswordField(10);
campoPassword2.setFont(fuentePlana);
campoPassword2.setForeground(Color.BLUE);
campoPassword2.setEditable(!modoBorrar);
campoEdad = new JSpinner();
campoEdad.setFont(fuentePlana);
campoEdad.setForeground(Color.BLUE);
campoEdad.setEnabled(!modoBorrar);
SpinnerNumberModel modeloSpinner = new SpinnerNumberModel();
modeloSpinner.setMinimum(18);
modeloSpinner.setValue(20);
campoEdad.setModel(modeloSpinner);
campoTipoUsuario = new JComboBox<String>(new String[] {"Administrador", "DeContenidos", "Limitado"});
campoTipoUsuario.setFont(fuentePlana);
campoTipoUsuario.setForeground(Color.BLUE);
campoTipoUsuario.setBackground(Color.WHITE);
campoTipoUsuario.setEnabled(false); //Siempre desactivado, no se puede modificar el tipo usuario
btAccion = new JButton(modoBorrar?"Borrar Usuario":"Modificar Usuario");
btAccion.setFont(fuenteNegrita);
btAccion.setForeground(Color.BLUE);
if (modoBorrar)
btAccion.addActionListener(new AccionBorrar());
else
btAccion.addActionListener(new AccionModificar());
comboUsuarios = new JComboBox<Usuario>(gestorUsuarios.getArrayUsuarios());
comboUsuarios.setFont(fuenteNegrita);
comboUsuarios.addActionListener(new AccionEscoger());
}

private void mostrarEscogido() {
escogido = (Usuario) comboUsuarios.getSelectedItem();

campoNombre.setText(escogido.getNombre());
campoUser.setText(escogido.getUserName());
campoPassword1.setText(escogido.getPassword());
campoPassword2.setText(escogido.getPassword());
campoEdad.setValue(escogido.getEdad());
if (escogido instanceof Administrador)
campoTipoUsuario.setSelectedIndex(0);
else if (escogido instanceof DeContenidos)
campoTipoUsuario.setSelectedIndex(1);
else
campoTipoUsuario.setSelectedIndex(2);
}

private boolean compruebaCampos() {
if (campoNombre.getText().isEmpty() || campoNombre.getText().isBlank()) {
advertencia("El campo Nombre Completo no puede estar vacío");
return false;
}
if (campoUser.getText().isEmpty() || campoUser.getText().isBlank()) {
advertencia("El campo Nombre Usuario no puede estar vacío");
return false;
}
if (campoPassword1.getPassword().length == 0) {
advertencia("El campo Contraseña no puede estar vacío");
return false;
}
if (campoPassword2.getPassword().length == 0) {
advertencia("El campo Confirmar Contraseña no puede estar vacío");
return false;
}

return true; //Todo OK
}

private void advertencia(String texto) {
JOptionPane.showMessageDialog(this, texto, "Crear Usuario", JOptionPane.WARNING_MESSAGE);
}

private class PanelTitulo extends JPanel {

public PanelTitulo() {
JLabel titulo = new JLabel(modoBorrar?"Eliminar Usuario":"Modificar Usuario");
titulo.setFont(new Font("Verdana", Font.ITALIC, 32));
JPanel pnBienvenido = new JPanel();
pnBienvenido.add(titulo);
pnBienvenido.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(pnBienvenido);
}
}

private class PanelSelectorUsuario extends JPanel {

public PanelSelectorUsuario() {
add(comboUsuarios);
TitledBorder bordeTitulo = new TitledBorder("Escoja Usuario");
bordeTitulo.setTitleFont(fuentePlana);
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20),
bordeTitulo));
}

}

private class PanelDatos extends JPanel {

public PanelDatos() {
setLayout(new GridLayout(3, 2, 4, 4));
add(new PanelConEtiqueta("Nombre Completo: ", campoNombre, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Nombre Usuario: ", campoUser, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Indique contraseña: ", campoPassword1, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Repita contraseña: ", campoPassword2, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Edad: ", campoEdad, FlowLayout.CENTER));
add(new PanelConEtiqueta("Tipo de Usuario: ", campoTipoUsuario, FlowLayout.CENTER));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
}
}

private class PanelBoton extends JPanel {

public PanelBoton() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JSeparator(SwingConstants.HORIZONTAL));
JPanel pnBoton = new JPanel();
pnBoton.add(btAccion);
pnBoton.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
add(pnBoton);
}
}

private class PanelConEtiqueta extends JPanel {

public PanelConEtiqueta(String textoEtiq, JComponent componente, int flotar) {
JLabel etiqueta = new JLabel(textoEtiq);
etiqueta.setFont(fuenteNegrita);
setLayout(new FlowLayout(flotar));
add(etiqueta);
add(componente);
}
}

private class AccionEscoger implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
mostrarEscogido();
}
}

private class AccionModificar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
if (compruebaCampos()) {
String nombreComp = campoNombre.getText();
String nombreUser = campoUser.getText();
String password = String.valueOf(campoPassword1.getPassword());
String password2 = String.valueOf(campoPassword2.getPassword());
int edad = (int) campoEdad.getValue();
if (password.equals(password2)) {
escogido.setNombre(nombreComp);
escogido.setUserName(nombreUser);
escogido.setEdad(edad);
escogido.setPassword(password);
gestorUsuarios.guardarUsuarios();
JOptionPane.showMessageDialog(null, "Usuario modificado",
"Modificar Usuario", JOptionPane.INFORMATION_MESSAGE);
}
else
advertencia("Las contraseñas no coinciden");
}
}
}

private class AccionBorrar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int respuesta = JOptionPane.showConfirmDialog(null,
"¿Seguro que quiere borrar este Usuario",
"Borrar Usuarios", JOptionPane.YES_NO_OPTION);
if (respuesta == JOptionPane.YES_OPTION) {
//Eliminamos del Gestor de usuarios
gestorUsuarios.borrarUsuario(escogido);
gestorUsuarios.guardarUsuarios();
//Eliminamos del combobox
comboUsuarios.removeItem(escogido);
}
}
}

}

Fijémonos en el JComboBox de Usuarios. Este combo no va a ser de tipo <String>, es decir, no es que simplemente muestre los nombres.
Es de tipo <Usuario>, es decir, contiene en su interior los objetos Usuario que recibe del Gestor de Usuarios.
Para poder recibirlos de forma fácil, lo que vamos a hacer es añadir un método a la clase GestorUsuarios que nos construya un array simple con todos los usuarios que hay registrados.
Código: [Seleccionar]
public Usuario[] getArrayUsuarios() {
Usuario[] arrayUsers = new Usuario[usuarios.size()];
for (int i = 0; i < usuarios.size(); i++)
arrayUsers[i] = usuarios.get(i);

return arrayUsers;

}

Gracias a este método, luego podemos construir rápidamente el combobox con esta línea:
Código: [Seleccionar]
comboUsuarios = new JComboBox<Usuario>(gestorUsuarios.getArrayUsuarios());
Sin embargo, esto no es suficiente. El combobox ahora contiene objetos de tipo Usuarios, bien, pero para mostrar en pantalla necesita mostrar un String que represente a cada uno de estos objetos.
Para hacer esto, el combobox va a mostrar lo que le devuelva el método toString() de cada uno de estos objetos. Este método lo heredan TODAS las clases de Java, pero para que funcione bien y lo haga según nuestras necesidades, tenemos que sobreescribirlo.

Así que nos vamos a ir a la clase padre Usuario y vamos a sobreescribir este método.
Yo he optado por hacer que muestre el nombre completo de la persona y a continuación el nombre de usuario con el que ha elegido registrarse.
Código: [Seleccionar]
@Override
public String toString() {
return nombre + " -- " + userName;
}

¡¡ATENCIÓN!!
Al modificar esta clase, por desgracia, los datos que tuviéramos guardados en disco ya no nos van a servir.
Este es uno de los inconvenientes de guardar datos de forma "serializada". Si alteramos el código de la clase de los objetos que estamos guardando, luego al intentar recuperarlos no sirven porque están creados con el código de clase anterior y se produce una excepción.

Así que tras hacer este cambio en la clase Usuario, lo mejor es borrar el archivo donde estemos guardando los usuarios (yo lo guardo en C:\JavaTicket\usuarios.dat) y que se genere uno nuevo.

(Continúa en el siguiente post)

507
Hola.
Necesitas unas variables para ir sumando las cantidades de grano que se vayan ingresando en el menú.
Puedes usar tres variables (lentejas, garbanzos, arvejas) o también un array de tres posiciones, que además es lo que te pide el enunciado.
Cada posición representaría un "silo" para cada tipo de grano.

Este array, yo lo declararía como static, fuera del método main(). De ese modo, será visible para todos los métodos/funciones que escribas, ya que se te pide que modules el código en varias funciones.
Puedes escribir una función para cada opción del menú, todas ellas necesitarán acceder al array de silos, por eso mejor ponerlo fuera del método main, para que esté visible para todos.
Ahí mismo pondría también el objeto Scanner, ya que las funciones también lo necesitarán.

Te dejo este código con la declaración del array y el Scanner como objetos de "ámbito global" y un boceto de como debería ser el método/función de la primera opción del menú.
Lo llamo ingresarGranos()
En ese boceto, escribo comentarios sobre los pasos que deberías seguir.
Intenta conseguirlo, y si no te sale, te ayudaremos a completarlo.

Un saludo.

Código: [Seleccionar]
public class SiloDeGranos {

static int[] silos = new int[3];
/*
* El array representa los tres silos:
* [0] --> garbanzos
* [1] --> lentejas
* [2] --> arvejas
*/

static Scanner sn = new Scanner(System.in);

public static void main(String[] args) {

int opcion = 0; //Guardaremos la opcion del usuario

while(opcion!=9999){

System.out.println("MENU");
System.out.println("1. Ingresar granos ");
System.out.println("2. Calcular costo por tipo de grano");
System.out.println("3. Calcular costo total");
System.out.println("Salir 9999");
System.out.println("Escribe una de las opciones");
opcion = sn.nextInt();

switch(opcion){
case 1:
ingresarGranos();
break;
case 2:
System.out.println("Has seleccionado la opcion 2");
break;
case 3:
System.out.println("Has seleccionado la opcion 3");
break;
default:
}

}

}

private static void ingresarGranos() {
//Preguntar tipo de grano (valor entre 1 y 3)
//Preguntar la cantidad de grano a ingresar
//Sumar en el array de silos la cantidad indicada
//La posición donde sumamos depende del tipo de grano
//ejemplo:
/*
if (posicion == 1) //Garbanzos
silos[0] = silos[0] + cantidad;
else if (posicion == 2) //Lentejas
silos[1] = silos[1] + cantidad;
else //Arvejas
silos[2] = silos[2] + cantidad;
*/
}
}

508
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 18 de Febrero 2021, 02:39 »
Lamento lo de tu computador, aunque quizás te sirva para ganar tiempo.
Algo más he podido añadir, funcionalidad para crear nuevos usuarios.

Uso otro JDialog con los campos necesarios para los atributos.
Dos JTextField para los nombres, dos JPasswordField  para recoger la contraseña dos veces y hacer la típica comprobación de que coinciden, un JSpinner con la edad (he limitado la edad mínima a 18 años, porque sí, porque me ha dado la gana xD ) y un JComboBox para escoger entre los tres posibles tipos de usuarios existentes:



Como esta JDialog no tiene que interactuar con otros elementos de la interfaz, simplemente hacer su labor y cerrarse cuando haya terminado, en su constructor sí va a recibir una referencia al objeto GestorUsuarios para que trabaje de forma autónoma.
El ActionListener de su botón si está escrito en esta misma clase, porque como digo, solo necesita interactuar con el gestor de usuarios.
Como expliqué antes, si necesitarse interactuar con la interfaz principal y/o más elementos, entonces el ActionListener sí lo escribiríamos en la clase principal.

Código: [Seleccionar]
public class DialogoCrearUsuario extends JDialog{

private GestorUsuarios gestorUsuarios;
private Font fuentePlana = new Font("Verdana", Font.PLAIN, 20);
private Font fuenteNegrita = new Font("Verdana", Font.PLAIN, 20);
private JTextField campoNombre;
private JTextField campoUser;
private JPasswordField campoPassword1;
private JPasswordField campoPassword2;
private JSpinner campoEdad;
private JComboBox<String> campoTipoUsuario;
private JButton btCrear;

public DialogoCrearUsuario(Frame padre, boolean modal, GestorUsuarios gestor) {
super(padre, modal);
gestorUsuarios = gestor;
iniciarComponentes();
setLayout(new BorderLayout());
add(new PanelTitulo(), BorderLayout.NORTH);
add(new PanelDatos(), BorderLayout.CENTER);
add(new PanelBoton(), BorderLayout.SOUTH);

setTitle("Crear Usuario");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}

private void iniciarComponentes() {
campoNombre = new JTextField(10);
campoNombre.setFont(fuentePlana);
campoNombre.setForeground(Color.BLUE);
campoUser = new JTextField(10);
campoUser.setFont(fuentePlana);
campoUser.setForeground(Color.BLUE);
campoPassword1 = new JPasswordField(10);
campoPassword1.setFont(fuentePlana);
campoPassword1.setForeground(Color.BLUE);
campoPassword2 = new JPasswordField(10);
campoPassword2.setFont(fuentePlana);
campoPassword2.setForeground(Color.BLUE);
campoEdad = new JSpinner();
campoEdad.setFont(fuentePlana);
campoEdad.setForeground(Color.BLUE);
SpinnerNumberModel modeloSpinner = new SpinnerNumberModel();
modeloSpinner.setMinimum(18);
modeloSpinner.setValue(20);
campoEdad.setModel(modeloSpinner);
campoTipoUsuario = new JComboBox<String>(new String[] {"Administrador", "DeContenidos", "Limitado"});
campoTipoUsuario.setFont(fuentePlana);
campoTipoUsuario.setForeground(Color.BLUE);
campoTipoUsuario.setBackground(Color.WHITE);
btCrear = new JButton("Crear Usuario");
btCrear.setFont(fuenteNegrita);
btCrear.setForeground(Color.BLUE);
btCrear.addActionListener(new AccionCrear());
}

private class PanelTitulo extends JPanel {

public PanelTitulo() {
JLabel titulo = new JLabel("Crear Nuevo Usuario");
titulo.setFont(new Font("Verdana", Font.ITALIC, 32));
JPanel pnBienvenido = new JPanel();
pnBienvenido.add(titulo);
pnBienvenido.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(pnBienvenido);
}
}

private class PanelDatos extends JPanel {

public PanelDatos() {
setLayout(new GridLayout(3, 2, 4, 4));
add(new PanelConEtiqueta("Nombre Completo: ", campoNombre, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Nombre Usuario: ", campoUser, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Indique contraseña: ", campoPassword1, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Repita contraseña: ", campoPassword2, FlowLayout.RIGHT));
add(new PanelConEtiqueta("Edad: ", campoEdad, FlowLayout.CENTER));
add(new PanelConEtiqueta("Tipo de Usuario: ", campoTipoUsuario, FlowLayout.CENTER));
setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
}
}

private class PanelBoton extends JPanel {

public PanelBoton() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JSeparator(SwingConstants.HORIZONTAL));
JPanel pnBoton = new JPanel();
pnBoton.add(btCrear);
pnBoton.setBorder(BorderFactory.createEmptyBorder(15, 15, 15, 15));
add(pnBoton);
}
}

private class PanelConEtiqueta extends JPanel {

public PanelConEtiqueta(String textoEtiq, JComponent componente, int flotar) {
JLabel etiqueta = new JLabel(textoEtiq);
etiqueta.setFont(fuenteNegrita);
setLayout(new FlowLayout(flotar));
add(etiqueta);
add(componente);
}
}

private class AccionCrear implements ActionListener {

@Override
public void actionPerformed(ActionEvent e) {
if (compruebaCampos()) {
String nombreComp = campoNombre.getText();
String nombreUser = campoUser.getText();
String password = String.valueOf(campoPassword1.getPassword());
String password2 = String.valueOf(campoPassword2.getPassword());
if (password.equals(password2)) {
int edad = (int) campoEdad.getValue();
String tipo = (String) campoTipoUsuario.getSelectedItem();
Usuario nuevo;
switch(tipo) {
case "Administrador":
nuevo = new Administrador(nombreComp, nombreUser, password, edad);
break;
case "DeContenidos":
nuevo = new DeContenidos(nombreComp, nombreUser, password, edad);
break;
default:
nuevo = new Limitado(nombreComp, nombreUser, password, edad);
}
if (gestorUsuarios.agregarUsuario(nuevo)) {
JOptionPane.showMessageDialog(null, "Nuevo Usuario creado",
"Crear Usuario", JOptionPane.INFORMATION_MESSAGE);
gestorUsuarios.guardarUsuarios();
cerrarDialogo(); //Cierra este JDialog
}
}
else
advertencia("Las contraseñas no coinciden");
}
}

}

private boolean compruebaCampos() {
if (campoNombre.getText().isEmpty() || campoNombre.getText().isBlank()) {
advertencia("El campo Nombre Completo no puede estar vacío");
return false;
}
if (campoUser.getText().isEmpty() || campoUser.getText().isBlank()) {
advertencia("El campo Nombre Usuario no puede estar vacío");
return false;
}
if (campoPassword1.getPassword().length == 0) {
advertencia("El campo Contraseña no puede estar vacío");
return false;
}
if (campoPassword2.getPassword().length == 0) {
advertencia("El campo Confirmar Contraseña no puede estar vacío");
return false;
}

return true; //Todo OK
}

private void advertencia(String texto) {
JOptionPane.showMessageDialog(this, texto, "Crear Usuario", JOptionPane.WARNING_MESSAGE);
}

private void cerrarDialogo() {
dispose();
}
}

Para que este JDialog aparezca al pulsar el botón de Nuevo Usuario en el panel de gestión de usuarios, si tenemos que escribir un ActionListener en la clase principal.

Código: [Seleccionar]
private class AccionNuevo implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
new DialogoCrearUsuario(null, true, gestorUsuarios);
}
}

Esta accion, se la hacemos llegar al botón correspondiente:

Citar
   private void iniciarVista() {
      //composicion panel principal
      pnPrincipal = new PanelPrincipal();
      pnPrincipal.setAccionGestionUsuario(new AccionGestionarUsuarios());
      //Composicion panel usuarios
      pnUsuarios = new PanelUsuarios();
      pnUsuarios.setAccionNuevo(new AccionNuevo());
      pnUsuarios.setAccionVolver(new AccionVolver());
      cardLayout = new CardLayout();
      panelActivo = new JPanel();
      panelActivo.setLayout(cardLayout);
      panelActivo.add(pnPrincipal, "principal");
      panelActivo.add(pnUsuarios, "usuarios");
      dialogoLogin = new DialogoLogin(this, true, gestorUsuarios);
      dialogoLogin.setAccionEnviar(new AccionEnviarLogin());
      dialogoLogin.setVisible(true);
   }

...usando un método que le hemos añadido a la clase PanelUsuarios:

Código: [Seleccionar]
public void setAccionNuevo(ActionListener accion) {
btNuevo.addActionListener(accion);
}


Y ahora podemos crear nuevos usuarios... pero claro, al cerrar programa se pierden. Así que conviene darles persistencia guardando en disco los datos de algún modo.
Lo más rápido, es "serializar" el ArrayList de Usuarios para guardarlo en disco.

Para lograr esto, lo primero es hacer que la clase Usuario implemente la interfaz Serializable

Citar
public abstract class Usuario implements Serializable{
   
   private String nombre;
   private String userName;
   private String password;
   private int edad;
   

Ahora, nos vamos a la clase GestionUsuarios y añadiremos código para que al iniciar el programa intente recuperar el fichero con los datos de usuario, si es que existe.
Primero añadimos un objeto File como atributo con la ruta que donde deseamos guardar el archivo con el ArrayList serializado.
Interesa que sea una ruta donde sepamos no habrá problema de permisos de escritura por parte del sistema operativo.
Citar
public class GestorUsuarios {

   private final File FICHERO_USERS = new File("C:/JavaTicket/usuarios.dat");
   private ArrayList<Usuario> usuarios;

Luego un método para intentar leer este fichero.
Tenemos que contemplar la posibilidad de que no exista, o que exista pero no hay permisos de acceso, o los datos se han corrompido, etc...
Si ocurre algo de estas posibilidades, lo notificamos con un mensaje y el ArrayList lo inicializamos nuevo, solo con el usuario Administrador que viene por defecto.

Si todo va bien, y el fichero existe y es accesible, lo recuperamos de disco y lo asignamos al atributo ArrayList. De este modo obtendremos los usuarios creados en sesiones anteriores.
Código: [Seleccionar]
private void cargarUsuarios() {
if (FICHERO_USERS.exists()) {
ObjectInputStream ois;
try {
ois = new ObjectInputStream(new FileInputStream(FICHERO_USERS));
Object aux = ois.readObject();
if (aux instanceof ArrayList) {
usuarios = (ArrayList<Usuario>) aux;
}
else {
JOptionPane.showMessageDialog(null, "No se reconocen los tipos de dato del fichero: "
+ FICHERO_USERS.getPath(), "Leer usuarios", JOptionPane.WARNING_MESSAGE);
usuarios = new ArrayList<Usuario>();
usuarios.add(new Administrador("Administrador", "admin", "admin", 20));
}
ois.close();
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "No se encuentra fichero de Usuarios en ruta: "
+ FICHERO_USERS.getPath(), "Leer usuarios", JOptionPane.WARNING_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "No se puede acceder a fichero de Usuarios en ruta: "
+ FICHERO_USERS.getPath(), "Leer usuarios", JOptionPane.WARNING_MESSAGE);
} catch (ClassNotFoundException e) {
JOptionPane.showMessageDialog(null, "Objeto de clase desconocida en fichero: "
+ FICHERO_USERS.getPath(), "Leer usuarios", JOptionPane.WARNING_MESSAGE);
}
}
else {
usuarios = new ArrayList<Usuario>();
usuarios.add(new Administrador("Administrador", "admin", "admin", 20));
}
}

Este método lo llamaremos en el constructor de la clase, para que sea lo primero que haga.
Código: [Seleccionar]
public GestorUsuarios() {
cargarUsuarios();
}

Ahora nos falta un método para hacer lo opuesto, guardar el ArrayList en disco.
Este método será publico porque lo llamaremos desde otras clases. De hecho, en el código del JDialog para crear usuarios que he puesto al principio, ya invoco este método despues de que el gestor de usuarios me confirme que ha logrado registrar el nuevo usuario.

El método que guarda estos datos, también ha de contemplar la posibilidad de que no exista ese fichero, en cuyo caso, primero creará la carpeta que lo va a contener y luego creará el archivo.

Código: [Seleccionar]
public void guardarUsuarios() {

try {
if (!FICHERO_USERS.exists()) {
File carpeta = new File("C:/JavaTicket");
carpeta.mkdirs();

FICHERO_USERS.createNewFile();
}

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(FICHERO_USERS));
oos.writeObject(usuarios); //Guardamos el ArrayList
oos.close();
} catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(null, "No se encuentra fichero de Usuarios en ruta: "
+ FICHERO_USERS.getPath(), "Guardar usuarios", JOptionPane.WARNING_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "No se puede acceder a fichero de Usuarios en ruta: "
+ FICHERO_USERS.getPath(), "Guardar usuarios", JOptionPane.WARNING_MESSAGE);
}
}

De este modo, ahora la próxima vez que iniciemos el programa, se conservarán los usuarios que hayamos creado y podremos hacer login con ellos.
Aquí por ejemplo hago login con un usuario de tipo "De Contenidos", el cuál no puede gestionar usuarios y por eso tiene el botón desactivado.




Y bueno, como puede verse, la dinámica vendrá a ser la misma. Para cada botón lanzar un JDialog (o si no paneles agregados en el CardLayout) con los elementos Swing necesarios para cumplir su función.
Escribir el listener correspondiente para su botón de acción y actualizar la clase principal para que incluya estos nuevos añadidos.

Enlace a Google Drive actualizado

509
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 17 de Febrero 2021, 12:02 »
Pequeña actualización.

Añado nuevo panel para la gestión de usuarios, de momento sin funcionalidad, excepto para el botón "volver"



Esta es la clase para este panel de usuarios. Es prácticamente calcada a la clase del panel principal.

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

private JButton btNuevo;
private JButton btModificar;
private JButton btEliminar;
private JButton btVolver;

public PanelUsuarios() {

iniciarBotones();

setLayout(new BorderLayout());
add(new PanelTitulo(), BorderLayout.NORTH);

JPanel pnCentro = new JPanel();
pnCentro.setLayout(new GridLayout(2, 2, 10, 10));
pnCentro.add(new PanelBoton(btNuevo, "Crear Usuario"));
pnCentro.add(new PanelBoton(btModificar, "Modificar Usuario"));
pnCentro.add(new PanelBoton(btEliminar, "Eliminar Usuario"));
pnCentro.add(new PanelBoton(btVolver, "Volver a menú principal"));
pnCentro.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

add(pnCentro, BorderLayout.CENTER);
}

private void iniciarBotones() {
btNuevo = new JButton();
btNuevo.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/crearUsuario.png")));
btNuevo.setFocusPainted(false);

btModificar = new JButton();
btModificar.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/editarUsuario.png")));
btModificar.setFocusPainted(false);

btEliminar = new JButton();
btEliminar.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/borrarUsuario.png")));
btEliminar.setFocusPainted(false);

btVolver = new JButton();
btVolver.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/volver.png")));
}

public void setAccionVolver(ActionListener accion) {
btVolver.addActionListener(accion);
}


private class PanelTitulo extends JPanel {

public PanelTitulo() {
JLabel titulo = new JLabel("Administrar Usuarios");
titulo.setFont(new Font("Verdana", Font.ITALIC, 32));
JPanel pnBienvenido = new JPanel();
pnBienvenido.add(titulo);
pnBienvenido.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(pnBienvenido);
}
}


private class PanelBoton extends JPanel {

public PanelBoton(JButton boton, String texto) {
setLayout(new BorderLayout());
add(boton, BorderLayout.CENTER);
JLabel etTexto = new JLabel(texto);
etTexto.setFont(new Font("Verdana", Font.BOLD ,18));
JPanel pnTexto = new JPanel();
pnTexto.add(etTexto);
add(pnTexto, BorderLayout.SOUTH);
setBorder(
BorderFactory.createCompoundBorder(BorderFactory.createRaisedSoftBevelBorder(),
BorderFactory.createEmptyBorder(15, 15, 15, 15)));
}
}
}

A la clase PanelPrincipal le añado un método para poder recibir el ActionListener que le asignaremos al boton "Gestionar Usuarios".
Código: [Seleccionar]
public void setAccionGestionUsuario(ActionListener accion) {
btUsuarios.addActionListener(accion);
}

Más adelante habrá que añadir otros métodos similares para las acciones de los otros botones.
Otra opción sería declarar los botones de estos paneles como PUBLIC, así desde la clase principal podríamos asignarle directamente las acciones sin tener que recurrir a un método setter

Las acciones de todos estos botones conviene escribirlas en la clase principal, para estar dentro del "ámbito total" y tener acceso al cardLayout, a los objetos que gestionarán Usuarios y Eventos, etc..

De este modo, evitamos tener que estar pasando referencias a estos objetos a través de los constructores de los paneles.
La pega es que la clase principal tendrá un montón de línea de código, pero bueno, para algo es la principal  ;D

Esta clase principal queda modificada de la siguiente manera para añadir el nuevo panel y escribir las nuevas acciones para los botones que de momento funcionan.
Marco cambios en negrita, para que se vea la dinámica que hay que seguir para ir añadiendo paneles y dotar de acciones a los botones.

El zip de Google Drive también ha sido actualizado

Citar
public final class JavaTicket extends JFrame{
   
   //Partes del MODELO
   private GestorUsuarios gestorUsuarios;
   private Usuario usuarioLogueado;
   
   //Partes de la VISTA
   private DialogoLogin dialogoLogin;
   private PanelPrincipal pnPrincipal;
   private PanelUsuarios pnUsuarios;
   private CardLayout cardLayout;
   private JPanel panelActivo;
   
   public JavaTicket() {
      
      iniciarModelo();
      iniciarVista();
      
      add(panelActivo);
      
      setTitle("Java Ticket");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      pack();
      setLocationRelativeTo(null);
      setVisible(true);
   }
   
   private void iniciarModelo() {
      gestorUsuarios = new GestorUsuarios();
      usuarioLogueado = null;
   }
   
   private void iniciarVista() {
      //composicion panel principal
      pnPrincipal = new PanelPrincipal();
      pnPrincipal.setAccionGestionUsuario(new AccionGestionarUsuarios());
      //Composicion panel usuarios
      pnUsuarios = new PanelUsuarios();
      pnUsuarios.setAccionVolver(new AccionVolver());

      cardLayout = new CardLayout();
      panelActivo = new JPanel();
      panelActivo.setLayout(cardLayout);
      panelActivo.add(pnPrincipal, "principal");
      panelActivo.add(pnUsuarios, "usuarios");
      dialogoLogin = new DialogoLogin(this, true, gestorUsuarios);
      dialogoLogin.setAccionEnviar(new AccionEnviarLogin());
      dialogoLogin.setVisible(true);
   }
   
   //Accion para boton del dialogo login
   private class AccionEnviarLogin implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         usuarioLogueado = dialogoLogin.getUsuarioLogin();
         
         if (usuarioLogueado != null) {
            pnPrincipal.setSaludo("Bienvenido " + usuarioLogueado.getNombre());
            String clase = usuarioLogueado.getClass().getName();
            switch(clase) {
            case "usuarios.Administrador":
               pnPrincipal.activarPanel(true);
               break;
            case "usuarios.DeContenidos":
               pnPrincipal.activarPanel(false);
               break;
            case "usuarios.Limitado":
               pnPrincipal.activarPanel(false);
               break;
            }
            
            dialogoLogin.setVisible(false);
         }
      }
   }
   
   //Acciones para los botones del menu principal
   private class AccionGestionarUsuarios implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         cardLayout.show(panelActivo, "usuarios");
         
      }
   }

   
   //Acciones para los botones del menu gestionar usuarios
   private class AccionVolver implements ActionListener {
      @Override
      public void actionPerformed(ActionEvent e) {
         cardLayout.show(panelActivo, "principal");
      }
   }


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

}


510
Comunidad / Re: Nuevo por aquí y daros las gracias
« en: 17 de Febrero 2021, 11:05 »
Bienvenido.
Estás en tu casa  ;)

511
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 17 de Febrero 2021, 03:18 »
La cosa es que yo tampoco dispongo de apenas tiempo, y un programa como este, requiere tiempo...
He hecho algunos avances más.

El programa inicia con pantalla de login:



Si el login es correcto, accedemos al panel principal:



Pero no funciona nada todavía, excepto el botón de salir.

Si se cancela la ventana de login, el panel principal aparece pero desactivado.



La idea sería añadir un barra de menu superior que permita login/logout sin tener que reabrir la aplicación, pero eso ya sería dejarlo para lo último, ante la falta de tiempo.

Para controlar los Usuarios registrados y el tema del login, uso una clase dedicada a eso, a gestionar usuarios:
Código: [Seleccionar]
import java.util.ArrayList;

import javax.swing.JOptionPane;

public class GestorUsuarios {

private ArrayList<Usuario> usuarios;

public GestorUsuarios() {
usuarios = new ArrayList<Usuario>();
usuarios.add(new Administrador("Administrador", "admin", "supersecreto", 0));
}

public boolean agregarUsuario(Usuario usuario) {
if (usuarios.contains(usuario)) {
JOptionPane.showMessageDialog(null, "Este usuario ya está registrado",
"Registrar nuevo Usuario", JOptionPane.WARNING_MESSAGE);
return false;
}
else
return usuarios.add(usuario);
}

public Usuario loginUsuario(String userName, String password) {

for (Usuario user: usuarios) {
if (user.getUserName().equals(userName)) {
if (user.getPassword().equals(password))
return user; //Usuario encontrado, lo retornamos para login
else { //Contraseña incorrecta
JOptionPane.showMessageDialog(null, "La contraseña no es correcta",
"Login Usuario", JOptionPane.WARNING_MESSAGE);
return null;
}

}
}
//Finalizado el for sin retornar nada, es que no existe ese username
JOptionPane.showMessageDialog(null, "Nombre de Usuario no existe",
"Login Usuario", JOptionPane.WARNING_MESSAGE);
return null;
}

}

La ventana de login, es un JDialog:
Código: [Seleccionar]
public class DialogoLogin extends JDialog {

private JTextField campoNombre;
private JPasswordField campoPass;
private JButton btEnviar;
private GestorUsuarios gestorUsuarios;
private Font fuente;

public DialogoLogin(Frame padre, boolean modal, GestorUsuarios gestor) {
super(padre, modal);
gestorUsuarios = gestor;
fuente = new Font("Verdana", Font.PLAIN, 20);

JPanel centro = new JPanel();
centro.setLayout(new BoxLayout(centro, BoxLayout.Y_AXIS));
centro.add(new PanelNombre());
centro.add(new PanelPass());
centro.setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(20, 25, 20, 25),
BorderFactory.createLoweredSoftBevelBorder()));

setLayout(new BorderLayout());
add(new PanelImagen(), BorderLayout.NORTH);
add(centro, BorderLayout.CENTER);
add(new PanelEnviar(), BorderLayout.SOUTH);

setTitle("Login Usuario");
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
pack();
setResizable(false);
setLocationRelativeTo(null);
}

private class PanelImagen extends JPanel {

public PanelImagen() {
ImageIcon imagen = new ImageIcon(DialogoLogin.class.getClassLoader().getResource("img/login.png"));
JLabel icono = new JLabel(imagen);
icono.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
add(icono);
}
}

private class PanelNombre extends JPanel {

public PanelNombre() {
campoNombre = new JTextField(10);
campoNombre.setFont(fuente);
JLabel etiq = new JLabel("Nombre de Usuario: ");
etiq.setFont(fuente);
setLayout(new FlowLayout(FlowLayout.RIGHT));
add(etiq);
add(campoNombre);
}
}

private class PanelPass extends JPanel {

public PanelPass() {
campoPass = new JPasswordField(10);
campoPass.setFont(fuente);
JLabel etiq = new JLabel("Contraseña: ");
etiq.setFont(fuente);
setLayout(new FlowLayout(FlowLayout.RIGHT));
add(etiq);
add(campoPass);
}
}

private class PanelEnviar extends JPanel {

public PanelEnviar() {
btEnviar = new JButton("Enviar");
btEnviar.setFont(new Font("Verdana", Font.BOLD, 24));
add(btEnviar);
}
}

public Usuario getUsuarioLogin() {
String userName = campoNombre.getText();
String password = String.valueOf(campoPass.getPassword());
if (userName == null || userName.isBlank() || userName.isEmpty()) {
java.awt.Toolkit.getDefaultToolkit().beep();
return null;
}

return gestorUsuarios.loginUsuario(userName, password);
}

public void setAccionEnviar(ActionListener accion) {
btEnviar.addActionListener(accion);
}

}

El panel principal es un JPanel con los 4 botones.
Uno de los métodos es para decidir si se activa o no el botón de administrar usuarios, dependiendo de si el usuario logueado es administrador o no.

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

private JButton btEventos;
private JButton btUsuarios;
private JButton btReportes;
private JButton btSalir;
private JLabel bienvenido;

public PanelPrincipal() {
iniciarBotones();
bienvenido = new JLabel("No hay usuario logueado");

setLayout(new BorderLayout());
add(new PanelSaludo(), BorderLayout.NORTH);

JPanel pnCentro = new JPanel();
pnCentro.setLayout(new GridLayout(2, 2, 10, 10));
pnCentro.add(new PanelBoton(btEventos, "Administrar Eventos"));
pnCentro.add(new PanelBoton(btUsuarios, "Administrar Usuarios"));
pnCentro.add(new PanelBoton(btReportes, "Mostrar Reportes"));
pnCentro.add(new PanelBoton(btSalir, "Cerrar Aplicación"));
pnCentro.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));

add(pnCentro, BorderLayout.CENTER);

}

private void iniciarBotones() {
btEventos = new JButton();
btEventos.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/administrarEvento.png")));
btEventos.setEnabled(false);
btEventos.setFocusPainted(false);

btUsuarios = new JButton();
btUsuarios.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/administrarUsuario.png")));
btUsuarios.setEnabled(false);
btUsuarios.setFocusPainted(false);

btReportes = new JButton();
btReportes.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/reportes.png")));
btReportes.setEnabled(false);
btReportes.setFocusPainted(false);

btSalir = new JButton();
btSalir.setIcon(
new ImageIcon(PanelPrincipal.class.getClassLoader().getResource("img/salir.png")));
btSalir.setFocusPainted(false);
btSalir.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int respuesta = JOptionPane.showConfirmDialog(null, "¿Cerrar Aplicacion?",
"Cerrar Programa", JOptionPane.YES_NO_OPTION);
if (respuesta == JOptionPane.YES_OPTION)
System.exit(0);
}
});
}

public void setSaludo(String saludo) {
bienvenido.setText(saludo);
}

public void activarPanel(boolean esAdmin) {
btUsuarios.setEnabled(esAdmin);
btEventos.setEnabled(true);
btReportes.setEnabled(true);
}

private class PanelSaludo extends JPanel {

public PanelSaludo() {
bienvenido.setFont(new Font("Verdana", Font.ITALIC, 32));
JPanel pnBienvenido = new JPanel();
pnBienvenido.add(bienvenido);
pnBienvenido.setBorder(BorderFactory.createLoweredSoftBevelBorder());
add(pnBienvenido);
}
}

private class PanelBoton extends JPanel {

public PanelBoton(JButton boton, String texto) {
setLayout(new BorderLayout());
add(boton, BorderLayout.CENTER);
JLabel etTexto = new JLabel(texto);
etTexto.setFont(new Font("Verdana", Font.BOLD ,18));
JPanel pnTexto = new JPanel();
pnTexto.add(etTexto);
add(pnTexto, BorderLayout.SOUTH);
setBorder(
BorderFactory.createCompoundBorder(BorderFactory.createRaisedSoftBevelBorder(),
BorderFactory.createEmptyBorder(15, 15, 15, 15)));
}
}

}

La clase principal, la que modela el JFrame, tendrá todos los paneles que se vayan creando más adelante y se irían mostrando mediante un CardLayout, igual que en el ejemplo que encontraste.
También tendrá los objetos propios del "modelo".

De momento lo que hace es inicializar todos estos objetos y mostrar el dialogo de login.
Si se cancela, muestra panel principal desactivado.
Si el login funciona, muestra el panel adecuado según el tipo de usuario.

Código: [Seleccionar]
public final class JavaTicket extends JFrame{

//Partes del MODELO
private GestorUsuarios gestorUsuarios;
private Usuario usuarioLogueado;

//Partes de la VISTA
private DialogoLogin dialogoLogin;
private PanelPrincipal pnPrincipal;
private CardLayout cardLayout;
private JPanel panelActivo;

public JavaTicket() {

iniciarModelo();
iniciarVista();

add(panelActivo);

setTitle("Java Ticket");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}

private void iniciarModelo() {
gestorUsuarios = new GestorUsuarios();
usuarioLogueado = null;
}

private void iniciarVista() {

pnPrincipal = new PanelPrincipal();
cardLayout = new CardLayout();
panelActivo = new JPanel();
panelActivo.setLayout(cardLayout);
panelActivo.add(pnPrincipal, "principal");
dialogoLogin = new DialogoLogin(this, true, gestorUsuarios);
dialogoLogin.setAccionEnviar(new AccionEnviarLogin());
dialogoLogin.setVisible(true);
}

private class AccionEnviarLogin implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
usuarioLogueado = dialogoLogin.getUsuarioLogin();

if (usuarioLogueado != null) {
pnPrincipal.setSaludo("Bienvenido " + usuarioLogueado.getNombre());
String clase = usuarioLogueado.getClass().getName();
switch(clase) {
case "usuarios.Administrador":
pnPrincipal.activarPanel(true);
break;
case "usuarios.DeContenidos":
pnPrincipal.activarPanel(false);
break;
case "usuarios.Limitado":
pnPrincipal.activarPanel(false);
break;
}

dialogoLogin.setVisible(false);
}
}
}

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

}

Dejo un enlace a Google Drive para descargar un zip con todo el proyecto, incluidas las imágenes que he usado. (aunque se pueden usar las que uno quiera, o no usar ninguna)

Mañana intentaré hacer algo más


512
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 16 de Febrero 2021, 12:29 »
Bufff... pues andas mal de tiempo.

Y cuidado con reaprovechar código de otros programas, a veces se tarda más intentando componer esas "piezas" para adaptarlas a tu programa, que escribirlas de cero.
Pueden servir de inspiración, pero no siempre se pueden copiar y usar directamente.

Por otro lado, es importante tener las clases principales más o menos ya establecidas, para luego tener claro que necesitamos pedir y hacer en la parte de la interfaz gráfica.

Las clases de Eventos podrían ser estas:

Clase Evento (abstracta)
Código: [Seleccionar]
/**
 * Clase abstracta con todos los atributos y métodos comunes
 * a todos los tipos de eventos que existirán en el programa.<br>
 * Incluye método <i>equals()</i> sobreescrito de manera que
 * dos objetos Evento son equivalentes si coincide el atributo
 * <i>codigo</i>, el cuál se considera como un identificador
 * de eventos <u>único</u>.
 */
public abstract class Evento {

private String codigo;
private String titulo;
private String descripcion;
private String fecha;
protected double renta; //Protected porque algunas clases hijas necesitan verla para calcular seguro

public Evento(String codigo, String titulo, String descripcion, String fecha, double renta) {
this.codigo = codigo;
this.titulo = titulo;
this.descripcion = descripcion;
this.fecha = fecha;
this.renta = renta;
}

public String getCodigo() {
return codigo;
}

public void setCodigo(String codigo) {
this.codigo = codigo;
}

public String getTitulo() {
return titulo;
}

public void setTitulo(String titulo) {
this.titulo = titulo;
}

public String getDescripcion() {
return descripcion;
}

public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}

public String getFecha() {
return fecha;
}

public void setFecha(String fecha) {
this.fecha = fecha;
}

public double getRenta() {
return renta;
}

public void setRenta(double renta) {
this.renta = renta;
}

@Override
public boolean equals(Object obj) {

if (obj instanceof Evento) {
Evento otroEvento = (Evento)obj;
return otroEvento.codigo.equals(codigo);
}
else
return false;
}

}

Clase Deportivo
Código: [Seleccionar]
import javax.swing.JOptionPane;

/**
 * Modela un Evento de tipo Deportivo.<br>
 * Este evento tiene un aforo máximo de 20000
 */
public final class Deportivo extends Evento{

//Constante que establece el limite de personas que pueden asistir a este evento
private final int AFORO_MAXIMO = 20000;
//Atributos
private int aforo;
private String equipo1;
private String equipo2;
private TipoDeporte tipoDeporte;

public Deportivo(String codigo, String titulo, String descripcion, String fecha, double renta,
int aforo, String equipo1, String equipo2, TipoDeporte tipoDeporte) {
super(codigo, titulo, descripcion, fecha, renta);

setAforo(aforo);
this.equipo1 = equipo1;
this.equipo2 = equipo2;
this.tipoDeporte = tipoDeporte;
}

public int getAforo() {
return aforo;
}

/**
* Establece la cantidad de gente que asistirá al evento.<br>
* Esta cantidad está limitada por la constante <i>AFORO_MAXIMO.</i>
* @param aforo Cantidad de gente que asistirá al evento.
*/
public void setAforo(int aforo) {
if (aforo > AFORO_MAXIMO) {
JOptionPane.showMessageDialog(null, "El aforo indicado supera el límite establecido."
+ "El aforo será establecido a " + AFORO_MAXIMO, "Crear Evento Derportivo",
JOptionPane.WARNING_MESSAGE);
this.aforo = AFORO_MAXIMO;
}
else
this.aforo = aforo;
}

public String getEquipo1() {
return equipo1;
}

public void setEquipo1(String equipo1) {
this.equipo1 = equipo1;
}

public String getEquipo2() {
return equipo2;
}

public void setEquipo2(String equipo2) {
this.equipo2 = equipo2;
}

public TipoDeporte getTipoDeporte() {
return tipoDeporte;
}

public void setTipoDeporte(TipoDeporte tipoDeporte) {
this.tipoDeporte = tipoDeporte;
}

}

Clase Musical
Código: [Seleccionar]
import javax.swing.JOptionPane;

/**
 * Modela un Evento de tipo Musical<br>
 * Este evento tiene un aforo máximo de 25000 personas.
 */
public final class Musical extends Evento{

//Constante que establece el limite de personas que pueden asistir a este evento
private final int AFORO_MAXIMO = 25000;
//Constante que establece el % de cuota de seguro que se cobra sobre la renta acordada
private final double CUOTA_SEGURO = 30d; //30%
//Atributos
private int aforo;
private TipoMusica tipoMusica;

public Musical(String codigo, String titulo, String descripcion, String fecha, double renta,
int aforo, TipoMusica tipoMusica) {
super(codigo, titulo, descripcion, fecha, renta);
setAforo(aforo);
this.tipoMusica = tipoMusica;
}

public int getAforo() {
return aforo;
}

/**
* Establece la cantidad de gente que asistirá al evento.<br>
* Esta cantidad está limitada por la constante <i>AFORO_MAXIMO.</i>
* @param aforo Cantidad de gente que asistirá al evento.
*/
public void setAforo(int aforo) {
if (aforo > AFORO_MAXIMO) {
JOptionPane.showMessageDialog(null, "El aforo indicado supera el límite establecido."
+ "El aforo será establecido a " + AFORO_MAXIMO, "Crear Evento Derportivo",
JOptionPane.WARNING_MESSAGE);
this.aforo = AFORO_MAXIMO;
}
else
this.aforo = aforo;
}

public TipoMusica getTipoMusica() {
return tipoMusica;
}

public void setTipoMusica(TipoMusica tipoMusica) {
this.tipoMusica = tipoMusica;
}

/**
* Establece el importe del seguro que se cobra
* por el uso de la grama.<br>Este importe es el
* 30% del valor de la renta acordada.
* @return Importe del seguro.
*/
public double getImporteSeguro() {
return  renta * CUOTA_SEGURO / 100;
}

}

Clase Religioso
Código: [Seleccionar]
import javax.swing.JOptionPane;

/**
 * Modela un Evento de tipo Religioso.<br>
 * Este evento tiene un aforo maximo de 30000 personas.
 */
public final class Religioso extends Evento{

//Constante que establece el limite de personas que pueden asistir a este evento
private final int AFORO_MAXIMO = 30000;
//Constante que establece el importe fijado como seguro por desgaste de la grama
public final int CUOTA_SEGURO = 2000; //Public, no será necesario usar getter para acceder a este valor
//Atributos
private int aforo;

public Religioso(String codigo, String titulo, String descripcion, String fecha, double renta,
int aforo) {
super(codigo, titulo, descripcion, fecha, renta);
setAforo(aforo);
}

public int getAforo() {
return aforo;
}

/**
* Establece la cantidad de gente que asistirá al evento.<br>
* Esta cantidad está limitada por la constante <i>AFORO_MAXIMO.</i>
* @param aforo Cantidad de gente que asistirá al evento.
*/
public void setAforo(int aforo) {
if (aforo > AFORO_MAXIMO) {
JOptionPane.showMessageDialog(null, "El aforo indicado supera el límite establecido."
+ "El aforo será establecido a " + AFORO_MAXIMO, "Crear Evento Derportivo",
JOptionPane.WARNING_MESSAGE);
this.aforo = AFORO_MAXIMO;
}
else
this.aforo = aforo;
}

}

Y se usarían estos dos enumerados:

Código: [Seleccionar]
public enum TipoDeporte {
FUTBOL, TENIS, RUGBY, BASEBALL
}

Código: [Seleccionar]
public enum TipoMusica {
POP, ROCK, RAP, CLASICA, REGGEATON, OTRO
}

513
Aprender a programar desde cero / Re: Ayuda con mi proyecto!
« en: 16 de Febrero 2021, 01:15 »
Hola.
Este mensaje me había pasado desapercibido.
¿Tienes algo hecho ya?

Se podría empezar por las clases de usuarios que participan:

Clase Usuario(abstracta):
Código: [Seleccionar]
/**
 * Clase abstracta con los atributos y métodos comunes
 * a todos los tipos de usuarios que existirán en el programa.<br>
 * Incluye método <i>equals()</i> sobreescrito de manera que
 * dos objetos Usuario son equivalentes si coincide el atributo
 * <i>userName</i>, el cuál se considera como un identificador
 * de usuarios <u>único</u>.
 */
public abstract class Usuario {

private String nombre;
private String userName;
private String password;
private int edad;

public Usuario(String nombre, String userName, String password, int edad) {
this.nombre = nombre;
this.userName = userName;
this.password = password;
this.edad = edad;
}

public String getNombre() {
return nombre;
}

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

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public int getEdad() {
return edad;
}

public void setEdad(int edad) {
this.edad = edad;
}

/**
* Dos usuarios son iguales si tienen el
* mismo nombre de usuario, es decir,
* coincide el atributo <i>userName</i>
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof Usuario) {
Usuario otroUser = (Usuario)obj;
return otroUser.userName.equals(userName);
}
else
return false;
}

}

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

import javax.swing.JOptionPane;

/**
 * Modela un Usuario de tipo Administrador, el cuál tendrá
 * poder total dentro del programa.<br>Podrá crear eventos y también
 * crear otros usuarios.
 */
public final class Administrador extends Usuario {

private ArrayList<String> idEventos;

public Administrador(String nombre, String userName, String password, int edad) {
super(nombre, userName, password, edad);
idEventos = new ArrayList<String>();
}

/**
* Agrega el id de un evento al registro
* de eventos creados por este usuario.
* @param id Identificador del evento
*/
public void agregarIdEvento(String id) {
idEventos.add(id);
}

/**
* Elimina el id de un evento del registro
* de eventos creados por este usuario.<br>
* Si el id no consta en el registro, se mostrará
* un mensaje emergente de advertencia.
* @param id Identificador del evento
*/
public void eliminaIdEvento(String id) {
if (esCreadorDeEvento(id))
idEventos.remove(id);
else
JOptionPane.showMessageDialog(null, "Este evento no ha sido creador por este usuario",
"Eliminar Evento", JOptionPane.WARNING_MESSAGE);
}

/**
* Comprueba si un determinado evento es
* creación de este usuario.<br>
* Para ello se comprueba la existencia del ID del evento
* en el ArrayList que registra los eventos creados por este usuario.
* @param id Identificador del evento.
* @return TRUE si el evento ha sido creado por este usuario,
* FALSE en caso contrario.
*/
public boolean esCreadorDeEvento(String id) {
return idEventos.contains(id);
}
}

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

import javax.swing.JOptionPane;

/**
 * Modela un Usuario de tipo "De contenidos".<br>
 * Este usuario podrá crear eventos, pero no podrá
 * crear otros usuarios.
 */
public final class DeContenidos extends Usuario{

private ArrayList<String> idEventos;

public DeContenidos(String nombre, String userName, String password, int edad) {
super(nombre, userName, password, edad);
idEventos = new ArrayList<String>();
}

/**
* Agrega el id de un evento al registro
* de eventos creados por este usuario.
* @param id Identificador del evento
*/
public void agregarIdEvento(String id) {
idEventos.add(id);
}

/**
* Elimina el id de un evento del registro
* de eventos creados por este usuario.<br>
* Si el id no consta en el registro, se mostrará
* un mensaje emergente de advertencia.
* @param id Identificador del evento
*/
public void eliminaIdEvento(String id) {
if (esCreadorDeEvento(id))
idEventos.remove(id);
else
JOptionPane.showMessageDialog(null, "Este evento no ha sido creador por este usuario",
"Eliminar Evento", JOptionPane.WARNING_MESSAGE);
}

/**
* Comprueba si un determinado evento es
* creación de este usuario.<br>
* Para ello se comprueba la existencia del ID del evento
* en el ArrayList que registra los eventos creados por este usuario.
* @param id Identificador del evento.
* @return TRUE si el evento ha sido creado por este usuario,
* FALSE en caso contrario.
*/
public boolean esCreadorDeEvento(String id) {
return idEventos.contains(id);
}

}

Clase Limitado:
Código: [Seleccionar]
/**
 * Modela un Usuario de tipo Limitado.<br>
 * Este usuario no podrá crear eventos ni otros usuarios.
 */

public final class Limitado extends Usuario{

public Limitado(String nombre, String userName, String password, int edad) {
super(nombre, userName, password, edad);
}

}

514
Hola.

No lo estás enfocando bien.

El usuario te va a dar una posición del vector, así que no tienes que comparar los números que contiene el vector con esa posición.
Primero porque son cosas diferentes, y segundo porque esa posición va a existir sí o sí.

Una vez te da la posición, no hay que comprobar nada. Simplemente mostrar el número que existe en esa posición.
Bueno, quizás la única comprobación que se podría hacer es asegurarnos de que para la posición nos da un valor entre 0 y 5.
Esto añádelo después si quieres, de momento, vamos a considerar que el usuarios nos va a dar una posición dentro del rango correcto.

Otra cosa, la variable num te la puedes ahorrar. Puedes leer los valores y guardarlos directamente en el vector.
No es necesario leerlos en num, y luego asignarlos a las posiciones del vector.

Tampoco necesitas la librería conio.h, es más, para C++ rara vez la vas a necesitar.

Prueba este código, a ver que te parece:

Código: [Seleccionar]
#include <iostream>

using namespace std;

int main()
{
    int vector[6];
    int posicion;

    for (int i = 0; i < 6; i++)
    {
        cout << "Ingrese numero: ";
        cin >> vector[i];
    }

    cout << "\nIndique la posicion que quiere consultar: ";
    cin >> posicion;

    cout << "Numero encontrado: " << vector[posicion];
}

515
Comunidad / Re: Nuevo miembro!
« en: 16 de Febrero 2021, 00:19 »
Bienvenido.

Por si no lo has visto ya, en la web tienes un enlace con distintos cursos con los que iniciarte.

516
En principio sirve cualquier IDE (entorno de desarrollo integrado) como NetBeans, Eclipse o IntelliJ.
Yo uso Eclipse, pero más que nada porque es con el primero que empecé y me siento más a gusto con él.

517
Comienza tú un código y te ayudamos a completarlo, o a resolver dudas concretas.

Aquí estamos para ayudar en vuestro aprendizaje, no para hacerle las tareas a nadie.

En este tema intenté que comentaras tus dudas. No lo hiciste y aun así decidí resolverlo.
No hubo respuesta.
Ni gracias, que es lo que menos me importa..., pero si me hubiera gustado que hubiese comentado si habías aprendido algo, si aún te quedaban dudas....

En este otro también di una solución.
De nuevo sin respuesta o comentario alguno.

En ambos casos habría sido interesante que hubieras compartido soluciones alternativas obtenidas de tus profesores o compañeros de clase.

En fin, intentamos que esto sea una comunidad donde todos compartan algo y sobre todo se aprenda a programar (así se llama la web)
Dar las tareas hechas sin más..., no es ayudar.

518
He jugado un par de partidas, en modo difícil.. y sí, es muy difícil.  ;D
Entre que el enemigo es el doble de potente, y que encima tiene cierta inteligencia...
Citar
      CREAR PERSONAJE
      ----- ---------
Clases disponibles:
[1] -> Caballero
[2] -> Mago
Elige clase: 2

Nombre: Gandalf
Nivel de Ataque(min = 1/max = 10): 10
Nivel de Defensa(min = 1/max = 10): 10
Nivel de Vida(min = 100/max = 1000): 150

Este es tu personaje:
Mago: Gandalf / Nivel de Vida: 150

      PULSA ENTER PARA SEGUIR


Y este será tu rival:
Caballero: Thanos / Nivel de Vida: 300
Nivel de Ataque: 20 / Nivel de Defensa: 20

      PULSA ENTER PARA SEGUIR



      NIVEL DIFICULTAD

      ----- ----------
[1] -> Modo NORMAL(Por defecto)
[2] -> Modo DIFICIL(La CPU será más inteligente)
Elige modo: 2


      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 150
> CPU: Caballero: Thanos / Nivel de Vida: 300

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 8 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos consigue bloquear ataque de Gandalf

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 150
> CPU: Caballero: Thanos / Nivel de Vida: 300

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 2

Gandalf se defiende.
Consigues una DEFENSA de 9 pts.

      PULSA ENTER PARA SEGUIR


Thanos ataca.

      PULSA ENTER PARA SEGUIR



Gandalf consigue bloquear ataque de Thanos

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 150
> CPU: Caballero: Thanos / Nivel de Vida: 300

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 3

Gandalf usa su hechizo de ATAQUE
Consigues un ATAQUE de 15 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos recibe daños.
Pierde 10 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 150
> CPU: Caballero: Thanos / Nivel de Vida: 290

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 1 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos consigue bloquear ataque de Gandalf

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 150
> CPU: Caballero: Thanos / Nivel de Vida: 290

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 4 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos consigue bloquear ataque de Gandalf

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 150
> CPU: Caballero: Thanos / Nivel de Vida: 290

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 3

Gandalf usa su hechizo de ATAQUE
Consigues un ATAQUE de 15 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.

Thanos recibe daños.
Pierde 15 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 120
> CPU: Caballero: Thanos / Nivel de Vida: 275

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
¡Gandalf consigue un Ataque Crítico!
Consigues un ATAQUE de 8 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.

Thanos recibe daños.
Pierde 8 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 90
> CPU: Caballero: Thanos / Nivel de Vida: 267

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 4

Gandalf usa su hechizo de SANACIÓN
¡Gandalf recupera 50 puntos de vida!

      PULSA ENTER PARA SEGUIR


Thanos ataca.

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 4 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 136
> CPU: Caballero: Thanos / Nivel de Vida: 267

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 1 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos consigue bloquear ataque de Gandalf

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 136
> CPU: Caballero: Thanos / Nivel de Vida: 267

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 3

Gandalf usa su hechizo de ATAQUE
Consigues un ATAQUE de 15 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos recibe daños.
Pierde 1 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 136
> CPU: Caballero: Thanos / Nivel de Vida: 266

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
¡Gandalf consigue un Ataque Crítico!
Consigues un ATAQUE de 12 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos recibe daños.
Pierde 6 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 136
> CPU: Caballero: Thanos / Nivel de Vida: 260

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 5 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos consigue bloquear ataque de Gandalf

      PULSA ENTER PARA SEGUIR


¡Gandalf recupera 1 punto de vida!

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 137
> CPU: Caballero: Thanos / Nivel de Vida: 260

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 3

Gandalf usa su hechizo de ATAQUE
Consigues un ATAQUE de 15 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.

Thanos recibe daños.
Pierde 15 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 107
> CPU: Caballero: Thanos / Nivel de Vida: 245

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 3

Gandalf usa su hechizo de ATAQUE
Consigues un ATAQUE de 15 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.

Thanos recibe daños.
Pierde 15 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 77
> CPU: Caballero: Thanos / Nivel de Vida: 230

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 2 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.

Thanos recibe daños.
Pierde 2 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 47
> CPU: Caballero: Thanos / Nivel de Vida: 228

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 3

Gandalf usa su hechizo de ATAQUE
Consigues un ATAQUE de 15 pts.

      PULSA ENTER PARA SEGUIR


Thanos se defiende.

      PULSA ENTER PARA SEGUIR



Thanos recibe daños.
Pierde 11 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 47
> CPU: Caballero: Thanos / Nivel de Vida: 217

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 5 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.

Thanos recibe daños.
Pierde 5 puntos de vida.

      PULSA ENTER PARA SEGUIR




      COMBATIENDO...
> Jugador: Mago: Gandalf / Nivel de Vida: 17
> CPU: Caballero: Thanos / Nivel de Vida: 212

Turno de Gandalf
[1] -> Atacar
[2] -> Defender
[3] -> Hechizo de ATAQUE
[4] -> Hechizo de SANACIÓN
Elige acción: 1

Gandalf ataca.
Consigues un ATAQUE de 10 pts.

      PULSA ENTER PARA SEGUIR


Thanos usa su ataque RELÁMPAGO

      PULSA ENTER PARA SEGUIR



Gandalf recibe daños.
Pierde 30 puntos de vida.
¡Gandalf ha muerto!

Thanos recibe daños.
Pierde 10 puntos de vida.

      PULSA ENTER PARA SEGUIR



Victoria para:
Caballero: Thanos / Nivel de Vida: 202


      FIN DE PROGRAMA

519
Bueno, pues esta es la explicación a grandes rasgos.
A continuación pongo el código completo de la clase main.
Con lo que he explicado, y la cantidad de comentarios que incluye el código, creo que se puede entender bastante bien como funciona.
Sin embargo, no dudes en preguntar lo que sea.

Por favor, no te conformes con tener un programa que funciona. Asegúrate de que además lo entiendes.

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

public class Combate {

private static Scanner teclado = new Scanner(System.in);
private static Personaje jugador; //Podrá ser un Mago o un Caballero
private static Caballero CPU; //La CPU siempre es un Caballero
private static boolean modoDificil = false; //Nivel de dificultad

public static void main(String[] args) {

crearPersonaje();

elegirDificultad();

//Comienza el combate...
while(jugador.getVida() > 0 && CPU.getVida() > 0) {
/*
* Las siguientes variables recogerán los valores de ataques o defensas
* de los combatientes, según la acción que escojan, en cada turno.
*/
int ataqueJugador = 0, ataqueCPU = 0;
int defensaJugador = 0, defensaCPU = 0;
System.out.println("\n\n\t\tCOMBATIENDO...");
System.out.println("> Jugador: " + jugador);
System.out.println("> CPU: " + CPU);
//Recogemos acción del jugador
int accionJugador = menuCombateJugador();
System.out.println(); //Salto de línea
//Evaluamos la acción escogida
switch(accionJugador) {
case 1: //Ataque normal
ataqueJugador = jugador.atacar();
System.out.println("Consigues un ATAQUE de " + ataqueJugador + " pts.");
break;
case 2: //Defender
defensaJugador = jugador.defender();
System.out.println("Consigues una DEFENSA de " + defensaJugador + " pts.");
break;
case 3: //Ataque relámpago o hechizo ataque, según clase
if (jugador instanceof Caballero) {
ataqueJugador = ((Caballero)jugador).ataqueRelampago();
System.out.println("Consigues un ATAQUE de " + ataqueJugador + " pts.");
}
else {
ataqueJugador = ((Mago)jugador).hechizoAtaque();
System.out.println("Consigues un ATAQUE de " + ataqueJugador + " pts.");
}
break;
case 4: //Hechizo sanación 50 puntos, solo sirve para Mago
if (jugador instanceof Caballero)
System.out.println("La acción no es válida. Pierdes tu turno");
else
((Mago)jugador).curar50puntos();
break;
default:
System.out.println("La acción no es válida. Pierdes tu turno");
}
pausa();
//A continuación elige acción la CPU
int accionCPU;
if (modoDificil)
accionCPU = CPUmodoDificil(ataqueJugador, defensaJugador);
else
accionCPU = CPUmodoNormal();
//Analizamos accion de CPU
switch(accionCPU) {
case 1:
ataqueCPU = CPU.atacar();
break;
case 2:
defensaCPU = CPU.defender();
break;
case 3:
ataqueCPU = CPU.ataqueRelampago();
}
pausa();
//Ambos Jugadores han actuado, evaluamos daños conseguidos
int danoRecibeJugador = ataqueCPU - defensaJugador;
int danoRecibeCPU = ataqueJugador - defensaCPU;
//Qué ha pasaso con Jugador
if (danoRecibeJugador > 0) {
System.out.println("\n" + jugador.getNombre() + " recibe daños.");
System.out.println("Pierde " + danoRecibeJugador + " puntos de vida.");
jugador.perderVida(danoRecibeJugador);
if (jugador.getVida() == 0)
System.out.println("¡" + jugador.getNombre() + " ha muerto!");
}
else {
//No recibe daños, comunicamos que ha conseguido bloquear ataque CPU
//Excepto si se debe a que CPU no atacó, por estar defendiendose.
if (accionCPU != 2)
System.out.println("\n" + jugador.getNombre() + " consigue bloquear ataque de "
+ CPU.getNombre());
}
//Qué ha pasado con CPU
if (danoRecibeCPU > 0) {
System.out.println("\n" + CPU.getNombre() + " recibe daños.");
System.out.println("Pierde " + danoRecibeCPU + " puntos de vida.");
CPU.perderVida(danoRecibeCPU);
if (CPU.getVida() == 0)
System.out.println("¡" + CPU.getNombre() + " ha muerto!");
}
else {
if (accionJugador == 1 || accionJugador == 3) {
System.out.println("\n" + CPU.getNombre() + " consigue bloquear ataque de "
+ jugador.getNombre());
}
}
pausa();
/*
* Acciones completadas, pero aún queda la probabilidad de que
* si el jugador humano es Mago, pueda recuperar un punto de vida.
* En caso de que el Mago estuviera muerto, ¡¡esto le da la posibilidad de resucitar!!
* aunque solo con un miserable punto de vida.
*/
if (jugador instanceof Mago) {
if (((Mago)jugador).recuperarVida()) {
//Comprobamos si estamos ante el milagro de la resurrección xD
if (jugador.getVida() == 1)
System.out.println("¡" + jugador.getNombre() + " ha resucitado!");

pausa();
}
}
/*
* Ahora sí se ha completado el turno.
* El bucle de combate se repetirá, excepto si alguien ha muerto
*/
}
//Fin bucle combate, comprobamos quien vive y quien no
if (jugador.getVida() > 0) {
System.out.println("\nVictoria para:");
System.out.println(jugador);
}
else if (CPU.getVida() > 0) {
System.out.println("\nVictoria para:");
System.out.println(CPU);
}
else //¡Ambos personajes han muerto!
System.out.println("\nNo hay ganador. Ambos jugadores se han destruido.");

System.out.println("\n\n\t\tFIN DE PROGRAMA");
}


private static void crearPersonaje() {
System.out.println("\t\tCREAR PERSONAJE");
System.out.println("\t\t----- ---------");
System.out.println("Clases disponibles:");
System.out.println("[1] -> Caballero");
System.out.println("[2] -> Mago");

System.out.print("Elige clase: ");
int clase = Integer.parseInt(teclado.nextLine());

System.out.print("\nNombre: ");
String nombre = teclado.nextLine();

System.out.print("Nivel de Ataque(min = 1/max = 10): ");
int ataque = Integer.parseInt(teclado.nextLine());
//Forzamos que el nivel escogido se ciña a los límites
if (ataque < 1)
ataque = 1;
else if (ataque > 10)
ataque = 10;

System.out.print("Nivel de Defensa(min = 1/max = 10): ");
int defensa = Integer.parseInt(teclado.nextLine());
if (defensa < 1)
defensa = 1;
else if (defensa > 10)
defensa = 10;

System.out.print("Nivel de Vida(min = 100/max = 1000): ");
int vida = Integer.parseInt(teclado.nextLine());
if (vida < 100)
vida = 100;
else if (vida > 1000)
vida = 1000;

//Tenemos todos los atributos necesarios, creamos Personaje
if (clase == 1) //Ha elegido Caballero
jugador = new Caballero(nombre, ataque, defensa, vida);
else //Mago
jugador = new Mago(nombre, ataque, defensa, vida);

System.out.println("\nEste es tu personaje:");
System.out.println(jugador);
pausa();

//Ahora creamos el Caballero CPU, con doble de potencia que el Jugador
CPU = new Caballero("Thanos", (ataque*2), (defensa*2), (vida*2));

System.out.println("Y este será tu rival:");
System.out.println(CPU);
System.out.println("Nivel de Ataque: " + CPU.getAtaque()
+ " / Nivel de Defensa: " + CPU.getDefensa());

pausa();
}

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
}

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());
}

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;
}

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)];
}


private static void pausa() {
System.out.println("\n\t\tPULSA ENTER PARA SEGUIR\n");
teclado.nextLine();
}
}

520
Bien, a ver, he vuelto a cambiar las clases de Personajes  :o

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.

Código: [Seleccionar]
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.

Código: [Seleccionar]
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);
}

}

Código: [Seleccionar]
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.
Código: [Seleccionar]
//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:

Código: [Seleccionar]
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

Código: [Seleccionar]
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:
Código: [Seleccionar]
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.
Código: [Seleccionar]
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..)

Páginas: 1 ... 21 22 23 24 25 [26] 27 28 29 30 31 ... 50

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