This is an abbreviated version of the book Make Your Own Python Text Adventure.
Typically, a text adventure game involves the player exploring and interacting with a world to tell a story. For this tutorial, I wanted the game to take place in a cave with the goal of escaping alive. You can of course use this idea too, but feel free to use your own idea! Almost all parts of this tutorial are interchangeable with your own custom elements. We’re going to start by defining some items and enemies.
Items
Start by creating a new directory called adventuretutorial
and create a blank file called __init__.py
. This tells the Python compiler that adventuretutorial is a Python package which contains modules. Go ahead and create your first module in this same directory called items.py
.
The first class we are going to create is the Item
class. When creating a class, consider the attributes that class should have. In general, it will be helpful for items to have a name and description. We can use these to give the player information about the item. Let’s also add a value attribute that will help players to compare items within an economy.
class Item():
"""The base class for all items"""
def __init__(self, name, description, value):
self.name = name
self.description = description
self.value = value
def __str__(self):
return "{}\n=====\n{}\nValue: {}\n".format(self.name, self.description, self.value)
This class has just two methods, but we’re going to see them pop up a lot. The __init__
method is the constructor and it is called whenever a new object is created. The __str__
method is usually a convenience for programmers as it allows us to print an object and see some useful information. Without a __str__
method, calling print()
on an object will display something unhelpful like <__main__.Item object at 0x101f7d978>
.
While we could create an Item
directly, it wouldn’t be very interesting for the player because there is nothing special about it. Instead, we’re going to extend the Item
class to define more specific items.
One of my favorite parts of games is finding treasure so let’s go ahead and create a Gold
class.
class Gold(Item):
def __init__(self, amt):
self.amt = amt
super().__init__(name="Gold",
description="A round coin with {} stamped on the front.".format(str(self.amt)),
value=self.amt)
The Gold
class is now a subclass of the superclass Item
. Another word for a subclass is child class and superclasses may be called parent or base classes.
The constructor here looks a little confusing but let’s break it down. First you’ll notice an additional parameter amt
that defines the amount of this gold. Next, we call the superclass constructor using the super().__init__()
syntax. The superclass constructor must always be called by a subclass constructor. If the constructors are exactly the same, Python will do it for us. However, if they are different, we have to explicitly call the superclass constructor. Note that this class doesn’t have a __str__
method. If a subclass doesn’t define its own __str__
method, the superclass method will be used in its place. That’s OK for the Gold
class because the value is the same as the amount so there’s no reason to print out both attributes.
I mentioned earlier that this game is going to have weapons. We could extend Item
again to make some weapons, but weapons all have something in general: damage. Whenever a lot of specific classes are going to share the same concept, it’s usually a good idea to store that shared concept in a superclass. To do this, we will extend Item
into a Weapon
class with a damage
attribute and then extend Weapon
to define some specific weapons in the game.
class Weapon(Item):
def __init__(self, name, description, value, damage):
self.damage = damage
super().__init__(name, description, value)
def __str__(self):
return "{}\n=====\n{}\nValue: {}\nDamage: {}".format(self.name, self.description, self.value, self.damage)
class Rock(Weapon):
def __init__(self):
super().__init__(name="Rock",
description="A fist-sized rock, suitable for bludgeoning.",
value=0,
damage=5)
class Dagger(Weapon):
def __init__(self):
super().__init__(name="Dagger",
description="A small dagger with some rust. Somewhat more dangerous than a rock.",
value=10,
damage=10)
This should feel very familiar as we are following the same process of creating subclasses to define more specific elements in the game.
Enemies
Now that you’re an expert at extending objects, creating the enemies will be a breeze. Go ahead and create a new module called enemies.py
.
Our base class Enemy
should include a name, hit points, and damage the enemy does to the player. We’re also going to include a method that allows us to quickly check if the enemy is alive.
class Enemy:
def __init__(self, name, hp, damage):
self.name = name
self.hp = hp
self.damage = damage
def is_alive(self):
return self.hp > 0
Next, create a few subclasses of Enemy
to define some specific enemies you want the player to encounter. Here are mine, but feel free to use your own ideas instead.
class GiantSpider(Enemy):
def __init__(self):
super().__init__(name="Giant Spider", hp=10, damage=2)
class Ogre(Enemy):
def __init__(self):
super().__init__(name="Ogre", hp=30, damage=15)
Notice that we don’t have to include is_alive()
in the subclasses. This is because subclasses automatically get access to the methods in the superclass.
If you are completely new to programming, some of this may be confusing. My book Make Your Own Python Text Adventure is great for beginners because it does not assume any knowledge of programming concepts.
Hey Philip, what version of python did you make this on? I’m very new to python in general, but I keep getting syntax errors regarding colons and indents, but I’m writing them exactly as you have them. I’m wondering if it might be a translation issue with a different version of python (I’m using v.3.2.3), or Maybe it is too soon to run the module?
Hi, Ryan! That version should be fine. If you run into too much trouble, try checking out the project from GitHub to compare to the code you’ve written. Also, at this point in the tutorial, there isn’t really anything to run.
Hi, i have a few questions. in the beginning you write that we should create a directory called adventuretutorial and two blank files called _init_.py and items.py. Can make the folder and files on my desktop? I’m a little bit confused on how I am going to use this.
Uhm, in your code example for the enemy main class, you just said Enemy: and then went onto the next thing. Are you sure there should not be empty parenthesis before the colon?
Also, in the example, it says giant spider twice, and no ogre ๐
Nice catch! I updated the post.
Yep! You only need to use parentheses when you extend a base class.
When I try to run it in Terminal, its says
Traceback (most recent call last):
File “/Users/lumbo/Desktop/adventuretutorial/game.py”, line 3, in
from player import Player
File “/Users/lumbo/Desktop/adventuretutorial/player.py”, line 2, in
import items, world
File “/Users/lumbo/Desktop/adventuretutorial/items.py”, line 1, in
class Gold(Item):
NameError: name ‘Item’ is not defined
Hmm, I’m not sure what the problem is. That probably means there is a typo or omission somewhere. You might want to compare your code against my code in the GitHub repository to make sure it is the same. Happy programming!
Okay, I fixed the:
NameError: name โItemโ is not defined
But now I have this:
TypeError: super() takes at least 1 argument (0 given)
Do I have to put something in between the parenthesis?
When I try to run the game, it gives me this error:
NameError: global name ‘__init__’ is not defined
How do I fix this?
Without more details, I would try a few things:
What does ‘amt’ mean?
It means “amount”. As in amount of gold.
Hey. I am used to python 2, and i dont know that much, but how do i make directories and stuff?
Mkdir adventuretutorial; cd adventuretutorial; touch __init__.py items.py
Hey, I’m getting a TypeError when I run the code. Says __init__ takes 1 positional argument but 3 were given. Using Python 3.5, and would gladly use any assistance.
This error means that an
__init__()
function is not set up for three arguments, but you’re trying to pass that many arguments in. Without seeing the complete code, my guess would be there’s a problem in theItem
class. Ignoringself
, count your arguments in each initializer starting with your most-specific class and working your way up to the most general class.I keep getting
AttributeError: “Gold” object has no attribute ‘description’
That looks OK to me, where are you getting that error? The Python stack trace should give you some more information. Compare your code where the error is against mine and make sure everything matches.
not sure why description wasn’t being read but I found an additional error …
super().__init__(name=”Gold”,
description=”A round coin with {} stamped on the front.”.format(str(self.amt)),
value=self.amt ) <—– #this class wasn't closed properly on yours
when you say to make a new class e.g. gold class do i make a new module or do i use the item module?
Thanks
Either would work, but it makes sense to me to include it in the existing items module.
A correction:
The constructor for a Python class is named โ__new__โ; it is a class method that returns the new instance. Most beginners never need to know about the constructor and never need to use โ__new__โ.
The โ__init__โ method of an instance is not the constructor. It is the initialiser for the instance; it has access to the new instance (as โselfโ) and returns None. Beginners should learn this as the โinitialiserโ (not the constructor, which is different).
Thank you for making this course!
How would I go about making a Bandage item that appends the players health so the player has a way of gaining his/her health back
There’s quite a few steps involved, but in general, you need a Heal action that checks the player’s inventory for the bandaid. If a bandaid is present, then call a Heal function that increases the player HP, and remove the bandaid from the inventory. This is actually something I go into detail about in the book, if you’re interested.
Do you need to create new modules to make the game? Or can you just put everything into one file.
You can put everything in one file, but it may make it more difficult to follow along or compare to my code.
I continually get the following error. The specifics change slightly, it seems, depending on which enemy is being assigned to an enemy tile. All throw name ‘enemies’ is not defined.
I figured out a pervious problem involving the import but non-evocation of the file Player.
i.e. I had imported it but not defined it!
Here I cannot find a similar problem. Any Suggestions?
I am in chapter 12 of the book.
Is it possible that you are missing the
import enemies
at the top of your file? That error means Python doesn’t know what “enemies” means. By using theimport
, you are telling Python that it is a package to access.I didn’t import enemies in world! AHHHHH!!!
Now it is throwing a list of other errors but they seem to be typos.
Thanks
After create your items class, are the actual items objects in the class or are you defining them in a dictionary or something outside the class?
When you get to part 3, you will see that we create a Player object and that object has a list of items (i.e. the inventory).
Another Question sorry, reading through the book, and im wondering is it necessary to create different classes of items or would be fine just creating instances of item with the specific name, descrip, and value. I’m making a dating based adventure game, and i cant see reason for a bunch of classes of items. Thanks
There’s no right or wrong answer to this question. It really depends on how you’re going to use the items in your code. In general, it’s nice to not repeat yourself. So if you know you are going to create a bunch of Cats in your code, you don’t want to retype the description of the Cat in lots of places, so you make a Cat class. It can also help with code readability to know something about the object based on its name.
The more dissimilar your objects are, the more you should lean towards using distinct classes. The more similar your are objects are the more you should lean toward using a generic class with slightly different attributes for each instance.
I only have Python 2.7, will this only run on Python 3.5?
I’m assuming the only difference I need to make is typing object: eg: class Item(object) ?
I am pretty sure it should work on any Python 3.X. I can’t vouch for this, but someone else in the comments made a 2.X-compatible fork.
Thanks Phillip!
Hi, thanks for the tutorial!
I would like to include another property of weight (and also add some other damage properties)
So here I am inheriting the name, desc and value from the parent Item, and then I am specifying the weapon-specific attributes (damage, damage2 and weight) that I would like all weapons to share.
However I am not sure how to modify the below:
I guess I would add (self.damage2, self.weight) to the bracketed text
But I am not sure what everything in the “{}….{}” means or what the .format means. Could you help, please?
Any help would be much appreciated ๐
Thank you again!
Hi DL,
The braces are like placeholders and everything inside of
format(...)
are the things that go into the placeholders. So, you’d need to add two more placeholders and then the new attributes into the format method. This DigitalOcean tutorial gives a lot more details, if you are interested.Hey Phillip,
Just wanted to say thank you for both the reply and the link. Python is the first prog language I’ve ever really looked at. The /n confused me and I assumed it was mathematical hahaha! ๐
Thank you so much for the explanation. All makes sense now.
DL
hey i keep my console keeps giving me:
Traceback (most recent call last):
“C:/Users/WILL/Documents/j785797398728/6583672687649819/code/Out_the_dark_hole/game.py”, line 2, in
from player import player
File “C:/Users/WILL/Documents/j785797398728/6583672687649819/code/Out_the_dark_hole\player.py”, line 1, in
import items, world
File “C:/Users/WILL/Documents/j785797398728/6583672687649819/code/Out_the_dark_hole\items.py”, line 20
class Weapon(Item):
^
SyntaxError: invalid syntax
yes it is copy and pasted but i can’t figure out what is wrong with the syntax
How do you make a directory?
How would one go about equipping and unequipping these weapons??
I’m getting an error message on the “class Item()” line. It says “expected an indented block” What do I do help?!
P.S: I’m using Python 3.7.4.
It somehow fixed itself.
nevermind
Why do we need the Item parent class for the Weapon child class? We don’t use any variables or methods from it.
We use its attributes (name, description, etc.) when viewing the inventory.
I keep getting a problem in my code where it says that “Item” is not defined. For example:
class Gold(Item):
def __init__(self, amt):
self.amt = amt
super().__init__(name=”Gold”,
description=”A round coin with {} stamped on the front.”.format(str(self.amt)),
value=self.amt)
I keep getting a yellow line under “Gold(Item)” and when I hover over it to see the issue it says that “Item” is not defined.
If it helps at all I am using Node to make this