views:

226

answers:

7

I am working on a role playing game for fun and to practice design patterns. I would like players to be able to transform themselves into different animals. For example, a Druid might be able to shape shift into a cheetah. Right now I'm planning on using the decorator pattern to do this but my question is - how do I make it so that when a druid is in the cheetah form, they can only access skills for the cheetah? In other words, they should not be able to access their normal Druid skills.

Using the decorator pattern it appears that even in the cheetah form my druid will be able to access their normal druid skills.

class Druid : Character
{
   // many cool druid skills and spells
   void LightHeal(Character target) { }
}

abstract class CharacterDecorator : Character
{
    Character DecoratedCharacter;
}

class CheetahForm : CharacterDecorator
{
    Character DecoratedCharacter;
    public CheetahForm(Character decoratedCharacter)
    {
       DecoratedCharacter= decoratedCharacter;
    }

    // many cool cheetah related skills
    void CheetahRun()
    {
       // let player move very fast
    }
}

now using the classes

Druid myDruid = new Druid();
myDruid.LightHeal(myDruid); // casting light heal here is fine
myDruid = new CheetahForm(myDruid);
myDruid.LightHeal(myDruid); // casting here should not be allowed

Hmmmm...now that I think about it, will myDruid be unable to us the Druid class spells/skills unless the class is down-casted? But even if that's the case, is there a better way to ensure that myDruid at this point is locked out from all Druid related spells/skills until it is cast back to a Druid (since currently it's in CheetahForm)

+1  A: 

Maybe rather than placing the skill set for a Druid into the Druid class, you could move this skill set into a decorator itself.

Druid myDruid = new Druid();
myDruid = new StandardDruid(myDruid);

Ignoring the horribly named "StandardDruid" class, I'm hoping you can see where I'm going with this :)

There are upsides to this arrangement, namely that all of your skill based code will be contained consistently inside of decorators, rather than half inside the original Druid class, and the remaining skills in decorators. If you were to have another class that could morph into a Druid, you could easily do this without any code duplication.

Adam MacLeod
I almost thought this would work! But for my case this wont work. For example, `Druid` derives from `Character` so I could downcast `Druid` to a `Character` before decorating with `CheetahForm`. But if I do that I'll lose some information about the `Druid`, for example, which spells the character currently has memorized.
mikedev
A: 

I would solve this by changing the LightHeal method to virtual, then override it in the CheetahForm class to either throw an exception, or show a message to the user that they cannot do that right now:

class Druid : Character
{
   // many cool druid skills and spells
   virtual void LightHeal(Character target) { }
}

abstract class CharacterDecorator : Character
{
    Character DecoratedCharacter;
}

class CheetahForm : CharacterDecorator
{
    Character DecoratedCharacter;
    public CheetahDecorator(Character decoratedCharacter)
    {
       DecoratedCharacter= decoratedCharacter;
    }

    // many cool cheetah related skills
    void CheetahRun()
    {
       // let player move very fast
    }

    override void LightHeal(Character target)
    {
        ShowPlayerMessage("You cannot do that while shape shifted.");
    }
}

Whenever I play a game that involves this type of scenario (e.g. World of Warcraft) the UI changes to prevent these spells from being casted, but the user is still able to attempt to cast these spells by way of macros, and so handling it in this way helps stop this.

I don't think there's a way of 'hiding' the method without using an interface in place of a class to describe the CheetahForm, as referencing it to this would only show methods available from the interface.

Codesleuth
+10  A: 

You should not have your spells and skills as method in the class. They should be a collection of Spell and Skill objects which are accessible through the Character (or possibly Creature) interface. Then your CheetaForm could override the getSkills getSpells methods to return a new modified collection of appropriate skills.

willcodejavaforfood
I very much like this idea. I did this for a game back when I was in university, and it works well. The user interface dynamically populated whenever a skill set changed.
Codesleuth
'You cannot' or 'You shouldn't'?
Mark Withers
@mark.withers - LOL fair point I'll update my post :)
willcodejavaforfood
A: 

I'd just make a property in the druid class "Form" and do a if check when casting the spell whether or not the druid is transformed or not.

Ravenheart
A: 

How about this?

interface ISkillState { ... }

interface ISkill { ... }

interface IForm
{
    Collection<ISkill> skills { get; set; }
    Dictionary<ISkill,ISkillState> skillStates { get; set; }
}

class Character
{
    private IForm actualForm;
    private Collection<IForm> currentForms;

    ...
}

and for example:

class Spell : ISkill {}

class SpellState : ISkillState {}
naeron84
A: 

Instead of making Druid a derived class from Character, consider using the Type Object pattern. First, split out character class-specific skills into their own objects:

abstract class Skill
{
    void Perform();
}

class LightHeal : Skill
{
    public void Perform()
    {
        // stuff...
    }
}

class CheetahRun : Skill
{
    public void Perform()
    {
        // stuff...
    }
}

// other skill classes...

Now, get rid of the derived character classes by doing this instead:

class CharacterType
{
    public readonly List<Skill> Skills = new List<Skill>();
}

class Character
{
    public IEnumerable<Skill> Skills { get { return mType.Skills; } }

    private CharacterType mType;
}

Your character "classes" can now be created as objects:

CharacterType druid = new CharacterType();
druid.Skills.Add(new LightHeal());

CharacterType cheetah = new CharacterType();
cheetah.Skills.Add(new CheetahRun());

Now, by changing mType in Character from druid to cheetah, you can in effect change its class, and swap out its available skills.

munificent
I'm really starting to dig this approach! One more thing I can do is make the `CharacterType` a `List<mType>`. Then I can even allow characters to subclass so they can keep all the skills/spells they subclass into. I will prototype this and see how it goes.
mikedev
Yes, that's exactly right. Once you're using Type Objects, you can implement single or even multiple inheritance with them. The sky's the limit.
munificent
please finish your game programming design patterns book so i can buy a copy! :)
mikedev
Ok, I took a slightly different approach to this. Instead of using `CharacterType` I am using `List<Character>` for the `mType`. Then I am putting all the skills/spells inside decorators (like my `CheetahForm` decorator). Now I can decorate my characters to give them different skills/spells instead of just setting their `mType`. I'm not sure if I like it though...it makes sense to me to do it this way but it feels like i'm making it more complicated than it needs to be.
mikedev
I found one advantage in doing it the decorator way I describe in my previous comment. It makes it easy for me find out if the `Character` is multi-classing, sub-classing, or a different form, etc.For example, I can down-cast like so `var cheetaType = CheetaCharacter as CheetahForm`, if it's null then I know the character isn't in CheetahForm.
mikedev
With a type object, you can accomplish the same by simply doing `if (mType != cheetah)`.
munificent
+1  A: 

Sorry to inform you guys but you are all wrong trying to use a hammer on thing that is not a nail :-P

To solve your problem mikedev you need to go beyond OOP since OOP cannot solve this properly. Take a look Entity Systems/Component Systems.

First read you should do is http://www.devmaster.net/articles/oo-game-design/

Then your journey to the world of game design will begin.

Mikeon