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