jueves, 22 de octubre de 2015

Crear juego RPG en C++ y Allegro 4 (23) Tiendas II

Aquí esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Continuamos con las tiendas.





Se crea un archivo nuevo llamado tiendas.h, donde irán todo lo referente a la tienda.

En el se crea una estructura llamada objetoventa, que contiene cuatro variables:
nid : para almacenar el id del objeto
id : almacena el id de la imagen del fichero dat
nombre: nombre del objeto.
precio: precio de venta.

Quedando de la siguiente forma:

struct objetoventa{
    int nid;   
    // id imagen   
    int id;
    char *nombre;   
    int precio;
};

vector vlista_tienda;

El vector vlista_tienda tendrá almacenado la lista de objetos que tiene la tienda para vender.

// la lista de objetos para que no se haga muy lento
// se crea con la imagen, nombre, precio
void lee_lista_objetos(){
    char contenido[255];
    
    packfile_password(NULL);
    PACKFILE *fichero;  
    
    objetoventa elemento;

    fichero = pack_fopen(clista_tienda,"r");
    
    while ( !pack_feof(fichero) ) 
    {
        pack_fgets( contenido, 255, fichero);
        elemento.nid = atoi( contenido );
        
        elemento.id = id_img_objeto( elemento.nid );
        
        elemento.nombre = nombre_objeto( elemento.nid );
        
        pack_fgets( contenido, 255, fichero);
        elemento.precio = atoi( contenido );        
            
        vlista_tienda.push_back( elemento );  
    }  

    pack_fclose(fichero);
};

La función lee_lista_objetos(), lee un archivo que viene dado por la variable clista_tienda. Este archivo tiene por cada objeto dos datos el id del objeto, y el precio de venta. Aunque en la lista vlista_tienda se almacenan cuatro datos, dos son cogidos del archivo que se esta leyendo y los otros se obtiene de la lista de objetos lobj, mediante las funciones id_img_objeto() y nombre_objeto(). Finalmente con el comando push_back() se añade la variable elemento al vector vlista_tienda.

void borra_lista_objetos(){
    
    int  it = 0;
    int max = vlista_tienda.size();
    while ( it <  max )
    {
          it++;
          vlista_tienda.pop_back();
    }        
}

Con esta función,  se borra todo el contenido del vector vlista_tienda.

void lee_tienda(int n){
    char contenido[255];
    
    packfile_password(NULL);
    PACKFILE *fichero;     

    fichero = pack_fopen("tiendas.txt","r");
    int num = 0;
    while ( !pack_feof(fichero) && num != n ) 
    {
        num++;
                  
        pack_fgets( contenido, 255, fichero);
        ifondo_tienda = atoi( contenido );
        
        pack_fgets( contenido, 255, fichero);
        x_tienda = atoi( contenido );
        
        pack_fgets( contenido, 255, fichero);
        y_tienda = atoi( contenido );        
        
        pack_fgets( clista_tienda, 255, fichero);      
    }  

    pack_fclose(fichero);
    borra_lista_objetos();
    lee_lista_objetos();
};


La función lee_tienda(), mediante el parámetro "n" se lee la tienda numero "n", del fichero tiendas.txt. Esto se ha hecho así para que se pueda tener variedad de tiendas, y sea fácil de crear.
El archivo tiendas.txt debe tener cuatro datos por cada tienda:
ifondo_tienda: contiene el id de la imagen de fondo de la tienda.
x_tienda, y_tienda: Es la posición donde se quiere que se muestre la tienda, de este modo podemos ajustar el texto con respecto a la imagen de fondo.
clista_tienda: Es el nombre del archivo que contiene la lista de objetos que se venden en dicha tienda.

Para nuestro ejemplo necesitaras tres archivos, el tiendas.txt que tiene la lista de tiendas, en nuestro caso dos tiendas. Y los archivos list01.txt y list02.txt que contiene cada uno los objetos que se vende en cada una de las tiendas.

Para descargar el archivo RAR haz clic aqui.


