Page 1 of 1

combat game in C++ part II adding inheritance and other features

#1 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

Reputation: 119
  • View blog
  • Posts: 690
  • Joined: 07-January 09

Posted 01 May 2009 - 02:14 PM

Howdy all, this is CrazyJugglerDrummer with part 2 of making a combat text-game in C++. If you havenít read the first tutorial I suggest you at least glance over it before looking at this one. The changes wonít be hugely apparent on the outside when running the program, but they make a difference on the inside and allow for further expansion and a more logical structure, like inheriting both Monster and Character from a Being class, and adding some random numbers.


So, down to business. First we need to factor out some characteristics from the Monster and Character classes. What do they both have? An attack method, health and name variables, etc. So we put those in our Being.h. We will also add several more variables that will allow for further growth in the game, such as magic and a more complex attack system.

#ifndef Being_h
#define Being_h

#include <iostream>
using namespace std;

class Being
	{public:
		
		string name;
		int attackBonus;
		int range;
		float attackMod;
		int health;
		int healthMax;
		int mp;
		int mpMax;
		int arrows;
		
		void attack(Being& target);
		
		void rangedAttack(Being& target);
		
		void heal();
	};
#endif



Now this is a bit of a jump ahead, so Iíll explain what each of these variables mean in the implementation. The first part is the attack method, which will definitely be the most complicated.

#include "Being.h"

void Being::attack(Being& target)
{
	int damage = (int)(attackMod*(  ( (rand()%100)*(range+1) /100)  +attackBonus));
	target.health -= damage;
	
	cout << name << " attacks " << target.name << " doing " << damage << " damage!" << endl;
	cout << target.name << "'s health: " << target.health << endl;
}



The money is all in the one line with a bunch of math in it. We are attacking a Being instead of a specific class like Character or Monster, so this will enable Monsters to attack each other without having to write another attack method. This line will combine all our combat variables, attackMod, range, and attackBonus, to figure out how much damage the Being inflicts. Letís break this down.

(rand()%100) //get random number from 0-99

(rand()%100) *(range+1) //multiply that number by range+1

((rand()%100) *(range+1)) /100 //divide that number by 100 

int damage = (int)(attackMod*(  ( (rand()%100)*(range+1) /100) //multiply by attackMod, parse to int, then assign the value to the damage variable

[code]

Then we subtract the damage from the other Beingís health. The rest is basically output of variables about the whom attacked whom, how much damage was done, and how much health the target now has left. The beauty of this is that it allows us to easily tell how much damage a Being can do. Their damage will be between attackBonus and attackBonus+range. So if a Monster had an AB of 3 and a range of 2, it would do 2-5 damage. The attackMod method will be used later for casting spells that either double or halve a Beingís attack power.

We will keep the rangedAttack method simple, instead of having attackBonus and range variables for the ranged attack as well, we will just use fixed variables. (later, you can implement the attack system for ranged attack if you like. I didnít include it in the tutorial because it would just be a repetition of code and add more variables to keep track of.) 

[code]
void Being::rangedAttack(Being& target)
{
	if (arrows>0)
	{
		arrows--;
		int damage=rand()%8;
		if (damage==0)
			cout << name << ď misses ď << target.name;

		else
		{
			if (damage==7)
				damage=15; 
			
			damage*= attackMod;
			target.health -= damage;
			cout << name << " shoots " << target.name << " doing " << damage << " damage!" << endl;
			cout<< target.name << "'s health: " << target.health << endl;
		}
	}
	else
		cout << name << " is out of arrows!" << endl;
}



If we still have arrows, we decrement the arrow count and assign damage a random value from 0-7. If that value is zero, the target was missed and we tell the user. Otherwise, if the damage was 7, the highest possible value, we consider that a critical hit and up damage to 15. We multiply damage by attackMod, subtract from the targetís health, and perform some output about what happened.

And last we throw in a heal method, which just assigns the health and mp value to their maximums. We will use this with spells and stuff later.

void Being::heal()
{
	health=healthMax;
	mp=mpMax;
}






Now that weíve done all this work in Being, it greatly simplifies our Character and Monster classes, as now all they need is just constructors, and maybe a method to display things about them.

So all our Monster.h has is a constructor:

#ifndef Monster_h
#define Monster_h

#include "Being.h"

#include <iostream>
using namespace std;

class Monster : public Being
	{
	public:
		
		Monster(string, int, int, int);
	};
#endif



We couldíve included <iostream> and namespace std only in the Being class, as it is included in all the other classes, but it doesnít hurt to put it in all the classes, and it can save some trouble in the testing process.

Monster.cpp
Monster::Monster(string newname, int newHealth, int newAttackBonus, int NewRange) 
{
	name=newname;
	health=newHealth;
	attackBonus=newAttackBonus;
	range=NewRange;
	attackMod=1;
	arrows=10;
}	



The constructor just sets the instance variables to its arguments. We donít actually need the arrows variable as we wonít implement Monster ranged attacks in this tutorial. (but itís there anyway! :D ) We also leave out the Monsterís mp stuff.

Character.h is very similar, with just one other method:

#ifndef Character_h
#define Character_h

#include "Being.h"

#include <iostream>
using namespace std;

class Character : public Being
	{
	public:
		
		Character(string, int, int, int, int, int);
		
		void display();	
	};
#endif



The character constructor takes a string that will be the Characterís name, as well as the other variables are going to be the Characterís stats. We will allow the user to pick some of their Characterís statistics at the beginning of the game and use those to create the Character.

#include "Character.h"

Character::Character(string newname, int newAttackBonus, int newArrows, int newMp, int newHealth, int newRange)
{
	name=newname;
	health=healthMax=newHealth;
	mp=mpMax=newMp;
	arrows=newArrows;
	attackMod=1
	attackBonus=newAttackBonus;
	range=newRange;
}

void Character::display()
{
	cout << name << "  health: " << health << "  arrows: " << arrows << endl;
}



The Character constructor has one more int in it for the mp values.



Our Combat headers and .cpp files remain the same as before, except we add a health check to the combatChoice function (which I forgot in the last tutorial :) ).

#ifndef Combat_h
#define Combat_h

#include <iostream>
using namespace std;

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

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



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

Combat::Combat(Monster& newM) : M(newM)
{
	
}


void Combat::combatChoice(Character& C)
{
	if (C.health>0)
	{
		C.display();
		
		cout << "What do you do? 1 attack, 2 fire arrow" << endl;
		short choice;
		cin >> choice;
		switch (choice)
		{
			case 1:
				C.attack(M);
				break;
				
			case 2:
				C.rangedAttack(M);
				break;
		}
	}
	else
		cout << C.name << " is dead!" << endl;
}


void Combat::combat1(Character& C)
{
	
	while  (M.health>0 && C.health>0 )
	{
		M.attack(C);
		
		combatChoice(C);
	}
	if (M.health<0)
		cout << "Congratulations! You killed the monster!" << endl;
	if (C.health<0)
		cout << "YOU HAVE DIED! GAME OVER" << endl;;
	
}






So now onto our last file and addition in main. (Since both Monster and Character inherit from Being now, they donít specify each other as the target of their attack method and specify Being instead. So we donít need our little main.h hack this time around.)

The only addition we make to main is a function chooseCharacterStats which allows the users to pick the stats of their Character. (you never wouldíve guess, would you?)


#include <iostream>
using namespace std;

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

Character chooseCharacterStats()
{
	string n;
	int range=1, attackBonus=2, arrows=5, health=100, mp=10, choice;
	
	cout << "What is your name?" << endl;
	cin >> n;
	
	cout << "Your current stats are, attackBonus: " << attackBonus << " arrows: " << arrows << " mp: "<< mp;
	cout << " health: " << health << " range: " << range << endl;
	cout << "you have 5 tokens which you can spend on increasing your Character's statistics. You can"
	<< "spend 1 for 1 attack-bonus, 3 arrows, 4 mp, or 10 health. You can also spend 2 to get 3 range" << endl;
	cout << "Choose 1 for attack bonus, 2 for arrows, 3 for mp, 4 for health, and 5 for range." << endl;
	
	for (int tokens=5; tokens>0; tokens--)
	{
		cout << "tokens: " << tokens << endl;
		cin >> choice;
		switch (choice)
		{
			case 1:
				attackBonus++;
				cout << "attackBonus: " << attackBonus << endl;
				break;
			case 2:
				arrows+=3;
				cout << "arrows: " << arrows << endl;
				break;
			case 3:
				mp+=4;
				cout << "mp: " << mp << endl;
				break;
			case 4:
				health+=10;
				cout << "health: " << health << endl;
				break;
			case 5:
				if (tokens>2)
				{
					tokens--;
					range+=3;
					cout << "range: " << range << endl;
				}
				else 
				{
					cout << "Not enough tokens!" << endl;
					tokens++;
				}
				break;
			default:
				cout << "Please choose a number from 1 to 5" << endl;
				break;
		}
	}// end for loop
	cout << "Congratulations, you may now set forth into the world, young adventurer." << endl;
	return Character(n,attackBonus,arrows,mp,health, range);
}
					
				
Monster goblin("goblin",50,2,1);


int main (int argc, char * const argv[]) {
	
	Character C=chooseCharacterStats();
	
	Combat combat(goblin);
	combat.combat1(C);
				
	return 0;
}




This looks complicated, but it really is just repetitive code to do simple input output. In our chooseCharacterStats function, we first declare a string n to hold the Characterís name, the normal stats, and a choice variable to hold user input. We get the userís desired name, then output the stat-selection process.

We enter a loop which continues until tokens becomes 0. We will use similar input and switch statements for many menus in our game. The token count is shown, and we act on the userís input. The first 4 cases simply increment the chosen variable and display the result. case 5 checks to see if the user has 2 tokens, and if they do it increments range and decrements tokens. Since the loop decrements tokens by one as well, we will effective have reduced tokens by 2 by -- ing in the switch as well. If the user doesnít have enough tokens, we tell them and ++ their token count, to level it out when the loop -- Ďs it. We make a Character out of their chosen stats, and return it. The other little bit of main is the same as last time.

You might want to comment out this feature when testing the executable a lot, because it gets annoying when you have to pick the stats every time you start it. :)



So thatís all I have for you today, I hope you enjoyed and found the tutorial helpful! Any and all comments and suggestions are welcome, so please post them! :D :D :D

Is This A Good Question/Topic? 1
  • +

Replies To: combat game in C++ part II

#2 Martyr2  Icon User is offline

  • Programming Theoretician
  • member icon

Reputation: 4334
  • View blog
  • Posts: 12,128
  • Joined: 18-April 07

Posted 02 May 2009 - 11:08 AM

I think this tutorial has some formatting issues. Perhaps you should either correct it or ask for a moderator to correct it. I noticed that some of your text has made it into your code windows (probably because you forgot closing code tags in some places).

With all that aside, you might want to try and adopt some basic commenting in the code in addition to your summary of what the code is doing afterward. That way the reader can follow along. If you want to know what I mean, look at any of my posts where I post program code and you will see that I usually put in comments as I step through the code along with an explanation afterward. Just a tip that may help you have more readable and easy to follow tutorials.

In many places I notice you use references. Not quite sure how to use pointers yet? I know pointers can be scary at times, but do at least try to use them from time to time, you will find they can make things nicer.

Lastly, just a general design thought, have you considered making your combat system a class with a static method called "combat" and take in two being parameters? The reason I suggest this is instead of something like...

Combat combat(goblin);
combat.combat1(C);



Which doesn't make much sense, you could instead do something like...

Combat::Combat1(goblin, hero);



Here we have a class that can take in two beings and handle all the damage delt to one another. After it is called, the goblin or hero could be dead, both be injured, one be injured etc. Just like a real fight.

Just a thought you could play around with.

Clean up a few parts of this tutorial and I think it will be an outstanding second part to your overall tutorial. Look forward to seeing more. :)
Was This Post Helpful? 0
  • +
  • -

#3 mc_teo  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 3
  • Joined: 21-May 09

Posted 21 May 2009 - 02:34 PM

I'm having trouble compiling this code, or your last tutorial.

Most of my errors are related to syntax, etc.

So, can you attach the full source, so far?
Was This Post Helpful? 0
  • +
  • -

#4 GreenBrainstormeR  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 4
  • Joined: 22-September 09

Posted 23 September 2009 - 07:07 AM

Hey Crazy. This is a really interesting project. Im new to coding and am looking forward to learn more. I think this tutorial will be awesome a little bit along the road for me once i get a bit better at coding. I really hope you continue to refine your game and code and most importantly, keep posting your tutorials so we (or atleast I) can keep following along. Keep up the good work.

Sincerely: Your new stalking fan!!

This post has been edited by GreenBrainstormeR: 23 September 2009 - 07:08 AM

Was This Post Helpful? 0
  • +
  • -

#5 JDKeller  Icon User is offline

  • New D.I.C Head

Reputation: 1
  • View blog
  • Posts: 17
  • Joined: 13-May 10

Posted 13 May 2010 - 08:50 AM

Can you compile this into a .exe so we can view the final product? I think it would be neat to try.

Cheers,
Joshua D. Keller
Was This Post Helpful? 0
  • +
  • -

#6 Braber01  Icon User is offline

  • D.I.C Head

Reputation: 5
  • View blog
  • Posts: 247
  • Joined: 29-November 08

Posted 17 May 2010 - 03:24 PM

I've noticed mp May I Request implmenting Somthing like FFX-2's Dresssphere System? so my "Guy" Can easily become a healer to heal him self, etc. you probally know what i'm talking about
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1