Extension - Player Class¶
In this extensions tutorial, we will refactor our code to create a Player class. This will give the game a player object which we can use to add features associated with the player (health, inventory, gear, weapons etc.). The first feature we will add is the player inventory, known in our game as the backpack.
Planning¶
Currently the code dealing with the backpack is held in main.py
. Before we plan our new class, we need to look at this code an identify all relevant feature we need to incorporate. So let’s look at the main.py
from the end of the standard tutorials.
1# main.py
2
3from room import Room
4from character import Enemy, Friend
5from item import Item
6
7# create rooms
8cavern = Room("Cavern")
9cavern.description = ("A room so big that the light of your torch doesn’t reach the walls.")
10
11armoury = Room("Armoury")
12armoury.description = ("The walls are lined with racks that once held weapons and armour.")
13
14lab = Room("Laboratory")
15lab.description = ("A strange odour hangs in a room filled with unknownable contraptions.")
16
17# link rooms
18cavern.link_rooms(armoury,"south")
19armoury.link_rooms(cavern,"north")
20armoury.link_rooms(lab,"east")
21lab.link_rooms(armoury,"west")
22
23# create characters
24ugine = Enemy("Ugine")
25ugine.description = "a huge troll with rotting teeth."
26ugine.weakness = "cheese"
27
28nigel = Friend("Nigel")
29nigel.description = "a burly dwarf with golden bead in woven through his beard."
30nigel.conversation = "Well youngan, what are you doing here?"
31
32# add characters to rooms
33armoury.character = ugine
34lab.character = nigel
35
36# create items
37cheese = Item("Cheese")
38cheese.description = "super smelly"
39
40chair = Item("Chair")
41chair.description = "designed to be sat on"
42
43elmo = Item("Elmo")
44elmo.description = "wanting to be tickled"
45
46# add items to rooms
47cavern.item = chair
48armoury.item = elmo
49lab.item = cheese
50
51# initialise variables
52running = True
53current_room = cavern
54backpack = []
55
56# ----- MAIN LOOP -----
57while running:
58 current_room.describe()
59
60 command = input("> ").lower()
61
62 # move
63 if command in ["north", "south", "east", "west"]:
64 current_room = current_room.move(command)
65 print(f"You travel {command}")
66 # talk
67 elif command == "talk":
68 if current_room.character is not None:
69 current_room.character.talk()
70 else:
71 print("There is no one here to talk to")
72 # hug
73 elif command == "hug":
74 if current_room.character is not None:
75 current_room.character.hug()
76 else:
77 print("There is no one here to hug")
78 # fight
79 elif command== "fight":
80 if current_room.character is not None:
81 weapon = input("What will you fight with? > ").lower()
82 available_weapons = []
83 for item in backpack:
84 available_weapons.append(item.name)
85 if weapon in available_weapons:
86 if current_room.character.fight(weapon):
87 current_room.character = None
88 if Enemy.num_of_enemy == 0:
89 print("You have slain the enemy. You are victorious!")
90 running = False
91 else:
92 running = False
93 else:
94 print(f"You don't have {weapon}")
95 print(f"{current_room.character.name} strikes you down.")
96 running = False
97 else:
98 print("There is no one here to fight")
99 # take
100 elif command == "take":
101 if current_room.item is not None:
102 backpack.append(current_room.item)
103 print(f"You put {current_room.item.name} into your backpack")
104 current_room.item = None
105 else:
106 print("There is nothing here to take")
107 # backpack
108 elif command == "backpack":
109 if backpack == []:
110 print("It is empty")
111 else:
112 print("You have:")
113 for item in backpack:
114 print(f"- {item.name.capitalize()}")
115 # help
116 elif command == "help":
117 print("Type which direction you wish to move,")
118 print("or use one of these commands:")
119 print("- Talk")
120 print("- Fight")
121 print("- Hug")
122 print("- Take")
123 print("- Backpack")
124 # quit
125 elif command == "quit":
126 running = False
127 # incorrect command
128 else:
129 print("Enter 'help' for list of commands")
130 input("\nPress <Enter> to continue")
131
132print("Thank you for playing Darkest Dungeon")
You will notice that there are four places that main.py
interacts with the player’s backpack.
line 54 → defines the backpack variable as an empty list
lines 82 - 85 → checks if chosen weapon is in the backpack
lines 101 - 106 → adds item to backpack
lines 109-114 → displays the contents of the backpack
If we were to move these features to a Player class, we need to consider the nature of the four features:
the backpack describes part of the player → attribute
checking for weapon in backpack is an action → method
adding an item to backpack is an action → method
displaying the contents of the backpack is an actions → method
Therefore the class diagram would look like this:
Now that we have a plan. Lets implement it in our code.
Coding¶
We are going to refactor this code is little chunks. That way we can regularly test to ensure we haven’t introduced new bugs
Create Player class¶
First we need to create the Player Class.
Create a new file called player.py. Make sure it is in the same folder as the other program files.
Next add the Player class and it’s
__init__
with the code below.
1# player.py
2
3class Player():
4
5 def __init__(self):
6 self.backpack = []
Replace references to backpack¶
Now in main.py we will create a instance of the Player class by making a player object.
Import the Player class (code below)
3from room import Room
4from character import Enemy, Friend
5from item import Item
6from player import Player
Create a player object before the initialization of variables
47# add items to rooms
48cavern.item = chair
49armoury.item = elmo
50lab.item = cheese
51
52# create player
53player = Player()
Delete the
backback
variable in line 58
55# initialise variables
56running = True
57current_room = cavern
58
59# ----- MAIN LOOP -----
Change the
backpack
reference in the fight command to refer toplayer.backpack
81 # fight
82 elif command== "fight":
83 if current_room.character is not None:
84 weapon = input("What will you fight with? > ").lower()
85 available_weapons = []
86 for item in player.backpack:
87 available_weapons.append(item.name)
88 if weapon in available_weapons:
89 if current_room.character.fight(weapon):
90 current_room.character = None
91 if Enemy.num_of_enemy == 0:
92 print("You have slain the enemy. You are victorious!")
93 running = False
94 else:
95 running = False
96 else:
97 print(f"You don't have {weapon}")
98 print(f"{current_room.character.name} strikes you down.")
99 running = False
100 else:
101 print("There is no one here to fight")
Change the
backpack
reference in the take command to refer toplayer.backpack
102 # take
103 elif command == "take":
104 if current_room.item is not None:
105 player.backpack.append(current_room.item)
106 print(f"You put {current_room.item.name} into your backpack")
107 current_room.item = None
108 else:
109 print("There is nothing here to take")
Change the
backpack
references in the backpack command to refer toplayer.backpack
110 # backpack
111 elif command == "backpack":
112 if player.backpack == []:
113 print("It is empty")
114 else:
115 print("You have:")
116 for item in player.backpack:
117 print(f"- {item.name.capitalize()}")
Test backpack replacement¶
That is our first block of refactoring. Time to test to ensure everything still works.
add_item method¶
To create effective code we should shift all backpack related code into the Player class. We will start with the add_method that add items to the backpack.
In player.py create the
add_item
method using the code below.
1# player.py
2
3class Player():
4
5 def __init__(self):
6 self.backpack = []
7
8 def add_item(self, item):
9 self.backpack.append(item)
10 print(f"You put {item.name} into your backpack")
Now we need to remove that code from main.py, and replace it with a call to the add_item
method.
replace the highlighted main.py code below:
102 # take
103 elif command == "take":
104 if current_room.item is not None:
105 player.backpack.append(current_room.item)
106 print(f"You put {current_room.item.name} into your backpack")
107 current_room.item = None
108 else:
109 print("There is nothing here to take")
with this highlighted code:
102 # take
103 elif command == "take":
104 if current_room.item is not None:
105 player.add_item(current_room.item)
106 current_room.item = None
107 else:
108 print("There is nothing here to take")
Test add_item method¶
Now go and test that you can still add items to your backpack.
display_contents method¶
Next we will refactor the code that displays the contents of the backpack.
Return to the player.py file
Add the code below to the Player class
1# player.py
2
3class Player():
4
5 def __init__(self):
6 self.backpack = []
7
8 def add_item(self, item):
9 self.backpack.append(item)
10 print(f"You put {item.name} into your backpack")
11
12 def display_contents(self):
13 if self.backpack == []:
14 print("It is empty")
15 else:
16 print("You have:")
17 for item in self.backpack:
18 print(f"- {item.name.capitalize()}")
Go back to main.py
Replace the highlighted code below:
109 # backpack
110 elif command == "backpack":
111 if player.backpack == []:
112 print("It is empty")
113 else:
114 print("You have:")
115 for item in player.backpack:
116 print(f"- {item.name.capitalize()}")
with this code:
109 # backpack
110 elif command == "backpack":
111 player.display_contents()
Test display_contents method¶
Run your code and ensure that you can still see your backpack content.
check_item_in method¶
Finally we need to change the backpack interaction in the fight command, but we’re going to do more than just a simple replace. We’re going to change the fight section so that the backpack returns the item rather than just item.name. This will allow future extensions including weapons with damage and players and characters with hit points.
In player.py add the code below:
1# player.py
2
3class Player():
4
5 def __init__(self):
6 self.backpack = []
7
8 def add_item(self, item):
9 self.backpack.append(item)
10 print(f"You put {item.name} into your backpack")
11
12 def display_contents(self):
13 if self.backpack == []:
14 print("It is empty")
15 else:
16 print("You have:")
17 for item in self.backpack:
18 print(f"- {item.name.capitalize()}")
19
20 def check_item_in(self, item_name):
21 for item in self.backpack:
22 if item.name == item_name:
23 return item
24 return None
This code is different, so lets investigate it:
def check_item_in(self, item_name):
→ defines the function and expects the item name (a string) to be provided.for item in self.backpack:
→ iterate through each item object stored inself.backpack
if item.name == item_name:
→ checks if the current item’s name is the same as theitem_name
providedreturn item
→ returns the current item ending the method. Note this only happens if the names match
return None
→ returns aNone
value to indicate that none of the items inself.backpack
have the same name as theitem_name
provided
In main.py change the fight command code to the same as below. Take note of the highlighted lines.
81 # fight
82 elif command== "fight":
83 if current_room.character is not None:
84 choice = input("What will you fight with? > ").lower()
85 weapon = player.check_item_in(choice)
86 if weapon:
87 if current_room.character.fight(weapon.name):
88 current_room.character = None
89 if Enemy.num_of_enemy == 0:
90 print("You have slain the enemy. You are victorious!")
91 running = False
92 else:
93 running = False
94 else:
95 print(f"You don't have {choice}")
96 print(f"{current_room.character.name} strikes you down.")
97 running = False
98 else:
99 print("There is no one here to fight")
Lets investigate those lines of code.
choice = input("What will you fight with? > ").lower()
→ we had to change the variable name, since weapon will be used to hold the item object return fromcheck_item_in
weapon = player.check_item_in(choice)
→ if the requested item is in the back, the item object is stored inweapon
otherwise,None
will be stored there.if weapon:
→ uses the Truthiness of objects. If there is an item_object inweapon
then this will equate toTrue
, ifNone
is stored inweapon
then this will equate toFalse
if current_room.character.fight(weapon.name):
→weapon
now stores a item object rather than a name, but thefight
method requires a string of the item name. So we have to pass theweapon.name
attribute.print(f"You don't have {choice}")
→ if thechoice
is not in the backpack, thenweapon
will beNone
, butNone
doesn’t have a name attribute, so we can use the same trick we use in line 87. Rather we will just feedback what the user entered.
check_item_in test¶
Final test. Run your code and make sure that all the fight options work.