3

have to transform a RPG game that I've created in a console application to a windows form application for a Intro to Programming class. Everything was going as expected but then I got stuck on transforming the combat system. Below are the button click event that calls the combat function and the combat function:

private void btStoryProgression_Click(object sender, EventArgs e)
{
     {...}

     else if (flagControl == 20)
        {
             Enemy enemy = new Enemy("Hellhound", "Oblivion Cave", 1);

             if (combat(player, enemy) == true)
             {
                 Lines.Text += "You won!\n";
                 player.itemList.Add(new Item("Fruit", 22));
             }
             else
             {
                 Lines.Text = "YOU DIED.";
                 Lines.Text += "GAME OVER.";
                 return;
             }
        }
}


bool combat(Player player, Enemy enemy)
{
    Lines.Text = enemy.getName() + " appeared!\n";
    int damage;
    bool battle = true;
    int battleChoice = 0, itemChoice = -1;

    turns = 0;
    Lines.Text += "What are you going to do?";
    buttonVisibility();
    buttonChoices("Attack", "Item");

    while (battle)
    {
         if (player.getSpeed() > enemy.getSpeed())
         {
            //Player turn
            if (battleChoice == 1)
            {
                Lines.Text = player.getName() + " attacked!\n";

                if (player.getAccuracy() < 100)
                {
                    Random rnd = new Random();
                    if (rnd.Next(1, 100) > player.getAccuracy())
                    {
                        Lines.Text += "The attack missed!\n";
                        goto enemyturn;
                    }
                }


                damage = player.getAtk() - enemy.getDef();
                if (damage < 0)
                    damage = 0;
                enemy.setHP(enemy.getHP() - damage);
                if (enemy.getHP() < 0)
                    enemy.setHP(0);

                    Lines.Text += "You dealt " + damage + " damage to " + enemy.getName();
                    Lines.Text += "Enemy HP is now " + enemy.getHP();

                    if (enemy.getHP() == 0)
                        return true;
                }
                else if (battleChoice == 2)
                {
                    Lines.Text += "Your items: ";
                    foreach (Item item in player.itemList)
                    {
                        Lines.Text += item.getName();
                    }
                    Lines.Text += "Choose a number based on the order the items appeared";
                    try
                    {
                        do
                        {
                            itemChoice = Convert.ToInt32(Console.ReadLine());
                            itemChoice--;

                        } while (itemChoice > player.itemList.Count());
                    }
                    catch (FormatException)
                    {
                        Console.WriteLine("SOMETHING WRONG!!");
                    }

                    Lines.Text += "Player HP was: " + player.getHP();
                    player.setHP(player.getHP() + player.itemList[itemChoice].getRecoverQtd());
                    Lines.Text += "Player HP is now: " + player.getHP();
                    player.itemList.RemoveAt(itemChoice);

                }

                enemyturn:
                //Enemy turn
                if (battleChoice != 0)
                {
                    Lines.Text += enemy.getName() + " attacked!\n";

                    damage = enemy.getAtk() - player.getDef();
                    if (damage < 0)
                        damage = 0;
                    player.setHP(player.getHP() - damage);
                    if (player.getHP() < 0)
                        player.setHP(0);

                    Lines.Text += enemy.getName() + " dealt " + damage + " damage to " + player.getName();
                    Lines.Text += player.getName() + " HP is now " + player.getHP();
                }

                if (player.getHP() == 0)
                    return false;

                turns++;
            }
            else
            {
                enemyturn:
                //Enemy turn
                if (battleChoice != 0)
                {
                    Lines.Text += enemy.getName() + " attacked!\n";

                    damage = enemy.getAtk() - player.getDef();
                    if (damage < 0)
                        damage = 0;
                    player.setHP(player.getHP() - damage);
                    if (player.getHP() < 0)
                        player.setHP(0);

                    Lines.Text += enemy.getName() + " dealt " + damage + " damage to " + player.getName();
                    Lines.Text += player.getName() + " HP is now " + player.getHP();

                    if (player.getHP() == 0)
                        return false;
                }
                //Player turn
                if (battleChoice == 1)
                {
                    Lines.Text += player.getName() + " attacked!\n";

                    if (player.getAccuracy() < 100)
                    {
                        Random rnd = new Random();
                        if (rnd.Next(1, 100) > player.getAccuracy())
                        {
                            Lines.Text += "The attack missed!";
                            goto enemyturn;
                        }
                    }

                    damage = player.getAtk() - enemy.getDef();
                    if (damage < 0)
                        damage = 0;
                    enemy.setHP(enemy.getHP() - damage);
                    if (enemy.getHP() < 0)
                        enemy.setHP(0);

                    Lines.Text += "You dealt " + damage + " damage to " + enemy.getName();
                    Lines.Text += "Enemy HP is now " + enemy.getHP();

                    if (enemy.getHP() == 0)
                        return true;
                }

                else if (battleChoice == 2)
                {
                    Lines.Text += "Your items: ";
                    for (int i = 0; i < player.itemList.Count; ++i)
                    {
                        Lines.Text += player.itemList[i].getName();
                    }
                    Lines.Text += "Choose a number based on the order the items appeared";

                }
                turns++;
            }
            return true;

        }

Basically, when I press the button and the combat is called, the whole function is skipped until some loose "else" is found or, if I restrict each comparison, the program just freezes. I've made some searches and I saw people suggesting using Delegate or BackgroundWorker but I'm still confused on using them. This link was the closest question to mine that I've found but it couldn't help me as well (maybe because I'm a newbie...).

Can you guys please help me? Thank you in advance :)

Community
  • 1
  • 1
  • I am not sure if you are saying that your problem is that the method no longer works or that your gui becomes unresponsive while it is running. If it is the second, you should look into [async and await](https://msdn.microsoft.com/en-us/library/hh191443.aspx?f=255&MSPPError=-2147217396). Fair warning though, in winforms you cannot access gui objects from any thread but the main thread. You will need to invoke any gui changes you are making. [This answer](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c) covers that quite well. – pquest Dec 13 '15 at 20:50
  • Oh, wow... I think it is this, but it looks really complex... I'll try to understand. In my case both button click event and combat method are on the Form class (the reason why I'm accessing the label "Lines" on combat). Oh, and the problem is the second one, unresponsive GUI while executing the function (it actually will skip most of the combat if it finds any unrestricted piece of code (like an "else" statement). – Diego Gonzales Dec 13 '15 at 21:25
  • Why do you call the combat method twice? You could simply store the returned bool in a variable and check this variable in your if statement, couldn't you? – joko Dec 14 '15 at 06:42
  • Oh, this was actually one test that I was doing, it's not part of the code. (I'll edit, thank you). Still stuck with my problem, though. Tried using Invoke, but I couldn't get it right... – Diego Gonzales Dec 14 '15 at 06:44
  • @DiegoGonzales did my answer help you? – pquest Dec 18 '15 at 01:11

1 Answers1

1

Your first issue is that your method to calculate combat is running on the GUI thread. Since it is busy, it cannot actually do its real job of updating the GUI and the GUI freezes. You should never do long running tasks on the GUI thread.

You can correct this using async and await by creating a new async method and just running that inside your button click:

private async Task RunCombat()
{
    Enemy enemy = new Enemy("Hellhound", "Oblivion Cave", 1);
    //this next line will run async now and will not block your gui thread.
    bool combatResult = await Task.Run(() => combat(player, enemy));

     if (combatResult)
     {
         Lines.Text += "You won!\n";
         player.itemList.Add(new Item("Fruit", 22));
     }
     else
     {
         Lines.Text = "YOU DIED.";
         Lines.Text += "GAME OVER.";
         return;
     }

and then just call this method inside your button click instead of what you already have. Once you do that, you will no longer be able to use Lines.Text += in your combat method as it will be running on a different thread.

I would create a delegate called LineAppenderDelegate just above your form class declaration:

public delegate void LineAppenderDelegate(string lineToAdd);

and then create a AppendLine method:

private void AppendLine(string lineToAdd)
{
    if(InvokeRequired) //if we are not on the gui thread
    {
        //re-call the same method on the gui thread
        LineAppenderDelegate d = new LineAppenderDelegate(AppendLine);
        Invoke(d, new object[]{lineToAdd});
    }
    else //we are on the gui thread and can just do the work
    {
        Lines.Text += lineToAdd;
    }
}

and use this to add lines instead. This method basically checks to see if you are on the correct thread to interact with the gui. If you are not, it recalls the method on the gui thread. If you are, it just does its work.

pquest
  • 3,151
  • 3
  • 27
  • 40