void pinta_tienda()
{
     if (muestra_tienda == true )   
     {
          MENSAJE tdesc;
          tdesc.crea("",font, 
                     x_tienda+5, y_tienda+415,
                     x_tienda+485, y_tienda+500);
                                                          
          if (swtienda == 0)
          {
              // muestra flecha e inicia a cero         
              swtienda = 1;     
              inicia_objeto = 0;
              moviendo = 0;
              tienda_estado = 1;
              swinv = 0;
              sel_obj = -1;
          } 
                         
          TBOTON comprar, vender, salir;
          TBOTON descripcion, precios;
          
          comprar.crear("Comprar",(FONT *)datosjuego[dftextos].dat );
          vender.crear("Vender",(FONT *)datosjuego[dftextos].dat ); 
          salir.crear("Salir",(FONT *)datosjuego[dftextos].dat ); 
          descripcion.crear("Descripcion",(FONT *)datosjuego[dftextos].dat ); 
          precios.crear("Precio",(FONT *)datosjuego[dftextos].dat ); 
                               
          masked_blit( (BITMAP*)datotiendas[ifondo_tienda].dat, buffer,
                       0,0,0,0, PANTALLA_ANCHO, PANTALLA_ALTO);
                       
          rect(buffer, x_tienda, y_tienda, x_tienda+490, y_tienda+372,0xffffff );                                    
          
          comprar.pinta( buffer, x_tienda+10, y_tienda+374, 0xffff00, 0xffffff );
          vender.pinta( buffer, x_tienda+125, y_tienda+374, 0xffff00, 0xffffff );
          salir.pinta( buffer, x_tienda+230, y_tienda+374, 0xffff00, 0xffffff );
          descripcion.pinta( buffer, x_tienda+34, y_tienda, 0xffff00, 0xffffff );
          precios.pinta( buffer, x_tienda+390, y_tienda, 0xffff00, 0xffffff );
           
    textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+305, y_tienda+374, 0xFFFFFF, -1, "Dinero %9d ", 
                   jugador.getdinero() );           
           
   if ( tienda_estado == 1 )
   {        
                       
          if ( vlista_tienda.size() > 10 )
          {

               rect(buffer, x_tienda+470, y_tienda, x_tienda+490, y_tienda+372,0xffffff );
               rect(buffer, x_tienda+470, y_tienda+30, x_tienda+490, y_tienda+342,0xffffff );
               triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
                             x_tienda+480,y_tienda+5,0xffffff ); 
               triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
                             x_tienda+480,y_tienda+367,0xffffff );  
               
               // barra central
               // distancia unidad
               float ny1 = ( 340.0 - 32.0 ) / vlista_tienda.size() ;
               // parte superior por donde se inicia
               int ny2 = int(inicia_objeto * ny1);
               // siempre se muestran 10 - longitud barra
               int ny3 = int(10.0 * ny1);

              
               rectfill(buffer, x_tienda+472, y_tienda+32+ny2, x_tienda+488, y_tienda+32+ny3+ny2, 0xffffff );                      

                                         
               if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&
                    mouse_y > y_tienda  && mouse_y < y_tienda+30)
               {
                    // esta situado encima de boton
                    triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
                             x_tienda+480,y_tienda+5,0xffff00 ); 
                    if ( mouse_b&1 )
                    {
                         // pulsa hacia arriba
                         inicia_objeto--;
                         if (inicia_objeto < 0 ) inicia_objeto = 0;
                    }         
               }     
               
               if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&
                    mouse_y > y_tienda+342  && mouse_y < y_tienda+372)
               {
                    // esta situado encima de boton
               triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
                             x_tienda+480,y_tienda+367,0xffff00 ); 
                    if ( mouse_b&1 )
                    {
                         // pulsa hacia arriba
                         inicia_objeto++;
                         if (inicia_objeto+10 > vlista_tienda.size() ) inicia_objeto--;
                    }         
               }                                                                        
          } 
          

          for ( int it=inicia_objeto; it < vlista_tienda.size() && it < inicia_objeto+10; it++ )  
          {
              int ty = y_tienda+((it+1)*34) - (inicia_objeto*34);
              masked_blit( (BITMAP*)datobjetos[vlista_tienda[it].id].dat, buffer,
                           0,0,x_tienda,ty, 32,32);
              
              textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+34, ty, 0xFFFFFF, -1, "%s", vlista_tienda[it].nombre );
              
              textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+367, ty, 0xFFFFFF, -1, "%8d", vlista_tienda[it].precio );                                         


                int numi = it - inicia_objeto;
                                                  
                if ( mouse_x > x_tienda+1    && mouse_x < x_tienda+470 &&
                     mouse_y > y_tienda+32+(numi*34) && mouse_y < y_tienda+64+(numi*34))
                {
                     rect(buffer, x_tienda+2, y_tienda+34+(numi*34),
                                  x_tienda+468, y_tienda+63+(numi*34),0xffff00 );
                     
                     
                     tdesc.cambia_texto( descripcion_objeto(vlista_tienda[it].nid) );
                     tdesc.pinta(buffer);
                     textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+274, y_tienda, 0xFFFFFF, -1, 
                                    "Tienes:%2d", jugador.cuantos_objetos(vlista_tienda[it].nid) );
                                      
                     if (  mouse_b&1 )
                     {
                           // intenta comprar objeto
                           // comprobar que existe hueco para la compra
                           // tiene dinero suficiente ?
                           if ( jugador.num_inventario() < 12 && 
                                jugador.getdinero() > vlista_tienda[it].precio )
                           {
                                sel_obj = it;
                           }else{
                              if ( swerror == 0 )
                              {    
                                 swerror = 1;
                              }
                           }      
                     }// fin se pulso el raton          
                }// fin dentro de un objeto               
              
          }
          
          if ( swerror == 2 ) swerror=0;
          if ( swerror == 1 && !mouse_b&1)
          {
              sonido_error(); 
              swerror=2;
          }
          
          
          if ( sel_obj != -1 && !mouse_b&1 )
          {
             jugador.menosdinero( vlista_tienda[sel_obj].precio );
             jugador.obtiene_objeto( vlista_tienda[sel_obj].nid );   
             sel_obj = -1; 
             sonido_boton();              
          }
                   
  }     
 // fin compras
 
 
 
 //inicia ventas
 if ( tienda_estado == 2 )
 {
      int obj_tienes = jugador.num_inventario();
      
      if ( obj_tienes == 0 )
      {
           textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, 
                          x_tienda+75, y_tienda+185, 0xFFFFFF, -1, "No tienes objetos en tu inventario" );           
      }
      
      
      
      if ( obj_tienes > 10 )
      {
               //pinta barra desplazamiento
               rect(buffer, x_tienda+470, y_tienda, x_tienda+490, y_tienda+372,0xffffff );
               rect(buffer, x_tienda+470, y_tienda+30, x_tienda+490, y_tienda+342,0xffffff );
               triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
                             x_tienda+480,y_tienda+5,0xffffff ); 
               triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
                             x_tienda+480,y_tienda+367,0xffffff );  

               // barra central
               // distancia unidad
               float ny1 = ( 340.0 - 32.0 ) / obj_tienes ;
               // parte superior por donde se inicia
               int ny2 = int(inicia_objeto * ny1);
               // siempre se muestran 10 - longitud barra
               int ny3 = int(10.0 * ny1);
          
 
               rectfill(buffer, x_tienda+472, y_tienda+32+ny2, x_tienda+488, y_tienda+32+ny3+ny2, 0xffffff );                      


               if ( mouse_x > x_tienda+470 && mouse_x < x_tienda+490 &&
                    mouse_y > y_tienda  && mouse_y < y_tienda+30)
               {
                    // esta situado encima de boton
                    triangle( buffer, x_tienda+473,y_tienda+25, x_tienda+487,y_tienda+25,
                             x_tienda+480,y_tienda+5,0xffff00 ); 
                    if ( mouse_b&1 )
                    {
                         // pulsa hacia arriba
                         inicia_objeto--;
                         if (inicia_objeto < 0 ) inicia_objeto = 0;
                    }         
               }     
               
               if ( mouse_x > x_tienda+460 && mouse_x < x_tienda+490 &&
                    mouse_y > y_tienda+342  && mouse_y < y_tienda+372)
               {
                    // esta situado encima de boton
               triangle( buffer, x_tienda+473,y_tienda+347, x_tienda+487,y_tienda+347,
                             x_tienda+480,y_tienda+367,0xffff00 ); 
                    if ( mouse_b&1 )
                    {
                         // pulsa hacia arriba
                         inicia_objeto++;
                         if (inicia_objeto+10 > obj_tienes ) inicia_objeto= obj_tienes - 10;
                    }         
               } 
                                                                                                                    
          } 

                            
          int it = 0;
          for ( int i=0; i < 12; i++ )
          {
              if ( jugador.getinventario(i) != 0 && 
                   it < 10+inicia_objeto && it >= inicia_objeto)
              {
                   int mid = jugador.getinventario(i);
                   int ty =  y_tienda+((it+1)*34) - (inicia_objeto*34);
                   
                   masked_blit( (BITMAP*)datobjetos[id_img_objeto(mid)].dat, buffer,
                                0,0,x_tienda,ty, 32,32);
                           
              textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+34, ty, 0xFFFFFF, -1, "%s", nombre_objeto(mid) );
              
              textprintf_ex( buffer, (FONT *)datosjuego[dftextos].dat, x_tienda+367, ty, 0xFFFFFF, -1, "%8d", precio_objeto(mid) );                               
                   
  
             int numi = it - inicia_objeto;
                                              
            if ( mouse_x > x_tienda+1    && mouse_x < x_tienda+470 &&
                 mouse_y > y_tienda+32+(numi*34) && mouse_y < y_tienda+64+(numi*34))
            {
                 rect(buffer, x_tienda+2, y_tienda+34+(numi*34),
                              x_tienda+468, y_tienda+63+(numi*34),0xffff00 );
                 
                 
                 tdesc.cambia_texto( descripcion_objeto(jugador.getinventario(i)) );
                 tdesc.pinta(buffer);                 

                 
                 if (  mouse_b&1 )
                 {
                       // vende objeto
                       sel_obj = i;      
                 }// fin se pulso el raton  
                         
            }// fin dentro de un objeto 
              
                 it++;  
              }else{
                    
                 if ( jugador.getinventario(i) != 0  && it < inicia_objeto) it++;
              }
          }
          
          if ( sel_obj != -1 && !mouse_b&1 )
          {
               
             jugador.masdinero( precio_objeto(jugador.getinventario(sel_obj)) );
             // objeto con id cero no existe
             jugador.pon_inventario( sel_obj, 0);   
             sel_obj = -1; 
             sonido_boton();  
             obj_tienes = jugador.num_inventario();
             
             if ( obj_tienes == 11 ) inicia_objeto = 1;
             if ( inicia_objeto > 0 && obj_tienes < 11 ) inicia_objeto=0;  
             
                       
          }          
          
          
 }
 // fin ventas
 
 
         
          if ( comprar.ratonb() && tienda_estado != 1)
          {
               tienda_estado = 1;
               inicia_objeto = 0;
               sel_obj = -1;
               sonido_boton();
          }
          
          if ( vender.ratonb() && tienda_estado != 2)
          {
               tienda_estado = 2;
               inicia_objeto = 0;
               sel_obj = -1; 
               sonido_boton();              
          }
                    
          if ( salir.ratonb() ){
                // oculta la flecha y sale
                swtienda = 0;                       
                muestra_tienda = false;  
                sonido_boton();
          }      
     }  
}


