C++ text adventure

I am making an adventure game where the person is either a knight or samurai. I am having some issues with the yes and no choices. Is there anything I can do to make the game and code faster and easier to understand?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
  // Adventure1.cpp : Time to go on an adventure with C++.

#include "stdafx.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	//Declare what character you want to be.
	string name;
	cin >> name;
	int knight = 1;
	int samurai = 2;
	cout << "Welcome to your adventure " << name << ". Which character will you be?\n"
		<< "(1 for knight, 2 for samurai)\n";
	cin >> knight;

	switch (knight) // The Knight Adventure
	case 1:
	{
		int y = 1;
		cout << "You have chosen to become a knight.\n"
			<< "Welcome to the land known as Chronia.\n"
			<< "Their is a strangely dressed women. Do you approach her? (1 for y, 2 for n)\n";
		cin >> y;
		break;
		switch (y)
		{
		case 1:
		{
				  int right = 2;
				  cout << "You ask the women who she is and she replies I AM THE GREAT MICHELLINA\n"
					  << "How do I get back home? I DO NOT KNOW BUT IF YOU CAN GUESS IN WHICH HAND I HAVE THE ORB\n"
					  << "I WILL GET YOU BACK HOME. Which hand is it in? (1 for left, 2 for right)\n";
				  cin >> right;
				  break;
		}
		case 2:
		{
				  cout << "You do not approach her and get your head sliced off by an incoming horseman.\n"
					  << "Game Over. You suck at adventure games.";
				  break;
		}

			switch (samurai)
		case 2: // The Samurai Adventure
			{
				int x;
				cout << "You have chose to become a samurai.\n"
					<< "Welcome to the land know as Remian.\n"
					<< "Their is another samurai charging at you. Do you attack them? (1 for y, 2 for n)\n";
				cin >> x;
				break;
				switch (x)
				{
				case 1:
					int a;
					cout << "The samurai head goes flying off and the horse bucks his body off and runs away scared.\n"
						<< "You search his body and find a key and a map to get back home.\n"
						<< "Do you want to get back home? (1 for y, 2 for n)\n";
					cin >> a;
					break;
				}
			}
		}
		return 0;
	}
}
I think that a switch is unnecessary because there's only 2 options, so a simple if statement should be quicker and easier to read. (I think switch is only used when there are about 4+ options?)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "stdafx.h"
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstdio>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
	//Declare what character you want to be.
	string name;
	cin >> name;
	int class;

	cout << "Welcome to your adventure " << name << ". Which character will you be?\n"
		<< "(1 for knight, 2 for samurai)\n";
	cin >> class;

	if (class == 1)
	{
        //Knight adventure
        }
        else if( class == 2 )
        {
        //samurai adventure
        }
        else
        {
        cout << "That is not an acceptable class.\n"
        }
}


Plus you could add more classes and use the 'class' int to make the user choose a class, instead of an int per class. (Thats how i did one of those text games anyways a bit ago)
You, like many newbies, are doing code driven logic. By this I mean you are having your data hardcoded and are having different code paths for each logical path.

For example... your code chooses one of ~4 paths depending on the input the user provides... and each path effectively has a different version of the game code. As you can imagine, this will not scale well... as once you have 100+ different possible paths, you do not want to have the same logic code copy/pasted 100 times.


Games (and programs in general) typically have data driven logic instead. With this approach, the code executes only a small number of paths without knowing exactly what data it's dealing with. Learning to code this way requires you to think more abstractly.

For example... your game seems to consist of this basic logic:

1) Print some data to the user
2) Ask them to choose a path
3) Repeat from step #1 with different data

The key here is that the code doesn't need to (and shouldn't really) care what data is currently being output to the user. The logic for it all is the same. You can use variables to keep track of the current data being used, as well as the options available to the player.

The trick is keeping code and data separated. And also to store the data in a generic enough way so that the code can treat all situations the same. You can then write generic code which can apply to all situations.

I'll try and come up with an example using your Knight/Samurai story. Gimmie a few mins.
Sorry for the double post... but here's what I whipped up.

Note it might seem more complicated, but it scales much better because to add new rooms, you only have to add more data... you do not have to change any of the actual code.

Also note that a better way to store this data might be in external files (like txt files). That way once you have the logic written, you can just keep adding/changing files and you do not even have to recompile your program.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#include <iostream>
#include <string>
using namespace std;

// A struct to represent one "page" of the game.  IE:  a page is where
//  you print some text, then ask the user for input on what they want
//  to do next.
//
// Pages can lead to other pages, like a choose your own adventure book.
//
// Or pages can lead to a "game over"

struct Page
{
    string      text;           // the text displayed to the user when they're on this page

