miércoles, 14 de octubre de 2015

Crear juego RPG en C++ y Allegro 4 (21) Inventario II

Otra entrega del curso crea tu juego RPG en C++ y Allegro. En esta entrega se continua el inventario.




Objetivo

En esta entrega, haremos que se pueda equipar el personaje con algunos de los objetos recibidos, y poder cambiar los objetos de lugar.



Programación

En el archivo global.h, se añade lo siguiente:

DATAFILE *datobjetos;

// control raton en inventario
int swraton;
int nsel;
int nid;

Se crea una nueva variable del tipo DATAFILE para el manejo del nuevo fichero de objetos.
Con estas tres variables, se va a controlar si se ha pulsado el boton del raton swraton, en que casilla se ha pulsado nsel, y cual es el objeto en caso de tener nid.

La función pintar_pantalla(), se borra de global.h, para ponerlo en el main.cpp.

En el archivo audio.h, se añade dos funciones:

void sonido_boton(){
    play_sample ( (SAMPLE *)datosjuego[dsboton1].dat, 110,128, 1100, 0 );   
}

void sonido_boton2(){
    play_sample ( (SAMPLE *)datosjuego[dsboton2].dat, 100,128, 1400, 0 );   
}

La función sonido_boton se ha utilizado para poner sonido cuando se suelta un objeto. La función sonido_boton2 se ha utilizado para poner el sonido cuando se coje el objeto. Tanto dsboton1 y dsboton2 son nuevos sonidos que se han añadido al fichero DAT.

En el archivo players.h, se han añadido todo lo referente a la equipación que lleva actualmente el personaje y algunas funciones para acceder y manejar las nuevas variables del equipo y el inventario.

Añadir lo siguiente en la parte privada de la clase player.

    // equipacion actual
    int casco;
    int armadura;
    int arma;
    int anillo;

Añadir lo siguiente al final, en la parte pública de la clase player.

       void cambia_inventario(int p1, int p2 );
       void pon_inventario(int pos, int id){ inventario[pos]=id; };
       
       int getarma(){ return arma; };
       int getarmadura(){ return armadura; };       
       int getcasco(){ return casco; };
       int getanillo(){ return anillo; };
       
       void pon_arma(int id){ arma=id; };
       void pon_armadura(int id);
       void pon_casco(int id){ casco=id; };
       void pon_anillo(int id){ anillo=id; };

Estas funciones son muy básicas, las "get" para obtener el valor, y las funciones "pon" para asignar un valor. La funcion pon_inventario(pos, id),  segun la posicion dada, coloca el objeto id en el inventario.

void player::cambia_inventario(int p1, int p2 )
{
     int t;
     t = inventario[p1];
     inventario[p1] = inventario[p2];
     inventario[p2] = t;
};

La funcion player::cambia_inventario(), intercambia dos posiciones del inventario.

void player::pon_armadura(int id)
{ 
     armadura = id; 
     if ( id != -1 )
          prota  = (BITMAP *)datobjetos[equipo_personaje(armadura)].dat; 
     if ( id == -1 )
          prota  = (BITMAP *)datosjuego[dipersonaje0].dat;
};

La función player::pon_armadura(), es un poco mas compleja que las demás ya que al quitar o cambiar la armadura tambien se cambia la imagen del personaje. En el caso de que id sea distinto de -1 indica que se esta equipando una armadura. En el caso de que id sea -1, indica de que no tiene ninguna armadura, por tanto esta desnudo.

En la función player::inicia(), se inicializan las nuevas variables para que el personaje tenga una equipación inicial.

    casco = -1;
    armadura = 4;
    arma = 5;
    anillo = -1;
      
    prota  = (BITMAP *)datobjetos[equipo_personaje(armadura)].dat; 

Según estos datos, estamos indicando de que no tendrá casco (-1), la armadura será la (4) el traje normal, el arma el (5) la espada, y anillo no tiene (-1). Y finalmente se asigna a la imagen del personaje, la imagen correspondiente a la armadura seleccionada.

Se crea un nuevo archivo llamado misobjetos.h, que se encargará del manejo de los objetos.

