• (2 Pages)
  • +
  • 1
  • 2

Make a text combat game in C++ plays like pokémon/final fantasy where you fight a monster

#1 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

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

Posted 31 March 2009 - 02:29 PM

Greetings Earthlies, this is crazyjugglerdrummer with the first in a hopefully long series of game tutorials. As mentioned in the title, this game plays something like pokémon, final fantasy, and many others I’m sure, making a combat system. This is all text based, but could later be adapted to run with fancier visual input/output. This is the beating heart of the program, the eyes, ears, and skin can be worked out later.

This game was my first real C++ project and I’ve been at it for a while. The first rewrite added object oriented techniques and the second rewrite fixed most of the object mistakes I’d make previously. The rewrites were horribly annoying as I had to completely reorganize the code and build it up almost from scratch again each time. I’m going to try to design these tutorials with as little of that as possible and a logical learning progression, so once code gets written, it shouldn’t have to be reorganized too much.

Every feature I wanted to add to the game required me to look in a new chapter of my C++ book, so this tutorial will compose of the apex of those efforts. I gradually added functions, then objects, then inheritance one after another. So to spare you the rewrites we are going to start with object-oriented code from the very beginning. These tutorials will use loops, functions, objects, inheritance, and vectors thoroughly (though this first tutorial will be simpler and not use all of them). Don’t let any of those scare you off! If you don’t understand any of them at all, look through some of the other tutorials here in this forum and come back to this when you have. This program is a good example of those and how to use them.


So, without further adieu, on to the actual program. We are going to make a combat system in which you fight a monster. Other tutorials will gradually add on to this, stuffing more features into the game. We need to have classes for monsters, characters, and the combat. These classes overlap profusely, so proper header files and implementations are essential. We are going to start with the Monster class as its the easiest.

So lets think of how to make a monster: What are the its properties? What does it do? How does it interact with other objects in the game? First we’ll need some variables for our monster to store our monster’s health, name, and damage. Name will be a string, and the other two will be integers. We need a method for the monster to attack a character, and a constructor that will set the instance variables. So know we have:
//monster.h
#ifndef Monster_h //header guards
#define Monster_h

#include <iostream>
using namespace std;

#include "Character.h" //we’ll make this in a second
#include “main.h”	  //to be explained later
class Monster
	{
	public:
		string name;
		short health;
		short damage;

		Monster(string newName, int newHealth, int newDamage);
	
		void attack(Character&);
	};
#endif



Now we have a header file for our monster class with a constructor and an attack method.

Next we have to make the Character.h that we mentioned. This is going to look pretty much like the Monster class, except with a few extra features for the character.
//Character.h
#ifndef Character_h
#define Character_h

#include "Monster.h" //we just made this
#include "main.h"	//I’ll get to this (I promise)

#include <iostream>
using namespace std;

class Character
	{
	public:
		string name;
		short health;
		short damage;
		short arrows;

		Character(string newName);

		void attack(Monster& target);
		
		void rangedAttack(Monster& target);
	};
#endif



The extra method rangedAttack and variable arrows are to allow our character to attack in multiple ways, making the game more interesting.

Now we’re going to make a combat class, which will primarily serve to house all of our combat related methods and members. We need to contain the monster the character is fighting, while passing the character as a parameter (we’re going to pass the Character around the functions a lot). We have a combat1 function to contain the cycle of the combat and a combatChoice to display the options the character has available to them.
//Combat.h
#ifndef Combat_h
#define Combat_h

#include <iostream>
using namespace std;

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

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



This class is going to be a little more complex than the others as it has a Monster address as an instance variable. We also have a constructor to implement which will initialize the Monster variable.


So now onto that main.h thing I mentioned. Since our Monster class includes the Character class and vice versa, one can’t be defined without the other. The compiler tries to define the first one, which can’t be defined because the first one hasn’t been defined. This leads to general confusion and a few “NOT DEFINED” errors. So we need to tell the compiler what they classes are so its header files can live happily ever after. So we make a simple header file, that will be implemented by the other header files.
#ifndef main_h
#define main_h

class Monster;
class Character;

#endif



Monster and Character are the only classes we really need here, as they are the only classes that include the file. The rest of the inclusions are normal.


So, now we can actually implement some stuff. Lets start with the Monster and its attack method. We need to subtract the monster’s damage from the Character’s health that we passed by reference, and display some information about what happened in the attack. We make a very simple constructor, that merely sets the instance variables to the arguments passed to it. So we have:
#include “Monster.h”

void Monster::attack(Character& target)
{
	target.health-=damage;
	cout << name << " attacks " << target.name << " doing " << damage << " damage!" << endl;
	cout << name << " health: " << health << endl;
}

Monster::Monster(string newname, int newHealth, int newDamage) 
{
	name=newname;
	health=newHealth;
	damage=newDamage;
}	



This should all be contained in a .cpp file, as it is the implementation for the monster.

Next, we create Character.cpp, with similar methods. We have an attack method, a similar ranged attack method, a display, and a constructor. The rangedAttack method will subtract its damage from the Monster’s health, then decrement the number of arrows the Character has. If the Character’s out of arrows, we display a message.
#include "Character.h"

void Character::attack(Monster& target)
{
	target.health-=damage;
	cout << name << " attacks the " << target.name << " doing " << damage << " damage!" << endl;
	cout << name << " health: " << health << endl;
}

void Character::rangedAttack(Monster& target)
{
	if (arrows == 0 )
		cout << name << " is out of arrows!" << endl;
	else
	{
		short rangedDamaged=3;
		target.health-=rangedDamaged;
		arrows--;
		cout << name << " shoots " << target.name << " doing " << rangedDamaged << " damage!" << endl;
	}
}

Character::Character(string newname)
{
	name=newname;
	health=100;
	damage=3;
	arrows=5;
}

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



Notice that all the display statements that involve variables have a space before the next strings that is displayed, so the line doesn’t just show up as one big word.

Now we can implement the Combat class, which serves as the container for our combat members and functions. The combat constructor will simply initialize the Character’s opponent. Combat1 is just a loop that repeatedly has the monster attack the character, then gives the Character the chance to choose what to do next, by means of the combatchoice function. CombatChoice just uses a menu switch with two different options. We loop as long as one of the members of the combat is alive, and when one “dies” we display what occurred.
#include "Combat.h"
#include "Monster.h"
#include "Character.h"

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

void Combat::combatChoice(Character& C)
{
	
	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;
	}
}


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




The Combat’s constructor may look a little weird, as it doesn’t have a body. All the constructor does is set the M instance variable to the address that gets passed to it. This is done in the style of an initializer list.

Last, but not least we have main, which is very simple as it makes a monster, constructs a combat from that monster, makes a character, and calls combat1 on that Character. (it doesn’t actually implement main.h)
#include <iostream> 
using namespace std;

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


Monster goblin("goblin",50,2);//declare Monster type outside of main


int main (int argc, char * const argv[]) {
	
	Character C("George"); //variable name C doesn’t matter as we pass by ref
	
	Combat combat(goblin);

	combat.combat1(C);
	
	cout << “thanks for playing!” << endl;

	return 0;
}



We don’t really need iostream or the Monster.h and Character.h as they are included elsewhere and passed on to main, but we’ll include them anyway as some will be used anyway and its easier to keep track of what is used where when they are included specifically.

You can create the combat outside of main if you want, but in future tutorials we’ll create the combat inside of main.

So there you have it, the basics of a simple combat game! I hope this you’ve enjoyed this tutorial and that is has been helpful! If it hasn’t (and you have constructive criticism to make) or any other comments, questions, concerns, please post them here. Thanks for reading!

Happy Coding! :)


Stay tuned for the next tutorial where we add random numbers to the combat system. Later, we will design an item system with weapons, spells, and healing items. We will add inheritance, inheriting both Character and Monster from a Being class. There will also be extensive uses of vectors, so brush up on those for the next part!

Is This A Good Question/Topic? 2
  • +

Replies To: Make a text combat game in C++

#2 asvati  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 29-April 09

Posted 29 April 2009 - 02:24 AM

sorry, but have you checked that this actually works? I typed it inn myself and got a bunch of errors, 20+ or so, after that I tryed to just copy and paste all of it, but still got 16 errors, by going through them I have narrowed them down to 3. One thing I noticed is that you havent included string? anyway, heres my errors:

combat.cpp(5) : error C2511: 'Combat::Combat(Monster &)' : overloaded member function not found in 'Combat'
combat.cpp(27) : fatal error C1004: unexpected end-of-file found
main.cpp(18) : error C2664: 'Combat::Combat(Character &)' : cannot convert parameter 1 from 'Monster' to 'Character &'

combat.cpp:
#include "Combat.h"
#include "Monster.h"
#include "Character.h"

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

void Combat::combatChoice(Character& C)
{
	
	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;
	}
}



main.cpp:
#include <iostream>
#include <string>
using namespace std;

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



Monster goblin("goblin",50,2);//declare Monster type outside of main


int main (int argc, char * const argv[]) {
	
	Character C("George"); //variable name C doesn’t matter as we pass by ref
	
	Combat combat(goblin);

	combat.combat1(C);
	
	cout << "thanks for playing!" << endl;

	return 0;
}

Was This Post Helpful? 0
  • +
  • -

#3 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

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

Posted 12 May 2009 - 05:03 PM

Its good to know people are watching :D . It compiled fine under Xcode GCC on mac os x, which included string automatically I think. I probably should've tested some other compilers... The line in Combat.h is a blatant typo on my part, it should read
Combat(Monster& C);



Did including <string> fix your other thirteen errors?

Thanks for keeping me honest ;)
Was This Post Helpful? 0
  • +
  • -

#4 scooterfort  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 26
  • Joined: 24-May 09

Posted 25 May 2009 - 11:58 AM

Nice system bro I like it. Thats a LOT of code tho ahahah
Was This Post Helpful? 0
  • +
  • -

#5 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

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

Posted 10 June 2009 - 08:22 AM

Part 2 Link
Was This Post Helpful? 0
  • +
  • -

#6 macmc2  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 5
  • Joined: 25-June 09

Posted 25 June 2009 - 03:46 PM

