Thoughts on spell "stacking" in an RPG

Started by
15 comments, last by OldRod 22 years, 2 months ago
I would try something like this:


struct modStates
{
int strChge
int hpChge
//and so on
};

class Creature //for monsters and characters
{
Private:
modStats mod
int baseStr
int baseHP //and so on

Public:
//constuctor and baseMod get/set functions
};


then if you have an item or a spell that moded your base steps you could just alter the the states in the struct and when calculating or testing a conditionyou could do something like this:

if (baseStr + strChge >= 10)
{
//do the stuff
}

this is kidda the n00b way to go about it i bet (gotta be a better way) but i seems easy to manage at least.
Advertisement
Fruny - that code is pretty good, but I''d be interested to see how you can cleanly implement non-cumulative affects (presumably a combination of modifying the Refresh function and overriding the ApplyTo function in a derived class).

Additionally, why does everyone call them Affects? That''s the term they used in DIKU MUD, and technically it should be Effects (affect is a verb, effect is a noun). Oh well.

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost ]
this is off-topic, sorry, one of my hobbies is the misuse of the english language (well, more accurately, pointing out its misuse)...
affect (n): the conscious subjective aspect of an emotion considered apart from bodily changes.
affect (v): to make a display of liking or using OR to put on a pretense of
effect (n): something that inevitably follows an antecedent (as a cause or agent)
effect (v): to cause to come into being
--- krez ([email="krez_AT_optonline_DOT_net"]krez_AT_optonline_DOT_net[/email])
I think one of the best ways to logically and easily develop a non-cumulative buff system is to come up with buff categories. The categorization is very subjective, but an idea would be something like the following... (a simple example)

There are three categories of buffs, mental, physical, and spiritual...

You can have a mental buff to AC, a physical buff to AC, or a spiritual buff to AC. The mental buff would affect your mind''s ability to process information, increasing your ability to react to any actions taken against you. The physical buff would make your skin physically hard. The spiritual buff would create some sort of spirit shield around you. Now, you can have all 3 AC buffs on you at the same time. You couldn''t have to physical buffs on you at the same time, whether that physical buff was another AC buff, or was a str buff. Now you have to prioritize what sort of buffs you have access to, and which ones you want on you. You can choose to focus on gettings as much AC as possible, as much str as possible, or a combination of both if you have spells from the different categories. Of course, the gameplay itself needs to be such that there will be noticable pros and cons to every choice. Full offense vs full defense. Protection from physical attacks vs protection from magical attacks. etc.
quote:Original post by Kylotan
Additionally, why does everyone call them Affects?

The Answer is 42. I too started on a DIKU.

Ok, here's just a quick hack. There may be an issue as to the order of evaluation of the effects (multiplicative, thresholds...), which was the point of our affect system (in layering spells, the order would matter, spells could be cast to protect other spells from being dispelled...). This example assumes, as you seem to want it, that effects from differents sources are indeed additive (say, from invisibility and shield). One restriction will be that the modified value is computed from the base value, not from other modifiers : if you have base STR = 5, a STR+1, a STRx2, and a STR<=3 that do add together, the STRx2 is computed from the original stat as a 'STR+5' and the STR<=3 becomes a 'STR-2'.

Beware of the memory requirements though (that can create a LOT of objects).

            template<class StatType> class Effect; // forward declarationtemplace<class StatType>class EffectCombiner// These are used as global objects// and represent the various types of // effects which would add together.{  // convenience typedefs  typedef Effect<StatType> eff_t;  typedef std::list<eff_t*> efflist_t;public:  virtual StatType Modifier( StatType& stat, const efflist_t& list ) const   // returns the modifier of the effect that does apply.  // example for a maximum, when StatType is a numeric type  {     if (list.empty() ) return 0;     StatType max = - std::numeric_limits<StatType>::infinity();     efflist_t::iterator itor;     for( itor = list.begin(); itor != list.end(); ++itor )     {        StatType mod = (*itor)->Modifier( stat );        if (max < mod) max = mod;     }          return max;}template<class StatType>class Effect  // Base Effect class for stats of type StatType// Would carry information used by EffectCombiner<StatType>// to take the decision on which effect to apply{  friend class EffectCombiner;public:  EffectCombiner<StatType>& combiner;  Effect( EffectCombiner<StatType>& _combiner )  : combiner( _combiner )  {}  virtual ~Effect() = 0;  virtual StatType Modifier( StatType& stat ) = 0;  // now returns the modifier only, which won't work   // as well for description modifying effects.  // May have to derive another hierarchy for those,  // or use Attribute::Modify()}template<class StatType>class Attribute  // Class for stats of type StatType{  // convenience typedefs  typedef Effect<StatType> eff_t;  typedef std::list<eff_t*> efflist_t;  typedef std::map<EffectCombiner<StatType>*, efflist_t> effmap_t;private:  StatType base_stat;  effmap_t effs;  mutable bool cached;  mutable StatType cached_stat;protected:  virtual StatType& Modify( const StatType& mod )  // example for a numeric StatType, where effects add up.  // strings would compare effects timestamps, or something.  {     return cached + mod;  }public:  Attribute( const StatType& stat )   : base_stat( stat ), cached( false )  {}  const StatType& () const { return base_stat; }  void Base( const StatType& stat )  {     base_stat = stat;     cached = false;  }  const StatType& Stat() const  {    Refresh();    return cached_stat;  }  void Refresh() const  {    if ( !cached )    {      cached_stat = base_stat;      effmap_t::iterator itor;      // get each effect combiner to apply its selected effect to the stat.      for( itor = effs.begin(); itor != effs.end(); ++itor )         cached_stat = Modify( itor->first->Modifier( base_stat, itor->second ) );      cached = true;    }  }  void AddEffect( eff_t* effect )  {     efflist_t& list = affs[&effect.combiner]; // will create if key doesn't exist     list.push_back( effect );     cached = false;     // lazy refresh  }  void DelEffect( eff_t* effect )  {     effmap_t::iterator itor = effs.find( &effect.combiner );     if( itor == effs.end() ) return;     itor->remove( effect );     if( itor->empty )       effs.remove( &effect.combiner );     cached = false;  }}          


This code is probably full of bugs, shouldn't be templated (litteral vs. numeric type issues) and should probably have more checks in it. But hey, I warned you it's a hack.

Note to GDNet staff : could you make the input form wider (e.g. proportional)

Edit: s/T/StatType/g for Kylotan

Edited by - Fruny on February 26, 2002 3:23:26 PM
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
Hmm, I think I see how it works, but it''s a bit complex. You should try using better typenames than ''T'' though

[ MSVC Fixes | STL | SDL | Game AI | Sockets | C++ Faq Lite | Boost ]
quote:Original post by Kylotan
Hmm, I think I see how it works, but it''s a bit complex. You should try using better typenames than ''T'' though


What''s wrong with T ? Oh, well, that''s only because you asked nicely. I don''t think it is overly complex, but I agree it could probably be done much better.

"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan

This topic is closed to new replies.

Advertisement