0

So just building an item and inventory/equipping system.

My solution to doing this is to have a base Item class, then subclasses like MeleeWeapon, RangedWeapon etc. that will have more specific values and functions attached to them.

My problem is here:

unsigned int Player::equipItem(Item item)
{
    //first, we equip the item
    switch (item.subClass) {
    case SC::MELEE_WEAPON :
        if (item.isOneHanded) {
            //unequip mainhand (returns 2! so we can see if something was already there, equip mainhand slot.
            //blabla
            return 1; // yay!
        }
        break;
    case SC::RANGED_WEAPON :
        break;
    case SC::SHIELD :
        break;
    case SC::ARMOR :
        break;
    }
    return 0; //somethings fucked up.
}

so, the error is line 6, and it's because item does not have isOneHanded, but MeleeWeapon does. This would be safe at runtime im 99.9999% sure, but compiler doesn't know. I saw somewhere you can dynamically cast it to a MeleeWeapon manually and I played with that, got even more confusing errors, and on and on.

SC::... is just what i use for identifying what subclass the item is.

As to the kind of solutions I'm looking for:

as simple as possible, and as much explanation as possible. I want to learn not copy paste! Thank you!

  • You have to cast down first - then access the subclass field. Currently you're testing what class it fits in - and then you don't cast. – Mio Bambino Aug 04 '16 at 09:19
  • 1
    *I saw somewhere you can dynamically cast it to a MeleeWeapon manually and I played with that, got even more confusing errors, and on and on.* What did you try, and what errors did you get? – Ry- Aug 04 '16 at 09:21
  • 1
    If you know you want to use `Item` then why isn't the `isOneHanded` in the item? In fact a much better design is not to use a `subClass` member to say what type something is and switch on it. Why not have a virtual `equip` method in an Item and then let that return whatever it needs to depending on the actual item type? – doctorlove Aug 04 '16 at 09:28
  • MeleeWeapon * obj = dynamic_cast(&item) error is "the operand of a runtime dynamic_cast must have a polymorphic class type" and I got even more confused and felt there must be an easier way or I'm casting it wrong even after looking at documentation so I went here. Can't I use static_cast if I'm 100% sure it'd be safe? – Max Perreault Aug 04 '16 at 09:42
  • This error means that Item isn't a virtual class. Just add to it a virtual destructor (`virtual ~Item(){}`), and you will can use dynamic cast. I think you have to look also for functions that derived class should override. See http://stackoverflow.com/questions/2391679/why-do-we-need-virtual-functions-in-c – Garf365 Aug 04 '16 at 09:52
  • @doctorlove because equip isn't being called from Item, it's being called from Entity in another file. I could move it there but I don't feel it is any simpler since I'd have to call item.equipItem(entity...) and also, item.unequipItem(entity...) doesn't make much sense to me when i can do entity.unequp/equip(item) – Max Perreault Aug 04 '16 at 09:54
  • @Garf365 thank you! worked, now that I can at least get it to compile I think i can solve anything else more easily. – Max Perreault Aug 04 '16 at 10:00
  • Don't forget to read about virtual and polymorphism, I think it's what you need here – Garf365 Aug 04 '16 at 10:14

3 Answers3

0

You have to cast item to real type. For that, use dynamic_cast. dynamic_cast checks on runtime that your are allowed to convert item to real type. If item is convertible to what you want, it return a correct pointer, and if not, it return null pointer (in case of dynamic cast pointer - in case of reference casting, it throw an exception). It only works on virtual class.

case SC::MELEE_WEAPON :
{ // Brace are required here to allow declaration of local melee
    MeleeWeapon * melee = dynamic_cast<MeleeWeapon *>(&item);
    if (melee) // If cast has succeed
    {
        if (melee.isOneHanded) {
            //unequip mainhand (returns 2! so we can see if something was already there, equip mainhand slot.
            //blabla
            return 1; // yay!
        }
    }
    break;
}
case SC::RANGED_WEAPON :
{
    RangedWeapon * ranged = dynamic_cast<RangedWeapon *>(&item);
    if (ranged) // If cast has succeed
    {
    break;
}
//.... and so on

If you use C++11, you can also replace line which do dynamic cast by:

auto melee = dynamic_cast<MeleeWeapon *>(&item);
Garf365
  • 3,619
  • 5
  • 29
  • 41
0

if you are 100% sure that Item is of type MeleeWeapon then you can use upcast. Or else You can use dynamic cast for solving this problem.

unsigned int Player::equipItem(Item item)
{
    //first, we equip the item
    switch (item.subClass) {
    case SC::MELEE_WEAPON :
        MeleeWeapon * weapon = dynamic_cast<MeleeWeapon *>(&item);
        if(weapon != nullptr)
        {
          if (weapon->isOneHanded) {
               //unequip mainhand (returns 2! so we can see if something was already there, equip mainhand slot.
            //blabla
            return 1; // yay!
          }
        }
        else //this item is not a MeleeWeapon 
        {
        }
        break;
    case SC::RANGED_WEAPON :
        break;
    case SC::SHIELD :
        break;
    case SC::ARMOR :
        break;
    }
    return 0; //somethings fucked up.
}
Ankur
  • 205
  • 4
  • 15
0

You have a function taking an Item and want to return an unsigned int depending on the type (and other attributes) of that Item.

You could use dynamic_cast provided MeleeWeapon (and other items) inherit from Item (and suggested by other answers).

To me it makes more sense to use polymorphism rather than a switch.

class Item
{
public:
    virtual ~Item() = 0;//don't forget this
    virtual unsigned int equip() const
    {
        return 0;
    }
};

class MeleeWeapon : public Item //you do have this already, right?
{
public:
    MeleeWeapon(bool oneHanded) : oneHanded(oneHanded) 
    {
    }
    virtual unsigned int equip() const
    {
        return oneHanded ? 1 : 0;
    }
private:
    bool oneHanded;
};

class Player
{
public:
    unsigned int equipItem(const Item & item);//note the signature change
};

unsigned int Player::equipItem(const Item & item)
{
    return item.equip();
}
doctorlove
  • 18,872
  • 2
  • 46
  • 62