How do I create multiple different class objects from a txt file

Hi im trying to create multiple different class objects by reading a text file for a text adventure game, but im a bit confused on how to go about this as i have 3 classes i’d like to create objects for, Location, Item and Container.

my main issue is that container is a subclass of Item, and location uses both item and container because of that im unsure on how to read the file correctly so it creates each object in order within ReadData() in Main.cpp.

Level.txt

ITEM_START
Item: Red Key
Description: A metal red key. looks dirty.
ITEM_END

ITEM_START
Item: Letter
Description: A enclosed letter talking about someones journey through the forest.
ITEM_END

ITEM_START
Item: Screwdriver
Description: Random screwdriver on the floor the handle is chipped but still can be used.
ITEM_END

CONTAINER_START
Item: Strongbox
Description: A sturdy box. It has seen better times.
Contents: Red Key, Letter
Keys: Screwdriver
CONTAINER_END

LOCATION_START
Location: 1
Name: Outside building
Description: You are standing in front of a small building.
Contents: Screwdriver, Strongbox
WEST 3
NORTH 2, Red Key
SOUTH 2, Red Key
LOCATION_END

LOCATION_START  
Location: 2
Name: Inside building
Description: You are inside a building, a well house for a large 
spring.
Contents: Rubber Duck
SOUTH 1
EAST 1
LOCATION_END

Location.h

#pragma once
#include"Globals.h"
#include "Item.h"
#include <algorithm>
#include "Enums.h"


class Location
{
//member variables
#pragma region Variables
public:
    int number;
    string id;
    string locationIDs[4];
    string name;
    string description;
    vector<Item> contents;
    vector<Location> keyItems;
    //Player player;
    
//connections (maps each possible direction to the location it leads to)
#pragma endregion
#pragma region Functions
//class member functions
public:
    void print(Direction direction);
    bool takeItem(Item* i);
    void dropItem(Item* i);
    Item* getKey(string& direction);
    Location* getConnection(string& direction);

    void SetLocations(string north, string south, string east, string west);
    void SetDefaultValues(string id = "", string name = "", string description = "", vector<Item> contents = {});
    bool DirectionOptions(Direction direction);
    string GetLocationID(Direction direction);
    string GetID();

    Location();//default constructor
    //Location(string name, string description); // initialise the variables through the constructor
    Location(string id, string name, string description, vector<Item> contents); // initialise the variables through the constructor
    

#pragma endregion
};

Location.cpp

#include "Location.h"
#include "Player.h"

Location::Location() { SetDefaultValues(); }
//Location::Location(string name, string description) { DefaultValues(name, description); }

Location::Location(string id,string name, string description, vector<Item> contents) {
    SetDefaultValues(id,name,description,contents);
}

//which displays the location’s description and the current list of items
void Location::print(Direction direction)
{
    cout << "You are currently located at: " << name << endl;
    cout << description << endl;
    cout << "Current List of Items Include: " << endl;
    for (Item i : contents)
    {
        cout << i.GetName()<<" ";
    }
    cout << endl;

    cout << "You can go:" << endl;

    for (int i = 0; i < 4; i++) {
        if (direction == Direction(i) && locationIDs[1] != "NULL")
            cout << GetDirectionString(Direction(i)) << " ";

    }
    cout << endl;
    
}

// to remove an item from the location; returns true/false for success / failure(because the item may not be here)
bool Location::takeItem(Item* i) 
{
    Item tempItem = *i;//dereference the pointer
    //this is the code throwing the error
    
    if (std::find(contents.begin(), contents.end(), tempItem) != contents.end()) {
        vector<Item>::iterator position = std::find(contents.begin(), contents.end(), tempItem);
        contents.erase(position);
        cout << tempItem.GetName() << " Was added to the inventory" << endl;
        return true;
    }
    else {
        cout << tempItem.GetName() << " Action Failed / Does not exist" << endl;
        return false;
    }   
}

