I am actually working on my own turn based tile game and have begun working out the ability system just recently. I am using the FlatRedBall game engine, which gives me a lot of good free functionality to support this sort of thing. There is still work that needs to be done, but here is the gist:
I have 2 main classes to handle abilities: BasicAbility and AbilityEffect. The numbers and names, etc for the abilities and effects will be stored in CSV format on disk, because FlatRedBall has some amazing code generation support for CSV files, which makes loading the data into C# objects trivial for me. A sample CSV entry for an ability might look like:
Poison Dart, new AbilityEffect(100, 0, 0, 0, 0, 1)
,new AbilityEffect(10, 0, 0, 0, 0, 5)
I could just as easily do these values as separate columns, but doing the above makes FlatRedBall hydrate the objects for me and link them to the ability dictionary. When I put a player type into a CSV file, I would simply add a list of ability names to the tacticalEntities.csv file as a column for the soldier for instance.
In short, you have data you need to load and not a scripting need necessarily.
BTW, I also wrote an EffectManager class to handle linking AbilityEffects to Entities in the game. It applies the effect values to the Entity objects over time based on the ticks.
Here are the classes:
public class BasicAbility : IAbility
{
public BasicAbility(ICollection<IAbilityEffect> effects)
{
Effects = effects;
}
public void execute(TacticalEntity source, ICollection<TacticalEntity> destination)
{
foreach (TacticalEntity entity in destination)
{
EffectManager.AddEffectsToEntity(entity, Effects);
}
}
public ICollection<IAbilityEffect> Effects
{
get;
private set;
}
}
public class AbilityEffect : IAbilityEffect
{
public AbilityEffect(int healthPerTick, int speedEffect, int defenseEffect, float aggroRadiusEffect, int strengthEffect, int totalticks)
{
HealthEffectPerTick = healthPerTick;
SpeedEffectWhileActive = speedEffect;
DefenseEffectWhileActive = defenseEffect;
AggroRadiusEffectWhileActive = aggroRadiusEffect;
StrengthEffectWhileActive = strengthEffect;
TotalTicks = totalticks;
}
public AbilityEffect(TacticalEntity source, TacticalEntity affectedEntity, IAbilityEffect that)
: this(that.HealthEffectPerTick, that.SpeedEffectWhileActive, that.DefenseEffectWhileActive, that.AggroRadiusEffectWhileActive, that.StrengthEffectWhileActive, that.TotalTicks)
{
AffectedEntity = affectedEntity;
SourceEntity = source;
}
public int HealthEffectPerTick
{
protected set;
get;
}
public int SpeedEffectWhileActive
{
protected set;
get;
}
public int DefenseEffectWhileActive
{
protected set;
get;
}
public float AggroRadiusEffectWhileActive
{
protected set;
get;
}
public int StrengthEffectWhileActive
{
protected set;
get;
}
private int _TotalTicks;
public int TotalTicks
{
get
{
return _TotalTicks;
}
private set
{
_TotalTicks = value;
TicksRemaining = value;
}
}
public int TicksRemaining
{
get;
private set;
}
public bool Active
{
get
{
return TicksRemaining > 0;
}
}
public TacticalEntity AffectedEntity
{
get;
private set;
}
public void ApplyConstantEffects()
{
AffectedEntity.strength += StrengthEffectWhileActive;
AffectedEntity.defense += DefenseEffectWhileActive;
AffectedEntity.speed += SpeedEffectWhileActive;
AffectedEntity.aggroCircle.Radius += AggroRadiusEffectWhileActive;
}
public void RemoveConstantEffects()
{
AffectedEntity.strength -= StrengthEffectWhileActive;
AffectedEntity.defense -= DefenseEffectWhileActive;
AffectedEntity.speed -= DefenseEffectWhileActive;
AffectedEntity.aggroCircle.Radius -= AggroRadiusEffectWhileActive;
}
public void ApplyEffectTick()
{
if (Active)
{
AffectedEntity.health += HealthEffectPerTick;
--TicksRemaining;
}
}
public IAbilityEffect Clone(TacticalEntity source, TacticalEntity entity)
{
return new AbilityEffect(SourceEntity, entity, this);
}
public TacticalEntity SourceEntity
{
get;
private set;
}
}
public static class EffectManager
{
private static Dictionary<TacticalEntity, ICollection<IAbilityEffect>> entityEffects = new Dictionary<TacticalEntity, ICollection<IAbilityEffect>>(20);
private static double lasttick = TimeManager.CurrentTime;
public static void AddEffectsToEntity(TacticalEntity entity, ICollection<IAbilityEffect> effects)
{
if (!entityEffects.ContainsKey(entity))
{
entityEffects.Add(entity, new List<IAbilityEffect>(effects.Count));
}
foreach (IAbilityEffect effect in effects)
{
AddEffectToEntity(entity, effect);
}
}
public static void AddEffectToEntity(TacticalEntity source, TacticalEntity entity, IAbilityEffect effect)
{
IAbilityEffect newEffect = effect.Clone(source, entity);
ICollection<IAbilityEffect> effects;
if (!entityEffects.ContainsKey(entity))
{
effects = new List<IAbilityEffect>(1);
entityEffects.Add(entity, effects);
}
else
{
effects = entityEffects[entity];
}
effects.Add(newEffect);
}
public static void AddEffectToEntity(TacticalEntity entity, IAbilityEffect effect)
{
AddEffectToEntity(null, entity, effect);
}
public static void Activity()
{
if ((TimeManager.CurrentTime - lasttick) > 1.0)
{
lasttick = TimeManager.CurrentTime;
foreach (KeyValuePair<TacticalEntity, ICollection<IAbilityEffect>> pair in entityEffects)
{
TickAndRemoveInactiveEffects(pair);
}
}
}
private static void TickAndRemoveInactiveEffects(KeyValuePair<TacticalEntity, ICollection<IAbilityEffect>> pair)
{
List<IAbilityEffect> effectsToRemove = null;
foreach (IAbilityEffect effect in pair.Value)
{
effect.ApplyEffectTick();
if (!effect.Active)
{
if (effectsToRemove == null)
{
effectsToRemove = new List<IAbilityEffect>();
}
effectsToRemove.Add(effect);
}
}
if (effectsToRemove != null)
{
foreach (IAbilityEffect effect in effectsToRemove)
{
pair.Value.Remove(effect);
}
}
}
}
eval
. The same idea will work for your language of choice. \$\endgroup\$