OOP C# - Design pattern for MMO mechanics simulator

431 views Asked by At

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!

2

There are 2 answers

5
c0deMonk3y On BEST ANSWER

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)
    {
        ...
    }
}
5
Euphoric On

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);
        }
    }
}