Build A Text Based Multiplayer RPG Part V

The fifth part of the tutorial on creating a text based multiplayer RPG game will see us creating the last part of the testing and game mechanics before the next section where we’ll launch the client and server code to actually test our game in action.

In this part of the tutorial we will complete the test and methods necessary to get the full functionality for the stand alone single player version. In the next part of the tutorial part 6, we will begin to develop both the client and server code that allows other users to connect and play our game over a local area network. So lets continue with the testing and move onto the player class. Go ahead and open up the “test.h” file and add line below.

1
void test_player_class();

Then open up the file “test.cpp” and add the methods for the player test class beginning at the line indicated.

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
void Test::test_player_class(void)
{
// Player constructor w/o initial room
Player no_room_player("hero");
assert(10 == no_room_player.mMaxHp);
assert(10 == no_room_player.mHp);
assert(1 == no_room_player.mStrength);
assert(0 == no_room_player.mDefence);
assert(NULL == no_room_player.mLocation);
assert("hero" == no_room_player.mName);
assert(NULL == no_room_player.mWeapon);
assert(NULL == no_room_player.mArmor);
// Player constructor w/room
Player room_player("dude", new Room("Home", "A big house"));
assert(NULL != room_player.mLocation);
// Player getters/setters
Player get_set("setter", new Room("Lake", "A clean lake"));
get_set.setDefence(1);
get_set.setHp(10);
get_set.setName("getter");
get_set.setStrength(2);
assert(1 == get_set.getDefence());
assert(10 == get_set.getHp());
assert("getter" == get_set.getName());
assert(2 == get_set.getStrength());
assert("Lake" == get_set.getLocation());
// Player equip test
// The Item is not found
// - There is no item within the room "There is no item to equip in this rooom"
// - The player misspelled the item name "To equip an item you must type its full name correctly"
Room no_item_room("Itemless", "There is no item here");
Player no_item_player("Hi");
no_item_player.mLocation = &no_item_room;
string response("There is no item to equip in this room");
assert(response == no_item_player.equip("Bad Item name"));
Room with_item("Item room", "Item filled room", new Item("dagger", Item::WEAPON, 10, 0, 0));
no_item_player.mLocation = &with_item;
response = "To equip an item you must type its full name correctly";
assert(response == no_item_player.equip("daggers"));
// Weapon is found
// - The weapon becomes unavailable
// - The players strength is set equal to the weapons power value
// - The response returned is "You equipped the Item.mName" Player item_player("Bye", &with_item);
response = "You equipped the dagger";
assert(response == item_player.equip("dagger"));
assert(false == with_item.mItem->mAvailiable);
assert(with_item.mItem->getPower() == item_player.getStrength());
// Armor is found
// - The armor becomes unavailable
// - The players vitality is set equal to the armor's defence // - The response returned is "You equipped the Item.mName"
// - The armor's health value is added to the players max hp
Item* steel_plate = new Item("steelplate", Item::ARMOR, 0, 10, 20);
with_item.setItem(steel_plate);
response = "You equipped the steelplate";
assert(response == item_player.equip("steelplate"));
assert(with_item.mItem->mAvailiable == false);
assert(steel_plate->getHealth() + item_player.getHp()
== item_player.mMaxHp);
assert(with_item.mItem->getVitality() == item_player.getDefence());
// Health is found
// - The health item becomes unavailable
// - The player's hp is fully restored
// - The response is "You were healed"
Item* herb = new Item("herbs", Item::HEALTH, 0, 0, 0);
with_item.setItem(herb);
response = "You were healed";
assert(response == item_player.equip("herbs"));
assert(with_item.mItem->mAvailiable == false);
assert(item_player.getHp() == item_player.mMaxHp);
// Player attack test
// a- There are no monsters within the Room then the method should //methodreturn "There are no monsters in this room to fight"
// b- The player mis-typed the monster name then
//name"The Monster.mName is not here"
// c- If damage was dealt to the monster then the message should say
//should"Monster.mName was dealt X damage" // d- If no damage was dealt to the monster then the message should say
//should"Monster.mName dodged the attack"
string response_a("There are no monsters in this room to fight");
string response_b("The roll is not here");
string response_c("troll was dealt 30 damage");
string response_d("supertroll dodged the attack");
Item* great_sword = new Item("greatsword", Item::WEAPON, 30, 0, 0);
Room* arena = new Room("arena", "A large crowd gathers", great_sword);
Monster* troll = new Monster("troll", 50, 0, 0);
arena->setItem(great_sword);
Player* attacker = new Player("Fighter", arena);
attacker->equip("greatsword");
assert(response_a == attacker->attack("troll"));
arena->addMonster(troll);
assert(response_b == attacker->attack("roll"));
assert(response_c == attacker->attack("troll"));
Monster* super_troll = new Monster("supertroll", 8, 0, 999);
arena->addMonster(super_troll);
assert(response_d == attacker->attack("supertroll"));
// Player move test
// - If the player moves in an invalid direction then //direction"You traveled nowhere" should be returned
// - If the direction is valid but no room is connected
//is"There is nothing in that direction"
// - If the direction is valid and a room exist
//room"You traveled directionname\nmLocation->getDescription()"
Room* moving_room = new Room("Moving room", "You can move from here");
Room* moving_north = new Room("Moving north", "North of the moving room");
Room* moving_south = new Room("Moving south", "South of the moving room");
Room* moving_west = new Room("Moving west",
"West of the moving room");
Room* moving_east = new Room("Moving east",
"East of the moving room");
Player* moving_player = new Player("Jill", moving_room);
response = "You traveled nowhere";
assert(response == moving_player->move('p'));
response = "There is nothing in that direction";
assert(response == moving_player->move('n'));
assert(response == moving_player->move('e'));
assert(response == moving_player->move('w'));
assert(response == moving_player->move('s'));
moving_player->mLocation = moving_room;
response = "You traveled North\n" + moving_north->getDescription();
moving_room->link('n', *moving_north);
assert(response == moving_player->move('n'));
moving_player->mLocation = moving_room;
response = "You traveled South\n" + moving_south->getDescription();
moving_room->link('s', *moving_south);
assert(response == moving_player->move('s'));
moving_player->mLocation = moving_room;
response = "You traveled West\n" + moving_west->getDescription();
moving_room->link('w', *moving_west);
assert(response == moving_player->move('w'));
moving_player->mLocation = moving_room;
response = "You traveled East\n" + moving_east->getDescription();
moving_room->link('e', *moving_east);
assert(response == moving_player->move('e'));
// Player search test
// Exact same as the string Room::searchResponse( void ) // method so it has already been tested
// Player death test
// -Returns true when the player's hp is less than zero
// -Returns false when the player's hp is greater than zero
Player* death_test = new Player("Dead guy");
assert(false == death_test->death());
death_test->setHp(-1);
assert(true == death_test->death());
}

