I am looking to make this more efficient, how would I Re-write the Enemy class to use inheritance and virtual functions? Including any new child classes.
class Enemy
{
public:
int type; // 0 = Dragon, 1 = Robot
int health; // 0 = dead, 100 = full
string name;
Enemy();
Enemy(int t, int h, string n);
int getDamage(); // How much damage this enemy does
};
Enemy::Enemy() : type(0), health(100), name("")
{ }
Enemy::Enemy(int t, int h, string n) :
type(t), health(h), name(n)
{ }
int Enemy::getDamage() {
int damage = 0;
if (type == 0) {
damage = 10; // Dragon does 10
// 10% change of extra damage
if (rand() % 10 == 0)
damage += 10;
}
else if (type == 1) {
// Sometimes robot glitches and does no damage
if (rand() % 5 == 0)
damage = 0;
else
damage = 3; // Robot does 3
}
return damage;
}
This calculates how much total damage the band will dish out.
int calculateDamage(vector<Enemy*> bandOfEnemies)
{
int damage = 0;
for (int i = 0; i < bandOfEnemies.size(); i++)
{
damage += bandOfEnemies[i]->getDamage();
}
return damage;
}
That's a good start, but with inheritance, you don't need to be so specific. For example, in the enemy class you have an attribute type. If you want to use inheritance, you don't need to specify the type, because the derived class would be the type.
As for your function getDamage(), you can leave it blank and turn it into a virtual function. Putting all of this together, your code should look something like this:
class Enemy
{
public:
int health; // 0 = dead, 100 = full
string name;
Enemy();
Enemy(int t, int h, std::string n);
virtual int getDamage() = 0; // pure virtual function
};
Enemy::Enemy()
: type(0), health(100), name("") {}
Enemy::Enemy(int t, int h, std::string n)
: type(t), health(h), name(n) {}
// class 'Dragon' inherits from class 'Enemy'
class Dragon : public Enemy
{
public:
Dragon() {}
int getDamage()
{
// dragon's damage
}
};
Notice how if you want to create another enemy, you just inherit from the Enemy class. And this way, you can store your characters in an array like this:
vector<Enemy> enemies = {
Dragon(),
Dragon(),
Robot()
};
Related
I'm working on a textual game, I wish to assign a weapon to a player or monster.
I have created a 2d array with min & max damage so I can randomize every hits between min/max.
But I'm stuck for now.
Should I integrate the 2d array in the weapon class ?
How to affect the weapon to each player ?
thanks for any help :)
#include <iostream>
class player{
public:
std::string playerName;
int health;
int maxmana;
int minDegatWeapon;
int maxDegatWeapon;
};
class weapon{
public:
std::string weaponName;
int maxDegatWeapon;
int minDegatWeapon;
};
void createPlayer(player *p, weapon *w){
int weaponSelection = 0;
std::cout << "Player Name ?\n";
std::getline (std::cin,p->playerName);
p->health = 20;
p->maxmana = 80;
std::cout << "Choose your Weapon : 1-Dagger / 2-Sword / 3-Axe ? \n";
std::cin >> weaponSelection;
};
int main(){
player human = {" ", 0, 0, 0, 0};
weapon humanWeapon = {" ", 0, 0};
int weapons[3][2] = {
{3,4}, //dagger
{1,6}, //sword
{0,7} //axe
};
createPlayer(&human, &humanWeapon);
return 0;
}
You can always have class Weapon be an abstract class. There is no "generic weapon" so it would not be appropriate to instantiate an object of type weapon. Instead, have multiple classes inherit from the weapon class, and adjust their min/max value as needed. Further more, you can assign this weapon to the player by adding a weapon to the Player class.
#include <random>
#include <time.h>
class Weapon
{
public:
virtual std::string getWeaponType() = 0;
virtual int generateDamage() = 0;
int minDmg, maxDmg;
};
class Sword : public Weapon
{
public:
Sword()
{
minDmg = 1;
maxDmg = 6;
}
std::string getWeaponType()
{
return "SWORD";
}
int generateDamage()
{
return ( rand() % maxDmg + minDmg );
}
};
class Player
{
public:
Player(int weapon)
{
if(weapon == 1)
{
w = new Sword();
}
//else if(weapon == 2).....
//..................
}
Weapon* getWeapon()
{
return w;
}
private:
Weapon* w;
};
int main()
{
srand(time(NULL)); //random seed
Player p1 = Player(1);
std::cout << p1.getWeapon().generateDamage() << '\n'; //see if it works
}
lets say that i have a vector
vector <Weapon*> W_List;
and those 3 classes
class Weapon {
protected:
string name;
int damage;
int level;
public:
Weapon() {
}
virtual void upgradeWeapon() {
}
};
class Dagger : public Weapon {
public:
Dagger(string _name){
this-> name = _name;
this-> damage = 30;
this-> level = 1
}
void upgradeWeapon(int _dmg,int _lvl){
this->damage = _dmg;
this->level = _lvl;
}
};
class Bow : public Weapon {
private:
int range;
public:
bow(string _name){
this-> name = _name;
this-> damage = 50;
this-> level = 2
void upgradeWeapon(int _dmg,int _lvl,int _range){
this-> damage = _dmg;
this-> level = _lvl;
this-> range = _range;
}
};
int main(int argc,char **argv) {
vector <Weapon*> W_List;
if (argv[1] == "new"){
if(argv[2] == "Dagger"){
Dagger* dagger = new Dagger("argv[3]");
W_List.push_back(dagger);
}
else if (argv[2] =="Bow"){
Bow* bot = new Bow("argv[3]");
W_List.push_back(bow);
}
else if(argv[1] == "Upgrade"){
//search in vector and upgrade the weapon
}
}
So my question is if i give
Upgrade "WeaponName" 70 4
How can i search in the vector for the name of the weapon and upgrade the attritubes of the spesific weapon?
Errors
Hero: undeclared identifier
std::shared_ptr: Hero is not a valid template type argument for parameter _Ty
unary ->: std::shared_ptr does not define this operator or a conversion to a type acceptable to the predefined operator
attackInput, getSkill os not a member of std::shared_ptr
void my::Weapon::hit(std::shared_ptr,std::shared_ptr): cannot convert argument 1 from std::shard_ptr<my::Hero> to std::shared_ptr
Source.cpp
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <windows.h>
#include "Hero.h"
void checkLife(my::Hero* hero)
{
if (hero->is_dead())
{
delete hero;
hero = nullptr;
}
if (hero == nullptr)
{
srand(time(0));
int rand = 1 + std::rand() % 40;
if (rand > 0 && rand <= 10) hero = new my::King();
else if (rand > 10 && rand <= 20) hero = new my::Queen();
else if (rand > 20 && rand <= 30) hero = new my::Troll();
else if (rand > 30 && rand <= 40) hero = new my::Knight();
}
}
int main()
{
my::Hero* hero = nullptr;
my::Hero* enemy = nullptr;
while (true)
{
checkLife(hero);
checkLife(enemy);
hero->attackOutput(std::shared_ptr<my::Hero>(enemy));
enemy->attackOutput(std::shared_ptr<my::Hero>(hero));
system("cls");
std::cout << hero->getName() << "`s health - " << hero->getHealth() << std::endl;
std::cout << hero->getName() << "`s health - " << hero->getHealth() << std::endl;
Sleep(1000);
}
if (hero != nullptr) delete hero;
if (enemy != nullptr) delete enemy;
system("pause");
return 0;
}
Hero.h
#pragma once
#include <memory>
#include <cstdlib>
#include <time.h>
#include <string>
#include "Weapon.h"
namespace my
{
class Hero abstract
{
protected:
std::shared_ptr<Weapon> weapon;
std::string name;
int health;
int skill;
int pressure;
int nobleness;
int beauty;
virtual void attack(int damage)
{
srand(time(0));
this->health -= damage + rand() % 20 - this->getPressurel();
}
public:
Hero(std::string name, int health, int skill, int pressure, int nobleness, int beauty)
{
this->name = name;
this->health = health;
this->skill = skill;
this->pressure = pressure;
this->nobleness = nobleness;
this->beauty = beauty;
}
std::string getName() const
{
return this->name;
}
int getHealth() const
{
return this->health;
}
int getSkill() const
{
return this->skill;
}
int getPressurel() const
{
return this->pressure;
}
int getNobleness() const
{
return this->nobleness;
}
int getBeauty() const
{
return this->beauty;
}
void attackInput(const int damage)
{
this->attack(damage);
}
void attackOutput(std::shared_ptr<Hero> enemy)
{
std::shared_ptr<Hero> thisHero(this);
this->weapon->hit(std::shared_ptr<Hero>(thisHero), enemy);
}
virtual void takeWeapon(std::shared_ptr<Weapon> weapon)
{
this->weapon = weapon;
}
bool is_dead()
{
if (this->health <= 0) return true;
else return false;
}
};
class King : public Hero
{
public:
King() : Hero("King", 300, 2, 4, 10, 15) {}
};
class Queen : public Hero
{
public:
Queen() : Hero("Queen", 300, 2, 4, 10, 15) {}
};
class Troll : public Hero
{
public:
Troll() : Hero("Troll", 300, 2, 4, 10, 15) {}
};
class Knight : public Hero
{
public:
Knight() : Hero("Knight", 300, 2, 4, 10, 15) {}
};
}
Weapon.h
#pragma once
#include "Hero.h"
namespace my
{
class Weapon abstract
{
protected:
const int damage;
int wear;
public:
Weapon(int damage, int weight, int size, int wear) : damage(damage)
{
this->wear = wear;
}
Weapon() : Weapon(1, 1, 1, 1) {}
virtual void setWeaponWear(int wear)
{
this->wear = wear;
}
virtual int getWeaponDamage() const
{
return this->damage;
}
virtual int getWeaponWear() const
{
return this->wear;
}
virtual void hit(std::shared_ptr<Hero> me, std::shared_ptr<Hero> enemy) = 0;
};
class Knife : public Weapon // NOZH
{
protected:
public:
virtual void hit(std::shared_ptr<Hero> me, std::shared_ptr<Hero> enemy)
{
int damage = this->damage * me->getBeauty();
this->wear--;
enemy->attackInput(damage);
}
};
class Bow : public Weapon // LUCK
{
protected:
public:
virtual void hit(std::shared_ptr<Hero> me, std::shared_ptr<Hero> enemy)
{
int damage = this->damage * me->getNobleness();
this->wear--;
enemy->attackInput(damage);
}
};
class Ax : public Weapon // TOPOR
{
protected:
public:
virtual void hit(std::shared_ptr<Hero> me, std::shared_ptr<Hero> enemy)
{
int damage = this->damage * me->getPressurel();
this->wear--;
enemy->attackInput(damage);
}
};
class Sword : public Weapon // MECH
{
protected:
public:
virtual void hit(std::shared_ptr<Hero> me, std::shared_ptr<Hero> enemy)
{
int damage = this->damage * me->getSkill();
this->wear--;
enemy->attackInput(damage);
}
};
}
As pointed out in the comments by #WhozCraig you have a circular dependency. You need Weapon.h in Hero.h and Hero.h in Weapon.h.
The only reason you need Hero.h in Weapon.h. Is for the hit(...) function. The best solution would be to declare the hit function somewhere else. Right now the best place would be to add it to the Hero. A hero has a weapon and attacks with that weapon another hero, a weapon itself can't attack, so just from the semantics it makes more sense to have the hit function inside the hero class.
You can probably get it to compile by splitting the code up into a header (.h) and source file (.cpp) and having a forward declaration of Hero in Weaopon.h. But this will not fix the underlying problem and the design is still flawed and will probably cause even more problems if functionality is extended.
PROBLEM
Exception occurs during program startup and during debugging. Exceptions occur when retrieving data using the get ???? () methods; Weapon class. The exception looks like this:
An exception occurred: violation of reading rights. std :: shared_ptr :: operator -> (...) returned nullptr. The code in Hero.h comments out the problem fragments, method calls.
Main.cpp
#include "Hero.h"
#include <iostream>
#include <windows.h>
#include <cstdlib>
#include <time.h>
Hero* GetSomeHero()
{
switch (1 + rand() % 5)
{
case 1: return new Human(100);
case 2: return new King(300);
case 3: return new Queen(150);
case 4: return new Knight(200);
case 5: return new Troll(250);
}
return new Human(100);
}
int main()
{
srand((unsigned int)time(0));
std::shared_ptr<Hero> hero(GetSomeHero());
std::shared_ptr<Hero> enemy(GetSomeHero());
while (true)
{
hero->attackOutput(*enemy);
enemy->attackOutput(*hero);
if (hero->isDead())
{
std::shared_ptr<Hero> temp(GetSomeHero());
hero.swap(temp);
}
if (enemy->isDead())
{
std::shared_ptr<Hero> temp(GetSomeHero());
enemy.swap(temp);
}
std::cout << *hero << std::endl << *enemy << std::endl;
Sleep(200);
system("cls");
}
system("pause");
return 0;
}
Weapon.h
#pragma once
#include <string>
class Weapon
{
protected:
std::string name;
float damage;
int wear;
public:
Weapon(std::string name, float damage, int wear)
{
this->name = name;
this->damage = damage;
this->wear = wear;
}
virtual void setWear(int wear) = 0;
virtual std::string getName() = 0;
virtual float getDamage() = 0;
virtual int getWear() = 0;
};
class Fist : public Weapon // KULAK
{
protected:
public:
Fist() : Weapon("Fist", 1, 3) {}
virtual void setWear(int wear)
{
this->wear = wear;
}
virtual std::string getName()
{
return this->name;
}
virtual float getDamage()
{
return this->damage;
}
virtual int getWear()
{
return this->wear;
}
};
class Knife : public Weapon // NOZH
{
protected:
public:
Knife() : Weapon("Knife", 5, 5) {}
virtual void setWear(int wear)
{
this->wear = wear;
}
virtual std::string getName()
{
return this->name;
}
virtual float getDamage()
{
return this->damage;
}
virtual int getWear()
{
return this->wear;
}
};
class Bow : public Weapon // LUCK
{
protected:
public:
Bow() : Weapon("Bow", 15, 10) {}
virtual void setWear(int wear)
{
this->wear = wear;
}
virtual std::string getName()
{
return this->name;
}
virtual float getDamage()
{
return this->damage;
}
virtual int getWear()
{
return this->wear;
}
};
class Ax : public Weapon // TOPOR
{
protected:
public:
Ax() : Weapon("Ax", 30, 5) {}
virtual void setWear(int wear)
{
this->wear = wear;
}
virtual std::string getName()
{
return this->name;
}
virtual float getDamage()
{
return this->damage;
}
virtual int getWear()
{
return this->wear;
}
};
class Sword : public Weapon // MECH
{
protected:
public:
Sword() : Weapon("Sword", 25, 8) {}
virtual void setWear(int wear)
{
this->wear = wear;
}
virtual std::string getName()
{
return this->name;
}
virtual float getDamage()
{
return this->damage;
}
virtual int getWear()
{
return this->wear;
}
};
Hero.h
#include "Weapon.h"
#include <iostream>
#include <string>
#include <cstdlib>
class Hero
{
protected:
std::shared_ptr<Weapon> weapon;
std::string name;
float health;
int pressure;
int beauty;
int skill;
int horror;
public:
Hero(std::string name, float health, int pressure, int beauty, int skill, int horror)
{
this->name = name;
this->health = health;
this->pressure = 1 + rand() % pressure;;
this->beauty = 1 + rand() % beauty;
this->skill = 1 + rand() % skill;
this->horror = 1 + rand() % horror;
}
friend std::ostream& operator<<(std::ostream& os, const Hero& obj)
{
os << std::endl
<< obj.name << std::endl
<< "Health: " << obj.health << std::endl
/*<< "Weapon: " << obj.weapon->getName() << "[" << obj.weapon->getDamage() << "]\n"*/
<< "Specifications:\n"
<< "Pressure[" << obj.pressure << "], Beauty[" << obj.beauty << "], Skill[" << obj.skill << "], Horror[" << obj.pressure << "]\n";
return os;
}
bool isDead()
{
if (this->health > 0) return false;
else return true;
}
virtual void weaponСhange() = 0;
virtual void attackOutput(Hero& enemy) = 0;
virtual void attackInput(int damage) = 0;
};
class Human : public Hero
{
protected:
public:
Human(float health) : Hero("Human", health, 1, 1, 1, 1) {}
virtual void weaponСhange()
{
weapon = std::shared_ptr<Weapon>(new Fist);
}
virtual void attackOutput(Hero& enemy)
{
/*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
enemy.attackInput(1 /*+ (int)this->weapon->getDamage()*/);
/*this->weapon->setWear(this->weapon->getWear() - 1);*/
}
virtual void attackInput(int damage)
{
this->health -= damage;
}
};
class King : public Hero
{
protected:
public:
King(float health) : Hero("King", health, 10, 15, 4, 3) {}
virtual void weaponСhange()
{
switch (1 + rand() % 3)
{
case 1: weapon = std::shared_ptr<Weapon>(new Fist);
case 2: weapon = std::shared_ptr<Weapon>(new Knife);
case 3: weapon = std::shared_ptr<Weapon>(new Ax);
case 4: weapon = std::shared_ptr<Weapon>(new Sword);
}
}
virtual void attackOutput(Hero& enemy)
{
/*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
enemy.attackInput(1 + this->pressure + this->skill /*+ (int)this->weapon->getDamage()*/);
/*this->weapon->setWear(this->weapon->getWear() - 1);*/
}
virtual void attackInput(int damage)
{
this->health -= damage;
}
};
class Queen : public Hero
{
protected:
public:
Queen(float health) : Hero("Queen", health, 2, 15, 10, 1) {}
virtual void weaponСhange()
{
/*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
switch (1 + rand() % 2)
{
case 1: weapon = std::shared_ptr<Weapon>(new Fist);
case 2: weapon = std::shared_ptr<Weapon>(new Knife);
case 3: weapon = std::shared_ptr<Weapon>(new Bow);
}
}
virtual void attackOutput(Hero& enemy)
{
/*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
enemy.attackInput(1 + this->beauty + this->skill /*+ (int)this->weapon->getDamage()*/);
/*this->weapon->setWear(this->weapon->getWear() - 1);*/
}
virtual void attackInput(int damage)
{
this->health -= damage;
}
};
class Knight : public Hero
{
protected:
public:
Knight(float health) : Hero("Knight", health, 15, 6, 20, 1) {}
virtual void weaponСhange()
{
/*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
switch (1 + rand() % 2)
{
case 1: weapon = std::shared_ptr<Weapon>(new Fist);
case 2: weapon = std::shared_ptr<Weapon>(new Ax);
case 3: weapon = std::shared_ptr<Weapon>(new Sword);
}
}
virtual void attackOutput(Hero& enemy)
{
if (weapon->getWear() <= 0) this->weaponСhange();
enemy.attackInput(1 + this->skill + this->pressure /*+ (int)this->weapon->getDamage()*/);
/*this->weapon->setWear(this->weapon->getWear() - 1);*/
}
virtual void attackInput(int damage)
{
this->health -= damage;
}
};
class Troll : public Hero
{
protected:
public:
Troll(float health) : Hero("Troll", health, 20, 1, 1, 10) {}
virtual void weaponСhange()
{
/*if (this->weapon->getWear() <= 0) this->weaponСhange();*/
switch (1 + rand() % 1)
{
case 1: weapon = std::shared_ptr<Weapon>(new Fist);
case 2: weapon = std::shared_ptr<Weapon>(new Ax);
}
}
virtual void attackOutput(Hero& enemy)
{
if (weapon->getWear() <= 0) this->weaponСhange();
enemy.attackInput(1 + this->horror + this->pressure /*+ (int)this->weapon->getDamage()*/);
/*this->weapon->setWear(this->weapon->getWear() - 1);*/
}
virtual void attackInput(int damage)
{
this->health -= damage;
}
};
The problem causing the crashes is most probably that you call weapon->getWear() in attackOutput() for Knight and Troll. You do this before you've assigned a weapon to them.
Another problem: You haven't made the bases classes destructors virtual. This means that the smart pointer will only destroy the base class part of the object when it's time to destroy it.
A third problem: Many of your switches assign variables, but don't break so the values will be overwritten over and over again and will finally be assigned the value in the last case. In this case, you'll get a random number, [1, 3] (not [1, 4]), and you'll get a Sword every time.
switch (1 + rand() % 3)
{
case 1: weapon = std::shared_ptr<Weapon>(new Fist); // missing break;
case 2: weapon = std::shared_ptr<Weapon>(new Knife); // missing break;
case 3: weapon = std::shared_ptr<Weapon>(new Ax); // missing break;
case 4: weapon = std::shared_ptr<Weapon>(new Sword); // missing break;
}
Sidenotes:
Prefer weapon = std::make_shared<Fist>(); etc. instead of the above.
Prefer std::unique_ptr over std::shared_ptr unless you actually need reference counted pointers.
Pick one type for damage and health etc. You now use a mix of float and int.
When you override a member function in a subclass, you can skip the virtual specifier (it does not matter) and instead add the override specifier. This will make the compiler complain if the function doesn't actually override a virtual function.
In logical expressions as
bool isDead() {
if (this->health > 0) return false;
else return true;
}
you already have a boolean result inside the if(...) so prefer to return it directly. In this case, you can simply do this instead:
bool isDead() {
return this->health <= 0;
}
Use the <random> random number generators and support functions for better randomization. Since you generate a lot of numbers between 1 and some max value (inclusive), you could make a support function for it. Example:
#include <random>
int my_rand(int max) {
// the generator will only be seeded once since it's static
static std::mt19937 generator(std::random_device{}());
std::uniform_int_distribution<int> dist(1, max); // range: [1, max]
return dist(generator);
}
If you have a default implementation for some of the virtual functions, you can implement it in the base class to save youself a lot of copy/pasting. Only override it in those cases you want something special. Example for a few Weapons:
class Weapon {
private:
// Direct access to member variables is usually not a good idea. Try to keep them
// private.
std::string name;
float damage;
int wear;
protected:
// The constructor can be protected, only subclasses will be able to use it.
Weapon(std::string Name, float Damage, int Wear) : // use the initializer list
name(Name), damage(Damage), wear(Wear)
{}
// virtual destructor
virtual ~Weapon() = default;
public:
virtual void setWear(int Wear) { wear = Wear; }
virtual void applyWear() { --wear; }
virtual const std::string& getName() { return name; }
virtual float getDamage() const { return damage; }
virtual int getWear() { return wear; }
};
class Fist : public Weapon // KULAK
{
public:
Fist() : Weapon("Fist", 1, 3) {}
};
class Knife : public Weapon // NOZH
{
public:
Knife() : Weapon("Knife", 5, 5) {}
};
...
I recently realized how rusty I have gotten at C++ so I decided to work on a small text-based RPG. I have a class for weapons and a class for a player. My default Player constructor is this:
Player::Player()
{
name = "Henry";
hp = 50;
mp = 25;
xp = 0;
Weapon wep = Weapon::Weapon("Club", 5);
}
I have two constructors for my weapons, as follows:
Weapon::Weapon()
{
name = "Hands";
damage = 1;
}
Weapon::Weapon(string n, int d)
{
name = n;
damage = d;
}
My Weapon class:
class Weapon
{
private:
string name;
int damage;
public:
Weapon();
Weapon(string n, int d);
string getName();
int getDmg();
};
The problem, however, is when I call the getName() function in my main file, it returns "Hands". Likewise, getDmg() returns 1. Is this normal or did I do something wrong?
Weapon wep = Weapon::Weapon("Club", 5); is local to your Player constructor, you probably mean wep = Weapon("Club", 5);
or even better:
Player::Player() :
name("Henry"),
hp(50),
mp(25),
xp(0),
wep("Club", 5)
{
}