// misobjetos.h

#include < vector > 
using namespace std;

struct m_objetos{         
    int id;   
    int tipo;
    char nombre[50];
    int img;
};

vector< m_objetos > lobj;


int tipo( int id ){
    for(int i = 0; i < lobj.size(); i++)
    {
            if ( lobj[i].id == id ) return lobj[i].tipo;
    }  
    return -1;
}

int equipo_personaje( int id ){
    for(int i = 0; i < lobj.size(); i++)
    {
            if ( lobj[i].id == id && lobj[i].tipo < 5 ) return lobj[i].img;
    }  
    return -1;  
}

void lee_objetos(){
    char contenido[255];

    m_objetos temp;
    
    packfile_password(NULL);
    PACKFILE *fichero;     

    fichero = pack_fopen("objetos.txt","r");
    
    while ( !pack_feof(fichero) ) 
    {
        pack_fgets( contenido, 255, fichero);
        temp.id = atoi( contenido );
        
        pack_fgets( contenido, 255, fichero);
        temp.tipo = atoi( contenido );
        
        pack_fgets( temp.nombre, 255, fichero);
        
        pack_fgets( contenido, 255, fichero);
        temp.img = atoi( contenido );
    
        lobj.push_back( temp );  
    }  

    pack_fclose(fichero);
};


Se crea una estructura llamada m_objetos, que contiene id, tipo, nombre y img. Se crea un vector de la estructura que se llama lboj, en la cual se va a guardar todos los objetos.

La funcion tipo(), devuelve el tipo del objeto segun el id recibido por parámetro.

La función equipo_personaje(), devuelve el valor de img del objeto si es un tipo de objeto equipable. En caso contrario devuelve -1 para indicar que no se ha encontrado.

La función lee_objetos(), se encarga de leer un archivo objetos.txt, en el cual estan almacenados todos los objetos que se utilizaran en el juego. Conteniendo de cada objeto el id, tipo, nombre y imagen. El id es el numero de la imagen dentro del fichero DAT, el tipo se utiliza para de que tipo de objeto es:

  1. armas
  2. cascos
  3. armaduras
  4. anillos
  5. pócimas
  6. otros
La img, no es mas que el numero de una imagen dentro del fichero DAT de objetos.

En el archivo mijuego.h, se suprime todo lo referente a los objetos, ya que se ha puesto en el nuevo fichero misobjetos.h.

En la función carga_juego(), antes de inicializar la variable jugador se carga el nuevo archivo objetos.dat, y se realiza la llamada a la función lee_objetos(). Y se inicializa la variable swraton a -1. Se debe borrar el bucle for que había para cargar los objetos.

    datobjetos = load_datafile("objetos.dat");
    if ( !datobjetos ){
       allegro_message("Error: archivo objetos.dat no encontrado\n%s\n", allegro_error);       
    }     
     
    lee_objetos(); 
                        
    jugador.inicia();

    swraton=-1;

