lunes, 19 de octubre de 2015

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

Aquí esta una nueva entrega del curso crea tu juego RPG en C++ y Allegro. Dedicada a explicar como programar una tienda.






Objetivo
Se trata de crear un npc con el que se hable y te de opción a comprar objetos o vender los objetos que tengamos en nuestro inventario. En esta primera parte nos vamos a centrar en los objetos y sus características.



Programación
Para crear la tienda debemos tener una lista de objetos. De cada objeto debemos tener un mínimo de información, el nombre, la imagen, descripción, etc. Todo código referente a los objetos se pondrá en el archivo misobjetos.h. Este archivo cambia mucho con respecto al anterior, por ello he decidido  poner todo su contenido.

Para almacenar estos objetos se crea una estructura llamada m_objetos.

struct m_objetos{  
    int nid;           
    int id;   
    int tipo;
    char nombre[30];
    char descripcion[255];
    int img;
    int precio;
};

vector< m_objetos > lobj;

nid: es un número para distinguir a los objetos. De esta manera si queremos guardar un objeto en el inventario, solo se guarda este dato. Ya que mediante este dato se puede acceder al resto de información.
id: es el id de la imagen almacenada en el archivo objetos.dat
tipo: Indica de que tipo de archivo se trata:

  1. armas
  2. cascos
  3. armaduras
  4. anillos
  5. pócimas
  6. otros
nombre: contiene el nombre del objeto, con un máximo de 30 caracteres.
descripción: contiene la descripción del objeto, teniendo un limite de 255 caracteres.
img: es el id de una imagen almacenada en el archivo objetos.dat, que será utilizada en el caso de que sea un objeto equipable, es decir, que el tipo sea entre 1 y 4.
precio: es el precio del objeto cuando se vende en la tienda.
En el archivo misobjetos.h, se pone las siguientes funciones:

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


char* descripcion_objeto( int id )
{
    for(int i = 0; i < lobj.size(); i++)
    {
            if ( lobj[i].nid == id ) return lobj[i].descripcion;
    }  
    return "";
}

char* nombre_objeto( int id )
{
    for(int i = 0; i < lobj.size(); i++)
    {
            if ( lobj[i].nid == id ) return lobj[i].nombre;
    }  
    return "";
}

int precio_objeto( int id )
{
    for(int i = 0; i < lobj.size(); i++)
    {
            if ( lobj[i].nid == id ) return lobj[i].precio;
    }  
    return 0;    
}


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

