views:

248

answers:

11

I am creating a role playing game for fun and as a learning experience. I am at the point where my character (a wizard) is cast spells. I am using a strategy pattern to set the spell they are going to cast before casting the spell. The reason I went with this approach is because I want to be able to add different spell types later on w/ out having to mess with the character/wizard class.

My question - is this a bad design? Is there a better/cleaner/easier approach for this?

I am trying to stay away from being "that guy" who tries to make everything fit into a design pattern. But in this case I feel like it's a decent fit.

Here is what my code looks like with 2 spells so far

public class Wizard : Creature
{
   public List<Spell> Spells { get; set; }

   public void Cast(Spell spell, Creature targetCreature)
   {
      spell.Cast(this, targetCreature);
   }
}

public abstract class Spell
{
   public string Name { get; set; }
   public int ManaCost { get; set; }
   public Spell(string name, int manaCost)
   {
      Name = name;
      ManaCost = manaCost;
   }
   public void Cast(Creature caster, Creature targetCreature)
   {
      caster.SubtractMana(ManaCost);
      ApplySpell(caster, targetCreature);
   }
   public abstract void ApplySpell(Creature caster, Creature targetCreature);
}

// increases the target's armor by 4
public class MageArmor : Spell
{
   public MageArmor() : base("Mage Armor", 4);
   public override void ApplySpell(caster, targetCreature)
   {
      targetCreature.AddAC(4);
   }
}

// target takes 7 damage
public class FireBall : Spell
{
   public FireBall() : base("Fire Ball", 5);
   public override void ApplySpell(caster, targetCreature)
   {
      targetCreature.SubtractHealth(7);
   }
}

now to cast a spell we do something like this:

Wizard wizard = new Wizard();
wizard.Cast(new Spell.MageArmor(), wizard); // i am buffing myself 

UPDATE: updated code with some suggestions from the answers below

+1  A: 

For some reason, "spells" feel more like a command pattern to me. But I've never designed a game so...

Josh Einstein
+3  A: 

It's not particularly clear why you'd want it to be a two stage process unless that's going to be exposed in the UI (i.e. if the user will set the "loaded spell" and can later change their mind).

Additionally, if you are going to have a property rather than just wizard.Cast(new Spell.MageArmor(), wizard), having a SetSpell method is a bit odd - why not just make the LoadedSpell property public?

Finally, do spells actually have any mutable state? Could you just have a fixed set of instances (flyweight/enum pattern)? I'm not thinking of the memory usage here (which is the normal reason for the flyweight pattern) but just the conceptual nature of it. It feels like you want something which is really just like a Java enum - a set of values with custom behaviour. It's harder to do that in C# because there's no direct language support, but it's still possible.

The actual pattern within the spell (having a caster and a target) seems reasonable, although you may find it becomes inflexible if you want to be able to have area effect spells (with a target location rather than a specific creature) or spells which curse/bless items etc. You may also need to pass in the rest of the state of the game world - e.g. if you have a spell to create minions.

Jon Skeet
diablo-style spells, i like it :)
erelender
Thanks for the feedback Jon. The only reason I had the SetSpell method is because that's how I learned to do the strategy pattern. I've cleaned up my code using your suggestion (in my source, not in my SO question) and it makes much more sense. As for you last paragraph...you've given me a lot to think about :)
mikedev
I reckon spells probably do have a state: what about spells with a dutration such as a regeneration spell, or something that has a charge time?
Ed Woodcock
@Ed: Good points. I'm not sure whether I'd model those in the same way, but they're certainly things to think about.
Jon Skeet
@Jon No, probably not, a better method would be to have an 'Effect' object or some other non-interlinked class, but it is definitely an option. One example I can think of where having a Spell class to handle effects would be something like Chain Lightning, which could simply just have a .Jump method on itself, instead of having to instantiate new Spell and Effect objects every time you want it to jump!
Ed Woodcock
A: 

First of all: There is always a better/cleaner/easier approach for everything.

But in my eyes you've made a decent abstraction of your challenge which can be a solid base for further improvements.

Björn
A: 

I might be missing something, but the trio WizardSpells, LoadedSpell, SetSpell seems like it could be clarified. Specifically, I don't see the list being used in your code so far. I would probably add spells available to the Wizard to the list, with LearnNewSpell(Spell newSpell), and check that LoadSpell uses a spell from that list.
Also, you might consider at some point adding some extra info about the type of caster on the Spell, if you are going to have multiple types of Casters.

Mathias
LoadedSpell and SetSpell are not needed but I had them in there because that's how I recently learned how to do the strategy pattern. It's been removed from my source code (but not from the question). And along the lines that you were thinking, `WizardSpells` is a list of spells the character currently knows. It's not used in this example though
mikedev
Probably update the question if you want further advice
Rob Fonseca-Ensor
Ok, updated the source code in the question
mikedev
+2  A: 

I probably would not use subclassing for each spell here. I would try and put it on disk by using XML or JSON and create them dynamically.

--Edit to clarify (hopefully)--

This approach would require to really plan out in advance as much as possible. You would have to defined attributes as:

  • Name
  • Description
  • Duration
  • Target (self, area, other)
  • Type (bonus, damage, curse)
  • Effect (ex: 1d6 frost damage, +2 Armor Class, -5 Damage Resistance)

Wrapping all this behaviour in a generic spell class should make it really flexible and more straight forward to test.

willcodejavaforfood
How do I do that while having different behavior for the spells? For example, MageArmor adds to the targets armor while FireBall does damage to the target.
mikedev
Well it would require some careful planning obviously. Take a look at a Pen and Paper RPG and you will see that everything is carefully defined. There are different types of bonuses, damage etc.
willcodejavaforfood
If you have generic properties on characters then most effects can be represented as a property_name, property_change pair. Look up the property by name, add the change to it. Works for healing, damage, armour, whatever. Aggregating those probably accounts for 90% of your spells. The other 10% can be done via inheritance, or perhaps a more complex generic scheme.
Kylotan
I agree with willcodejavaforfood. I wrote a detailed description of how this might work. I simply couldn't contain my excitement.
Benny Jobigan
willcodejavaforfood
A: 

What do your unit tests look like?

Does the design make it easy for you to write the tests you want?

Ian Ringrose
-1 "...role playing game *for fun*". Who the hell writes unit tests for fun?!?!
Ed Woodcock
Actually, TDD is something I am practicing as well :) To answer Ian, yes the tests are easy to write but as people have been pointing out, I'm in trouble if I want the target of the spell to be something other than another creature (items, area of effect spells, etc)
mikedev
@ed me. It's not fun if your game crashes. TDD is more about design than test coverage anyway, and the OP wanted to improve his design. Easier to test == better design, no question.
Rob Fonseca-Ensor
@Rob sometimes I think I'm in the wrong career: to me testing is something you do to prove to the customer and the rest of your dev team that your product is algorithmically sound, not something to spend your sunday afternoon doing ! :)
Ed Woodcock
@Ed ok so there's no customer and there's no rest of the dev team on this project, but how do you prove to *yourself* that the product is algorithmically sound? Run the app and test it by hand? I'm going to assume mike doesn't even have a UI yet - so that's going to be hard. Would you write a tiny (5 minute's work, max) console app? It might pay for you to think of a unit test as a repeatable, keepable console app snippet...
Rob Fonseca-Ensor
@Rob - sorry to be pedantic but tests don't prove anything. Proof comes from inspecting the algorithm for correctness. A test only shows that the tested code can produce the right answers under the circumstances of the test. I would argue that learning to inspect your own code for correctness is far more important than learning to write tests that merely check for absence of obvious incorrectness.
Kylotan
@Kylotan, if it is hard to write a unit test, then the design is not clean. (However being easy to test is not enough to prove it is a good design)
Ian Ringrose
I wouldnt unit test a project like this unless I was being paid :-P (or learning)
@Rob To be honest, I wouldn't bother proving that this is sound, I'd just mess about until the point that I was happy with my work and happy that I'd learnt something new, or improved on an existing skill. I think tests are superficial to that goal, and only provide any value if what you're trying to learn is Unit Testing itself.
Ed Woodcock
@Ian - I don't necessarily agree. Good unit tests often involve the need to be able to inject mock objects via the interface that would normally not need to form part of the interface if not for the testing. That is an example of where the test makes the design less clean in my opinion. Using dependency injection when only testing needs it is making the interface wider and less manageable.
Kylotan
@Kylotan @Ed so you never need to compile your code until you ship it? All i'm trying to say is that before you've even built a GUI, unit tests are a really easy, simple, repeatable way to check that code does what you expect it to. If you test-first, then it gets really easy to come up with a good design (SOLID principles etc). If you've not had this experience when using TDD so far, then you need to keep trying and learning and reading, because to me, TDD is second nature and I couldn't do without it. But it took a long time to get there.
Rob Fonseca-Ensor
+1  A: 

The biggest problem I see with this pattern is that all spells have to remember to subtract their mana cost. How about:

public abstract class Spell
{
   public string Name { get; set; }
   public int ManaCost { get; set; }
   public Spell(string name, int manaCost)
   {
      Name = name;
      ManaCost = manaCost;
   }

   public void Cast(Creature caster, Creature targetCreature)
   {
       caster.SubtractMana(ManaCost); //might throw NotEnoughManaException? 
       ApplySpell(caster, targetCreature);
   }

   protected abstract void ApplySpell(Creature caster, Creature targetCreature);
}

Also, should Wizard extend PlayerCharacter, which would extend Creature?

Rob Fonseca-Ensor
Thanks for this. In my source I actually have pre and post casting methods. My pre-casting method is where I check to see if there is enough mana but I like the way you've done it. And yes, wizard extends Creature...updated source code.
mikedev
A: 

I tend to think that your spells and items should really not be classes but a composition of effects.

Here's my take on it, feel free to expand. It's basically using a composite approach and a two-phase evaluation of spell effects, so each class can add specific resitance.

[Serializable]
class Spell
{
    string Name { get; set; }
    Dictionary<PowerSource, double> PowerCost { get; set; }
    Dictionary<PowerSource, TimeSpan> CoolDown { get; set; }
    ActionProperty[] Properties { get; set; }
    ActionEffect Apply(Wizzard entity)
    {
        // evaluate
        var effect = new ActionEffect();
        foreach (var property in Properties)
        {
            entity.Defend(property,effect);
        }

        // then apply
        entity.Apply(effect);

        // return the spell total effects for pretty printing
        return effect;
    }
}

internal class ActionEffect
{
    public Dictionary<DamageKind,double> DamageByKind{ get; set;}       
    public Dictionary<string,TimeSpan> NeutralizedActions{ get; set;}       
    public Dictionary<string,double> EquipmentDamage{ get; set;}
    public Location EntityLocation{ get; set;} // resulting entity location
    public Location ActionLocation{ get; set;} // source action location (could be deflected for example)
}

[Serializable]
class ActionProperty
{
    public DamageKind DamageKind { get;  set; }
    public double? DamageValue { get; set;}
    public int? Range{ get; set;}
    public TimeSpan? duration { get; set; }
    public string Effect{ get; set}
}

[Serializable]
class Wizzard
{
    public virtual void Defend(ActionProperty property,ActionEffect totalEffect)
    {
        // no defence   
    }
    public void Apply(ActionEffect effect)
    {
        // self damage
        foreach (var byKind in effect.DamageByKind)
        {
            this.hp -= byKind.Value;
        }
        // let's say we can't move for X seconds
        foreach (var neutralized in effect.NeutralizedActions)
        {
            Actions[neutralized.Key].NextAvailable += neutralized.Value;
        }

        // armor damage?
        foreach (var equipmentDamage in effect.EquipmentDamage)
        {
            equipment[equipmentDamage.Key].Damage += equipmentDamage.Value;
        }
    }
}

[Serializable]
class RinceWind:Wizzard
{
    public override void Defend(ActionProperty property, ActionEffect totalEffect)
    {
        // we have resist magic !
        if(property.DamageKind==DamageKind.Magic)
        {
            log("resited magic!");
            double dmg = property.DamageValue - MagicResistance;
            ActionProperty resistedProperty=new ActionProperty(property);
            resistedProperty.DamageValue = Math.Min(0,dmg);                
            return;
        }           
        base.Receive(property, totalEffect);
    }
}
Florian Doyon
I wrote a similar suggestion, and you beat me too it by 1 minute. =P However, I focused more on the xml side of things.
Benny Jobigan
Want to build a roguelike then ? :D
Florian Doyon
+3  A: 

Following what Willcodejavaforfood said, you could design a SpellEffect class that describes a single effect your spell could have. You can create a "vocabulary" to use to describe:

Attributes for Spell:

  • Name
  • Mana cost
  • Target restriction of the whole spell (player, npc, monster,...)
  • Total duration of spell (highest of SpellEffect durations) (10 sec, 5 ticks,...)
  • Casting time
  • Spell range (5 meters, 65 units, ...)
  • Fail rate (5%, 90%)
  • Time to wait before this spell can be cast again (Recast time)
  • Time to wait before ANY spell can be cast again (Recovery time)
  • etc...

Attributes for SpellEffect:

  • Type of effect (defense, offense, buff, debuff,...)
  • Target of effect (self, party, target, area around target, line to target,...)
  • Property or stat the effect acts on (hp, mana, max hp, strength, attack speed,...)
  • How much the effect changes the stat (+10, -500, 5%,...)
  • How long the effect lasts (10 sec, 5 ticks,...)
  • etc.

I would imagine that your vocabulary (the words in parentheses above) would be defined in a set of enums. It might also be advisable to create a class hierarchy to represent the SpellEffect types, instead of using an enum for that particular attribute, because there might be a SpellEffect type that doesn't need all these attributes or perhaps there's some kind of custom logic for each basic SpellEffect type that I'm not thinking about. But that also might complicate things too much. KISS principle =).

Anyway, the point is that you are pulling out the specific information on a Spell's effect into a separate data structure. The beauty of this is that you can create 1 Spell class and make it hold a List of SpellEffects to apply upon activation. Then the spell can perform multiple functions (damage enemy and heal player, aka life tap) in one shot. You create a new instance of Spell for each spell. Of course, at some point you will have to actually create the spells. You could easily throw together a spell editor utility to make that easier.

Furthermore, each SpellEffect you define can very easily be written to and loaded from XML by using System.Xml.Serialization's XmlSerializer class. It's a breeze to use on simple data classes like SpellEffect. You can even just serialize your final List of Spell to xml too. For example:

<?xml header-blah-blah?>
<Spells>
  <Spell Name="Light Healing" Restriction="Player" Cost="100" Duration="0s"
         CastTime="2s" Range="0" FailRate="5%" Recast="10s" Recovery="5s">
    <SpellEffect Type="Heal" Target="Self" Stat="Hp" Degree="500" Duration="0s"/>
  </Spell>
  <Spell Name="Steal Haste" Restriction="NPC" Cost="500" Duration="120s"
         CastTime="10s" Range="100" FailRate="10%" Recast="15s" Recovery="8s">
    <SpellEffect Type="Buff" Target="Self" Stat="AttackSpeed" Degree="20%" Duration="120s"/>
    <SpellEffect Type="Debuff" Target="Target" Stat="AttackSpeed" Degree="-20%" Duration="60s"/>
  </Spell>
  ...
</Spells>

You could also chose to put your data in a database instead of xml. Sqlite would be small, fast, easy, and free. You can also use LINQ to query your spell data from xml or sqlite.

Of course, you could do something similar for your monsters and such, too--at least for their data. I'm not sure about the logic part.

If you use this kind of system, you can get the added benefit of being able to use your Creature/Spell system for other games. You can't do that if you "hard code" your spells. It will also allow you to change the spells (class balancing, bugs, whatever) without having to rebuild and redistribute your game executable. Just a simple xml file.

Holy cow! I'm really excited about your project now and how something like I described could be implemented. If you need any help, let me know!!

Benny Jobigan
I like the "Then the spell can perform multiple functions". TheElderScroll allowed you to do this, with an in-game editor :)
PATRY
Oh yea, that would be sweet... You could craft your own unique spells in-game instead of just picking from a set list. Each effect can have a cost which adds up to the total spell cost.
Benny Jobigan
ElderScroll had a bug with that : if you created a spell that costed more than 2*16 mana point, only the overflow was taken into account. Can you say "orbital nuke spell" :)
PATRY
@Benny thank you very much for this answer. I am going to try prototyping this approach
mikedev
@mikedev My pleasure. =)
Benny Jobigan
+2  A: 

It's natural to encapsulate "Spells" with the Command Pattern (which is basically what you've done). But you run into two problems:-

1) You've got to recompile to add more spells

  • You can enumerate every possible action it is possible for a spell to take, then define the spells in some external format (XML, Database) which gets loaded into your application on startup. Western RPGs tend to be coded like this - a "spell" consists of "Apply spell effect #1234 with parameter 1000", "play animation #2345", etc.

  • You can expose your gamestate to a scripting language, and script your spells (you can also combine this with the first idea so that in most cases your scripted spells are just calling pre-defined effects in code). Duel of the Planeswalkers (the M:TG game on X-Box 360) was written broadly with this approach

  • Or you can just live with it (I do...)

2) What happens when your spell target isn't a creature?

  • If you're exposing your gamestate to your spell scripts, this isn't a problem because your scripts can do anything they like within the context of what you're exposing.

  • Otherwise, you'd be best making a generic type.

I generally do something like the following (and not just in games either, I've been using this kind of pattern to represent behaviours in mutli-agent-systems):-

public interface IEffect<TContext>
{
  public void Apply(TContext context);
}

public class SingleTargetContext
{
  public Creature Target { get; set; }
}
public class AoEContext
{
  public Point Target { get; set; }
}
// etc.

The advantage of this pattern is that it's really flexible for doing those "odd" things that you'd often expect spells to be able to do that more fixed models won't be capable of. You can do things like chain them together. You can have an Effect which adds a TriggeredEffect to your target - good for doing something like a Thorns Aura. You can have an IReversibleEffect (with an extra Unapply method) good for representing buffs.

That article on Duel of the Planeswalkers is really excellent reading though. So good I'll link it twice!

Iain Galloway
A: 

I think your design looks fine. Since each Spell class is basically a wrapper around a function (this is more properly the Command pattern, not Strategy), you could get rid of spell classes completely and just use functions with a little bit of reflection to find the spell methods and add some metadata to them. Like:

public delegate void Spell(Creature caster, Creature targetCreature);

public static class Spells
{
    [Spell("Mage Armor", 4)]
    public static void MageArmor(Creature caster, Creature targetCreature)
    {
        targetCreature.AddAC(4);
    }

    [Spell("Fire Ball", 5)]
    public static void FireBall(Creature caster, Creature targetCreature)
    {
        targetCreature.SubtractHealth(7);
    }
}
munificent
What happens when you need to add another behaviour, such as triggering an animation or rendering a sprite to a spell?
Rob Fonseca-Ensor