Now that we have the methods for the player class all set the next thing we have to do is to write out the methods for the player class. To do so open up the file “database.cpp” and add the methods for the player class below.

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
Player::Player( string name, Room* start ) : mName(name), mLocation(start), mWeapon(NULL), mArmor(NULL),
mHp(10), mMaxHp(10), mStrength(1), mDefence(0)
{
}
void Player::setHp( int newHp )
{
mHp = newHp ;
}
int Player::getHp( void ) const
{
return mHp ;
}
void Player::setStrength( int newStrength )
{
mStrength = newStrength ;
}
int Player::getStrength( void ) const
{
return mStrength ;
}
void Player::setDefence( int newDefence )
{
mDefence = newDefence ;
}
int Player::getDefence( void ) const
{
return mDefence ;
}
string Player::getLocation( void )
{
return mLocation->mName ;
}
void Player::setName( string newName )
{
mName = newName ;
}
string Player::getName( void ) const {
return mName ;
}
string Player::attack( string monsterName )
{
list< Monster* >::iterator iter ;
int damage = 0 ;
string response( "The " + monsterName + " is not here" ) ; stringstream ss ;
string strDamage ;
if( mLocation->mMonsters.empty() )
{
return "There are no monsters in this room to fight" ;
}
for( iter = mLocation->mMonsters.begin() ; iter != mLocation->mMonsters.end() ; ++iter )
{
if( (*iter)->getName() == monsterName )
{
damage = getStrength() - (*iter)->getDefence() ;
ss << damage ;
ss >> strDamage ;
ss.clear() ;
if( damage > 0 )
{
(*iter)->setHp( (*iter)->getHp() - damage ) ;
response = (*iter)->getName() + " was dealt " + strDamage + " damage" ;
}
else
{
response = (*iter)->getName() + " dodged the attack" ;
}
break ;
}
}
return response ;
}
string Player::equip( string itemName )
{
string response ;
if( mLocation->mItem != NULL && mLocation->mItem->getName() == itemName )
{
if( Item::WEAPON == mLocation->mItem->getType() && true == mLocation->mItem->mAvailiable )
{
mWeapon = mLocation->mItem ;
mLocation->itemTaken() ;
setStrength( mWeapon->getPower() ) ;
response = "You equipped the " + mWeapon->getName() ;
return response ;
}
else if( Item::ARMOR == mLocation->mItem->getType() && true == mLocation->mItem->mAvailiable )
{
mArmor = mLocation->mItem ;
mLocation->itemTaken() ;
setDefence( mArmor->getVitality() ) ;
mMaxHp += mArmor->getHealth() ;
response = "You equipped the " + mArmor->getName() ;
return response ;
}
else
{
setHp( mMaxHp ) ;
mLocation->itemTaken() ;
response = "You were healed" ;
return response ;
}
}
else
{
if( mLocation->mItem == NULL )
{
return "There is no item to equip in this room" ;
}
response = "To equip an item you must type its full name correctly" ;
return response ;
}
}
string Player::move( char direction )
{
string response = "You traveled " ;
switch( direction )
{
case 'n':
case 'N':
if( NULL == mLocation->mNorth )
{
response = "There is nothing in that direction" ;
break ;
}
mLocation = &(mLocation->north()) ;
response = response + "North\n" + mLocation->getDescription() ;
break ;
case 's':
case 'S':
if( NULL == mLocation->mSouth )
{
response = "There is nothing in that direction" ;
break ;
}
mLocation = &(mLocation->south()) ;
response = response + "South\n" + mLocation->getDescription() ;
break ;
case 'e':
case 'E':
if( NULL == mLocation->mEast )
{
response = "There is nothing in that direction" ;
break ;
}
mLocation = &(mLocation->east()) ;
response = response + "East\n" + mLocation->getDescription() ;
break ;
case 'w':
case 'W':
if( NULL == mLocation->mWest )
{
response = "There is nothing in that direction" ;
break ;
}
mLocation = &(mLocation->west()) ;
response = response + "West\n" + mLocation->getDescription() ;
break ;
default:
response = response + "nowhere" ;
}
return response ;
}
string Player::search( void )
{
return mLocation->searchResponse() ;
}
Player::dead Player::death( void )
{
if( getHp() <= 0 )
{
return true ;
}
return false ;
}