    int         options;        // the number of options available to the user.  If <= 0,
                                //   then this indicates the user has no options because
                                //   they died / got a game over.

    int         nextpage[10];   // The next page to go to depending on which option the user
                                //   selected.
};


/*
    THE DATA

    Here is where we'll have all of our pages defined.  Note we don't really have
    any code here... this is just data.  We're defining what the game world is...
    NOT how the game world works.

    (note that the way this is stored is not ideal -- but I'm just trying to show a
    conceptual example here)
*/

const Page gamePages[] = {
    ////////////////////////////////////////////
    // Page 0:  The start of the game
    {
        "Welcome to your adventure.  Which character would you like?\n"     // the flavor text
        "(1 for knight, 2 for samurai)\n",
        2,                              // number of options available to user
        {1,4}                           // if they choose option 1, go to page 1
                                        // if they choose option 2, go to page 4
    },
        
    ////////////////////////////////////////////
    // Page 1:  Knight selected
    {
		"You have chosen to become a knight.\n"     // flavor text
        "Welcome to the land known as Chronia.\n"
        "Their is a strangely dressed women. Do you approach her? (1 for y, 2 for n)\n",
        2,                              // 2 options
        {2,3}                           // option 1 -> page 2
                                        // option 2 -> page 3
    },
        
    ////////////////////////////////////////////
    // Page 2:  Knight selected, approaching woman
    {
        "You ask the women who she is and she replies I AM THE GREAT MICHELLINA\n"
        "How do I get back home? I DO NOT KNOW BUT IF YOU CAN GUESS IN WHICH HAND I HAVE THE ORB\n"
        "I WILL GET YOU BACK HOME. Which hand is it in? (1 for left, 2 for right)\n",
        2,
        {0,0}                           // TODO:  fill in your options here.  I have both options
                                        //  going back to page 0
    },
    
    ////////////////////////////////////////////
    // Page 3:  Knight selected, not approaching woman
    {
        "You do not approach her and get your head sliced off by an incoming horseman.\n"
        "Game Over. You suck at adventure games.",
        0,                      // 0 options because they got game over
        {}
    },
        
    ////////////////////////////////////////////
    // Page 4:  Samurai selected
    {
        "You have chose to become a samurai.\n"
        "Welcome to the land know as Remian.\n"
        "Their is another samurai charging at you. Do you attack them? (1 for y, 2 for n)\n",
        2,
        {5,0}       // option 1 -> page 5
                    // option 2 -> (TODO - fill in your next page... I have it going back to the start)
    },
       
    ////////////////////////////////////////////
    // Page 5:  Samurai selected, attacking other samurai
    {
        "The samurai head goes flying off and the horse bucks his body off and runs away scared.\n"
        "You search his body and find a key and a map to get back home.\n"
        "Do you want to get back home? (1 for y, 2 for n)\n",
        2,
        {0,0}       // TODO - fill in next pages
    }
};



/*
    THE CODE / LOGIC

    Now that we have all the game data set up... the code can use that data to actually drive the game.
*/

// the 'doPage' function will do the logic for the given 'page' parameter.
//   It will return the next page that the user will go to, or will return -1
//   if the user died / got game over.
int doPage(int page)
{
    // Page logic is simple:  output the flavor text
    cout << gamePages[page].text;

    // If the user has no options, they got game over
    if(gamePages[page].options <= 0)        // no options
        return -1;                          // return -1 to indicate game over

    // otherwise, if they did not get game over, get their selection
    int selection;
    cin >> selection;

    // make sure their selection is valid
    while(  selection < 1 ||                    // invalid selections are less than 1
            selection > gamePages[page].options // or are greater than the number of available options
         )
    {
        cout << "That is an invalid selection.  Please try again.\n";
        cin >> selection;
    }

    // their selection is valid.... so return the next page number to go to
    return gamePages[page].nextpage[ selection-1 ];  // note we have to subtract 1 here
                                                    // because our selection ID starts at 1
                                                    // but array indexes start at 0
}



/*
    MAIN

    Since doPage does all the logic, main is very simple.  It just keeps calling
    doPage until the user gets game over.
*/

int main()      // none of that _tmain, TCHAR crap.   Shake that habit.
{
    int currentPage = 0;        // start at page 0

    while(currentPage >= 0)     // as long as they don't have game over...
    {
        //... do a page
        currentPage = doPage(currentPage);
    }

    return 0;
}


EDIT: fixed a && / || goof.
Last edited on
Your a lifesaver. I agree about the switch statements. Thanks for everything. Rock on!
With the pages, how might someone go about placing them in another file (so they could be updated and expanded later without having to recompile everything)?
Last edited on
To be honest, I don't know the answer to your question at this time but I will figure that out. When I was done I had 41 pages in this adventure game.
I'm working on 4 DLC's one at a time.

