Jump to content

  • Log In with Google      Sign In   
  • Create Account

The Beginner's Programmicon

Let's Make: BattleGame! [Part 2 - The Meat]

Posted by , 07 May 2013 - - - - - - · 889 views

Introduction!
Hello again, people! Posted Image

In Part 2, we're going to fill out all the .cpp files we made last tutorial. These are the meat and potatoes, the thing that makes it all run, what'll give us our game (mostly)!
Hopefully the last tutorial was easy to follow, and you've already prepared the headers for everything we'll be doing here.


Coding Time!
Alright, first off, we're going to create the attack types we defined in Being.h. Open up the Being.cpp you made yesterday, and type this:
#include "Being.h"

using namespace std;

void Being::meleeAttack(Being& target) {
    target.health -= baseDamage;
    cout << name << " dealt " << damage << " damage to " << target.name << "!" << endl;
    cout << name << "'s HP: " << health << "\n" << endl;
}

void Being::gunAttack(Being& target) {
    target.health -= gunDamage;
    ammo--;
    cout << "You shot " << target.name << ", dealing " << Damage << " damage!" << endl;
    cout << "Your HP: " << health << "\n" << endl;
}

void Being::useHP(Being& target) {
    short healed = 10;
    health += healed;
    potions--;
    cout << name << " used a health potion, and it heals for " << healed << " health!\n" << endl;
}
Everything here should be pretty self-explanatory; in each option, we're damaging the target we defined as a parameter in the methods. For potions and ammo, we're subtracting one from their current ammo/potion count, so that the player doesn't have an unlimited supply.

But we're not done in Being.cpp yet; we still need to define all those 'get' methods, as well as our Reset() method. Underneath our attack types, write this:
string Being::getName() {
    return name;
}

int Being::getHealth() {
    return health;
}

int Being::getAmmo() {
    return ammo;
}

int Being::getPotions() {
    return potions;
}

void Being::Reset() {
    health = maxHealth;
    ammo = maxAmmo;
    potions = maxPotions;
}
This should be self-explanatory as well! For each 'get' method, we're returning whatever value it is that we need. We're doing it this way, so as to keep our stats from being modified outside the entity. We're using the Reset method to bring our player back to how he started. You'll see why a bit later.


Monsters Are Not People.
Now that we've got Being.h written up, it's time for our Monster and Character to get their meat!

Open up the Character.cpp and type this in there:
#include "character.h"

Character::Character(std::string newName, int newHealth, int newDamage, int newAmmo, int newPotions) {
    name = newName;
    health = newHealth;
    maxHealth = newHealth;
    
    baseDamage = newDamage;
    
    gunDamage = 7;
    ammo = newAmmo;
    maxAmmo = newAmmo;
    
    potions = newPotions;
    maxPotions = newPotions;
}

void Character::Display() {
    std::cout << "You have " << health << " health, " << ammo << " ammo, and " << potions << " potion(s) left." << std::endl;
}
Alrighty, so what's going on here? The first method, Character(), is the classes' constructor. Calling this method constructs a new instance/object of Character. The parameters we set in the constructor will be the Characters' stats/values, as you can see.
Our Display method is just to show the player what his or her current status is.

Time for Monster.cpp!
#include "Monster.h"

Monster::Monster(string newName, short newHealth, short newDamage) {
    name = newName;
    health = newHealth;
    baseDamage = newDamage;
}
Hahaha, I get a little laugh at how small the Monster class is. For now, this is all we need to define. Monsters aren't Characters. Posted Image


Mortal Combat~.... Again!
I felt like reusing our previous title for the Combat part of this. Posted Image

Combat is simply the container for all our battle-related options and actions. We're going to have to define our constructor, which will actually be empty but is important because it creates the new instance of our Monster. We'll have to display the results of a particular action, see if the monster/player is dead yet and also give the player their set of options. Lots to do!

Let's write our constructor in Combat.cpp:
#include "Combat.h"

using namespace std;

Combat::Combat(Monster& newM) : M(newM) {
    
}
Even though it's empty, this part is really important; we're creating a new instance of Monster here (newM) and then passing it by reference (M).

Let's move on to our combatChoice(). This will be slightly lengthy, so grab a cup of joe, first. Posted Image

Write this code under the constructor:
void Combat::combatChoice(Character& C) {
    bool action = false; //To check for if a player performed an action.
    while (!action) { //While the player hasn't performed an action...
        if (C.getHealth() <= 0) { //If they're dead, then
            cout << "----------------- You died... ------------------" << endl; //Say so
            cout << "Oh dear, it seems you have died... Game Over." << endl;
        } else { //Otherwise...
            cout << "\n\n----------------- Battle Options ------------------" << endl;
            C.Display(); //Display the player's current status.
            cout << "What do you want to do? \n" //Give them a list of actions...
                << "Type the number of the action and press enter. \n"
                << "[1] Melee Attack \n"
                << "[2] Gun Attack \n"
                << "[3] Health Potion" << endl;
    
            int choice; //To track what number they submit.
            cin >> choice; //Let them type a number and hit Enter.
            switch (choice) { //Create a switch statement to go over the player's options
                case 1: //If they typed 1
                    cout << "\n\n----------------- Battle Results ------------------" << endl;
                    C.meleeAttack(M); //Perform the Melee attack type, with the monster as the target
                    action = true; //Mark that we performed an action to end the loop.
                    break;
                
                case 2: //If they typed 2
                    cout << "\n\n----------------- Battle Results ------------------" << endl;
                    if(C.getAmmo() == 0) { //If the player doesn't have anymore ammo...
                        cout << "You're out of ammo! \n" << endl; //Say so.
                        break; //End without anything happening; this will send us to choose another option
                    } else {
                        C.gunAttack(M); //Else, if we have ammo, use the Gun type attack.
                        action = true; //Mark we performed an action!
                        break;
                    }
                
                case 3: //And if they typed 3...
                    cout << "\n\n----------------- Battle Results ------------------" << endl;
                    if(C.getPotions() == 0) { //If we're outta potions...
                        cout << "You're out of potions! \n" << endl; //Say so.
                        break; //End early; sends us back to option choosing.
                    } else {
                        C.useHP(C); //Else we'll use a Health Potion on ourselves.
                        action = true; //Mark we performed an action.
                        break;
                    }
            }
        }
    }
}
Okay, so I commented on most of what's going on here. Hopefully, you can follow along without too much trouble. We're just going over a loop that allows our players to choose their action. The switch statement switches between different cases based on what the player's typed, or what the value of choice is.

Now, we'll finish up with the last method:
void Combat::combat1(Character& C) {
    while (M.getHealth() > 0 && C.getHealth() > 0) {
        M.meleeAttack(C);
        
        combatChoice(C);
    }
    
    if(M.getHealth() <= 0) {
        cout << "----------------- You won! -------------------" << endl;
        cout << "Congratulations, you have killed the monster!" << endl;
    }
}

You could say that this method 'initializes' our combat. The while loop checks to see if both the Monster and Character are still alive, and if they are then we loop though the Monster's meleeAttack on the Character, and the Character's choices for combat. The if statement here is just saying the player won if the Monster is dead.

Conclusions.
Alright, so we typed a lot today and we're on the threshold of fighting our own monster! In the next Part of this series, we'll be writing the code for Main.cpp that will run all of this and give us a monster to fight. Posted Image

I'm planning on releasing Part 3 by tonight, since it'll be pretty quick. The tutorial after Part 3 will be adding dynamic damage, which I will explain at that time. Posted Image

Anyhow, thanks for reading, and leave feedback! Posted Image


Let's Make: BattleGame! [Part 1 - Classes]

Posted by , 06 May 2013 - - - - - - · 3,556 views
C++, Game, Beginner, text based and 2 more...
Introductions!
Hello everehbody! Posted Image