Afterwards you can begin to test the Dungeon class, so open up the file “test.h” file and add the following line.

1
void test_dungeon_class(void);

Here are the methods for the dungeon test, it is quite long so you can copy it if you want to but I suggest typing it out and reading through the comments in order to better understand the codebase; the file for the code below is “test.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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
void Test::test_dungeon_class(void)
{
// Dungeon constructor test
Dungeon* test_dungeon = new Dungeon(1, 1);
assert(1 == test_dungeon->mMaxPlayers);
assert(1 == test_dungeon->mMaxRooms);
assert(true == test_dungeon->mPlayers.empty());
assert(true == test_dungeon->mRooms.empty());
// - A dungeon without monsters is considered complete
assert(true == test_dungeon->isComplete());
// Dungeon addPlayer test
// - Successful addition of a new player should display the following
//the"The player named newPlayer->getName() entered the Dungeon"
Player* newbie = new Player("Noob");
string response("The player named Noob entered the Dungeon");
assert(response == test_dungeon->addPlayer(newbie));
assert(false == test_dungeon->mPlayers.empty());
// Dungeon addRoom test
// - "The room: newRoom->mName was added" will be displayed
//beif the addition was successful
Room* main_room = new Room("Main hall", "The main hall");
response = "The room: Main hall was added";
assert(response == test_dungeon->addRoom(main_room));
assert(false == test_dungeon->mRooms.empty());
// Dungeon removePlayer test
response = "Noob has left the dungeon";
assert(response == test_dungeon->removePlayer(newbie->getName()));
// Dungeon generatePlayerStats test
// -Sets the players max hp to 10, strength and defence to 2
test_dungeon->generatePlayerStats(newbie);
assert(10 == newbie->getHp());
assert(2 == newbie->getStrength());
assert(2 == newbie->getDefence());
// Dungeon combatLoop test
// - If the hero is stronger than the monster then the monster //theshould die else the hero should die
Dungeon* combat_test = new Dungeon(1, 1);
Room* combat_room = new Room("Battle", "Fight here");
Item* longsword = new Item("longsword", Item::WEAPON, 100, 0, 0);
Player* brawler = new Player("brawler", combat_room);
Monster* ogre = new Monster("ogre", 10, 0, 0); Monster* superogre = new Monster("superogre", 100, 100, 100); combat_room->setItem(longsword);
combat_room->addMonster(ogre);
combat_room->addMonster(superogre);
combat_test->addRoom(combat_room);
combat_test->addPlayer(brawler);
combat_test->generatePlayerStats(brawler);
brawler->mLocation = combat_room;
brawler->equip("longsword");
combat_test->combatLoop(*brawler, *ogre);
assert(combat_room->mMonsters.empty() == false);
assert(combat_test->isComplete() == false);
Item* badsword = new Item("badsword", Item::WEAPON, 0, 0, 0);
combat_room->setItem(badsword);
brawler->equip("badsword");
combat_test->combatLoop(*brawler, *superogre);
assert(true == brawler->death());
// Dungeon playerStatus test
Dungeon* status_test = new Dungeon(1, 2);
Room* start_room = new Room("Starting room", "Start here");
Room* end_room = new Room("Ending room", "End here");
Player* stats_hero = new Player("Mike", start_room);
Item* weapon_x = new Item("weaponx", Item::WEAPON, 20, 0, 0);
Item* armor_x = new Item("armorx", Item::ARMOR, 0, 20, 10);
start_room->link('e', *end_room);
end_room->link('w', *start_room);
start_room->setItem(weapon_x);
end_room->setItem(armor_x);
status_test->addRoom(start_room);
status_test->addRoom(end_room);
status_test->addPlayer(stats_hero);
// The stats reported are the following
/*
1. Hp
2. Strength
3. Defence
4. Location
5. Name
6. Weapon
7. Armor
*/
const int stat_total = 7;
string* status_before_weapon = new string[stat_total];
string* status_with_weapon = new string[stat_total];
string* status_with_armor = new string[stat_total];
status_before_weapon[0] = "Current HP: 10 out of 10";
status_before_weapon[1] = "Current Strength: 2";
status_before_weapon[2] = "Current Defence: 2";
status_before_weapon[3] = "Current Location: Starting room";
status_before_weapon[4] = "Current Name: Mike";
status_before_weapon[5] = "Current Weapon: none equipped";
status_before_weapon[6] = "Current Armor: none equipped";
assert(status_before_weapon = status_test->playerStatus(*stats_hero));
stats_hero->equip("weaponx");
status_with_weapon[0] = "Current HP: 10 out of 10";
status_with_weapon[1] = "Current Strength: 20";
status_with_weapon[2] = "Current Defence: 2";
status_with_weapon[3] = "Current Location: Starting room";
status_with_weapon[4] = "Current Name: Mike";
status_with_weapon[5] = "Current Weapon: weaponx";
status_with_weapon[6] = "Current Armor: none equipped";
assert(status_with_weapon = status_test->playerStatus(*stats_hero));
stats_hero->move('e');
stats_hero->equip("armorx");
status_with_armor[0] = "Current HP: 10 out of 20";
status_with_armor[1] = "Current Strength: 20";
status_with_armor[2] = "Current Defence: 20";
status_with_armor[3] = "Current Location: Ending room";
status_with_armor[4] = "Current Name: Mike";
status_with_armor[5] = "Current Weapon: weaponx";
status_with_armor[6] = "Current Armor: armorx";
assert(status_with_armor = status_test->playerStatus(*stats_hero));
}

