• Advertisement
Sign in to follow this  

text games

This topic is 4249 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I'm trying to make one of those old text based games, but i'm have some problems moving the player from room to room. I can't figure out how to transition to the next room object. Also, one of my variables is being changed and I can't figure out why. The description of the room the player starts in changes when you tell it you want to go north. You can't actually go anywhere yet because I can't figure out how to get the next room. Sorry if this seems vague, but it the best way I can think of to phrase it. I'm going to post the code, sorry it's so long. //main.cpp //main file #include <iostream> #include "room.h" #include "player.h" using namespace std; int main() { //SETUP //create 3 rooms room r1("A Room.",1); r1.setexit(1,"n","r2"); room r2("A big room.",2); r2.setexit(1,"n","r3"); r2.setexit(2,"s","r1"); room r3("A bigger room.",3); r3.setexit(1,"s","r2"); //create a player and put the player in room 1 player p1; p1.setrnum(r1); // //intro (*p1.getroom()).getdesc(); (*p1.getroom()).dispexits(); //main loop while(true) { string in; cin>>in; if (in == "quit") break; if (in == "look") { (*p1.getroom()).getdesc(); (*p1.getroom()).dispexits(); } if (in == "n") { if ((*p1.getroom()).checkexit("n")) { int num = (*p1.getroom()).checkexit("n"); string t = (*p1.getroom()).getexitto(num); cout<<t<<endl; } else cout<<"There is no way to go that way.\n"; } } system("pause"); } //room.h //room object class #ifndef ROOM_H #define ROOM_H #include <iostream> #include <vector> #include <string> using namespace std; class room { public: room(string d, int i): desc(d), num(i) {} void getdesc() { cout<<desc<<endl; } int getnum() { return num; } void setexit(int index, string s, string toroom) { if (index <= 10) { exit[index] = s; exitto[index] = toroom; } } string getexit(int index) { return exit[index]; } string getexitto(int index) { return exitto[index]; } void dispexits() { for (int i = 0; i < 10; i++) { if (exit != "") cout<<"Direction: "<<exit<<endl; } } bool checkexit(string t) { bool check = false; int num = 0; for (int i = 0; i < 10; i++) { num++; if (exit == t) cout<<exit<<" : "<<i<<endl; check = true; } return check, num; } private: string desc; int num; string exit["",10]; string exitto["",10]; }; #endif //player.h //player header file #ifndef PLAYER_H #define PLAYER_H #include <iostream> using namespace std; class player { public: player(string n = "unknown"): name(n) {} void setname(string n) { name = n; } string getname() { return name; } void setrnum(room n) { rin = &n; rnum = n.getnum(); } int getrnum() { return rnum; } room* getroom() { return rin; } private: string name; room* rin; int rnum; }; #endif Thanks again for all the help.

Share this post


Link to post
Share on other sites
Advertisement
I only looked at a fraction of your code, but one thing that jumped out is

void setrnum(room n) { rin = &n; rnum = n.getnum(); }
which is a mistake. You're passing the room by value, after which you take the address of the argument. You can't do that.

The arguments are always placed on the stack, which is a temporary piece of memory which only exists until the moment you return from the function. By taking the address of the argument and saving it in a data member, you're storing a pointer to no-man's-land.

If you need to keep a pointer to the room, use

void setrnum(room* n) { rin = n; rnum = n->getnum(); }