La función pinta_tienda(), es la que se encarga de todo referente a la tienda.
Primero se comprueba si se muestra la tienda. Se crea una variable tipo MENSAJE que se encarga de mostrar la descripción de los objetos. Cuando entra la primera vez swtienda vale cero y por tanto se inicializan todas la variables utilizadas en la función tienda. Se inicializan a cero las variables inicia_objeto, swinv, Y se inicializa a 1 la variable tienda_estado. La variable swtienda pasa a valer 1, para que la próxima vez que entre no se vuelvan a inicializar las variables. Y la variable sel_obj se inicializa a -1, para indicar que no hay ninguno seleccionado.

Se crean tres botones, comprar, vender y salir. Que se declaran del tipo TBOTON. Al igual que las variables descripción, y precios. Y se inicializan utilizando la función crear, que definen el texto que tienen y el tipo de letra que se utilizará.

Mediante la variable tienda_estado, se consideran dos posibles valores: 1 para cuando se muestra el listado de comprar, y 2 para cuando se muestra nuestros objetos para vender.

En el caso de que se tengan que mostrar mas de 10 objetos entonces se pinta la barra de desplazamiento, que nos servirá para movernos por la lista de objetos.

Todo se muestra con una posición relativa a las variables x_tienda, y_tienda. Para que se pueda situar donde queramos con tan solo cambiar el valor de estas variables.

