Containers, Take Two

I left things a little open-ended last time when I said I’d simply add code to my Container class to open and close container objects. Well, as it turns out doing that was a lot more fun than I expected. No, seriously, it was really fun to do, because I was able to reshuffle my code quite a bit while I was programming it, to make it neater and neater. I’m sure you’ll agree when I show you.

I started out writing code to open and close a container but then I realized that I also want to be able to have locked containers that require a key to unlock. This was fairly simple to program but I didn’t like the way the code looked, so I went over it again and rewrote it. But… I’m boring you with details. Let me just show you how it turned out.

First I had to modify the __init__() method to include a key in the parameter list and I had to change my PrintContents() method to make sure, the contents are printed only if the container is actually open.

class Container ( Supporter ):
	""" This class defines all sorts of container behavior """

	def __init__ ( self, _initState, _key, *args, **kwargs ):
		super ().__init__( *args, **kwargs  )
		self.State = _initState
		self.Key = _key
		self.EmptyString = "There's nothing in it."
		self.FullString = "The {0} contains {1}."
		self.Silent = False									# Do empty contents create a message?

	def IsOpen ( self ):
		return globals.ContainerStates.Open == self.State


	def PrintContents ( self ):
		if globals.ContainerStates.Open == self.State:
			return super().PrintContents ()
		if globals.ContainerStates.Locked == self.State:
			print ( self.GetName ( globals.Articles.Def ).capitalize () + " is locked." )
		else:
			print ( self.GetName ( globals.Articles.Def ).capitalize () + " is closed." )

Now that I have the basic functionality, it is time for stuff like opening and closing. The stuff that makes the container different from the supporter.

As I thought about how to do this, I decided that, like in the infocom adventures, I want the game to have a bit of smarts. If I want to open a chest that is locked and I have the key in my inventory, I want the game to automatically unlock the chest. If the player wants to take something out of the chest while it is closed, I’d also want the game to open the chest by itself first. To do that, I will need methods that check and perform the respective actions.

def TryUnlock ( self ):
	if globals.ContainerStates.Locked == self.State:
		if InInventory ( self.Key ):
			self.State = globals.ContainerStates.Closed
			print ( "(After unlocking it first.)" )

def TryOpen ( self ):
	if globals.ContainerStates.Closed == self.State:
		self.State = globals.ContainerStates.Open
		print ( "(After opening it first.)" )

Then comes the fun part… the actual logic, opening, closing locking and unlocking a container. At first glance, this may seem a little convoluted, but once you start really thinking of these actions, you’ll realize that there are a number of different states that need to be handled, such as, what to do when you try to open a container, but it’s locked. Do you have the key to open it? Or, if you try to close a container that’s already closed? Or, what if, the player wants to lock a container that doesn’t even have a lock?

All these little details will have to be handled, but it’s a very rewarding process, I found, because the program becomes so much more responsive to situations, and to the player, it appears as if the game has just become so much smarter… and I like that. To me, that’s what text adventures are all about. For the game to be aware of the game world and the circumstances and to respond to it in a sensible way.

def Open ( self ):
	self.TryUnlock ()

	if globals.ContainerStates.Open == self.State:
		print ( self.GetName ( globals.Articles.Def ).capitalize() + " is already open." )
	elif globals.ContainerStates.Locked == self.State:
		print ( self.GetName ( globals.Articles.Def ).capitalize()  + " is locked and you don't have a key that fits." )
	else:
		self.State = globals.ContainerStates.Open
		print ( "You open " + self.GetName ( globals.Articles.Def ) + "." )

		_listString = self.ListContents ()
		if not _listString:
			_listString = "nothing"

		print ( "Inside you find " + _listString )

	return True

def Close ( self ):
	if globals.ContainerStates.Closed == self.State or globals.ContainerStates.Locked == self.State:
		print ( self.GetName ( globals.Articles.Def ).capitalize() + " is already closed." )
	else:
		self.State = globals.ContainerStates.Closed
		print ( "You close " + self.GetName ( globals.Articles.Def ) + "." )
	return True

def Lock ( self ):
	if self.Key:
		if globals.ContainerStates.Locked == self.State:
			print ( self.GetName ( globals.Articles.Def ).capitalize() + " is already locked." )
		else:
			if InInventory ( self.Key ):
				self.State = globals.ContainerStates.Locked
				print ( "You lock " + self.GetName ( globals.Articles.Def ) + "." )
			else:
				print ( "You don't have a key that fits the lock." )
	else:
		print ( "You can't see any lock on " + self.GetName ( globals.Articles.Def ) + "." )
	return True

def Unlock ( self ):
	if self.Key:
		if globals.ContainerStates.Locked != self.State:
			print ( self.GetName ( globals.Articles.Def ).capitalize () + " is already unlocked." )
		else:
			if InInventory ( self.Key ):
				self.State = globals.ContainerStates.Open
				print ( "You unlock and open " + self.GetName ( globals.Articles.Def ) + "." )
			else:
				print ( "You don't have a key that fits the lock." )
	else:
		print ( "You can't see any lock on " + self.GetName ( globals.Articles.Def ) + "." )
	return True

As you can see from my code, I have a tendency to create responses on the fly. While it would be possible to simply say something non-specific, like “It’s already locked!”, I like it a great deal better, if the game actually responds much more specifically, saying, for example, “The wooden chest is already locked!” or “You can’t see any lock on the cake box.” instead of “You can’t see a lock on it.” It simply adds more atmosphere and personality to the game as a whole.

All that is needed now is to add to the Evaluate() method, check for verbs like open, close and so on and call the respective methods to perform the actions. At this point this is so trivial that I think, it is not necessary to post the actual code for it.

Leave a Reply

Your email address will not be published. Required fields are marked *