theBigDream
theBigDream

Reputation: 11

Virtual keyword seems to be ignored

This is my first "big" C++ project and I am stuck. I am trying to create a simple ASCII roguelike. I have a character class that is inherited by a Player class and a Monster class. The Monster class is inherited by Vampire and Werewolf classes.

In the startGame function of the GameSystem class I submit every element of a Monsters array (that is supposed to be filled with Vampire and Werewolf objects) to the function moveAround. Vampires, Werewolfs, and Monsters all have this function yet only the Monster moveAround function is being accessed. If you notice in the code below I have the virtual keyword provided in the Monster class.

This leads me to think I messed up when I filled the Monster array with the subclasses. I did this in the GameSystem constructor by randomly determining if a particular element of the Monster array was going to be a werewolf or a vampire.

I am using codeblocks and in terms of compiling I have g++ following C++11.

GameSystem.cpp

#include <iostream>
#include <string>
#include "GameSystem.h"
#include "Map.h"
#include "Player.h"
#include "Werewolf.h"
#include "Vampire.h"
#include "conio.h"
#include <cstdio>
#include <stdio.h>
#include <unistd.h>
#include <termios.h>
#include <time.h>

GameSystem::GameSystem(string mapName){

    srand (time(NULL));

    _map.load(mapName);

    _map.obtainSpawningLocations(_player.getToken(),_player);

    Werewolf werewolf;
    Vampire vampire;

for(int i = 0; i < 5; i++){

    _spawnValue = rand() % 2;   //We generate either 1 or 2

    if(_spawnValue==1){

        _theMonsters[i] = werewolf;
        cout<<"Werewolf"<<endl;
    }
    else{
        _theMonsters[i] = vampire;
        cout<<"Vampire"<<endl;
    }
    _map.obtainSpawningLocations(_theMonsters[i].getToken(),_theMonsters[i]);
}
}
void GameSystem::startGame(){
bool isOver = false;
    while(isOver!=true){

        _map.print();
        movePlayer();
        for(int i = 0; i <5; i++){
        _theMonsters[i].moveAround(); //prints out Monster.moveAround()
        //I need it to print out Vampire.moveAround() and //Werewolf.moveAround

        }

       }
}

void GameSystem::movePlayer(){
    char input;
    input = getch();
    string clearScreenString(100,'\n'); //Prints out a newLine char 100 times
    cout << clearScreenString;
    _map.checkMovement(input, _player);
}


char GameSystem::getch(){

    char buf=0;
    struct termios old={0};
    fflush(stdout);
    if(tcgetattr(0, &old)<0)
        {perror("tcsetattr()");}
    old.c_lflag&=~ICANON;
    old.c_lflag&=~ECHO;
    old.c_cc[VMIN]=1;
    old.c_cc[VTIME]=0;
    if(tcsetattr(0, TCSANOW, &old)<0)
        {perror("tcsetattr ICANON");}
    if(read(0,&buf,1)<0)
        {perror("read()");}
    old.c_lflag|=ICANON;
    old.c_lflag|=ECHO;
    if(tcsetattr(0, TCSADRAIN, &old)<0)
        {perror ("tcsetattr ~ICANON");}
    //printf("%c\n",buf);
    return buf;
 }

GameSystem.h

#pragma once
#include "Map.h"
#include <string>
#include <list>

using namespace std;

class GameSystem
{

public:
    GameSystem(string mapName);       //Constructor

    void startGame();    //Start the game
    char getch();
    void movePlayer();

private:

    //int _numberOfMonsters = 5;    //We'll make this a random number later
    Map _map;
    Player _player;
    Monster _monster;
    Monster _theMonsters[5];

    int _x;
    int _y;
    int _spawnValue;
};

Character.cpp

#include <string>
#include "Character.h"

using namespace std;


Character::Character(){

}


char Character::getToken(){
return _token;
}

void Character::setLocation(int x, int y){
_x = x;
_y = y;
}

void Character::getLocation(int &x, int &y){
x = _x;
y = _y;
}

Character.h

#pragma once
#include <string>

class Character{

public:
    Character();

    char getToken();
    void setLocation(int x, int y);
    void getLocation(int &x, int &y);

protected:
    int _x;
    int _y;
    char _token = '!';
};

Map.cpp

#include <iostream>
#include <vector>
#include <stdlib.h>
#include <string>
#include <fstream>
#include <cstring>
#include <random>
#include <ctime>
#include <tuple>
#include "Map.h"
#include <time.h>

Map::Map(){
srand (time(NULL));
}

void Map::load(string levelName){
    ifstream theStream;
    theStream.open(levelName);

    if(theStream.fail()){
        perror(levelName.c_str());
        system("PAUSE");
        exit(1);
    }

    string line;
    while(getline(theStream, line)){
    _mapData.push_back(line);
    }
        theStream.close();

}

void Map::obtainSpawningLocations(char characterToken,Character &_character){
/*
Below code provides all the possible spawning locations for the player
and stores them in an array of tuples.
*/

tuple<int,int> myTuple[600];    //Hard coded 600 value is messy.  Change later

int numberOfSpawnPoints = 0;
int upperLimitForNumberGenerator = 0;
/*
The for loop below records all of the possible spawning locations and stores them in the tuple array
*/
    for(int i = 0; i<_mapData.size();i++){
        for(int j = 0; j<_mapData[i].size();j++){
            if(_mapData[i][j]=='.'){
                    get<0>(myTuple[numberOfSpawnPoints]) = j;
                    get<1>(myTuple[numberOfSpawnPoints]) = i;
                    numberOfSpawnPoints++;
                    }
                }
    }
upperLimitForNumberGenerator = numberOfSpawnPoints;

int characterCoordinates = rand()%upperLimitForNumberGenerator;

int xCoordinate = get<0>(myTuple[characterCoordinates]);
int yCoordinate = get<1>(myTuple[characterCoordinates]);

_mapData[yCoordinate][xCoordinate] = characterToken;   //Remember y is first and x is second

_character.setLocation(xCoordinate, yCoordinate);
}

void Map::print(){
    for(int i=0;i<_mapData.size(); i++){
        printf("%s\n", _mapData[i].c_str());
    }
    printf("\n");
}

void Map::checkMovement(char input, Player &aPlayer){ 

int x;
int y;

aPlayer.getLocation(x,y);

char aLocation;

 switch(input) {
    case 'w':
    case 'W':   //If 1 up from the player token is a '.' then we move him up
                //via a different function
                //Otherwise we do nothing.

                aLocation = returnLocation(x,y-1);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y-1][x] = '@';
                aPlayer.setLocation(x,y-1);
                }
                else
                cout<<"Can't go here!"<<endl;
        break;




    case 'a':
    case 'A':
                aLocation = returnLocation(x-1,y);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y][x-1] = '@';
                aPlayer.setLocation(x-1,y);
                }
                else
                cout<<"Can't go here!"<<endl;
        break;
    case 's':
    case 'S':

                aLocation = returnLocation(x,y+1);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y+1][x] = '@';
                aPlayer.setLocation(x,y+1);
                }
                else
                cout<<"Can't go here!"<<endl;
        break;
    case 'd':
    case 'D':
                aLocation = returnLocation(x+1,y);
                if(aLocation == '.'){
                _mapData[y][x] = '.';
                _mapData[y][x+1] = '@';
                aPlayer.setLocation(x+1,y);
                }
                else
                cout<<"Can't go here!"<<endl;






        break;
    default:
        cout<<"Invalid input";
        system("PAUSE");
        break;
    }
}

char Map::returnLocation(int x, int y){
cout<<x<<endl;
cout<<y<<endl;
char aSpot = _mapData[y][x];
return aSpot;

}

Map.h

#pragma once
#include <vector>
#include <fstream>
#include <string>
#include <tuple>
#include <ctime>
#include <random>
#include "Player.h"
#include "Monster.h"

using namespace std;

class Map

{
public:
    Map();      //Constructor
    void load(string levelName);
    void obtainSpawningLocations(char characterToken, Character &aCharacter);
    void checkMovementMonsters(char input, Monster &aMonster);

    //void obtainSpawningLocationsForMonsters(char characterToken, Monster aMonster);

    void print();
    void checkMovement(char input, Player &aPlayer);
    void movement(char characterToken);
    char returnLocation(int x,int y);

   // int numberOfSpawnPoints;

private:
    vector <string> _mapData;
    Player _player;
    Monster _monster;


};

Monster.cpp

#include <iostream>
#include <string>
#include "Monster.h"

using namespace std;

Monster::Monster(){

}
void Monster::moveAround(){
cout<<"Monster Mash"<<endl;
}

Monster.h

#pragma once
#include <string>
#include "Character.h"

class Monster: public Character{

public:
    Monster();
    virtual void moveAround();


protected:

    char _token = 'M';
    int _x;
    int _y;
};

Werewolf.cpp

#include <iostream>
#include <string>
#include "Werewolf.h"

using namespace std;

Werewolf::Werewolf(){

}

void Werewolf::moveAround(){
    cout<<"Werewolf moving around"<<endl;
}

Werewolf.h

#pragma once
#include <string>
#include "Character.h"      //For inheritance/polymorphism
#include "Monster.h"


class Werewolf: public Monster{
public:
    Werewolf();
    void moveAround();

private:

    char _token = 'W';

};

Vampire.cpp

#include <iostream>
#include <string>
#include "Vampire.h"

using namespace std;

Vampire::Vampire(){

}

void Vampire::moveAround(){
    cout<<"Vampire moving around"<<endl;
}

Vampire.h

#pragma once
#include <string>
#include "Character.h"      //For inheritance/polymorphism
#include "Monster.h"


class Vampire: public Monster{
public:
    Vampire();
    virtual void moveAround();

private:

    char _token = 'V';

};

Player.cpp

Player::Player(){

}

Player.h

#pragma once
#include <string>
#include "Character.h"
using namespace std;

class Player : public Character {

public:
    Player();  

protected:
    int _x;
    int _y;
    char _token = '@';
};

Main

#include <iostream>
#include "GameSystem.h"
using namespace std;

int main()
{
GameSystem gameSystem("LevelOne.txt");
gameSystem.startGame();
    return 0;
}

The level text file:

############################################
#..........................................#
#..........................................#
#...........................^..............#
#..........................................#
#......................#...................#
#......................#...................#
#......................#...................#
#............^.........#...................#
#......######..........#..&................#
#......\...............#...................#
#......................#...................#
#..........................................#
#..........................................#
############################################

Upvotes: 0

Views: 73

Answers (2)

Miles Budnek
Miles Budnek

Reputation: 30569

There are a couple of issues here.

  1. Monster::_token, Werewolf::_token, and Vampire::_token are all different variables. A Werewolf object has both Monster::_token and Werewolf::_token.
  2. Monster _theMonsters[5] is an array of 5 Monster objects. It will never be a Vampire or a Werewolf, always a Monster. There is no virtual dispatch to be done since the objects will always be the same type: Monster.

Virtual dispatch only applies to pointers and references. To make this work, you'll need to use an array of pointers to Monsters: Monster* _theMonsters[5]. Then you can fill it like _theMonsters[i] = new Vampire(). If you do this, you'll need to remember to delete the objects when you're done with them, or you could use a smart pointer like std::unique_ptr or std::shared_ptr instead of raw Monster*s.

Upvotes: 1

Dirk Herrmann
Dirk Herrmann

Reputation: 5939

There is no virtual dispatch happening on _theMonsters[i].moveAround(). You need to have an array of pointers to objects of type monster. In fact, there are quite some problems in the way you try to set up the class hierarchy, like, having identically named member variables in derived classes.

Upvotes: 1

Related Questions