I have started development on a text-based adventure game. The environment behaves like a Sierra game, but there's no graphic interface and all commands are text.
Currently, any objects in the game (such as characters, rooms, items, etc) are defined by a dictionary. The keys are the name of the data value (name, description, etc) like this:
flash_light={
'name':'Flashlight',
'desc':'A flashlight, probably belonging to an eningeer on the ship.',
'consumable':False
}
The player's inventory and the global inventory are dictionaries where the key is the 'codename' string to call the item by, and the item is the dictionary referencing it. If you wanted to, say, take an item you found in the room, the dictionary of the room has a property that tracks the inventory of the room as a list. This list contains strings that are 'codenames' for the items in it.
my_room={
'name':'My Room',
'codename':'my_room',
'desc':'A hot, stuffy room shared with 3 other passengers. My trunk is here.\
The door is on the NORTH wall.',
'access':True,
'direc':{'north':'st_hall_1'},
'inven':['flashlight']
}
After the command has decided the item you asked to take is actually IN the room, it runs:
current_pos.inven().remove(target)
player_inv.update({target:global_inv[target]})
These lines remove the string from the room's inventory list, then add a key and item to the player's inventory dictionary. The key is the string name of the object you tried to take, and the item is defined as the dictionary in the GLOBAL inventory at the string name.
The reason I have so many calls around the program is that I kept running into problems when I gave dictionaries references to other dictionaries (i.e. A room has a list of the rooms it connects to), and those dictionaries may not be defined yet. I kept the functions as general as possible, and use the string-to-call-a-dict thing to avoid giving something undefined names as a parameter.
My main question is this: How could I make this more efficient, if possible? Or, the way I built the system, would it take major restructuring to improve?
You should use classes instead of dicts. Start by making a generic item class that looks something like that:
class Item(object):
def __init__(self, name, codename, description, is_consumable):
self._name = name
self._description = description
self._is_consumable = is_consumable
And then create a class for each type of item:
class Flashlight(Item):
def __init__(self, codename):
super(Flashlight, self).__init__('Flashlight', codename,
'A flashlight, probably belonging to an eningeer on the ship', False)
Consider creating a separate item for consumables which has a consume()
method that processes the consumption of the item.
Create a Room class that looks something like that:
class Room(object):
def __init__(self, name, codename, description, access, directions, inventory):
self._name = name
self._codename = codename
self._description = description
self._access = access
self._directions = directions.copy()
self._inventory = inventory[:]
I used copy
in the definition of self._directions and [:]
in the definition of self._inventory to create a copy of each object, instead of using the original.
Instantiate it for each room you want.
my_room = Room('My Room', 'my_room',
'A hot, stuffy room shared with 3 other passengers. My trunk is here. The door is on the NORTH wall.',
True, {'north': 'st_hall_1'}, [Flashlight('flashlight')])
And then you could add methods that enable the players to do whatever the game allows them to do inside the room. You should also create a Character class, a Player class and so on.
So classes are the key to making your game more efficient. Instead of including all the game's logic inside the same function (or a few of them), each class should contain its own things.
EDIT: Now that I see your code, there are some things you can do to make it better:
In your "main", use a function-dict that just picks the right action according to the command instead of a long chain of if
statements (also, you did it without using else
! that means that each of those statements is still evaluated after the action has been taken), like this: https://softwareengineering.stackexchange.com/questions/182093/why-store-a-function-inside-a-python-dictionary
Your classes only contain getter functions, which can be defined using the @property
decorator (How does the @property decorator work?)
In your room definitions you can clearly see how classes are better: instead of writing the same fields over and over again (and hoping you didn't have a typo anywhere), you could just instantiate them using the class you've defined. The same goes for the item definitions.
You could also create a consume()
method, which applies whatever effect the specific consumable has, use()
for usables (such as your flashlight and screwdriver), etc.