I've spent the last couple of days re-learning how status effects work in battle mode and thinking about how it should work. Right now our only status effects are those that modify stat attributes, and one paralysis/stun effect. The way this works if the following:

Status effects all have a timer that is used to determine when the effect degrades in intensity

In lua, we define a duration time for each status effect. The duration applies the same time to each intensity, so we currently have no way to make one intensity last longer than another

In lua we define three functions for each affect: Apply, Update, and Finish

Apply is called when the status effect first becomes active

Update is called every once every game loop. Currently all update functions check to see if the status effect intensity changed and if not, it does nothing

Finish is called when the status effect is cured or otherwise dissipates

This scheme works for what we have right now, but it's not powerful or flexible enough to do anything interesting with it. For example, there's no way for an effect to do a periodic update, like poison removing HP every few seconds. There's no way for an effect to do anything interesting, like putting an actor in a state where they can not be targeted (a stasis sort of effect). And so on. Furthermore thinking beyond status effects, we would like some skills to have certain outcomes on the battlefield. This could be reducing an enemies' position on the action bar, a character defending an ally by taking a hit for them, etc. Using the current effects system we have and applying it to more than just status effects seems like it would be the way to go for things like this.

I'm thinking of improving the battle effects system by mimicing the design of map events, where we have a single base class and multiple subclasses for different types of effects. More specifically, I'm thinking of doing the following:

BattleEffect would be a base class (might be abstract, might not). It holds a _status_type member that determines whether or not this represents a status effect, and if so what status effect it is

BattleStaticEffect would do what are current effects do now. It only makes a static change when the effect is applied or the intensity changes.

BattlePeriodicEffect would have a second timer in addition to the duration timer. This timer would be shorter than the duration timer and when it completes, we make some change such as draining HP for a poison effect. The duration timer loops infinitely while the effect is active.

BattleConditionalEffect would be an effect that activates only when certain conditions are checked and met. For example, when an ally is about to damage from an attacker.

BattleCustomEffect would be implemented entirely in Lua, allowing us to do virtually anything we wanted there.

In Lua, depending on the type of effect we would have to define specific functionality (for example, the duration of the periodic timer for the periodic effects). Additionally, we should have some member in Lua that declares what type of class effect this is

I think this is a good start to a more advanced and flexible effects system. For the time being, I'm going to implement the first three effect classes and leave placeholders for the last two (which are more complicated to get working). Getting a working poison status effect (BattlePeriodicEffect type) is my first immediate goal.

Does anyone have any thoughts or suggestions on how to best support effects? I want us to have a well-designed and elegant system that at the same time grants a lot of flexibility. We don't need to be able to support absolutely every kind of crazy effect we can dream up, but I want to eliminate most barriers from implementing new effects on a whim, which we will likely do for boss battles later in the game.

I began working on this and I think we'll need separate classes for status effects versus non-status effects (which I think I'll just call "actor effects"). The reason being that there's actually a lot more to status effects than just an enum stating the status effect type. There's also members and methods for: intensity, status icons, etc. So I'm going to make a single base class "BattleEffect", and then build out the status effect class types from there. We can add the actor effect classes sometime in the future once we have a more pressing need for this sort of functionality.

BattleEffect would be a base class (might be abstract, might not). It holds a _status_type member that determines whether or not this represents a status effect, and if so what status effect it is

BattleStaticEffect would do what are current effects do now. It only makes a static change when the effect is applied or the intensity changes.

BattlePeriodicEffect would have a second timer in addition to the duration timer. This timer would be shorter than the duration timer and when it completes, we make some change such as draining HP for a poison effect. The duration timer loops infinitely while the effect is active.

BattleConditionalEffect would be an effect that activates only when certain conditions are checked and met. For example, when an ally is about to damage from an attacker.

BattleCustomEffect would be implemented entirely in Lua, allowing us to do virtually anything we wanted there.In Lua, depending on the type of effect we would have to define specific functionality (for example, the duration of the periodic timer for the periodic effects). Additionally, we should have some member in Lua that declares what type of class effect this is

BattlePeriodicEffect: Specifically things like damage over time from poison / burning. Would love to see the ability to put a scaling effect on this (increasing Burn damage over time, for example), and possibly a "max charge" feature that triggers a different condition, so you could do something like Petrification (which might manifest as increased Speed loss over time, and, if it ticks for 5 instances, the actor receives a separate status effect--maybe Paralyzed or Stunned, in this case). Needs to make sure we can set the frequency time...unless we're going to have all duration-dependent status effects tick at the same rate (which might be cleaner from a player comprehension perspective).

