Combat not working correctly in text RPG

Hello, I am working on a small text-RPG. The problem I am currently having is
when I encounter an enemy I attack once and then the combat ends. It is not enough damage to kill the enemy, I don't get any xp, and the enemy doesn't attack. It just goes back to the selection menu. Here is all my code.

Game.cpp(file with main)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include "Player.h"
#include "Map.h"

using namespace std;

int main()
{
	//Seed random number generator
	srand(time(0));

	//Instantiate a game map
	Map gameMap;

	//Instantiate the main player
	Player mainPlayer;

	//Go to create a class for main player
	mainPlayer.createClass();

	//Begin Adventure
	bool done = false;
	while (done == false)
	{
		//Each loop cycle output player position and a selection menu
		gameMap.printPlayerPos();

		int selection = 0;
		cout << "1)Move 2)Rest 3)View Stats 4) Quit : ";
		cin >> selection;

		Monster* monster = 0;
		
		switch (selection)
		{
		case 1:
			//Move the Player
			gameMap.movePlayer();

			//Check for random encounter, a null pointer is returned
			//if no monsters are encountered
			monster = gameMap.checkRandomEncounter();

			//monster not null, run combat
			if (monster != 0)
			{
				//Loop until a break statement
				while (true)
				{
					//Display hitpoints
					mainPlayer.displayHitPoints();
					monster->displayHitPoints();
					cout << endl;

					//Players turn to attack first
					bool runAway = mainPlayer.attack(*monster);

					if (runAway)
						break;

					if (monster->isDead())
					{
						mainPlayer.victory(monster->getXPReward());
						mainPlayer.levelUp();
						break;
					}

					//Monsters turn to attack
					monster->attack(mainPlayer);

					if (mainPlayer.isDead())
					{
						mainPlayer.gameover();
						done = true;
						break;
					}
				}

				//The pointer to a monster returned from checkRandomEncounter
				//was allocated with new so it must be deleted
				delete monster;
				monster = 0;
			}

			break;

		case 2:
			mainPlayer.rest();
			break;

		case 3:
			mainPlayer.viewStats();
			break;

		case 4:
			done = true;
			break;
		}//End switch statement
	}//End while loop
}//End main function 



Weapon.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//Weapon.h
#ifndef WEAPON_H
#define WEAPON_H

#include <string>
#include "Range.h"

struct Weapon
{
	std::string mName;
	Range       mDamageRange;
};

#endif//WEAPON_H 


Monster.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//Monster.h
#ifndef MONSTER_H
#define MONSTER_H

#include <string>
#include "Weapon.h"

class Player;

class Monster
{
public:
	Monster(const std::string& name, int hp, int acc, int xpReward,
		int armor, const std::string& weaponName, int lowDamage,
		int highDamage);

	bool isDead();

	int         getXPReward();
	std::string getName();
	int         getArmor();

	void attack(Player& player);
	void takeDamage(int damage);
	void displayHitPoints();

private:
	std::string mName;
	int			mHitPoints;
	int			mAccuracy;
	int			mExpReward;
	int			mArmor;
	Weapon		mWeapon;

};

#endif//MONSTER_H 


Monster.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <iostream>
#include <string>
#include "Monster.h"
#include "Random.h"
#include "Player.h"

Monster::Monster(const std::string& name, int hp, int acc,
	int xpReward, int armor, const std::string& weaponName,
	int lowDamage, int highDamage)
{
	mName = name;
	mHitPoints = hp;
	mAccuracy = acc;
	mExpReward = xpReward;
	mArmor = armor;
	mWeapon.mName = weaponName;
	mWeapon.mDamageRange.mLow = lowDamage;
	mWeapon.mDamageRange.mHigh = highDamage;
}

//Returns true if monster is dead
bool Monster::isDead()
{
	return mHitPoints <= 0;
}

//Return xp amount for defeating monster
int Monster::getXPReward()
{
	return mExpReward;
}

//Returns monsters name
std::string Monster::getName()
{
	return mName;
}

//Returns copy of monsters armor
int Monster::getArmor()
{
	return mArmor;
}

/*This method is the only nontrivial method of Monster. This method executes the code which has a
 *monster attack a game Player. Because a monster attacks a Player, we
 *pass a reference to a Player into the function. We pass a reference for efficiency; that is, just as we
 *do not want to copy an entire array into a parameter, we do not want to copy an entire Player object.
 *By passing a reference, we merely copy a reference variable (a 32-bit address).
 *The attack method is responsible for determining if the monster’s attack hits or misses the player. We
 *use the following criteria to determine whether a monster hits a player: If the monster’s accuracy is
 *greater than a random number in the range [0, 20] then the monster hits the player, else the monster
 *misses the player:
 */

