Thread: Handling movement in text-adventures.

  1. #1
    Registered User
    Join Date
    Jan 2005
    Posts
    106

    Handling movement in text-adventures.

    Okay, I've search the forum, and the main method that people suggests involved setting up a 2D array of integers to be a coordinate plain, and basically charting out movement on that.

    My main problem with this is that it'd call for a large number of if-statements to check the player's position.

    I had a variant on this, whic basically involved using multiples of two and threes to generate a massive grid of non-repeating numbers. For all intents and purposes, it worked the same way, but there was no messing with arrays.

    Same problems remain, though.

    I also saw gameprogramming's text adventure. I found there method to be totally worthless.

    Now, on IRC a while back, a guy was talking about how he was doing HIS text-adventure.

    Basically, he had a room class, and from that, room objects. Each room object would point to/contain pointers to whatever rooms were surrounding it, I think. For instance, I think that roomA's toNorth variable would be a pointer to roomB's toSouth variable, and vice versa. This sound somewhat wrong to me, but that's the best I can recall.

    I also seem to recall some sort of current_room object, which would basically pick up aspects of whatever room had been moved into by the player.

    In other words, the gameworld was not a grid, but constructed of several objects linked by pointers.

    Does anyone get what I'm talking about, and could work through my gaping headwound of a description and figure out what I'm trying to do or something?

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    You mean something like my reply here
    http://cboard.cprogramming.com/showthread.php?t=21974
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  3. #3
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    "His method" is a standard method used in MUDs. (Think DIKU.) Typically the room structure is something like this:
    Code:
    struct room
    {
        ...room information...
        struct room *exits[DIR_MAX];
    };
    We'll use the standard 6 points of movement for this exercise.
    Code:
    #define DIR_NORTH 0
    #define DIR_EAST 1
    #define DIR_SOUTH 2
    #define DIR_WEST 3
    #define DIR_UP 4
    #define DIR_DOWN 5
    #define DIR_MAX 6
    Or if you prefer enumeration:
    Code:
    enum { DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST, DIR_UP, DIR_DOWN, DIR_MAX };
    Then you make pointers to the other rooms. Another alternative would be to use their vnum approach.
    Code:
    struct room
    {
        unsigned long vnum;
        ...other relevant information for the room...
        unsigned long exits[DIR_MAX];
    };
    Now you simply have an array of numbers which is the number for the room they're linked to in a given direction. Now this isn't really as fast as the pointer method, at least at runtime, because you have to have a way to fetch the room based on a vnum. So you'd want to have functions for things like that.

    The pointer method works well, because by accessing the pointer you can obtain all the information of the room they're heading to, or what not. That way you can see oh, I don't know, if the room is full, or full of water which will disallow access if they can't swim, or what not. With the direct number assosiation method, you have to look up the room to check all of that.

    Rooms are stored in files and when they're read in, they're all stashed nicely away. Each room stores the vnum of the room in a given direction that they're supposed to link to. (They actually split it, IIRC, so there's a room template (not C++ template, but the concept of a template itself, as not related to C++ programming) which stores this type of information, and the room itself is a seperate instance with more data. The template stores information that doesn't change about the room usually. Things like it's description, its exits (what number of room it links to in each direction, not a pointer) and what not.

    The room instance has the actual pointer-links to the rooms whose vnums are stored in the template, the current state of things, people in the room, creatures in the room, objects in the room (all pointers to lists), etc.

    Quzah.
    Hope is the first step on the road to disappointment.

  4. #4
    Registered User
    Join Date
    Jan 2005
    Posts
    106

    dr.yes

    Hm. Well, I hashed something out last night. It's got a lot of fragments in it, but I think I'm getting somewhere. At the very least, I got current_room to act as a pointer to other room objects and sort of absorb their stats.

    However, it crashed after the parser() function. Anyway, here's what I have.

    Code:
    #include <iostream>
    #include <string>
    #include "roomClass.h"
    
    using namespace std;
    
    room farm;
    room hall;
    room *current_room;
    
    void parser(string to_parse)
    {
         if(to_parse == "north")
         {
              current_room = current_room->at_north;
         }
    
              else if(to_parse == "south")
              {
                   current_room = current_room->at_south;
              }
    
              else if(to_parse == "east")
              {
                   current_room = current_room->at_east;
              }
    
              else if(to_parse == "west")
              {
                   current_room = current_room->at_west;
              }
         else
         {
              cout << "Invalid Command." << endl;
         }
         return;
    }
    
    void get_input()
    {
         string command;
         cout << current_room->room_desc << endl << endl;
         cout << "> ";
         cin >> command;
         cout << endl;
         parser(command);
    }
    
    int main()
    {
         farm.init("A small farmery at the dusk of god.", &hall, &farm, &farm, &farm);
         hall.init("A darkly hallish hallway.", &hall, &farm, &hall, &hall);
         current_room = &farm;
         get_input();
         return 0;
    }
    And class.h

    Code:
    #include <string>
    
    using namespace std;
    
    class room
    {
         public:
    
         /*room set_to(room *getr)
         {
    
              this->room_desc = getr->room_desc;
              this->at_north = getr->at_north;
              this->at_south = getr->at_south;
              this->at_east = getr->at_east;
              this->at_west = getr->at_west;
         } */
    
         void init(string desc, room *room1, room *room2, room *room3, room *room4)
         {
              room_desc = desc;
              at_north = room1;
              at_south = room2;
              at_east = room3;
              at_west = room4;
         }
    
         string room_desc;
         room *at_north;
         room *at_south;
         room *at_east;
         room *at_west;
    };
    Hm... Having current room pointing back at whatever it's currently pointing at... would that be what's crashing it?
    Last edited by suzakugaiden; 01-24-2005 at 04:26 PM.

  5. #5
    Registered User
    Join Date
    Jan 2005
    Posts
    106
    Actually, I think I left the while loop out of the get_input or whatever function...

    Oddly enough, compiling it in Dev-C++ 4 makes it work perfectly. Weird.

  6. #6
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The parser should tokenze the sentence based on grammatical consistencies. This will allow your parser to be much more flexible.

    First define a word:

    {<A-Z,A-Z,...>}

    Then define some tokens in grammar.

    {<.,!"">}

    1. Start your parser in_whitespace mode.
    2. When the first letter is encountered, it switches to in_text mode.
    3. While in in_text mode move the letters from the command string to another string.
    4. When you reach a space, you know you have parsed in 1 word.
    5. Check the word, tokenize it according to your vocab and save it.
    6. Continue process - if you encounter a string from your grammar tokens definition you know you have reached some type of grammatical token in the sentence.
    7. Also check for the presence of important words like {<or, and, but, the, yes, no>}


    So now with your grammar you can create sentences like:

    Take the <object>, open the door to the north, and then look at the room.

    This is a lot less frustrating for your players and will make your parser really stick out amongst the crowd. Several parsers have ruined very good text games. You want your players to solve the puzzles in your game, not solve how to type in phrases so your lousy parser code can interpret them.

    Look at all of the fan base that Infocom still has and yet the company is completely defunct. Their parsers were some of the best ever made and the players really appreciated the extra work put into them by Infocom.

    A text adventure by Infocom was unlike any other.

  7. #7
    Registered User
    Join Date
    Jan 2005
    Posts
    106
    The "parser" I have running there is an incredibly simple testing one.

    I had a far more complex and flexible one written; however, I wasn't experienced at the time, so it's a bit bloated and the method is weird. It basically makes everything lower case, cuts out a certain number of defined words (The, of, etc.), and takes the remaining first three nouns, assuming that all input sentences work like, "Verb the object," or "Verb the object to ind. object."

    Of course, I'm going to be rewriting all of that.

  8. #8
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Personally, I find that method of a parser to be too much of a pain in the ass for a user. No one likes to have to guess as to the correct wording to get something done. A much simpler approach is commonly used in muds:

    <command> <optional arguments>

    "walk north"
    "north"
    "get bread"
    "get bread from backpack"
    "wield sword"

    Basicly you just peel off the first word, and see if it's in your list of commands. Optionally, you could peel the first word and compare it against every object around which has that functionality. Example:

    "take sword"

    The 'door' object cannot be 'take'n, so it skips that. The 'ground' object can't be taken either. Your shoes can be taken, but they're not in a state to be taken right now, so it's skipped. The flower growing here can be taken, but it isn't called 'sword'. The sword in the stone can be taken, but only if you're a king, but it does have the name of sword, so it responds with the appropriate action.

    It just depends if you want the objects to have a list of associated actions, or if you want a list of commands which is by itself that parses everything and then looks around at the arguments on a per command basis.

    Code:
    void command_take( Actor *a, string args )
    {
        Object *o = a->in_room->objects;
    
        while( o )
        {
            if( !is_word( args, o->names ) )
            {
                o = o->next_object;
            }
            else
            {
                break;
            }
        }
        if( !can_be_taken( o ) )
        {
            output( "You cannot take that!", a );
        }
        if( a->getcapacity() =< o->getweight() )
        {
            objectfrom( o, o->in_room );
            objectto( o, a );
            output( "Ok.", a );
        }
    }
    Again, this really depends if you want each object to have functions for getting taken, opened, moved, pushed, pulled, stacked, burned, etc, or, if you want to have special cases in each action function to handle all of these scenarios.

    However, I've personally never been a fan of "full sentence" parsers:

    "Run north, jump over the gate and draw my sword."

    Also, if you're getting pummeled by an Ogre, you don't wan't to have to form a whole sentence:
    "An Ogre **SMASHES** you with its club!"
    "OUCH! That really did HURT!"
    "You wish your wounds would stop BLEEDING so much!"

    > "Take the blue potion from my backpack, pull the cork, and drink it!"

    "You take the blue potion from your backpack."
    "You pull the cork from the blue potion."
    "You cannot drink a cork!"
    "An Ogre **SMASHES** you with it's club!"
    "You fall to the ground... DEAD!"
    "An Ogre **SMASHES** you with its club!"
    "OUCH! That really did HURT!"
    "You wish your wounds would stop BLEEDING so much!"

    > "quaff blue potion"

    "You quaff a potion!"
    "You feel much better now!"
    Considering how few people on this forum can actually compose full sentences on a regular basis...

    "plz hlp wt hmrk thx iz du 2mrow"


    However, do consider that some people like full sentences. You want an interface that's quick to learn and easy to use. One that isn't horribly tedious, or cryptic.

    Quzah.
    Last edited by quzah; 01-25-2005 at 06:04 AM. Reason: Added fun with Ogres!
    Hope is the first step on the road to disappointment.

  9. #9
    Self-Taught Noob
    Join Date
    Jan 2005
    Location
    Ohio
    Posts
    38
    back to the movement in my map i made a coordinate plane that will work for what you want.

    iactivated two ints called x and y, if you want a 3rd dimension make a z.

    then you can set rooms and functions based on what point they are in the plane you can also make people move pretty easily this way
    No one died when Clinton lied.

    Compiler: Borland C++ Builder
    OS: Windows XP

  10. #10
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    The full sentence was only an example. Even if the full sentence is not used the parser will still understand the phrase. Using Backaus Naur form will most certainly produce a much better parser than stripping just noun and verb from the user.

    With Backaus Naur format you can also create a scripting language quite easily for your text game or any other game for that matter. I'd seriously consider looking into it. Your text adventure game isn't any fun if the parser is so picky it rejects about 80% of the phrases.

  11. #11
    Registered User
    Join Date
    Jan 2005
    Posts
    106
    quzah: Something I learned from playing ADOM. The single most important command in a text-based game with lots of commands is "help."

    Anyway, about the parser method...

    To put it... uh... I'm doing this thing as a learning project. In other words, I have a tenuous grasp at some of these things, and I'm doing a relatively non-complex text-adventure thing to learn them, and as I pick up more advanced techniques, I'll be remaking/rewriting/retooling stuff as I go along.

    Anyway, back to the parser. That sort of goes back to handling items, which I'm still not entirely certain about. I was wondering if I should have the item objects pointing back to the room they were located in. The main problem I see here is that I don't know how to check if the object is in said room, for when you tried to interact with it, other than constructing a long chain of if statements, and I somehow think that that's NOT the only way.

    Also, at risk of sounding like a moron, might I ask what tokenization is, in reference to the command list?

  12. #12
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Tokenizing is when you break something up into blocks. To tokenize this sentence, you'd split it up with the "token" being the "space", which would give you each word.

    As for objects, you may want to again pull a page from muds. They typically do something like this:
    Code:
    struct object
    {
        struct object *prev, *next; /* the entire list of objects */
        struct object *prev_in_room, *next_in_room; /* the list of what's in the same room as this */
        struct room *in_room; /* the room this object is in */
        struct person *owner; /* the person who has this */
        ...stuff...
    };
    
    struct room
    {
        ...stuff...
        struct object *objects_here;
        struct person *people_here;
        ...stuff...
    };
    That sort of thing. When an object goes into a room, it is added to the list of objects in that room. It is linked both ways. It is also unlinked both ways when it is taken from a room. The same applies when it's put on a person, or removed from them. Any action of moving an object or person links and unlinks the object correctly. Here's an example:
    Code:
    struct object *object_from_room( struct object *o )
    {
        struct room *r;
        r = o->in_room;
        
        /* First we unhook it from the room itself... */
        if( r->objects_here == o )
        {
            /* we're the top of the list, let's fix that... */
            r->object_here = o->next_in_room;
        }
    
        /* Now delink from the entire list of room objects... */
        if( o->prev_in_room )
            o->prev_in_room->next_in_room = o->next_in_room;
        if( o->next_in_room )
            o->next_in_room->prev_in_room = o->prev_in_room;
    
        /* We're now cut from this room, linked to no room... */
        return o;
    }
    You can apply the same concept to pulling the object off of a person. Putting it in a room, etc. To move from a room to a person, you'd do something like:
    Code:
    struct object *o;
    ...stuff...
    
    o = object_from_room( o );
    if( o )
        object_to_person( p );
    Now I've left out a bit of error checking, and there may be a few variables hanging around which you won't need or hadn't considered, but I'll leave all that up to you.

    Quzah.
    Hope is the first step on the road to disappointment.

  13. #13
    Registered User VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,607
    You can also use the non-pointer method and assign each object to have a roomID. When the roomID of the object matches the current room you show it as being in the room. I normally use a roomID of 0xFFFF to indicate that item is in inventory, not inside of a room.

  14. #14
    Self-Taught Noob
    Join Date
    Jan 2005
    Location
    Ohio
    Posts
    38
    another way to do items is int he class or struct you use reference a boolean to each room its in if you are in the room and the item is also pick it up and then at that time turn the boolean into false and then the program if coded right can give the error

    object is not here

    or sometthing similar
    No one died when Clinton lied.

    Compiler: Borland C++ Builder
    OS: Windows XP

  15. #15
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by Bubba
    You can also use the non-pointer method and assign each object to have a roomID. When the roomID of the object matches the current room you show it as being in the room. I normally use a roomID of 0xFFFF to indicate that item is in inventory, not inside of a room.
    The problem with this method is that you end up looping through the entire object list every single time you move to another room. Bad idea when you have a good sized number of objects. You don't even really save much memory here, because you still have to have an integer to mark the room number. Where as with the addition of one pointer more (assuming that the size of an integer and one pointer are roughly the same), you can walk backwards and forewards through the room's object list at will. Plus you gain a ton of speed over looping through the entire list of objects for everything you do.

    Quzah.
    Hope is the first step on the road to disappointment.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. DirectX | Drawing text
    By gavra in forum Game Programming
    Replies: 4
    Last Post: 06-08-2009, 12:23 AM
  2. event handling is serialized in MS Visual Studio C++ 2005 ??
    By mynickmynick in forum Windows Programming
    Replies: 3
    Last Post: 08-07-2008, 04:47 AM
  3. Scrolling The Text
    By GaPe in forum C Programming
    Replies: 3
    Last Post: 07-14-2002, 04:33 PM
  4. Replies: 1
    Last Post: 07-13-2002, 05:45 PM
  5. Ok, Structs, I need help I am not familiar with them
    By incognito in forum C++ Programming
    Replies: 7
    Last Post: 06-29-2002, 09:45 PM