943,614 Members | Top Members by Rank

Ad:
  • C++ Discussion Thread
  • Marked Solved
  • Views: 603
  • C++ RSS
Sep 3rd, 2009
0

Adding modules and saving data

Expand Post »
#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!
Last edited by Belthemet; Sep 3rd, 2009 at 5:38 pm.
Similar Threads
Reputation Points: 10
Solved Threads: 0
Newbie Poster
Belthemet is offline Offline
8 posts
since Aug 2009
Sep 3rd, 2009
2

Re: Adding modules and saving data

Click to Expand / Collapse  Quote originally posted by Belthemet ...
#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.

Quote ...
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
C++ Syntax (Toggle Plain Text)
  1. 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:
C++ Syntax (Toggle Plain Text)
  1. class NPC : public GameObject {
  2. private:
  3. std::string name;
  4. Inventory *inventory;
  5. Disposition *disposition;
  6. // ...
  7. public:
  8. virtual void write(std::ostream &stream);
  9. };
  10.  
  11. void NPC::write(std::ostream &stream) {
  12. stream << "[class NPC]\n";
  13. stream << name << std::endl;
  14. inventory->write(stream);
  15. disposition->write(stream);
  16. }
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/conte...lus&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 . . . .

Quote ...
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.
C++ Syntax (Toggle Plain Text)
  1. name: long_hall
  2. description: You are standing in a long hallway.
  3. east: shadowed_room
  4. west: dead_end
  5.  
  6. name: shadowed_room
  7. ...
then you could just create a class which "reads" itself in the manner described above. If you want more complicated structures like
C++ Syntax (Toggle Plain Text)
  1. message += "Before you stands the messenger."
  2. if(has_seen_message) {
  3. message += "'Well, what are you waiting for?'"
  4. }
  5. else {
  6. message += "'Sir! The <insert suitably medieval name here> are attacking!'";
  7.  
  8. has_seen_message = 1;
  9. }
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.
Reputation Points: 185
Solved Threads: 28
Posting Whiz in Training
dwks is offline Offline
269 posts
since Nov 2005
Sep 4th, 2009
0

Re: Adding modules and saving data

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)

c++ Syntax (Toggle Plain Text)
  1. void Dialogue(unsigned short int const num) /*num is the ID of the dialogue*/
  2. {
  3. switch(num)
  4. {
  5. case 0: Display("My text here."); /*Display is just a function that takes strings, formats them, adds colour if necessary and displays them*/
  6. break;
  7. }
  8. }

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:
c++ Syntax (Toggle Plain Text)
  1. #define (npc name: "myname") myNameVector.push_back("name here")
  2. #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...
Last edited by Belthemet; Sep 4th, 2009 at 2:36 am.
Reputation Points: 10
Solved Threads: 0
Newbie Poster
Belthemet is offline Offline
8 posts
since Aug 2009
Sep 5th, 2009
0

Re: Adding modules and saving data

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

Quote ...
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:
C++ Syntax (Toggle Plain Text)
  1. npc name:
  2. text:
  3. player choice1:
  4. player choice2:
  5. response1:
  6. 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
C++ Syntax (Toggle Plain Text)
  1. NPC <name>
  2. <friendliness>
  3. State <number>
  4. Response <destination-state> <user-speech>
  5. Response <destination-state> <user-speech>
  6. ...
  7. State <another number>
  8. Response <destination-state> <user-speech>
  9. ...
  10. ~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.
Attached Files
File Type: zip game.zip (3.5 KB, 15 views)
Reputation Points: 185
Solved Threads: 28
Posting Whiz in Training
dwks is offline Offline
269 posts
since Nov 2005
Sep 6th, 2009
0

Re: Adding modules and saving data

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
Reputation Points: 10
Solved Threads: 0
Newbie Poster
Belthemet is offline Offline
8 posts
since Aug 2009

This thread is solved

Either the thread starter or a moderator has marked this thread as solved. You can most likely trust the responses and answers given. There is most likely no reason for any further responses to be posted here. If you have a related question, please start a new thread in this forum instead.

This thread is more than three months old

No one has posted to this discussion for at least three months. Please let old threads die and do not reply to them unless you feel you have something new and valuable to contribute that absolutely must be added to make the discussion complete. Otherwise, please start a new thread in this forum instead.
Message:
Previous Thread in C++ Forum Timeline: Get Week Number of a Given Date
Next Thread in C++ Forum Timeline: File Position tellg()





About Us | Contact Us | Advertise | Acceptable Use Policy
Forum Index | Build Custom RSS Feed


Follow us on Twitter


© 2011 DaniWeb® LLC