Looking for feedback on/alternatives to my method for buffing and debuffing character stats

A reasonably core element of my game is the addition of status effects that confer both time-limited buffs or debuffs, like what you would see in any MMO (Slow reduces Speed by 10% for 30 seconds), and semi-permanent buffs/debuffs that persist until some state is triggered (taking medicine to heal sickness, missing an attack and breaking the combo chain). I have an idea of one way to accomplish this and part of a second, but I’ve been teaching myself to program as I develop and this feels like a very brutish, unclean way to accomplish things, so I would love to hear any feedback on my method, or suggestions if there’s a much cleaner way to accomplish this.

The functionality I’m trying to encompass in this system is, simply, “Change any character statistic, by any set amount or percentage, for any duration”. This obviously implies that the pre-buff stats need to be retained in a sacrosanct area that buffing never sees or touches, and so in the character data class I’m putting three variables for each statistic: BaseStat, StatModifier, EffectiveStat. BaseStat is the sacred, untouchable variable that contains the stat’s unadulterated value, it only gets modified when the player levels up or otherwise permanently adjusts that stat. StatModifier starts at 0, and represents the total number of adjustments made to that stat: a minor debuff would set StatModifier -=1, for instance. Finally, EffectiveStat is the variable most of the game will look at to determine that stat’s effective value, and every time StatModifier or BaseStat changes, I run EffectiveStat= BaseStat + StatModifier.

This effectively makes every buff or debuff a matter of changing StatModifier, which is simple, but I don’t like it: it’s very easy to get a wrong number (for instance, if StatModifier = 3, and instead of subtracting 3 from it I call StatModifier -= -3, I get 6 instead of 0), and I’m uncomfortable lumping every buff into a single float.

An alternative to this, which I haven’t entirely thought through, would be to instantiate a StatModifier class for each new debuff: it could be fairly pared down, and only contain the debuff’s name (for menu purposes), a reference to the statistic it modifies (I’m not quite sure how to do this without pointers- could I use delegates to retain a reference to a specific statistic from the character data class?), the amount it modifies that statistic by, and its duration, with either -1 being indefinite, or using some bool NoDuration to specify semi-permanent buffs.

That seems much cleaner than using a single modifier variable, but where I trip up is thinking about the best way to actually apply StatModifiers to the character- I could give the character data sheet a List, then every time that list was modified, call an UpdateBuffs() function in character data that zeroes out all of the EffectiveStat values, then iterates through my list of modifiers and applies them, but that’s going to be a lot of effort wasted, as I would end up setting every stat every time something changed, instead of only targeting a single value.

So is this looking like a generally reasonable way to go about things? Am I missing something huge and obvious?

I already have an effect system;

  • On your character, you should have a list that contains all effects on the character
  • You should have an abstraction of effects (AEffect?)
  • This abstract should force his children to implement “apply(target)” and “remove(target)”
  • You just have to do whatever you want in “apply” and do the opposite in “remove”
  • Do not forget, in the Update function of your character, to check the list and remove the effects when they should end (OR if the effect will be applied AGAIN)

You want to remove 6 points in constitution;

// public class EffectMalusConstitution : AEffect

    public int malusConstitution = 6;
    private int _malusApplied;
    public override void apply(Character target) {
        int result = target.constitution - malusConstitution;

        _malusApplied = malusConstitution;

        if (result < 0) {
            _malusApplied = malusConstitution + result; // result is negative
        }

        target.constitution -= _malusApplied;
    }

    public override void remove(Character target) {
        target.constitution += _malusApplied;
    }

So, if you want to apply your effect, you just have to do something like this :

EffectMalusConstitution effect = new EffectMalusConstitution();

effect.apply(target);

I’m not going to do all your system, but I think this will lead you to the right path ! I do that in my game, it works perfectly :wink: However, it’s a bit more complicated.

I would take a component-based approach with a bit of polymorphism. Just have a base class, say StatBuff, that has an enum, say, ModifierType. Something like this:

public class StatBuff
{
public ModifierType type;
public int power = 0;
public bool isPercentage = false;

public StatBuff(BuffManager manager, ModifierType type, int power, bool isPercentage)
{
this.type = type;
this.power = power;
this.isPercentage = isPercentage;
manager.RegisterBuff(this);
}
}

public enum ModifierType
{
Strength,
Intelligence,
Dexterity,
}

Then you have a MonoBehaviour called BuffManager that maintains a list of active buffs and makes sure everything adds up and plays nicely, while applying the effects.

These are both great ideas, with the first example I’m trying to wrap my head around the logic- based on your example, can I assume that you have a different class extending AEffect for each type of malus? I’m trying to think, and is there an extremely efficient way to streamline it so you only have one generic abstraction that can be configured, so you could do something like

List<StatType> statList;
[LIST=1]
[*]public override void add(Character target) {
[*]   
for (int i = 0l i < statList.count; i++){
[*]target.statList += _malusApplied;
}  
}

[/LIST]
It’s messier, but it would allow you to use a single class to manage a potentially large number of maluses, am I crazy to think along these lines? :slight_smile:

For Daemon_'s implementation, this is likely a silly question, but what is the cleanest way to handle both percentages and raw numbers without resorting to some kind of fancy contextual input parsing? My immediate thought is to either use bools or a custom enumerator to specify type (e.g. Raw, Percentage, Something Else) and use a quick conditional block to check how to handle it when you apply and unapply effects, but that feels like an extra step and a lot of CPU to burn on a pretty simple function

@Sendatsu_Yoshimitsu As I said, my “effect” system is a bit more complicated ; my spells apply their own effects, I don’t have classes which represent effects. For example, I have a spell named “Freeze”, it will apply his own effects ; malus: movement speed + bonus: armor + malus: resistance water + bonus: resistance fire. If I have 2 spells which will apply the same effect(s), I will have to implement it twice. It is designed like this, on purpose.

It’s up to you! If your game can allow this, just do it. But you have to be sure of what you’re doing otherwise you’ll waste your time to change everything. I can’t do it in my game 'cause my effects depend on several things : caster’s stats, target’s stats and spell’s stats (and some conditions ; maybe I want to apply the speed bonus if there are 3 allies around me ? whatever).

Bool would be good if there’s only two options, a custom enum if you’re going to have more than just straight damage and percentage based. When the manager registers the buff, you would want to keep track of its effect types there, probably with a simple state machine. It would hardly use the CPU.

Ideally, you want the code spread between lots of classes. There is no benefit in cramming as much code into a single class as you can.

Ooh I see… so just to be sure I have the structure right, you have a single class AEffect with an empty Add and Remove function, then every single spell is a new class, extended from AEffect, that overrides Add/Remove with the specific stats it effects?

this is an interesting thread, it’s going in my bookmarks.

mostly what i have to say here, is that this is usually one of those areas where efficiency isn’t important (unless you’re applying modifiers to thousands of units at once) and therefore you should code for flexibility and ease of use as priorities.

Is it a spell or an effect ? :stuck_out_tongue: If it is a spell, name your abstract class ASpell :wink:

But yeah you got it. Just one last thing: I don’t know if this solution is the best one ; it works in MY game, it does what I want. Maybe there is another way, a better one. You must know what you want exactly in your game.

Awesome, thank you again for the feedback! Only continued playtesting will show my exact needs, but your approach is both more efficient, and much more flexible than what I’ve been doing, and that’s definitely a big step forward- thank you very much for taking the time to explain :slight_smile: