Going to the core
With the parser firmly in place now, allowing me to interpret the player’s input command, it is time to move to the next stage. When you think about how a text adventure game is set up and how it works, it very quickly becomes obvious that at the heart of it all is the Room
.
Everything in the game takes place in a room. The player moves from room to room. Many of the responses to actions are dependent on the room… So, naturally, I will next turn my attention to the implementation of rooms.
This is where object-oriented programming becomes very handy because it allows me to think of a room as a general entity which I will later supplement with more specialized versions of it. To start, however, I have to nail down what all rooms have in common.
↳ Name
↳ Description
↳ Scenery
↳ Items
↳ Exits
That’s actually a pretty nice list. Not only does it define the functionality any room in the game has to provide but it also gives me a nice to-do list, of sorts, as I can work my way through the different elements one at a time.
To make the magic happen, here’s how I set up my class.
class Room ( object ): def __init__ ( self, _name, _desc, _scenery, _items, _exits ): self.Name = _name self.Description = _desc self.Scenery = _scenery self.Items = _items self.Exits = _exits
Never mind that I do not have implementations for things like scenery, items or exits. It does not matter. I know they will be lists of some sort, so all I need to do for now is pass empty lists when I create a room object. No sweat.
The next logical step is to provide a function that prints the room description. It is one of the key functions in any text adventure game and one that is needed all the time.
def Describe ( self ): print ( self.Name.upper() ) print ( self.Description ) return True
I am printing the name of the room in capital letters, followed by the room description. Later on, as I finish the respective implementations, I will also add the listing of items and exits here so that one call to this method will print the entire room description. Having this in its own function also allows me to prepare different ways of printing the description, such as verbose and brief versions, without having to find all instances in my code base to make the necessary changes.
And then, most importantly, the class needs some logic function that evaluates player actions and responses. I am calling it Evaluate()
.
def Evaluate ( self ): if Tokens.Look == theVerb and None == theNoun: return self.Describe ()
You might be wondering why I am returning a value here, but the answer is very simple. Clearly, I will need some sort of indicator that tells the game whether a command has been successfully processed—in which case it can stop looking for responses. If I’ve run all my room logic and no response has been found I can then force the game to print some sort of a default response. The True/False flag allows me to do what. Whenever a user command has been handled, the function doing it will return True
.
A small change in the game loop will make that happen.
while not ( Tokens.Exit == theVerb and None == theNoun ): if True == Prompt (): if False == theRoom.Evaluate (): print ( "I am not sure, I understand." )
Now, in order to actually make all of this work, I will need a room object or two. In order to access them later in the game and connect them—yes, I am thinking ahead here—I am putting all rooms into a list as well so I can easily address them through an index. Here’s what it looks like.
theRooms = [ Room ( "Room0", "This is room 0", [], [], [] ), Room ( "Room1", "This is room 1", [], [], [] ) ]
This gives me two empty rooms. Note how I simply defined the scenery, items, and exits as empty lists, just like I explained earlier. This will make it very easy for me to add in more information and details as my game becomes bigger.
To get started, all I need now is a variable I call theRoom
, which is the starting point of the game. It goes right before the game loop at the start of the program, and it looks like this.
theRoom = theRooms [ 0 ] while not ( Tokens.Exit == theVerb and None == theNoun ): if True == Prompt (): if False == theRoom.Evaluate (): print ( "I am not sure, I understand." )
When I run the program, I can now type Look
into the command line and the game will print the room description for me. Cool!
Now, the next logical step is obviously, connecting the rooms with exits and allowing the player to move between them. As you may have suspected, that that is what I’ll take a look at next time.