Monday, June 21, 2010

A generic system for implementing skills, traits, and item bonuses...

I know the title is a mouthful, but if beyond it is an idea I'm excited about. Like most ideas, it came to me when I was doing something other than programming.

This iteration I'm planning on implementing varying difficulty monsters and hopefully items of varying strengths. In my previous post, I suggested using a "trait" system to implement some of the monster characteristics. For example, one wolf might be "fast"-er or "strong"-er than another, trading that off against some additional base attributes. In my head I hoped to sneak in items with enchantments; a sword that could make you tougher or a staff that made you case spells better.

It came to me that both of these use cases are very similar to the skill tree attributes I implemented last iteration. Those skills could increase base stats or allow new skills. As a programmer, when I hear of three different implementations of an idea, I get an itch to make a generic framework or factor out the pattern or something.

The crux of the idea is that all of these ideas can be implemented as a system of tags and values. For example, a skill that increases the characters base hitpoints by 5 could be:

m_player["HPBonus"] = 5;

Then to calculate the number of hitpoints in this simple example one can do something like:

// Player
int GetSkillTags(string tagName)
{
    int total = 0;
    foreach(ISkill s in Skills)
    {
         if(s.attributes.Contains(tagName))
             total += s.attributes[tagName];
    }

    foreach(IItem i in Equipment)
    {
         if(i.attributes.Contains(tagName))
             total += s.attributes[tagName];
    }
    return total;
}

// Monster
int GetSkillTags(string tagName)
{
    int total = 0;
    foreach(Dictionary<string, int> d in Traits)
    {
         if(d.Contains(tagName))
             total += d[tagName];
    }
    return total;
} 

Once I implement a given skill or trait checking in one section of the game engine using this API, creating a new skill, item attribute, or monster trait to do the same thing is trivial. Also, since checking if a dictionary contains a key is fast, it should be efficient as well.

Thoughts?

4 comments:

tndwolf said...

Hi, first post and I love your blog! (and dearly hope that Magecrawl will support linux in the future ;) )

The idea of tags is something that I like very much. I've used them in my 7DRL and I think they worked well. Like you say: they are very versatile and easy to use.

Tags could also work well with temporary effects, like the ones you mentioned in an earlier post (foreach effect in TempEffects...).

I was thinking of going even further. What if everything can have tags? For example, certain cells can be be "squeaky" and bestow a penalty on stealth while certain others can have a strong fire aura and augment fire spells, or water can dampen them, if you allow negative values.
On the same vein, a whole level, or dungeon branch, can have tags.

Taking the idea to the extreme, maybe a little too much, even abstract concepts like quests can have tags ("story tags"?).

My fear is that these for loops will begin to be too cumbersome, and the memory overhead too large...

What do you think?

donblas said...

The 2nd tech demo for magecrawl supported Linux. The issues I ran into were libtcod related (using svn version), not related to magecrawl itself. I have a "must fix" task bug to get it working this iteration.

I really do like the concept of tags for some data structures, as I noted in the post. Once I get implemented what I have outlined in my post, I might look at allowing tags of other things (Status Effects, floor tiles, etc).

The idea seems sound as it'd allow at least some effects (like a status effect boosting strength) to be implemented simply as a tag and not an effect that changes the base strength and needs to be undone later.

Memory usage shouldn't be that bad, as dictionaries of strings and ints aren't that huge. Depending on how often you needed to walk though all (equipped items, skills, status effects, traits, current floor tile), it could get slow. However, caching and other forms of performance boosting could be done to minimize this.

Jotaf said...

Awesome, it's not often that I read about a skill/stats-related system that makes so much sense. I always dreaded the moment when I had to implement this myself, as I thought it would have to be cumbersome to maintain and buggy. This tags system though looks pretty slick!

About caching for performance, it shouldn't be too hard; you could mark stats as "dirty" when a related tag is added/deleted and in the stat's getter method either return the cached value or recalculate based on that flag.

But C# is fast enough; I'd probably only worry about that if/when I actually found that it impaired performance. Most monsters shouldn't have more than 2 or 3 modifier tags, only the player would have more; given that number of look-ups it shouldn't take a huge amount of CPU cycles compared to, say, FOV and graphics.

donblas said...

Yeah, I always implement first and optimize second. Bottlenecks (or the lack of them) are in too surprising of locations for me to guess. :)