Now we can write out the methods for the dungeon class itself. Go open up the file “database.cpp” and add the lines below to the file, remember to start at the line of code indicated in the margins.

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
Dungeon::Dungeon( int maxPlayers, int maxRooms ) : mMaxPlayers(maxPlayers), mMaxRooms(maxRooms)
{
mRooms.reserve( maxRooms ) ;
mPlayers.reserve( maxPlayers ) ;
}
bool Dungeon::isComplete( void )
{
bool flag = true ;
for( unsigned int i = 0 ; i < mRooms.size() ; ++i )
{
if( mRooms[i]->mMonsters.empty() )
{
continue ;
}
else
{
flag = false ;
break ;
}
}
return flag ;
}
string Dungeon::addPlayer( Player *newPlayer )
{
mPlayers.push_back( newPlayer ) ;
string response = "The player named " + newPlayer->getName() + " entered the Dungeon" ;
generatePlayerStats( newPlayer ) ;
return response ;
}
void Dungeon::generatePlayerStats( Player* newPlayer )
{
newPlayer->mMaxHp = 10 ;
newPlayer->setHp( newPlayer->mMaxHp ) ;
newPlayer->setStrength( 2 ) ;
newPlayer->setDefence( 2 ) ;
}
string Dungeon::addRoom( Room *newRoom )
{
mRooms.push_back( newRoom ) ;
string response = "The room: " + newRoom->mName + " was added" ;
return response ;
}
string Dungeon::removePlayer( std::string playerName )
{
string response = playerName + " has left the dungeon" ;
for( unsigned int i = 0 ; i < mPlayers.size() ; ++i )
{
if( playerName == mPlayers[i]->getName() )
{
mPlayers.erase( mPlayers.begin() + i ) ;
break ;
}
}
return response ;
}
//Should change this to have the monstersName as an argument
void Dungeon::combatLoop( Player& hero, Monster& villain )
{
villain.inCombat = true ;
while( true )
{
cout << hero.attack( villain.getName() ) << endl ;
if( villain.getHp() <= 0 )
{
cout << villain.getName() + " was slain by the valiant " << hero.getName() << endl;
hero.mLocation->killMonster( villain.getName() ) ;
return ;
}
else
{
int damage = 0 ;
damage = villain.getStrength() - hero.getDefence() ;
if( damage > 0 )
{
hero.setHp( hero.getHp() - damage ) ;
cout << hero.getName() + " was dealt " << damage << " damage" << endl ;
}
else
{
cout << hero.getName() + " dodged the attack" << endl ;
}
if( hero.death() )
{
cout << hero.getName() + " was slain by the horrible " + villain.getName() << endl;
return ;
}
}
}
}
string* Dungeon::playerStatus( Player& player )
{
stringstream ss ;
string temp ;
const int statusCount = 7 ;
string* response = new string[statusCount] ;
ss << player.getHp() ;
ss >> temp ;
response[0] = "Current HP: " + temp ;
ss.clear() ;
temp.clear() ;
ss << player.mMaxHp ;
ss >> temp ;
response[0] = response[0] + " out of " + temp ;
temp.clear() ;
ss.clear() ;
ss << player.getStrength() ;
ss >> temp ;
response[1] = "Current Strength: " + temp ;
ss.clear() ;
temp.clear() ;
ss << player.getDefence() ;
ss >> temp ;
response[2] = "Current Defence: " + temp ;
ss.clear() ;
temp.clear() ;
if( NULL == player.mLocation )
{
response[3] = "Error: BAD LOCATION" ;
}
else
{
response[3] = "Current Location: " + player.getLocation() ;
}
response[4] = "Current Name: " + player.getName() ;
if( NULL == player.mWeapon )
{
response[5] = "Current Weapon: none equipped" ;
}
else
{
response[5] = "Current Weapon: " + player.mWeapon->getName() ;
}
if( NULL == player.mArmor )
{
response[6] = "Current Armor: none equipped" ;
}
else
{
response[6] = "Current Armor: " + player.mArmor->getName() ;
}
return response ;
}
void Dungeon::displayHelp( void )
{
cout << "////////////////" << endl ;
cout << "Help menu" << endl ;
cout << "////////////////" << endl ;
cout << "Attack a monster type -> a:monster_name_here" << endl ;
cout << "Move to a new location type -> mv:location_name_here" << endl ;
cout << "Look for items type -> find:" << endl ;
cout << "Search the room for exits and monsters type -> loc:" << endl ;
cout << "Equip new items type -> eq:weapon_or_armor_name" << endl;
cout << "See your current status type -> you:" << endl;
}