int equipo_personaje( int id ){
    for(int i = 0; i < lobj.size(); i++)
    {
            if ( lobj[i].nid == 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.nid = atoi( contenido );          
          
        pack_fgets( contenido, 255, fichero);
        temp.id = atoi( contenido );
        
        pack_fgets( contenido, 255, fichero);
        temp.tipo = atoi( contenido );
        
        pack_fgets( contenido, 255, fichero);
        strncpy( temp.nombre, contenido, 29 );
        temp.nombre[30]='\0';
        
        pack_fgets( temp.descripcion, 255, fichero);
        
        pack_fgets( contenido, 255, fichero);
        temp.img = atoi( contenido );
        
        pack_fgets( contenido, 255, fichero);
        temp.precio = atoi( contenido );       
    
        lobj.push_back( temp );  
    }  

    pack_fclose(fichero);
};

La función id_img_objeto(), recibe un id y devuelve el id de la imagen almacenada.
La función descripcion_objeto(), recibe un id de objeto y devuelve su descripción.
La función nombre_objeto(), recibe un id de objeto y devuelve el nombre.
La función precio_objeto(), recibe un id de objeto y devuelve el precio.
La función tipo(), recibe un id de objeto y devuelve el tipo.
La función equipo_personaje(), recibe un id de objeto y devuelve el id de la imagen si el objeto es equipáble.

Todas estas funciones, actúan del mismo modo. Reciben un id del objeto y van recorriendo el vector en busca del objeto que tenga dicho id, y devuelve lo que se solicita de dicho objeto.

La función lee_objetos(), accede al archivo objetos.txt y vuelca su contenido a la memoria almacenándola en la variable lobj, que contendrá la lista de objetos.

El archivo objetos.txt debe tener unas características especiales, para que el programa funcione y no de ningún error.

Por cada objeto debe tener:
un entero que indique el id
un entero para indicar el id de la imagen del objeto
un entero para indicar el tipo el cual debe tener un valor entre el 1 y el 6.
una cadena de caracteres para indicar el nombre, a ser posible que no supere los 30 caracteres.
una cadena de caracteres para indicar la descripción, con un máximo de 255 caracteres.
un entero para indicar el id de la imagen secundaria
un entero para indicar el precio del objeto.

Ejemplo de un objeto:

1
0
6
Margarita
flor del campo
0
1

A continuación dejo el archivo que se utiliza para nuestro ejemplo, pero si uno quiere puede modificarlo para añadir mas objetos, cambiar nombres, descripciones, precios, etc. Se recomienda que no se borren estos primeros objetos, ya que algunos son utilizados por el jugador al empezar.

Haz clic aqui para descargar archivo objetos.txt en RAR.

Mediante la instrucción pack_fgets, se va obteniendo una línea del archivo. Y según la línea que sea se realiza una operación diferente. Se considera que una línea no puede superar los 255 caracteres, en el caso de que en el archivo exista una línea supere este valor el programa dará un error.
Cuando se lee el nombre, aunque se obtiene la línea entera de 255 caracteres, el programa guarda en el nombre solo los 29 primeros caracteres e ignora los demás. El carácter 30 se le asigna el valor de fin de línea.
En la mayoría de los casos aunque se lee una cadena de caracteres es necesario transformarlo en un formato numérico, para ellos se utiliza el comando atoi(), que se encarga de transformar una cadena de caracteres a un entero.


Al inicio del archivo misobjetos.h se debe añadir la librería vector que se utiliza en nuestras funciones.

#include < vector > 
using namespace std;


Si deseas tener el archivo misobjetos.h, puedes descargar el RAR haciendo clic aqui.

Aunque se ha variado la clase objeto, las imágenes no se han tocado, por tanto se mantiene el archivo objetos.dat del tema anterior.

Se crea un nuevo archivo llamado botones.h, que contendrá todo lo necesario para crear botones de texto.

/* botones.h
   F. Finalizado : 22-12-12
   Autor: KodayGames
      
   clase TBOTON
   para crear texto como  boton
*/


#ifndef _BOTONES_H_
#define _BOTONES_H_

#include < allegro.h >


class TBOTON{
      public:
             void crear(const char* text, const FONT* bfuente);
             void pinta(BITMAP* bfondo, int x, int y, int color1, int color2);
             bool ratonb();
 
      private:
              int bx, by;
              int bpulsa;
              int bancho,balto;
              const char* btext;
              const FONT* bfuente;
};

// incializa las variables de la clase
void TBOTON::crear(const char* text, const FONT* fuente)
{
       btext = text;   
       bpulsa = 0;
       bfuente = fuente;
       bancho = text_length(bfuente, btext);
       balto  = text_height(bfuente);       
};    

// muestra en pantalla el texto, sobre el fondo indicado, con la fuente indicada, en la posicion x,y
// con el color1 cuando esta señalado y color2 cuando no
void TBOTON::pinta(BITMAP* bfondo, int x, int y, int color1, int color2)
{
     bx=x;
     by=y;

     if (mouse_x >= bx && mouse_x < bx+bancho+1 && mouse_y >= by && mouse_y < by+balto+1)
     {
         textout_ex(bfondo, bfuente, btext, bx, by, color1, -1);
     }else{
         textout_ex(bfondo, bfuente, btext, bx, by, color2, -1);
     }           
};

// comprueba si el raton esta situado encima de nuestro texto y a pulsado el boton del raton
bool TBOTON::ratonb()
{
     return (mouse_x >= bx && mouse_x <= bx+bancho && mouse_y >= by && mouse_y <= by+balto && mouse_b&1 );
};

#endif


La clase TBOTON se utiliza para poner un texto por pantalla, y que pueda ser pulsado con el raton.

Tiene tres funciones crear, pinta y ratonb.
crear inicializa la clase, requiere el texto a mostrar y la fuente (tipo de letra).
pinta muestra el texto sobre la imagen, en la posición x,y. Si el ratón esta encima del texto se muestra con el color1 y si no con el color2.
ratonb se utiliza para saber si el ratón esta situado encima del texto y se ha pulsado el botón del ratón.

Haz clic aqui para descargar en RAR el archivo botones.h


Bueno esto es todo por ahora, aun no se ha empezado con las tiendas, pero se debe ir poco a poco. Este tema de las tiendas abarcará mas de una entrega. Es por ello, que con esto nuevo que se acaba de hacer no se podrá comprobar hasta que se acabe todo lo referente a las tiendas.

2 comentarios:

  1. Aquí hay un error, en las funciones tipo() y equipo_personaje() en el condicional dentro del bucle en vez de ser:
    lobj[i].nid == id ...
    Debería ser:
    lobj[i].id == id
    Es decir, no hay que cambiarlo con respecto a como estaba antes, las únicas funciones que usan el campo nid son las nuevas añadidas, no estás. Me tomó muchos dolores de cabeza y confusiones el darme cuenta, y cosas extrañas pasaran con el inventario si se deja así. Gracias

    ResponderEliminar
    Respuestas
    1. He mirado el código, y el primer error que he visto es que puse unos nombres de variables muy parecidos y esto causa mucha confusión.
      Comprobando la definición de cada variable las condiciones estan correctamente.
      lobj es un vector de m_objetos, y m_objetos tiene la siguiente estructura:
      nid : que indica el id del objeto.
      id : almacena el id de la imagen del objeto.
      Para evitar confusión, podrías cambiar el nombre "id" de m_objetos por "idImagen". Además a los parámetros recibidos que todos se llaman "id", cambiarlo por "idObjeto". De esta forma se evitará mas posibles confusiones.

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