// to add an item to the location
void Location::dropItem(Item* i) // try an iteration for loop for the function as there could be multiple o9f the same item in a vector
{
    Item tempItem = *i; //dereference the pointer.
    contents.push_back(tempItem);
    cout << tempItem.GetName() << " Has been dropped " << endl;
    
}

// to get the key item required to go in some direction; returns nullptr if no item required
Item* Location::getKey(string& direction)
{
    return nullptr;
}

// to get the location the direction leads to; returns null if you cannot got in that direction from this location
 Location* Location::getConnection(string& direction)
 {
    return nullptr;
}


void Location::SetLocations(string north, string south, string east, string west)
{
    locationIDs[NORTH] = north;
    locationIDs[SOUTH] = south;
    locationIDs[EAST] = east;
    locationIDs[WEST] = west;

}

void Location::SetDefaultValues(string id, string name, string description, vector<Item> contents = {})
{
    this->id = id;
    this->name = name;
    this->description = description;
    this->contents = contents;

    for (int i = 0; i < 4; i++)
        locationIDs[i] = "NULL";


}

bool Location::DirectionOptions(Direction direction)
{
    for (int i = 0; i < 4; i++) {
        if (direction == Direction(i) && locationIDs[i] != "NULL")
            return true;
    }
    return false;
}

string Location::GetLocationID(Direction direction)
{
    return locationIDs[direction];
}

string Location::GetID()
{
    return id;
}

Item.h

#pragma once
#include"Globals.h"

class Item
{
private:
    string name;
    string description;
public:
    //getter
    string GetName() const;
    string GetDescription() const;
    ////setter
    virtual bool operator==(const Item& rhs) const {
        return name == rhs.name;
    }
public:
    Item();
    Item(string name, string description);
};


Item.cpp

#include "Item.h"

string Item::GetName() const
{
    return name;
}

string Item::GetDescription() const
{
    return description;
}

Item::Item()
{
}

Item::Item(string name, string description)
{
}

Container.h

#pragma once
#include "Globals.h"
#include "Item.h"
#include "Player.h"
class Container: Item
{
public:
    vector<Item> contents;
    Item keyItem;
    bool opened;
    Player* player;

public:
    bool open(); // Open and empty contents into player inventory if player has key item:

    Container();
    Container(Item keyItem, bool opened, Player* player) { this->player = player; };
};


Container.cpp

#include "Container.h"

inline bool Container::open()
{
    bool hasKeyItem = false;

    if (!opened) {
        if (std::find(player->pInventory.begin(), player->pInventory.end(), keyItem) != player->pInventory.end()) {
            for (auto& cItems : contents) {
                player->pInventory.push_back(cItems);
                cout << "Added " << cItems.GetName() << " To inventory" << endl;
            }
            contents.clear();
            return true;
        }
    }
    else
        return false;
        
}

Main.cpp

#pragma once
#include <iostream>
#include "Globals.h"
#include "Player.h"
#include "Item.h"
#include "Container.h"
#include <fstream>
#include <map>;
#include <cctype> // used to eliminate case sensitivity problems
#include <algorithm>


#pragma region Variables
using std::ifstream;

Player player;
string command;
string word_1, word_2;

std::map<string,Location*> main_Locations;

Location temp;


#pragma endregion

#pragma region Command Fucntions
void LookCommand() {

    cout << "Location Description: " << endl; 
    cout << player.pLocation.description << endl;
    cout << "The items you can find here: " << endl;
    for (auto& items : player.pLocation.contents) // prints all the contents in the current location
        cout << items.GetName() << " ";
    cout << endl;

}

// section the command into two parts or one
void section_command(string cmd, string& w1, string& w2) {
    size_t pos = cmd.find(' ');
    // npos the maximum for the size
    if (pos == string::npos) {
        w1 = cmd; // sets word one to the whole command as space not found
    }  
    else {
        w1 = cmd.substr(0, pos); // sets the first word to word1
        w2 = cmd.substr(pos + 1); // sets the second word to word2
    }
        
}

#pragma endregion

#pragma region Parser
// stores the valid words in a strongly typed enum ( treating the enum as its own type instead of in )

