Extension - Player Class

In this tutorial, you’ll reorganise your code by making a Player class. This will give you a player object that can hold all the player’s features, like health, items, gear, and weapons. The first thing you’ll add is the player’s inventory, which we call the backpack.

Planning

Right now, all the backpack code is written inside main.py. Before we create our new Player class, we need to look at that code and figure out which parts should be moved into the class. So we’ll start by checking the main.py file from the end of the earlier 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.

  1. line 54 → defines the backpack variable as an empty list

  2. lines 82 - 85 → checks if chosen weapon is in the backpack

  3. lines 101 - 106 → adds item to backpack

  4. 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:

  1. the backpack describes part of the player → attribute

  2. checking for weapon in backpack is an action → method

  3. adding an item to backpack is an action → method

  4. displaying the contents of the backpack is an actions → method

Therefore the class diagram would look like this:

player class diagram

Now that we have a plan. Lets implement it in our code.

Coding

We’re going to update this code in small steps. This makes it easier to test as we go and check that we haven’t accidentally created any new bugs.

Create Player class

First, we need to make the Player class.

  1. Create a new file called player.py in the same folder as your other files.

  2. Add the Player class and its __init__ method using the code shown below.

1# player.py
2
3class Player():
4    
5    def __init__(self):
6        self.backpack = []

Replace references to backpack

Now in main.py, we’re going to make a player object so we can use the Player class in our program.

  1. Import the Player class using the code shown below.

3from room import Room
4from character import Enemy, Friend
5from item import Item
6from player import Player
  1. 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()
  1. Delete the backpack variable on line 58.

55# initialise variables
56running = True
57current_room = cavern
58
59# ----- MAIN LOOP -----
  1. Change the backpack in the fight command so it uses player.backpack instead.

 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")
  1. Change the backpack in the take command so it uses player.backpack instead.

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")
  1. Change the backpack in the backpack command so it uses player.backpack instead.

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’s the first section of our code changes done. Now test your program to make sure everything still works.

add_item method

To make our code cleaner, we should move all the backpack code into the Player class. We’ll start by adding the add_item method, which puts items into the backpack.

  1. 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 take that code out of main.py and replace it with a call to the add_item method.

  1. Remove the highlighted code in main.py shown 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")
  1. And replace it 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 test your program to make sure you can still add items to your backpack.

display_contents method

Next, we’ll update the code that shows what’s inside the backpack.

  1. Go back to the player.py file.

  2. 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()}")
  1. Go back to main.py

  2. Remove the highlighted code shown 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()}")
  1. And replace it with this highlighted code:

109    # backpack
110    elif command == "backpack":
111        player.display_contents()

Test display_contents method

Run your program and check that you can still see everything inside your backpack.

check_item_in method

Finally, we need to change how the fight command uses the backpack, and this time it’s more than a simple swap. We’re going to update the fight code so the backpack gives us the whole item, not just item.name. This will make it easier later to add things like weapon damage and health points for players and characters.

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:

Code Explaination

  • def check_item_in(self, item_name): sets up the function and expects you to give it the item’s name as a string.

  • for item in self.backpack: goes through every item object in the backpack.

  • if item.name == item_name: checks if the current item’s name matches the name you typed.

    • return item sends back that item and ends the function. This only happens when the names match.

  • return None sends back None if no item in the backpack has the same name as what you typed.

In main.py, change the fight command so it matches the code shown below. Pay attention to 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.

Code Explaination

  • choice = input("What will you fight with? > ").lower() → we changed the variable name because weapon will now store the actual item object returned from check_item_in.

  • weapon = player.check_item_in(choice) → if the item is in the backpack, weapon will hold the item object. If not, weapon will be None.

  • if weapon: → this checks if weapon is a real item object. If it is, this is True. If it’s None, it’s False.

  • if current_room.character.fight(weapon.name):weapon now has the whole item object, not just the name, but the fight method still needs a string, so we use weapon.name.

  • print(f"You don't have {choice}") → if the item isn’t in the backpack, weapon is None, which doesn’t have a name. So instead, we just print whatever the player typed.

check_item_in test

Final test: run your program and check that all the different fight options still work properly.