Putting All the Pieces TogetherWe have all the components of the complete parser and text interpreter engine. Let's see how all the pieces go together to form the backbone of a text game (see Figure 14.4). Referring to the figure, we see that the input stream is broken into words by the front end of the lexical analyzer, then the word strings are converted into integer tokens. The token stream is then fed into the front end of the syntactic parser, which "sends" the sentence of tokens to the proper action function. The action function then tries to apply the action requested by the player to the object(s) in the sentence. As the action is being applied, syntax checking and semantic integrity are also being tested simultaneously by the action function. Figure 14.4. The sections of the input parser.The important concept to grasp is that all the input parsing, syntax checking, and so forth are for the single reason of trying to figure out what the player wants to do. Once this is determined, then the coding is straightforward. The only caveat is that the actions performed are acted upon internal data structures. And the only output is text. Remember, the player only "views" the game universe by way of strings of static or synthesized text that the game outputs as each "move" is made in the game. Hopefully, you have a grasp of how the text input phase of a text game works. If I were to leave the subject now, you should be able to make a text game, but instead let's cover some techniques to control and manipulate other aspects of a text game. However, be warned these techniques are illustrative only and I'm sure there are better, different, and more clever ways of doing them. Representing the UniverseThe universe in a text game can be represented in many ways. Whatever representation is used, it should have some kind of geometrical relationship to the virtual environment the player is running around in. For example, if you created a full 3-D space text adventure then you might have a 3-D model in a database. Even though the player would never see the world visually, all collision detection and motion would be done within the 3-D model. Since this book is primarily about 2-D games, we'll make our representations more flat. For a moment, I want you to think of a one-story house. Imagine the roof is removed and you are looking down at the house from above (you're in a hover craft; see Figure 14.5). You could see the rooms of the house, the objects, and the people moving around. This is the kind of representation we want to use for our text games. Figure 14.5. A top-down "blue print" view of an environment.How do we implement a data structure of such a model? It can be done in two ways. We could use a vector-based model based on lines and polygons. There would be a database of this geometry and the game code would use it to move the game objects around. This technique is fine, but a bit hard to visualize since you would be working in pure "vector space." A better approach would be to have a 2-D matrix of cells that represented the game universe. Admittedly, you will never "see" the world, but having an actual 2-D floor map that can be accessed very simply will make the game code very simple to write. For example, the game Shadow Land uses a 2-D matrix of characters to represent the game universe (which is my old apartment). Take a look at Listing 14.8 to see the actual data structure used in the game. Listing 14.8 The Data Structure Used to Represent the Game Universe of Shadow Land// this array holds the geometry of the universe // l - living room // b - bedroom // k - kitchen // w - washroom // h - hall way // r - restroom // e - entry way // o - office // ^ // NORTH // < WEST EAST > // SOUTH // v char *universe_geometry[NUM_ROWS]= {"********************************", "*lllllllll*bbbbbbbbbbbbbbbbbbbb*", "*llllllllll*bbbbbbbbbbbbbbbbbbb*", "*lllllllllll*bbbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbbbbbbbbbbbbbbb*", "*llllllllllll*bbbb*rrr**********", "*lllllllllllhhhhhh*rrrrrrrrrrrr*", "*lllllllllllhhhhhh*rrrrrrrrrrrr*", "*lllllllllhhh******rrrrrrrrrrrr*", "*********hhhh*rrrrrrrrrrrrrrrrr*", "*kkkkkkk*hhhh*rrrrrrrrrrrrrrrrr*", "*kkkkkkk*hhhh*rrrrrrrrrrrrrrrrr*", "*kkkkkkk*hhhh*rrrrrrrrrrrrrrrrr*", "*kkkkkkkhhhhh*******************", "*kkkkkkkhhhhhhhhhhhwwwwwwwwwwww*", "*kkkkkkkhhhhhhhhhhhwwwwwwwwwwww*", "*kkkkkkk*hhhhhhhhhhwwwwwwwwwwww*", "*kkkkkkk*hhhh*ooooo*************", "*kkkkkkk*hhhh*ooooooooooooooooo*", "*kkkkkkk*hhhh*ooooooooooooooooo*", "*kkkkkk*hhhhh*ooooooooooooooooo*", "*******hhhhhh*ooooooooooooooooo*", "*eeeeeeeeeeee*ooooooooooooooooo*", "*eeeeeeeeeeee*ooooooooooooooooo*", "*eeeeeeeeeeee*ooooooooooooooooo*", "********************************",} ; It might be a bit hard to see the rooms due to the print font, but if you blur your eyes a little, you will be able to see all the rooms. You will notice that walls are defined by the '*' character and different rooms are defined by placing a single character within the confines of the room, such as 'o' for office. Placing characters in the each room that describe the room type is helpful in determining where the player is at any time. If the player is placed at any (x,y) position in the map, then the code that prints out "where" the player is only has to index into the array and look at the character the player is standing on. Then, using this information, the appropriate text strings can be displayed. Placing Objects in the WorldObjects in the game world can be done using two main techniques. The first technique is to have an array or linked list of objects where each element defines the object, its position, and any other relevant properties. The only problem with this technique is that when a player is in a room and asks to "look," then the object list must be accessed and visibility of each object must be determined. Also, collision detection, "putting," and "getting" are more complex when the objects are stored in this way. An easier method that is related to the way the universe is defined is to have another 2-D matrix of characters with the same geometry as the universe map. However, instead of walls and floor tiles within it, the objects are placed in it. Then you can imagine that both data structures are synthetically overlaid by the game code. For example, Listing 14.9 is the object placement map for Shadow Land. Listing 14.9 The Object Placement Database for Shadow Land// this array holds the objects within the universe // l - lamp // s - sandwich // k - keys // ^ // NORTH // < WEST EAST > /// SOUTH // v char *universe_objects[NUM_ROWS]= {" ", " l k ", " ", " ", " ", " ", " ", " ", " ", " l ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " s ", " ", " ", " ", " ", " s ", " l ", " ", " ",}; As you can see, the object map is very sparse, and it should be since it contains only the objects in the universe. However, determining if the player is near an object or even on top of one is very simple since there is a 1-1 relationship between the geometry of the universe, the universe of objects, and the position of the player. The only drawback to using the "cell" technique to represent a game universe for a text game is that the cell world has only a finite number of cells and thus positions within the universe, but this is really a small inconvenience and the player will never know! Making Things HappenWe previously learned that making things happen merely means to print out some text or change some values in data structures such as the position of the player. A text game is really just a "live" database that is accessed and modified as the game is played. Frankly, an arcade game is also, but there is a more complex output device being used (the screen) and arcade games must be in real time. Text games wait for the user to input a text string before doing anything. When this "waiting" is taking place, the code is stuck in the line input function and the game universe is "motionless" during this period. Moving AroundMotion in a text game is accomplished by changing the position of the player's character in the game. This is usually as simple as modifying a couple variables. For example, if the player typed "move north" the computer would output "You take a few steps…" The game code might decrement the Y position of the player's character (north is in the negative Y direction) and then that would be it. Of course, the player's position would be tested to see if the player stepped on something, hit a wall, or fell off a cliff, but the actual motion is accomplished with only a couple variable changes. This simplicity stems from the fact that the player is plopped down in the 2-D map and is just a square that can either move East, West, North, or South. The Inventory SystemThe inventory system in a text game is a list that contains a description and tally of all the objects that the player is holding on himself. This list can be an array, a linked list, or whatever. If the objects are complex, then the list might be a list of structures. In Shadow Land, the inventory is so simple that nothing more than an array of characters was used to hold the inventory. The player can have a sandwich 's', a set of keys 'k', and a lamp 'l'. If the player is holding one or more of these objects then, in a character array called inventory[], the characters representing these objects are stored. Then when the player asks to see what he is holding, a simple traversal of this list and a few output strings are all that is needed. But how does the player get the objects? Well, he picks them up from the game universe. For example, if a player was in a room and "saw" the keys, then he might request the game to "get" the keys. The game would then "pick up" the keys by removing the 'k' from the object universe, replacing the spot the keys were in with a blank, and then inserting a 'k' into the inventory list of the player. Of course, making the computer "see" what's in a room and ascertain if the player is within reach is a bit complex, but you get the idea. |