Cthulhu Dreams
My project has been growing quite a bit and, yes, I am kind of proud of it, even though the term game could hardly be applied to it yet. But I’m getting there… you just wait.
But, since the project has grown, I began to feel that I need to take it out of the vacuum that it is in right now and give it an actual theme. If I want to make a game, I actually need to think of a game, right? I need a setting, I need a story of sorts, I need to think of specific locations, puzzles, all that.
So, I spent some time thinking of a cool game I could make and after a few false starts, I finally decided.
I am making a game in the vein of H.P. Lovecraft’s stories. Something dark and strange. Something about the Old Gods… something with Cthulhu. For now, I decided to call the project Cthulhu Dreams.
Now that I have a theme in mind, it is much easier for me to actually create proper locations and fill them with the proper scenery. But as my scenery became more specific, so did its requirements and as a result, I have begun to create specialized classes that inherit from the Scenery
base class.
For example, I want there to be a rug in the starting room. It is a bedroom and the player has just awoken from a strange dream that involved tentacles chasing him. In the room is an old rug.
Since the Scenery
class only supports the most basic functionality, I decided a more specialized class for it was in order so I could accommodate behavior and responses that are tailored toward that one particular rug. Here’s what it looks like.
class Rug ( Scenery ): def __init__ ( self, _token, _name, _description ): super().__init__ ( _token, _name, _description ) self.State = False def Evaluate ( self ): if Tokens.Look == globals.theVerb and Tokens.Under == globals.thePrep: print ( "There's nothing hidden under the rug." ) return True elif Tokens.Move == globals.theVerb: print ( "You move the rug and lift a corner but there is nothing unusual beneath it." ) return True else: return super ().Evaluate ()
Most of what you see here is pretty standard fare by now. Adding new words to the vocabulary and then checking from them in the Evaluate() function. You may have noticed that I’ve also added a new word type—the Preposition, which you see in this example as it checks for Look under the rug
.
But what’s with all the super()
stuff?
When you look at my class definition, you can see that instead of deriving it from a generic object like before, I am now deriving it from Scenery. That makes Scenery the Rug’s superclass.
The idea is, of course, that I still want access to all the functionality that I built into the Scenery
class and that I merely want my Rug class to expand on it. In practical terms, it means that I don’t want to rewrite all the code I have already written for Scenery
, but instead I want it to seamlessly become part of my new class. Inheritance…
In order to achieve that, I need to make sure I call the Scenery
superclass whenever my Rug
class doesn’t do anything. This is achieved by calling <>super() to access the superclass and then simply calling whatever method you need.
In this case, I am calling super().__init__()
to make sure the token, name and description are properly initialized and at the end of Evaluate()
I make sure to call super().Evaluate()
to handle any responses I have not taken care of in my new class. By making this call, I am passing control over to the superclass which will then check if it has a response and react accordingly.
This is pretty cool stuff and it is starting to dig into some of the more complex features of object-oriented programming, because as I realized already, things can get a bit confusing at times, as to who calls what, when. But for now, I’ll simply try to keep things simple and hopefully I won’t lose my way.
I was still not entirely happy with this, though. From the beginning of this project, I knew I wanted the game to be a little snippy, you know, take potshots at the player here and there, and this just seemed like a perfect occasion.
What if the player looks under the rug a few times? Wouldn’t it be a good place to start giving him a different kind of response after about three times or so? I think it is…
To make it happen, I decided to write a small method that implements that behavior.
def UnderRug ( self ): if self.UnderCount > 3: print ( random.choice ( [ "There's STILL nothing hidden under the rug.", "No matter how many times you will look under the rug, nothing will materialize, I'm sure."] ) ) else: print ( "There's nothing hidden under the rug." ) self.UnderCount += 1 return True
For the first three times, the answer will always be the same, but after that, the program will pick one of two answers at random, each of which is a little sassier than the original one. This may seem superficial and a detail that will probably get lost in the project as a whole, but it’s the kind of thing I really enjoy doing. It gives the program a bit more personality.
In addition, I also decided to recognize the commands Lift the rug
and Raise the rug
as well. Added the necessary vocabulary and responded to it by also simply calling the UnderRug()
function. Barely any new code, but more flexibility when it comes to the player’s input, and if you’ve ever played a text adventure game, I am sure you will appreciate that kind of flexibility. Having to find the exact phrase for a command to solve a puzzle can be truly frustrating and it’s something I am trying to avoid. So here is another look at the Rug
class as it is now.
class Rug ( Scenery ): def __init__ ( self, _token, _name, _description ): super().__init__ ( _token, _name, _description ) self.State = False self.UnderCount = 0 def UnderRug ( self ): if self.UnderCount > 3: print ( random.choice ( [ "There's still nothing hidden under the rug.", "No matter how many times you will look under the rug, nothing will materialize, I'm sure."] ) ) else: print ( "There's nothing hidden under the rug." ) self.UnderCount += 1 return True def Evaluate ( self ): if Tokens.Look == globals.theVerb and Tokens.Under == globals.thePrep: return self.UnderRug () elif Tokens.Raise == globals.theVerb: return self.UnderRug () elif Tokens.Move == globals.theVerb: print ( "You move the rug and lift a corner but there is nothing unusual beneath it." ) return True else: return super ().Evaluate ()
From here, I can easily add all sorts of behavior to the class that is relevant only to the rug. Things like Move the rug
or Tear the rug
or Roll up the rug
and so forth, all become very easy to implement, while throughout the process, whatever logic I add to the Scenery class itself will also instantly translate to the Rug
class. I love programming…