La función pinta_inventario(), cambia tanto, que he decidido ponerla entera.

 
void pinta_inventario()
{
     if ( swinv >  0 )
     {
                    
          int jx = jugador.getx();
          int jy = jugador.gety();
          int posx;
          int posy;
          
          if ( jx > PANTALLA_ANCHO/2 ){
               jx = (PANTALLA_ANCHO/4) - 160;
          }else{
               jx = (PANTALLA_ANCHO*3/4) - 160; 
          }
          
          if ( jy > PANTALLA_ALTO/2 ){
               jy = (PANTALLA_ALTO/4) - 81;
          }else{
               jy = (PANTALLA_ALTO*3/4) - 81; 
          }          
       
          // se muestra fondo inventario
          masked_blit( (BITMAP *)datosjuego[diinventario].dat, buffer, 0,0, jx,jy, 320,162);
    
    if ( swraton == 1 && mouse_b&2 )
    {
         swraton = -2; 
    }
          
          masked_blit( jugador.getimg(), buffer, 32,0, jx+65,jy+60, 32,32); 
          
    if ( jugador.getcasco() != -1 )
    {
         masked_blit((BITMAP *)datobjetos[equipo_personaje(jugador.getcasco())].dat, buffer, 32, 0, jx+65, jy+60, 32,32); 
    }      
    
    
          posx=jx+116;
          posy=jy+40;
          if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
          {
              if ( mouse_b&1 && swraton != -2)
               {
                    // se ha pulsado dentro de un hueco
                    if ( swraton == -1 )
                    {
                         // primer clic
                         swraton = 1;
                         nsel = -4;
                         nid = jugador.getanillo(); 
                         if ( nid == -1 ){
                               swraton = -1;
                         }else{
                            sonido_boton2();   
                         }
                    }else{
                         
                         if ( nid != jugador.getanillo() && tipo(nid) == 4 )
                         {                                    
                              // cambia objeto de sitio
                              if ( jugador.getanillo() != -1 ){
                                 jugador.pon_inventario(nsel,jugador.getanillo());
                              }else{
                                 jugador.pon_inventario(nsel,0);   
                              }
                              
                                 
                              jugador.pon_anillo(nid);
                              sonido_boton();

                              swraton = -2;  
                          } 
                    }
               }               
          }
        
          
          
          if ( jugador.getanillo() != -1 )
          {
             masked_blit( (BITMAP *)datobjetos[jugador.getanillo()].dat, buffer, 0,0, jx+116,jy+40, 32,32); 
          }          
          
          posx=jx+116;
          posy=jy+74;
          if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
          {
              if ( mouse_b&1 && swraton != -2)
               {
                    // se ha pulsado dentro de un hueco
                    if ( swraton == -1 )
                    {
                         // primer clic

                         swraton = 1;
                         nsel = -1;
                         nid = jugador.getarmadura(); 
                         if ( nid == -1 ){
                               swraton = -1;
                         }else{
                            sonido_boton2();   
                         }
                    }else{
                         
                         if ( nid != jugador.getarmadura() && tipo(nid) == 3 )
                         {                                       
                              // cambia objeto de sitio
                              if ( jugador.getarmadura() != -1 ){
                                 jugador.pon_inventario(nsel,jugador.getarmadura());
                              }else{
                                 jugador.pon_inventario(nsel,0);   
                              }
                              
                              jugador.pon_armadura(nid);
                              sonido_boton();

                              swraton = -2;  
                          } 
                    }
               }               
          }
                 
          if ( jugador.getarmadura() != -1 )
          {
             masked_blit( (BITMAP *)datobjetos[jugador.getarmadura()].dat, buffer, 0,0, jx+116,jy+74, 32,32); 
          }
          

          posx=jx+13;
          posy=jy+74;
          if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
          {
              if ( mouse_b&1 && swraton != -2)
               {
                    // se ha pulsado dentro de un hueco
                    if ( swraton == -1 )
                    {
                         // primer clic

                         swraton = 1;
                         nsel = -2;
                         nid = jugador.getarma(); 
                         if ( nid == -1 ){
                               swraton = -1;
                         }else{
                            sonido_boton2();   
                         }
                    }else{
                         
                         if ( nid != jugador.getarma() && tipo(nid) == 1 )
                         {                                       
                              // cambia objeto de sitio
                              if ( jugador.getarma() != -1 ){
                                 jugador.pon_inventario(nsel,jugador.getarma());
                              }else{
                                 jugador.pon_inventario(nsel,0);   
                              }
                                                            
                              jugador.pon_arma(nid);
                              sonido_boton();

                              swraton = -2;  
                          } 
                    }
               }               
          }          
          
          
          if ( jugador.getarma() != -1 )
          {   
             masked_blit( (BITMAP *)datobjetos[jugador.getarma()].dat, buffer, 0,0, jx+13,jy+74, 32,32);          
          }
          
          posx=jx+13;
          posy=jy+40;
          if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
          {
              if ( mouse_b&1 && swraton != -2)
               {
                    // se ha pulsado dentro de un hueco
                    if ( swraton == -1 )
                    {
                         // primer clic

                         swraton = 1;
                         nsel = -3;
                         nid = jugador.getcasco(); 
                         if ( nid == -1 ){
                               swraton = -1;
                         }else{
                            sonido_boton2();   
                         }
                    }else{
                         
                         if ( nid != jugador.getcasco() && tipo(nid) == 2 )
                         {                                       
                              // cambia objeto de sitio
                              if ( jugador.getcasco() != -1 ){
                                 jugador.pon_inventario(nsel,jugador.getcasco());
                              }else{
                                 jugador.pon_inventario(nsel,0);   
                              }
                              
                                 
                              jugador.pon_casco(nid);
                              sonido_boton();

                              swraton = -2;  
                          } 
                    }
               }               
          }           
          
          if ( jugador.getcasco() != -1 )
          {   
             masked_blit( (BITMAP *)datobjetos[jugador.getcasco()].dat, buffer, 0,0, jx+13,jy+40, 32,32);          
          }    
                
          int id;
          for ( int i=0; i < 4; i++){
              for ( int j=0; j < 3; j++){
                  int num = (j*4) + i ;
                  id = jugador.getinventario( num );  
                  posx = jx + 172 + i*34;
                  posy = jy + 40 + j*34;               
                  if ( id != 0 ){                      
                      masked_blit( (BITMAP *)datobjetos[id].dat, buffer, 0,0, posx,posy, 32,32);                                             
                  }
                  if ( mouse_x > posx && mouse_x < posx+32 && mouse_y > posy && mouse_y < posy+32 )
                  {
                       if ( mouse_b&1 && swraton != -2)
                       {
                            // se ha pulsado dentro de un hueco
                            if ( swraton == -1 )
                            {
                                 // primer clic
                                 if ( id != 0 )
                                 {
                                      swraton = 1; 
                                      nsel = num;
                                      nid  = id;
                                      sonido_boton2();
                                 }
                            }else{
                                
                               if ( nsel >= 0 )
                               {  
                                 if ( nsel != num && nsel != -1)
                                 { 
                                      // cambia objeto de sitio
                                      jugador.cambia_inventario(nsel,num);
                                      sonido_boton();
                                      swraton = -2;  
                                  } 
                               }else{   
                                  
                                 // ha quitado algo del ekipo
                                 if ( nsel == -1 && (tipo(id) == 3 || id == 0) )
                                 {
                                      // armadura
                                      jugador.pon_inventario( num, nid );
                                      if ( id == 0 ) id = -1;
                                      jugador.pon_armadura( id ); 
                                      sonido_boton();
                                      swraton = -2;                                     
                                 }  
                                 if ( nsel == -2 && (tipo(id) == 1 || id == 0) )
                                 {
                                      // arma
                                      jugador.pon_inventario( num, nid );
                                      if ( id == 0 ) id = -1;
                                      jugador.pon_arma( id ); 
                                      sonido_boton();
                                      swraton = -2;                                     
                                 }                                  
                                 if ( nsel == -3 && (tipo(id) == 2 || id == 0) )
                                 {
                                      // arma
                                      jugador.pon_inventario( num, nid );
                                      if ( id == 0 ) id = -1;
                                      jugador.pon_casco( id ); 
                                      sonido_boton();
                                      swraton = -2;                                     
                                 }  
                                 
                                 if ( nsel == -4 && (tipo(id) == 4 || id == 0) )
                                 {
                                      // arma
                                      jugador.pon_inventario( num, nid );
                                      if ( id == 0 ) id = -1;
                                      jugador.pon_anillo( id ); 
                                      sonido_boton();
                                      swraton = -2;                                     
                                 }                                   
                               }  
                            }
                       }
                            
                  }
                  if ( swraton == -2 && !mouse_b&1 ) swraton=-1;
              }    
          }
          
          if ( !key[KEY_I]) swinv = 2;
     }
}


En el archivo main.cpp, se añade la funcion pintar_pantalla().

// Copiar el buffer a la pantalla del juego (screen)
void pintar_pantalla()
{
        
    if ( swinv > 0 )
    {
         masked_blit( (BITMAP *)datosjuego[dicursor].dat, buffer, 0,0,mouse_x, mouse_y, 16,16);
    }  
    if ( swinv > 0 && swraton > -1 )
    {
         masked_blit( (BITMAP *)datobjetos[nid].dat, buffer, 0,0,mouse_x, mouse_y, 32,32);
    }
    
    blit(buffer, screen, 0, 0, 0, 0, PANTALLA_ANCHO, PANTALLA_ALTO);
} 

En esta función se ha añadido, que mediante la variable swinv se muestre la flecha del raton. Esto es para que cuando se muestre el inventario, se muestre la flecha del raton y si se pulsa sobre un objeto se pinte el objeto donde esta el cursor.

En el inicio del main.cpp, se añade el include del nuevo archivo.

                                                             
#include < allegro.h > 
#include "datosjuego.h"
#include "global.h"
#include "audio.h"
#include "misobjetos.h"
#include "dialogos.h"
#include "players.h"
#include "mijuego.h"

En la función inicia_allegro(), se debe inicializar el raton que se añade justo despues de inicializar el teclado.

 allegro_init();  
 install_timer();
 install_keyboard();   
 install_mouse();

En el archivo npc.h, se cambia lo referente al objeto ya que se ha cambiado el formato y uso. En la función enemigo::herida(), se elimina la siguiente linea:

           int q = objetos[rand()%6].id; 

Para ser sustituida por las siguientes:

           int q2 = lobj.size();
           int q = lobj[rand()%q2].id;

Ya que ahora se utiliza lobj para almacenar la lista de objetos.

Llegados a este puntos, solo nos faltan algunos archivos como son:

  • objetos.dat: contiene las nuevas imagenes de los objetos
  • objetos.txt: contiene información de los objetos (id, tipo, nombre, img).
  • datosjuego.dat contiene el material multimedia del juego ( imagenes, sonidos, etc ).

9 comentarios:

  1. tuve un roblema con el raton, cuando abro el inventario, se queda pegado en la esquina de mi pantalla, yo cambie mi inventario por KEY_E y en este código cambie al final de pinta_inventario, igual el key_I por mi KEY_E, no se por que se queda pegado

    ResponderEliminar
    Respuestas
    1. Eso se debe a que no esta instalado el mouse, debes añadir en el main despues del install_keyboard(); , install_mouse(); ... es algo que me faltaba ... gracias por avisar.

      Eliminar
    2. mmm muy obvia la cosa, si tenia que instalar el teclado igual tenia que instalar el mouse, tengo mas pregunta entro al foro y hago un tema? si es asi donde seria?, ya que no veo un lugar que diga "Preguntas", solo enuentro "videos" , "muestras" y "donaciones" Gracias

      Eliminar
    3. En el apartado "Aportes y Dudas en General" y luego dentro de "Lenguajes de escritorio"

      Eliminar
  2. Hola, buenas, tengo un problema al llegar aquí. Tengo todos los archivos descargados, pero me falta el header de los dos .dat... ¿Cómo los consigo, o en su defecto, podrías subirlos para descargarlos? Muchas gracias, me está siendo de mucha ayuda este curso :D

    ResponderEliminar
    Respuestas
    1. Gracias por tu ayuda, tienes razón falta el archivo datosjuego.h. Ya lo he añadido al curso para que todos puedan descargarlo. De objetos.dat no requiere el archivo.

      Eliminar
    2. Muchísimas gracias :D

      Eliminar
  3. una pregunta como hago para limpiar una imagen.dat en el buffer.
    yo lo tengo así pero me genera un error ..::clear((BITMAP*)person[1].dat);::.. esta es la forma correcto.

    ResponderEliminar
    Respuestas
    1. El funcionamiento es facil, tu tienes las imagenes en el archivo .dat y lo vas pegando en el buffer, que es lo que finalmente muestra por pantalla. Tu puedes hacer clear_bitmap(buffer) , con esto borras el contenido del buffer. Pero lo que tu intentas hacer es borrar el contenido de una imagen del archivo.dat y eso no es posible.

      Eliminar