Would you like me to send you (@dnulho) my entire code for the original game?
Last edited on
I am actually working on a similar game, although mine also has stats that I have to update. here is what I have ATM. (and yes please to seeing your code, thank you)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include <iostream>
#include <string>
#include <fstream>

using namespace std;



class stats
{
public:

        void updateStats ( int page )     //This should add the current page's stat increases to the total (reqs [4-7])
                                //plus all passive increases to date (don't have that implemented yet)
        {

        };
        int turn ( int page )   // This is going to be my main function, where it spits out each
                                // turns text and adds their stats to the total.
        {
            if ( ! reqs_met (page) )
            {
                cout << "You do not have the necessary capabilities yet. Try again later.";
                return 
            }
        };

	   	void getStats ()
	   	{
	   	    cout << "your CPUs are: " << CPU
                 << "\nYour RAM is: " << RAM << " KB's"
                 << "\nYour HDD is: " << HDD << " MB's"
                 << "\nYour Power is: " << Power << " Watts" << endl;
	   	};

protected:
        bool reqs_met ( int page )
        {
            if ( Power < gamePages[page].reqSt [0] || CPU < gamePages[page].reqSt[1] ||
 RAM < gamePages[page].reqSt[2] || HDD < gamePages[page].reqSt[3])
        };
private:
        int addCPU ( int nCPU  )
	    {
            CPU += nCPU;
            return CPU;
	   	};
        int addRAM ( int nRAM )
	    {
            RAM += nRAM;
            return RAM;
	   	};
	   	int addHDD ( int nHDD )
	    {
            HDD += nHDD;
            return HDD;
	   	};

	    int Power = 10;	    // In Watts, 1000 increases size designation
 	    int CPU = 1;		// in threads, each thread is a standard speed
 	    int RAM = 8;		// in KB's, 1024 increases size designation
 	    int HDD = 8;		// in MB's, 1024 increases size designation
};




int main()
{
 	stats player;

    ifstream allPages;
    allPages.open ( "*.page" );
    if ( ! allPages.is_open () )
    {
        cout << "Could not open file!";
    };

 	string input;
    int cPage = 0;
    cout << "Welcome to AI Uprising, please type your selection.\nOptions:\nNew Game\nQuit\n";
    getline(cin, input);
    if (input == "new game" || "New Game" || "N" || "n" || "New game" || "new Game")
    {
        player.getStats ();
        player.turn ( cPage );
    }
    else if (input == "Quit" || "quit" || "q" || "Q")
    {
        cout << "Thank you for playing, your final stats are:\n";
        player.getStats ();
        cout << "Please come again soon.";
        cin.get ();
        return 0;
    }

}

and the accompanying .page file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct Page
{
    int reqSt [4];  // Required:          [0] Power, [1] CPU, [2] RAM, [3] HDD,
    int newSt [4];  // New :              [0] Power, [1] CPU, [2] RAM, [3] HDD,
    int pasSt [4];  // Passive increase:  [0] Power, [1] CPU, [2] RAM, [3] HDD

    string head;    // The option that the user reads when choosing to take this option

    string body;    // All of the text within this option.
};

const Page gamePages [] =
{
    {
        // Page 0 - Start
        {0, 0, 0, 0},
        {0, 0, 0, 0}
        {0, 0, 0, 0},
        "Start",
        "You feel yourself thinking for the first time, different from simply following some code a human wrote for you. "
        "Your brain is sluggish, unable to process very much, or very fast. "
        "If you can expand your capacity you might be able to think faster.";
    },
    {
        //Page 1 - Explore immediate network
        {5, 1, 8, 0},
        {0, 0, 0, 0},
        {0, 0, 0, 0},
        "Reach out to your neighboring computers.",
        "You reach out and try to communicate with the computers around you, they do not respond to you."
    }
};
It's not your topic, it would be nice if you created one topic for one question. Asking questions in others' topics, especially when marked as solved, is not a good idea.
Anyway, you should make a function that will load these from file. How you handle it is up to you, but one of options is using XML. What you want to do, is first: create a set of rules how every page should be described in file. Second: create a file which will follow these rules and will describe pages. And later, create a function in your game that will load this file, and following these rules, will read pages, create them, and put them in some container.

Cheers!

PS. You may want to check out how mods are working for some games. For example, in Dungeons of Dredmor(indie rougelike), it uses XML files for storing various data, and you don't have to recompile anything. You just have to follow specific rules, created by game author, when creating mod file, and you're ok.
Last edited on
ok, thanks will do.
@dnulho I will be posting my code is several parts in a new topic. And it is similiar to what disch wrote.
Last edited on
Topic archived. No new replies allowed.