Added object interaction - healthpotion, lock and key
[butaba-adventures.git] / maingame.py
1 import pygame
2 import sys
3 import random
4 import os.path
5
6 import level
7 import butaba
8 import utility
9 import gameobjects
10
11 class MainGame:
12
13 # initialize the game
14 def __init__ (self):
15 pygame.init ()
16 self.screen = pygame.display.set_mode ((720, 512))
17 pygame.display.set_caption ("The Adventures of Butaba")
18
19 # initalize background graphics
20 self.img_tileset = pygame.image.load (os.path.join ("background", "tileset.png")).convert ()
21
22 self.img_menu = pygame.image.load (os.path.join ("background", "menu_screen.png")).convert ()
23
24 self.img_inventory = pygame.image.load (os.path.join ("background", "inventory.png")).convert ()
25
26
27 # initialize object graphics
28 self.img_redpotion = pygame.image.load (os.path.join ("objects", "red-potion.png")).convert ()
29 self.img_redpotion.set_colorkey (pygame.Color (0, 255, 0))
30 self.img_goldcoins = pygame.image.load (os.path.join ("objects", "gold-coins.png")).convert ()
31 self.img_goldcoins.set_colorkey (pygame.Color (0, 255, 0))
32 self.img_wand = pygame.image.load (os.path.join ("objects", "wand.png")).convert ()
33 self.img_wand.set_colorkey (pygame.Color (0, 255, 0))
34 self.img_bulb = pygame.image.load (os.path.join ("objects", "bulb.png")).convert ()
35 self.img_bulb.set_colorkey (pygame.Color (0, 255, 0))
36 self.img_lightning = pygame.image.load (os.path.join ("objects", "lightning.png")).convert ()
37 self.img_lightning.set_colorkey (pygame.Color (0, 255, 0))
38 self.img_key = pygame.image.load (os.path.join ("objects", "key.png")).convert ()
39 self.img_key.set_colorkey (pygame.Color (0, 255, 0))
40 self.img_chest = pygame.image.load (os.path.join ("objects", "chest.png")).convert ()
41 self.img_chest.set_colorkey (pygame.Color (0, 255, 0))
42
43 # initialize player graphics
44 self.img_butabafront = pygame.image.load (os.path.join ("sprite", "butaba-front.png")).convert ()
45 self.img_butabafront.set_colorkey (pygame.Color (0, 255, 0))
46 self.img_butababack = pygame.image.load (os.path.join ("sprite", "butaba-back.png")).convert ()
47 self.img_butababack.set_colorkey (pygame.Color (0, 255, 0))
48 self.img_butabaleft = pygame.image.load (os.path.join ("sprite", "butaba-left.png")).convert ()
49 self.img_butabaleft.set_colorkey (pygame.Color (0, 255, 0))
50 self.img_butabaright = pygame.image.load (os.path.join ("sprite", "butaba-right.png")).convert ()
51 self.img_butabaright.set_colorkey (pygame.Color (0, 255, 0))
52
53 # set level data
54 self.setup_levels ()
55 # set current level and position of our character
56 self.currentlevel = self.level1
57
58 # set the status message
59 self.status_message = "Game started"
60
61 self.butaba = butaba.Butaba (5,0, butaba.Butaba.RIGHT, 75)
62
63 # set up the levels and their interactions
64 def setup_levels (self):
65
66 self.level1 = level.Level (level.LEVEL_1)
67 self.level1e = level.Level (level.LEVEL_1E,
68 objects = [ gameobjects.Key (4, 3, "a chest key", self.img_key, level.KEY_CHEST1),
69 gameobjects.Key (4, 3, "a room key", self.img_key, level.KEY_ROOM1),
70 gameobjects.HealthPotion (4, 2, self.img_redpotion),
71 gameobjects.Chest (2, 5, "chest", self.img_chest, level.KEY_CHEST1, True)
72 ]
73 )
74 self.level1.levelright = self.level1e
75 self.level1e.levelleft = self.level1
76
77 def main_loop (self):
78 # main game loop
79 while 1:
80 # clear screen
81 self.screen.fill (pygame.Color (0,0,0))
82 # draw the level
83 self.draw_level_background (self.currentlevel)
84 # draw level objects
85 self.draw_level_objects (self.currentlevel)
86 # draw our character
87 self.draw_butaba ()
88 # display the character's inventory
89 self.draw_inventory ()
90 # draw the status info
91 self.draw_status ()
92 # update the display
93 pygame.display.update ()
94
95 # get keyboard events
96 for event in pygame.event.get ():
97 if event.type == pygame.QUIT:
98 sys.exit (0)
99 if event.type == pygame.KEYDOWN:
100 if event.key == pygame.K_UP:
101 self.move_butaba_up ()
102 elif event.key == pygame.K_DOWN:
103 self.move_butaba_down ()
104 elif event.key == pygame.K_LEFT:
105 self.move_butaba_left ()
106 elif event.key == pygame.K_RIGHT:
107 self.move_butaba_right ()
108
109 def move_butaba_up (self):
110 # clear any status messages
111 self.status_message = None
112 # first if butaba is not facing up, make him face up
113 if self.butaba.position <> butaba.Butaba.BACK:
114 self.butaba.position = butaba.Butaba.BACK
115 return
116
117 # if butaba is trying to move off the top of the screen
118 if self.butaba.row <= 0:
119 # if there is a level above set current level to that one
120 if self.currentlevel.leveltop is not None:
121 lastrow = len (self.currentlevel.leveltop.background) - 1
122 # if there is any object in that place interact with it
123 # if any object is a blocking object then avoid movement
124 if self.interact_objects (self.currentlevel.leveltop, lastrow, self.butaba.col) is False:
125 return
126
127 # make sure there is no obstacle
128 if self.check_background_obstacle (self.currentlevel.leveltop, lastrow, self.butaba.col) is False:
129 self.currentlevel = self.currentlevel.leveltop
130 self.butaba.row = lastrow
131 # normal upward movement
132 else:
133 # if there is any object in that place interact with it
134 # if any object is a blocking object then avoid movement
135 if self.interact_objects (self.currentlevel, self.butaba.row-1, self.butaba.col) is False:
136 return
137
138 if self.check_background_obstacle (self.currentlevel, self.butaba.row-1, self.butaba.col) is False:
139 self.butaba.row -= 1
140
141 def move_butaba_down (self):
142 # clear any status messages
143 self.status_message = None
144 # first if butaba is not facing forward, make him face forward/down
145 if self.butaba.position <> butaba.Butaba.FRONT:
146 self.butaba.position = butaba.Butaba.FRONT
147 return
148
149 # if butaba is trying to move off the bottom of the screen
150 if self.butaba.row >= len (self.currentlevel.background)-1:
151 # if there is a level below set current level to that one
152 if self.currentlevel.levelbottom is not None:
153 # interact with objects if any
154 # if any object is a blocking object then avoid movement
155 if self.interact_objects (self.currentlevel.levelbottom, 0, self.butaba.col) is False:
156 return
157 # make sure there is no obstacle at that position
158 if self.check_background_obstacle (self.currentlevel.levelbottom, 0, self.butaba.col) is False:
159 self.currentlevel = self.currentlevel.levelbottom
160 self.butaba.row = 0
161 # normal downward movement
162 else:
163 # interact with objects if any
164 # if any object is a blocking object then avoid movement
165 if self.interact_objects (self.currentlevel, self.butaba.row+1, self.butaba.col) is False:
166 return
167 if self.check_background_obstacle (self.currentlevel, self.butaba.row+1, self.butaba.col) is False:
168 self.butaba.row += 1
169
170 # check if a background tile is an obstacle
171 def check_background_obstacle (self, level, row, col):
172 if (level.background[row][col][2] == 1):
173 return True
174 else:
175 return False
176
177 # get and interact with objects if present in a particular row/col
178 def interact_objects (self, level, row, col):
179 objs = []
180 # get list of objects at current location
181 for obj in level.objects:
182 if obj.row == row and obj.col == col:
183 objs.append (obj)
184
185 # overall flag for blocking/non-blocking objects
186 notblock = True
187 # now perform interaction
188 for obj in objs:
189 # run the object interact function
190 if obj.interact () is False:
191 notblock = False
192 # if object can be picked up ask
193 self.pickup_object (obj)
194
195 return notblock
196
197 # picking up an object
198 def pickup_object (self, obj):
199 # only if object can be picked up, pick it up or use it
200 if obj.can_pickup is True:
201 ans = utility.ask_question (self.screen, "Found %s." % obj.text, ["Pick up", "Use", "Ignore"], self.img_menu)
202 # if the answer is "carry"
203 if ans == 1:
204 # check if the inventory is full
205 if len (self.butaba.inventory) >= butaba.Butaba.MAXITEMS:
206 self.status_message = "Failed. Inventory full"
207 else:
208 # add item to inventory
209 self.butaba.inventory.append (obj)
210 self.currentlevel.objects.remove (obj)
211
212 self.status_message = "You picked up %s" % obj.text
213 elif ans == 2:
214 # use the object according to its type
215 self.use_object (obj)
216 # if it cannot be picked up, try to use it anyway
217 else:
218 self.use_object (obj)
219
220 # this method uses the object first by calling the object use () method
221 # and then performing specific actions as necessary
222 def use_object (self, obj):
223 # if the object is a health potion
224 if isinstance (obj, gameobjects.HealthPotion) is True:
225 obj.use (self.butaba)
226 self.currentlevel.objects.remove (obj)
227 self.status_message = "You gained health"
228 # if the object is a chest
229 elif isinstance (obj, gameobjects.Chest) is True:
230 # if chest is locked, try to open it
231 if obj.locked is True:
232 # try opening the chest with every item 9the use () function
233 # of the chest determines if item is a key anyway
234 fittedkey = None
235 for invobj in self.butaba.inventory:
236 fittedkey = obj.use (invobj)
237 # if a key fits
238 if fittedkey is not None:
239 break
240 # if no key found
241 if fittedkey is None:
242 self.status_message = "No key found to open %s" % obj.text
243 # chest successfully unlocked
244 else:
245 self.status_message = "You unlocked the %s" % obj.text
246 # remove the key from inventory
247 self.butaba.inventory.remove (fittedkey)
248 # display the contents of the chest
249 else:
250 pass
251
252 def move_butaba_left (self):
253 # clear any status messages
254 self.status_message = None
255
256 # first if Butaba is not facing left, make him face left
257 if self.butaba.position <> butaba.Butaba.LEFT:
258 self.butaba.position = butaba.Butaba.LEFT
259 return
260
261 # if butaba is trying to move off the left edge
262 if self.butaba.col <= 0:
263 # if there is a level to the right set current level to that one
264 if self.currentlevel.levelleft is not None:
265 # get the last column of the previous level
266 lastcol = len (self.currentlevel.levelleft.background[0]) - 1
267 # interact with objects if any
268 # if any object is a blocking object then avoid movement
269 if self.interact_objects (self.currentlevel.levelleft, self.butaba.row, lastcol) is False:
270 return
271 # make sure there is no obstacle at that position of movement
272 if self.check_background_obstacle (self.currentlevel.levelleft, self.butaba.row, lastcol) is False:
273 self.currentlevel = self.currentlevel.levelleft
274 self.butaba.col = lastcol
275 # normal left movement
276 else:
277 # interact with objects if any
278 # if any object is a blocking object then avoid movement
279 if self.interact_objects (self.currentlevel, self.butaba.row, self.butaba.col-1) is False:
280 return
281 if self.check_background_obstacle (self.currentlevel, self.butaba.row, self.butaba.col-1) is False:
282 self.butaba.col -= 1
283
284 def move_butaba_right (self):
285 # clear any status messages
286 self.status_message = None
287
288 # First if Butaba is not facing right make him face right
289 if self.butaba.position <> butaba.Butaba.RIGHT:
290 self.butaba.position = butaba.Butaba.RIGHT
291 return
292
293 # if butaba is trying to move off the right edge
294 if self.butaba.col >= len (self.currentlevel.background[0])-1:
295 # if there is a level to the right swap current level with that one
296 if self.currentlevel.levelright is not None:
297 # interact with objects if any
298 # if any object is a blocking object then avoid movement
299 if self.interact_objects (self.currentlevel.levelright, self.butaba.row, 0) is False:
300 return
301
302 # make sure there is no obstacle at that position of movement
303 # get the last column of the previous level
304 if self.check_background_obstacle (self.currentlevel.levelright, self.butaba.row, 0) is False:
305 self.currentlevel = self.currentlevel.levelright
306 self.butaba.col = 0
307 # normal right movement
308 else:
309 # interact with objects if any
310 # if any object is a blocking object then avoid moving
311 if self.interact_objects (self.currentlevel, self.butaba.row, self.butaba.col + 1) is False:
312 return
313 if self.check_background_obstacle (self.currentlevel, self.butaba.row, self.butaba.col + 1) is False:
314 self.butaba.col += 1
315
316 def draw_butaba (self):
317 if self.butaba.position == butaba.Butaba.FRONT:
318 self.screen.blit (self.img_butabafront, (self.butaba.col*48, self.butaba.row*48))
319 elif self.butaba.position == butaba.Butaba.BACK:
320 self.screen.blit (self.img_butababack, (self.butaba.col*48, self.butaba.row*48))
321 elif self.butaba.position == butaba.Butaba.LEFT:
322 self.screen.blit (self.img_butabaleft, (self.butaba.col*48, self.butaba.row*48))
323 elif self.butaba.position == butaba.Butaba.RIGHT:
324 self.screen.blit (self.img_butabaright, (self.butaba.col*48, self.butaba.row*48))
325
326
327 # Draw the status infodisplay
328 def draw_status (self):
329 self.screen.blit (self.img_redpotion, (485, 10))
330 utility.put_text (self.screen, 550, 25, 24, (255, 0, 0), "%d" % self.butaba.health)
331
332 self.screen.blit (self.img_lightning, (620, 10))
333 utility.put_text (self.screen, 660, 20, 24, (255,255,255), "%d" % self.butaba.strength)
334
335 self.screen.blit (self.img_wand, (485, 65))
336 utility.put_text (self.screen, 550, 75, 24, (0, 0, 255), "%d" % self.butaba.magic)
337
338 self.screen.blit (self.img_bulb, (620, 65))
339 utility.put_text (self.screen, 660, 75, 24, (0, 255, 0), "%d" % self.butaba.experience)
340
341 self.screen.blit (self.img_goldcoins, (485, 110))
342 utility.put_text (self.screen, 550, 130, 24, (255, 255, 0), "%d" % self.butaba.gold)
343
344 if self.status_message is not None:
345 utility.put_text (self.screen, 10, 485, 18, (255,255, 0), "%s" % self.status_message)
346
347 # display the inventory of the player
348 def draw_inventory (self):
349 # draw the inventory slots
350 r = 1
351 c = 1
352 utility.put_text (self.screen, 490, 170, 22, (255,255 , 0), "Inventory")
353 for i in range (butaba.Butaba.MAXITEMS):
354 self.screen.blit (self.img_inventory, (440+c*54, 150+r*54))
355 if c % 4 == 0:
356 r += 1
357 c = 1
358 else:
359 c += 1
360
361 r = 1
362 c = 1
363 for obj in self.butaba.inventory:
364 self.screen.blit (obj.image, (440+c*54+2, 150+r*54+2))
365 if c % 4 == 0:
366 r += 1
367 c = 1
368 else:
369 c += 1
370
371 # Draw the level background tiles on surface
372 def draw_level_background (self, level):
373 i = 0
374 for row in level.background:
375 j = 0
376 for tilerow, tilecol, is_solid in row:
377 tilex = tilecol * 48
378 tiley = tilerow * 48
379 self.screen.blit (self.img_tileset, (j*48, i*48), pygame.Rect (tilex, tiley, 48, 48))
380
381 j += 1
382 i += 1
383
384 # Draw the level objects
385 def draw_level_objects (self, level):
386 for obj in level.objects:
387 if obj.image is not None:
388 self.screen.blit (obj.image, (obj.col*48, obj.row*48))