Classes

Pages: 1234
I would like to make a small text based rpg, with some basic items and enemies.

My question is how would I create a class to get equipment. I want it to act as a "router", receiving and passing data. I have a class which is the heroes "character menu", a class with children "swords", and a class "hero equipment. It displays stats. I would like the "damage" stat to be updated based on the users equipment.

This is the basic set up as I see it.

1) "equipment class" get weapon of choice
2) "equipment class" get weapon of choice damage.
3) "character menu class" get weapon damage
4) "character menu class" display info in character menu.


Not sure if this is the correct way to go about doing it.
Im not sure how you could do this, as I am currently learning C++... Maybe you could put a function into a If statement. Example, if you have this weapon decrease50hp(). But I would love to test out your RPG when it's finished! :)
Last edited on
I believe you're designing a D&D style game, which is a common choice for C++ programmers (we're all nerds at heart). I believe you may want to reconsider what your classes are and do. A simple outline would be something like so:
Hero class
   Hero Name
   Hero Health
   Hero Weapons
   Hero Damage
   Hero Strength
   Hero Dexterity
   Hero Stash
   Hero Gold
   And so on...
Weapon class
   Weapon Name
   Weapon Damage
   Weapon Type
   Weapon Value
   Weapon Quality
   And so on...


How you implement each one should depend on what you actually want to do with them. I'd make sure your hero class is set up and maybe use a basic weapon class as well. A quick example of how it could look programming (assuming the classes are all set up correctly):
1
2
3
Hero myHero("SGM3", Dwarf, Ranger, 100, 10, 15, 14, 18);
myHero.AddStash(Great Sword);
myHero.Equip(Great Sword);


The classes should handle all of the math and assignments so your main code doesn't need to. This can be a deterrent for beginner programmers since it can involve a lot of coding without understanding how it can help you later on.

Let's say it takes you two weeks to type up your Hero and Weapon classes (you should obviously be testing it while doing so as well). And you begin to write your main game. To implement just one hero and one character seems like a waste, but think about the number of weapons just one character can use and how nice it is that you don't have to worry about the actual damage done since it's all hard coded into the classes already.

The classes should be meaningful and very helpful. If you find that your classes aren't doing much (have several methods that are only a line or two long) then maybe it's time to rethink that class (or maybe it shouldn't be a class). A hero selector menu is one of those examples. It will be used exactly once. It doesn't mean it can't be a class, but it might be better off designed a different way.
I'm curious - your "outline" at the beginning of the post. The "indented", are they derived classes or member methods?
They're going to be data members, you'd have to generate your own methods based of whatever data members you choose. You could use a default constructor or a constructor that accepts defaults to set the behaviors of each, or you can use seperate methods to statistics for each. A Hero should be it's own class (It could be a derived class of character just like an enemy could be since they will all share health damage names etc.).
Thanks! This gives me a lot to think about.

Any and all future comments will be greatly appreciated.
Last edited on
Is it possible to assign a number to a class? Say I wanted to create a class with derived classes. Could I assign a number to the derived classes if I wanted a function to decide which derived class to use.

Eg:
class weapons
(1)  class sword : public weapons
(2)  class axe : public weapons
(3)  class dagger : public weapons
1
2
3
4
5
6
7
8
9
10
11
12
if ( weapon = '1' )
{
    //sword data
}
if ( weapon = '2' )
{
    //axe data
}
if ( weapon = '3' )
{
    //dagger data
}


I would like to assign the derived classes to an array so I don't have to write out each weapon as it correlates to its assigned number.
Last edited on
I would suggest using an array of weapons and store each type (sword, axe, dagger, etc.) in the "type" of the weapon class. Remember a sword is a weapon so therefore the weapon is the class and you must set up the object (maybe basicSword) to reflect this.
Forgive me, would you elaborate a little more.

I am fairly new to classes. Just finished 3 different chapter of classes in three different books. I find that I learn better tackling a task beyond my knowledge, so I can come across "problems" that books don't cover.
Last edited on
I typed up a very simple example. It's kind of making me want to build a basic game engine (or at least the classes). But here it is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
using namespace std;

// enum can be used later on for determining what stats a weapon can have
// Daggers are short and fast
// Axes are heavy and slow
// A 2 handed weapon would limit the ability to use a shield
enum WeaponType {Sword, Dagger, Axe};

class Weapon {
   private:
      string mName;
      int mDamage;
      WeaponType mType;
   public:
      // Default Constructor
      Weapon() :
         mName("Default Weapon"),
         mDamage(0),
         mType(Dagger) {}
      Weapon(string name, int damage, WeaponType type) :
         mName(name),
         mDamage(damage),
         mType(type) {}
      int GetDamage() { return mDamage; }
};

int main() {
   // We're going to make three basic weapons
   Weapon ShortSword("Short Sword", 3, Sword);
   Weapon SharpDagger("Sharp Dagger", 2, Dagger);
   Weapon SteelAxe("Steel Axe", 4, Axe);
   
   // Let's say our user picked the sword
   // Since I didn't make a Hero class yet, the rest is commented out
   // myHero->AddItem(ShortSword);
   // myHero->Equip(ShortSword);
   // The Hero class will calculate the hero's damage when a new item is equipped
   // myHero->DisplayDamage();
   return 0;
}
This is my weapon class:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
class weapons
{
    private:
    std :: string name;
    int damage;
    int atr;
    double ats;
    int type[42];
    int value;
    double quality;

    public:
        weapons();
        ~weapons();
    //Member Methods
    void displayWeaponStats()
    {
        std :: cout << "\t\t------------Weapon Stats---------------" << std :: endl;
        std :: cout << "\t\tName: " << name << "\t\t\tQuality: " << quality << std :: endl;
        std :: cout << std :: endl;
        std :: cout << "\t\tDamage: " << damage << std :: endl;
        std :: cout << "\t\tAttack Rating: " << atr << std :: endl;
        std :: cout << "\t\tAttack Speed: " << ats << std :: endl;
        std :: cout << std :: endl;
        std :: cout << "\t\t---------------------------------------" << std :: endl;
    }

        //Accessor Methods
        std :: string getName() { return name; }
        void setName( std :: string x ) { name = x; }
        int getDamage() { return damage; }
        void setDamage( int x ) { damage = x; }
        int getAtr() { return atr; }
        void setAtr( int x ) { atr = x; }
        double getAts() { return ats; }
        void setAts( double x ) { ats = x; }
        int getType() { return type[42]; }
        void setType( int x ) { type[42] = x; }
        int getValue() { return value; }
        void setValue( int x ) { value = x; }
        double getQuality() { return quality; }
        void setQuality( double x ) { quality = x; }

    protected:

};
//-----------------------------------------------------------------------------------------
class woodenSword : public weapons
{
  public:
        woodenSword();
        ~woodenSword();
        void woodSwordStats()
        {
            setName("Wooden Sword");
            setDamage(7);
            setAtr(90);
            setAts(1.2);
            setType(0);
            setValue(6);
            setQuality(1.00);
        }
};
//------------------------------------------------------------------------------------------  



How do I update the displayed data inside the displayWeaponStats method? Your way is probably more efficient. I know this is not what you showed me. But, I thought I would show you what I have written.
Last edited on
You wouldn't use derived classes like that. You use a derived class to do something like:
class RangedWeapon: public Weapon
class TwoHandedWeapon: public Weapon
class Staff: public Weapon

Deriving classes means they share the same properties as the "parent" class, but they each will have a different feature. When creating the Weapon class, you need to figure out what every single weapon in the game will have. When you make a derived class, that derived weapon will still have all the same features as a Weapon, but has additional features as well. For instance, a two handed weapon might allow the Hero to do a crushing blow, or a Ranged Weapon might allow the Hero to "Snipe" someone, or a Staved Weapon might allow the Hero to cast spells. Notice how no other derived classes share these new properties. A class should be something unique from everything else

You're look at a class as it should be a definition of everything. One class for each instance. There's not really anything wrong with this, but you're going to cause yourself a lot of extra code and time to do the same thing I was.

Edit: But instead of declaring separate classes, that's what I left the WeaponType for.
Last edited on
Ok I understand.

I think I resolved my problem with updating data, like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class woodenSword : public weapons
{
  public:
        woodenSword();
        ~woodenSword();

            //Member Methods
        void woodSwordStats()
        {
            setName("Wooden Sword");
            setDamage(7);
            setAtr(90);
            setAts(1.2);
            setType(0);
            setValue(6);
            setQuality(1.00);

            displayWeaponStats();  //Include this member method within the item.
        }
};

I see what you are getting at with derived classes, by assigning unique properties.
Going to crack open my books again =) Need to understand enum better. Not sure where you are going with the enum type. I get the idea, but unsure how to implement it.

So I would use derived classes in a hero class for, a wizard learns spells but can't learn say shield bash as a warrior can?
Last edited on
Think of enum as creating a new data type. You define what values it can accept. The types I put in signify different properties of each basic weapon. I'll use Diablo 2 as an example since I'm familiar with the game and you might be as well.

A sword has the default properties of being fast, having a medium damage range, and average value.

A dagger has the default properties of being very fast, having a small damage range, and low value.

An axe has the default properties of being slow, having a large damager range, and higher value.

You can use these WeaponType's to classify your weapons to a similar way you would have used a derived class. And instead of rewriting your code over and over again, you can use a BaseLevel enum as well.

WeaponType Sword BaseLevel 1 would be a basic short sword.
WeaponType 2hSword BaseLevel 1 would be a basic long sword
WeaponType Club BaseLevel 3 would be a level 3 club with added stats to it

A lot of design needs to go into a game before you just jump in. You need to determine how different values are going to be given, what you want the computer to do for you (this is called scaling) and which values you want to write yourself (this is to be avoided as much as possible)

This is where classes come in handy. You can make a million weapons just off one class. It takes time to know what each class should contain, what it should do for itself, and how other classes should work with it.

A Hero should be able to pick up any ItemType which could be another enum but you can't just classify everything as an enum or you start getting away from the classes. A Weapon can be derived from the Item class since every item should have a value, possible a weight/size, and rarity.

Edit: An example of having the class do everything is in the design of the names. If you know Diablo 2, a low quality weapon has a name of "Rusty"/"Worn"/"Used" where a good quality weapon gets a name like "Fine". Magic items get 1-2 properties. Rare gets up to 6, and Unique get's a predetermined set of properties the developers created.

Also, the quality plays a role in what the stores will offer you/charge you to fix it/charge you to buy. The basic getter/setter functions no longer work. The class handles everything and only allows certain information to be available.
Last edited on
Thank you for your time. Giving me a lot of good stuff to think about.
I think I found what you were showing me with the constructor. One of my books calls it a "Parameterized Constructor". As I understand it, I am basically wanting the constructor to automatically initialize the weapon of choice.

I do so by sending arguments to the class when the class is called in main. I think this is right. Instead of setting each parameter in the derived classes and creating a derived class for every weapon.

I am typing up the code now, will I be able to use this in my displayWeaponStats method?

There isn't much about enum in my books. Still not sure how to define enum variables. I understand you can assign data to enums but not sure on how to assign multiple data.

eg. sword has damage, quality, speed, attack rating, and etc. how do you assign that all to sword. Plus, how do you assign multiple sword types.

But, I will cross that bridge when I reach it.
Last edited on
Well, the Parameterized Constructor, as your book calls it, should accept the "default" values for that Object. Remember, the Weapon class is just the class, the object will be different (or can be the same) as every other object, they'll just share properties if you will. Here:
Weapon ShortSword("Short Sword", 3, Sword);

Using my example, I'm creating a ShortSword Weapon that has a name of "ShortSword", damage of 3 and is a WeaponType of Sword.

You can create anything, and some people prefer to use pointers for their objects. The only thing to remember is that pointers need to be "delete"d when you "new" them.
I got it up and running with default constructor values. I'm curious why I did that.

I was thinking about your enumerators post. If I am going to assign values to the enumerators why would I send parameters to the class when called, to define the values of an item?

Seems like I would need a chart to remember the values of each item if I am manual passing values to the class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
enum weaponType {sword, axe, mace, dagger, staff, twoHandSword, twoHandAxe};

class weapons
{
    private:
    std :: string name;
    int damage;
    int atr;
    double ats;
    int value;
    double quality;

    public:
        weapons() :
        name("Fist"),
        damage(0),
        atr(0),
        ats(0.0),
        value(0),
        quality(0) {}

            weapons(std :: string mName, int mDamage, int mAtr,
                    double mAts, int mValue, double mQuality) :
            name(mName),
            damage(mDamage),
            atr(mAtr),
            ats(mAts),
            value(mValue),
            quality(mQuality) {}
        ~weapons();

