Plants vs. Zombies: Battle for Neighborville is a third-person shooter and platformer video game developed by PopCap Games and published by Electronic Arts. It was released as a 'Founder's Edition' on September 4, 2019, and was available to purchase until September 30, 2019, with the worldwide release being out on October 18th. On June 5th, 2020, it was made available for purchase on Steam. It was discontinued in September 2020.
For this assignment, we are going to implement a simple text-based version of Plants vs Zombies: Battle in Neighborville. In our version of the game, we are going to play as a plant that is trying to defeat the zombies while wandering through a town.
As this project is focused on inheritance and polymorphism, there is an inheritance chain as defined in the figure below. see image.
In addition to these classes, there is a room class that defines the characteristics for each room and a game class the manages the game itself. Specific information about the amount of damage that each plant or zombie can do is included in the table below:
Type | Name | Regular Attack | Attack Damage | Special Attack Verbiage | Damage | Health |
Zombie | Buckethead | deals 1 point of damage! | 1 | throws its bucket at you! | 1 | 7 |
Zombie | Imp | deals 1 point of damage! | 1 | blasts you with their Impkata attack! | 4 | 10 |
Zombie | Engineer | deals 1 point of damage! | 1 | blasts you with their steam blaster! | 2 | 5 |
Plant | Sunflower | attacks dealing 1-6 damage | 1-6 | uses a high beam turret - **SUNBEAM** | 1-8 | 15 |
Plant | Chomper | attacks dealing 1-6 damage | 1-6 | performs a sneak attack! CHOMP | 2-12 | 12 |
Plant | Peashooter | attacks dealing 1-6 damage | 1-6 | spins up doing GATLING DAMAGE! | 3-12 | 10 |
Plant | Plant | attacks dealing 1-6 damage | 1-6 | None | N/A | 10 |
The provided map files are formatted like this: see image.
If we look at the first row in the map 1 file, we are saying the following:
If you are having a tough time visualizing the rooms structures, here is an example of map1_data.txt in an image: see image.
And here is the larger map from map2_data.txt: see image.
This is a list of the requirements of this application. For this project, it is up to you exactly how you want to implement it. For you to earn all the points, however, you will need to meet all the defined requirements.
For this project, input files are not too challenging. For this project, the data files can change based on the map you are going to adventure in. The standard format of the filename will be map1_data.txt. Included in the main function is the command line arguments to use each of these files. When you load the project, you will be loading the file at the same time. For example, if I wanted to load the map1_data.txt file, I could use ./proj4 map1_data.txt. Additionally, the makefile has all three already coded. You can use make run1 and make run2 for the normal runs and make val1 and make val2 for the valgrind runs.
For example, map1_data.txt would look like this because it only has two room. Larger examples (map2 for example) has many more lines.
0|Dead End Street|You are at the end of the dead-end street. The streets smells bad - almost like death. There are walls on both sides of the street. The only exit appears to be down the road to the east.|-1|1|-1|-1|
1|End of Empty Street|This is the strangest street. It went from a dead end to another dead end. There do not appear to be any other exists to this neighborhood except back to the dead-end to the west. It smells like old oil and vomit here. The road extends to the
west.|-1|-1|-1|0|
The file can be downloaded from Prof. Dixon's data folder by navigating to your project 1 folder and typing the following command:
cp /afs/umbc.edu/users/j/d/jdixon/pub/cs202/proj4/* .
After you copy the data file, you can type "cat map1_data.txt" and it should show you the entire data file.
In the sample output below, user input is colored blue for clarity. After compiling and running proj4, the output would look like this:
[[jdixon@linux1 newPlants]$ make run1
./proj4 map1_data.txt
Loading file: map1_data.txt
Welcome to UMBC Adventure!
Plant Name: Chomper
Select a class
1. Peashooter
2. Chomper
3. Sunflower
4. No Class
2
Dead End Street
You are at the end of the dead-end street. The streets smells bad - almost like death. There are walls on both sides of the street. The only exit appears to be down the road to the east.
Possible Exits: E
A large zombie stands here wearing khakis with a sign that says ENGINEER.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
6
Good bye!
Let's look at map1 again which has two rooms. Again, any time the plant (player) goes into another room, there is a 25% chance for one of the three zombies (buckethead, engineer, or imp) or 25% for nothing (peaceful). Here is an example of a full run.
[jdixon@linux1 newPlants]$ make run1
./proj4 map1_data.txt
Loading file: map1_data.txt
Welcome to UMBC Adventure!
Plant Name: Shooter
Select a class
1. Peashooter
2. Chomper
3. Sunflower
4. No Class
1
Dead End Street
You are at the end of the dead-end street. The streets smells bad - almost like death. There are walls on both sides of the street. The only exit appears to be down the road to the east.
Possible Exits: E
It is peaceful here.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
1
Dead End Street
You are at the end of the dead-end street. The streets smells bad - almost like death. There are walls on both sides of the street. The only exit appears to be
down the road to the east.
Possible Exits: E
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
2
Which direction? (N E S W)
2
Which direction? (N E S W)
e
End of Empty Street
This is the strangest street. It went from a dead end to another dead-end. There do not appear to be any other exists to this neighborhood except back to the deadend to the west. It smells like old oil and vomit here. The road extends to the west.
Possible Exits: W
A buckethead zombie teaters here with a bucket on his
head.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
3
1. Normal Attack
2. Special Attack
2
Shooter spins up doing GATLING DAMAGE!
Shooter deals 6 damage!
Buckethead deals 1 point of damage!
Shooter's health:9
Buckethead's health:1
1. Normal Attack
2. Special Attack
1
Shooter attacks dealing 6 damage.
Buckethead deals 1 point of damage!
You have defeated the Buckethead.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
5
Name: Shooter
HP: 8
Rests: 1
Special: 2
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
6
Good bye!
[jdixon@linux1 newPlants]$
Here we have an example of make val2 (with valgrind). It is a much larger map (it is a city that leads into a cave):
make val2
valgrind ./proj4 map2_data.txt
==2722438== Memcheck, a memory error detector
==2722438== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2722438== Using Valgrind-3.16.0 and LibVEX; rerun with -h for copyright info
==2722438== Command: ./proj4 map2_data.txt
==2722438==
Loading file: map2_data.txt
Welcome to UMBC Adventure!
Plant Name: Sunny
Select a class
1. Peashooter
2. Chomper
3. Sunflower
4. No Class
3
City Entrance
The streets that were once sleek new tarmac are now greyed by the bleaching of the sun. The road is a monochrome patchwork, each one lined with a shiny boarder of tar. Despite these fixes there are still cracks and the potholes grow larger with each passing year. The trees that were once fine saplings with soft spring foliage are now gnarled embittered trees growing tall but without strength, competing unnaturally against the towering apartment blocks they were planted too close to. Their bark is mossy from the perennial dampness and incessant rain, except a few weeks of reprieve in the summer months. The sidewalk for the most part is still smooth concrete, albeit scattered with litter and the debris of the molting trees. The air stinks of death.
Possible Exits: E
A large zombie stands here wearing khakis with a sign that says ENGINEER.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
3
1. Normal Attack
2. Special Attack
2
Sunny uses a high beam turret - **SUNBEAM**
Sunny deals 6 damage!
Engineer blasts you with their steam blaster!
You take 2 points of damage.
You have defeated the Engineer.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
2
Which direction? (N E S W)
e
A Wide Street
The road is flanked by large old homes with deep red bricks. There are leaves blowing between old broken lampposts flickering in disrepair. Piles of old clothes and rotten food are scattered throughout the area. The air stinks of death.
Possible Exits: EW
It is peaceful here.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
2
Which direction? (N E S W)
e
A Square
A wide shelf of rock overlooks a pool of water in the center of the square. The street continues around the small water feature in an east and west fashion. The small pool has thousands of small stones that seem to glow providing a soft light creating a shimmer in the
water.To the north and south the intersecting roads have been barricaded. The air stinks of death.
Possible Exits: EW
A dirty imp licks his lips and glares at you.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
3
1. Normal Attack
2. Special Attack
1
Sunny attacks dealing 1 damage.
Imp deals 1 point of damage!
Sunny's health:12
Imp's health:9
1. Normal Attack
2. Special Attack
2
Sunny uses a high beam turret - **SUNBEAM**
Sunny deals 8 damage!
Imp deals 1 point of damage!
Sunny's health:11
Imp's health:1
1. Normal Attack
2. Special Attack
1
Sunny attacks dealing 5 damage.
Imp deals 1 point of damage!
You have defeated the Imp.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
5
Name: Sunny
HP: 10
Rests: 1
Special: 1
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
6
Good bye!
==2722438==
==2722438== HEAP SUMMARY:
==2722438== in use at exit: 0 bytes in 0 blocks
==2722438== total heap usage: 44 allocs, 44 frees, 90,870 bytes allocated
==2722438==
==2722438== All heap blocks were freed -- no leaks are possible
==2722438==
==2722438== For lists of detected and suppressed errors, rerun with: -s
==2722438== ERROR SUMMARY: 0 errors from 0 contexts
(suppressed: 0 from 0)
Here is a short run showing resting.
./proj4 map1_data.txt
Loading file: map1_data.txt
Welcome to PvZ: Battle for Neighborville!
Plant Name: Chomper
Select a class
1. Peashooter
2. Chomper
3. Sunflower
4. No Class
2
Dead End Street
You are at the end of the dead-end street. The streets smell bad - almost like death. There are walls on both sides of the street. The only exit appears to be down the road to the east.
Possible Exits: E
A large zombie stands here wearing khakis with a sign
that says ENGINEER.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
4
You cannot rest with a zombie in the room.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
3
1. Normal Attack
2. Special Attack
1
Chomper attacks dealing 2 damage.
Engineer deals 1 point of damage!
Chomper's health:11
Engineer's health:3
1. Normal Attack
2. Special Attack
2
Chomper performs a sneak attack! CHOMP
Chomper deals 10 damage.
Engineer deals 1 point of damage!
You have defeated the Engineer.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
4
You rest and wake up refreshed
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
4
You do not have any more rests available.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
Here is an example of validating the direction (checkDirection):
[make run1
./proj4 map1_data.txt
Loading file: map1_data.txt
Welcome to UMBC Adventure!
Plant Name: Champ
Select a class
1. Peashooter
2. Chomper
3. Sunflower
4. No Class
4
Dead End Street
You are at the end of the dead-end street. The streets smells bad - almost like death. There are walls on both sides of the street. The only exit appears to be down the road to the east.
Possible Exits: E
It is peaceful here.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
2
Which direction? (N E S W)
sick
Which direction? (N E S W)
Which direction? (N E S W)
Which direction? (N E S W)
Which direction? (N E S W)
e
End of Empty Street
This is the strangest street. It went from a dead end to another dead-end. There do not appear to be any other exists to this neighborhood except back to the deadend to the west. It smells like old oil and vomit here. The road extends to the west.
Possible Exits: W
It is peaceful here.
What would you like to do?
1. Look
2. Move
3. Attack Zombie
4. Rest
5. Check Stats
6. Quit
You need to write a makefile for this project as one will not be provided for you in order to compile and test. Make sure to create any macros to help!
Once you have compiled using your makefile, enter the command make run or ./proj4 to run your program. Make sure you have implemented that as part of your make file. If your executable is not proj4, you will lose points. It should look similar to the sample output provided above. Some of the simulation is based on randomness so the output will vary.
Because we are using a significant amount of dynamic memory for this project, you are required to manage any memory leaks that might be created. Remember, in general, for each item that is dynamically created, it should be deleted using a destructor.
One way to test to make sure that you have successfully removed any of the memory leaks is to use the valgrind command.
Since this project makes extensive use of dynamic memory, it is important that you test your program for memory leaks using valgrind:
valgrind ./proj4 map1_data.txt
Note: If you accidently use valgrind make run1, you may end up with some memory that is still reachable. Do not test this - test using the command above where you include the input file. The makefile should include make val1 (which is ok).
If you have no memory leaks, you should see output like the following:
==5606==
==5606== HEAP SUMMARY:
==5606== in use at exit: 0 bytes in 0 blocks
==5606== total heap usage: 87 allocs, 87 frees, 10,684 bytes allocated
==5606==
==5606== All heap blocks were freed -- no leaks are possible
==5606==
==5606== For counts of detected and suppressed errors, rerun with: -v
==5606== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 6 from 6)
The important part is "in use at exit: 0 bytes 0 blocks," which tells me all the dynamic memory was deleted before the program exited. If you see anything other than "0 bytes 0 blocks" there is probably an error in one of your destructors. We will evaluate this as part of the grading for this project.
Additional information on valgrind can be found here: http://valgrind.org/docs/manual/quick-start.html
Once you have compiled using the provided makefile, enter the commands (make run1, make run2, make val1, or make val2) or ./proj4 map1_data.txt to run your program.
Room.h
/*Title: Room.h
Description: This class defines rooms in Plants vs Zombies: Battle for Neighborville
*/
#ifndef ROOM_H //Header Guard
#define ROOM_H //Header Guard
#include < iostream>
#include < string>
using namespace std;
//Enum defining the directions in array n/N = 0, e/E = 1, s/S = 2, w/W = 3
enum direction{ n = 0, N = 0, e = 1, E = 1, s = 2, S = 2, w = 3, W = 3 };
class Room
{
public:
//Name: Room (Overloaded Constructor)
//Precondition: Must have valid input for each part of a room
//Postcondition: Creates a new room
Room(int, string, string, int, int, int, int);
//Name: GetName
//Precondition: Must have valid room
//Postcondition: Returns room name as string
string GetName();
//Name: GetID
//Precondition: Must have valid room
//Postcondition: Returns room id as int
int GetID();
//Name: GetDesc
//Precondition: Must have valid room
//Postcondition: Returns room desc as string
string GetDesc();
//Name: CheckDirection
//Precondition: Must have valid room
//You pass it a char (N/n, E/e, S/s, or W/w) and if that is a valid exit it
//returns the ID of the room in that direction
//Postcondition: Returns id of room in that direction if the exit exists
//If there is no exit in that direction, returns -1
int CheckDirection(char myDirection);
//Name: PrintRoom
//Precondition: Room must be complete
//Postcondition: Outputs the room name, room desc, then possible exits
void PrintRoom();
private:
int m_ID; //Unique int for room number
string m_name; //Name of room
string m_desc; //Description of room
int m_direction[4]; //Array holding room to north, east, south, west (-1 if no exit)
};
#endif
Plant.h
#ifndef _PLANT_H_
#define _PLANT_H_
#include "Entity.h"
class Plant : public Entity {
public:
// Name: Plant() - Default Constructor
// Description: Creates a new plant
// Preconditions: None
// Postconditions: Can create a plant
Plant();
// Name: Plant(string name, int hp) - Overloaded constructor
// Description: Creates a new plant
// Preconditions: None
// Postconditions: Can be used to populate plant or child classes
Plant(string, int);
// Name: ~Plant - Virtual Destructor
// Description: Makes sure everything in child class is deallocated
// Preconditions: None
// Postconditions: Everything dynamically allocated is deallocated
~Plant();
// Name: Attack()
// Description: Describes attack statements for plant
// Preconditions: None
// Postconditions: Returns damage
int Attack();
// Name: SpecialAttack()
// Description: Tells player that plant does not have special attack
// may be used for child class special attacks
// Preconditions: None
// Postconditions: May be used to call child class SpecialAttack
virtual int SpecialAttack();
};
#endif
Zombie.h
#ifndef _ZOMBIE_H_
#define _ZOMBIE_H_
#include "Entity.h"
//The parent class for all of the cute and smelly zombies
class Zombie : public Entity {
public:
// Name: Zombie() - Default Constructor
// Description: Would be used to create a zombie but abstracted
// Preconditions: None
// Postconditions: Used to populate child class data
Zombie();
// Name: Zombie(string name, int hp) - Overloaded Constructor
// Description: Would be used to create a zombie but abstracted
// Preconditions: None
// Postconditions: Used to populate child class data
Zombie(string, int);
// Name: Attack()
// Description: Describes attack statements for zombie
// Preconditions: None
// Postconditions: Returns damage
int Attack();
// Name: SpecialAttack()
// Description: Purely virtual SpecialAttack
// Preconditions: None
// Postconditions: May be used to call child class SpecialAttack
virtual int SpecialAttack() = 0;
};
#endif
Sunflower.h
#ifndef _SUNFLOWER_H_
#define _SUNFLOWER_H_
#include "Plant.h"
class Sunflower : public Plant {
public:
// Name: Sunflower() - Default Constructor
// Description: Creates a new sunflower
// Preconditions: None
// Postconditions: Can create a sunflower
Sunflower();
// Name: Sunflower(string name, int hp)
// Description: Creates a new sunflower
// Preconditions: None
// Postconditions: Can create a sunflower
Sunflower(string, int);
// Name: ~Sunflower - Virtual Destructor
// Description: Makes sure everything in child class is deallocated
// Preconditions: None
// Postconditions: Everything dynamically allocated is deallocated
~Sunflower();
// Name: SpecialAttack
// Description: Defines the Sunflower's special attack
// Preconditions: None
// Postconditions: Returns damage from special attack
int SpecialAttack();
};
#endif
Peashooter.h
#ifndef _PEASHOOTER_H_
#define _PEASHOOTER_H_
#include "Plant.h"
//The traditional plant that shoots its own peas. Yikes
class Peashooter : public Plant {
public:
// Name: Peashooter() - Default Constructor
// Description: Creates a new peashooter
// Preconditions: None
// Postconditions: Can create a peashooter
Peashooter();
// Name: Peashooter(string name, int hp)
// Description: Creates a new peashooter
// Preconditions: None
// Postconditions: Can create a peashooter
Peashooter(string, int);
// Name: ~Peashooter - Virtual Destructor
// Description: Makes sure everything in child class is deallocated
// Preconditions: None
// Postconditions: Everything dynamically allocated is deallocated
~Peashooter();
// Name: SpecialAttack
// Description: Defines the Peashooter's special attack
// Preconditions: None
// Postconditions: Returns damage from special attack
int SpecialAttack();
};
#endif
Chomper.h
#ifndef _CHOMPER_H_
#define _CHOMPER_H_
#include "Plant.h"
//Sweet plant that chomps zombies - think Little Shop of Horrors
class Chomper : public Plant {
public:
// Name: Chomper() - Default Constructor
// Description: Creates a new chomper
// Preconditions: None
// Postconditions: Can create a chomper
Chomper();
// Name: Chomper(string name, int hp)
// Description: Creates a new chomper
// Preconditions: None
// Postconditions: Can create a chomper
Chomper(string, int);
// Name: ~Chomper - Virtual Destructor
// Description: Makes sure everything in child class is deallocated
// Preconditions: None
// Postconditions: Everything dynamically allocated is deallocated
~Chomper();
// Name: SpecialAttack
// Description: Defines the Chomper's special attack
// Preconditions: None
// Postconditions: Returns damage from special attack
int SpecialAttack();
};
#endif
Buckethead.h
#ifndef _BUCKETHEAD_H_
#define _BUCKETHEAD_H_
#include "Zombie.h"
//Silly zombie that wears a bucket on its head
class Buckethead : public Zombie {
public:
// Name: Buckethead() - Default Constructor
// Description: Creates a new buckethead type of zombie
// Preconditions: None
// Postconditions: Can create a buckethead
Buckethead();
// Name: Buckethead(string name, int hp)
// Description: Creates a new buckethead
// Preconditions: None
// Postconditions: Can create a buckethead
Buckethead(string, int);
// Name: SpecialAttack
// Description: Defines the Buckethead's special attack
// Preconditions: None
// Postconditions: Returns damage from special attack
int SpecialAttack();
};
#endif
Engineer.h
#ifndef _ENGINEER_H_
#define _ENGINEER_H_
#include "Zombie.h"
//A zombie that was created in ITE or Engineering building :)
class Engineer : public Zombie {
public:
// Name: Engineer() - Default Constructor
// Description: Creates a new engineer
// Preconditions: None
// Postconditions: Can create a engineer
Engineer();
// Name: Engineer(string name, int hp)
// Description: Creates a new engineer
// Preconditions: None
// Postconditions: Can create a engineer
Engineer(string, int);
// Name: SpecialAttack
// Description: Defines the Engineer's special attack
// Preconditions: None
// Postconditions: Returns damage from special attack
int SpecialAttack();
};
#endif
Imp.h
#ifndef _IMP_H_
#define _IMP_H_
#include "Zombie.h"
//A little mean looking zombie
class Imp : public Zombie {
public:
// Name: Imp() - Default Constructor
// Description: Creates a new imp
// Preconditions: None
// Postconditions: Can create an imp
Imp();
// Name: Imp(string name, int hp)
// Description: Creates a new imp
// Preconditions: None
// Postconditions: Can create an imp
Imp(string, int);
// Name: SpecialAttack
// Description: Defines the Imp's special attack
// Preconditions: None
// Postconditions: Returns damage from special attack
int SpecialAttack();
};
#endif
proj4.cpp
#include < iostream>
#include < cstdlib>
#include < string>
#include < ctime>
#include "Game.h"
using namespace std;
int main(int argc, char *argv[])
{
if (argc != 2)
{
cout << "This requires a room file to be loaded." << endl;
cout << "Usage: ./proj4 map2_data.txt" << endl;
}
cout << "Loading file: " << argv[1] << endl << endl;
string mapName = argv[1];
srand(time(NULL));
Game g(mapName);
return 0;
}