I’m working on a template system for creating any kind of item, weapon, equipment or ability.
The way I do it is designed around modability, but your game doesn’t have to be modable in order to support this system. All of the following code is directly from the system I have implemented in my project. It is meant to be an example of how you can implement such a system, and may not be exactly what you are looking for.
Note: Having a stable folder and organization system is important.
The following abstract class, AbilityTemplate, is a template class that has absolutely no functionality. Modders, and myself, inherit from this class in order to implement new abilities. Scroll down to see an example of an Ability that’s in my game.
public abstract class AbilityTemplate {
// Displayed name of the ability
public abstract string AbilityName
{
get;
}
// Displayed RichText description of the ability
public abstract string AbilityDescription
{
get;
}
// Whether or not AI can learn and use this ability
public abstract bool UsedByAI
{
get;
}
// Can the player hold down a key to continuously use this ability?
public abstract bool UsedContinuously
{
get;
}
// Does the ability have a charging period before it can be used?
public abstract bool ChargedUsage
{
get;
}
// The ID of the image that will be used as an avatar for the ability
public abstract string AvatarID
{
get;
}
internal int m_Level = 0;
public int AbilityLevel
{
get
{
return m_Level;
}
protected set
{
m_Level = value;
}
}
// Called the first frame that the player begins using the ability
public virtual void OnAbilityUsedByPlayer(PlayerController player)
{
}
// Called the first frame the AI begins using the ability
public virtual void OnAbilityUsedByAI(Entity other, Entity target)
{
}
// Called every frame while the player has learned and equipped this ability
public virtual void OnAbilityPassivePlayerUpdate(PlayerController player)
{
}
// Called every frame while the AI has learned and equipped this ability
public virtual void OnAbilityPassiveAIUpdate(Entity other)
{
}
// Called every frame while the player is using this ability
public virtual void OnAbilityActivePlayerUpdate(PlayerController player)
{
}
// Called every frame while the AI is using this ability
public virtual void OnAbilityActiveAIUpdate(Entity other)
{
}
// Determines whether or not an AI will use this ability on a target
public virtual bool AICanUseOnTarget(Entity AI, Entity target)
{
return false;
}
// Called the first frame an AI/Player begins chargin this ability
// Only called if ChargedUsage is true
public virtual void OnBeginCharge()
{
}
// Called every frame after OnBeginCharge is called until the player stops charging
// the ability or until it returns true. If it returns true, then the ability is used.
// Otherwise, the ability is not used.
public virtual bool GetIsReady()
{
return false;
}
// Called whenever the player attempts to level up the ability
public virtual bool TryLevelUp()
{
AbilityLevel++;
return true;
}
}
Here is an example ability that implements some very basic charging and damage functionality.
public class ExampleAbility : AbilityTemplate
{
#region Overriden Properties
public override string AbilityName
{
get
{
return "Example Ability";
}
}
public override string AbilityDescription
{
get
{
return "This is the <color=#7F00FF>Example Ability</color>.
\r";
}
}
public override bool UsedByAI
{
get
{
return true;
}
}
public override bool ChargedUsage
{
get
{
return true;
}
}
public override bool UsedContinuously
{
get
{
return false;
}
}
public override string AvatarID
{
get
{
return "ExampleAbility";
}
}
#endregion
public int InitialDamage = 10;
public int Damage = 10;
public float UsageRange = 5f;
public float TimeSinceChargeBegan = 0f;
public float InitialCharginTime = 0.5f;
public float ChargingTime = 0.5f;
public override void OnAbilityUsedByPlayer(PlayerController player)
{
Entity target = new Entity();
// Find target ...
target.SendMessage("DoDamage", Damage);
}
public override void OnAbilityUsedByAI(Entity other, Entity target)
{
target.SendMessage("DoDamage", Damage);
}
public override bool GetIsReady()
{
TimeSinceChargeBegan += Time.deltaTime;
return TimeSinceChargeBegan > ChargingTime;
}
public override bool AICanUseOnTarget(Entity AI, Entity target)
{
return Vector3.Distance(AI.Position, target.Position) < UsageRange;
}
// Every time the ability is leveled up, decrease charging time
// and increase damage done.
public override bool TryLevelUp()
{
AbilityLevel++;
ChargingTime = InitialCharginTime * (AbilityLevel * 0.9f);
Damage = InitialDamage + (1 * AbilityLevel);
return true;
}
}
Later, in an init function, I call some code that Registers the Type of my example ability class:
RegisterTemplate("ExampleAbility", typeof(ExampleAbility));
This function records the ExampleAbility Type in a Dictionary with the string ID “ExampleAbility” as the key.
Later i can invoke that type via
RegisteredAbilities["ExampleAbility"].Invoke()
and assign it to an Ability object which contains behavior for calling all of the functions defined in AbilityTemplate, as well as using the properties defined.
The entire process relies heavily on Reflection in my project, but they don’t have to in your’s. The only time you’d have to use reflection would have to be when you invoke the type and call the functions defined.
IMO this is a very effective method of creating a dynamic system of items or abilities. It doesn’t rely on static properties in order to implement behaviour outside of the few boolean flags that are defined.
In Summary:
Create a class you can inherit from that doesn’t immediately define behaviour. Define your behaviour in the respective ability’s class that extends the base class. Invoke the ability later through some registration and object system.
If you need help actually creating all the functionality for registration, invoking, and declaration, feel free to PM or email me.