2

So I made a "game" in a c# console app project which is basically a hero who must kill a monster and both the hero and the monster have HP and a random amount of damage given and taken (as it should be). I have a couple tiny problems which don't make sense to me. There is a Regeneration Potion that I have added to the game which obviously adds a random amount of hp between 10-30 to the player.

//Regenerating or not
        if (isRegen == false) //if it doesn't
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine("You've failed to regenerate!");
            Console.WriteLine("Enemy: " + monster.name);
            Console.WriteLine("Enemy's hp: " + monster.hp);
            GetDamage();
        }
        else //if it does
        {
            if (myHP + regenAmount > 100 || myHP == 100)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("You can't regenerate above 100 hp.");
                Game();
            }
            else
            {
                if (potionCounter == 0)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("You are out of health potions. Cannot regenerate!");
                    Game();
                }
                potionCounter--;
                myHP += regenAmount;
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("You consumed a Health Potion!");
                Console.WriteLine("Your hp was regenerated successfully! HP is raised by " + regenAmount);
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("Enemy: " + monster.name);
                Game();
            }
        }

Now, as you can see in the code above, I have made an if statement that checks whether the sum of myHP and the regenAmount is higher than 100, a message that says "You can't regenerate above 100 hp" is displayed. Thing is, when I try this, sometimes it does display the message as presented here, but eventually it decides to display "You've failed to regenerate!" in here and well the game just goes on and the player gets hit. (which obviously shouldn't happen). There is even another similar problem with the potionCounter. As presented in the code:

if (potionCounter == 0)
                {
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.WriteLine("You are out of health potions. Cannot regenerate!");
                    Game();
                }

Whenever the potion counter reaches 0, it should display a message that says that the user is out of potions. It happens and works, but similarly to the previous problem, it sometimes ignore the if statement and allows the user to either heal and continue on decrementing the potionCounter variable or fail to regenerate and get hit by the monster.

I know some of this code is kinda bad and well that's because im kinda new to programming but im trying my best to develop myself and learn as much as I can, which is why I decided to share it here.

Thank you for reading it and I hope you find the solution :)

Edit: The code for Game():

static void Game()
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine(monster.name + "'s hp: " + monster.hp);
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Your hp: " + myHP + "\n");
        Console.ForegroundColor = ConsoleColor.White;
        while(monster.hp > 0 && myHP > 0) //Running until the enemy or the player dies.
        {
            Console.Write("A - ATTACK (Give damage but always get hit)\nD - DEFEND (50% chance of not getting hit)\nR - REGENERATE (Regenerates HP, but if it fails you get hit) - ");
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(potionCounter + " POTIONS LEFT\n");
            string input = Console.ReadLine();

            //ATTACK

            if (input == "a" || input == "A")
            {
                Console.Clear();
                Attack();
            }
            else if (input == "d" || input == "D")
            {
                Console.Clear();
                Defend();
            }
            else if (input == "r" || input == "R")
            {
                Console.Clear();
                Regenerate();
            }
            else
            {
                Console.Clear();
                Game();
            }
        }
    }
Ongoaviv
  • 21
  • 3
  • 2
    There are multiple calls to `Game()`, which makes me guess you're using recursion to manage the "turns" in your game, where you should be using iteration (an almost-infinite main loop usually in games). – AKX Feb 07 '20 at 11:57
  • @AKX The game runs like this: The main method calls a method called Game(). Inside Game(), you have the input message (press A, D or R). This whole thing runs inside this while loop: `while(monster.hp > 0 && myHP > 0) //Running until the enemy or the player dies.` So if i understood you right, I did use iteration. – Ongoaviv Feb 07 '20 at 12:02
  • What is `Game()`, though? – AKX Feb 07 '20 at 12:06
  • I wanted to add it too but I thought maybe it was too much code sharing lol. It's a method that displays the Attack, Defend and Regenerate messages as presented in the pictures, and awaits for user input. – Ongoaviv Feb 07 '20 at 12:08
  • hi @Ongoaviv, can you post the code for Game() – Clint Feb 07 '20 at 12:13
  • Note that there with be automatic scrollbars if your code is long. The important is that you show what's needed to actually reproduce the problem. (see [mcve]) . Currently, this call to "Game()" in both of your snippets has probably something to do with your issue, but without the code, we can't be sure, and thus can't help you. Maybe there is a lot of code in there, so you could try to remove everything that's not needed to show the problem. Doing such exercise is by the way very good as a learning practice ;) ! – Pac0 Feb 07 '20 at 12:15
  • Sounds like a race condition. What's initiating the loop here? – Barry O'Kane Feb 07 '20 at 12:20
  • I have edited the post and added `Game()` for you guys :) – Ongoaviv Feb 07 '20 at 12:24
  • Right, with your `Game()` posted, things are indeed as I suspected – you call Game() re-entrantly from within a function you call in Game(), so you end up with many, many copies of those `while...` loops. – AKX Feb 07 '20 at 12:42
  • @AKX I've been playing around with my project through the recent 10 mins and i was able to fix the regeneration issue by looking at what you did. It helped a lot. I figured that what caused the problem with the regeneration is that I have used the Random class for it, which means you will have a 50% chance of succeeding to heal. Which is, when i think of it, kinda ridiculous... Why did I add that? If you have a potion in your inventory, you shouldn't have a problem using it... I removed it and now it works properly! – Ongoaviv Feb 07 '20 at 12:57
  • Your structure is still problematic. You should call `Game()` exactly once in your game. – AKX Feb 07 '20 at 13:05
  • I agree that my structure is not so professional, as I mentioned before, I am still trying to do my best to learn and overcome mistakes. I will look at what you did and learn from that. Thanks a lot you were very kind! – Ongoaviv Feb 07 '20 at 18:26

2 Answers2

0

This is how I'd structure a simple game like this.

You already seem to have a "Monster" class – good! – but it's elided from this example for simplicity. Similarly, you would probably encapsulate Player as a class to keep track of each player's potions and HP individually.

Either way, the idea is that there's

  • an infinite loop governed by the "game over" flag
  • a function to run a single turn – checking whether the game is over, printing the game state, waiting for input, processing it, running AI actions (which, given our monster is a simple ogre of some ilk, is just it bashing your head in)
  • separate functions for the different events that occur in the game
  • ... and a helper function that lets you change the console color and print a line of text in a single invocation ;-)

I hope this helps! It's not exactly a laser-focused pin-point answer, I know...

class Game
{
    private int playerHp = 100;
    private int monsterHp = 100;
    private int nPotions = 3;
    private readonly Random random = new Random();
    private bool gameOver = false;

    static void ColorPrint(string message, ConsoleColor color)
    {
        Console.ForegroundColor = color;
        Console.WriteLine(message);
    }
    public void Play()
    {
        while (!gameOver)
        {
            RunTurn();
        }
    }
    void RunTurn()
    {
        CheckGameOver();
        if (gameOver) {
            return;
        }
        ColorPrint(String.Format("Monster HP: {0}", monsterHp), ConsoleColor.Red);
        ColorPrint(String.Format("   Your HP: {0}", playerHp), ConsoleColor.Green);
        ColorPrint(String.Format("   Potions: {0}", nPotions), ConsoleColor.Yellow);
        ColorPrint("(A)ttack / (P)otion / anything else to skip your turn?", ConsoleColor.White);
        var command = Console.ReadLine().Trim().ToLower();
        switch (command)
        {
            case "a":
                DoPlayerAttack();
                break;
            case "p":
                DoPlayerPotion();
                break;
            default:
                ColorPrint("You decide to loiter around.", ConsoleColor.White);
                break;
        }
        DoEnemyAttack();
    }
    void CheckGameOver() {
        if (monsterHp <= 0)
        {
            gameOver = true;
            ColorPrint("The monster is slain!", ConsoleColor.White);
        }
        if (playerHp <= 0)
        {
            gameOver = true;
            ColorPrint("You are dead. :(", ConsoleColor.White);
        }
    }
    void DoPlayerAttack()
    {
        var damage = random.Next(1, 10);
        ColorPrint(String.Format("You strike the monster for {0} damage.", damage), ConsoleColor.White);
        monsterHp -= damage;
    }
    void DoPlayerPotion()
    {
        if (nPotions > 0)
        {
            var heal = random.NextDouble() < 0.7 ? random.Next(5, 15) : 0;
            if (heal > 0)
            {
                ColorPrint(String.Format("You heal for {0} HP.", heal), ConsoleColor.Gray);
            }
            else
            {
                ColorPrint("That potion was a dud.", ConsoleColor.Gray);
            }
            playerHp = Math.Min(100, playerHp + heal);
            nPotions--;
        }
        else
        {
            ColorPrint("You rummage through your belongings to find no potions.", ConsoleColor.Gray);
        }
    }
    void DoEnemyAttack()
    {
        var damage = random.Next(1, 10);
        ColorPrint(String.Format("The monster nibbles on you for {0} damage.", damage), ConsoleColor.White);
        playerHp -= damage;
    }
}
AKX
  • 152,115
  • 15
  • 115
  • 172
  • Wow, you're awesome, I will take a look in that. Thanks! I have just added `Game()` to the post, would you mind looking at it and say if you know the solution? :) – Ongoaviv Feb 07 '20 at 12:30
0

Just focusing on this bit of your code for a moment:

if (potionCounter == 0)
{
    // ...
}
potionCounter--;
myHP += regenAmount;

Think about what will happen when the potionCounter is zero:
- It will then set potionCounter to -1
- It will give you the HP

You probably meant to put this code inside an else statement.

Additionally, checking if (potionCounter <= 0) would be more robust than testing for zero exactly.

Buh Buh
  • 7,443
  • 1
  • 34
  • 61