Design Question: Damage model

Hi there,
im currently thinking about a flexible damage system and unfortunately im stuck at a certain point.

We have several different objects, that will be modeled as such:

  • Weapons, that provide “damage sets” consisting of one or several damagetypes
  • Damage types, that represent some kind of real damage. This could be anything, from physical damage to magic stuff like paralysis
  • Armor: They have a set of filter objects that flter applied damage
  • Damagefilter: A object that can modify a specific damagetype.

If damage should be inflicted to some object, the damage set is retrieved from the weapon. this set is then passed to the filterset provided by the armor, which will modify each damage type trough a interface which is common for all damagetypes. After that, each damagetype will be apply()-ed with the target object as parameter.
The apply() method of the damagetype implements modifying a target object through a interface, like “damageable” wich deals with physical damage. Other damage types may use different interfaces like “movable”.

And this is the point where im stuck; because the target object will be not only a “unit” object that implements all that interfaces.
Say, for example, a unit is attacking some wall.
The used weapon has physical damage and paralization.

Let me explain that with some basic pseudocode:
(interface damagetype is cut out, it provides methods to the filter objects. That works i think.)

class PhysicalDamage implements damagetype{
    // implementing the apply() method from damagetype
    function apply(object) {
        object.removeHealth(10); // remove 10 HP; this is a method provided by the "damageable" interface
    }
}

class ParalizerDamage implements damagetype{
    // implementing the apply() method from damagetype
    function apply(object) {
        object.slowDown(5); // slow down target; provided by the "movable" interface
    }
}

class TestWeapon {
    function getDamageSet() {
         return new Array(new ParalizerDamage(), new PhysicalDamage());
    }
}

So, like you see, apply() misses some technique to see, if it can apply its damage to the target object. However, it cannot know which objects will be passed in, so i need some generic way to test if damage an be applied.
And exactly that is the point, which i cannot solve. How to do that in a generic, object oriented way that may be extended in the future?
As a side note, it is okay if the DamageType would silently do nothing if the target object cant be modified by it because the damaga does not apply (wall cant move anyway).
I thougt about using handlers, but dont know if this is the best way to do that. In this idea, i wanted apply() to fetch a suitable handler from the target object, which then handles the damagetype; but i wanted the DamageType to contain the damage-apply code, not some handler.

I think you have two options here:

  1. use instanceof on the passed in object to see, if it implements the interface you want to call a method on
    (before you ask, instanceof is fast enough. I for myself also don’t find it impropriate OO design, like other might)

  2. weave all methods calleable by apply() into the damageable interface and just do nothing, if this particular
    damage won’t affect the object. This is only extendable, if you let all your objects extend an AbstractDamageable
    which implementes all Damageable methods empty, so when you add another method to Damageable, you don’t
    have to change all implementing classes every time.

Hello and thanks for your input.
I already considered using instanceof, but was not sure if this is appropriate.
But if i think about that twice, it should be sufficient and easy, because the damageType objects will know how to modify objects based on the knowledge of interfaces. This way, i only need to implement the neccessary interfaces (eg damageable, movable in the example above) without knowing that some weapon can inflict damage upon the object. This should be much more flexible since:

  • the damageType dont kneed to know which objects can be targeted
  • the objects dont need to know if and which damageTypes must be considered
  • extension of both, damageTypes and target objects is very easy.

However, i just dont know if i am missing some important design thought…

Maybe KISS. You may be on the edge of over-engineering here. It would probably easier to simply have a takeDamage(DamageType) method and appropriately implement that on the different possible entity subclasses of your game.

Having said that, I think designing abstractions are the more interesting parts of programming, but you may have diffculties to get anything done this way (I have ;))

[quote]takeDamage(DamageType)
[/quote]
Wouldnt this result in a run time exception since if a DamageType, that could not be handled by the target, would be called?
One damageSet could be applied to many different targets!
Or must all available applyDamage() Methods be implemented by each object that may become a target object, regardless which damageType it may apply?
And each class extending a abstract damagetaker-class would render me inflexible in the extension hierarchy…
I think that would be harder to maintain as the model above, dont you think that too?

There would only be the takeDamage() method on target-objects. In that way, any entity type would be responsible for implementing its specific reaction when taking damage, like


void takeDamage(DamageType damage)
{
   if(damage instanceof PhysicalDamage)
   {
      health -= ((PhysicalDamage) damage).getHitPoints() / shield;
   }

   if(damage instanceof ParalizerDamage)
   {
      agility -= ((ParalizerDamage) damage).getEffectStrength() / resistence;
   }

   // etc.
}

I am not saying that this is a better approach, but you might get faster progress with your game, because you don’t try to implement a damage handling framework but implement the game logic right away.

Just ask yourself any time, when you think about creating a framework: “Do I really need it for my game?” I tend to create frameworks, because I have no real idea where my game should be going and I am a little afraid of finishing something :wink:

But maybe you simply need a more flexible damage framework to achieve the goals of your game. Only you do know that…

Just to be sure, i understand you correct:

[quote]There would only be the takeDamage() method on target-objects.
[/quote]
This implies, there is a abstract class or interface “damageType”.

That sound simple and good, i wonder why i havent considered this at first.
The way this forms in my mind is now:

  • Interface “attackable”, requiring the takeDamage(DamageType) method
  • Interface “DamageType” providing standardized methods for getting the needed values (duration and strenght) and manipulating them (for filters as mentioned above)

This sounds good because this way, not the DamageType decides its impact on targets, but the targets themselves decide that, which is more logical. A Sword strike has a different impact on a human than on a wall, but the sword inflicting the damage does not know that at all.

This way i also can make a class AbstracTUnit with default handling for most damage Types, but i am still flexible on that.

Just one question left, the casting, why is it needed? for the compiler not to complain? or is the passed variable “damage” casted to DamageType at call time? I thougt, variables (and thus the type) would be passed by reference?

Usually it would be done this way, yes.

If you let DamageType provide all your values, you won’t even need any subclasses for the damages.

Filters won’t affect the DamageType instance though, just how it is applied to the target object, so I don’t see special methods for filters needed on a DamageType. It should be more like a filter factor lookup on your armor/inventory objects or something.

That was my motivation.

Yep. Implement as much as possible in a base class and just override it in subclasses. You may want to group your sections in dedicated methods like:


void takeDamage(DamageType damage)
{
   applyPhysicalDamage(damage);
   applyMagicalDamage(damage);
   applyHandycaps(damage);
   // etc.
}

so you can either override the takeDamage method in your subclasses and call the applyXXX methods you want to remain the same or override a specific applyXXX() method that would be called from your default takeDamage implementation (template method pattern).

I was under the impression, that you would have different DamageType subclasse providing different informations, so you would of course need to cast to this specific classes at runtime. The compiler only sees an object of the declared argument type of your method (here DamageType) and does not know the additional methods and properties a specific subclass would provide. Hence the cast.

If you generalize all your information and make it accessible via the DamageType interface, you wouldn’t need those of course. It might also be more practical to only hava one Damage class, since you would produce your spells and attacks only by creating a new instance and don’t have to handle different classes.

The OO way of doing this is probably to use the Visitor pattern. However, in game programming sometimes you have to leave OO purism to one side because at the end of the day getting a decent framerate is more important than theoretical purity.

You already helped me alot!

[quote]Filters won’t affect the DamageType instance though, just how it is applied to the target object, so I don’t see special methods for filters needed on a DamageType. It should be more like a filter factor lookup on your armor/inventory objects or something.
[/quote]
The idea was that a unit can have several armors, each one consisting of several filter types. Armors may be extendet at runtime (for example, the user inserts some gem into his mailshirt that now provides additional protection against burn damage).
This should be accomplished by just adding some new filter object to the units filterstack which is provided by one or several worn armours.
In takeDamage() there would be a lookup to the units worn armor which returns a filter set that could be applied do the damage objects.

The same intention is true for the damageTypes, it should be possible to modify the damage set inflicted by a particularly weapon (ie. user inserts gem to units sword that now also inflicts poison damage)

One particular armor can be worn by different units, as well as a particular weapon can be used by different units. However they remain the same armor and the same weapon.

Also I find the visitor pattern a bit clumsy and confusing at times. It has some use cases, however.

This may be dangerous to do, since the damage objects are passed in per reference. So if you apply a filter directly on the given damage, you would change its values outside of the takeDamage() method. This may lead to hard to debug errors, since you have to know, that your damage objects are throw away instances. If you store the damage as a field in a weapon for instance, you would change the weapons damaging ability on every attack. This is not what you want!

Best practice is to decouple this like e.g.:


void takeDamage(Damage damage)
{
  // modifiedDamage is a new instance (maybe using damage.clone())
  Damage modifiedDamage = filterStack.applyTo(damage);
  //...
}

Yes, the DamageSets are fresh each time; the filters are operating on one particular damage set in time, not on the “original” one.

A weapon provides a damage set (some sort of factory) which is then changed by several (or one or none) filters and the remining damageTypes are then applied to a unit.
Next stroke starts with a fresh damageset and a fresh filterstack.

Damage as you call it is the effect of degrading of sturdiness or health depending on the target. Or something I would model it differendly to begin with…

anyways I would figure out your game model (as in gameplay not OO) first and work with that instead of designing a genetric thing. if you want to go that way per se

I’d just have an class effect and apply the visitor pattern both ways. or visitor one way and instanseoff the other. As some effect behave different depending on the target and some targets behave differend depending on the effect. (or effects already applying to the target.)