HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Game passive skill system

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
passivegameskillsystem

Problem

I'm trying to create some easily accessible database of different skills. General idea is that every skill should do something different and be able to act on different things (ex. one skill which icreases weapon attack by 'x' [it acts on weapon] and other one which icreases chances to drop item by 'z'% [it acts on monster]).

I see it that way:

  • There is static database of skills accessible from everywhere



  • Skills have effects(methods with different return types, different parameters and calculations)



-
To use skill we add it's effect in particular calculations;

  • Example: If we want to increase drop chance of monster by 'z'% then, in place where we calculate that drop chance (in this case in Monster class), lets say (Monster.DropChance = 0.5f) we add skill effect : after skill implementation (Monster.DropChance = 0.5f + SkillDatabase.IncreaseDropChance.Effect (this))



I came up with a code like this:

```
public static class SkillData // Static class acting as database for keeping skills in one place
{
// Names of skills
public enum Name
{
IncreaseDropChance = 1,
IncreaseWeaponAtk = 2
}

// T - return type of skill effect, U - type of object on which effect works
public class Skill
{
public int Level { get; set; }
public float ExperienceCost { get; set; }
public Name SName;

// Delegate for creating unique effects for each skill
public delegate T EffectDel (U onWhat);
public EffectDel Effect;

public Skill (Name name)
{
SName = name;
}
}

// Custom dictionary for generic skill
public class SkillDictionary
{
private Dictionary _dict = new Dictionary();

public void Add(Name key, Skill skill)
{
_dict.Add(key, skill);
}

public Skill GetValue(Name key)
{
return _dict[key] as Skill;
}
}

// Static for easy access
public sta

Solution

Hmmm,

Well I think the way you are going about it, while functionally correct won't scale well.

Ideally you want to try more of an aggregation. Off the top of my head I would approach this like:

interface Skill
{
   string Name{get;}
   double value{get;}
}


now with this base object you can write the rest of your code doing calculations based on your skills value.

As for the modifying, well nothing else needs to know if a Skill is modified to use it.
To handle that I would make a ModifiedSkill agnostic from everything else:

public interface ModifiedSkill : Skill
{
    IEnumerable Modifiers { get; }

    double BaseValue { get; }
    double Value { get; }

}


Now, what is a modifier you ask? well:

public interface Modifier
{
    string Name { get; }
    string Cause { get; }
    double Apply(double skill);
    Boolean Active { get; set; }
}


So that is all the interfaces you need to cover pretty much all eventualities, allowing for some potentially cool things down the line like timed modifiers or conditional modifiers or whatever.

Now, how to actually implement this?

Public class ModifiedSkillImpl : ModifiedSkill
    {
        private readonly List _modifiers;
        private readonly double _baseValue;

        public ModifiedSkillImpl(string name, double baseValue, IEnumerable modifiers)
        {
        if (name == null) throw new ArgumentNullException("name");
        if (modifiers == null) throw new ArgumentNullException("modifiers");
        Name = name;
        _baseValue = baseValue;
        _modifiers = modifiers.ToList();
    }

    public string Name { get; private set; }

    public IEnumerable Modifiers
    {
        get
        {
            return _modifiers;
        }
    }

    public double BaseValue
    {
        get
        {
            return _baseValue;
        }
    }

    public double Value
    {
        get
        {
            return Modifiers.Aggregate(_baseValue, (current, modifier) => modifier.Apply(current));
        }
    }
}


Now, this is only one example of course, you could do a lot of alternate skill implementations.
The final step would be i suppose to create PoisonModifier/ItchyLeftEarModifier etc
and then apply them with some effect like +5% -20% etc, or whatever.

This separation also means that while most things will be using the Skill.Value which in the case of a modified skill will be the modified version some special something could check if the skill is a Modified skill, if so cast it and use the base stat instead!

(one final note)
I was a bit wasteful with the Value aggregation in ModifiedSkillImpl, tbh I would probably have a private variable for modified Skill and only recalculate if the modifiers had changed since last call but you know, this is just a quick POC.

Code Snippets

interface Skill
{
   string Name{get;}
   double value{get;}
}
public interface ModifiedSkill : Skill
{
    IEnumerable<Modifier> Modifiers { get; }

    double BaseValue { get; }
    double Value { get; }

}
public interface Modifier
{
    string Name { get; }
    string Cause { get; }
    double Apply(double skill);
    Boolean Active { get; set; }
}
Public class ModifiedSkillImpl : ModifiedSkill
    {
        private readonly List<Modifier> _modifiers;
        private readonly double _baseValue;

        public ModifiedSkillImpl(string name, double baseValue, IEnumerable<Modifier> modifiers)
        {
        if (name == null) throw new ArgumentNullException("name");
        if (modifiers == null) throw new ArgumentNullException("modifiers");
        Name = name;
        _baseValue = baseValue;
        _modifiers = modifiers.ToList();
    }

    public string Name { get; private set; }

    public IEnumerable<Modifier> Modifiers
    {
        get
        {
            return _modifiers;
        }
    }

    public double BaseValue
    {
        get
        {
            return _baseValue;
        }
    }

    public double Value
    {
        get
        {
            return Modifiers.Aggregate(_baseValue, (current, modifier) => modifier.Apply(current));
        }
    }
}

Context

StackExchange Code Review Q#42278, answer score: 3

Revisions (0)

No revisions yet.