Now that I have my scenery objects in play, the next big thing to tackle is Items. As you may expect, I consider items to be game objects that you can pick up, carry around and put to use in other places. Naturally, creating items will also include having an inventory.
When you think about it, items are actually not all that different from scenery items. The key difference is really that they can be picked up and, perhaps, manipulated in a different kind of way. Structurally, however, they share the same implementation.
As a result, the implementation of the class looks similar to the one I used for Scenery.
class Item ( object ): def __init__ ( self, _token, _name, _description ): self.Token = _token self.Name = _name self.Description = _description def Describe ( self ): print ( self.Description ) return True
Naturally, the Evaluate() method will have to look a bit different, allowing the player to pick up the item and add it to the player’s inventory.
def Evaluate ( self ): if Tokens.Look == globals.theVerb: self.Describe () elif Tokens.Take == globals.theVerb: if not InInventory ( self.Token ): print ( "You pick up " + self.GetName ( globals.Articles.Def ) + "." ) globals.theInventory.append ( self ) globals.theRoom.RemoveItem ( self.Token ) else: print ( "You already have " + self.GetName ( globals.Articles.Def ) + "." ) return True
There are a couple of new things in this small bit of code that I want to explain. First is the InInventory() function.
The implementation of the player’s inventory is really very simple and straight forward. All it takes is a variable called theInventory in globals.py that contains an empty list at first. Whenever the player picks up an item, I will simply append the item to the list and whenever an item is taken out of the inventory, I will remove it from the list. Couldn’t be simpler.
The function InInventory(), simply runs through that list and checks if the desired item is in it, by comparing the token. If it finds the item, it returns the item so my program can use it to process it further without having to run through the loop again at some later point. It looks like this.
def InInventory ( _token ): for _item in globals.theInventory: if _token == _item.Token: return _item return None
The other function I should explain here is GetName(). It is part of the Item class and returns the name of the item itself, but it can also add an article to it, turning “fork” into “a fork” or “the fork,” depending on how I call the function. By default, it will return the name without any article.
def GetName ( self, _article=globals.Articles.No ): """ Name of the item, complete with an indefinite or definite article """ if globals.Articles.Undef == _article: _aString = "a " if self.Name [ 0 ].lower () in globals.theVowels: _aString = "an " elif globals.Articles.Def == _article: _aString = "the " else: _aString = "" return _aString + self.Name
The article definitions are part of my globals.py file where I define all my variables that need to be accessible throughout the project.
class Articles ( Enum ): No = auto() Undef = auto () Def = auto ()
Seeing this now, also reminds me that I should explain the auto() call as part of the definition. You may recall that when creating the vocabulary, I numbered my words by hand. As the list got longer, I got tired of doing that because I wanted to be able to move words around so I can keep logical groups of words together and doing so would have required me to constantly renumber the definitions.
That’s when I found out about the auto function in Enums. All it does is really creating the next consecutive number for me. This way, “No” is 0, “Undef” is 1 and “Def” is 2. If I change their order, they will be simply renumbered accordingly, which makes things a lot easier and cleaner. I am using auto() on all my enums at this point. No need locking down numbers when I can have Python do it for me.
Going back to my Evaluate() method and the picking up of objects, after making sure the item in question is not yet in the inventory, I print out the message that tells the player that he’s picking up the item, and then I simply add the item to the inventory. The last step to do is to remove the item from the room because, evidently, now that the player picked it up, it should no longer remain there.
This requires a new method in the Room class.
def RemoveItem ( self, _token ): for _obj in self.Items: # Is it an item in the room? if _token == _obj.Token: self.Items.remove ( _obj ) return True
It’s really quite simple, in that it simply runs through all items in the room and compares the token, then removes it from the list. It is, in fact exactly the same process the inventory function will run through when I remove an item from the player’s inventory.
All that is left now, is to throw some item into a room. That, however, is also pretty standard stuff by now, simply adding it to the room definition.
theRooms = [ Room ( "Room0", "This is room 0 with a lonely painting on the wall.", [ Scenery ( Tokens.Painting, "painting", "It’s Van Ghogh’s ‘The Church at Auvers’." ), Rug ( Tokens.Rug, "tattered rug", "Threadbare, the rug is on the verge of falling apart. The pattern, which " "used to be some kind of symmetrical arrangement, has faded almost entirely." ) ], [ Item ( Tokens.Key, "bronze key", "An old door key that has a long shaft." ], [ Exit ( Tokens.South, 1 ) ] ), <<! Snipped !>>
The problem now is that if I pick up the key, there’s no way for me to see that I have it. I need a function to list the player’s inventory.
def ListInventory (): _counter = 0 _outString = "" for _item in globals.theInventory: if 0 != _counter: _outString += " and " _outString += _item.GetName( globals.Articles.Undef ) _counter += 1 if 0 == _counter: _outString = "Aside from some useless lint, your pockets contain absolutely nothing." else: _outString = _outString.replace ( " and ", ", ", _counter - 2 ) _outString = "As you rummage through your pockets, you find " + _outString + "." print ( _outString ) return True
This may, once again, look vaguely familiar to you, because I’ve done something similar when listing exits. I am simply running through the inventory list, stringing up the names of all items with “and” and then, at the end, replacing all but the last one with commas, to create a printed list that will look something like this.
As you rummage through your pockets, you find a key, a green rock and a sonic screwdriver.
Naturally, the function also catches cases where the player carries no items with its own text message.
That’s pretty much what it takes to manage standard items in the game, but naturally, I will have to create specialized, derived classes as I go forward to allow for special behavior of certain items. But I’ll cross the bridge when I get there.