Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » Items stack in a RPG Inventory system

This thread is locked; no one can reply to it. rss feed Print
Items stack in a RPG Inventory system
NickyP
Member #15,390
November 2013

Hello everyone.
I have finally created an inventory for my RPG game, right now It can do:

-> Show the items I have in a 20 slot inventory.
-> Show the icon of the item
-> Show the description (3 lines max)

Here is how it looks now:

{"name":"at18.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/0\/e0ca6a4ca3e8a6cbf411a05ef11c1ee6.png","w":636,"h":476,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/0\/e0ca6a4ca3e8a6cbf411a05ef11c1ee6"}at18.png

But, as you can see I have many potions on my inventory, what I want to do is to use only 1 slot for them, I mean have a "stack" of items, a number will show the quantity of items I have. Like that:

{"name":"3vg5.png","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/e\/ce647b46fa8d611541f39a0f30dd237a.png","w":636,"h":476,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/c\/e\/ce647b46fa8d611541f39a0f30dd237a"}3vg5.png

I have tried to do that, but no success, here is my code for the Scene_Inventory class:

#SelectExpand
1#pragma once 2#include <allegro5\allegro.h> 3#include <allegro5\allegro_primitives.h> 4#include <allegro5\allegro_image.h> 5#include <allegro5\allegro_font.h> 6#include <allegro5\allegro_ttf.h> 7#include <algorithm> 8#include "item.h" 9#include <vector> 10using std::vector; 11 12/////// 13int potions = 0; 14 15vector<Item> Inventory; 16vector<Item>::iterator it; 17 18int slot_w = 275; 19int slot_h = 30; 20 21int slot = -1; 22 23int selection = 0; 24int total_slots = 20; 25 26int cur_items = 0; 27 28int player_coins = 102034; //Just for testing 29 30Item itemname; 31 32class Scene_Inventory : public Item 33{ 34public: 35 Scene_Inventory(); 36 ALLEGRO_BITMAP *inv; 37 ALLEGRO_FONT *font; 38 ALLEGRO_FONT *font2; 39 void Init(); 40 void Draw(); 41 void addItem(Item &itemname); 42}; 43 44Scene_Inventory::Scene_Inventory() 45{ 46 cur_items = Inventory.size(); 47} 48 49void Scene_Inventory::Init() 50{ 51 inv = al_load_bitmap("inventory.png"); 52 font = al_load_font("arial.ttf", 16, 0); 53 font2 = al_load_font("arial.ttf", 14, 0); 54} 55 56void Scene_Inventory::Draw() 57{ 58 al_draw_bitmap(inv, 0, 0, 0); 59 for(unsigned int i = 0; i < Inventory.size(); i++) 60 { 61 if(i <= 9) 62 { 63 al_draw_bitmap(Inventory.at(i).icon, 50, 75 + (i * (slot_h + 2)), 0); 64 al_draw_textf(font, al_map_rgba(100,100,100, 0.5), 80, 76 + (i * (slot_h + 2)), 0, "%s", Inventory.at(i).getName()); 65 al_draw_textf(font2, al_map_rgba(100,100,100, 0.5), 45, 82 + (i * (slot_h + 2)), 0, "%i", Inventory.at(i).cur_quantity); 66 67 } 68 else if(i >= 10 && i < total_slots) 69 { 70 al_draw_bitmap(Inventory.at(i).icon, 334, 75 + ((i-10) * (slot_h + 2)), 0); 71 al_draw_textf(font, al_map_rgba(100,100,100, 0.5), 364, 76 + ((i-10) * (slot_h + 2)), 0, "%s", Inventory.at(i).getName()); 72 } 73 } 74 75 if(selection <= 9) 76 { 77 al_draw_rectangle(40,70 + (selection * (slot_h + 2)),315,100 + (selection * (slot_h + 2)),al_map_rgba(100,100,100, 0.5),1); 78 } 79 else if(selection >= 10 && selection < 20) 80 { 81 al_draw_rectangle(323,70 + ((selection-10) * (slot_h + 2)),600,100 + ((selection-10) * (slot_h + 2)),al_map_rgba(100,100,100, 0.5),1); 82 } 83 84 //DRAWING DESCRIPTION 85 if(selection < Inventory.size()) 86 { 87 al_draw_textf(font, al_map_rgb(255,255,255), 40, 405, 0, "%s", Inventory.at(selection).getDescription(1)); 88 al_draw_textf(font2, al_map_rgb(255,255,255), 40, 425, 0, "%s", Inventory.at(selection).getDescription(2)); 89 al_draw_textf(font2, al_map_rgb(255,255,255), 40, 440, 0, "%s", Inventory.at(selection).getDescription(3)); 90 } 91 92 //Drawing coins 93 al_draw_textf(font2, al_map_rgb(255,255,255),622, 15, ALLEGRO_ALIGN_RIGHT, "Coins: %i", player_coins); 94 95 //Size test 96 al_draw_textf(font, al_map_rgb(255,255,255), 20, 20, 0, "%i", Inventory.size()); 97 98} 99 100void Scene_Inventory::addItem(Item &itemname) 101{ 102 switch(itemname.have) 103 { 104 case 0: 105 Inventory.push_back(itemname); 106 itemname.cur_quantity++; 107 itemname.have = true; 108 break; 109 110 case 1: 111 itemname.cur_quantity++; 112 break; 113 } 114}

The last function is the one I am talking about, this is how it should work:
1- Check if the player has the item (itemname.have)
2- If the player doesn't has the item, then we add a new element to the vector with the item name provided. Then increment the quantity by 1, then set "have" to true.
3- If the player has the item, then just increment it's quantity by 1.

Please help me, I really don't know how to do that, the function doesn't work...

Thank you very much :D

Lupuss.Umbrae
Member #8,387
March 2007
avatar

Well, unless I'm missing something(how do you call addItem?), the function can't work, because you aren't checking if an Item of the same type already exiss within the inventory. Allow me to explan, your method does the following:
1. Check if the itemname.have is 1 or 0. This is a bad idea and (in this case) meaningless. More about that later.
2. if itemname.have is zero, the Item gets put in the vector
3. if itemname.have is one, you incremet the quantity of the Item and then you actually don't do anything with it.

Example:
You want to add two SnackBars to the inventory. You have two options.

Option 1:
You create two Item objects, lets say snack1 and snack2, and pass them both to addItem()
Outcome:
-snack1 get added to the vector
-snack1.have is set to one
-snack1.cur_quantity is incremented
-snack2.cur_quantity is incremented, never to be used again

Option 2:
You create one Item object, lets say snacks, and pass it two times to addItem()
Outcome:
-snacks get added to the vector
-snacks.have is set to one
-snacks.cur_quantity is incremented
-snacks.cur_quantity is incremented again

Option two would do the job, actually. If that's what you're going for, that is. Assuming vector<Item> Inventory takes pointers isnstead of the actual objects. (Sorry, my C++ skills get a bit rusty... :/ )
Well... no. Just did some testing and it seems vectors store a copy of the object.
Either way, you should implement a function that checks if the type of item is in the inventory by scanning the vector instead of relying on a variable and incrementing the cur_quantity of the object that is already in the vector. That would be cleaner and more understandable, anyway.

--------------------------------------------------
I'm not a signature, I'm just cleaning here...

NickyP
Member #15,390
November 2013

Thanks for the reply.

Well, I call the function like that:

#SelectExpand
1Scene_Inventory.addItem(potion);

The vector takes the actual item... Maybe there is something that I'm not doing correct.
Maybe I'm not passing correctly the item to the function..

I'm really stuck with this :S

Lupuss.Umbrae
Member #8,387
March 2007
avatar

Ah, ya beat me to it, was just editing my post...

Here is a cleaner and more understandable approach

#SelectExpand
1int Scene_Inventory::containsItem(Item &itemname) 2{ 3 for(int i=0; i<Inventory.size(); i++) 4 { 5 if(Inventory.at(i).name==itemname.name)// i.e. "potion" or "gold ring" (use string class for name) 6 return i;//return position of Item of same type as itemname 7 } 8 return -1; 9} 10 11void Scene_Inventory::addItem(Item &itemname) 12{ 13 int itemPos=containsItem(itemname); 14 if(itemPos) // itemPos>=0 means the item is already there, increment quanity 15 { 16 Inventory.at(itemPos).cur_quantity++; 17 } 18 else //itemPos==-1 means this item is not yet listed in the inventory, add it 19 { 20 Inventoy.push_back(itemname); 21 } 22}

--------------------------------------------------
I'm not a signature, I'm just cleaning here...

NickyP
Member #15,390
November 2013

Thanks for the example.
But I can't use Inventory.size() because at the start the vector is empty, so I'm getting an "out of range" error...

I tried to switch Inventory.size() to 20, in line 3 of your code and anyway get the error...

Edgar Reynaldo
Member #8,592
May 2007
avatar

Lupuss.Umbrae
Member #8,387
March 2007
avatar

Oh, right. Sorry, my bad.

make that

int Scene_Inventory::containsItem(Item &itemname)
{
   if(!Inventory.empty())
   {
      for(int i=0; i<Inventory.size(); i++)
      {
         if(Inventory.at(i).name==itemname.name)// i.e. "potion" or "gold ring" (use string class for name)
            return i;//return position of Item of same type as itemname
      }
   }
   return -1;
}

EDIT:
Or do what Edgar said :)

--------------------------------------------------
I'm not a signature, I'm just cleaning here...

NickyP
Member #15,390
November 2013

Thanks everyone, I think I got it to work finally.
The only thing is that now I have changed the Item name from char to string, the name is not displaying in the inventory... Here is how I'm drawing my item's name: al_draw_textf(font, al_map_rgba(100,100,100, 0.5), 80, 76 + (i * (slot_h + 2)), 0, "%s", Inventory.at(i).name);

;D fixed now :)

Thanks

OnlineCop
Member #7,919
October 2006
avatar

If name is now a std::string, use name.c_str() to convert it into a const char * array.

I made a VR game!

Lupuss.Umbrae
Member #8,387
March 2007
avatar

NickyP said:

Thanks everyone, I think I got it to work finally.

You're welcome. Though Edgar Reynaldo brought up a valid point.
Unless your Item objects have other important properties that a name you could use a map as well. Would save up some RAM.

--------------------------------------------------
I'm not a signature, I'm just cleaning here...

Edgar Reynaldo
Member #8,592
May 2007
avatar

You could register classes of items with a registry. And if you wanted stackable inventory, you could use a multimap which allows you to have multiple copies of the key (item) and since you store an int then that is the count of the item in that 'pile'.

Go to: