#Off topic When I was logging in, I have noticed 600,00x members registered on this forum. Congrats on the 600k! ;)

Hello.
My console game is going pretty well, even though I don’t have much spare time to expand it. I have some ideas I would like to add, but I'm not sure how to accomplish them.
First of all, I would like to save data. Most of my data is encapsulated in classes, so I could pass a pointer of all my classes to a function, make it write the data to a file, and then use a similar function to load it. However, I guess writing the data like that would make it very messy, and adding extra content would mean completely rewriting the save\load functions. Also, some of my classes are inherited from other classes, and I have used some aggregation, which makes saving a bit more complex...
So, if you have any ideas on how to save all my data and then load it up without having to worry about future content, I would like to hear them. I was thinking, writing to something like an .ini file, and separating the data with sections. I'm not sure how it would work though.

My second idea was to add easy to edit modules. So that other users could create content, send it to me, and I would link the modules to my game. The reason I just don't create some easy to inherit classes and editable headers, is because not all players would have a compiler and knowledge of C++, so I was thinking of something like .lua files. I'm not sure how that works either. Technically it could be something like this:

My user defined template, saved to a text file:
Data 1:
Data 2:
Data 3:
.... etc.
The user would edit each "Data" segment, then I link it to the game, a function converts the module to an inherited class, where "Data" are the necessary parameters, such as dialogues or stats.
Any ideas are greatly welcomed!

#Off topic When I was logging in, I have noticed 600,00x members registered on this forum. Congrats on the 600k! ;)

Yes, new members register at an astonishing rate here . . . when I logged in there were six less members than there are now. :)

Hello.
My console game is going pretty well, even though I don’t have much spare time to expand it. I have some ideas I would like to add, but I'm not sure how to accomplish them.
First of all, I would like to save data. Most of my data is encapsulated in classes, so I could pass a pointer of all my classes to a function, make it write the data to a file, and then use a similar function to load it. However, I guess writing the data like that would make it very messy, and adding extra content would mean completely rewriting the save\load functions. Also, some of my classes are inherited from other classes, and I have used some aggregation, which makes saving a bit more complex...

[Sorry if this is a bit advanced. I assume you're a pretty good programmer from that description, but still, I do get carried away sometimes.]

Here's one way you could go about it that I can think of; it's a reasonably clean way. Have each class write itself out. This is a nicely object-oriented solution, and it means that adding new content isn't too laborious. If all of your classes are derived from some base class, for example, you could have a function like

virtual void write(std::ostream &stream) = 0;

which would write the data to a stream. You probably have one or a few top-level classes, so to save the game you'd just call write() on those classes, which would in turn propogate down and call write() on the other methods. Just as a quick example:

class NPC : public GameObject {
private:
    std::string name;
    Inventory *inventory;
    Disposition *disposition;
    // ...
public:
    virtual void write(std::ostream &stream);
};

void NPC::write(std::ostream &stream) {
    stream << "[class NPC]\n";
    stream << name << std::endl;
    inventory->write(stream);
    disposition->write(stream);
}

You can see how each class only has to write out the data it is responsible for. It makes for a nice design. Sorry if you don't understand virtual functions and stuff that I've used here . . . you can ignore it, or better yet look up what it is. :)

The trouble comes when you're trying to read stuff in, especially if you've used inheritance extensively. You basically need a way to decide which class to construct when you see something like "NPC" in the file. A few ways to do this:

  • Maintain a list of function pointers, in a std::map, say, mapping from std::strings like "NPC" to functions which know how to construct an NPC object. Or if maps are too advanced, think of a giant if-else or switch statement. :) This isn't too bad of a solution, but it's not very object-oriented.
  • You could also implement a virtual constructor system. (This is a nice reference for this type of thing: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=184) You could, at the beginning, construct some empty objects and register them in a std::map (yes, I like maps). Then just call a construct() method on that object . . . perhaps an example will make things clear.

Here's the example I promised.

#include <iostream>
#include <map>
#include <string>

class Base {
public:
    /** Obligatory virtual destructor. */
    virtual ~Base() {}
    
    virtual void deal_with_things() = 0;
    
    virtual Base *construct() = 0;
};

class One : public Base {
public:
    virtual void deal_with_things() { std::cout << "I'm a One.\n"; }
    
    virtual One *construct() { return new One(); }
};

class Two : public Base {
public:
    virtual void deal_with_things() { std::cout << "I'm a Two.\n"; }
    
    virtual Two *construct() { return new Two(); }
};

int main() {
    std::map<std::string, Base *> name_map;
    
    name_map["One"] = new One();
    name_map["Two"] = new Two();
    
    // Let's pretend this came from a file somewhere.
    const std::string construct = "One";
    Base *object = name_map[construct]->construct();
    
    object->deal_with_things();
    
    return 0;
}

Anyway, hopefully that will give you some interesting ideas . . . .

So, if you have any ideas on how to save all my data and then load it up without having to worry about future content, I would like to hear them. I was thinking, writing to something like an .ini file, and separating the data with sections. I'm not sure how it would work though.

My second idea was to add easy to edit modules. So that other users could create content, send it to me, and I would link the modules to my game. The reason I just don't create some easy to inherit classes and editable headers, is because not all players would have a compiler and knowledge of C++, so I was thinking of something like .lua files. I'm not sure how that works either. Technically it could be something like this:

My user defined template, saved to a text file:
Data 1:
Data 2:
Data 3:
.... etc.
The user would edit each "Data" segment, then I link it to the game, a function converts the module to an inherited class, where "Data" are the necessary parameters, such as dialogues or stats.
Any ideas are greatly welcomed!

If you want your program to have Lua scripts, it's far from trivial. You have to have some sort of interpreter for the scripting language; you could write your own (quite difficult, though less so if you use bison and flex) or use an existing Lua scripting library. I hear they exist. I've never tried it myself.

It really depends on your game, but I'd consider implementing something like I described above. If you want the structure of your file to be, e.g.

name: long_hall
description: You are standing in a long hallway.
east: shadowed_room
west: dead_end

name: shadowed_room
...

then you could just create a class which "reads" itself in the manner described above. If you want more complicated structures like

message += "Before you stands the messenger."
if(has_seen_message) {
    message += "'Well, what are you waiting for?'"
}
else {
    message += "'Sir! The <insert suitably medieval name here> are attacking!'";

    has_seen_message = 1;
}

then you're in for a lot of code. :) It's quite complicated to process that sort of thing. I'd probably implement a C++ interface, just so that you can make sure it works and write some basic modules yourself, and then write an interpreter that uses the C++ interface. You can write a simple interpreter and make it more complicated from there.

But I'm rambling now. Have fun with your coding.

Comments
Nice Post
Yes!

dwks Thanks for the very detailed answer! I think your second example with maps would work perfectly for saving, I'll try to adapt it to my needs, although I havent used maps in real programms before, just for testing. *Is going to read STL manual*

About the modules part, I have done some research on .lua, and it seems to be too complicated for my small console game :)
I guess most of the modules would represent dialogues and locations. Lets say I have a dialogues function, its pretty trivial... (Actually I don't have the source on this PC, so this is not the exact function)

void Dialogue(unsigned short int const num) /*num is the ID of the dialogue*/
{
switch(num)
{
case 0: Display("My text here."); /*Display is just a function that takes strings, formats them, adds colour if necessary and displays them*/
break;
}
}

So, the module would need:
Text of the NPC.
Available choices of the player -> Responses to the choice.

I'm not sure if this could work, the template would be like:
npc name:
text:
player choice1:
player choice2:
response1:
response2:

And then it would be defined, something like:

#define (npc name: "myname") myNameVector.push_back("name here")
#define (text: "text here") myVector.push_back(myNameVector[i++]+": text here") /*Vector of strings*/

Although, I don't know how to make such a complex define, since you would need to split the string out of a text file into parts and define it all at once...

#define isn't what you need. #define does compile-time text substitution, as if you'd done a search-replace just before every compilation.

So, the module would need:
Text of the NPC.
Available choices of the player -> Responses to the choice.

I'm not sure if this could work, the template would be like:

npc name:
text:
player choice1:
player choice2:
response1:
response2:

That looks like a reasonable idea. You'll probably want different "states" for the conversation to be in; you could give them names, or just numbers. So in state 0, the user can say "Greetings.", "Who are you?", or "Go away, you're wasting my time." The first two lead to state 1 (and maybe the first one increases the NPC's friendliness or something), where you can ask further questions. The third leads to state 2, which involves the NPC walking away in a huff.

You'd have to encode all of that in the file. I might choose something like

NPC <name>
<friendliness>
State <number>
Response <destination-state> <user-speech>
Response <destination-state> <user-speech>
...
State <another number>
Response <destination-state> <user-speech>
...
~NPC

and use the destination state -1 as "walk away from the NPC" or something like that.

There are two basic ways you can encode this sort of information, I suppose. The first is to get everything from context. So if you see "NPC", you know there will be one line containing the friendliness and name, in that order, or something like that. Then you say "5", which means there will be 5 States; inside each state you say "3" to mean 3 responses, and so on.

I really don't recommend that. It's hard to read and harder to write. You could instead say "NPC", which goes into the NPC reading loop or whatever. Now NPC recognizes lines beginning with "name" as the NPC's name, a line beginning with "State" as the beginning of a state, and so on. You can use "~State" or something to terminate a section if necessary.

Of course, you could combine these formats; every NPC has exactly one name, so it makes sense to include this by context. But there are lots of states and other parts of NPCs, so maybe this should be included afterwards by introducing a "State" line and finishing with a "~State" line.

I've written up a quick and simple game for you . . . it's attached. A few of my philosophies in it:

  • All classes can be constructed with the appropriate data, or constructed from a std::istream&, to allow reading from files.
  • All classes also have a write() member to write their data to a file.
  • I've used "contextual" representation for things like an NPC's name, and otherwise for things like PlayerResponse.
  • Note that every class expects its name to have already been read by the previous class, so that the current class was decided upon. So Attitude, which writes out "Attitude\n<some-number>\n", only reads in "<some-number>\n".

It's not the best example, but maybe it will inspire you to write something similar. I didn't use a std::map<> for construction of classes, instead using just constructors, and if-statements to decide among them. It's simpler and probably more than sufficient for your purposes.

Well, now I've spent way too much time on this. Hope you find it useful.

Attachments

Thanks allot dwks! I really appreciate your help.
I have been doing some testing on the module part, it seems like making a finite state machine wold work very well. What I've got so far is a finite state machine that checks for a required "start" such as Name: or Text: then it checks if it has anything input, and converts the data to a class. Thanks again for your help!
I won't be able to continue the project soon though, having exams this month :(

This question has already been answered. Start a new discussion instead.