void Monster::attack(Player& player)
{
	std::cout << "a " << mName << " attacks you! " 
		"with a " << mWeapon.mName << std::endl;

	if (Random(0, 20) < mAccuracy)
	{
		int damage = Random(mWeapon.mDamageRange);
		int totalDamage = damage - player.getArmor();

		if (totalDamage <= 0)
		{
			std::cout << "The Monster's attack failed "
				<< "to penetrate your armor!" << std::endl;
		}

		else
		{
			std::cout << "You are hit for " << totalDamage
				<< " Damage!" << std::endl;

			player.takeDamage(totalDamage);
		}
	}

	else
		std::cout << "The " << mName << " missed!" << std::endl;

	std::cout << std::endl;

}

//This method is called when a player hits the monster and determines how much hitpoints the monster loses
void Monster::takeDamage(int damage)
{
	mHitPoints -= damage;
}

/*
 *This method outputs the monster’s hit points to the console window. This is used in the game during
 *battles so that the player can see how many hit points the monster has remaining.
 */

void Monster::displayHitPoints()
{
	std::cout << mName << "'s hitpoints = " << mHitPoints << std::endl;
}


Player.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//Player.h
#ifndef PLAYER_H
#define PLAYER_H

#include <string>
#include "Weapon.h"
#include "Monster.h"

class Player
{
public:
	//Constructor
	Player();

	//Methods
	bool isDead();

	std::string getName();
	int			getArmor();

	void takeDamage(int damage);

	void createClass();
	bool attack(Monster& monster);
	void levelUp();
	void rest();
	void viewStats();
	void victory(int xp);
	void gameover();
	void displayHitPoints();

private:
	//data members
	std::string mName;
	std::string mClassName;
	int			mAccuracy;
	int			mHitPoints;
	int			mMaxHitPoints;
	int			mExpPoints;
	int			mNextLevelExp;
	int			mLevel;
	int			mArmor;
	Weapon		mWeapon;
};

#endif//PLAYER_H 

Player.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#include <iostream>
#include <string>
#include "Player.h"
#include "Random.h"
#include "Monster.h"

using namespace std;

Player::Player()
{
	mName = "Default";
	mClassName = "Default";
	mAccuracy = 0;
	mHitPoints = 0;
	mMaxHitPoints = 0;
	mExpPoints = 0;
	mNextLevelExp = 0;
	mLevel = 0;
	mArmor = 0;
	mWeapon.mName = "Default Weapon Name";
	mWeapon.mDamageRange.mLow = 0;
	mWeapon.mDamageRange.mHigh = 0;
}

bool Player::isDead()
{
	return mHitPoints <= 0;
}

int Player::getArmor()
{
	return mArmor;
}

void Player::takeDamage(int damage)
{
	mHitPoints -= damage;
}

void Player::createClass()
{
	cout << "=====================================" << endl;
	cout << "=====================================" << endl;
	cout << "===Create your character and class===" << endl;
	cout << "=====================================" << endl;
	cout << "=====================================" << endl;

	//Input Characters name
	cout << "Enter your character's name: ";
	getline(cin, mName);

	//Class selection
	cout << "Please select a class number..." << endl;
	cout << "1)Fighter 2)Wizard 3)Cleric 4)Thief : ";
	int characterNum = 0;
	cin >> characterNum;

	switch (characterNum)
	{
	case 1: //Fighter
		mClassName = "Fighter";
		mAccuracy = 10;
		mHitPoints = 20;
		mMaxHitPoints = 20;
		mExpPoints = 0;
		mNextLevelExp = 1000;
		mLevel = 1;
		mArmor = 6;
		mWeapon.mName = "Two-Handed Sword";
		mWeapon.mDamageRange.mLow = 1;
		mWeapon.mDamageRange.mHigh = 8;
		break;

	case 2: //Wizard
		mClassName = "Wizard";
		mAccuracy = 13;
		mHitPoints = 10;
		mMaxHitPoints = 10;
		mExpPoints = 0;
		mNextLevelExp = 1000;
		mLevel = 1;
		mArmor = 2;
		mWeapon.mName = "Staff";
		mWeapon.mDamageRange.mLow = 1;
		mWeapon.mDamageRange.mHigh = 4;
		break;

	case 3: //Cleric
		mClassName = "Cleric";
		mAccuracy = 8;
		mHitPoints = 15;
		mMaxHitPoints = 15;
		mExpPoints = 0;
		mNextLevelExp = 1000;
		mLevel = 1;
		mArmor = 3;
		mWeapon.mName = "Flail";
		mWeapon.mDamageRange.mLow = 1;
		mWeapon.mDamageRange.mHigh = 6;
		break;

	case 4: //Thief
		mClassName = "Thief";
		mAccuracy = 15;
		mHitPoints = 13;
		mMaxHitPoints = 13;
		mExpPoints = 0;
		mNextLevelExp = 1000;
		mLevel = 1;
		mArmor = 4;
		mWeapon.mName = "Dagger";
		mWeapon.mDamageRange.mLow = 4;
		mWeapon.mDamageRange.mHigh = 8;
		break;
	}
}
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
bool Player::attack(Monster& monster)
{
	int selection = 0;
	cout << "1)Attack 2)Run : ";
	cin >> selection;
	cout << endl;

	switch (selection)
	{
	case 1:
		cout << "You attack an " << monster.getName() <<
			" with a " << mWeapon.mName << endl;

		if (Random(0, 20) < mAccuracy)
		{
			int damage = Random(mWeapon.mDamageRange);
			int totalDamage = damage - monster.getArmor();

			if (totalDamage <= 0)
				cout << "Your attack failed to penetrate the monster's armor!" << endl;

			else
			{
				cout << "You attack for " << totalDamage << " Damage!" << endl;

				//subtract from monsters hitpoints
				monster.takeDamage(totalDamage);
			}
		}

		else
			cout << "You miss!" << endl;

		cout << endl;
		break;

	case 2:
		//25% chance of running
		int roll = Random(1, 4);

		if (roll == 1)
		{
			cout << "You run away!" << endl;
			return true; //Return out of function
		}

		else
		{
			cout << "You could not escape!" << endl;
			break;
		}
	}
}

/*This method tests whether or not the player has acquired enough experience points to level up. It is
 *called after every battle. If the player does have enough experience points then some of the player’s
 *statistics such as hit points and accuracy are randomly increased.
 */
void Player::levelUp()
{
	if (mExpPoints >= mNextLevelExp)
	{
		cout << "You have gained a Level!" << endl;

		//Increment level
		++mLevel;

		//Set exp points required for next level
		mNextLevelExp = mLevel * mLevel * 1000;

		//Increase stats randomly
		mAccuracy += Random(1, 3);
		mMaxHitPoints += Random(2, 6);
		mArmor += Random(1, 2);

		//Give the player full hitpoints when they level up
		mHitPoints = mMaxHitPoints;
	}
}

/*This method is called when the player chooses to rest. Currently, resting simply increases the player’s
 *hit points to the maximum.Later you may wish to add the possibility of random enemy encounters
 *during resting or other events.
 */
void Player::rest()
{
	cout << "Resting..." << endl;
	mHitPoints = mMaxHitPoints;
}

/*
 *Often, a player in an RPG likes to view his player’s statistics, so that he knows what items are in his
 *inventory, how many hit points he has, or how many experience points are required to reach the next
 *level. We output this type of information with the viewStats method:
 */
void Player::viewStats()
{
	cout << "======================" << endl;
	cout << "=====PLAYER STATS=====" << endl;
	cout << "======================" << endl;
	cout << endl;

	cout << "Name = " << mName << endl;
	cout << "Class = " << mClassName << endl;
	cout << "Accuracy = " << mAccuracy << endl;
	cout << "Hitpoints = " << mHitPoints << endl;
	cout << "Maximum hitpoints = " << mMaxHitPoints << endl;
	cout << "XP = " << mExpPoints << endl;
	cout << "XP for next level = " << mNextLevelExp << endl;
	cout << "Level = " << mLevel << endl;
	cout << "Armor = " << mArmor << endl;
	cout << "Weapon = " << mWeapon.mName << endl;
	cout << "Weapon Damage = " << mWeapon.mDamageRange.mLow << " - " << mWeapon.mDamageRange.mHigh << endl;

	cout << endl;
	cout << "=========================" << endl;
	cout << "===END OF PLAYER STATS===" << endl;
	cout << "=========================" << endl;
	cout << endl;
}

/*
 *This method is called after a player is victorious in battle. It displays a victory message and gives the
 *player an experience point award.
 */
void Player::victory(int xp)
{
	cout << "You won the battle!" << endl;
	cout << "You win " << xp << " experience points!" << endl << endl;

	mExpPoints += xp;
}

//This method is called if the player dies in battle. It displays a “game over” string and asks the user to
//press ‘q’ to quit.
void Player::gameover()
{
	cout << "You died in battle...." << endl;
	cout << endl;
	cout << "=========================" << endl;
	cout << "========GAME OVER========" << endl;
	cout << "=========================" << endl;
	cout << "Press 'q' to quit : ";
	char q = 'q';
	cin >> q;
	cout << endl;
}

//Display player's hitpoints
void Player::displayHitPoints()
{
	cout << mName << "'s  hitpoints = " << mHitPoints << endl;
}


Map.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Map.h
#ifndef MAP_H
#define MAP_H

#include <string>
#include "Monster.h"
#include "Weapon.h"

class Map
{
public:

	//Constructor
	Map();

	//Methods
	int getPlayerXPos();
	int getPlayerYPos();
	void movePlayer();
	Monster* checkRandomEncounter();
	void printPlayerPos();

private:
	//data members
	int mPlayerXPos;
	int mPlayerYPos;
};

#endif//MAP_H 


Map.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <iostream>
#include <string>
#include "Map.h"
#include "Random.h"

using namespace std;

Map::Map()
{
	//Player starts at origin
	mPlayerXPos = 0;
	mPlayerYPos = 0;
}

int Map::getPlayerXPos()
{
	return mPlayerXPos;
}

int Map::getPlayerYPos()
{
	return mPlayerYPos;
}

void Map::movePlayer()
{
	int selection = 0;
	cout << "1)North 2)East 3)South 4)West : ";
	cin >> selection;

	//Update coordinates based on selection
	switch (selection)
	{
	case 1://North
		++mPlayerYPos;
		break;

	case 2://East
		++mPlayerXPos;
		break;

	case 3://South
		--mPlayerYPos;
		break;

	default://West
		--mPlayerXPos;
		break;
	}

	cout << endl;
}

/*
 *This function is the key function of the Map class. It generates a random number in the range [0, 20].
 *Depending upon which sub-range in which the generated number falls, a different encounter takes place:
 * Range [0, 5] – The player encounters no enemy.
 * Range [6, 10] – The player encounters an Orc.
 * Range [11, 15] – The player encounters a Goblin.
 * Range [15, 19] – The player encounters an Ogre.
 * Range [20] – The player encounters an Orc Lord.
 *The bulk of this method code consists of testing in which range the random number falls and then
 *creating the appropriate kind of monster.
 */
Monster* Map::checkRandomEncounter()
{
	int roll = Random(0, 20);

	Monster* monster = 0;

	if (roll <= 5)
	{
		//No encounter return a null pointer
		return 0;
	}

	else if (roll >= 6 && roll <= 10)
	{
		monster = new Monster("Orc", 10, 8, 200, 2, "War Axe", 2, 7);
		cout << "You encountered an Orc!" << endl;
		cout << "Prepare for battle!" << endl;
		cout << endl;
	}

	else if (roll >= 11 && roll <= 15)
	{
		monster = new Monster("Goblin", 12, 8, 350, 3, "Twin Axes", 3, 7);
		cout << "You encountered a Goblin!" << endl;
		cout << "Prepare for battle!" << endl;
		cout << endl;
	}

	else if (roll >= 16 && roll <= 19)
	{
		monster = new Monster("Ogre", 13, 10, 450, 4, "Battle Axe", 3, 8);
		cout << "You encountered an Ogre!" << endl;
		cout << "Prepare for battle!" << endl;
		cout << endl;
	}

	else if (roll == 20)
	{
		monster = new Monster("Orc Warlord", 25, 15, 2000, 7, "Dark Axe of Brutel Dismemberment", 6, 10);
		cout << "You encountered an Orc Warlord!" << endl;
		cout << "You better run away!" << endl;
		cout << endl;
	}

	return monster;
}

void Map::printPlayerPos()
{
	cout << "Player position = (" << mPlayerXPos << ", " << mPlayerYPos << ")" << endl << endl;
}



Random.h
1
2
3
4
5
6
7
8
9
10
11
//Random.h
#ifndef RANDOM_H
#define RANDOM_H

#include "Range.h"

int Random(Range r);

int Random(int a, int b);

#endif//RANGE_H 


Random.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
#include "Random.h"

using namespace std;

//Returns a random number in r
int Random(Range r)
{
	return r.mLow + rand() % ((r.mHigh + 1) - r.mLow);
}

//Returns a random number in [low high]
int Random(int low, int high)
{
	return low + rand() % ((high + 1) - low);
}


Range.h
1
2
3
4
5
6
7
8
9
10
11
12
//Range.h
#ifndef RANGE_H
#define RANGE_H

//Defines a range [mlow, mhigh]
struct Range
{
	int mLow;
	int mHigh;
};

#endif//RANGE_H 


I had to separate Player.cpp because I couldn't get it to fit in one post.
Thanks
Last edited on
In Player.cpp in bool Player::attack(Monster& monster) try adding return false at the end.

Also in the combat loop you could print something inside each if statement to see which one is breaking the loop

In Player.cpp in bool Player::attack(Monster& monster) try adding return false at the end.


That worked. Thanks a lot.

Although now I'm wondering how that stopped the loop.
Topic archived. No new replies allowed.