C++ Console RPG Game

HybridSquirrel

Diamond Member
Nov 20, 2005
6,161
2
81
I'm trying to write a console based RPG game, but I am stuck on the damage function part of it.

The damage portion works fine, it takes ((attack power - defense roll) - armor) and will come up with the correct number. But when I go to modify the enemy hit points it doesn't subtract. It just prints the default value (12 in this case for Goblins).

Is there something wrong with my syntax? I am trying to use inheritance from a base class and as long as I get the attack function it should roll into other classes just fine.

My issue is that the damage is calculated correctly, it just doesn't modify hit points to reflect the damage.


Code:
//Hit Point Function in base class

        void setHP(double hp){

            hitpoints = hp;
        }

        void modHP(double hp, double dmg) //will modify based on attack roll
        {
            hp = hp - dmg;
            hitpoints = hp;
        }

        double getHP(){
            return hitpoints;

        }
         do
                {

                    double ap = rand() % 12; //effectively rolls a 2D6                   
                    double arp = 3; //armor;
                    double hp = 12;


                    cout << "Your attack power was rolled to: " << ap << endl;


                    dp = rand() % 6; //goblin defense roll
                    cout << "Defense power for that enemy is: " << dp << endl; //shows user the defense power for the current enemy
                    gobs->setdefensepower(dp);  //sets goblin defense power
                    double dmg = ((ap - dp) - arp);

                    //will print if there was no damage dealt
                    if (dmg <= 0) {

                        cout << "Your enemy defended your attack!" << endl;
                        break;
                    }


                    else if (dmg > 0) {

                        gobs->modHP(hp, dmg);
                        gobs->setHP(hp);
                        cout << "Enemy health remaining: " << gobs->getHP() << endl;
                    }



                    else if (gobs->getHP <= 0){

                                cout << "You have slain the enemy!" << endl;
                            }

                    }

                    break;

                } while(select == 1);
 

HybridSquirrel

Diamond Member
Nov 20, 2005
6,161
2
81
I tried to re-write it as:
Code:
class Character
{
    public:
        double attackpower;
        double hitpoints;
        double defensep;
        double armorp;

        Character() {};

    virtual void attack(){

    }

    bool death(){
        if(hitpoints <= 0){
            return false;
        }

        else
            return true;
    }

};


class Barbarian : public Character
{

public:
    Barbarian(){

    hitpoints = 12;
    armorp = 0;
    defensep = rand() %6;
    attackpower = rand() % 12;

    }
    void attack(Character*){

        hitpoints -= ((attackpower - armorp) - defensep);
    }


};

class Goblin : public Character
{

public:
    Goblin(){

    hitpoints = 8;
    armorp = 3;
    defensep = rand() % 6;
    attackpower = rand() % 12;

    }
    void attack(Character*){

        hitpoints -= ((attackpower - armorp) - defensep);

    }

};


int main(){

//create characters objects
Barbarian barb;
Goblin gob;


//create pointers from classes to character attributes.
Character *barba = &barb;
Character *gobs = &gob;


//game menu
int f = 0;

do
    {
        int choice = 0;
        cout << "\nPlease select your class: " << endl;
        cout << "1. Barbarian " << endl;
        cout << "2. Goblin " << endl;
        cin >> choice;

        do
        {
            int select;
            //select target
            cout << "Who do you want to attack?" << endl;
            cout << "1. Goblin " << endl;
            cin >> select;

                //attacking goblins
                do
                {

                    barba->attack(&gobs);

                    //will print if there was no damage dealt
                    if (gob.death() == false) {

                        cout << "You have slain the enemy!" << endl;
                        break;
                    }
                    else if (gob.death() == true){
                        cout << "The enemy still lives, attack again. " << endl;
                    }


                } while(select == 1);


        } while (choice == 1);


    }while (f == 0);

return 0;


}



but now I get compile errors:

: In function ‘int main()’:
:160: error: no matching function for call to ‘Character::attack(Character**)’
.cpp:38: note: candidates are: virtual void Character::attack()
 

purbeast0

No Lifer
Sep 13, 2001
53,478
6,317
126
your virtual void declaration in the base class takes no parameters, but in the derived class it does. the line calling:

barba->attack(&gobs)

isn't finding a match because the base class, again, has no function matching that. add the parameter of Character* to the virtual declaration and it should fix that issue.

i also noticed that your death function returns true when they have hitpoints, and false if they have none. that looks backwards.

also, your "//create characters objects" lines of code don't actually create those characters - you have to new() them to create them. that is just creating a reference to them, so even when this ran, it would crash.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
Simple rule of thumb:

If you apply the address of (&) operator to a type, the result adds one * to the type of the input.
If you apply a dereferencing operator (* or ->) to a type, the result removes one * from the type of the input.

Examples:

Code:
int i;
&i; // result is int*
*i; // error, type is not pointer

int* j = &i;
&j; // result is int**
*j; // result is int

int** k = &j;
&k; // result is int***
*k; // result is int*

You have two problems:

Your Barbarian::attack function takes a Character* parameter. You have a pointer to character (Character* gobs) but you pass the address of gobs to attack, which is of type Character**.

Also, Character::attack needs to take a parameter of Character*. At present Character has one attack function, taking no parameters. Barbarian has two attack functions, one taking no parameters, the second taking a Character*. The latter function is only available if you cast your Character pointer to a Barbarian pointer (but don't do this, just fix Character::attack).
 

Schmide

Diamond Member
Mar 7, 2002
5,688
921
126
Just a side note nothing to do with actual code.

You should invert your defense power. For example LoL uses a 100 base for its armor mult to prevent full damage mitigation. It's kind of a diminished returns function.
 

Tweak155

Lifer
Sep 23, 2003
11,448
262
126
So glad I don't have to work with pointers. I get the concept but writing the code is a headache to me as far as syntax.
 

HybridSquirrel

Diamond Member
Nov 20, 2005
6,161
2
81
your virtual void declaration in the base class takes no parameters, but in the derived class it does. the line calling:

barba->attack(&gobs)

isn't finding a match because the base class, again, has no function matching that. add the parameter of Character* to the virtual declaration and it should fix that issue.

i also noticed that your death function returns true when they have hitpoints, and false if they have none. that looks backwards.

also, your "//create characters objects" lines of code don't actually create those characters - you have to new() them to create them. that is just creating a reference to them, so even when this ran, it would crash.



So, in the base class I should put:

virtual void attack(Character*){

}


but I still get:


In function &#8216;int main()&#8217;:
152: error: no matching function for call to &#8216;Character::attack(Character**)&#8217;
35: note: candidates are: virtual void Character::attack(Character*)
 
Last edited:

purbeast0

No Lifer
Sep 13, 2001
53,478
6,317
126
So, in the base class I should put:

virtual void attack(Character*){

}


but I still get:


In function ‘int main()’:
152: error: no matching function for call to ‘Character::attack(Character**)’
35: note: candidates are: virtual void Character::attack(Character*)

the answer is in Mirad's post.

HINT: look at what you are passing into the function when you call it
 

HybridSquirrel

Diamond Member
Nov 20, 2005
6,161
2
81
the answer is in Mirad's post.

HINT: look at what you are passing into the function when you call it

I got it to compile with:

barba->attack(gobs)

but the health doesn't get modified on the goblin class. *Profanity redacted*

Please don't swear in the technical forums -- Programming Moderator Ken g6
 
Last edited by a moderator:

purbeast0

No Lifer
Sep 13, 2001
53,478
6,317
126
I got it to compile with:

barba->attack(gobs)

but the health doesn't get modified on the goblin class. *Profanity redacted*
Please don't quote swearing in the technical forums -- Programming Moderator Ken g6

yes, because in your attack function, you are modifying the barbarians variables, not the goblins variables.

if it were me, i would do it completely different and do the following (in psuedo code) so that i don't have to worry about references and crap.

this would be in the barbarian code...
Code:
public int attack(Character *character) {
  return character.hitpoints - ((character.attackpower - character.armorp) - character.defensep);
}

then i would create setters and getters then call...
Code:
goblin.setHitpoints(barbarian.attack(goblin));

personally i would clean it up a bit more than that as well.

a function called "attack" that is void and modifies the parameter being passed in is not very clean/readable to me if i didn't know what is going on. i would make something more explicit as to what is going on.
 
Last edited by a moderator:

HybridSquirrel

Diamond Member
Nov 20, 2005
6,161
2
81
Sorry this was frustrating. I fixed it a bit and it works now:

Code:
        void attack(Character* c1, Character* c2){

            hp = c1->health(); //set health to current health
            tdmg = (c2->getap() - c1->getd()); //set tdmg to attack power - defense
            tots = tdmg - c1->getarm(); //set tots to tdmg - armor

in int main:
Code:
barba->attack(gobs, barba)


I hate pointers.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
Code:
barba->attack(gobs, barba)

To be frank, that isn't a good design. Passing a second character to the attack function is redundant, because attack() already has access to two characters via is parameter and its this pointer. Also, there is the potential for a confusing situation like below to happen:

Code:
Character* hero = ...
Character* gobs = ...
Character* barba = ...

hero->attack(gobs, barba); // wtf?

I hate pointers.

So use a language without them. Like 95% of the games that you will work on as a hobbyist have no need of C++. And I'm saying this as someone who's been using C++ for over 10 years and loves it as a language. Most of my recent work has been in C#.
 

HybridSquirrel

Diamond Member
Nov 20, 2005
6,161
2
81
Code:
barba->attack(gobs, barba)

To be frank, that isn't a good design. Passing a second character to the attack function is redundant, because attack() already has access to two characters via is parameter and its this pointer. Also, there is the potential for a confusing situation like below to happen:

Code:
Character* hero = ...
Character* gobs = ...
Character* barba = ...

hero->attack(gobs, barba); // wtf?



So use a language without them. Like 95% of the games that you will work on as a hobbyist have no need of C++. And I'm saying this as someone who's been using C++ for over 10 years and loves it as a language. Most of my recent work has been in C#.

The idea being each character class will have a unique attack function, and enemy return attacks are going to be rolled at the same time. It works, and i'm happy.
 

Merad

Platinum Member
May 31, 2010
2,586
19
81
The idea being each character class will have a unique attack function, and enemy return attacks are going to be rolled at the same time. It works, and i'm happy.

And what if barba wants to attack gobs, but gobs doesn't care about attacking in return and wants to attack hero instead?

It's your program though, you're more than welcome to make design mistakes... how else will you learn? ;)
 

purbeast0

No Lifer
Sep 13, 2001
53,478
6,317
126
Merad is right - there is absolutely no reason to pass the 2nd parameter in that function. it gains you absolutely nothing and is poor design as mentioned.

instead of you passing it in and doing barbs->whatever, you can just do this->whatever from inside the function, without passing in a second parameter. you can still accomplish what you want of each one having their own attack function.

it also "reads" better in plain english. and making code "read" better in plain english is a sign of good design and programming.

and with that note, i'd change your varaible names big time. i have no clue what those variable names mean.

tots? getarm i'm assuming means to get armor, but why not name it "getArmor" instead? same with tdmg. why not make it tDamage (no clue what the t means)?
 
Last edited: