3
\$\begingroup\$

In my game (simple RPG with Java) there will be an abstract class Creature.

Critter, Beast, Vampire... will inherit from Creature. But not ALL classes that inherit from Creature will have an inventory.

Should I

a) make another abstract class such as CreatureWithInventory and inherit from it?

or

b) make an interface such as Inventory and make stuff as (Vampire implements Inventory)?

Is there a reason to prefer one over the other? Any considerable trade-offs?

Should Inventory itself be a class and let some Creatures have a field for it? If so, how would I test if Creature foo has Inventory? Is that even possible?

\$\endgroup\$
  • \$\begingroup\$ might i ask why critters do have an inventory? is it to keep the 'loot' inside or is the critter itself a looter and picks up items? or do items within the inventory modify stats of the critter? \$\endgroup\$ – Martin Frank Sep 17 '14 at 7:11
  • \$\begingroup\$ answer this question can help to decide what kind of implementation is required ... \$\endgroup\$ – Martin Frank Sep 17 '14 at 7:13
  • \$\begingroup\$ The inventory will be used as a loot container. A rabbit will have rabbit fur (or something similar) in its inventory. \$\endgroup\$ – Bernardo Sulzbach Sep 20 '14 at 16:05
  • 1
    \$\begingroup\$ I was asking because i was expecting this answer... don't judge me but consider the pros of my advice: use a dropping rule rather than implementingan inventory... you can apply this rule for several monsters... a rule needs no space to store (on disk AND in memory)... you alsoo don't have weird stuff in your inventory like fur or meat — remember that spell pickpocket in Ultima6 where you could steal those things from inventory)... if you have questions on looting rules, let me know... \$\endgroup\$ – Martin Frank Sep 20 '14 at 21:02
  • \$\begingroup\$ "Letting you know." I could just google it, but you told me to let you know. I am clueless about how I would implement it. \$\endgroup\$ – Bernardo Sulzbach Sep 20 '14 at 23:10
3
\$\begingroup\$

An Inventory class with a matching field on the abstract class Creature seems best.

You can perform a null check against this field to see if a creature has an inventory.

This also allows you to reuse the inventory class as a field of non-creatures like bags, chests, rooms or anything else that suits your game.

|improve this answer|||||
\$\endgroup\$
3
\$\begingroup\$

In general you should prefer composition over inheritance if answer to question "A is a B?" is "No". E.g. Inventory is a Creature? No, then it should be a component. But Vampire is a Creature, so it's logically inherited.

My other answer to similar question can also be useful.

|improve this answer|||||
\$\endgroup\$
0
\$\begingroup\$

As others have pointer out, you should generally prefer composition over inheritance(is-a vs. has-a):

public class Creature {
    private Inventory mInventory;

    public boolean hasInventory() {
        return mInventory != null;
    }

    public Inventory getInventory() {
        return mInventory;
    }

    public void setInventory(Inventory inventory) {
        mInventory = inventory;
    }
}

This makes a much more flexible architecture in cases where you might want to make an exception for some specific creature. Consider the famous mudcrab merchant in Morrowind for example.

This approach lends itself well to data-drivenness as well. For example, if you would load all your creatures from JSON-files, you could specify a default creature with no inventory and specialize from that if needed:

vampire.json

{
  "type": "vampire",
  ...
}

frank.json

{
  "type": "vampire",
  "name": "Frank",
  "inventory": [
    { "item": "sword", "count": 1 },
    { "item": "gold", "count": 75 },
    ...
  ]
}

Then while reading the creature definitions you could just check if a creature has the inventory-field and set it accordingly:

public Creature parseCreature(JsonObject json) {
    Creature creature;
    if (json.getString("type").equals("vampire")) {
        creature = new Vampire();
    } else if (...) {
        ...
    }

    if (json.has("inventory")) {
        creature.setInventory(parseInventory(json.getJsonObject("inventory")));
    }

    return creature;
}

As you can see, you can achieve many things that would be quite difficult with inheritance/interfaces.

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ "Consider the famous mudcrab merchant in Morrowind for example." Awesome. A lot of stuff made more sense after reading this. EDIT REASON: Enter without hold SHIFT. \$\endgroup\$ – Bernardo Sulzbach Sep 17 '14 at 15:07
0
\$\begingroup\$

Create a drop rule:

here are my assuptions:

  • you have a round based game
  • you have a method that handles input (best case: as a command)
  • you have a list for monsters and a currentMap

when will a drop rule apply? at the beginning of the monsters turn:

//assuming you have a kind of listener that handles all inputs
public void parseInput(Command cmd){

    //1. do players turn
    player.applyEffects(); //like poison or so
    boolean isDead = player.isDead(); //check life points
    if(!isDead){
       executeCommand(cmd);
    }else{
        ...
    }

    //2. do monsters turn
    for (Monster monster: monsterList){
         monster.applyEffects();
         boolean isDead = monster.isDead(); //check life points again

         if(isDead){
             applyDropRule(monster, currentMap); 

             //most important: remove the monster from the map!
             monster.markAsRemoved = true; //but you can't remove the monster right
                                           //now, because you're still iterating 
                                           //throug this list (see below)
         }else{
             ...
         }
    }

    //remove dead ones here
    List<Monster> removeList = new ArrayList<Monster>();
    for (Monster monster: monsterList){
        if(monster.markAsRemoved){
            removeList.add(monster);
        }
    }
    monsterList.removeAll(removeList );

    //3. let the environment do something too
    Field f = currentmap.getField(player.getPos() );
    if(f.isTrap() ){
        ...
    }
}

ok, as mentioned above this is an assupmtion, no we're coming to the interesting part: the drop rule:

again, i have to make another assumptions

  • you know the dropping rules from books or ebooks

ok, then lets see how to do it ^^

public void applyDropRule(monster, currentMap){
    Field field = currentMap.getField(monster.getPos() );

    //dropList depending on current monster
    List<Item> dropList = DropCalculator.getDropsForMonster(monster.getType() );

    //another droplist depending on your level - maybe you can combine these two methods
    List<Item> goodyList = DropCalculator.getDropsForMap(currentMap.getType() );

    //adding 'default' items        
    for (Item item: dropList){
        field.addItem(item);
    }

    //adding goodies
    for (Item item: dropList){
        field.addItem(item);
    }

    //maybe adding a bloodstain (or a green squishy stain)
    if(monster.leavesStain){
         field.addDeco(...);
    }        
}

so ok, lets have a closer look at the dropCaluculator:

public class DropCalculator {

    //we have static access methods, you don't need an instance of this calculator
    //same as java.lang.Math
    public static List<Item> getDropsForMonster(MonsterType type){
        ArrayList<Item> dropList = new ArrayList();

        //it's always about probability
        //e.g. Sheep.chanceToLeaveCrorpse = 0.9 - most likely
        //e.g. Newt.chanceToLeaveCrorpse = 0.2 - not very likely
        //take these values from your rule book
        if (type.chanceToLeaveCorpse > Math.random() ){
            double nutritionValue = type.getNutrition;
            Item meat = ItemFactory.createItem(ItemType.Meat);
            meat.nutrition = nutritionValue;
            dropList.add(meat);
        }
    }

    return dropList;

}

there are a lot of rules that can be applied when a monster dies, so this is just a small impression of what can be done...

|improve this answer|||||
\$\endgroup\$
  • \$\begingroup\$ have you seen how OO-Programming can help you? if player and monster inherit from one class you can use currentmap.getField(player.getPos() ); and monster.isDead(); for both instances \$\endgroup\$ – Martin Frank Sep 22 '14 at 5:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.