En la opción comprar, la lista a mostrar es vlista_tienda. Y en la opción de venta, la lista que se muestra es el inventario del jugador.

Si deseas descargar el archivo tiendas.h en RAR haz clic aqui.

Y hasta aqui hemos llegado por hoy, aun no ha acabado esto de la tienda. Aunque ya tenemos el codigo principal, hay que intergrarlo en el juego.

6 comentarios:

  1. Hola. ¿Podrías explicar la parte que falta para que la tienda quede acabada? Gracias.

    ResponderEliminar
  2. Hay un error con el id de los objetos a comprar, según veo en el condicional donde se evalúa sel_obj, justo antes del comentario de "//fin compras", el valor a pasarle a la función jugador.obtiene_objeto() por alguna razón es el id de vlista_tienda[sel_obj] y no el nid como estaba puesto

    ResponderEliminar
    Respuestas
    1. Te refieres a esta línea:
      jugador.obtiene_objeto( vlista_tienda[sel_obj].nid );

      ¿ cual es el error ?

      Eliminar
    2. No sé si sea por todos los arreglos que he tenido que hacerle al código para que me funcione, pero si lo dejo como está en la explicación, cuando compro los objetos y salgo de la tienda estos se ven en el inventario con un icono diferente al que deberían tener y al que sale en la tienda, e incluso algunos salen invisibles. Supongo que esto es, porque en las funciones de inventario y en la inicializacion del personaje, los objetos se representan por su id y no por su nid (en la función player::inicia() la armadura es asignada a 4 y el arma a 5, que son los id's de ambos objetos en el archivo objetos.txt, mientras que sus nid's respectivos son 5 y 10)

      Eliminar
    3. Ahora que lo veo, esa solución que propuse solo enmienda el error de manera temporal, pero el sigue estando ahí. La manera de resolverlo completamente, según he ido descubriendo (y con esta si me funciona todo perfectamente) es dejando todos los nid's y no cambiarlos por id, al contrario, hacer que los objetos se identifiquen por su nid y no por su id, como pasa en algunos lugares, por ejemplo player.inicia(). En dicha función deberán asignarse a las variables armadura y arma los valores 10 y 5 respectivamente, y en todo lugar en que se quiera acceder a su imagen como objeto, usar la función id_img_objeto(), esto incluye en la función pinta_inventario() en casi todos los masked_blit() y en la función pintar_pantalla() de main.cpp, para pintar el objeto que se toma sobre el cursor. El objetivo es que todos los objetos se representen por su nid, y que no haya algunos que lo hagan por su id y otros por su nid, como pasaba antes, que era lo que al fin y al cabo ocasionaba tantos errores y confusiones. Y el id, ya que en la práctica es solo el numero de la imagen del objeto, ponerle otro nombre, y al nid ponerle solo id para que no hayan dos id's y surjan problemas como este

      Eliminar

Antes de publicar un comentario

Todos los comentarios que se realicen en el blog son moderados.

Debido a esto es muy probable que tu comentario no aparezca de inmediato ya que previamente debe ser revisado para evitar un mal uso (SPAM).

Podrán realizar comentario cualquiera que tenga una cuenta de Google.

Related Posts Plugin for WordPress, Blogger...