enum class Directions
{
    NORTH,
    SOUTH,
    EAST,
    WEST,
    UP,
    DOWN,
    IN,
    OUT,
    INVALID
};

enum class Verb
{
    LOOK,
    TAKE,
    DROP,
    OPEN,
    QUIT,
    INVALID
};

// Map that maps a string to a given verb
std::map<string, Verb> KnownVerbs;
std::map<string, Directions> KnownDirections;

Verb parseVerb(string& word) {
    // Make word uppercase and prevents errors if ascii characters are entered
    std::transform(word.begin(), word.end(), word.begin(), [](unsigned char word) {return std::toupper(word); }); 
    auto w = KnownVerbs.find(word);// finds the word in verb and compares
    if (w == KnownVerbs.end()) // verb not found return invalid
        return Verb::INVALID; // invalid verb
    return w->second;  // return the verb
}

Directions parseDirections(string& word) {
    // Make word uppercase and prevents errors if ascii characters are entered
    std::transform(word.begin(), word.end(), word.begin(), [](unsigned char word) {return std::toupper(word); });
    auto w = KnownDirections.find(word);// finds the word in verb and compares
    if (w == KnownDirections.end())// Direction not found return invalid
        return Directions::INVALID; //invalid Direction
    return w->second;

}

#pragma endregion

#pragma Region ReadData

void ReadData(ifstream levelFile) {
    
    string line;
    levelFile.open("Level.txt", std::ios::out, std::ios::in);
   
    string token;
    levelFile.close();
}

void CreateLocations(string id, string name, string desc, string north, string south, string east, string west,vector<Item> contents) {

    main_Locations[id] = new Location(id, name, desc, contents);
    main_Locations[id]->SetLocations(north, south, east , west);
}


#pragma endregion


int main()
{
#pragma region Variables

    Verb verb;
    ifstream levelData;
    //mapped the following commands to key strings

#pragma endregion

#pragma region KNOWN WORDS

    KnownVerbs["LOOK"] = Verb::LOOK;
    KnownVerbs["TAKE"] = Verb::TAKE;
    KnownVerbs["DROP"] = Verb::DROP;
    KnownVerbs["OPEN"] = Verb::OPEN;
    KnownVerbs["QUIT"] = Verb::QUIT;

    KnownDirections["NORTH"] = Directions::NORTH;
    KnownDirections["SOUTH"] = Directions::SOUTH;
    KnownDirections["EAST"] = Directions::EAST;
    KnownDirections["WEST"] = Directions::WEST;

    KnownDirections["UP"] = Directions::UP;
    KnownDirections["DOWN"] = Directions::DOWN;
    KnownDirections["IN"] = Directions::IN;
    KnownDirections["OUT"] = Directions::OUT;

#pragma endregion


    while (parseVerb(command) != Verb::QUIT)
    {
        command.clear();// empties the command for input;
        cout << "What shall i do? " <<endl;
        std::getline(std::cin, command);
        section_command(command, word_1, word_2);
        verb = parseVerb(word_1);

        switch (verb)
        {
        case Verb::LOOK:
            LookCommand();
            break;
        case Verb::DROP:
            //check the noun uses is valid then perform action.
            break;
        case Verb::TAKE:
            //check the noun used is valid then perform action.
            break;
        case Verb::OPEN:
            //check the noun used is valid then perform action.
            break;

        default:
            break;
        }

        word_1.clear(); // clear the words at the end for loop
        word_2.clear(); // clear the words at the end for loop
    }

}

I tried splitting the text and utilising #include but im not too familiar with working with larger txt files utilising data from different classes

  • 3

    Start small. For example, just have one data member in each class and see if it works. If it does then add other members one by one and check again if that works. That is, reduce your example to a minimal reproducible example.

    – 




  • 1

    Can you reduce your code to the relevant bit ? This is too much to walk through. But it sounds like what you should look into is how to build a factory function and how to serialize/deserialize objects. Maybe also change you save file format to something standard, like XML or JSON. A good JSON library will make things easier for you.

    – 




Leave a Comment