The next class we have to test is the filter class, if you recall from previous parts, the filter class’ job is to validate user input to check if it is safe to “dispatch” to the database. You can think of the filter as a sanitizer for bad commands just like you can image a filter swearing words on message boards. Now go open up “test.h” and add the following line.

1
void test_filter_class(void);

You know the drill, create the test methods within the file “test.cpp” for the filter class(don’t worry this class is short).

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
void Test::test_filter_class(void)
{
// Filter chat test
// string Filter::chat() just truncates the command from the //fromchat message if it was t:message Filter filter;
string input("t:Hello world");
string response("Hello world");
assert(response == filter.chat(input));
// Filter validateMessage test
/*
The valid commands are the following
a:
mv:
find:
loc:
eq:
you:
help:
*/
assert(true == filter.validateMessage("mv:n"));
assert(true == filter.validateMessage("a:goblin"));
assert(true == filter.validateMessage("find:"));
assert(true == filter.validateMessage("loc:"));
assert(true == filter.validateMessage("eq:weapon"));
assert(true == filter.validateMessage("you:"));
assert(true == filter.validateMessage("help:"));
assert(false == filter.validateMessage("move:north"));
assert(false == filter.validateMessage("move:south"));
assert(false == filter.validateMessage("move:east"));
assert(false == filter.validateMessage("move:west"));
assert(false == filter.validateMessage("attack:goblin"));
assert(false == filter.validateMessage("fid:"));
assert(false == filter.validateMessage("location:"));
assert(false == filter.validateMessage("eqip:weapon"));
assert(false == filter.validateMessage("me:"));
assert(false == filter.validateMessage("zelpeafaf:"));
assert(false == filter.validateMessage("gg:"));
assert(false == filter.validateMessage("Pi:affg"));
assert(false == filter.validateMessage("Eqip:weapon"));
assert(false == filter.validateMessage("Ye:"));
assert(false == filter.validateMessage("no command input"));
}

There are only two methods for the filter class, chat and validate message. As you can probably guess, chat just sends out the text following the chat command “t:”. However validate message a bit more involved. Validate message works by running three distinct test, the “keywordtest” the “commandtest” and the movecommandtest”. Keyword just makes sure that the user entered a keyword such as “a:” for attack, “you:” for player status or any of the others. The “commandtest” just checks after the keyword has been validated that the fullname of the command matches the possible command inputs. Lastly the “movecommandtest” ensures that the direction of travel for the move command is north, south, east or west. So go open up “database.cpp” and add the lines below paying attention to which line the code begins on as shown in the margin.

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
Filter::valid Filter::validateMessage( std::string message )
{
//Necessary for the std::tolower() method
locale loc ;
for( unsigned int i = 0 ; i < message.length() ; ++i )
{
message [ i ] = tolower( message[ i ], loc ) ;
}
bool keywordTest = false ; bool commandTest = false ; bool moveCommandTest = true ; // Checks for a valid direction
unsigned int inputIndex = 0;
switch( message[ 0 ] )
{
case 'a': //Attack case 'm': //Move
case 'f': //Find
case 'l': //Location
case 'e': //Equip
case 'y': //You
case 't': //Talk
case 'h': //Help Menu
keywordTest = true ;
break ;break
default:
keywordTest = false ;
}
if( false == keywordTest )
{
return false ;
}
string subMessage ;
for( unsigned int i = 0 ; i < message.size() ; ++i )
{
if( message[ i ] == ':' )
{
inputIndex = i;
subMessage += message[ i ] ;
break ;
}
else
{
subMessage += message[ i ] ;
continue ;
}
}
if( subMessage == "a:" ||
subMessage == "mv:" ||
subMessage == "find:" ||
subMessage == "loc:" ||
subMessage == "eq:" ||
subMessage == "you:" ||
subMessage == "t:" ||
subMessage == "help:" )
{
commandTest = true ;
}
else
{
return false;
}
if( subMessage == "mv:" )
{
//Checks if a direction was entered by the player,
// accomplishes this by seeing if the mv: command
// is followed by at most a single character representing
// the directions 'n' or 'e' or 'w' or 's'
if( inputIndex + 1 == message.size() )
{
moveCommandTest = false;
}
while( inputIndex < message.size() )
{
if( false == moveCommandTest )
{
break ;
}
if(message[ inputIndex ] == ' ')
{
moveCommandTest = false;
break ;
}
++inputIndex ;
}inputIndex
}
if( true == commandTest && true == keywordTest && true == moveCommandTest )
{
return true ;
}
else
{
return false ;
}
}
string Filter::chat( string message )
{
return message.substr( 2, message.length() ) ;
}

The dispatch class is the last class we need for thesingle player version of the game. Being the dispatchits sole purpose is to separate the value and command fromthe filtered user input and “dispatch” it to the database.Now lets open up “test.h” and add the line below.

1
void test_dispatch_class(void);

Next we’ll write out the test for the dispatch class within “test.cpp”, so go open that file and insert the code below.

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
void Test::test_dispatch_class()
{
// Dispatch extractCommand test
//a Attack //m Move
//f Find
//l Location
//e Equip
//y You
//t Talk
//h Help Menu
Dispatch dispatch;
assert('a' == dispatch.extractCommand("a:"));
assert('m' == dispatch.extractCommand("mv:"));
assert('f' == dispatch.extractCommand("find:"));
assert('l' == dispatch.extractCommand("loc:"));
assert('e' == dispatch.extractCommand("eq:"));
assert('y' == dispatch.extractCommand("you:"));
assert('t' == dispatch.extractCommand("t:"));
assert('h' == dispatch.extractCommand("help:"));
// Dispatch extractValue test
/*
The only commands that take values.
attack move equip
talk */
assert("goblin" == dispatch.extractValue("a:goblin"));
assert("north" == dispatch.extractValue("mv:north"));
assert("armor" == dispatch.extractValue("eq:armor"));
assert("hello world" == dispatch.extractValue("t:hello world"));
assert(" s p a c e s " == dispatch.extractValue(
"t: s p a c e s "));
}

Now we can add the methods for the dispatch class to our “database.cpp”file. Open up the file “database.cpp” and add the lines below.

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
char Dispatch::extractCommand(std::string fromFilter)
{
//a Attack //m Move
//f Find
//l Location
//e Equip
//y You
//t Talk
//h Help Menu
return fromFilter[ 0 ] ;
}
string Dispatch::extractValue(std::string fromFilter)
{
/*
The value of the string from the filter is all of the text
that follows the command.
-Example-
a:goblin
The command is a:
The value is goblin
*/
// For single character commands such as t:
if( fromFilter[ 1 ] == ':' )
{
return fromFilter.substr( 2, fromFilter.length() ) ;
}
return fromFilter.substr( 3, fromFilter.length() ) ;
}

