OOP C# - Design pattern for MMO mechanics simulator - c#

I've been working on a simulator for FFXIV;
I keep getting really close to finishing, then I get walled into an object access issue that doesn't let me access the stats of the main player. My project can be found at: https://github.com/eein/chocobro
There's plenty of things i can optimize, I know. :P
Basically i'm kind of brute-force winging it to have objects gain access to the objects they need, but i'm looking for the right way to do things.
I'm going to start rewriting it so don't clutch too hard to the example code but its there to see the issue i'm having. :(
Ideally in the next attempt, this is what i'd like to do:
Start with a player class that contains all of the informatin about the player object (in the future, i'd like to create multiples for full group simulation).
Something like:
int main(){
Player p = new Player();
public void setJob()
{
if (job == "bard"){ Player p = new Bard(); }
if (job == "warrior"){ Player p = new Warrior(); }
}
public class Player
{
private string name {get;set;}
private string job {get;set;}
private string STR;
private string DEX;
private string VIT;
//etc..
public virtual void rotation()
{
}
}
//I want to make the program a bit modular for jobs (roles/classes)
//So..
public class Bard : Player
{
public override void rotation()
{
heavyshot.execute();
//etc.
}
Ability heavyshot = new Heavyshot();
public class Heavyshot : Ability
{
public Heavyshot()
{
name = "Heavy Shot";
potency = 150;
dotPotency = 0;
recastTime = 2.5;
TPcost = 60;
animationDelay = 0.8;
abilityType = "Weaponskill";
castTime = 0.0;
duration = 0.0;
}
public override void impact()
{
//add heavier shot buff activation here
base.impact();
}
}
}
public class Ability{
public int cooldown;
public int cost;
public virtual void impact()
{
public virtual void impact()
{
//Deal some damage.
// !! - the key problem is here, i want to initiate a roll to compare to the players CRIT rating versus the roll to determine the bonus damage. But I can't access the initiated players crit from here. The rating may change depending on abilities used so I can't create a new object. I know i need an object reference but I can't figure it out...
log(time.ToString("F2") + " - " + name +
" Deals " + potency +
" Potency Damage. Next ability at: " + nextability);
}
}
I'm probably not being too clear, but basically I want to be able to access the player's crit from ability, and i'm assuming ability can't be set up this way in order for it to work. Does anyone have a good idea what kind of design pattern I should be using so that the virtual functions in ability can access the parent classes players stats?
Ideally, I want the bard class to contain all of the abilities and stat updates pertaining to the bard job, once bard inherits player and the object is changed to reference the Bard object, how do I make it so abilities created by the Ability class dont need an object reference to the parent at the time of creation when accessing that function.
I'm confusing myself, but many thanks to whoever understands my gibberish and can help!

One option is to pass the crit to the Abillity's constructor.
Another option, if an Ability is always connected to the player.
Have the crit property public with private set:
public class Player
{
private double crit { get; private set;}
...
}
Then pass the Player to the Ability's constructor and hold it there.
Note however that this will increase the coupling.
This could be done like this:
public class Ability
{
protected Player _player;
public Ability(Player player)
{
_player = player;
}
}
you would want to change the Headshot class deriving from Ability as well
public class Headshot : Ability
{
public Headshot(Player player) : base(player)
{
...
}
}

Instead of executing abilities rotation directly, save the abilities in a list. This way, you can have setup method in Player, that injects the player into all abilities in rotation. Adding conditions and some kind of "abilityused", that negates next ability is actually one more reason to express the rotation in some kind of list. So you don't have to duplicate the "abilityused" check everywhere, but have it in one place. Something like this:
public class Player
{
private string name {get;set;}
private string job {get;set;}
private string STR;
private string DEX;
private string VIT;
//etc..
public struct RotationAbility
{
public RotationAbility(Func<bool> cond, Ability ability)
{
this.cond = cond;
this.ability = ability;
}
public Func<bool> cond;
public Ability ability;
}
private List<RotationAbility> rotation = new List<RotationAbility>();
public void execute()
{
foreach (var ab in rotation)
{
if (ab.cond())
ab.ability.execute();
}
}
public void setUpRotation()
{
setUpRotation(rotation);
foreach (var ab in rotation)
{
ab.ability.Player = this;
}
}
protected virtual void setUpRotation(List<RotationAbility> rotation) { }
}
//I want to make the program a bit modular for jobs (roles/classes)
//So..
public class Bard : Player
{
protected override void setUpRotation(List<RotationAbility> rotation)
{
rotation.Add(new RotationAbility(()=>buff>0, new Heavyshot());
//etc.
}
public class Heavyshot : Ability
{
public Heavyshot()
{
name = "Heavy Shot";
potency = 150;
dotPotency = 0;
recastTime = 2.5;
TPcost = 60;
animationDelay = 0.8;
abilityType = "Weaponskill";
castTime = 0.0;
duration = 0.0;
}
public override void impact()
{
//add heavier shot buff activation here
base.impact();
}
}
}
public class Ability{
public Player Player { get; set; }
public int cooldown;
public int cost;
public virtual void impact()
{
//Deal some damage.
// !! - the key problem is here, i want to initiate a roll to compare to the players CRIT rating versus the roll to determine the bonus damage. But I can't access the initiated players crit from here. The rating may change depending on abilities used so I can't create a new object. I know i need an object reference but I can't figure it out...
log(time.ToString("F2") + " - " + name +
" Deals " + potency +
" Potency Damage. Next ability at: " + nextability);
}
}
}

Related

Dictionary of different types, where the pulled object must remember its type, best approach?

I'm trying to find the best way to create a dictionary that can hold 6 different sub-types of the abstract Reward type, while also remember the individual item's original sub-type when pulled out again. Several approaches have worked, but I feel like they were all sub-par and that there must be better solutions.
Solutions that works, but I think could be improved on:
A list for each reward sub-type. (Will not scale well as I add large amounts of sub-types)
A list of lists. (This is both ugly and seems inefficient)
Multidimensional array. (Same issues as a list of lists)
Here are the relevant class snippets for my scenario:
public class RewardAssetContainer : MonoBehaviour
{
// Sounds
private static AudioClip losenedBowels = new AudioClip();
public static AudioClip LosenedBowels { get { return losenedBowels; } }
}
public abstract class Reward
{
protected EffectTypes effectType;
protected Rareity rareity;
protected Sprite itemIcon;
protected string itemName;
protected GameObject specialEffect;
}
interface iSoundEffect
{
void SetVolume(float _newVol);
void PlaySound();
void StopSound();
}
class DeathSoundEffect: Reward, iSoundEffect
{
private AudioSource sound;
public DeathSoundEffect(string _itemName, AudioClip _sound, float _vol, EffectTypes _effectType, Rareity _rareity, Sprite _itemIcon)
{
sound = new AudioSource();
itemName = _itemName;
sound.volume = _vol;
sound.clip = _sound;
effectType = _effectType;
rareity = _rareity;
itemIcon = _itemIcon;
}
public void PlaySound()
{
sound.Play();
}
}
public class Item_Inventory : MonoBehaviour
{
private static Dictionary<string, Reward> rewardItemsOwned = new Dictionary<string, Reward>();
public static Reward GetSpecificItem(string _keyValue) { return rewardItemsOwned[_keyValue]; }
private void TestPutToInventory()
{
rewardItemsOwned.Add("LosenedBowels", new DeathSoundEffect("LosenedBowels", RewardAssetContainer.LosenedBowels, 0.5f, EffectTypes.DeathSoundEffect, Rareity.Rare, RewardAssetContainer.EpicExample));
}
}
I would like to, in a different class, be able to do something like:
var tempRewardItem = Item_Inventory.GetSpecificItem("LosenedBowels");
tempRewardItem .PlaySound();
But i can't figure out an elegant way to do this with one collection.
Will you always know the original sub-type you expected ahead of time when pulling out an item, like in your example? If so, you can attempt to cast the object you pulled out before using it:
var tempRewardItem = Item_Inventory.GetSpecificItem("LosenedBowels") as DeathSoundEffect; // Could also use 'as isSoundEffect'
if (tempRewardItem != null)
{
tempRewardItem.PlaySound();
}

C# is it possible to store like classes into a collection?

Tried to title my post as best I could, but here goes.
I have 5 classes for various "effect" animation stuff that can be done to an animation (alpha change, color change, position change, rotation change, scale change). They are all the same aside from variable types. They all use an abstract class for methods that could be shared between them all along with methods that are override in the derived classes.
I have a class that is for handling all the animations of a game object called AnimationHandler. What it does is store all the effect animations (along with the sprite animations) for my game's objects and handling them.
What I want to do is store all the effects into one dictionary that is then used to handle all changes to an animation for that object. I was wondering if it was possible to do or if it would be just easier to have 5 separate dictionaries to handle each effect separately?
The issue that I'm trying to figure out is how to access the variables that aren't in the abstract class.
Here is the sample code for my base class and a derived class:
abstract class EffectAnimation
{
protected EffectInfo Info;
public EffectInfo info
{
get
{ return Info; }
}
protected EffectType TypeOfEffect;
public EffectType typeofeffect
{
get
{ return TypeOfEffect; }
}
public abstract void NewAnimation();
public void Update(double time)
{
AnimationDone(time);
if (!info.Done)
{
if (UtilityAnimation.ReadyForNextFrame(time, Info.FrameLength))
{
Info.NextFrameTime = time + Info.FrameLength;
ChangeValue();
}
}
}
public void Start(double time)
{
Info.StartTime = time;
Info.NextFrameTime = time + Info.FrameLength;
}
public abstract void ChangeValue();
public abstract void Clamp();
protected abstract void AnimationDone(double time);
}
class AlphaAnimation : EffectAnimation
{
private float Change;
public float change
{
get
{ return Change; }
set
{ Change = value; }
}
private float End;
public float end
{
get
{ return End; }
set
{ End = value; }
}
private float Total;
public float total
{
get
{ return Total; }
set
{ Total = value; }
}
public void NewAnimation(EffectInfo stuff, float starting, float ending, float partialtotal)
{
Total = starting + partialtotal;
Info = stuff;
End = ending;
Info.Initialize();
Change = UtilityAnimation.MakeFadeAmount(stuff.AnimationLength, starting, ending, stuff.FPS);
}
public void ChangeValue()
{ Total += Change; }
private void Clamp()
{
if (Change > 0) // animation is positive
{
if (Total > End)
{
Total = End;
}
}
else // animation is negative
{
if (Total < End)
{
Total = End;
}
}
}
private void AnimationDone(double time)
{
Clamp();
if ((Total == End) && (time >= Info.DoneTime()))
{ Info.Done = true; }
}
}
What you ask for (accessing for instance change of AlphaAnimation via a reference of type EffectAnimation) is impossible without reflection or checking for the actual type. That being said, if possible, the design should be changed such that the desired effect of the animation can be triggered without knowing its type. This can be difficult depending on the case; in some cases it might make no sense altogether.
This is a possible solution;
List<EffectAnimation> animations = new List<EffectAnimation>();
animations.Add(new AlphaAnimation());
EffectAnimation item = animations[0];
if(item is AlphaAnimation)
{
AlphaAnimation alphaItem = item as AlphaAnimation;
float total = alphaItem.Total;
float change = alphaItem.Change;
}
This is basically just casting it to the right class and then you can easily access the properties you need. You can still add all your animations to the same list - just make sure to check if it is the right one when you take one out.

Assign multiple values to one object(?)

Note: I am somewhat of a beginner to C#.
I'm working on a little game that will have a bunch of different levels. Each level has its own class that contains variables (and other irrelevant code). Since I need to pass these values to the main class (my form) I have made them all into methods that returns the value I want (since I can't come up with a better solution). example:
class Level01
{
public int Boxes() { return 3; }
public int MaxPoints() { return 46; }
public int Health() { return 63; }
public int[,] SolidBoxes()
{
int[,] position = new int[Boxes(), Boxes()];
position[1, 1] = 1;
return position;
}
}
When I access these values from my form class I do
int boxes;
int maxPoints;
int health;
int[,] solidBoxes;
void readLevelData() //Starts each new level
{
//Reads the correct level
switch (levelNo)
{
case 1:
setValues(Lvl01.Boxes(), Lvl01.MaxPoints(), Lvl01.Health(), Lvl01.SolidBoxes());
break;
//The same case 2:, 3: for Level02,03..
}
}
void setValues(int getBoxes, int getMaxPoints, int getHealth, int[,] getSolidBoxes)
{
boxes = getBoxes;
maxPoints = getMaxPoints;
health = getHealth;
solidBoxes = getSolidBoxes;
}
I am aware that there's probably a million things in my code here that can be done better and I gladly listen if you have any suggestions, but the thing I wish to ask is:
How can I get all the values from each class using maybe just one name? Ex. Instead doing as I do now, is there a way so I can do something similar to this:
case 1:
setValues(Lvl01.Values);
break;
The problem here is in the setValues method, some of the levels has quite a lot of settings that I wish to use, but I doubt the method would want to take like 15 parameters, and I'm not sure what to do when some levels are not using settings that other levels use.
How should I change my code so I do not have to use every single value as a parameter?
You could use a Dictionary<int, Level> to lookup the object representing each level. Instead of the switch/case, you would do something like
Level level = myLevelDictionary[currentLevel];
That requires you change your classes from having one class per level, to one class that represents any level, e.g.:
class Level
{
public int Boxes { get; set; }
public int MaxPoints { get; set; }
public int Health { get; set; }
public int[,] SolidBoxes()
{
int[,] position = new int[boardSize, boardSize];
position[1, 1] = 1;
return position;
}
}
You would then populate your dictionary like
Dictionary<int, Level> myLevelDictionary = new Dictionary<int, Level>()
{
{ 1, new Level() { Boxes = 3, MaxPoints = 46, Health = 63 } },
// etc.
};
UPDATE
A note about abstract classes
You mention abstract classes in your comments. They are useful if all of your levels have some behavior in common, but some have specialized behavior too. They will often be used in games for things that can move in the game, e.g. something like
abstract class Character
{
// Something everyone has
public int HitPoints { get; set; }
// Something everyone does, but does differently
public abstract void Attack(Character target);
}
public class Fighter : Character
{
public int SwordDamage { get; set; }
public void Attack(Character target)
{
target.Damage(this.SwordDamage - target.PhysicalDamageResistance);
}
}
public class Mage : Character
{
public int MagicFireDamage { get; set; }
public int MagicColdDamage { get; set; }
public void Attack(Character target)
{
if (UseFireDamage()) // e.g. roll random chance that the mage uses a fire spell
{
target.Damage(this.SwordDamage - target.FireDamageResistance);
}
else
{
target.Damage(this.SwordDamage - target.ColdDamageResistance);
}
}
}
one way maybe to use a dictionary.
class Level01
{
Dictionary<string,int> values;
public level01()
{
values.Add("Boxes",3);
values.Add("MaxPoints",3);
values.Add("Health",3);
}
//indexer
public int this[string s] {get{return values[s];} set {values[s] = value;}}
}
and use like:
Level01 lv = new Level01();
somemethod(lv["Boxes"]); //passes 3 to some method
although really you would want to use Dictionary<string,object> and add some type checking and other things to make it work smoothly but hopefully you can get started with that

How to subscribe to an event fired from any Instance/Object of type X

i'm currently working on a small game-engine and i'm kinda stuck on finding the right pattern.
First here are some code snippets to understand what i'm trying to achieve :
The Unit : the ability-caster/target ( throw the ability or receive it)
public class Unit
{
public string Name { get; set; } // Unit name
public List<BaseAbility> MyAbilities{get; set;} // abilities that an unit has.
}
The Ability :
public abstract class BaseAbility
{
public string Name {get; set;} // ability name
public Unit Caster { get; set; } // ability caster (owner)
public List<BaseEffect> EffectList {get; set;} // effects that an ability possess
// apply all effects that this ability has on the selected target
public virtual void DoAbility(Unit target)
{
foreach (BaseEffect eff in this.EffectList)
{
eff.CalculateEffect(target);
}
}
// here we gonna subscribe to some events
public abstract void initListeners();
}
The Effect :
public abstract class BaseEffect
{
public BaseAbility Owner { get; set; } // each effect belong to an ability
protected string Name { get; set; } // effect name
public EventDispatcher Event { get; set; } // the event to dispatched when this effect is called or used
//apply effect on target
public virtual void CalculateEffect(Unit target)
{
// Do Stuffs here like damage and poison etc etc
this.Event.DispatchMyEvent();
}
}
the Event Dispatcher :
public class EventDispatcher
{
private object mySender;
private EffectEventArgs myArgument;
public delegate void EventHandler(object sender, EffectEventArgs argument);
public event EventHandler OnMyEvent;
public EventDispatcher(object sender, EffectEventArgs argument)
{
this.mySender = sender;
this.myArgument = argument;
}
public void DispatchMyEvent()
{
if (OnMyEvent != null)
{
OnMyEvent(this.mySender, this.myArgument);
Debug.WriteLine("event has been raised");
}
}
}
The Effect Event Arguments :
public class EffectEventArgs : EventArgs
{
private Unit target; // target affected by effect
// not sure if we even need this EffectEventArgs ?!
public EffectEventArgs(Unit unit)
{
this.target = unit;
}
}
Now i'm gonna create 2 effects and 2 abilities and 2 units for the simulation :
Fire Effect :
public class FireEffect : BaseEffect
{
public FireEffect(BaseAbility effectOwner)
{
this.Owner = effectOwner;
this.Name = "Fire";
}
public override void CalculateEffect(Unit target)
{
// set the event here (to get the target as argument for the event)
this.Event = new EventDispatcher(this.Owner, new EffectEventArgs(target));
base.CalculateEffect(target);
}
}
Wind Effect : same as fire effect but with a different name and event and apply also a different Effect.
Fire Ball ability :
public class FireBall : BaseAbility
{
public FireBall(Unit caster)
{
this.Name = "FireBall";
this.Caster = caster;
this.EffectList = new List<BaseEffect>();
this.EffectList.Add(new FireEffect(this)); // fire ball ability has "FireEffect" as effect
}
public override void DoEffect(Unit target)
{
base.DoEffect(target);
}
}
Two Units , let´s say :
"Dragon" as Unit => has FireBall (ability) and gonna hit =>
"shaolin master of the wind " as Unit has a passiveAbility called "WindFury"
WindFury :
public class PassiveAbility : BaseAbility
{
public PassiveAbility(Unit caster)
{
this.Name = "WindFury";
this.Caster = caster;
this.EffectList = new List<BaseEffect>();
this.EffectList.Add(new WindEffect(this));
this.initListeners(); // subscribe to events (aka add listeners)
}
public override void DoEffect(Unit target)
{
base.DoEffect(target);
}
// THE MAIN PROBLEM :
public override void initListeners()
{
// here i need to subscribe to an event fired from any Instance/Object of type FireEffect so that the targeted Unit
// can react to the Unit caster (kind of counter attack)
// to resume this , it should follows this logic :
// if any Ability has The effect(FireEffect => FireBall for example) was casted
// on this Unit thats possess this passive, react to it and counter with (WindEffect)
// otherwise put : when our dragon or Ryu or even Sasuke throw an ability with
// a FireEffect on our friend "Shaolin" ,he should react to it
// and send the attacker flying with a powerfull WindEffect
}
public void CallBack(object sender, EffectEventArgs e)
{
BaseAbility ab = (BaseAbility)sender;
this.DoEffect(ab.UnitCaster); // hit the unit caster back (revenge)
}
}
Notice please that we can have so many dragons and Shaolins in this game or simulation and any passive holder (unit) should only react to the attacker (unit) etc.
Edit : (Recap of the main problem)
How to subscribe via PassiveAbility.initListeners() to an event fired from any instance of type FireEffect (an ability that use fireEffect) ???
Many thanks in advance for your support and contribution.
Youness
Add a static EventDispatcher to FireEffect and subscribe to that.
public class FireEffect : BaseEffect
{
public static EventDispatcher FireEffectEventDispatcher = new EventDispatcher(null,null);
public FireEffect(BaseAbility effectOwner)
{
this.Owner = effectOwner;
this.Name = "Fire";
}
public override void CalculateEffect(Unit target)
{
// set the event here (to get the target as argument for the event)
FireEffectEventDispatcher.mySender = Owner;
FireEffectEventDispatcher.myArgument = new EffectEventArgs(target);
// Fire the event
FireEffectEventDispatcher.DispatchMyEvent();
//If you still want the base effect firing.
base.CalculateEffect(target);
}
}
To make this work, you'll also need to change your EventDispatcher to let you set the sender / argument after construction (not being able to doesn't make any sense anyway - it's a useless restriction).
I would recommend redoing (or removing) your EventDispatcher implementation, however. It makes no sense to set your event parameters in the constructor when they can more reasonably be set during event triggering.

Implementing a tick/round based combat system

i like to get some thoughts on how to implement a tick based system.
Every action a player or non player got has a initial time to perform and a cooldown time. Once a creatures cooldown time has passed it gets to choose a new action. If the player has to choose a action the game is "paused".
Example:
1: Player heavy swing (50 ticks to perform, 50 ticks to cool down)
2: Game goes on for 50 ticks.
3: NPC's can set actions.
4: Player swings and cools down for 50 ticks.
5: NPC's can set actions.
6: Game paused for the player.
What i currently have works but is not efficient. I have a class with each action as a static method. These method output a struct containing all the data. This will be passed to a actioncue of a individual creature.
Every update loop call the cue and start counting down the attack time if the player has put in a action. Once the attack should be solved i call a static method in the actions class again. And i start counting down the cooldown timer.
So what i should have is probably a list holding all actions and sorting that list skipping unnecessary time/ticks and go straight to the next action. But there will be different types of actions like move, attack, ability and i cant wrap my head around a good implementation of this.
When a creature performs a basic attack this gets called (attack is the creatures own instanced attack struct)
attack = Actions.BasicAttack(this, player, rand);
This is how the Actions class looks like.
public struct Attack
{
public int Damage;
public string Type;
public int Time;
public int Cooldown;
public Creature target;
public bool solved;
}
public static Attack BasicAttack(Creature attacker, Creature defender, Random rand)
{
Attack attack = new Attack();
attack.Damage = rand.Next(attacker.MinBaseDmg, attacker.MaxBaseDmg + 1);
attack.Type = "Melee";
attack.Time = 50;
attack.Cooldown = 30;
attack.target = defender;
attack.solved = false;
return attack;
}
And this gets called in the update method of each creature when the player has a action cued. Tick = 0 if player has no action cued and tick = 1 when player has a action cued up.
protected void ActionCue(int tick)
{
if (attack.target != null)
{
if (attack.Time > 1)
{
Console.WriteLine(attack.Time);
attack.Time -= tick;
this.free = false;
}
else if (!attack.solved)
{
Actions.SolveAttack(attack.Damage, attack.Type, attack.target);
attack.solved = true;
}
else if (attack.solved && attack.Cooldown > 1)
{
//Console.WriteLine(attack.Cooldown);
attack.Cooldown -= tick;
}
else
free = true;
}
}
Consider something like this (i will use pseudocode - its far from being optimized etc. but it might be just fast enough, or set you on your way to optimize what youre trying to do)
class CombatEventList
{
public static AddEvent(CombatEvent event, int ticksTillHappens)
}
virtual class CombatEvent
{
public virtual void CombatAction()
}
class PlayerActionChoice : ComabtEvent
{
public void CombatAction
{
var playerAction = GetUserDecision();//returns i.e CombatEvent PlayerMeeleAttack
CombatEventList.AddEvent(playerAction, 0);
}
}
class PlayerMeeleAttack : CombatEvent
{
int cooldownInTicks = 50;
public void CombatAction
{
MakeAttack()//damages the moster etc - all the stuff the attack is supposed to do
var nextEvent = new PlayerActionChoice();
CombatEventList.AddEvent(nextEvent, cooldownInTicks);
}
}
So, how this works?
We got a list of events.
The list checks all the events that are supposed to happen now and, executes their CombatAction.
In their CombatAction, the events add new events to the list. For example a PlayerMeeleAttack event sets the PlayerActionChoice event after an appropriate cooldown, so that he can take another action later.
After all current CombatEvents are resolved and have added their own CombatEvents to the list, the list checks the next Event (lowest delay)
The list sleeps for the specified number of ticks (the delay of the next Event). Once its done sleeping, it lowers the cooldowns on all events by an appropriate amount, and handles all the current events (those that just hit 0 delay)
This goes in a loop
The list starts with the CombatStartEvent on it, thats going to happen right away(delay 0). It sets the PlayerActionChoice and MonsterActionChoice events in the CombatAction method.
Of course this is far from being optimal, its just a sketch, or an idea for you to think through. There may be better ideas, i didnt give the problem very much thought - but this is obviously more efficient than your current solution :)
Ok after a couple of hours it seems i got this working. Here is the code for anyone who needs it. I am open for feedback too.
This is the ability class, everything needed can be added here. For the tick based system just the time variables are important.
public string AbilityName { get; private set; }
public int minDamage { get; private set; }
public int maxDamage { get; private set; }
public int ActivationTime { get; private set; }
public int CooldownTime { get; private set; }
public int Timer;
public Ability(string AbilityName)
{
if (AbilityName == "attack")
{
this.AbilityName = AbilityName;
minDamage = 10;
maxDamage = 20;
ActivationTime = 20;
CooldownTime = 30;
Timer = ActivationTime;
iconPath = "ability/icon/attack";
}
}
This is the task class, the ability, attacker and targets get passed as parameters, a ability name or type can be used to perform different kinds/types of abilities such as movement vs attacking.
public Ability ability { get; private set; }
public bool onCooldown;
public Creature attacker { get; private set; }
List<Creature> targets = new List<Creature>();
/// <summary>
/// Initiates a attack task
/// </summary>
/// <param name="attacker"></param>
/// <param name="defender"></param>
public Task(Creature attacker, List<Creature> targets, Ability ability)
{
this.ability = ability;
this.attacker = attacker;
this.targets = targets;
onCooldown = false;
}
public void Perform()
{
//performce abilty
Console.WriteLine(attacker.Name + " performce ability");
}
The player or AI can now create a task from the abilities they own like so:
targets.Add(player); //This is just a basic attack so only one "creature" gets in the list
task = new Task(this, targets, abilityList[0]); //Task is created
taskList.Add(task); //Task is added to a list i manage in a main class
free = false; //creature is put on hold and cant do anything till task is completed
This is where most of the magic happens. In the main class this method is being called each update if the player is not "free". I update all the tasks before i do anything with the task next in line because i dont want to edit its stats after updating its status.
private void TaskHandler()
{
int ticksToAdvance = 0;
// get the next task requiring a action
taskList.Sort((x, y) => x.ability.Timer.CompareTo(y.ability.Timer));
//get the amount of cooldown left
ticksToAdvance = taskList[0].ability.Timer;
//Update all tasks
foreach (Task t in taskList)
{
t.ability.Timer -= ticksToAdvance;
}
//check if this task is on cooldown
if (taskList[0].onCooldown)
{
//Reset ability timer, free creature and remove task from the list.
taskList[0].ability.Timer = taskList[0].ability.ActivationTime;
taskList[0].attacker.free = true;
taskList.RemoveAt(0);
}
else
{
//perform ability
taskList[0].Perform();
//set timer to cooldown
taskList[0].onCooldown = true;
taskList[0].ability.Timer = taskList[0].ability.CooldownTime;
}
}

Categories

Resources