Hello, I’m trying to create a flexible ability system for my card game. The combat is turn-based. Each card represents a skill or ability. Some cards can’t be played in certain conditions. The effects for a card include cause damage, add a buff, move some distance, etc. To achieve that, here’s what I’m working on:
public class CardData : ScriptableObject
{
//Other data...
public int Cost;
public int Range;
public Condition[] conditions;
public Effect[] Effects;
}
Each card contains arrays of conditions and effects. Conditions are used to check if this card can be played or this effect should be ignored.
[System.Serializable]
public struct Effect
{
public enum Type
{
Damage, //value = (physical, magic)
AddBuff, //value = buffID
Move, //value = moveDistance
//...
}
public Type type;
public float value1;
public float value2;
public Condition[] conditions;
public void Perform(Character attacker, Character target)
{
type switch
{
Type.Damage => target.CurrentHealth -= value,
Type.AddBuff => target.AddBuff(Id: (int)value1),
Type.Move => attacker.Move(value1);
};
}
}
[System.Serializable]
public struct Condition
{
public Type type;
public float val;
public bool IsSatisfied(Character character, Character target)
{
return type switch
{
Type.NotInAir => character.IsInAir,
Type.NeedWeapon => character.HasWeapon,
Type.InRange => Game.GetDistance(character.transform, target.transform) <= val,
Type.OutOfRange => Game.GetDistance(character.transform, target.transform) > val,
_ => false
};
}
public enum Type
{
NotInAir,
NeedWeapon,
InRange,
OutOfRange,
}
}
To use the system in combat:
public class CombatManager
{
public bool CanPlayCard(CardData card, Character attacker, Character defender)
{
foreach (var condition in card.conditions)
{
if (!condition.IsSatisfied(attacker, defender))
return false;
}
return true;
}
public void PlayCard(CardData card, Character attacker, Character defender)
{
foreach (var effect in card.Effects)
{
effect.Perform(attacker, defender);
}
}
}
Sample of the card data:
Currently this works okay for me, but it’s not very flexible. And the “valueX” thing in Effect is stupid as I have to remember meanings of the values for every Type. I am thinking of finding a way to add different types of node to the effects list. e.g. the AddBuff type will show a field where I can drag a BuffData in instead of using ids. Any advice on improving this system?