The last thing we have to do before we can call our single player version of the game complete is to make sure all the test pass with flying colors. Beingthat we only implemented unit test, you will be notified when a test failsby the console giving you a “assertion failed at line:XYZ” where XYZ is the line number. So open up “main.cpp” and make sure it looks like the code below.

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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
//Filename main.cpp
#include "Database.h"
#include"Test.h"
using namespace MUD ;
void run_test(void)
{
cout << "Ignore the combat printouts from the\n";
cout << "test_dungeon_class() method\n" << endl;
Test::get_instance()->test_item_class();
Test::get_instance()->test_monster_class();
Test::get_instance()->test_room_class();
Test::get_instance()->test_player_class();
Test::get_instance()->test_dungeon_class();
Test::get_instance()->test_filter_class();
Test::get_instance()->test_dispatch_class();
}
void run_sample_game(void);
int main(void)
{
run_test();
//run_sample_game();
return 0;
}
void run_sample_game(void)
{
//Construct Dungeon
Dungeon caves( 2, 21 ) ;
//Items power vitality health
Item* herb_a = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* herb_b = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* herb_c = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* herb_d = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* herb_e = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* herb_f = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* herb_g = new Item( "herb", Item::HEALTH, 0, 0, 0 ) ;
Item* woodsword = new Item( "woodsword", Item::WEAPON, 10, 0, 0 ) ;
Item* steelblade = new Item( "steelblade", Item::WEAPON, 20, 0, 0 ) ;
Item* crystalsword = new Item( "crystalsword", Item::WEAPON, 35, 0, 0 ) ;
Item* genjiblade = new Item( "genjiblade", Item::WEAPON, 60, 0, 0 ) ;
Item* angelicsword = new Item( "angelicsword", Item::WEAPON, 80, 0, 0 ) ;
Item* finalweapon = new Item( "dragonslayer", Item::WEAPON, 2000, 0, 0 ) ;
Item* woodarmor = new Item( "woodarmor", Item::ARMOR, 0, 5, 15 ) ;
Item* steelarmor = new Item( "steelarmor", Item::ARMOR, 0, 7, 20 ) ;
Item* diamondarmor = new Item( "diamondarmor", Item::ARMOR, 0, 12, 30 ) ;
Item* crystalarmor = new Item( "crystalarmor", Item::ARMOR, 0, 15, 35 ) ;
Item* dragonarmor = new Item( "dragonarmor", Item::ARMOR, 0, 20, 100 ) ;
Item* ultimatearmor = new Item( "ultimatearmor", Item::ARMOR, 0, 1000, 1000 ) ;
//Monsters hp str def
Monster* bear = new Monster( "bear", 15, 2, 3 ) ;
Monster* goblin = new Monster( "goblin", 5, 3, 1 ) ;
Monster* imp = new Monster( "imp", 5, 5, 2 ) ;
Monster* redgoblin = new Monster( "redgoblin", 5, 5, 5 ) ;
Monster* demon = new Monster( "demon", 6, 3, 7 ) ;
Monster* zergling = new Monster( "zergling", 10, 10, 9 ) ;
Monster* bats = new Monster( "bats", 20, 15, 3 ) ;
Monster* morebats = new Monster( "morebats", 25, 20, 15 ) ;
Monster* golem = new Monster( "golem", 50, 21, 9 ) ;
Monster* goatdemon = new Monster( "goatdemon", 100, 10, 10 ) ;
Monster* cyclops = new Monster( "cyclops", 150, 90, 0 ) ;
Monster* behemoth = new Monster( "behemoth", 250, 30, 40 ) ;
Monster* alien = new Monster( "alien", 50, 21, 4 ) ;
Monster* giantrat = new Monster( "giantrat", 55, 40, 1 ) ;
Monster* fallenangel = new Monster( "fallenangel", 300, 100, 100 ) ;
Monster* greatdragon = new Monster( "greatdragon", 500, 300, 1 ) ;
Monster* invincibledragon = new Monster( "invincibledragon", 999, 999, 999 ) ;
//Rooms name description item
Room* mainHall = new Room( "The main cavern", "A murkey swamp cavern", woodsword ) ;
Room* river = new Room( "The black river", "A river with dark water", woodarmor ) ;
Room* greencave = new Room( "The greencave", "A moss covered cave", herb_a ) ;
Room* bluecave = new Room( "The bluecave", "A frosty cave", steelblade ) ;
Room* darkpit = new Room( "The darkpit", "An endless hole lies before you", steelarmor ) ;
Room* morbidfield = new Room( "The morbidfield", "Pitch black grass surrounds you", herb_b ) ;
Room* graveyard = new Room( "The graveyard", "Tombstones abound", diamondarmor ) ;
Room* crypt = new Room( "The crypt", "A nasty rotten crypt", crystalsword ) ;
Room* evilpassage = new Room( "The evil passage", "An evil pasage", crystalarmor ) ;
Room* darklair = new Room( "The dark lair", "A dark lair", herb_c ) ;
Room* firepit = new Room( "The firey pits", "Fire is every where", herb_d ) ;
Room* greenpasture = new Room( "The greenpastures", "A meadow is before you", genjiblade ) ;
Room* icecave = new Room( "The icy caves", "Ice everywhere you look", herb_e ) ;
Room* livingroom = new Room( "The living room", "The room is alive", dragonarmor ) ;
Room* cellar = new Room( "The cellar", "A dirty cellar filled with rats", angelicsword ) ;
Room* lifespring = new Room( "The lifespring", "A beautiful fountain stands", herb_f ) ;
Room* waterfall = new Room( "The waterfall", "Clear blue water falls", herb_g ) ;
Room* hellsgate = new Room( "The gates of hell", "Large rusty doors lead to hell", ultimatearmor ) ;
Room* dragoncave = new Room( "The dragon's cave", "An ancient cave..." ) ;
Room* dragonsden = new Room( "The dragon's den", "A powerful monster awaits" ) ;
Room* lostforest = new Room( "The secret forest", "The strongest sword lies hidden here", finalweapon ) ;
//Link Rooms
mainHall->link( 'e', *river ) ;
river->link( 's', *greencave ) ;
river->link( 'e', *bluecave ) ;
river->link( 'w', *mainHall ) ;
greencave->link( 'n', *river ) ;
bluecave->link( 'w', *river ) ;
bluecave->link( 'n', *darkpit ) ;
darkpit->link( 's', *bluecave ) ;
darkpit->link( 'n', *morbidfield ) ;
morbidfield->link( 's', *darkpit ) ;
morbidfield->link( 'e', *graveyard ) ;
graveyard->link( 'w', *morbidfield ) ;
graveyard->link( 'n', *crypt ) ;
graveyard->link( 'e', *evilpassage ) ;
crypt->link( 's', *graveyard ) ;
evilpassage->link( 'w', *graveyard ) ;
evilpassage->link( 's', *darklair ) ;
darklair->link( 'n', *evilpassage ) ;
darklair->link( 's', *firepit ) ;
firepit->link( 'n', *darklair ) ;
firepit->link( 'e', *greenpasture ) ;
greenpasture->link( 'w', *firepit ) ;
greenpasture->link( 's', *icecave ) ;
greenpasture->link( 'e', *livingroom ) ;
icecave->link( 'n', *greenpasture ) ;
livingroom->link( 'w', *greenpasture ) ;
livingroom->link( 'e', *cellar ) ;
cellar->link( 'w', *livingroom ) ;
cellar->link( 'n', *lifespring ) ;
cellar->link( 'e', *hellsgate ) ;
lifespring->link( 's', *cellar ) ;
lifespring->link( 'n', *waterfall ) ;
waterfall->link( 's', *lifespring ) ;
hellsgate->link( 'w', *cellar ) ;
hellsgate->link( 'e', *dragoncave ) ;
dragoncave->link( 'w', *hellsgate ) ;
dragoncave->link( 'e', *dragonsden ) ;
dragonsden->link( 'w', *dragoncave ) ;
dragonsden->link( 'e', *lostforest ) ;
lostforest->link( 'w', *dragonsden ) ;
//Add monsters to rooms
mainHall->addMonster( goblin ) ;
mainHall->addMonster( imp ) ;
river->addMonster( bear ) ;
river->addMonster( redgoblin ) ;
bluecave->addMonster( demon ) ;
bluecave->addMonster( zergling ) ;
morbidfield->addMonster( bats ) ;
morbidfield->addMonster( morebats ) ;
graveyard->addMonster( golem ) ;
evilpassage->addMonster( goatdemon ) ;
firepit->addMonster( cyclops ) ;
greenpasture->addMonster( behemoth ) ;
livingroom->addMonster( alien ) ;
cellar->addMonster( giantrat ) ;
hellsgate->addMonster( fallenangel ) ;
dragoncave->addMonster( greatdragon ) ;
dragonsden->addMonster( invincibledragon ) ;
//Add the rooms to the dungeon
caves.addRoom( mainHall ) ;
caves.addRoom( river ) ;
caves.addRoom( greencave ) ;
caves.addRoom( bluecave ) ;
caves.addRoom( darkpit ) ;
caves.addRoom( morbidfield ) ;
caves.addRoom( graveyard ) ;
caves.addRoom( crypt ) ;
caves.addRoom( evilpassage ) ;
caves.addRoom( darklair ) ;
caves.addRoom( firepit ) ;
caves.addRoom( greenpasture ) ;
caves.addRoom( icecave ) ;
caves.addRoom( livingroom ) ;
caves.addRoom( cellar ) ;
caves.addRoom( lifespring ) ;
caves.addRoom( waterfall ) ;
caves.addRoom( hellsgate ) ;
caves.addRoom( dragoncave ) ;
caves.addRoom( dragonsden ) ;
caves.addRoom( lostforest ) ;
//Create a filter Filter filter ;
//Create a dispatch object with a command char and a value string
Dispatch dispatch ;
char command ;
string value ;
//Simulate a new game
//Create a new player and add to the dungeon
string input ;
cout << "Welcome to the game\nEnter in a new name: " ;
getline(cin, input, '\n');
Player* one = new Player( input, mainHall ) ;
cout << caves.addPlayer( one ) << endl ;
//Display needed info for the players
cout << "The objective is to clear the dungeon of all monsters. Good Luck" << endl ;
cout << "Enter in a new command to start, to display help type the following command" << endl ;
cout << "help:" << endl ;
cout << "To quit simply enter \"quit\" without the quotes as a new command" << endl ;
cout << "New Command" << endl ;
getline(cin, input, '\n');
//Start the game loop
while( input != "quit" )
{
//Pass the users message through the filter
if( filter.validateMessage( input ) )
{
//Split the users message into a command and value pair
command = dispatch.extractCommand( input ) ;
value = dispatch.extractValue( input ) ;
//Match the command and forward the value
switch( command )
{
case 'a': //Attack
//If the monster does not exist or the name was mispelled
if( &(one->mLocation->getMonsterRef( value )) == NULL )
{
cout << "There is no " + value + " at this location" << endl ;
break ;
}
//If the monster is there but in combat the player cannot fight it
if( one->mLocation->getMonsterRef( value ).inCombat == true )
{
cout << "The " + value + " is currently fighting" << endl ;
break ;
}
caves.combatLoop( *one, one->mLocation->getMonsterRef( value ) ) ;
break ;
case 'm': //Move
cout << one->move( value[ 0 ] ) ;
break ;
case 'f': //Find
cout << one->search() ;
break ;
case 'l': //Location
cout << one->getLocation() ;
break ;
case 'e': //Equip
cout << one->equip( value ) ;
break ;
case 'y': //You
{
string* playerStats = new string ;
playerStats = caves.playerStatus( *one ) ;
for( int i = 0 ; i < 7 ; ++i )
{
cout << playerStats[i] << endl ;
}endl
}
break ;
//case 't': //Talk
case 'h': //Help Menu
caves.displayHelp();
break ;
}
}
else
{
cout << "Invalid command please refer to the help menu.\nhelp:" << endl ;
cout << "Or type \"quit\" to quit the game" << endl ;
}
if( caves.isComplete() )
{
cout << "Dungeon Cleared, YOU WIN!" << endl ;
break ;
}
if( one->death() )
{
cout << one->getName() + " was slain by the " + one->mLocation->getMonsterRef( value ).getName() << endl ;
one->mLocation->getMonsterRef( value ).inCombat = false ;
cout << "*Hint* Secret rooms hidden abound; find them and victory shall be yours" << endl ;
input = "quit" ;
}
else
{
//Get the next command
cout << "\n\nNew Command" << endl ;
getline(cin, input, '\n');
}
}
caves.removePlayer( one->getName() ) ;
}

For reference here are all the files for the project below.If none of the dropbox links are working then mention it in the comments section and I’ll re-upload the files.


http://dl.dropbox.com/u/22280460/database.cpp


http://dl.dropbox.com/u/22280460/database.h


http://dl.dropbox.com/u/22280460/main.cpp


http://dl.dropbox.com/u/22280460/Test.cpp


http://dl.dropbox.com/u/22280460/Test.h

Click here for part VI

Share Comments