Can someone remind me why I can't do this:

int s = b.Activate(war.get_tp(), damage, 50);

//prototype
int Activate ( int &tp, int weaponDamage, int attack );

I get an error at war.get_tp(). It says cannot convert param 1 from int to int&. I need it to be a reference parameter - is that possible without doing something like,

int tmp = war.get_tp()
int s = b.Activate(tmp, damage, 50);

Recommended Answers

All 28 Replies

It's because get_tp() returns a copy of tp as a temporary (and most likely const) int. It doesn't return the actual member.

This should work:

int s = b.Activate(war.get_tp(), damage, 50);

//prototype
int Activate ( [B]const[/B] int &tp, int weaponDamage, int attack );

That does work - but I need to be able to modify tp within the function.

How can I return a reference to the actual member? Maybe if I use a pointer?

Pointers aren't required, simply change the prototype & definition of the function get_tp() to something like:

int& get_tp() { return myint; }

However, If you want to maintain const-correctness (and I think that that's usually a good idea), then you don't have to change the function at all, simply use a std::const_cast<int&>() to temporarily suspend the effect of the const keyword on the variable, so your function call would look like this:

int s = b.Activate(std::const_cast<int&>(war.get_tp()), damage, 50);

// Prototype
int Activate ( int &tp, int weaponDamage, int attack );

As you can see, no changes need be made to your original code other than adding the extra std::const_cast<int&>() statement.

For more information on std::const_cast , see this.

Hope this helps!

commented: Umm...I don't think so. Bad advice -1

That's awesome. Thanks!

However, If you want to maintain const-correctness (and I think that that's usually a good idea), then you don't have to change the function at all, simply use a std::const_cast<int&>() to temporarily suspend the effect of the const keyword on the variable, so your function call would look like this:

The problem I see with this is that it allows the user to pass a constant value to the function, which would then have no effect, depending on the code in Activate.

int Activate( const int& tp, int weaponDamage, int attack );
int result = Activate( [B]5[/B], 6, 7 ); // This is legal, but what is the effect?

I think it is a better idea to make get_tp() return a reference.

@dusktreader

I agree that this can be a potential problem, if the function Activate() was prototyped as such, but then the O/P has mentioned that the value of tp will change inside the function, and if this is the case, then the statement Activate( 5, 6, 7 ); wouldn't even compile.

Besides, the O/P's original prototype of the function Activate() , which looks like:

int Activate( int& tp, int weaponDamage, int attack ); // Note that tp is not const

so you could never really pass a constant value(like 1, 6 etc) to the function.

@OP: whenever you use const_cast, you have a serious design and logic problem in your
code. The cases are very very rare, where it would justify to use const_cast in a
program, and I assure you that you case is not one of them.

What does this function do,
int Activate ( int &tp, int weaponDamage, int attack );

Is it supposed to initialize the data? Why is tp pass by reference? Does it
necessarily have to be passed by references? Should activate be a member function?

Instead of watching you accept a bad advice just to "get it working", I'd rather you
leave with a good advice and a better design.

Activate should reset tp to 0 after it's done. I'm looking at just switching tp to a pointer, so I can pass that along and maybe reset it that way?

My structure goes:

Adventurer ---> int tp
Ability ---> Berserk ---> Activate ---> dostuff, reset tp

No no, no need for pointers here. the original solution posted is fine (without the const_cast non-sense). Just do this:

int& get_tp() { return tp; };
..

int Activate(int& tp, int weaponDamage, int attack );

That's totally fine, as long as it is acceptable in your design that the outside code can modify tp at will. Otherwise, you would have to make it a member function of Adventurer or whatever.

If tp is going to be altered, like that then why even have getter/setters? Its
pointless.

>If tp is going to be altered, like that then why even have getter/setters?
Getters and setters are member functions, you can do more than just get/set a single data member. You can include logging, or debug output without any trouble, whereas if the data members are public/protected, life becomes harder.

>Its pointless.
Pointless as-is, perhaps, but it's not a bad idea to future proof your code in such a way. Though I would agree there's a design issue here, either in the class or in the function being called.

>>You can include logging, or debug output without any trouble, whereas if the data members are public/protected, life becomes harder

Yes I hear the debugging argument from time to time. But from the little information
I got from the OP's code. It does not seem like the type that needs logging.

I mean its a good thing to be able to put breakpoints and perform validation on data members,
but doesn't it seem like its decreasing encapsulation a bit? Its simply letting the properties of the
object available to the public interface.

I mean if OP is looking for "good object oriented design" then I disagree that having
getters and setters is a good thing. One can avoid getters and setters by having a
better design.

>It does not seem like the type that needs logging.
It was an example. I wasn't suggesting that debugging or logging were reasons for accessors in this case, only that your blanket statement about pointlessness wasn't entirely correct.

>I mean if OP is looking for "good object oriented design" then
>I disagree that having getters and setters is a good thing.

"Good object oriented design" is a vague and moving target. You may not agree, but that doesn't make accessors poor design.

>One can avoid getters and setters by having a better design.
In my experience, such a "better design" is often vastly more complicated than necessary. Sure, when accessors can be removed easily and there's a verifiable benefit, it should be considered. But to remove them at all costs for the sake of removing them, that's a symptom of bad design.

I'm pretty sure a getter in my situation is pretty necessary for my circumstance. I have a member function of a class modifying a variable of another class. I don't want to provide unrestricted access to the class through the use of friend, so the only other way I can think of is using a getter to pass a reference.

If someone has a better solution, by all means... I'm open to ideas.

>If someone has a better solution, by all means... I'm open to ideas.
We need more detail. What do the classes do? What's the purpose of class B modifying a data member in class A? Can you post a short example program that exhibits your design? So far I've only seen two lines of code posted (at most), and a less than obvious description of the execution flow.

>>I don't want to provide unrestricted access to the class through the use of friend, so the only other way I can think of is using a getter to pass a reference.

So, ask yourself this question: Is it better to grant complete access to the data members of a class, to another class which you implement? Or is it better to grant access to one data member of a class to the entire software?

You made the second choice... was it wise? Only you can answer that.

I'm just saying that because a big part of making the right design choices is to ask yourself the right questions.

As for the little debate between Narue and firstPerson, I would just say that you are doing the logic flaw of "false generalization". You say that a getter / setter functions are symptoms of bad design (which they are), and thus, the design must be bad (like if you say having freckles is a symptom of skin cancer, so if you see a single freckle on your body you should run to your nearest chemotherapy center!). A getter and setter here and there is nothing to worry about, if you start needing them everywhere, then you have a problem. Even a very good design can have a few flaws here and there, it's not worth the pain to redesign for that ultimate, perfect design just to avoid a few flaws.

Maybe my statement of having getters and setters leads to bad design, was taken a
little to the extreme. And it was my fault that I didn't point out the implicit implication
that I though was obvious for me. So let me clarify, if one has a lot of getters and setters,
its usually a hint of bad design. There are always cases where having the necessary getters and
setters are a need. It is up to the user to decide if having them is worth it?

I am not saying using getters and setters are necessarily bad in itself. I am just saying that
it could be a symptom of a bad design and usually is. And that one should really think
whats the advantage it provides. And does it necessarily need to be part of the interface?

>if one has a lot of getters and setters, its usually a hint of bad design.
That works for me.

commented: agreed ! +2
int s = b.Activate(std::const_cast<int&>(war.get_tp()), damage, 50);

I do not recommended this code through my past C++ experience.
Please note that this practice will lead to thousands of hidden semantic bugs that
you can't even easily track.

The best thing is create a new int and use it there.(and if this is not a premitive data type
like int& then you need to write the copy constructor to do deep copy too).Think what will happen if the
temporary will initialized as just after it returns? :S those are the very hidden
bugs that I really don't like to DEBUG.Sorry for being rude here.But really ugly bugs.
believe me.

No no, no need for pointers here. the original solution posted is fine (without the const_cast non-sense). Just do this:

int& get_tp() { return tp; };
..

int Activate(int& tp, int weaponDamage, int attack );

That's totally fine, as long as it is acceptable in your design that the outside code can modify tp at will. Otherwise, you would have to make it a member function of Adventurer or whatever.

No sorry can't say good to losing the const.The C++ access levels are completely statically checked, and there's no magic way that C++ can detect a access violation
in the dynamic code where both `private` and `public` code runs in a same hardware
privilege level.All the access violations are noted in the compilation time statically,
in most C++ implementations.

so const is a tool for the developer(both library user and the library writer).Breaking
that means hiding the syntactic bugs.(bugs are ugly yes,but static bugs are little prettier).

You can include logging, or debug output without any trouble, whereas if the data members are public/protected, life becomes harder

When you need to generate a snapshot of the object, then it's a responsibility of that
class itself.Unless you are doing serious multi-threading programming ,where the debugger is available to you always you even don't need that snapshot method too.
Using debugger you can inspect the object clearly.(in both gdb and the microsoft debugger,possibly others also).

>if one has a lot of getters and setters, its usually a hint of bad design.
That works for me.

that works fine, of course it does.A good design!. For a example when the object
status changed into some state that you should not use the setter method.Then you
could assert that and say "Hay this can't be happen like this !",or use throw to
throw an exception to the stack down until you reach a catch() deeper in the stack.

The get method restrict the availability and increase the configuration too.

And also decreases the complexity too(by giving up a good solution to avoid code duplication). For a example as soon as the member changed suppose you have to notify
to other object that this member had just changed.Then it's fine write object.NotifyMemberXChanged() or object.SendMessage(MEMBER_X_JUST_CHANGED) to the
second object.

Otherwise you need to write it in many places which leads to code duplication and
the code duplication will leads to lots of BUGS :S.

@NicAx: I've been reading your last few posts... I have to say your language is a bit peculiar. I agree and disagree on what you say, or at least what I understand of what you mean:

>> I do not recommended this code through my past C++ experience.

I don't either, const_cast is never a good idea. It is a last resort (like any other cast function for that matter), and in the OP's case it can be avoided for sure (like the solution I posted, which is not very good either, but better than a const_cast).

>>Think what will happen if the temporary will initialized as just after it returns?

That will never happen, all the parameters sent to a function are evaluated before the function call. And there is no temporary in this case anyways.

>>No sorry can't say good to losing the const.

I can't say it's good either. Returning a non-const reference to a data member of a class is like if the class declares the following semantics: "You can grab my data member and do whatever you want with it, whenever you want." This is valid semantics (not a bug per se) but very poorly designed semantics and ideally should be avoided by design (but if the cost of redesign is too much, then it can be acceptable).

>>The C++ access levels are completely statically checked, and there's no magic way that C++ can detect a access violation in the dynamic code where both `private` and `public` code runs in a same hardware privilege level.All the access violations are noted in the compilation time statically, in most C++ implementations.

Did you think I didn't know that! That's why giving out a non-const reference is a bad idea in general.

>>so const is a tool for the developer(both library user and the library writer).Breaking that means hiding the syntactic bugs.(bugs are ugly yes,but static bugs are little prettier).

Again, of course, const is a tool to catch _semantic_ conflicts at compile-time by turning them into _syntactical_ errors, which is better than searching for them at run-time, of course. But you say "breaking that", you don't break const-correctness by having non-const getters or function ref-parameters, you simply define a different semantic for the class' interface (not a good one in general, but life is never perfect).

>>that works fine, of course it does.A good design!. For a example when the object status changed into some state that you should not use the setter method.Then you could assert that and say "Hay this can't be happen like this !",

You seem to be comparing having a getter/setter pair for a private data member or having a public data member instead. Of course, in that case, the first option is better by design in the sense that you can add some other functionality to the get/set methods (like a debug print, a mutex lock, throwing an exception, etc.). But the point that was discussed is the difference between having get/set methods for private data members or having none at all (no direct access to the private data). In general, for encapsulation and independence of your classes, having a completely self-contained class is better (ideally RAII), with only clear semantics in its interface and enforcing valid object-states at all times. If any state is a valid state and the data contained IS the interface, than public data members are prescribed (or with get/sets to add some functionality). However, a gray area arises when you mostly want to encapsulate the internal data, but for some unsuspected reason, some other functionality in your code requires access to some internal data, which is not desirable in any way, but can happen, then you might have to bend the rules a bit.

BTW, having to throw exceptions or assert an invalid state of your object when a particular method is called, indicates poor semantics. If you know there is a possibility that a user of your class calls a method "illegally" (i.e. when preconditions of the method are not met) and design an assert or exception for it, maybe the semantics of your class' interface are unclear, leaving the user confused and possibly using the class inappropriately.

>>However, a gray area arises when you mostly want to encapsulate the internal data, but for some unsuspected reason, some other functionality in your code requires access to some internal data

Form my bookish knowledge this is why there is a exception that is coming with the `friend` keyword.Please correct me that I'm wrong, since op declared this thread as a open discussion. So like to heard form some real on the job experience.

>>BTW, having to throw exceptions or assert an invalid state of your object when a particular method is called, indicates poor semantics. If you know there is a possibility that a user of your class calls a method "illegally" (i.e. when preconditions of the method are not met) and design an assert or exception for it, maybe the semantics of your class' interface are unclear, leaving the user confused and possibly using the class inappropriately.

This is also something that I read some time ago(another bookish knowledge).
http://www.parashift.com/c++-faq-lite/exceptions.html
There they encouraged using exceptions over the zombie object(making the object
state invalid).This may be too wrong in the real industry always.so you're suggesting
the old method 'zombie states' back? Right? Could you explain more.

>>Form my bookish knowledge this is why there is a exception that is coming with the `friend` keyword.Please correct me that I'm wrong, since op declared this thread as a open discussion. So like to heard form some real on the job experience.

You are right, in your "bookish knowledge" (I like that term). I usually prefer the friend relation with regards to accessing private data members (or in some cases, a nested accessor-class). Because giving full access to a data member with a non-const reference is usually bad because it allows any user to really do anything with the data member. But if the data member cannot break the state of the object, then it is a bit better (of course, you still have the problem that the user could keep the reference to the data member after the containing object has been deleted, which is a horrible bug, but still a const reference will have the same problem). Usually in practice, when you need to use this technique (bending the rules of best practices), you usually give proper comments (doxygen: \pre \post \note \throw, etc.) to warn about the dangers of using the method inappropriately. A friend relationship is better because you can control which classes can temper with the data members, and usually that class is also implemented by you and you can make sure it's safe. But of course, ideally, no friends and no getter/setter for data members that can break the state of the object is always preferred, but rarely completely avoidable. Again, in practice, proper documentation about the usage of the class can help a lot.


>> This may be too wrong in the real industry always.so you're suggesting
the old method 'zombie states' back? Right? Could you explain more.

No, the other way around, I am saying that zombie states should be avoided altogether. Of course, very often you cannot, again life is not perfect, you need to throw exceptions or have some failure-state behavior. RAII (Resource Allocation Is Initialization) is a technique that tries to avoid this problem. I'll give an example. Say you are implementing a class to open and read the content of a file, you have essentially those two choices of interface for the class:

//Procedural implementation:
class file_input {
   ..
  public:
    file_input(); //default constructor. initial state is invalid.
    bool open(const std::string& fileName) throw(no_such_file); //opens a file, state is valid afterwards.
    int read(char* buffer, int size) throw(file_not_open); //reads "size" bytes from file into buffer. Throws file_not_open if you did call open() before.
    void close(); //close the file. Does nothing if no file is open.
    ~file_read(); //destructor, closes file if still open.
};

//RAII implementation:
class file_read {
    ..
  public:
    file_read(const std::string& fileName) throw(no_such_file); //constructor opens the file, throws if non exist.
    int read(char* buffer, int size) throw(); //reads "size" bytes from file into buffer. no throw.
    ~file_read(); //default constructor.
};

//in the main() or wherever else.
..
  //procedural version:
  file_read fin;
  // need a try-catch for the no_such_file case when opening it.
  try {
    fin.open("myfile.bin");
  } catch (no_such_file e) {
    std::cerr << "error! invalid file name!" << std::endl;
  };
  // with read(), you technically still need a try-catch for the file_not_open case because the class' interface says so.
  try {
    ...
    fin.read(...);
    ...
  } catch (file_not_open e) {
    std::cerr << "The file was not opened!" << std::endl;
    return;
  };
  fin.close();

  //RAII version:
  {
    //still need a try-catch for the no_such_file.
    try {
      file_read fin("myfile.bin");
    } catch(no_such_file) {
      std::cerr << "error! invalid file name!" << std::endl;
      return;
    };
    //but now you are free for the reading operations, because a zombie-state is impossible by the interface of the class.
    ...
    fin.read(...);
    ...
  }; //the closing bracket deletes fin by making it go out-of-scope.
..

Maybe the above example is not the best, but the idea is that it is better to design in a way that rules out the possibility of a zombie-state, that is generally more robust to misuse by other programmers using your code. Also, in the above, you get the additional information in the RAII version that says that the operation can only fail (not open) if the file does not exist, while the procedural version's read function has ambiguous behavior because it has to consider the possibility that the user ignores the no_such_file exception on open() or doesn't call open() at all, and still tries to read the content. In that case, you would have to put a comment in the code saying "the only reason for read() to throw file_not_open is if the open() function was not called or not successful", this way the user could skip the second try-catch block, but this is not a nice way to enforce the semantics of your class, because you require the user to be well informed which they are usually not. It's in the same vein as const-correctness, try to convey the interface through the code as much as possible (it's more robust), and you can always double-up with a comment too. But in general, in industry at least, programmers prefer to be able to just look at the synopsis of a class and be able to deduce all the important information about how to use it, they don't like to have to dig up the in-declaration comments (even doxygen documentation) or worse, having to look into the source code. Imagine if you had to look through the source code of the STL every time you use one of the containers, usually looking at the first page in the reference webpage on STL for that container is sufficient, because the interfaces are clear and most of the time if you misuse them, they don't get compiled (cause compile-time errors).

To get a bit more to the original topic here -

As I'm sure you've realized by now, I'm just piddling around with a small text rpg.

Here are a couple of headers to demonstrate some of the design.

//Ability.h

#pragma once
#include "Standard Libs.h"
#include "Adventurer.h"
#include "BattleSystem.h"
#include "Weapon.h"

class Ability
{
public:
	Ability();
};

class Berserk : public Ability
{
public:
	Berserk();
	int Use ( int &tp, int weaponDamage, int attack );
};

class RagingAxe : public Ability
{
public:
	RagingAxe();
	int Use ( int &tp, int weaponDamage, int attack);
};

class Guardian : public Ability
{
public:
	Guardian();
	void Activate ( int lvl, int &att, int &def, int &tpp ) ;
	void Deactivate ( int lvl, int &att, int &def, int &tpp ) ;
};

class Shielding : public Ability
{
public:
	Shielding();
	void Activate ( int lvl, int &att, int &def );
	void Deactivate( int lvl, int &att, int &def );
};

class Agression : public Ability
{
public:
	Agression();
	void Activate ( int lvl, int &att, int &def );
	void Deactivate( int lvl, int &att, int &def );
};

class Intellect : public Ability
{
public:
	Intellect();
	void Activate ( int lvl, int &mpp, int &def );
	void Deactivate ( int lvl, int &mpp, int &def );
};
//Adventurer.h
#pragma once
#include "Standard Libs.h"
#include "Ability.h"
#include "Spell.h"
#include "Item.h"

class Adventurer
{
private:
	string name;

	int level;
	int xp;
	int gold;
	int attack;
	int defence;
	int max_hp;
	int hp;
	int max_mp;
	int mp;
	int max_tp;
	int tp;

	int ability_uses;
	/*
	vector<Spell> spells;
	vector<Item> items;
	vector<Ability> abilities;
	*/
protected:
	Adventurer();
	Adventurer(string user);

	virtual void set_name(string user) { name = user; }
	virtual void set_level(int lvl) { level = lvl; }
	virtual void add_xp(int exp) { xp += exp; }
	virtual void add_gold(int gld) { gold += gld; }
	virtual void remove_gold(int gld) { gold -= gld; }
	virtual void add_hp(int cure) { hp += cure; }
	virtual void remove_hp(int damage) { hp -= damage; }
	virtual void add_mp(int mana) { mp += mana; }
	virtual void remove_mp(int mana) { mp -= mana; }
	virtual void add_tp(int tpp) { tp += tpp; }
	virtual void reset_tp(int tpp) { tp = 0; }

public:
	virtual int& get_tp() { return tp; }
	virtual int& get_ability_uses() { return ability_uses; }

	/*
	virtual void add_spell(Spell);
	virtual void add_item(Item);
	virtual void add_ability(Ability);
	*/
public:
	virtual void set_base(int att, int def, int maxtp, int maxhp, int maxmp);
	void print();
};
#pragma once
#include "Standard Libs.h"
#include "Adventurer.h"

class Warrior : public Adventurer
{
public:
	Warrior(string user);
};

Ok, so here's an example ... but it's just a test environment.

#include "Standard Libs.h"
#include "Warrior.h"
#include "Mage.h"
#include "Paladin.h"
#include "GoblinRecruit.h"
#include "PawnLizzard.h"
#include "EarthFairy.h"
#include "Ability.h"
#include "BattleSystem.h"
#include "Weapon.h"

void InitializeEnvironment()
{
	srand (time(0));

	int con = 1;
	while(con)
	{
		Warrior war("Me");
		Mage mage("You");
		Paladin pld("Them");
	
		//war.print();
		//pld.print();
		//mage.print();

		Weapon wpn("Great Axe", 15);
		Berserk b;
		int s = b.Use(war.get_tp(), wpn.get_dmg(), 50);

		RagingAxe r;
		//cout << "\n\nRaging Axe:  " << r.Use(war.get_tp(), wpn.get_dmg(), 50) << endl ;

		cout << "\n\t\tcontinue (no = 0):  ";
		cin >> con;
	}
}

Probably doesn't help at all... but meh

Hmm, I think it makes more sense for the adventurers to have their own list of usable abilities. That follows the more standard progression where new abilities can be learned, and it saves you the trouble of trying to determine which abilities are usable by which characters when there's no solid relationship between the two.

The details can certainly be ironed out more, but here's a rough idea of what I mean in the form of a basic battle system:

#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <boost/shared_ptr.hpp>

class Actor;

class Ability {
    std::string _name;
public:
    Ability(const std::string& name): _name(name) {}
    const std::string& DisplayName() const { return _name; }
    virtual void Use(Actor& user, Actor& target) = 0;
};

class Actor {
    typedef boost::shared_ptr<Ability> AbilityPtr;
    typedef std::map<std::string, AbilityPtr> AbilityList;
private:
    std::string _name;
    int _health;
    int _attack;
    AbilityList _abilities;
public:
    Actor(const std::string& name, int attack)
        : _name(name), _health(30), _attack(attack)
    {}

    const std::string& Name() const { return _name; }
    int Health() const { return _health; }
    int AttackDamage() const { return _attack; }
    void AdjustHealth(int difference) { _health += difference; }

    bool HasAbility(const std::string& ability_name)
    {
        return _abilities.find(ability_name) != _abilities.end();
    }

    void AddAbility(Ability *ability)
    {
        if (!HasAbility(ability->DisplayName()))
            _abilities.insert(std::make_pair(ability->DisplayName(), AbilityPtr(ability)));
    }

    void UseAbility(Actor& target, const std::string& ability_name)
    {
        if (HasAbility(ability_name))
            _abilities[ability_name]->Use(*this, target);
    }
};

class PrimaryAttack: public Ability {
public:
    PrimaryAttack(): Ability("Primary Attack") {}

    virtual void Use(Actor& user, Actor& target)
    {
        bool crit = rand() < RAND_MAX / 10;
        int dmg = user.AttackDamage();

        if (crit)
            dmg *= 2;

        target.AdjustHealth(-dmg);
        std::cout<< user.Name() <<" uses "<< DisplayName() <<" on " << target.Name() <<".\n";

        if (crit)
            std::cout<<"CRITICAL ATTACK!\n";

        std::cout<< target.Name() <<" sustains "<< dmg <<" points of damage!\n";
    }
};

class BasicHeal: public Ability {
public:
    BasicHeal(): Ability("Basic Heal") {}

    virtual void Use(Actor& user, Actor& target)
    {
        if (rand() < RAND_MAX / 3)
            std::cout<< DisplayName() <<" failed.\n";
        else {
            int hp = static_cast<int>(user.AttackDamage() * .3);

            target.AdjustHealth(hp);
            std::cout<< user.Name() <<" casts "<< DisplayName() <<" on " << target.Name() <<".\n"
                     << target.Name() <<" regains "<< hp <<" points of health!\n";
        }
    }
};

namespace {
    const std::string PrimaryAttackName = "Primary Attack";
    const std::string BasicHealName = "Basic Heal";
}

int main()
{
    std::srand(static_cast<unsigned>(std::time(0)));

    Actor pc("Adventurer", 7);
    Actor npc("Imp", 5);

    // Initialize party abilities
    pc.AddAbility(new PrimaryAttack);
    pc.AddAbility(new BasicHeal);

    // Initialize mob abilities
    npc.AddAbility(new PrimaryAttack);

    std::cout<<"Your party has been attacked by 1 "<< npc.Name() <<"!\n";

    bool done = false;

    while (!done) {
        npc.UseAbility(pc, PrimaryAttackName);

        int health_remaining = pc.Health();

        if (health_remaining < 0)
            health_remaining = 0;

        std::cout<< pc.Name() <<" has "<< health_remaining <<" health remaining.\n";

        if (health_remaining == 0) {
            std::cout<<"Your party has fallen.\nGame Over.\n";
            done = true;
            continue;
        }

        std::cout<<"1) Attack!\n2) Heal\n3) Run away\nWhat will you do? ";

        int choice = 0;

        switch (std::cin>> choice, choice) {
        case 1:
            pc.UseAbility(npc, PrimaryAttackName);

            if (health_remaining <= 0) {
                std::cout<<"The "<< npc.Name() <<" has been slain!\n";
                done = true;
            }
            break;
        case 2:
            pc.UseAbility(pc, BasicHealName);
            break;
        case 3:
            if (rand() < RAND_MAX / 10) {
                std::cout<< pc.Name() <<" ran away like a coward.\n";
                done = true;
            }
            else {
                std::cout<< pc.Name() <<" tried to run away, but was too slow.\n";
            }
            break;
        default:
            std::cout<<"Invalid choice\n";
        }
    }
}

You just blew my mind.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.