This is my first journal entry/tutorial here on GameDev, and I hope I do a good job at explaining the concepts I want to get across. This tutorial series is mainly aimed at beginners (given the blog's name) and I hope it's simple enough.


What We'll Be Making.
Using the fantastical language of C++ (which is what this blog is primarily for), we'll be making a turn-and-text-based combat game.

"What's a turn-and-text-based combat game," you ask? Well, you'll see by the end of this tutorial.

The point of this series is to introduce beginners to the basic game loop and to simple algorithms, such as would be seen in any basic RPG game. We'll be making a game where you have a set of things you can do while fighting an enemy.

The target product of the first few tutorials is a game where you:
  • Have an intro to set your name
  • Allow the player to allocate his/her stat points to various upgrades
  • Allow the player to choose what enemy he/she will be fighting
  • Give the player three options during combat: Melee Attack, Gun Attack and using a Health Potion
  • Create an enemy that can perform all three of the above
Hopefully, as we go along, this list will become more apparent. Posted Image


Let's Get Started!
Alright, so fire up whatever IDE you're using and let's get started!

So, first off, we're only making the barebones of the system in this tutorial. We'll be implementing an Engine later. Open up a new C++ file and name it Being.h.
#ifndef Being_h
#define	Being_h

#include <iostream>

class Being {
    protected:
        std::string name;
        int health, maxHealth;
    
        int baseDamage, damage, gunDamage;
    
        int ammo, maxAmmo;
        int potions, maxPotions;
    
    public:
    
        void meleeAttack(Being& target);
        void gunAttack(Being& target);
        void useHP(Being& target);
        
        std::string getName();
        int getHealth();
        int getAmmo();
        int getPotions();
    
        void Reset();
};

#endif
What're we doing here? This is actually pretty simple. We're going to use inheritance to make our Character and Monster classes smaller and simpler.
In Protected, we're defining the values that both Character and Monster will need. Protected makes sure that only Being and it's 'children' will be able to get/modify those values.
In Public, we're defining the methods that other classes (outside of Character and Monster) will need to use; for example, since string name; is a Protected value, we'll need some way for other classes to find out what that Being's name is. The attack types will also need to be used outside of Being.


Not too bad, right? Grab a cup of joe and let's continue. Posted Image


Alright, so we've got our Being class now. Create a new file and name it Being.cpp, but leave it alone for now. Create a new header file and call it Character.h. Put this stuff in it:
#ifndef Character_h
#define	Character_h

#include "Being.h"

class Character : public Being {
        public:
          Character(string newName, int newHealth, int newDamage, int newAmmo, int newPotions);
          
          void Display();
};

#endif
Much shorter, thanks to Being. Time for explanations!
Character is inheriting all the values that Being.h has, and that's why we don't need to redefine all the ints. You can call Character a 'child' of Being.

Like before, make a new file called Character.cpp and leave it be. Now, we're going to need to be able to create a Monster just like we will for Character. Create a new header called Monster.h.
#ifndef Monster_h
#define	Monster_h

#include "Being.h"

class Monster : public Being {
      public:
           Monster(string newName, short newHealth, short newDamage);
};

#endif
Hopefully, this is self-explanatory. Monster is also a 'child' of Being, since we're inheriting it from Being as well. If you haven't noticed, we're defining fewer values in Monster's constructor; this is because (for now) Monster will only have the Melee Attack type. Once we delve into a more involved combat system, we'll give the monsters their own special attacks.


Mortal Combat~!
I'm sure you've gathered what class is next, based on the title of this section. We'll need to have our Combat class up and ready for the next part of the tutorial, and I think this will be the most interesting part of our game.
#ifndef Combat_h
#define	Combat_h

#include "Character.h"
#include "Monster.h"

class Combat {
    private:
        Monster& M;
        
        void combatChoice(Character& C);
    
    public:
        Combat(Monster& newM);
          
        void combat1(Character& C);
};

#endif
Although the class itself is small, our Combat.cpp is going to be pretty involved. Alright, time to explain the things we see in here...
  • Monster& M; - This is creating a reference to Monster, and it's how we'll be affecting the monster in Combat.cpp. Our reference here will be referencing the instance of the Monster we'll make in Main.cpp.
  • Combat(Monster& newM); - This is Combat's constructor, which is how we'll create new combat instances for when we start a fight with a monster. We're referencing a new Monster here, similar to Monster& M.
  • void combatChoice(Character& C); - Here, we're going through loop for the player's options in battle. We're referencing the Character instance (or object?) that will be created in Main.cpp.
  • void combat1(Character& C); - This will be the loop for our combat. We'll use this to go through the attacks, and check to see wether or not the Monster died. We're referencing the Character instance (or object?) here.
Simple enough, right? Now, the reason we're putting our reference to Monster (Monster& M) and our combatChoice() into Private, is because only Combat will need to use these. For an explanation of the reasons behind using Public, Private and Protected, click here.

Conclusion
Well, we've set up all the classes we'll need to begin our game, and that's what we'll be doing in the next part. Hopefully I get it out by tomorrow, or tonight if I'm lucky. Posted Image

Leave all the feedback, positive or negative, you want! I'm taking all criticisms into account. Posted Image





May 2013 »

S M T W T F S
   1234
567891011
12131415161718
19202122232425
262728293031