(I'd suggest you use the code or source tags to make your code more readable.)
(And what do you think
return check, num;

does?)
(Also, what does

string exit["",10];
string exitto["",10];

do? I'm unfamiliar with that syntax)

Share this post


Link to post
Share on other sites
I'm pretty sure that string exit["",10] would be the same as string exit[10], since the bit in the brackets would be interpreted as the comma operator, evaluate both expressions and return the right hand one. If this is not correct, I would also be fascinated to know what it does [smile].

2122 - I'm not being funny but you seem to be inventing syntax. I'd suggest you perhaps spend a bit more time with a good C++ text book then post back when you have more specific questions.

[Edited by - EasilyConfused on July 6, 2006 4:42:51 AM]

Share this post


Link to post
Share on other sites
Not to belittle what you are doing, but if you just want to make a text adventure, there are quite a few tools out there that do all the framework stuff for you already. I recommend TADS. That will save you a lot of work and get you right to the creation of the story.

Unless on the other hand the work of creating the framework is what you are interested in, continue.

Share this post


Link to post
Share on other sites
You could always "class it up". C# would be awsome for this but C++ would work too. Basically, you have something like:

struct CRoomExit
{
char* ExitCommand; // Command that takes you there, your engine should
// automatically change "N" to "NORTH", etc..
int RoomID; // The numeric value of the room this goes to
char* ExitDescription; // When describing exits, this is displayed for
// each of the actuial exits.
}
struct CRoom
{
char* Name; // The actuial name of the room
char* ShortDescription; // What they normally see when they enter
char* LongDescription; // First time entered, or "look"
int* Items; // Items in this room
CRoomExit* Exits; // Exits valid for this room
bool Visited; // If this is false, show full desc
}


Then you can have the actuial player struct:

struct CPlayer
{
char* Name; // If this is a 3rd person game and they can choose
int CurrentRoom; // Numeric value of the current room
int* Items; // The character's inventory
int Score // The current score
}


Basically, when you are in a room you can say:

cout << Rooms[Player.CurrentRoom]->Name << endl;
if ( !Rooms[Player.CurrentRoom]->Visited )
{
cout << Rooms[Player.CurrentRoom]->LongDescription << endl;
Rooms[Player.CurrentRoom->Visited = true;
} else
{
cout << Rooms[Player.CurrentRoom]->ShortDescription << endl;
}


Now,lets say they have typed in their input, and you have decided they chose "north". You have a function that loops through the room exits. If you see north is valid, you get the room ID. Then you simply set Player.CurrentRoom to the new room, and loop back to the room description thing.

The only other thing to watch for, is if you have scripts. Like locking and unlocking a door.. For that you may have to do some additional checks before an exit can be performed.

For example, if RoomA has a north exit to RoomB, in the function where you would set the current player room, you can say something like:

if ((Player.CurrentRoom == RID_ROOMA) && (TargetRoom == RID_ROOMB))
{
if (Conditions[CID_ROOMBUNLOCKED])
{
// go to room now
} else
if (PlayerHasItem(IID_ROOMBKEY))
{
cout << "You unlook the door with your key!" << endl;
Conditions[CID_ROOMBUNLOCKED] = true;
RemovedInventoryFromPlayer(IID_ROOMBKEY);
// go to the room now
} else
{
cout << "You must have the key first!" << endl;
}
}



Just some ideas :p

Share this post


Link to post
Share on other sites
Creating the framework is interesting, I'm doing this to see if I can.

About the syntax, string exit["",10] makes an array with ten indexes, and initializes them with and empty string variable, if you put something between the quotes it initializes the array with whatever is between the quotes in all ten "slots" of the array.

return check, num;
does just that it returns both variables. I was going to use the num variable to tell wich room to pick next, thats the step I haven't figured out yet.

How do I use code tags?

I'll try Dreq's idea, but I think that leaves me with one of the same problems as before. How do I use the the room number, instead of whatever name I gave the room object when it was instantiated to work with the room?

Thanks for the responses.

Share this post


Link to post
Share on other sites
Quote:
Original post by 2122
About the syntax, string exit["",10] makes an array with ten indexes, and initializes them with and empty string variable, if you put something between the quotes it initializes the array with whatever is between the quotes in all ten "slots" of the array.

return check, num;
does just that it returns both variables. I was going to use the num variable to tell wich room to pick next, thats the step I haven't figured out yet.


Not in C++ it doesn't. I would suggest learning the lanbguage a lttle more. Good Luck!

Share this post


Link to post
Share on other sites
Quote:
Original post by 2122
Creating the framework is interesting, I'm doing this to see if I can.

About the syntax, string exit["",10] makes an array with ten indexes, and initializes them with and empty string variable, if you put something between the quotes it initializes the array with whatever is between the quotes in all ten "slots" of the array.

return check, num;
does just that it returns both variables. I was going to use the num variable to tell wich room to pick next, thats the step I haven't figured out yet.

How do I use code tags?

I'll try Dreq's idea, but I think that leaves me with one of the same problems as before. How do I use the the room number, instead of whatever name I gave the room object when it was instantiated to work with the room?

Thanks for the responses.


You can see the code tags by clicking "edit" on the posts containing code (it does indeed work on other people's posts).

Can I know which source (book/site/...) you use for learning C++?

Share this post


Link to post
Share on other sites
Quote:
I'll try Dreq's idea, but I think that leaves me with one of the same problems as before. How do I use the the room number, instead of whatever name I gave the room object when it was instantiated to work with the room?


Pretty simple, you can either A: Create a funciton called:
int GetRoomIDByName( char* RoomName )


And have it loop through all the rooms, until you find the name that matches, and simply return the index you're on.

- or (what I would do) -
Define constants. Assuming you create the rooms dynamically, instead of just having a constant array, you could have like:

int RID_STARTINGROOM = CreateNewRoom();


Ans simply reference that value whenver you want to access that room. Though if you are going to be dynamically adding AND removing rooms, for whatever reason, you would most likely have to stick to RoomName (but make sure the names are all unique!).

Share this post


Link to post
Share on other sites
here is something i did a few months ago. its simple and uses the w a s d keys to move around. i was just messing around with structers when i did it. hope it helps.

main.cpp



#include <iostream>
#include <stdlib.h>

#include "textgame.h"

using namespace std;

int main(int argc, char *argv[])
{
init_game();
run_game();
end_game();
system("PAUSE");
return 0;
}

void init_game()
{
// init person
person.health = 100;
person.position = 0;
person.alive = 1;

// init house
house[0].name = "Entrance";
house[0].description = "entryway";
house[0].north = 4;
house[0].east = 2;
house[0].west = 1;
house[0].south = -1;

house[1].name = "Den";
house[1].description = "den";
house[1].west = -1;
house[1].south = -1;
house[1].east = 0;
house[1].north = 3;

house[2].name = "Foyer";
house[2].description = "foyer";
house[2].east = -1;
house[2].west = 0;
house[2].north = 5;
house[2].south = -1;

house[3].name = "Hallway";
house[3].description = "hallway";
house[3].east = -1;
house[3].west = -1;
house[3].south = 1;
house[3].north = 6;

house[4].name = "RecRoom";
house[4].description = "RecRoom";
house[4].south = 0;
house[4].north = -1;
house[4].west = -1;
house[4].east = 5;

house[5].name = "Hall";
house[5].description = "hall";
house[5].east = -1;
house[5].west = 4;
house[5].north = 8;
house[5].south = 2;

house[6].name = "Kitchen";
house[6].description = "kitchen";
house[6].east = 7;
house[6].north = -1;
house[6].south = 3;
house[6].west = -1;

house[7].name = "Bathroom";
house[7].description = "A small bathroom";
house[7].east = 8;
house[7].north = -1;
house[7].south = -1;
house[7].west = 6;

house[8].name = "Bedroom";
house[8].description = "bedroom";
house[8].east = -1;
house[8].north = -1;
house[8].south = 5;
house[8].west = 7;

};

////////////////////////////////////////////

void run_game()
{
while(person.alive) // while the person is alive
{
system("CLS"); // clear screen
showRoom(); // show what room we are in
getInput(); // get which way they want to go
}
}

///////////////////////////////////////////

void end_game()
{
cout << "Game Over!!!" << endl;
}

///////////////////////////////////////////

void getInput()
{
char choice;

cout << " choose your direction ..." << endl;
cin >> choice;

switch(choice)
{
case 'w': // use lower and uppercase incase of caps lock
case 'W':
if(house[person.position].north != -1) // make sure the room to the north is accessable
{
person.position = house[person.position].north; // if it is person is now in that room
}
else
{
wrongway(); // else they are going the wrong way
}
break;

case 's':
case 'S':
if(house[person.position].south != -1)
{
person.position = house[person.position].south;
}
else
{
wrongway();
}
break;

case 'd':
case 'D':
if(house[person.position].east != -1)
{
person.position = house[person.position].east;
}
else
{
wrongway();
}
break;

case 'a':
case 'A':
if(house[person.position].west != -1)
{
person.position = house[person.position].west;
}
else
{
wrongway();
}
break;

default:
cout << "Invalid Entry" << endl;
break;
}

}

///////////////////////

void wrongway()
{
cout << "There is no path in that direction." <<endl;
}

///////////////////////

void showRoom()
{
cout << house[person.position].description << endl;
}








textgame.h



#ifndef _TEXT_GAME_H
#define _TEXT_GAME_H

#include <cstring>


/*
[6]-[7]-[8]
| |
[3] [4]-[5]
| | |
[1]-[0]-[2]

*/


struct ROOM
{
std :: string name;
std :: string description;
int north; // holds the element or id # of the room to the north
int south; // holds the element or id # of the room to the south
int west; // west
int east; // east
};

struct PLAYER
{
int position; // should always be the same as current_room
int health;
int alive;
};

ROOM house[9];
PLAYER person;

void init_game(); // initialize all our data
void run_game(); // run our game loop
void end_game(); // end our game
void getInput(); // get user input
void wrongway(); // when the wrong direction is chosen
void showRoom(); // display the room name and description


#endif



Share this post


Link to post
Share on other sites
Quote:
Original post by 2122
About the syntax, string exit["",10] makes an array with ten indexes, and initializes them with and empty string variable, if you put something between the quotes it initializes the array with whatever is between the quotes in all ten "slots" of the array.


I'm afraid that is just plain wrong.

Would you care to share where you got this information from? My concern is that a lot of beginners use this site and I would hate to see misinformation spread via these forums.

Paul

Share this post


Link to post
Share on other sites
OP: Put your code in [source][/source] tags please. That will toss it into one of the little boxes, and preserve your indentation (you do have some of that, right?) And yeah, I have no idea where you got that idea about the string array syntax. Perhaps you are thinking of something like "std::vector<std::string>("", 10);"?

Dreq: The OP has one thing going for him that you don't: usage of std::string where appropriate. :
Rherm23: That's nice, but doesn't seem to be along quite the same lines. The OP seems to be looking for a bit more flexibility in terms of specifying directions.




Now then. It looks like the idea is:

- We have a bunch of room objects.
- Each room has a name, and a bunch of exits. Each exit has a name, and "connects to" a room.
- Given an exit name from the user, we want to find that exit in the current room, and then find the room that the exit connects to. We could do that by having a process that looks up a room by name, and then specifying the name as the exit target.

We don't need to model the actual exits - yet. However, we do need to model the process of finding an exit, and thereby an adjacent room.

We can do all of this without explicit pointers (of course, the standard library tools that we use will invoke lots of them behind the scenes on our behalf :) ). The idea is that we "look things up" by name, in an associative container. The C++ standard library provides such a beast, called std::map.

This is basically a container which, instead of being sequential, has "keys" of arbitrary value. We can index in with a key, and, if there is an associated "value" is stored in the map, get the value.

The std::map allows us to do this indexing with the operator[], so that it looks like "natural" indexing syntax (such as you would use with an array or vector). If the key is not found, operator[] will insert a key/value pair with the provided key and a default-constructed value (for std::string, that's a blank string), and then return a reference to that default-constructed value. Otherwise, it returns a reference to the default-constructed value that was already there. That behaviour is sometimes desirable though often not. When it isn't (and we can't be sure about what keys are present), we can use lower-level access functions, as will be illustrated.

What we will do is build a global map of strings to Rooms, and in each Room, build a map of strings to strings. That per-Room map represents the exits, mapping an exit name into a Room name.


// Just relevant fragments of code are shown; this is not a complete or
// working program

class Room {
typedef exitmap;
std::map&lt;std::string, std::string&gt; exits;

public:
// default constructor is fine so far.

void connect(std::string exitname, std::string roomname) {
exits[exitname] = roomname;
}

Room* neighbour(const std::string& exitname) {
// If the exit name is found, look for the room with the room name.
// If that room is found, return a pointer to it. The main loop will
// hold a pointer to the current Room.
// If there is no valid room to go to, return NULL.
exitmap::iterator it = exits.find(roomname);
return (it == exits.end()) ? 0 : findRoom(it-&gt;second);
}

void display(); // to implement

// other stuff?
};

typedef std::map&lt;std::string, Room&gt; worldmap;
worldmap world;

// Add a room to the world.
void add (const std::string& roomname) {
world[roomname];
}

// Add a connection between rooms, both ways.
// Add the rooms if they don't exist.
void connect(const std::string& room1, const std::string& exit1,
const std::string& room2, const std::string& exit2) {
world[room1].connect(exit1, room2);
world[room2].connect(exit2, room1);
}

// Get a pointer to the indicated Room.
// Return NULL if it does not exist.
Room* findRoom(const std::string& roomname) {
worldmap::iterator it = world.find(roomname);
return (it == world.end()) ? 0 : &(it-&gt;second);
}

// A sample main loop that handles movement between rooms, taking the user
// input line as the exit name.

void gameLoop() {
Room* location = findRoom("start");
std::string desiredExit;
while (getline(std::cin, desiredExit)) {
Room* target = location-&gt;neighbour(desiredExit);
if (!target) {
std::cout &lt;&lt; "Can't go that way." &lt;&lt; std::endl;
} else {
location = target;
location-&gt;display();
}
}
}

Share this post


Link to post
Share on other sites
I'm using Beginning c++ Game programing by Michael Dawson. I was wrong about the array thing, I was thinking of vectors.

This is what I was thinking of:

#include <iostream>
#include <vector>
using namespace std;

int main()
{

vector<string>x(10,"akjasf;pjs");

for (int i = 0; i < 10; i++)
cout<<x<<endl;

system("pause");

}



Sorry about this misinformation.

The return thing works just fine. I'm going to read up on the map container, but for this program I think I will just loop through the rooms.

Share this post


Link to post
Share on other sites
I started over and got it all to work. Thanks for all the help and examples. Is there any way to clear the screen of the text the program puts on it while it's still running?

Share this post


Link to post
Share on other sites
Quote:
Original post by 2122
The return thing works just fine.


Assuming you mean that "return x, y" works for returning both x and y, would you care to show me how you actually retrieve both those values?


int func()
{
return 1, 2;
}

a, b = func(); // ?


Also, AFAIK, these is no standard way to clear the console, except by printing a lot of std::endl. You might try system("cls") though, that should work.

Share this post


Link to post
Share on other sites
2122 - sorry to keep on but I should just clarify:

The return thing only APPEARS to work fine. What is actually happening is that the first value is being evaluted, then discarded then the second value is being evaluated and returned. This is due to C++'s comma operator.

Thus:

return 1,2;

is EXACTLY THE SAME AS

return 2;

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement