TugRulz
TugRulz

Reputation: 65

Design Pattern suggestion for Item Use from Inventory

I'm making an adventure game with java and slick2d because of my school's object oriented software engineering project, unfortunately.

I'm applying MVC architecture and I'm creating Items by reading from a Tiled map file in an ItemFactory class. They seem to be suitable for this kind of game.

There are multiple items in the game which affects the game and especially character. (character is the same as player in this context)

There is a character model and it has an inner class Inventory which basically consist of an ArrayList of items.

Character controllers have a function that is called takeItem that takes a container argument (i.e. shelf) which has also an ArrayList of items, then removes what inside and puts to the inventory.

And it has a function which is called useItem, takes an int index and uses the item in the appropriate index.

Until that, everything is fine.

Now the main problem is, these items have specific uses. For example, health potion increases character's health, invisibility potion makes it invisible etc.

To be sustain a good design, how and where can I interpret and attach functions to these items?

So far I think something like this: 1- I did not add any function to item class. It only has name, description and Enumerated ItemType and int power 2- I'm doing every work in the character controller's useItem() method, i.e.

if (itemBeingUsed.getType() == Common.ItemType.POTION)
    `characterModel.increaseHealth(itemBeingUsed.getPower())`

As you can see, this seems odd because character controller acts like an item interpreter.

3- Another approach may be creating an ItemInterpreter class that takes a character controller. Although this seems more elegant, it will make me do bunch of needless functions in character controller like "increaseCharacterHealth" function that calls character model's increaseHelth() function an so on.

Another possible problem is that I'm using Character Controller's functions to use items, but the items may also affect the game, i.e. slowing time, but Character Controller is below the class hierarchy of game and does not access to Game/GameController functions. Slowing game time etc. seems not the case at least right now, so it looks like I can use Items in CharacterController class but I would like to know your opinion if it is a good design or what could be better.

Some codes to clear issues: (note: they are not complete) Note: I put enumarated types like ItemType, Direction etc. in a Common class which si common to all classes.

Use item function in CharacterController:

public void useItem(int index){ 
        Item item = ((Character)obj).inventory.takeItem(index);
        if (item != null) {
            if (item.getType() == Common.ItemType.POTION) {
                ((Character)obj).increaseHealth(item.getPower()); // I have extended a Person class, now I have to typecast to character everytime, which is also nonsense
            }
            else
                System.out.println("and other items..");
        }
        else
            System.out.println("Cannot use");
    }

I did not put all the code, but I can if I was not clear in explaining.

Upvotes: 0

Views: 2500

Answers (2)

Gnucki
Gnucki

Reputation: 5133

As an SOA (service-oriented architecture) developer, I would not add logic to my items because it becomes really hard to factorize and rationalize my code in that kind of situation. Moreover, remember that composition is far better than inheritance if you want scalable code.

public interface ItemInterface { 
    public String getName();
    public String[] getCharacteristics(); 
}

I would define some item effects handling the logic:

public interface ItemEffectInterface {
    public String getName();
    public void affectCharacter(ItemInterface item, CharacterInterface character, parameters);
    public void affectInventory(ItemInterface item, InventoryInterface inventory, parameters);
    public void affectGame(ItemInterface item, GameInterface game, parameters); 
}

I use interfaces for all my objects to create low coupling between my classes (this provides more scalable, maintainable, and testable code).

You can define an abstract class AbstractItemEffect if you want to define default behavior "do nothing" in your extender item effects for all methods and just define the active methods for each effect.

Then, if you want a dynamic configurable game, you could define the interaction of item and effects in a file/database. Here is a JSON example:

[
    {
        "item": "health potion",
        "effect": "heal",
        "parameters": {
            "hp": 100
        }
    },
    {
        "item": "mana potion",
        "effect": "regen",
        "parameters": {
            "mp": 150
        }
    },
    {
        "item": "mana potion",
        "effect": "drunk",
        "parameters": {
            "duration": 1000
        }
    },
    ...
[

Then use a service class to map the effect to the items when used:

public class ItemUser {
    private Map itemEffects = new HashMap();

    // Call it at service initialization after interpreting the configuration
    // (configuration is optional, you can call it directly of course).
    public void addItemEffect(ItemInterface item, ItemEffectInterface itemEffect, parameters) {
        Map effect = new HashMap();
        effect.push("effect", itemEffect);
        effect.pust("parameters", parameters);

        this.itemEffects.put(item.getName(), effect);
    }

    // Call it when you want to use an item.
    public void useItem(ItemInterface item, Character character, GameInterface game, InventoryInterface inventory) {
        Map itemEffects = this.itemEffect[item.getName()];

        // Process all the effects attached to your item 
        // on game, character, inventory, ...
    }
}

With this architecture, you can attach an effect to many items and an item to many effects. This should prevent you from making code duplications.

At the same time, each item and item effect will handle its own complexity and won't make the whole system more complex. This is not the case with a pattern 'switch case' like the one you describe in 2.. Imagine your class if you have 100 effects and 200 items...

Sorry for my bad/incomplete JAVA code!

Upvotes: 1

Tim B
Tim B

Reputation: 41168

This is a classic case where interfaces, inheritance and polymorphism come into play. Whenever you use a switch or sequence of if then you should consider polymorphism.

In this case define an interface or abstract class for a useable item.

public interface Useable {
   public void useItem(Person personUsing);
}

Now each item just implements useable and you can just check if an item is useable, if it is display the option to use it and call the method when used.

Upvotes: 4

Related Questions