0

I apologise if my question seems a little ambiguous, and I know its been asked before but my circumstances are significantly different from what I've read.

I'm looking for a SIMPLE, ELEGANT solution, not a complex workaround.

I'm working on coding an RPG, and ran into a snag while working on the inventory/item system. Here is my object structure(not sure if that's the correct terminology) in English first, and some example code second.

A Player class has an instance variable of type Inventory.

The Inventory class has an array of Item objects, that represent an item stored in an inventory slot.

The Item class has children such as Weapon, Armour, Potion etc.

I don't know what item the player will have, so the Inventory is initialised with Item objects, but when the player picks up a sword for example a new Weapon(ID) object is created and stored in the inventory, i still can't access the Weapon variables or methods without Typecasting every calling method over. This wouldn't normally be too much of an issue but with 20+ Item subsclasses totalling 100's of different variables and methods, they code required would be enormous to Typecast every time.

if (you see no problem and that's how its done) {

let me know

} else if (you have a better way of doing this) {

let me know.

}

Thanks!

Player--has--Inventory--has--Item--hasChildren--Weapon/Armour.

Below is a snipped to show what i have right now. NOTE: There is significantly more variables and methods for each class, just showing what feels important.

class Player {
   private Inventory inventory = new Inventory();
   public Item getInventoryItem(int slot){
      return inventory.getItemFromSlot(slot);
   }
}

class Inventory {
   private Item[] item;
   public Inventory() {
      this.item = new Item[INVENTORY_SIZE];
   }

   public Item getItemFromSlot(int slot) {
      return item[slot];
   }

// called when i pick up ANY ITEM.
// having a addWeapon(Weapon weapon) would make no difference (see end of code)
   public addItem(int slot, Item item) {
      inventory[slot] = item;  
   }
}

class Item {
   private int itemID;
   private int itemType;
   private String itemName;

   public Item(int ID) {
      this.itemID = ID;-
      this.itemName = getItemName(ID); //access a database which holds all item data
      this.itemType = getItemType(ID); //access a database holding all item types
   }
}

class Weapon extends Item {
   private int damage;

   public Weapon(int ID) {
      super(ID);
      this.damage = getWeaponDamage(ID);
   }

   public getDamage(){
      return damage;
   }
}

PROBLEM:

class Game {

//lets assume i have picked up a Weapon and it is now stored in slot i, as a Weapon not as an Item.

int test = player.getInventoryItem(i).getDamage(); //doesn't work, even when the item (i) class IS weapon
int test = ((Weapon)player.getInventoryItem(i)).getDamage(); //works
}

3 Answers3

0

If you want to access Weapon specific methods or variables you have to cast the Item list element to a weapon, if you want to use polymorphis the class item has to have that method or variable, so it can be called for every item.

R4yY
  • 112
  • 13
0

Casting is necessary in this case because the class Item has no Method getDamage(). Calling getInventoryItem() returns an Item, not a Weapon, so the parent class does not know anything about the methods of its child. One possible solution would be using self-type pattern mentioned here: Avoid casting in inherited java-classes

WinterMute
  • 145
  • 2
  • 11
0

So I've been testing some things myself. Because the compiler THINKS getInventoryItem() will always return an Item, it doesn't allow for the method getDamage() to be called. But when the object referenced by getInventoryItem() is a Weapon, if I simply add int getDamage(){return -1} as a method of the Item class, I still get the correct int damage value from Weapon, and a -1 from anything else. Perfect!

  • It is possible solution, but in the future it may get hard to mantain the code. Consider that every item type like weapon, quest item, armor will have their own properties only for their case. Either you will do it for all types and get ton of codes that has no functionality or you will have inconsistent structure. Maybe you should think about other solution, like instantiating the objects by getting it with factory pattern or something like this. Just think about it. – WinterMute Nov 18 '20 at 11:41
  • @RavenDark - So i understand, the logic would go: read itemID of the object i want from the inventory. Using the itemID, determine what kind of object it is. Create a second instance of that object, call the method I want, and then discard the object? I hadn't thought of that. – Plasmatech Studios Nov 18 '20 at 12:08
  • It is the correct direction. You could improve it by ommiting creating an instance twice. Maybe you could store in the inventory item id's instead of items. But I don't know your requirements. I would rather go into casting than creating unnecessary code or holding the object twice. It will cost you performance. Another way could go with checking for ```instance of```. You could store the items in your array and with checking kind of the item getting it with a factory pattern. For instance. – WinterMute Nov 18 '20 at 14:01