    //Member Methods
    void displayWeaponStats(std :: string mName, int mDamage,
            int mAtr, double mAts, int mValue, double mQuality )
    {
        std :: cout << "\t\t------------Weapon Stats---------------" << std :: endl;
        std :: cout << "\t\tName: " << mName << std :: endl;
        std :: cout << std :: endl;
        std :: cout << "\t\tDamage: " << mDamage << std :: endl;
        std :: cout << "\t\tAttack Rating: " << mAtr << "%" << std :: endl;
        std :: cout << "\t\tAttack Speed: " << mAts << std :: endl;
                std :: cout << "\t\tQuality: " << mQuality << std :: endl;
        std :: cout << std :: endl;
        std :: cout << "\t\t---------------------------------------" << std :: endl;
    }
int main()
{
    weapons * weapon = new weapons();

    weapon->displayWeaponStats("Axe", 23, 95, 1.2, 6, 1 );
Last edited on
Not really. I'll give you an example of an ItemType enum:
enum ItemType {Potion, Weapon, Armor, Scroll, Food, Projectile};

And then it would be used later on. Say you had a random number (a dice roll) and any one of the items could have been dropped. You create a new item with that type. Then you create a corresponding object from the class template. An example:
1
2
3
4
5
6
7
8
// Let's say you rolled a weapon as loot from the treasure chest
Item *randomLoot = new Item(randomRoll);
// The ItemType parameterized constructor of Item creates a random item
// Now we check to see what we got
if (randomLoot->Type() == Weapon) {
   Weapon *newWeapon = new Weapon(*randomLoot);
   myHero->AddItem(*newWeapon);
}


That code isn't complete since I don't know how to use polymorphism yet. And the pointers idea is what most people use, but it still confuses me in even a simple program. Maybe I'll type up a functioning Item system so you can see how it works.
Last edited on
I created empty derived classes for my weapon types.
How do I assign properties to them if I shouldn't define them within the derived class?

Also, say I wanted different qualities(bronze, iron, etc)? I know you touched a bit with the diablo 2 example.

Edit - As you said the classes should be unique properties but I now have an easier way to display stats for each weapon with the default constructor. I could use:
1
2
3
4
5
6
7
8
9
10
11
12
13
class shortSword : public weapons
{
  public:
        ~shortSword();
        
        //Member Methods
        void shortSwordStats()
        {
            displayWeaponStats("Short Sword", 23, 95, 1.2, 6, 1, sword );
        }


};


I found information on assigning data to enumerators but only assign one set of data. Unless you use a switch statement or if statement. I'm not sure of any other way to display stats for the items.
But, I can see how I will run into problems. If I want all maces to have a chance to stun then a bronze mace will have less damage than a steel one. Which the mace stats will have to be updated for every quality. Wouldn't I have to write code for these item qualities anyway?
Last edited on
I really need to write another article on this, as this question (or a variation of it) seems to come up frequently.

There are a few design flaws I see here that I feel compelled to bring up.

1) Don't derive new classes for each new kind of weapon if the only difference in weapons is their stats

Inheritance is easy to get carried away with. It is most useful when you need the behavior of an object to change depending on its type. I find that unlikely to be the case here. It looks like the only difference between weapons is how strong they are -- but they all will behave the same.

By making each different kind of weapon its own class, you might just be making things more difficult for yourself.

2) Function names should typically be verbs, and should not be redundant

"shortSwordStats" is a poor function name mainly because it's redundant. You're already in the shortSword class, so you know you're dealing with a shortSword, no need to put that in the name of the class in the function. Notice how classes like std::string and std::vector have functions like "size" or "length", not "vectorsize" or "stringlength". There are many reasons for this but I won't get into details -- I'll just say redundancies make maintenance and writing dynamic code more difficult.

I would also recommend adding verbage to the function name to describe what it's actually doing. Function names that lack any verbage tend to be assumed that they are "getter" functions (ie vector's "size" function is like "getsize"). So most people would expect a function called "shortSwordStats" to return some kind of structure which contains the stats of a short sword -- but that is not what your function does at all -- so the name is misleading.

"displayStats" is a much better name.


3) A class that represents a singular object should be a singular noun

"weapons" is a somewhat confusing name because the class only represents 1 weapon. Again I turn to the standard classes for an example: the "string" class represents a single string:

1
2
3
string foo;  // makes a lot of sense.  We are making one string named foo

strings foo;  // huh?  are we making multiple strings? 


4) Do not hardcode weapon stats into the actual weapon class

This is HUGE. Ideally, you want to minimize hardcoding and separate data and code as much as possible. Embedding data in your code makes the program very rigid and hard to expand/maintain.

A better way would be to have a centralized list which has the stats of all available items. When you need to create a new short sword, you simply pull out the short sword stats from that list and assign it to a new weapon.

A simple (but not ideal) way to do this is to have a global hardcoded array:

1
2
3
4
5
6
const weapon baseWeaponStats[] = {
  weapon( "Dagger",      1,  5, 6, 2, whatever ),
  weapon( "Short Sword", 15, 5, 6, 2, whatever ),
  weapon( "Long Sword",  20, 5, 6, 2, whatever ),
  weapon( "Pike",        12, 5, 6, 2, whatever )
};


Then when you want a new weapon, you can just do this:
 
weapon newweapon = baseWeaponStats[ x ];


This also allows you the benefit of randomly modifying generated weapons, as is common practice in roguelikes. For example, the player might find an enchanted weapon that is slightly stronger than normal... or a cursed one that is slightly weaker:

1
2
3
4
5
weapon newweapon = baseWeaponStats[ x ];
if( random_chance_to_see_if_weapon_is_enchanted )
{
  // modify newweapon's stats here
}



An arguably superior alternative to the hardcoded array is the "load the data from a file" approach. Maybe create a .txt file that has all your weapon types and their typical stats and load that file into an array when your game starts. That way you can modify your weapon stats without even having to recompile the program.

In a small scale game this doesn't offer that much benefit, but for larger games it does wonders because it allows for things like custom game mods.


That's about all for now. I really need to go back to bed....
Last edited on
Pages: 1234