ООП C # - шаблон проектирования для симулятора механики MMO

Я работаю над симулятором для FFXIV;

Я очень близко подхожу к завершению, а затем сталкиваюсь с проблемой доступа к объектам, которая не позволяет мне получить доступ к статистике основного игрока. Мой проект можно найти по адресу: https://github.com/eein/chocobro

Я знаю, что я могу оптимизировать множество вещей. :П

В основном я использую грубую силу, чтобы объекты получили доступ к объектам, которые им нужны, но я ищу правильный способ делать что-то.

Я собираюсь начать его переписывать, так что не цепляйтесь слишком сильно за пример кода, но он там, чтобы увидеть проблему, с которой я столкнулся. :(

В идеале в следующей попытке я хотел бы сделать вот что: начать с класса игрока, который содержит всю информацию об объекте игрока (в будущем я хотел бы создать мультипликаторы для полного группового моделирования).

Что-то вроде:

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

Я, вероятно, не слишком ясен, но в основном я хочу иметь доступ к критическому урону игрока от способности, и я предполагаю, что способность не может быть настроена таким образом, чтобы она работала. Есть ли у кого-нибудь хорошее представление о том, какой шаблон проектирования я должен использовать, чтобы виртуальные функции в способностях могли получить доступ к статистике игроков родительских классов?

В идеале я хочу, чтобы класс барда содержал все способности и обновления характеристик, относящиеся к работе барда, после того как бард унаследует игрока и объект будет изменен так, чтобы он ссылался на объект барда, как мне сделать так, чтобы способности, созданные классом способностей, не при доступе к этой функции требуется ссылка на родительский объект во время создания.

Я запуталась, но огромное спасибо тем, кто понимает мою тарабарщину и может помочь!


person Will Volin    schedule 05.01.2014    source источник


Ответы (2)


Один из вариантов - передать crit конструктору Abillity.

Другой вариант, если к плееру всегда подключен Ability. Сделайте свойство крита публичным с частным набором:

public class Player 
{
    private double crit { get; private set;}
    ...
}

Затем передайте Player конструктору Ability и удерживайте его там. Однако обратите внимание, что это увеличит связь.

Это можно было сделать так:

public class Ability
{
    protected Player _player;
    public Ability(Player player)
    {
        _player = player;
    }
}

вы также захотите изменить класс Headshot, производный от Ability

public class Headshot : Ability
{
    public Headshot(Player player) : base(player)
    {
        ...
    }
}
person c0deMonk3y    schedule 05.01.2014
comment
В настоящее время в моем классе Job (который в основном является классом игрока) у меня есть общедоступный Int Crit. См. Здесь: ссылка - идеально, где я хотите использовать рейтинг критического удара Барда: строка 54 здесь и передайте логическое значение true или false, когда он начинает вычислять урон (для чего также нужен доступ к статистике барда). - person Will Volin; 06.01.2014
comment
У вас есть способность расширять MainWindow, что кажется неправильным. Я думаю, что добавление ссылки Player к классу способностей решит вашу проблему. - person c0deMonk3y; 06.01.2014
comment
Как я уже сказал, я взламывал его, чтобы все заработало. Я не продвинутый программист, поэтому пытаюсь понять, что я делаю неправильно. Я предполагаю, что это связано с моими отношениями с объектами. Может быть, некоторые должны быть по-другому связаны с другим классом. @ c0deMonk3y - person Will Volin; 06.01.2014
comment
Я отредактировал ответ, чтобы добавить игрока к способности. Надеюсь это поможет - person c0deMonk3y; 06.01.2014
comment
Спасибо, я думаю, это то, что я ищу. Я попробую переставить кое-что и посмотреть, поможет ли это. Спасибо за понимание! - person Will Volin; 06.01.2014
comment
Еще раз спасибо, ваш совет помог нам наладить работу. :) @ c0deMonk3y - person Will Volin; 06.01.2014

Вместо того, чтобы выполнять ротацию способностей напрямую, сохраните способности в списке. Таким образом, у вас может быть метод настройки в Player, который вводит игрока во все способности по очереди. Добавление условий и какой-то «используемой способности», которая отменяет следующую способность, на самом деле является еще одной причиной для того, чтобы выразить ротацию в виде какого-то списка. Таким образом, вам не нужно дублировать проверку «использованных способностей» повсюду, а иметь ее в одном месте. Что-то вроде этого:

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);
        }
    }
}
person Euphoric    schedule 05.01.2014
comment
Замысел дизайна не позволяет мне сделать это таким образом, поскольку ротация анализируется сверху вниз с условными выражениями для каждой способности в цикле. - person Will Volin; 06.01.2014
comment
@WillVolin, какие именно условия? Может быть, вы могли бы создать какой-то RotationAbility класс, который инкапсулирует это. И вы можете сделать хороший синтаксис, используя лямбда-выражение. - person Euphoric; 06.01.2014
comment
если (бафф ›0) {способность.execute (); } if (cooldown ‹gcd) {capacity2.execute ()} - каждая из перечисленных ниже будет запускать для свойства bool used to true, отменяя использование другой способности до следующего GCD. Я все еще довольно новый программист, поэтому стараюсь не уходить на территорию, где требуются вещи, выходящие за рамки моего уровня знаний. :) - person Will Volin; 06.01.2014
comment
Последний вопрос по этому поводу, прежде чем я приму: в классе Ability я предполагаю общедоступный Player Player {get; set;} вызывает этот объект в способность (инъекция, как вы это выразились?), скажем, я хотел использовать значение крита игрока (которое не отображается atm, но будет общедоступным int CRIT = 341;) Могу ли я просто использовать Player. CRIT, когда в публичной виртуальной пустоте Impact ()? @Euphoric - person Will Volin; 06.01.2014
comment
@WillVolin Да, именно так. - person Euphoric; 06.01.2014