I am trying to write what I expected to be a very simple program for a text-based game where a player unit and an enemy unit deal damage to each other until one unit hits 0 health. However, somehow both the player and the enemy are resetting to full health between each call of my attack function. This makes no sense to me because the unit health correctly updates in order to print within the attack function. Here is my code. I called the attack function multiple times in my code to demonstrate.
#include <iostream>
using namespace std;
class player; // this is to avoid a syntax error in the enemy attack function
class enemy
{
public:
int health;
int strength;
int defense;
enemy(int h, int s, int d);
void attack(player u);
};
class player
{
public:
int health;
int strength;
int defense;
player(int h, int s, int d);
void attack(enemy u);
};
player::player(int h, int s, int d)
{
health = h;
strength = s;
defense = d;
srand(time(NULL));
cout << "You arrive to the battlefield..." << endl;
}
void player:: attack(enemy u)
{
int damage = 0;
damage = strength - u.defense + (rand() % 3);
u.health -= damage;
cout << "You strike the enemy and deal " << damage << " damage. The enemy now has " << u.health << " health.\n" << endl;
}
enemy::enemy(int h, int s, int d)
{
health = h;
strength = s;
defense = d;
cout << "An enemy has appeared!" << endl;
}
void enemy::attack(player u)
{
int damage = 0;
damage = strength - u.defense + (rand() % 6);
u.health -= damage;
cout << "The enemy strikes you and deals " << damage << " damage. You now have " << u.health << " health.\n" << endl;
}
void battle()
{
player p(50,35,20);// Both healths are set at 50
enemy e(50,34,19);
p.attack(e); // Target health resets after each attack function runs. Unsure why.
//cout << e.health << endl;
e.attack(p);
p.attack(e);
e.attack(p);
p.attack(e);
e.attack(p);
}
int main()
{
battle();
return 0;
}
It's simple enough. Your code is copying your objects and you are altering the hit points of the copied objects, not the original objects in battle. That's why it seems to reset when you go back to battle. You're making the common but incorrect assumption that C++ objects are references, but they are not, they are values and they do get copied.
Change this
void enemy::attack(player u)
to this
void enemy::attack(player& u)
The extra & makes u a reference, and not a copy, so the changes you make will affect the original object, not a copied object. Similar changes needed elsewhere.
Depending on if you only have one copy of the player object vs multiple you can just simply make void enemy::attack() without an argument.
If there's only one player ever, then you can just simply do e.attack() and not pass in the value and reference the player object.
Player could easily be a singleton class (since there's only one copy of it, ever). If there are multiple copies of Player or multiple players then this wouldn't work clearly.
Related
I have been trying to access a series of integers from the main class, and then display them inside a method.
However, I've been having a bit of trouble with this due to my own ineptitude with a language I've not been coding in for too long.
After quite a bit of searching, I have been unable to find anything that can help me. How would I go about doing this, if it's possible at all?
#include "inventory.h"
inventory::inventory(){
int maxhealth = 100;
int maxmana = 0;
int health = 100;
int mana = 0;
int level = 1;
int agility = 1;
int strength = 1;
int healthpotions = 0;
int manapotions = 0;
int armourlevel = 0;
int weaponlevel = 0;
int crystals = 0;
int gold = 0;
int rock = 0;
int wood = 0;
}
string inventory::getinv(){
return inventory; //I know this sort of return thing won't work, just a placeholder until I figure out what to do.
}
This is what I have been using thus far, but I'm having a hard time even getting that to not display the "Member 'X' was not initialized in this constructor." I'm clearly doing something quite wrong.
Inventory.h:
#ifndef INVENTORY_H_
#define INVENTORY_H_
#include <iostream>
class inventory{
private:
int maxhealth;
int maxmana;
int health;
int mana;
int level;
int agility;
int strength;
int healthpotions;
int manapotions;
int armourlevel;
int weaponlevel;
int crystals;
int gold;
int rock;
int wood;
public:
inventory();
string getinv();
};
#endif /* INVENTORY_H_ */
EDIT: Thanks to the help so far I've been able to get rid of most of the errors. The only one left is "..\src\zoria.cpp:1616:36: error: 'rock' was not declared in this scope"
You do not need the type in the constructor to access the member variables, otherwise the compiler would think that you are trying to declare new local variables.
Here is a basic correction for the inventory.cpp file:
#include "inventory.h"
inventory::inventory(){ // this is the constructor
maxhealth = 100;
maxmana = 0;
health = 100;
mana = 0;
level = 1;
agility = 1;
strength = 1;
healthpotions = 0;
manapotions = 0;
armourlevel = 0;
weaponlevel = 0;
crystals = 0;
gold = 0;
rock = 0;
wood = 0;
}
string inventory::getinv(){
return "inventory"; //I know this sort of return thing won't work, just a placeholder until I figure out what to do.
}
Note the "" added to the placeholder (return "inventory") to make it a valid string.
Note: You cannot access the variable that is in a class directly without an object of the class, even if it is declared public (static being the only exception). However you have declared all your member variables as private and will thus require getter and setter functions to access their values.
EDIT:
Classes are like containers which contain things. But just the definition of a class is just like a stencil which can be used to create objects of that type. The actual existence of the object occurs when you write inventory someobject;.
Now every class has a special function called the constructor which goes by the name of the class itself and is called as soon as the an object of this class is declared. You can initialize all member variables of the class in the constructor.
To access the members of a class you have to use the . dot operator. Members have to be declared public if they need to be directly accessed outside the class' body.
So you change the class definitions like this:
inventory.h:
#ifndef INVENTORY_H_
#define INVENTORY_H_
class inventory{
public:
int maxhealth;
int maxmana;
int health;
int mana;
int level;
int agility;
int strength;
int healthpotions;
int manapotions;
int armourlevel;
int weaponlevel;
int crystals;
int gold;
int rock;
int wood;
inventory();
void printinv();
};
#endif /* INVENTORY_H_ */
and
inventory.cpp:
#include "inventory.h"
#include <iostream>
using namespace std;
inventory::inventory()
{
maxhealth = 100;
maxmana = 0;
health = 100;
mana = 0;
level = 1;
agility = 1;
strength = 1;
healthpotions = 0;
manapotions = 0;
armourlevel = 0;
weaponlevel = 0;
crystals = 0;
gold = 0;
rock = 0;
wood = 0;
}
void inventory::printinv(){
cout << "LEVEL: " << level << endl;
cout << "HEALTH: " << health << endl;
cout << "MANA: " << mana << endl;
cout << "AGILITY: " << agility << endl;
cout << "STRENGTH: " << strength << endl;
cout << endl;
cout << "HEALTH POTIONS: " << healthpotions << endl;
cout << "MANA POTIONS: " << manapotions << endl;
cout << endl;
cout << "ARMOUR LEVEL: " << armourlevel << endl;
cout << "WEAPON LEVEL: " << weaponlevel << endl;
cout << "CRYSTALS: " << crystals << endl;
cout << endl;
cout << "GOLD: " << gold << endl;
cout << "ROCK: " << rock << endl;
cout << "WOOD: " << wood << endl;
}
Now declare the object of the class in main like:
inventory inv;
and access every member variable (such as maxhealth, maxmana, health, mana, level, agility, strength, healthpotions, manapotions, armourlevel, weaponlevel, crystals, gold, rock and wood) like:
inv.gold = 10;
inv.rock++;
etc. throughout the code.
and to display the inventory replace all the redundant display code with:
inv.printinv();
everywhere.
See zoria.cpp here, I have done the changes for all display code and changed the variable accesses for: maxhealth, maxmana, health and healthpotions you also have to do it for the rest of the variables like: mana, level, agility, strength, manapotions, armourlevel, weaponlevel, crystals, gold, rock and wood.
Hope this helps. Tell if there are any more questions.
In Inventory.h you define the member variables (maxhealth, maxmana etc) and in Inventory.cpp, you should declare them in its constructor. The problem is that you're redeclaring them. Try removing the "int" from every variable in the constructor because you only need to specify the type when you define the variable.
A few points here:
In you constructor you are shadowing the instance variables by method-local re-definition of identically named variables. Please, look up on how to initialize instance member variables in constructors.
You try to implicitly convert an instance of your inventory class into a std::string with the signature of your getinv() method, but you have not defined any operator for this kind of conversion.
(probably related to (2)) From the error you posted, I assume you are using getinv() something along the line of
inventory my_inventory{};
int inv = my_inventory.getinv();
This will not work as there is no implicit conversion from std::string to int defined. Change the return type of inventory::getinv() to int and return an actual int there.
I am just started learning OOP concepts and to help myself learning, I have created a Characters class. From this class I have made instance called main and an instance called monster. Here is the code for the class:
#include <iostream>
#include <string>
using namespace std;
class Character {
public:
string name;
float health;
int attackLevel;
int defenseLevel;
void setAttr(string sName,float sHealth, int sAttackLevel, int sDefenseLevel) {
name = sName;
health = sHealth;
attackLevel = sAttackLevel;
defenseLevel = sDefenseLevel;
}
void attack(int whatInstanceToAttack) {
whatInstanceToAttack.hitpoints -= 20; //obviously not valid but how do i do this?
return whatInstanceToAttack;
}
int defend(string defend) {
int damageRelieved = defenseLevel * 2;
return damageRelieved;
}
};
int main() {
Character main;
Character monster;
main.setAttr("Rafael",200,100,30);
monster.setAttr("Monster1",30,40,30);
cout << "Default Values for Raf are;" << endl;
cout << main.name << endl;
cout << main.health<< endl;
cout << main.attackLevel << endl;
cout << main.defenseLevel << endl;
cout << "Default values for monster are" << endl;
cout <<monster.name << endl;
cout <<monster.health << endl;
cout << monster.attackLevel<< endl;
cout << monster.defenseLevel << endl;
return 0;
}
Basically what I want to do is somehow access the monster instance via the main instance. I want to do this by running the attack method. So if I run
main.attack(monster);
then I want the monster to lose 20 hitpoints.
How do I go about doing this?
All you need is to pass reference of Character in attack method.
I think you must be aware of pass by value and pass by reference concept. If not you can read it here
void attack(Character &whatInstanceToAttack) {
whatInstanceToAttack.hitpoints -= 20; //obviously not valid but how do i do this?
}
Yes you can access the variables of an instance from another instance of the same class. You need to use a reference to the object to ensure the changes are reflected in the other instance. So here is what your attack function should look like.
void attack(Character &c)
{
c.hitpoints - = 20;
}
Now when you call main.attack(monster) from the main() function, the hitpoints of monster will get decremented by 20.
As a side note, it is considered a good practice to make the data members of a class private, to avoid illegal access/modification of the data. Always use the member functions as an interface to your class instances.
overload the method attack and you can pass by value or reference as per your requirement.
void attack(Character chr)
or
void attack(Character &chr)
I'm working on a simple console application to prototype a method of calculating battle between two large units of medieval soldiers. The idea is I'll set up the calculations, and run the program several times with different parameters to get a feel for it and if it's what I want. Anyways, on to the problem.
Each of the two units is represented by a Unit class. The class includes variables for "attack", "defense", "tactics", and "unit size" as well as a few others which I'm commenting out and ignoring until I fix this issue. Here's the code for the class header:
#ifndef UNITS_H
#define UNITS_H
#include <string>
#include <iostream>
class Unit
{
public:
int attack;
int defense;
int charge;
int brace;
int tactics;
int unit_size;
public:
bool isAlive;
std::string name;
Unit(std::string name_target)
{
name = name_target;
attack = 1;
defense = 1;
charge = 1;
brace = 1;
tactics = 1;
unit_size = 1;
isAlive = true;
}
float GetAttack() {return (float)attack;}
float GetDefense() {return (float)defense;}
float GetCharge() {return (float)charge;}
float GetBrace() {return (float)brace;}
float GetTactics() {return (float)tactics;}
float GetSize() {return (float)unit_size;}
void SetAttack(int change) {attack = change;}
void SetDefense(int change) {defense = change;}
void SetCharge(int change) {charge = change;}
void SetBrace(int change) {brace = change;}
void SetTactics(int change) {tactics = change;}
void SetSize (int change) {unit_size = change;}
void TakeCasualties (int casualties);
int Defend(Unit enemy, float base_chance, float base_multiplier);
void DisplayStats();
};
#endif // UNITS_H
Sorry if it's sloppy, but I'm a bit of an amateur.
I would include the .cpp code for the class, but everything in there works fine. I've done test runs with just the default values with no issue. The only problem I have is changing the values after they're constructed.
In main.cpp I have a function AssignStats(Unit unit_target) which I call once the two Units are constructed. I call it on each one in turn. The code for that is as follows:
void AssignStats (Unit unit_target)
{
int x;
cout << unit_target.name << endl;
cout << "Value for attack?" << endl;
cin >> x;
cout << x << endl;
unit_target.SetAttack(x);
cout << "Value for defense?" << endl;
cin >> x;
unit_target.SetDefense(x);
//unit_target.SetCharge(x);
//unit_target.SetBrace(x);
cout << "Value for tactics?" << endl;
cin >> x;
unit_target.SetTactics(x);
cout << "Value for size?" << endl;
cin >> x;
unit_target.SetSize(x);
}
As far as I can tell, this code should work. However, when I display the stats of each Unit afterwards, it shows the default values put in by the constructor. For the life of me I can't figure out why my Set functions aren't working. I rewrote the code a little earlier to check if they were getting input right, like so:
void Unit::SetAttack()
{
int x;
std::cout << "Input target value for attack." << std::endl;
std::cin >> x;
std::cout << x;
attack = x;
std::cout << attack;
}
I would input 10, and each of the couts would show me 10 right back, even the one that supposedly displayed the attack variable of the class, but later on when I called DisplayStats(), it would show everything to be at default values again.
Can someone more experienced than me please clear this up?
I think that the problem is that function AssignStats accepts the argument by value
void AssignStats (Unit unit_target);
That is the function deals with a copy of the original object. The original object itself is not changed.
Change it the following way
void AssignStats (Unit &unit_target);
I tested your Set functions and they are working fine. I think the problem is the
void AssignStats (Unit unit_target);
You are passing the Unit parameter by value, not by reference. This causes a local copy of the Unit object to be made, which initializes it with new parameters.
You need to call by reference:
void AssignStats (Unit &unit_target);
That way, a reference to the original object is passed and your Set() function assignments will work on it.
Some additional advice beyond the question:
In C++ passing parameters by reference is usually preferable to passing by value, because the overhead of passing a large object by value due to copying can be substantial.
If you are using setter/getter methods to set parameters, those parameters should be declared private or protected, not public. That's one aspect of object oriented encapsulation - don't allow the parameters to be changed outside the object.
e.g you should have
//or protected if you will subclass Unit later
private:
int attack;
int defense;
int charge;
int brace;
int tactics;
int unit_size;
So I'm working on a program which is a game of Blackjack. I have a class called Player for each player in the game. The problem lies in this block of code here which is inside Player.cpp:
void Player::SetFunds(int amt){
cout << "setting funds:" << endl;
cout << m_funds << "m_funds" << endl;
m_funds = amt;
cout << "done" << endl;
}
When compiled, I get a segmentation fault. The line "setting funds:" prints, but I cannot print m_funds, nor can I set it to amt (I tried it before adding the print statement for m_funds).
Here's the declaration of the function in Player.h (which is included in Player.cpp).
void SetFunds(int amt);
And here is where I have m_funds, among other private variables in the Player class.
private:
char* m_name;
int m_funds;
int m_bet;
Hand m_hand;
bool m_busted;
};
Is there something I'm missing? Why can't I seem to access a private Player class variable even though I am inside a Player class function? Is it possible the error is elsewhere even though I get a segmentation fault right after the "setting funds" prints?
Also, I can change the private variables in the constructor just fine, as follows:
Player::Player(char *name, int amt){
m_name = name;
cout << amt << endl; //Amount prints as 100
m_funds = amt; // sets m_funds to 100
cout << m_funds << endl; //m_funds prints as 100 just fine
m_bet = 0;
m_hand = Hand();
m_busted = false;
}
EDIT:
Here is where I call the function SetFunds inside Blackjack.cpp:
(m_players is a vector of Players as a private variable of Blackjack class.) Again, amt couts to 100.
void Blackjack::SetPlayerFunds(int player, int amt){
cout << amt << endl;
m_players[player].SetFunds(amt);
}
And here is where I call that function from Project.cpp (which contains main):
(Note: This is for a project where I cannot edit this file and have to base my code around it)
Blackjack *CreateGame(int argc, char *argv[]) {
char **names;
int *funds;
int numPlayers;
Blackjack *game;
numPlayers = ProcessArgs(argc - 1, &argv[1], names, funds);
game = new Blackjack(names, numPlayers);
for (int p = 0; p < numPlayers; p++) {
game->SetPlayerFunds(p, funds[p]);
}
EDIT 2:
Here is the Blackjack class constructor which is called inside Blackjack::CreateGame.
Blackjack::Blackjack(char *names[], int numPlayers){
std::vector<Player> m_players;
m_dealer = Player();
int amt = 100;
for(int i = 0; i < numPlayers; i++){
Player player(names[i], amt);
m_players.push_back(player);
}
}
Your issue is in the Blackjack constructor.
Blackjack::Blackjack(char *names[], int numPlayers){
std::vector<Player> m_players;
m_dealer = Player();
int amt = 100;
for(int i = 0; i < numPlayers; i++){
Player player(names[i], amt);
m_players.push_back(player);
}
}
You are creating m_player on the heap in the constructor, assigning and adding players, but probably never initializing the real member of the same name in the class...
To solve the issue, remove the std::vector m_players;
Umm, It seems that this pointer is strange..
I suggest this test code.
void Blackjack::SetPlayerFunds(int player, int amt)
{
cout << amt << endl;
// if "m_players" is std::vector
m_players.at(player).SetFunds(amt);
// elif "m_players" is C-array
if (player >= sizeof(m_players)/sizeof(m_players[0]))
abort(); // assert(false) or setting breakpoint will be better, if you can use debugger
m_players[player].SetFunds(amt);
}
This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
So basically, I have to write a program that is kind of like an RPG game where there are different types of creatures. Each creature is an object of the class Creature, and has member variables for hitpoints, strength, etc.
What I am having trouble with is writing the function that handles dealing and taking damage between the classes.
#include <iostream>
#include <string>
#include <stdlib.h>
using std::cout;
using std::cin;
using std::endl;
using std::string;
//Creature Base Class (Type 0)
class Creature
{
public:
Creature(int, int, int);
int gettype() {return type;}
int getstrength(){return strength;}
int gethitpoints() {return hitpoints;}
void sethitpoints(int);
string getspecies();
void printstats();
void dealdamage(Creature, Creature);
void takedamage(int);
private:
int type;
int strength;
int hitpoints;
};
Creature::Creature(int t, int s, int hp)
{
type = t;
strength = s;
hitpoints = hp;
}
void Creature::printstats()
{
cout << "This creature has: " << endl;
cout << strength << " strength" << endl;
cout << hitpoints << " hitpoints" << endl;
cout << "and is of type " << type << "(" << getspecies() << ")" << endl;
}
void Creature::sethitpoints(int a)
{
hitpoints = a;
}
string Creature::getspecies()
{
switch(type)
{
case 0: return "Creature";
case 1: return "Human";
case 2: return "Elf";
case 3: return "Demon";
case 4: return "Balrog";
case 5: return "Cyberdemon";
}
}
void Creature::dealdamage(Creature dealer, Creature target)
{
srand(5);
int damage;
damage = rand() % strength+1;
cout << dealer.getspecies() << " inflicts " << damage;
cout << " damage to " << target.getspecies() << "!" << endl;
target.takedamage(damage);
}
void Creature::takedamage(int damage)
{
sethitpoints((gethitpoints()-damage));
}
int main()
{
Creature monster1(0, 10, 100);
Creature monster2(1, 7, 90);
monster1.printstats();
monster2.printstats();
monster1.dealdamage(monster1, monster2);
monster2.printstats();
return 0;
}
Right now, the output the program gives me is:
This creature has:
10 strength
100 hitpoints
and is of type 0(Creature)
This creature has:
7 strength
90 hitpoints
and is of type 1(Human)
Creature inflicts 5 damage to human!
This creature has:
7 strength
90 hitpoints
and is of type 1(Human)
So the dealdamage() function seems to be working, but the takedamage() function is not properly changing the hitpoints of the creature that is taking the damage.
Any help would be appreciated.
The problem is
void Creature::dealdamage(Creature dealer, Creature target)
Firstly, this is called "pass by value". New "Creature" objects are constructed and the values of the "Creature" objects you call the function with are copied into them. The procedure executes, and these temporary Creature objects EOL - the original Creature objects are never touched.
You need to take a pointer or a reference to the original object. But unless you are intending to support some kind of 3-way fight, you shouldn't be requiring both items anyway -- this is a non-static member function, so it's already operating in the context of one of the creatures, hence the syntax with which you invoked it:
monster1.dealdamage(monster1, monster2);
Change your dealdamage like this:
void Creature::dealdamage(Creature& target) // takes a reference
{
//srand(5); <- this will cause rand() to always return the same value. dont do it.
//int damage; <- don't separate declaration and assignment when you can avoid it.
int damage = rand() % strength+1;
cout << getspecies() << " inflicts " << damage
<< " damage to " << target.getspecies() << "!" << endl;
target.takedamage(damage);
}
You can use this->getspecies() if you find just using getspecies() unclear.
Instead of srand(constant value) try something like 'srand(time(NULL))', or better still, do it once at the start of the program.