   Dialogue system
  I'm creating a text based rpg and I'm trying to make a good dialogue system.At the moment it looks something like this:// Code startclass cDialogue {vector  Option;public:void AddOption(cOption* o) {Option.push_back(o);}void ShowOptions();};class cOption {string Text;bool Visible;cOption* Answer;cOption* Unlocks;public:void Select() {SetVisible(0); Unlocks->SetVisible(1);}void SetVisible(bool v) {Visible = v;}bool IsVisible() {return Visible;}void SetText(string t) {Text = t;}string GetText() {return Text;}void SetAnswer(cOption* a) {Answer = a;}void SetUnlocks(cOption* u) {Unlocks = u;}};// Code endMy code isn't complete because I'm trying to improve it. I didn't include all the function code either, but I haven't improved them yet. I haven't found any dialogue systems on the net, so I wondered what you guys use.My system works like this:All options are stored (as pointers) in a vector in the dialogue class.When you select an option it gets hidden, gives the answer and unlocks the options pointed to.I just recently began using vectors, so it should be easier to sort the options for viewing (making sure that visible ones are labeled in the right order by a sort function.)Hope someone has some advice.Edit: Small code adjustment.[edited by - Corvwyn on February 4, 2004 6:15:13 PM]
  while only you know the exact details of your game, and this might not apply, i would suggest you make it so each option can both unlock and hide multiple other options. for example, an option that gets the NPC to talk about some cave with a dragon in it (how cliche) could unlock options to ask about the cave and the dragon separately, and maybe even a "but isn''t there any treasure?" option.also, depending on your needs, you might want to throw in something that allows scripting based on the selected option. it doesn''t have to be complex, but it would be nice to be able to have options that can instantly end the conversation (in case you pick the "NPC, you suck!" option), or start combat, or give an item, etc. ;  Yes, I think I'll use a vector for Unlocks*.Good idea with the ending conversation thing. I'll have to add in something like that once I get the basic system up and going again. I've already done a bit since my last post, once it works I'll post the whole code.Thanks for the advice.   [edited by - Corvwyn on February 4, 2004 6:09:45 PM];  Ok. I've finished the basic dialogue system. Answers will probably be put in a vector or something later, but this is what I'll use at the moment. I'm trying to make separate system with Dialogue, Characters, Rooms etc. I find it quite helpful to make this separately and put it together when I'm done.It would be quite helpful if people could give me some pointers if I could have done something better.I'll just list the source below. I also included the main file incase you'd want to test it.Classes.h #ifndef __MAIN_CLASSES_H_#define __MAIN_CLASSES_H_#include <iostream>#include <string>#include <vector>using namespace std;class cOption {string Text;string Answer;bool Visible;bool Quit;cOption* Unlocks;public:cOption() {Text = "";}string Select();void SetText(string t) {Text = t;}string GetText() {return Text;}void SetAnswer(string a) {Answer = a;}void SetUnlocks(cOption* Option) {if (Option != NULL) Unlocks = Option;}void SetVisible(bool vis) {Visible = vis;}bool IsVisible() {return Visible;}void SetQuit(bool q) {Quit = q;}bool DoQuit() {return Quit;}};class cDialogue {vector <cOption*> Option;public:void AddOption(cOption* o);vector <cOption*> GetOption() {return Option;}void Show();};class cChar {string Name;cDialogue* Dialogue;public:void SetName(string n) {Name = n;}string GetName() {return Name;}void SetDialogue(cDialogue* d);void Talk(cChar* Char);};#endifClasses.cpp  #include <iostream>#include <string>#include <vector>#include "classes.h"using namespace std;   string cOption::Select(){if(Unlocks != NULL){Unlocks->SetVisible(1);}SetVisible(0);return Answer;}void cDialogue::AddOption(cOption* o){if(o != NULL){Option.push_back(o);}}void cDialogue::Show(){int Nr = 0;for(int i=0;i<(signed)Option.size();i++){if(Option[i]->IsVisible()){Nr++;cout << Nr << ": " << Option[i]->GetText() << endl;}}}void cChar::SetDialogue(cDialogue* d){if(d != NULL){Dialogue = d;}}void cChar::Talk(cChar* Char){int input = 0;while(input <= 4){// Show available optionscout << "You say: " << endl;Char->Dialogue->Show();// User inputcout << "> ";cin >> input;// Temp option for visble optionsvector <cOption*> Option;// Fill talk vector with visible optionsfor(int i=0;i<(signed)Char->Dialogue->GetOption().size();i++){if(Char->Dialogue->GetOption()[i]->IsVisible()){Option.push_back(Char->Dialogue->GetOption()[i]);}}// End on input errorif (input >= 4) break;// Select optioncout << endl;cout << Char->GetName() << " says: " << Option[input-1]->Select() << endl;// Check if user chose byeif (Option[input-1]->DoQuit() == 1)break;}} main.cpp #include <iostream>#include <string>#include <vector>#include "classes.h"using namespace std;int main(){cOption Option[3];cDialogue Dialogue;cChar Char[2];    cOption* pOption[3];for (int i=0;i<4;i++){pOption[i] = &Option[i];}cDialogue* pDialogue = &DialoguecChar* pChar[2];for (int i=0;i<3;i++){pChar[i] = &Char[i];}Option[0].SetText("Hello");Option[1].SetText("Job?");Option[2].SetText("Bye");Option[0].SetAnswer("Well met");Option[1].SetAnswer("Warrior");Option[2].SetAnswer("Farewell");// Set option special propertiesOption[0].SetUnlocks(pOption[1]);Option[2].SetQuit(1);// Set visible optionsOption[0].SetVisible(1);Option[2].SetVisible(1);Dialogue.AddOption(pOption[0]);Dialogue.AddOption(pOption[1]);Dialogue.AddOption(pOption[2]);// Set character infoChar[0].SetName("Corvwyn");Char[1].SetName("Merwyndin");Char[1].SetDialogue(pDialogue);Char[0].Talk(pChar[1]);return 0;} [edited by - Corvwyn on February 7, 2004 8:07:42 AM];  I won''t bump this thread more than this. I''d just like to say that I''d appreciate any input even if it''s just "it sucks" or something like that (well, maybe I wouldn''t be too happy about that). ;  The design is decent.  This is the output I got when I compiled and ran it:quote:You say:1: Hello2: Job?> 1Merwyndin says: Well metYou say:1: Job?> 1Merwyndin says: WarriorYou say:> 0Segmentation fault (core dumped)So error management leaves something to be desired, but I realize this is just a prototype, so I won''t harp on it.You might want to add the ability to load dialogues from a data file, if you haven''t thought of that.  Then you can modify them without changing/recompiling the source.;  Yeah, I''ll have to improve the error checking, that''s for sure. Thanks for pointing it out.I''ve thought about saving dialogue files etc. The problem with this is that I might have to use serialization, and I have yet to find any good, understandable code about that. I want to create a kind of toolset so that I can create the dialogue, rooms etc and load it from a file in the game.Since the topic of serialization is so much asked I won''t ask any of you to explain it to me, since everyone''s probably sick of it already. I just wonder why nobody has given a easy code with i.e. character information w/ pointers. ;quote:Original post by Corvwyn  Since the topic of serialization is so much asked I won''t ask any of you to explain it to me, since everyone''s probably sick of it already. I just wonder why nobody has given a easy code with i.e. character information w/ pointers.What do you mean? How are pointers relevant here? And there''s no single right way to serialise data so it''s hard to give out a snippet. [ MSVC Fixes | STL Docs | SDL | Game AI | Sockets | C++ Faq Lite | Boost  Asking Questions | Organising code files | My stuff | Tiny XML | STLPort] ;quote:Original post by Kylotan  How are pointers relevant here?i suspect he meant "pointers" to mean "hints" or "tips"... at least that''s how i read it Corvwyn: all you have to do is save each and every member variable in a class, except for pointers (for these you must save what they point to, as the pointer value itself changes each time you run the program). to load it, just read each member variable from that file and put it back into the object. ;  I''m using XML to store the dialog tree. It''s very easy to parse (i use TinyXML) and edit\create (XMLPad, XmlShell with tree-views).The example of dialog tree in XML which also contains scripting info: <dialog script="dlg_start.script"><npc text=""Greetings!"" script="greeting.script"><you text=""How do you do?""><npc text=""Ok.""><you text=""Then let''s travel.""><npc text=""Travel? Ok!""><you text=""Let''s go..."" script="another.script"/></npc></you><you text=""News?""><npc text=""Your dialog system obviously works.""><you text=""Thanks. Bye.""/></npc></you><you text=""Ok, bye!""/></npc></you><you text=""Who are you?""><npc text=""I''m NPC. And you...""><you text=""I''m player.""><npc text=""Player, I gotta go.""><you text=""End.""/></npc></you><you text=""Not your business!!!"" script="change_alignment_to_evil.script"><npc text=""Then f. off!!!""><you text=""F. you man!""/></npc></you></npc></you><you text="End."/><you text="Secret dialog cue (maybe)!!!" condition="secret_condition.script"><npc text=""What have you done?..""><you text="End."/></npc></you></npc></dialog>Code (you''ll need tinyXML to compile it): // dlg.cpp : the simple XML-based dialog system #include "tinyxml.h"#include <windows.h>#include <iostream>#include <fstream>using namespace std;bool run_element_script(TiXmlElement* element); // executes script from the ''script'' attribute in XML-element if there''s anyint  run_condition_script(TiXmlElement* element); // executes ''condition''-script and returns the resultvoid console_clear(); // clears screen and returns cursor// XML elements'' namesconst char* DLG_DIALOG="dialog";const char* DLG_NPC="npc";const char* DLG_YOU="you";// XML attribs'' namesconst char* DLG_TEXT="text";const char* DLG_SCRIPT="script";const char* DLG_CONDITION="condition";HANDLE  hConsoleOut;    /* Handle to the console */CONSOLE_SCREEN_BUFFER_INFO csbiInfo;   /* Console information */int main(int argc, char* argv[]){/* Get display screen information & clear the screen.*/    hConsoleOut = GetStdHandle( STD_OUTPUT_HANDLE );    GetConsoleScreenBufferInfo( hConsoleOut, &csbiInfo );TiXmlDocument doc("dialog.xml");    doc.LoadFile();TiXmlHandle dlg(&doc);// first scriptrun_element_script(dlg.FirstChild(DLG_DIALOG).Element());// get npc''s greetingTiXmlHandle npc_cue_handle=dlg.FirstChild(DLG_DIALOG).FirstChild(DLG_NPC);TiXmlElement* npc_cue=npc_cue_handle.Element();if(!npc_cue){cerr<<"error: no first dialog cue"<<endl;return 1;}while(npc_cue){run_element_script(npc_cue);cout<<"NPC - "<<npc_cue->Attribute(DLG_TEXT)<<endl;// get your questionsint num=0; // number of your cuesTiXmlHandle your_cue_handle=npc_cue_handle.FirstChildElement(DLG_YOU);TiXmlElement* your_cue=your_cue_handle.Element();if(your_cue)num=1;cout<<endl;for(; your_cue; your_cue=your_cue->NextSiblingElement(DLG_YOU), num++ ){   if(run_condition_script(your_cue))  cout<<"  "<<num<<": "<<your_cue->Attribute(DLG_TEXT)<<endl;elsecout<<"  ("<<num<<": "<<your_cue->Attribute(DLG_TEXT)<<")"<<endl;}if(num==0) // no cuesbreak;cout<<"\nyou say> ";cin>>num; // your selected cue numberyour_cue_handle=npc_cue_handle.Child(DLG_YOU, num-1);your_cue=your_cue_handle.Element();console_clear();if(your_cue){run_element_script(your_cue);cout<<"\nYOU - "<<your_cue->Attribute(DLG_TEXT)<<endl;}else{cerr<<"error: wrong number"<<endl;return 1;}npc_cue_handle=your_cue_handle.FirstChild(DLG_NPC);npc_cue=npc_cue_handle.Element();}cout<<endl;return 0;}bool run_element_script(TiXmlElement* element){if(!element)return 0;const char* filename=element->Attribute(DLG_SCRIPT);if(!filename) // no attributereturn 0;// ...return 1;}int run_condition_script(TiXmlElement* element){if(!element)return 0;const char* filename=element->Attribute(DLG_SCRIPT);if(!filename) // no attributereturn 1;// ...return 1;}void console_clear(){    DWORD    dummy;    COORD    Home = { 0, 0 };    FillConsoleOutputCharacter( hConsoleOut, '' '', csbiInfo.dwSize.X * csbiInfo.dwSize.Y, Home, &dummy );SetConsoleCursorPosition(hConsoleOut, Home);}Goodluck! 