So I've been working on a small console game for about a week now, and I would like some feedback on it. I consider it finished, it does exactly what I want it to do. What could be improved? done better?
Here is the code and here is a download for the entire VS 2019 project if you wish to look at that instead: Monster Fight
main.cpp
Code:
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <iostream>
#include <string>
#include <ostream>
#include <vector>
#include <random>
#include <limits>
#include "Character.h"
#include "Player.h"
#include "Enemy.h"
#include "Attack.h"
using std::cout;
using std::endl;
using std::string;
using std::ostream;
using std::vector;
using std::default_random_engine;
using std::uniform_int_distribution;
using std::random_device;
using std::cin;
using std::numeric_limits;
using std::streamsize;
using std::getline;
using std::mt19937;
int main()
{
random_device rd;
default_random_engine generator(rd());
//AttackObjectName(Attack Name, Attack Power)
Attack Punch("Punch", 3);
Attack Kick("Kick", 1);
Attack Slash("Slash", 2);
Attack BodySlam("Body Slam", 4);
Attack Missed("Missed Attack", 0);
vector<Attack> playerAttacks;
playerAttacks.reserve(4);
playerAttacks.push_back(Punch);
playerAttacks.push_back(Kick);
playerAttacks.push_back(BodySlam);
playerAttacks.push_back(Missed);
uniform_int_distribution<int> randomPlayerAttacks(0, playerAttacks.size() - 1);
vector<Attack> enemyAttacks;
enemyAttacks.reserve(3);
enemyAttacks.push_back(Slash);
enemyAttacks.push_back(BodySlam);
enemyAttacks.push_back(Missed);
uniform_int_distribution<int> randomEnemyAttacks(0, enemyAttacks.size() - 1);
Player Hero("Hero", 100, playerAttacks, 0, 0);
uniform_int_distribution<int> randomMoneyReward(0, 80);
uniform_int_distribution<int> randomXPReward(0, 40);
//EnemyObjectName(Enemy Name, Health, AttackList, XP to Give, Money)
Enemy Dragon("Dragon", 70, enemyAttacks, 40, 0);
Enemy Skeleton("Skeleton", 10, enemyAttacks, 20, 0);
Enemy Troll("Troll", 25, enemyAttacks, 30, 0);
Enemy GiantRat("Giant Rat", 15, enemyAttacks, 25, 0);
Enemy Raptor("Raptor", 35, enemyAttacks, 15, 0);
vector<Enemy> enemyContainer;
enemyContainer.reserve(5);
enemyContainer.push_back(Dragon);
enemyContainer.push_back(Skeleton);
enemyContainer.push_back(Troll);
enemyContainer.push_back(GiantRat);
enemyContainer.push_back(Raptor);
int choice{ 0 };
int turn{ 1 };
while (choice != -1)
{
choice = 0;
//Choose a random enemy and set it inside vector, if we dont do this
//A random enemy will be chosen for each call in the fight, we want
//the same eney for the duration of the fight.
uniform_int_distribution<int> chooseRandomEnemy(0, enemyContainer.size() - 1);
Enemy randomEnemy{ enemyContainer[chooseRandomEnemy(generator)] };
//Randomly generate a reward and set it for this fight, then re-randomize it
//for the next fight.
randomEnemy.GiveMoney(randomMoneyReward(generator));
randomEnemy.XpToGive(randomXPReward(generator));
cout << "What would you like to do?\n" << endl;
cout << "1) Fight" << endl;
cout << "2) quit" << endl;
cout << "\n> ";
cin >> choice;
switch (choice)
{
case 1:
cin.ignore(numeric_limits<streamsize>::max(), '\n');
//Initial encounter
cout << "\n" << Hero.GetName() << " encountered a " << randomEnemy.GetName() << "!" << endl;
cout << "It has " << randomEnemy.GetHealth() << " Health!\n" << endl;
while (Hero.GetHealth() > 0)
{
//Re-roll player and enemy attacks and power levels for random attacks and powers.
//Find better way to do this.
Attack randomPlayerAttack{ playerAttacks[randomPlayerAttacks(generator)] };
Attack randomEnemyAttack{ enemyAttacks[randomEnemyAttacks(generator)] };
cout << "\nACTION----------------------------------------------------------" << endl;
if (randomPlayerAttack.GetName() == "Missed Attack")
{
cout << Hero.GetName() << "'s attack Missed!" << endl;
}
else
{
cout << Hero.GetName() << " uses " << randomPlayerAttack.GetName();
cout << " against the " << randomEnemy.GetName() << ", and it does ";
cout << randomPlayerAttack.GetPower() << " damage!\n" << endl;
}
randomEnemy.TakeDamage(randomPlayerAttack.GetPower());
if (randomEnemy.GetHealth() <= 0)
{
cout << Hero.GetName() << " defeated " << randomEnemy.GetName();
cout << " and got " << randomEnemy.GetMoney() << " gold and ";
Hero.GiveExperience(randomEnemy.GetXpToGive());
cout << randomEnemy.GetXpToGive() << " experience.\n" << endl;
Hero.GiveMoney(randomEnemy.GetMoney());
cout << Hero.GetName() << "'s Health was fully restored." << endl;
Hero.ResetHealth();
cout << "\n================================================================\n" << endl;
break;
}
if (randomEnemyAttack.GetName() == "Missed Attack")
{
cout << randomEnemy.GetName() << "'s attack missed!" << endl;
}
else
{
cout << randomEnemy.GetName() << " uses " << randomEnemyAttack.GetName();
cout << " against " << Hero.GetName() << ", and it does ";
cout << randomEnemyAttack.GetPower() << " damage!\n" << endl;
}
Hero.TakeDamage(randomEnemyAttack.GetPower());
if (Hero.GetHealth() <= 0)
{
cout << randomEnemy.GetName() << " defeated " << Hero.GetName() << endl;
cout << "\n================================================================\n" << endl;
cout << "\nGAME OVER\n" << endl;
return 0;
break;
}
cout << "\nSTATS=========================================================== Turn: " << turn++ << endl;
cout << Hero.GetName() << '\n' << endl;
cout << Hero.GetName() << "'s Health: " << Hero.GetHealth() << endl;
cout << Hero.GetName() << "'s Gold: " << Hero.GetMoney() << endl;
cout << Hero.GetName() << "'s Experience: " << Hero.GetExperience() << endl;
cout << '\n' << randomEnemy.GetName() << '\n' << endl;
cout << randomEnemy.GetName() << "'s Health: " << randomEnemy.GetHealth() << endl;
cout << "\n================================================================\n" << endl;
cout << "Press Enter to continue Fight\n" << endl;
cin.get();
}
break;
case 2:
return 0;
break;
default:
cout << "\nInvalid Decision, please enter an integer or valid integer choice.\n" << endl;
cin.clear(); //Clear failure state
cin.ignore(numeric_limits<streamsize>::max(), '\n'); //discard bad characters
}
}
return 0;
}
player.h
Code:
#pragma once// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <iostream>
#include <string>
#include <ostream>
#include <vector>
#include "Attack.h"
#include "Enemy.h"
using std::cout;
using std::endl;
using std::ostream;
using std::string;
using std::vector;
class Player : public Character
{
public:
Player
(
const string& name,
int health,
vector<Attack> attackList,
int money,
int experience
) : Character{ name, health, attackList, money}, mExperience(experience)
{}
int GetExperience() const { return mExperience; }
void GiveExperience(int amount);
friend ostream& operator<<(ostream& os, const Player& player)
{
os << player.GetMoney() << endl;
os << player.GetExperience() << endl;
os << static_cast<const Character&>(player);
return os;
}
private:
int mExperience{ 0 };
};
player.cpp
Code:
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include "Player.h"
void Player::GiveExperience(int amount)
{
mExperience += amount;
}
attack.h
Code:
#pragma once// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <iostream>
#include <string>
#include <ostream>
using std::endl;
using std::ostream;
using std::string;
class Attack
{
public:
Attack(const string& name, double power) : mName(name), mPower(power)
{}
Attack() = default;
string GetName() const { return mName; }
double GetPower() const { return mPower; }
friend ostream& operator<<(ostream& os, const Attack& attack)
{
os << attack.GetName() << endl;
os << attack.GetPower() << endl;
return os;
}
private:
string mName{ "Attack Name" };
double mPower{ 0 };
};
character.h
Code:
#pragma once// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <iostream>
#include <string>
#include <ostream>
#include <vector>
#include "Attack.h"
//#include "Player.h"
using std::endl;
using std::ostream;
using std::string;
using std::vector;
class Enemy;
class Character
{
public:
string GetName() const { return mName; }
int GetHealth() const { return mHealth; }
int GetMoney() const { return mMoney; }
void TakeDamage(Attack& attack);
void TakeDamage(int amount);
void GiveMoney(int amount);
void ResetHealth();
friend ostream& operator<<(ostream& os, const Character& character)
{
os << character.GetName() << endl;
os << character.GetHealth() << endl;
return os;
}
//Protected because we dont want the user to create Characters, we want them to create players.
protected:
Character
(
const string& name,
int health,
vector<Attack> attackList,
int money
) : mName(name),
mHealth(health),
mAttackList(attackList),
mMoney(money)
{}
Character() = default;
private:
string mName{ "Character Name" };
int mHealth{ 100 };
vector<Attack> mAttackList;
int mMoney{ 10 };
};
character.cpp
Code:
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <iostream>
#include <string>
#include <ostream>
#include "Character.h"
#include "Attack.h"
#include "Enemy.h"
using std::cout;
using std::endl;
using std::ostream;
using std::string;
void Character::TakeDamage(Attack& attack)
{
mHealth -= attack.GetPower();
}
void Character::TakeDamage(int amount)
{
mHealth -= amount;
}
void Character::GiveMoney(int amount)
{
mMoney += amount;
}
//This will simply just set health to 100
void Character::ResetHealth()
{
mHealth = 100;
}
enemy.h
Code:
#pragma once// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include <iostream>
#include <string>
#include <ostream>
#include <vector>
#include "Attack.h"
#include "Character.h"
using std::endl;
using std::ostream;
using std::string;
using std::vector;
class Character;
class Enemy : public Character
{
public:
Enemy
(
const string& name,
int health,
vector<Attack> attackList,
double xpToGive,
int money
) : Character{ name, health, attackList, money}, mXpToGive(xpToGive)
{}
int GetXpToGive() const { return mXpToGive; }
void XpToGive(int amount);
private:
int mXpToGive{ 0 }; //How much XP the player will recieve when this enemy is defeated
};
enemy.cpp
Code:
// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com
#include "Enemy.h"
void Enemy::XpToGive(int amount)
{
mXpToGive += amount;
}