so wait im suppossed to put all those codes in my compiler right
i think
sorry im kind of an idiot
Was This Post Helpful? 0
  • +
  • -

#7 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

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

Posted 26 June 2009 - 12:24 PM

You're not an idiot any more than we all were/are starting out. Yes: you need to make separate class files for each one, naming them the appropriate thing (monster.h and monster.cpp, combat.h and combat.cpp). The code must go into the appropriate file for it.

I definitely admit that I could've made this task a lot easier by posting all the code and what file it goes into at the end of the tutorial. I will make this and several other improvements as soon as we can edit our own tutorials. (feature coming soon, see DIC user suggestions )

I will say that this is probably not the greatest project for anyone to try when just starting out. Look at some of the other tutorials on the web and on DIC on C++. Try making some of your own simpler programs, like black jack and a date converter, then come back to this one. :D

This post has been edited by crazyjugglerdrummer: 26 June 2009 - 12:36 PM

Was This Post Helpful? 0
  • +
  • -

#8 Jaakuuta  Icon User is offline

  • D.I.C Head
  • member icon

Reputation: 1
  • View blog
  • Posts: 163
  • Joined: 02-July 09

Posted 18 July 2009 - 01:03 AM

Hello... I tried both sections of your tutorial...
I like them, but I have a few comments.
First, they need a bit of editing, the code didn't work outright, I had to do a bit of debugging before it compiled correctly.
Notably, a few things were left out of your tutorial that had to be added.
Also, even though this is only a beginner's tutorial, you didn't use any kind of data hiding to help prevent unwanted side-effects...
Aside from a few other little inconsistencies (using newname on one variable but newAttackBonus on another, and using health and mp, rather than hp and mp) This was actually pretty good.
Oh, and one more thing, a few of the include files as was mentioned before were not specified. Although my compiler auto-imported for strings I still needed to include stdlib.h to use the rand() function.
Was This Post Helpful? 0
  • +
  • -

#9 crazyjugglerdrummer  Icon User is offline

  • GAME OVER. NERD WINS.
  • member icon

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

Posted 18 July 2009 - 08:59 AM

Yes, I agree with you. This was one of my first programming tutorials and it needs some improvement. However, Dream in code does not offer us the feature of editing our posts after a certain amount of time. Hopefully we'll get this feature soon!

This post has been edited by crazyjugglerdrummer: 24 July 2009 - 06:38 PM

Was This Post Helpful? 0
  • +
  • -

#10 Guest_xSmallDeadGuyx*


Reputation:

Posted 06 July 2010 - 04:23 AM

Sorry, I'm a complete n00b to c++ programming and ran through your tutorial (after doing a few basic tutorials) and ended up with loads of errors when it compiled. Exactly how am I supposed to fix them?
Was This Post Helpful? 0

#11 Guest_VASH*


Reputation:

Posted 05 December 2010 - 05:59 PM

Nice one! although it's sort of the basic but nevertheless quite important, finaly someone who uses classes instead of structs... keep going man most guys that i know use structs and you used classes and you separated all of em to make a more organized display of code to whoever is going to change some parts in the code.
Was This Post Helpful? 0

#12 Hezekiah  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 207
  • View blog
  • Posts: 550
  • Joined: 12-July 09

Posted 01 January 2011 - 07:16 AM

I thought it might be helpful to upload the bug-free source while crazyjugglerdrummer can't edit his tutorial.

Attached File(s)


Was This Post Helpful? 0
  • +
  • -

#13 AbeLinclolnstein  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 13
  • Joined: 21-April 12

Posted 21 April 2012 - 07:51 AM

I was curious as to what the "#ifndef" meant? I was looking around and couldn't find a clear answer.
Was This Post Helpful? 0
  • +
  • -

#14 modi123_1  Icon User is offline

  • Suitor #2
  • member icon



Reputation: 8948
  • View blog
  • Posts: 33,544
  • Joined: 12-June 08

Posted 21 April 2012 - 08:01 AM

@abelincoln - read up here. (hint - Conditional inclusion).
Was This Post Helpful? 0
  • +
  • -

#15 Hezekiah  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 207
  • View blog
  • Posts: 550
  • Joined: 12-July 09

Posted 21 April 2012 - 09:02 AM

In this case, the #ifndef directive is being used to prevent the header from accidently being included multiple times. This is known as a header guard. Consider this example:
//a.h

class Test {
    //...
};


//b.h

#include "a.h"

Test test();


//main.cpp

#include "a.h"
#include "b.h"

int main() {
    Test t = test();
    return 0;
}

The class Test will be defined twice in main.cpp, because a.h is included once directly, and then again from inside b.h. To fix this we can add header guards:
//a.h

#ifndef A_H
#define A_H

class Test {
    //...
};

#endif


//b.h

#ifndef B_H
#define B_H

Test test();

#endif

The first time a.h is included, A_H is not defined, and the code between the #ifndef and #endif directives is compiled, which defines A_H. The next time you include the header, A_H is defined and the code up to #endif is not compiled.
Was This Post Helpful? 0
  • +
  • -

  • (2 Pages)
  • +
  • 1
  • 2