So... here I am again. Items are a pretty important part of RPGs, so I'm probably going to have to code them in at some point. My basic idea is this:

I'll start with an abstract class called "Item". From there, it'll be broken down into several subclasses, including "Equipment" (weapons and armor), "Useable Items" (potions, Phoenix Downs), and Key Items. I have a vague idea of the kind of member functions I would include for both Equipment and Useable items, functions that will increase my characters stats depending on what items he has equipped, and ones that will use up items that I have in my inventory.

My idea (and correct me if I'm wrong, since I probably will be) is to have an array (or vector, I haven't done vectors yet but apparently they're containers of some sort) to store what items that I currently have, whether it's equipment, useable items or key items. Maybe I'll have a separate storage container for each of the sets of items that I have.

To equip/use an item, I was thinking I should have all the contents of each specific container displayed on the screen, with a switch that allows you to choose what item you want to use. Then a member function to iterate (I think that's the term) through the container to the selected index, and finally use/equip the item.

Also another thing, I want to be able to carry quantities of items. So referring to my above plan, I need make sure that 50 potions doesn't take up 50 spaces in my container, or my plan is never going to work.

Any ideas/suggestions/general advice would be great.

Recommended Answers

All 20 Replies

This is actually a much harder task than you think.

I, myself, have tried to make games. Sure some worked but when it came to upgrading them (implementing new features, etc) it became problematic because so many of my functions relied on each other.

What you want to do is generalize how the items and the Characters will interact. You're on the right track for determining how to separate your items in terms of classes, but what role will they play?

Will items be responsible for their own behavior, or will the character determine the behavior of the items?

For example, when you use a potion, will the potion access a member of the Character and increase/decrease said stats, or will the character activate a potion and perform actions based on the supposed effect of the potion?

These are fairly deep topics of design patterns for complex programs. Making an RPG can be incredibly difficult if the design is done improperly.

I don't mean to sound discouraging, but if you have some kind of stopping point or limit for your RPG then what you're doing is perfectly fine. If you plan on building up on this RPG, though, you will encounter a fair amount of issues as you decide how the program will be structured.

I think the easiest thing to do would be to have my Character class directly affected by the items that you're using. So when you equip an item, it increases the strength in your Character class, or when you use a potion it directly increases your health.

The only thing I'm wondering is if this violates the principals of OO programming by having outside functions determining the values of my character's stats within it's own separate class. If it does, I'm going to have to reconsider how my functions are going to indirectly affect your stats without changing them.

I think the easiest thing to do would be to have my Character class directly affected by the items that you're using. So when you equip an item, it increases the strength in your Character class, or when you use a potion it directly increases your health.

The only thing I'm wondering is if this violates the principals of OO programming by having outside functions determining the values of my character's stats within it's own separate class. If it does, I'm going to have to reconsider how my functions are going to indirectly affect your stats without changing them.

You can have relationships between classes. That's perfectly fine in the OO sense.

Obviously the relationship between a Character and the item is such that--

A Character can have many items.
An item can effect a character (or multiple characters, depending on the item).

--

class Character
{
     private:
                 Item *items; //resembles a character capable of having many items                 
                 
                 //private stats variables...
     public:
                Character(/*code for stats...*/){/*constructor code...*/};
                void setItems(Item *theItems); //sets the items
                Item getItem(); //returns a copy of the item, not a reference
                void selectItem(int n); //selects the nth item from the list
                void useItem(); //use the currently selected item
                void selAndUse(int n); //selects an item and immediately uses it
};

//Chances are the above code would be applicable mainly for useable items--

struct Item //most likely a Useable item
{
      private:
                 Character invoker;
      public:
                Item();
                virtual void use() = 0; //pure virtual function, no general Item can be created
                                                //unless it extends this class and overrides this function
                void setCharacter(Character &c); //sets the character to access when use is invoked
       protected:
                Character &getCharacter();//returns a reference to the character, but only for derived Items //EDIT
};

//more code...

--This is just a broad overview. I left out the extensions since I didn't know where you were at with understanding abstraction, etc.

I was actually going to explain abstraction to you last night when it came to having different towns with different behavior and generalizing a case, but I didn't want you to make a ton of different classes just to implement into your code. Looks like that can't be avoided now.

As far as abstraction goes, I think I get the gist of it, as well as overriding functions/virtual functions/purely virtual functions.

The thing that you're doing that I read about but never actually understood how/why you would do it is use an Object for a function call. Such as

void setCharacter(Character &c); //sets the character to access when use is invoked
Character getCharacter();//returns a copy (not reference) of the character

Another thing: if all the use item functions are member functions of my Character class, then how do I use those functions with the item objects that will eventually be created?

As far as abstraction goes, I think I get the gist of it, as well as overriding functions/virtual functions/purely virtual functions.

The thing that you're doing that I read about but never actually understood how/why you would do it is use an Object for a function call. Such as

void setCharacter(Character &c); //sets the character to access when use is invoked
Character getCharacter();//returns a copy (not reference) of the character

Another thing: if all the use item functions are member functions of my Character class, then how do I use those functions with the item objects that will eventually be created?

Simply invoke the use command the each Useable item should have.

The Character should be aware of the item they have selected and aware that the item does something when used.

The items should be responsible for the actions it takes, not the Character.

The pure virtual function in the Item class will become inherited from members that extend from the Item class--

struct Hi_Potion : public Item
{
       
         void use()
         {
               //increase the HP of the character by 20
               getCharacter().setHP((getCharacter().getHP() + 20));
               (*this).~Hi_Potion(); //invoke the destructor of this object
         }

};

--Fixed, sorry.

I'm still confused as to what exactly &getCharacter and SetCharacter(Character &c) are doing. It's one of the things I read about but still don't quite understand.

I'm still confused as to what exactly &getCharacter and SetCharacter(Character &c) are doing. It's one of the things I read about but still don't quite understand.

One last edit should make things clear, since I didn't take into account that we need a way to properly retrieve the Character object being set.

class Character
{
     private:
                 Item *items; //resembles a character capable of having many items                 
                 
                 //private stats variables...
     public:
                Character(/*code for stats...*/){/*constructor code...*/};
                void setItems(Item *theItems); //sets the items
                Item getItem(); //returns a copy of the item, not a reference
                void selectItem(int n); //selects the nth item from the list
                void useItem(); //use the currently selected item
                void selAndUse(int n); //selects an item and immediately uses it
};

//Chances are the above code would be applicable mainly for useable items--

struct Item //most likely a Useable item
{
      private:
                 Character *invoker; //a pointer, EDIT
      public:
                Item();
                virtual void use() = 0; //pure virtual function, no general Item can be created
                                                //unless it extends this class and overrides this function
                void setCharacter(Character &c); //sets the character to access when use is invoked
       protected:
                Character &getCharacter();//returns a reference to the character, but only for derived Items //EDIT
};

//more code...

This is going to be fairly difficult to explain, so please bare with me.

When you create an item, you want it to have some kind of Character to associate with when it is used.

This concept is fine, but when we do this--

void setCharacter(Character &c)

--its because I want a reference to an actual character object, and I don't want the object passed as an argument to be a copy.

The reference symbol means that we will be passing the actual character and can change/alter data or even assign a pointer to the address of the passed Character object to use later.

For a clearer example, consider the following code--

void sum(int a, int b)
{
     a = a + b;
}

int main()
{
   int x = 4, y = 5;

    sum(x, y);

    cout << x << "  " << y << endl;
    
   cin.get();   
   return 0;
}

Notice that x and y remain 4 and 5 despite the fact that I stated-- a = a + b; --in the method. x should be 9 correct?

The answer is no, because a copy of variable x was sent to the method, not the actual reference of x.

Let's say that I want to make a change to the variable by using the method. I would have to make a minor change--

void sum(int &a, int &b)
{
     a = a + b;
}

int main()
{
   int x = 4, y = 5;

    sum(x, y);

    cout << x << "  " << y << endl;
    
   cin.get();   
   return 0;
}

--Now x is 9 and y is 5, because the actual variable x was passed as an argument, not a copy of x.

The main reason why I wanted to pass the actual reference of the Character instead of a copy, is somewhat for the same reason but I goofed up slightly. Notice that I made Character *invoker a pointer. The reason is for the following--

void setCharacter(Character &c)
{
     invoker = &c; //now invoker pointer points to the Character reference c
}

--and now we should be able to access the actual Character that was sent as an argument, and not a copy.

If we sent a copy, any changes we would do to the value pointed to by invoker would be for the copy and not the actual Character.

Edit: Had I left invoker a non-pointer, the assignment-- invoker = c; --would have sent invoker a copy of the current state of the Character &c, but even so it would still be a copy and not the actual reference that I want control of.

getCharacter().setHP((getCharacter().getHP() + 20)); .

I think I get what's going on now. getCharacter(). #1 is saying "use the character that is being referenced by the function", with the .setHP being the call for the member function. Then getCharacter() #2 is doing the same thing as the first, and getHP is taking the value of the characters HP and adding 20 to it.

I think I get what's going on now. getCharacter(). #1 is saying "use the character that is being referenced by the function", with the .setHP being the call for the member function. Then getCharacter() #2 is doing the same thing as the first, and getHP is taking the value of the characters HP and adding 20 to it.

Exactly.

However, you should set some bounds in your Character::setHP(int amount) method.

For example--

void setHP(int amount)
{
   if(amount >= 0) //if hp is being increased...
   {
      if((getMaxHP() - getHP()) >= amount) //if amount added to current hp doesn't exceed maxHP...
      {
              setHP(amount + getHP());
      }
      else if((getMaxHP() - getHP()) < amount) //if amount added is too much, just set to maxHP..
      {
              setHP(getMaxHP());
      }
   }
   else
   {
      //code for when character receives damage...
   }

}

What's up with the Item *items and then later setItems(Item *theitem)

What's up with the Item *items and then later setItems(Item *theitem)

Item *items simply states that I have a private pointer variable named items. It's a declaration.

setItems(Item *theItem) allows an assignment of multiple items to the private variable items.

For example, suppose I wanted to move all of the items from a storage box to the characters inventory. I can simply use a pointer (or array) as a storage value argument and assign a copy of the address to the private-scoped variable items. items would then point to all of the values accessible by the storage pointer.

That can be fairly complicated though, and unwanted. Unfortunately a pointer's job is simply to do what it says - point to an address of something for access. If you were to do multiple assignments, like--

items = storage; //assuming storage is a pointer (or array) to items within the storage
items = bag; //assuming bag is a different array of items

--the pointer would no longer be pointing to the address of storage, but instead the address of bag.

This will be inconvenient if you want to store many different item objects. It might be more reasonable to use a container, such as a vector, to store said items.

Actually, the items pointer can be replaced with a vector that holds items--

vector<Item> items; //stores many individual items - like a resizable array

--where you won't have to understand memory management via pointers.

I really do recommend using a vector. It won't hurt to study that container class.

I've already read a bit about vectors, and it seemed like the way to go. Then my functions would display all the items in the vector, allow you to select which item you want to use, and that selection would call the proper item use function

I've already read a bit about vectors, and it seemed like the way to go. Then my functions would display all the items in the vector, allow you to select which item you want to use, and that selection would call the proper item use function

You seem to know what you're doing, but I'll leave this chunk of code to be helpful to you for future pointer and reference usage --

#include <cstdlib>
#include <iostream>

using namespace std;

int *a, b;
void setInt(int& x)
{
     a = &x;
}

int main(int argc, char *argv[])
{
    b = 2;
    
    setInt(b); //sending reference of b as argument
    
    //referenced pointer a should now point to the address of b
    
    *a = 5; //assigning the value 5 to the address pointed by the dereferenced pointer
    
    cout << *a << " " << b << endl; //notice both have the same value, although I never directly assigned b with 5
    
    //pretend b is the private Character variable that I want to access and a is the pointer referencing the
    //address of b. Now we can control b by using the pointer a.
    
    cin.get();
    return 0;
}

In your example, what is the difference between setCharacter() and &GetCharacter() functions? In my current code I have setCharacter

void Item::setCharacter(Character &cPtr){
	invoker= &cPtr;}

But I'm not really sure what to do with getCharacter

Edit: I just remembered, I have my object Player being pointed at by a global pointer, not really sure how that changes anything

In your example, what is the difference between setCharacter() and &GetCharacter() functions? In my current code I have setCharacter

void Item::setCharacter(Character &cPtr){
	invoker= &cPtr;}

But I'm not really sure what to do with getCharacter

Edit: I just remembered, I have my object Player being pointed at by a global pointer, not really sure how that changes anything

You want your classes to be generalized so that in the event that you add more players, the functionality of the classes wont change much.

Having handled data within the class via containment (or composition) is a much better approach.

The reason you have your global pointer is to (currently) reference the only character you're going to use. Your classes should be made adaptable so that a change outside of the classes will not change the functionality inside your classes.

So if I don't want to pass &cPtr, what reference do I want to pass?

So if I don't want to pass &cPtr, what reference do I want to pass?

No you do, that is correct.

I thought you were planning on directly using your Globally defined pointer (the one that was mentioned in one of your posts quite some time ago) as part of your class.

My apologies for the confusion.

Ugh, I think this is out of my league for now. I need to a more basic way to figure this out.

As for your other question - what do you do with Character &getCharacter() -- I'll explain briefly.

Notice that in your Item class, getCharacter() is marked for protected access--

//previous code in Item class...

protected:
                Character &getCharacter();

This allows anything that derives from the Item class (basically, anything that IS-A item) to inherit this method.

If I hadn't done this, and left it private for example--

private:
            Character &getCharacter();

--derived objects of type Item would not have access of getCharacter(). Nobody would, except within the class Item, which is pointless because we aren't working within the Item class environment.

If we made it public, we'd have other problems - it would be visible and accessible so long as the Item object is publicly visible itself.

Basically, we only want to concern the reference to the Character with Item types, which is why it is marked protected.

Now that we know that the getCharacter() method will only be accessed by Item types, we then know that only Item types will be altering the actual Character reference when they encapsulate the Character reference to something they can access it with (in this case, the invoker pointer).

After setting the Character reference, we can retrieve the character reference via getCharacter(), since invoker is a private variable within the abstract Item class that we will not inherit when extending from that class.

struct Hi_Potion : public Item
{
    //this struct does NOT inherit private member invoker from Item!
    
}

Because we need to access the current Character reference that was set, we can use Character &getCharacter(), which would be defined in this way in the Item class--

//in item class

Character &getCharacter()
{

      return *invoker;

}

Now we have a way of getting the currently set Character reference, since getCharacter() is inherited by derived objects of type Item.

Edit: And sorry if I'm not being thorough enough.

commented: Lots of very patient advice in this thread. +4

No, you're being very helpful. It's just I'm trying to take a concept I only partially understand and trying to apply it in a really tough situation.

Your last post cleared up a bit of confusion for me. I at least got the item structure right, now I just need to figure out how to create a vector for Items

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.