I've been trying to wrap my head around this architectural design problem. Think of a game that has lots of spells and buffs (World of Warcraft, Diablo II etc), i.e. spells may cause buffs on characters. Each buff may affect one or several properties. For example a frost bolt normally slows down the target it hits, but if it does a critical hit, it will freeze the target for a moment. The frost bolt will also cause cold damage. A shadow bolt might cause some kind of "black" damage and cause confusion (i.e. invert controls, randomize spell target etc).

I tried to map out the architecture for this kind of spell-buff system, but I'm having trouble connecting the dots. First I thought of entity-component system where each character would have a list of components (health, mana, input...), and each buff would "affect" all these components. The components would then choose whether or not the buff affects it. For example, a "damage-over-time" buff should affect the health component, but not the mana component. The aforementioned shadow bolt would affect both health and input components. However, this feels a bit iffy to do if there's tens or hundreds of buffs and components.

Any ideas on how to approach such a design?

I've seen this done two ways for sure, and no doubt there are plenty of others I don't know about.

1. Buffs directly add to stats. When they end they subtract their value from the stats.
2. Buffs are tracked in an array or list and when the stats are relevant, the array or list is processed to find the current value.

The advantage to the first is that it's relatively easy to manage. The disadvantage is that it is relatively easy to get strange behavior when buffs wear off. As a result you tend to do a lot of sanity checking before and after the buff, but don't have to worry about it during the buff period.

The advantage to the second is that you don't have to do any sanity checking before or after the buff. The disadvantage is that you constantly have to do sanity checking while it's active.

DOTs are a bit different. Depending on your system, it might be easiest to make them invisible short lived enemies that do the damage rather than being part of the same system as stat boosters.

• I'd probably do something along the lines of the second one. It would keep the base stats and the buffs separate, right? – manabreak Sep 12 '14 at 8:33
• Yep, which is why I ended up going that route myself. It also allows you to have the same buff object (I'm in Java for this project) handle both the stat changes as well as knowing what other buffs it cancels and is immune to. But I was making a turn based game, so calculating all the buff stuff taking time wasn't a worry. For a real time game it might be. – Eben Sep 12 '14 at 8:35
• for a real-time game, there possibly could be a "TemporalBuff" and "StaticBuff" child classes, depending on whether the buff should happen over time or be static? – manabreak Sep 12 '14 at 8:46
• you could certainly do it that way. If you're going to do that split and find that calculation time matters, you could make the Temporal ones that are likely to change use the #2 method and the static ones that are not likely to change use the #1 method – Eben Sep 12 '14 at 8:56
• What would you do with 2nd approach if there was a buff "adds 30% of you damage as bonus HP"? Would you have to iterate all buffs for each HP query? – wondra Sep 12 '14 at 10:13

The way I am implementing this in the game I am working on is to have a mixture of character states and what I have termed damage pipelines.

So a character can be normal, disorientated, slowed, stunned, paralysed or dead and character skills check against that state when deciding what options are available to them right now. Some attacks will change character state.

The damage pipeline idea works on the concept of having a tree of increasingly specialised damage types, a bit like this:

sharp      |
blunt      |- - - - - - - - physical
projectile |

fire       |
cold       |
shock      |- elemental |
acid       |            |
| - magical
|
infernal   |- spiritual |
soulfire   |


Basically a creature has both an incoming and outgoing damage pipeline and a buff can be applied at any level, so something that buffs magical defences or attacks will affect everything to the left of magical whereas one could alternatively apply a very specific buff to - for example - fire which only affected attacks with that aspect.

This can simply model both positive and negative buffs and as long as you remember to pass all your attacks and defences through the pipeline, it's reasonably easy to work with.

Edit: In terms of my game I have two types of buff that typically come into play, some are standing buffs, typically related to equipment so while a certain item is equipped the user has a bonus to a certain defence or damage type, others are per-use or power based buffs where a character has a buff placed on them for a certain number of turns ( I'm working with turn-based play, which makes it easier, but could work fine otherwise ) in both cases the buff which is added to the pipeline has a value by which it affects that pipeline and a timeout - technically I am using an internal timeout so buffs remove themselves when they hit their limit, but you could as easily have a manager attached to your damage pipeline that just sweeps the buffs on that pipeline before running damage through it and removes any that are out of date - how exactly you would choose to operate this would probably depend somewhat on other factors, such as what you are showing in the interface to indicate if a buff is active- if you're timing out the "active buff" indicator you might as well pop it off the damage pipeline at the same time.

Damage Over Time is not treated as a buff in quite the same way - that is operated as an effect that - once per turn - inflicts typed damage through the damage pipeline for a certain number of turns. I use the same timed counter model for the cooldown on most powers- a turn based environment makes this easier but if I was working it in a real time system I would probably have this on virtual "turns" ever second or part thereof to get the same effect.

This DOT model gives some interesting possibilities because it is always going through the pipeline so if a character is hit with a 5 point fire DOT they apply a fire defence buff on the next turn to reduce the effect or on the other hand apply a weakness against fire debuff to an opponent after inflicting a damage over time attack.

• This looks like an interesting approach, thanks for sharing. :) Have you run into any caveats using this? Do you have both "static" and "temporal" buffs in your system? – manabreak Sep 12 '14 at 10:07

You could use an event system where you can have abilities and buffs emit and listen for events. In your example you could have a character emit a 'damage taken' event that contains the source and type of the damage upon taking damage. You can then listen for these events and if it was critical hit by a frost bolt, change the state of the character to frozen. If it was frost damage, add a timed listener for shadow damage, if the character then gets hit by shadow damage, your can set the character state to confused.

This allows you to create complex interactions fairly easily while keeping it scalable.

I would go for a model that allows the actual state to be easily determined.

Something like ... Create a character component that has all the various properties like health, mana move speed ect. Create your buff objects and hand them to the Character as "child components".

Whilst active (which the buff component should be able to figure out for itself by seeing if it is attached to a chracter) the buff would examine its parent and ensure its effects are applied (as needed, or possibly on attach) to the parent character objects properties.

Then when it ended it would revert its changes and remove itself from the parent.

Each update cycle in the game would result in the character calling update on each buff to allow it to "redetermine" any thing that needs to be done (like applying damage, or removal)

Taking this approach, the current value for any property on the character is always the fully calculated one "rollup of all buffs and the base stats".

This is of course very subjective as it very much depends on your engine design.