Putting the 'role' back in role-playing games since 2002.
Donate to Codex
Good Old Games
  • Welcome to rpgcodex.net, a site dedicated to discussing computer based role-playing games in a free and open fashion. We're less strict than other forums, but please refer to the rules.

    "This message is awaiting moderator approval": All new users must pass through our moderation queue before they will be able to post normally. Until your account has "passed" your posts will only be visible to yourself (and moderators) until they are approved. Give us a week to get around to approving / deleting / ignoring your mundane opinion on crap before hassling us about it. Once you have passed the moderation period (think of it as a test), you will be able to post normally, just like all the other retards.

What is the best way to code buffs, debuffs and DoT in a TBS game.

desocupado

Magister
Joined
Nov 17, 2008
Messages
1,802
What the title said.

Imagine a blobber, with classes for monsters and pc's. How you deal with that? Buffing is easy, just set a method so it changes the stat, but let's say it's supposed to last 3 turns only, how can you make the game keep track of it and restore the stat at the right moment?

Same thing with DoT. You can set the initial damage, but how to make the damage be applied the next turn?
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
Look up priority list and the 'memento pattern'

I say priority list and not simply sorted list based on timeouts because you might want your debuffs to affect one 'buff' at the time, so you'd want to check first the 'protection from magic' before the 'shield' buff for a 'dispel' debuff.


edit: the suggestion below for removal is far more important for speed.

This has the problem of you having to check the whole list (so it's slower - because having two parameters to check on a single sorted list is impossible in general - you have to choose one to win. And the better one to win might be the timeout parameter so you don't have to check the rest of the list).
In this case, two lists, one for the 'protections' buffs for buffs and other for generic harmful effects (like DoT) and normal buffs might be good. So you can check the timeout of the first element on each tick on the two lists and just do nothing if the first is not timeout

(Of course, this kind of micro-optimization should only be applied after profiling to see if it really matters)..


'permanent' buff (eg: from items), can also be represented thus. You have to be careful with percentage effects so that order of the buffs doesn't affect their potency.

Also you probably want to make it so that your buff items have more than one elemental atomic buff not many pieces of code 'for the item' and that their user interface statistics derive from them (toString of "10+fire damage" for instance").
This is because description getting out of sync with the actual effect is very common when the engine is not designed like this - for instance see the Infinity Engine mods, where item text is bugged to hell and back in most megainstalls because of this). This is only for the 'statistics' part of course - the rest is impossible to derive.
Also this makes a interface for comparing to your character current statistics easy (a old jrpg user-interface wisdom that even IE hadn't learned at the time).
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
Finally there is a old trick for dealing with lists removal you might want to use

http://www.java-gaming.org/topics/a...-objects/25388/msg/219160/view.html#msg219160

this is very important in game loops so you're not allocating new garbage every frame (if you're in C you might use realloc i dunno).
The example is for 'active' visual objects (ex: npc die etc), but is just as relevant for debuffs (only with my suggestion above would require 4 lists which is far too much, so i cut off the suggestion above after thinking about it).

BTW; the lists above are not 'per npc' but are global lists of entities processed per tick. In your case, they would be 'buffs of everyone on the area|radius per tick) and would be a separate loop before the main loop entity processing body. If you're not too hot on duplicate loops you can put smaller lists on the entities themselves and to the same thing on process() - however, that would probably be very slightly slower because on the nested loop complexity and loss of cpu L-cache shenanigans.

Also as a aside, whatever design you use, don't fall on the same error as BG, that sometimes has the buffs living longer than the character so you can see a shield spell on a dead npc etc. This is more difficult if your buffs list(s) are outside of the 'entity' class of course.
 

Alex

Arcane
Joined
Jun 14, 2007
Messages
8,752
Location
São Paulo - Brasil
I dunno SCO. I agree with most of what you have said, but I prefer something like the decorator pattern than the memento to represent buffs because it leaves the original object intact. It is a bit slow, I admit, and it is a pain to implement meta-rules like D&D 3e's typed bonuses not stacking with the same type (except for the really special types). But still, maybe a better solution could be found. I once tried to implement this once as a pair of objects. The shell, which was the fully modified object, and kept track of all the modifiers, and the core, which was the original, unadulterated object.
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
hummhum. I suggested memento because the buffs need to be inverted eventually.
to use a decorator for the same, would probably required some kind of memento-like structure anyway no? Otherwise you lose the ordering and the state unwrapping, that you'd need anyway for BG-style debuffs and counter-debuffs.
I am thinking that your 'decorator' is actually a implicit linked list there, where removal of a buff 'drills-down' because you might want to remove something that is not the outermost onion-layer. Unless your shell is actually only one object, in which case, i don't see how you can implement undo().

