Handling RPG-like stats

Hello. I’ve been thinking about a way to handle stats like strength, agility, intelligence, move speed, e.t.c. in my game, and I’m not sure how to make it flexible enough. As an example of what I want to achieve, take the stat system of League of Legends or the Warcraft 3 heroes, or even WoW.
My current idea is reading in a stat file (XML?) for each unit type or character which contain a list of all the stats that kind of unit/character has. This would contain the stat name, maybe an ingame description, e.t.c. My idea here is to have either a simple base value for the stat (a single double variable), or a mathematical function parsed with Exp4J so that the base value can depend on other stats.
A WoW example:

Strength = 10
AttackPower = 5 + Strength * 2

Some stats will be required (HP, Mana, move speed, attack damage, attack speed, e.t.c.) and will be read semi-hardcoded by the unit manager.

To achieve the flexibility of buffs, debuffs, equipment and stat modifiers in general I will need a very powerful system for this. My idea is that each stat has a list of modifiers. These modifiers are either flat bonuses (+5 to strength) or percentage bonuses (+20% to strength). Their actual value will probably be a Exp4J expression, so the modifiers can depend on other stats, even from other units (for example: the slowing effect of a slow debuff is dependent on the caster’s intelligence).
Stacking is controllable by using stack groups IDs, e.g. if you have two modifiers of the same stack group only the most powerful will apply.
I also plan to keep a way to add on-hit and on-cast effects like in League of Legends, which means that buffs, upgrades, etc can add the ability to apply debuffs like slow and poison on attacks, grant new abilities, e.t.c.

With these things I would be able to do exactly what I want. However, there is one problem. Infinite loops with rare buff combinations. I have a good example of this in League of Legends. In LoL there is a champion (hero) called Jax who has a unique passive ability that grants him more max HP from each point of bonus damage and ability power he gets from equipment and buffs. You can also buy an item called Atma’s Impaler that grants bonus damage depending on your max HP. Ouch.
To calculate damage:


Damage = 10 + HP*0.02
HP = 100 + Damage*3
Damage = 10 + (100 + Damage*3)*0.02
Damage = 10 + (100 + (10 + HP*0.02)*3*0.02
Damage = 10 + (100 + (10 + (100 + Damage*3)*0.02)*3*0.02
...
StackOverflowException!

I need some way of detecting and preventing an overflow, while still making it possible to have these buffs at the same time. These calculations might be done pretty often too, so they have to be pretty fast. Of course I will cache the stats, as they only change when their modifiers change.

Has anyone done this before? I mean, somebody has to have made an RPG before or another kind of game that has stats and modifiers. How did/would you implement it?

StackOverflow only happens when you have too many methods in the stack, i.e. a method calls itself recursively too many times.

Do you mean overlowing the max value? If so, then that is not something you should worry about.

If a stat depends on another stat, it will trigger a calculation of that other stat. In my example, get-ting for the value of HP will trigger a calculation of Damage, as the current value HP depends on Damage. However, Damage depends on HP. As I’m recursively calculating them, they will recursively try to calculate each other until I get a stack overflow.

This makes no sense. Why would you have 2 numbers that are eternally dependent on each other? This is most likely a case of “You’re Doing it Wrong ®”

what you need to do is copy the starting point of the variables into another variable, so like damage2 and health2. and use thes in you calculations so that it will only ever go up 1 time rather than just continuing forever.

Stats are “scriptable” to some extent. As stats can depend on each other, there is a chance for this to happen. I won’t have many of these cases, but I just want to be able to handle it gracefully if I or someone happen to create such a scenario.

Yes, LoL seem to use some kind of system that converges over time, so this would probably be a good approach. I’ll have to experiment, I guess.

Thanks for the responses so far, but has no one ever done something similar? I mean, RPGs are quite common…

[quote]In my example, get-ting for the value of HP will trigger a calculation of Damage, as the current value HP depends on Damage. However, Damage depends on HP.
[/quote]

[quote]This makes no sense.
[/quote]
+1

[quote]As I’m recursively calculating them…
[/quote]
well don’t.
I personally never even use recursions, and

[quote]they will recursively try to calculate each other until I get a stack overflow.
[/quote]
this proves why. If you aren’t 100% sure of all the cases and know what happens, don’t do it.

I have rpg stats and all, but I don’t know what to say.

I can show you some examples I guess. Depending on another stat doesn’t imply recursiveness of course.

int exhStress = (int)(-Math.pow((stats.getLevel()*2), 1.1d)+100);

here some exhaustion gets added by doing an action. In this case it only depends on the Level.

exhaustion*=(int) ( (float)ap/(float)stamina * ( (60 + (float)stamina) / 60) );

Here an example for when it depends on AP which is like mana (cost of a spell)

Generally what I do is, take a math graph programm applet thing, and then develop a formular which doesnt exceed a certain point and doesnt go below 0 or 1, taking in all the factors

If you have a lot of buffs and debuffs, it gets more complicated, but its “just” more factors.
Just don’t make it potentially infinite. A system of values has boundaries, there is no temperature below absolute zero, no speed above speed of light, no level about 99 and no single damage above 9999 for example

luck is tricky sometimes, but it should only give an additional chance and shouldn’t overflow stats or anything

[quote]This makes no sense.
[/quote]
+1

[quote]As I’m recursively calculating them…
[/quote]
well don’t.
I personally never even use recursions, and

[quote]they will recursively try to calculate each other until I get a stack overflow.
[/quote]
this proves why. If you aren’t 100% sure of all the cases and know what happens, don’t do it.
[/quote]
Obviously I shouldn’t use “real” recursion in this case because of this.

That isn’t at all how I want to do this. How would you implement items that modify stats? Talents? Skills? Buffs? Stacking? And obviously I’d clamp my stats to a relevant range. Having negative move speed would be kind of weird.

Like I said, having stats depend on each other can make for some very dynamic item/talent builds etc, and this is actually one of the reasons I think League of Legends is better than the competition. Having to avoid loops would limit the things you can do a lot, so being able to handle such cases is pretty important. If you guys think I’m insane for wanting this feature, just ignore me or play League of Legends or something. I’ll start working on my system tomorrow. Thanks anyway.

you have your stats and you have modifiers
now you could just add values to the stats and subtract them, instead of using modifiers
when using items which I would assume only work for a limited time, eg buffs. Or equip which changes the stats as long as you wear it.

so you could have core stat + equip modifier + temp modifier
these can be also minus of course, for some equip that has benefits but also handicaps for example

it doesn matter how much you want to stack (if possible)

Example: Character has x STRENGTH, some equip which also effects it, lets call it y, and the mentioned temp modifier for example, lets call is t.
The spell “furious rage” add +5 to this temp modifier. Then the player also uses “strength potion” giving him an additional +3 strength. so they just do t+=5 and t+=3
So we have strength which is x + y + t
when there are no buffs or anything, t is zero, now its +8
after time these buffs will vanish and do t-=5 and t-=3 to kill the buff
actually you probably have these values somewhere and it would be t+= -value, whatever

I’m using doubles for all stats. I also want percentage bonuses. Things quickly get tricky due to rounding errors if I handle temp bonuses like that. That kind of system is also too limited in my opinion when it comes to bonuses. What if I want “furius rage” to give a 20% bonus to strength, not a flat +5 bonus? What if I want it to give <dexterity * 0.5> to strength? You’re not really solving what I’m asking about…
If your answer is “Why would you do that?”, then I’m probably explaining something badly.

well it doesn’t matter if its a value or a percentage. then just calculate core strength + equip strength, which is the effective strength, calculate 20%, add it, there you go
rounding errors… well I don’t consider that these things have to be perfectly calculated, if there are some rounding errors, I argue that nobody would notice.

Don’t know what dexterity has to do with strength, but its very simple too right ? just take the effective dexterity, * 0.5 and add that value to strength.

Nah but I just think that you think too hard about it, its not that complicated.

The rounding errors appear when they wear off. Say I have a 20% bonus to strength from a buff and receive another buff that grants me +10 to strength. That 20% bonus should increase the other buff’s bonus to +12, but your method won’t consider that and will add +10 to the temporary bonus counter. When the 20% bonus buff wears off, it will do t /= 1.2; to undo the t*=1.2 when it was added, effectively reducing the flat buff too. When the flat buff wears off, it’ll remove the full +10 buff, corrupting the stat permanently. Even if you have a variable to keep track of the current percentage bonus to solve the first problem, rounding errors would prevent the value to go back to its previous value exactly, building up the rounding error as buffs are added and removed in different order. And once again, how would you handle buffs not stacking with each others?

What if my dexterity changes while I have that buff? What if my dexterity depends on another stat? What if my dexterity depends on strength?
And if you want a more plausible example: Maybe it’s a spikey guy like Rammus in LoL, and the more armor he gets the more attack damage he gets, as his spikes get harder. Damage depends on Armor and Magic Resistance.

It actually IS pretty complicated. I just want it to be flexible enough for this game. If you have the time, try to experiment with it. I think I might have a small solution to dependency loops coming up…
It seems like not many people have actually done anything like this… I’m a little surprised… xd

Take it from someone who’s been there: RPG mechanics are a very deep rabbit hole that you can end up writing an entire mini-language and logic system to express and deal with. My advice is, don’t worry about them all that much and concentrate on what’s actually fun. There will always be areas where the mechanics are weak or have exploits and loopholes, and you’ll have to nerf, buff, and sometimes just plain rework the system when you come across those parts. Make your system adaptable to those necessary changes, but don’t spend all your time writing an RPG framework instead of a game that’s fun for people to play and fun for you to write.

Now for what I might call useful advice instead of my preachy babbling: You might want to express RPG mechanics like bonuses and derived stats and so on with an actual scripting language. Lua is an option (there’s lua to java interfaces), and javascript is another decent choice, but it’s a wide open field in case more exotic languages tickle your fancy.

I’ve had similar problems before, when writing a combat system for a mud.

My advice is to let each bonus/malus be its own object.

Always keep the “base value” untouched, - you don’t want to meddle with it.

Suggestion:
Each stat would have a list of bonuses.
Each bonus knows what stats affect it. (In Rammus’ example; Attack damage would contain a bonus, call it “Spikes” or whatever. “Spikes” lists Armour as a stat that if it changes, “Spikes” requires recalculation).
If/When a bonus wears off, remove it from the list, and recalculate that stat. (Again, in Rammus’ example; “Spikes” would never wear off, but temporary effects will).
Resolve and calculate static bonuses first ( +5 etc ), and then accumulate dynamic bonuses (like %), and calculate those.

Does that make sense?

Thanks for the advice! We’ll see if I can manage to NOT spend days on this. I kind of like this kind of stuff… How things like this works is something I’ve wondered for a very long time. xD
Having an actual scripting language would be kind of nice at times if I would like to create even more advanced stats, but that kind of contradicts your first advice… xd Anyway, as a starter I’ll just go with Exp4J expressions. They are easy to use, fast enough and have all the mathematical functions I would probably ever need.

Hello, mister mind reader. That is pretty much exactly what I was planning to do. The only real problem (as I was trying to explain earlier) is that because the bonus of Rammus’ “Spikes” buff depends on the current value of Armour, I will need to calculate Armour first. The easiest way to ensure the ordering is to have Spikes trigger the calculation of Armor recursively, but if Armor has a buff that makes it depend on Damage, it will endlessly try to calculate itself until I get a StackOverflowException. I’m not saying that this will happen for every stat all the time, just that it MIGHT happen, and getting a stack overflow due to a “bad” script or config file feels batman. But like I said in my last post, I think I’m onto a solution.

I knew there were people who’d done this before! Thanks for the advice, all of you!

IMHO, damage is a process and should not be a stat.

What is labelled here as damage is really “Attack Power”. Damage is, as you say, a process, that can have a multitude of twists, turns, and outcomes.

Yes, but attack-power is also a calculated value and is closer to a process than it is a stat. A sword’s damage or “Attack Power” could be calculated using the characters dexterity & sword ability for example. The higher their skill(s), the deadlier they are with that weapon.

A secondary stat then?
In WoW attack power depends on your strength (and agility for some classes). DPS depends on the weapon’s damage and your attack power, and Damage per hit depends on the weapon’s attack speed. However, it’s possible that any of these stats receive a buff. There are items that grant attack power. There are items that grant DPS (weapons). It’s easier to handle this if they all are stats.

Try checking WoWpedia or looking up their damage formulas, I’m sure there’s something useful in that.