Yes, I have virtual functions and polymorphism in mind, but I wouldn't keep items in the inventory at all.
What is an inventory in real life? Basically, it's a collection of
information about things.
So I would store weapons, shields, etc. in their own separate containers. So that I know how I use them: as weapons, as shields?
Then you have the actual inventory, which is updated as needed, and only stores metadata, and not the items themselves.
I've chosen to have a virtual
generateDescription()
function that classes derived from
Item
can
overload override as needed. The
Inventory
uses an
std::map to associate an object's memory address with its description.
This probably isn't the best way to do things. But at least you know what your items are.
Source code below is C++11. I hope your compiler supports it.
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
struct ItemDescription {
std::string name;
std::string trivia;
ItemDescription() = default;
ItemDescription(const std::string &name, const std::string &trivia):
name(name),
trivia(trivia)
{
}
};
class Item {
public:
virtual ~Item()
{
}
virtual ItemDescription generateDescription() const
{
return ItemDescription("Item", "Nothing interesting.");
}
};
class Weapon: public Item {
public:
Weapon(int damage, const std::string &name, const std::string &trivia):
damage(damage),
name(name),
trivia(trivia)
{
}
ItemDescription generateDescription() const
{
return ItemDescription(name, trivia + " It does " + std::to_string(damage) + " damage.");
}
private:
int damage;
std::string name;
std::string trivia;
};
class Inventory {
public:
void addOrUpdate(const std::shared_ptr<Item> &spi)
{
m[spi.get()] = spi->generateDescription();
}
void erase(const std::shared_ptr<Item> &spi)
{
m.erase(spi.get());
}
void display() const
{
for (const auto &id: m) // auto == pair<key, data>
std::cout << id.second.name << " *** " << id.second.trivia << '\n'<< std::endl;
}
private:
std::map<const Item *, ItemDescription> m;
};
int main()
{
Inventory i;
std::vector<std::shared_ptr<Item>> weapons;
std::shared_ptr<Item> sword(new Weapon(100, "Durandal", "It's French."));
std::shared_ptr<Item> gun(new Weapon(20, "M16", "It's an American rifle."));
weapons.push_back(sword);
weapons.push_back(gun);
for (const std::shared_ptr<Item> &spi: weapons)
i.addOrUpdate(spi);
i.display();
i.erase(gun);
i.display();
}
|
Durandal *** It's French. It does 100 damage.
M16 *** It's an American rifle. It does 20 damage.
Durandal *** It's French. It does 100 damage.
|
Edit: cleaning up some stupid things.
Edit 2: I realized that the code can be simplified by associating objects' descriptions with their name instead of memory address. With the small inconvenience of not being able to have two objects named the same... but, no more smart pointers.
Edit 3: a bit less atrocious now, although
ItemDescription
is now totally useless and can be replace with a plain
std::string. Important lesson here: strive to simplify your code. Preferably before you hit the Submit button.
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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
|
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
struct ItemDescription {
std::string trivia;
ItemDescription() = default;
explicit ItemDescription(const std::string &trivia):
trivia(trivia)
{
}
};
class Item {
public:
Item(const std::string &name, const std::string &trivia):
name(name),
trivia(trivia)
{
}
virtual ~Item()
{
}
virtual ItemDescription generateDescription() const
{
return ItemDescription("Nothing interesting.");
}
std::string getName() const
{
return name;
}
protected:
std::string name;
std::string trivia;
};
class Weapon: public Item {
public:
Weapon(int damage, const std::string &name, const std::string &trivia):
Item(name, trivia),
damage(damage)
{
}
ItemDescription generateDescription() const
{
return ItemDescription(trivia + " It does " + std::to_string(damage) + " damage.");
}
private:
int damage;
};
class Inventory {
public:
void addOrUpdate(const Item &i)
{
m[i.getName()] = i.generateDescription();
}
void erase(const Item &i)
{
m.erase(i.getName());
}
void display() const
{
for (const auto &id: m) // auto == pair<key, data>
std::cout << id.first << " *** " << id.second.trivia << '\n'<< std::endl;
}
private:
std::map<std::string, ItemDescription> m;
};
int main()
{
Inventory i;
std::vector<Weapon> weapons;
Weapon sword(100, "Durandal", "It's French.");
Weapon gun(20, "M16", "It's an American rifle.");
weapons.push_back(sword);
weapons.push_back(gun);
for (const Weapon &spi: weapons)
i.addOrUpdate(spi);
i.display();
i.erase(gun);
i.display();
}
|