Regardless, it would be nice to have a data-structure that would have different 'views' with different that could be removed/added quickly.

Sometimes (most) you only want to check timeouts, so it would be nice to be quick and early return when you can; when dispel is cast you want to check for protections and if you don't find any to dispel, etc. Problem is making it quick. Maybe the sorting/state checks of the secondary; user command / least often used stuff (like dispel, or some kind of 'buff structure' where if you cast 4 elemental buffs you get a mega buff etc) could be made on demand and keep the timeouts amortized sort for main loop stuff.

I'm probably over thinking it as usual. One thing you definitively do not want is to keep the sorting precedences in code.
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
1) It's a turn-based game. In CPU time you have a million years to figure out the value of a stat.
2) If you feel dirty about recomputing the total value all of the time use an isDirty flag.

There are lots of optimizations you can make when it comes to your data structure for storing mods on a character. For example, you could store and retrieve them in a hashmap based on the stat you are calculating.

Decide right now if you are going to mix +constant and +percentage modifiers. If so, decide the order you are going to calculate them right now and never change it.
 

Alex

Arcane
Joined
Jun 14, 2007
Messages
8,752
Location
São Paulo - Brasil
Ye, I know decorators aren't quite the fit, given how you eventually need to mess with them with dispels, find what is the strongest buff of a certain type affecting a stat and only apply it, etc. But what I like about them is that they don't really change the internal state of the object. So, if something goes wrong, you still won't end up with something weird like a character with -10 strength or some bizarre stuff like that. Truth is, it was thinking about this stuff that led me to try that weird language adaption I mentioned a long time ago here in the Codex. Buffs and debuffs and whatnot are, surprisingly, a real pain in the ass to code right. Maybe not so surprisingly, given how often games mess up there.
 

desocupado

Magister
Joined
Nov 17, 2008
Messages
1,802
Ok, I gave the Memento pattern a look, and it seems a nice way to handle the buffing and debuffing.

However, the timing is the main issue here. I'm not sure how to deal with that. I searched for priority lists on google, and came up with nothing. There's priority queues and plain lists, and I don't see how any of those two can help me with that.

Or were the priority lists a way to not get a memento of a PC already buffed by other buff, and thus getting a "wrong" state to roll back to? Because that's an issue I see with the memento pattern.

Let's say I buff a PC on turn 3. How do I make sure that at the end of turn 6 he's debuffed? That's my main issue.

PS.: I'm working with C++, I now nothing about java.
PS2.: Go easy on me, I'm a begginer, while I appreciate the help, I always get overwhelmed by the amount of stuff I don't even know you guys refer to.
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
Ok, I gave the Memento pattern a look, and it seems a nice way to handle the buffing and debuffing.

However, the timing is the main issue here. I'm not sure how to deal with that. I searched for priority lists on google, and came up with nothing. There's priority queues and plain lists, and I don't see how any of those two can help me with that.

Or were the priority lists a way to not get a memento of a PC already buffed by other buff, and thus getting a "wrong" state to roll back to? Because that's an issue I see with the memento pattern.

Let's say I buff a PC on turn 3. How do I make sure that at the end of turn 6 he's debuffed? That's my main issue.

PS.: I'm working with C++, I now nothing about java.
PS2.: Go easy on me, I'm a begginer, while I appreciate the help, I always get overwhelmed by the amount of stuff I don't even know you guys refer to.
The base class for your buffs could contain duration and turnApplied variables. At the beginning or end of each turn as part of an upkeep phase you can iterate through all of the modifiers on a character and remove them if needed. (current turn >= duration + turnApplied)

Additionally, you probably want to implement a callback or virtual method in the base class that is executed when the buff is removed. This will allow you a lot of flexibility in terms of effects. For example, you could have a rage buff that gave +30 strength for 3 turns that when removed will apply a -10 strength winded 'buff' for the next three turns.

I recommend that you get the system working as intended first and worry about optimizing it later if needed. Unless you are planning hundreds of buff effects, a list is probably fine.

EDIT: Ok, I looked up the Momento pattern you were talking about. I think using it would be a great way to add lots of bugs to your game. Based on the skill level you have described yourself as, I would recommend you just do something like this:

  • Buff shall refer to anything that modifies a character. That includes debuffs, equipped items, etc.
  • Make a StatEnum with a list of every stat a buff can alter.
  • Create a base Buff class:
Code:
-name
-iconTexture
-statType (that stat enum from above)
-statModifier (int)
-onBuffAdded
-onBuffRemoved
-onTurnUpkeep (poison would do damage here)
-duration
-turnApplied
-characterThatAppliedThisBuff

  • In your character class, have a list of buff effects.
Code:
List<BaseBuff> buffList;

  • Use a method to provide access to a character's stats:
Code:
public int calculateStat(StatEnum stat){
    int subtotal;
    //iterate through the whole list of buffs
    if(buff[i].statType == stat){
        subtotal += buff[i].statModifier;
    }
    return subtotal;
}

Later, if you want, you can add a boolean flag to character that gets set to true if the list of buffs change. If it's false you can just return the last computed value instead of recalculating everything.
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
However, the timing is the main issue here. I'm not sure how to deal with that. I searched for priority lists on google, and came up with nothing. There's priority queues and plain lists, and I don't see how any of those two can help me with that.

You either iterate over all the elements in your buff collection and check if their timeout has already occurred as per the previous post, or keep the data sorted by timeout at insertion (which is what a priority queue does). If you keep data sorted, you should only check for if you have to remove until you find one where you don't have to remove, whereuppon you can return. Which is handy if your character has permanent effects applied all of the time, for instance from items, since you need to check for timeout every turn (or if you're doing a mixed realtime-turnbased game like ToEE is*, even every tick() of the game engine when not on turnbased mode).


*toee can cast buffs outside combat, not to mention the items.
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
However, the timing is the main issue here. I'm not sure how to deal with that. I searched for priority lists on google, and came up with nothing. There's priority queues and plain lists, and I don't see how any of those two can help me with that.

You either iterate over all the elements in your buff collection and check if their timeout has already occurred as per the previous post, or keep the data sorted by timeout at insertion (which is what a priority queue does). If you keep data sorted, you should only check for if you have to remove until you find one where you don't have to remove, whereuppon you can return. Which is handy if your character has permanent effects applied all of the time, for instance from items, since you need to check for timeout every turn (or if you're doing a mixed realtime-turnbased game like ToEE is*, even every tick() of the game engine when not on turnbased mode).


*toee can cast buffs outside combat, not to mention the items.
While more elegant, that's still an O(n) operation. After putting in a dirty flag for the calculation, there isn't a lot of room for meaningful optimizations left. (And you should have profiling before worrying about it.)
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
The main problem is that a dirty flag will not save you from having to check the timeout of non-permanent buffs, a dirty flag is fine if you don't want to modify the main entity object with the buff effects as per memento, but you have to cycle the collection of them every time anyway for removal on timeout. So you might as well amortize on insertion, which is uncommon, and get the benefit on iteration, which is common.

Memento will just allow him to remove the buff when he removes it from the list by their undo operation, which i think it's easier than resetting a boolean state flag and recalculating that the next turn.
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
The main problem is that a dirty flag will not save you from having to check the timeout of non-permanent buffs, a dirty flag is fine if you don't want to modify the main entity object with the buff effects as per memento, but you have to cycle the collection of them every time anyway for removal on timeout. So you might as well amortize on insertion, which is uncommon, and get the benefit on iteration, which is common.

Memento will just allow him to remove the buff when he removes it from the list by their undo operation, which i think it's easier than resetting a boolean state flag and recalculating that the next turn.
If memento was easier, there wouldn't be all sorts of bugs relating to stats and equipment swapping/changing zones/etc. in so many single player games. :P

Where we disagree is the need to optimize at all. Checking for buff timeouts happens once per character per round. That's probably something like 12 times every 90 seconds. Iterating through a tiny (~100 item) list is going to take under a millisecond.
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
I was thinking of consistency towards equipped items buffs and normal buffs - they go into the same list with the same behaviour and that's it. Also thinking of mixed mode TB on combat realtime outside it as ToEE.

Forward, i was thinking of using a single list for all the world's buffs currently since they all check the same sort of stuff for the timeout, but that is probably not a good idea (you likely want to expose the buff states for scripting, as in 'is the character hasted?' and that is hard to do if the characters don't have a 'has-a collection of buffs' relationship).

BTW; the list/queue maybe should be ordered set, since reapplying the same buff is a bad idea for balance.
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
I was thinking of consistency towards equipped items buffs and normal buffs - they go into the same list with the same behaviour and that's it. Also thinking of mixed mode TB on combat realtime outside it as ToEE.

Forward, i was thinking of using a single list for all the world's buffs currently since they all check the same sort of stuff for the timeout, but that is probably not a good idea (you likely want to expose the buff states for scripting, as in 'is the character hasted?' and that is hard to do if the characters don't have a 'has-a collection of buffs' relationship).

BTW; the list maybe should be a set, since reapplying the same buff is bad form for balance.
I would go so far as to put base stats, level up bonuses, and everything else in the list.
 

SCO

Arcane
In My Safe Space
Joined
Feb 3, 2009
Messages
16,320
Shadorwun: Hong Kong
Yeah i'm in low-power thinking mode. :(
Level up could be a configured buff, and level drain would just remove the last level up.
Though maybe not, since that would lead to 'respec' exploits with level draining monsters if the buff is completely lost.
 

GordonHalfman

Scholar
Joined
Nov 5, 2011
Messages
119
Another thing I find quite challenging is making a system for checking all the conditions the designer might want to attach to effects. A simple example being something like protection from evil which buffs your saving throws but only against evil creatures, or a spell that gives bonus AC when being attacked by undead. Some of these can be checked when the buff is applied but other's will change depending on the context.

You'd have to set things up so that at the point you poll for an attribute you have access to contextual information about things like who the relevant enemy creature is and so on. Then you need a set of variables for describing the conditions themselves. Stuff you might need:

Environmental: time of day, indoors or outdoors etc
User conditions: stuff about the creature the effect is applied to
Target conditions: stuff about the creature you are attacking, or who is attacking you

User and target conditions have a bunch of subtypes, like racial type, alignment, sex. I guess you'd also need to specify it in a way that lets you define AND or OR conditions. e.g. is this sword good against evil creatures and outsiders or just evil outsiders?

I'm probably going to end up making a bunch of simplifying assumptions about the range of conditions available and assume that scripting can be used if more complicated stuff is needed. (Like a plot specific thing where a sword gives a strength bonus at sunset of some holy day or similar.)
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
Yeah i'm in low-power thinking mode. :(
Level up could be a configured buff, and level drain would just remove the last level up.
Though maybe not, since that would lead to 'respec' exploits with level draining monsters if the buff is completely lost.
Personally, I don't like level drain effects, but I would still use this strategy to track things for respec purposes.

If you wanted a level drain effect, on application it could search for the last levelup buff applied to the target and copy its effects but return the negative of them. Both effects would remain on the character until the drain wears off, etc.

In other words, just reverse the polarity. M:
 
Self-Ejected

Davaris

Self-Ejected
Developer
Joined
Mar 7, 2005
Messages
6,547
Location
Idiocracy
Another thing I find quite challenging is making a system for checking all the conditions the designer might want to attach to effects. A simple example being something like protection from evil which buffs your saving throws but only against evil creatures, or a spell that gives bonus AC when being attacked by undead. Some of these can be checked when the buff is applied but other's will change depending on the context.

You'd have to set things up so that at the point you poll for an attribute you have access to contextual information about things like who the relevant enemy creature is and so on. Then you need a set of variables for describing the conditions themselves. Stuff you might need:

Environmental: time of day, indoors or outdoors etc
User conditions: stuff about the creature the effect is applied to
Target conditions: stuff about the creature you are attacking, or who is attacking you

User and target conditions have a bunch of subtypes, like racial type, alignment, sex. I guess you'd also need to specify it in a way that lets you define AND or OR conditions. e.g. is this sword good against evil creatures and outsiders or just evil outsiders?

I'm probably going to end up making a bunch of simplifying assumptions about the range of conditions available and assume that scripting can be used if more complicated stuff is needed. (Like a plot specific thing where a sword gives a strength bonus at sunset of some holy day or similar.)


I was just thinking about this kind of thing. Good comment.
 

J1M

Arcane
Joined
May 14, 2008
Messages
14,626
If you are attempting an object oriented design, the character should not care what is targeting it. There should be another component that resolves the combat situation. First asking the initiator if they have any special modifiers, then checking if the character matches those requirements. A dungeon master class, if you will.
 

As an Amazon Associate, rpgcodex.net earns from qualifying purchases.
Back
Top Bottom