BattleConditionalEffect: Things like "Vulnerable -- next instance of damage is increased by 150%." I'd like to see conditional logic able to be cleanly mixed with Periodic and Static effects though -- see my example above.

Either way, this is a really strong set-up. It's clear, and probably really easy to make functional.

Limiting max HP is very easy to do. A static effect is something that changes some property one time and does nothing else unless the intensity of the effect changes or the effect is removed.

Djinn_in_Tonic wrote:BattlePeriodicEffect: Specifically things like damage over time from poison / burning. Would love to see the ability to put a scaling effect on this (increasing Burn damage over time, for example), and possibly a "max charge" feature that triggers a different condition, so you could do something like Petrification (which might manifest as increased Speed loss over time, and, if it ticks for 5 instances, the actor receives a separate status effect--maybe Paralyzed or Stunned, in this case). Needs to make sure we can set the frequency time...unless we're going to have all duration-dependent status effects tick at the same rate (which might be cleaner from a player comprehension perspective).

That shouldn't be too difficult, although it depends on what precisely we're taking about. A periodic effect is nothing more than a static effect with another timer that, when it expires, calls an update function in the script code. That update function can do anything we want it to, including changing the duration of the update timer. These types of effects would absolutely have to define the duration of the update timer.

One of the more technical challenges is that the status needs to be able to save data for the update function to act upon. For example, how many times the actor has received damage since the effect became active. This is not only true for periodic effects, but may be a need of all types of status effects. I think the best way to do this is to add some sort of map container that keeps a list of status variables (ie: damage_received_count) in the C++ class object and allow the Lua functions to retrieve and modify these values as necessary. I'm thinking a std::map with a string key and a float number may do the trick.

Djinn_in_Tonic wrote:BattleConditionalEffect: Things like "Vulnerable -- next instance of damage is increased by 150%." I'd like to see conditional logic able to be cleanly mixed with Periodic and Static effects though -- see my example above.

Yeah, that's a good point. I'll have to think more about how to make conditional logic more flexible. Maybe having a separate conditional class wouldn't be so useful after all. This isn't something we need to figure out right away though, but I'll keep it in the back of my mind.

Atypikal_Arkitect wrote:Would we be able to have it so effects can be used together create new effects (Magicka style)? E.g hit with water spell then with lightening does more damage.

The example you gave falls into the real of elemental effects (which may be another group of classes entirely). Elemental effects are sort of like static status effects in that when they are active, the actor has a weakness or resistance to certain elemental types. The issue here is that for some actors, we want an elemental status to be permanent (ie, equipping a flame armor grants +2 intensity resistance to fire, and maybe a -1 resistance to water). So applying a temporary change in elemental status on top of the "permanent" statuses can get a little bit confusing to handle. Essentially, all that this would do is set the new "neutral" state for these specific elements to a different value. So if I had a +2 fire resistance and an enemy used a skill that caused my fire resistance to go -4, it would actually go to -2, then gradually return to +2 provided no other elemental effect modifiers occur.

BTW: I really like the example you gave there. Being able to use two character in a combination attack like that to open a weakness and then exploit it would be awesome.

Outside of element effects, yes it should be possible for other effects to be used together and interact with one another.

Another change that needs to be made is the way we manage our active effects. At the present, each actor has their own EffectsSupervisor class object that manages all the effects for that actor, including any GUI components. Due to the requirements and greater expansion of effects mentioned in the above posts, I think this route is not maintainable. There may be effects on one actor that affect another, for example an ally defending another character. So I'm planning to make EffectsSupervisor have only one instance, much like all the other supervisor classes in battle code and elsewhere. Within this class, we would contain all effects in different sets of containers to make it easy to get to effects with various properties

// All effects sorted by each actorstd::map<BattleActor*, std::vector<BattleEffect*> > _actor_effects;

// Active effects that affect more than one actorstd::vector<BattleEffect*> _shared_effects;

This allows us to easily find access to effects that apply to a specific actor or group of actors. Yes, there will be duplication of effects in these containers, but the alternative is to store all effects in a single container and then have to iterate over each one looking to see if they meet the desired properties that we want to check for. For now I'm just going to implement the actor_effects container and it should be easy enough to add in the others when we need to.

One thing we haven't touched on or discussed is visual changes that are the result of effects. For example, showing the characters taking a different stance, or showing some effect of status on the enemies. I'm not concerned about the graphical elements of effects right now because I want to just get them working first. We'll think about what we need graphically for effects later (other than the status icons, which are already available and functional).

It should be obvious by now, but this work is some heavy duty redesign. It's going to take me a while to get this done, and there might be some bugs in status effects as the result of this work.