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