Added dialogues for the mayor
[butaba-adventures.git] / maingame.py
1 import pygame
2 import sys
3 import random
4 import os.path
5 import cPickle
6
7 import level
8 import butaba
9 import utility
10 import gameobjects
11 import constants
12 import npcs
13 import gamestate
14
15 class MainGame:
16
17 # initialize the game
18 def __init__ (self):
19 random.seed ()
20 pygame.init ()
21
22 self.clock = pygame.time.Clock ()
23 self.screen = pygame.display.set_mode ((720, 512))
24 pygame.display.set_caption ("The Adventures of Butaba")
25
26 # initalize background graphics
27 self.img_tileset = pygame.image.load (os.path.join ("background", "tileset.png")).convert ()
28
29 self.img_menu = pygame.image.load (os.path.join ("background", "menu_screen.png")).convert ()
30
31 self.img_inventory = pygame.image.load (os.path.join ("background", "inventory.png")).convert ()
32
33 self.img_chestbg = pygame.image.load (os.path.join ("background", "chestcontent.png")).convert ()
34
35 self.img_dialogue = pygame.image.load (os.path.join ("background", "dialog_screen.png")).convert ()
36 self.img_dialogue.set_colorkey (pygame.Color (0, 255, 0))
37
38
39 # initialize object graphics
40 self.img_redpotion = pygame.image.load (os.path.join ("objects", "red-potion.png")).convert ()
41 self.img_redpotion.set_colorkey (pygame.Color (0, 255, 0))
42 self.img_goldcoins = pygame.image.load (os.path.join ("objects", "gold-coins.png")).convert ()
43 self.img_goldcoins.set_colorkey (pygame.Color (0, 255, 0))
44 self.img_wand = pygame.image.load (os.path.join ("objects", "wand.png")).convert ()
45 self.img_wand.set_colorkey (pygame.Color (0, 255, 0))
46 self.img_bulb = pygame.image.load (os.path.join ("objects", "bulb.png")).convert ()
47 self.img_bulb.set_colorkey (pygame.Color (0, 255, 0))
48 self.img_lightning = pygame.image.load (os.path.join ("objects", "lightning.png")).convert ()
49 self.img_lightning.set_colorkey (pygame.Color (0, 255, 0))
50 self.img_key = pygame.image.load (os.path.join ("objects", "key.png")).convert ()
51 self.img_key.set_colorkey (pygame.Color (0, 255, 0))
52 self.img_key2 = pygame.image.load (os.path.join ("objects", "key2.png")).convert ()
53 self.img_key2.set_colorkey (pygame.Color (0, 255, 0))
54 self.img_chest = pygame.image.load (os.path.join ("objects", "chest.png")).convert ()
55 self.img_chest.set_colorkey (pygame.Color (0, 255, 0))
56 self.img_bucket = pygame.image.load (os.path.join ("objects", "bucket.png")).convert ()
57 self.img_bucket.set_colorkey (pygame.Color (0, 255, 0))
58
59 # initialize player graphics
60 self.img_butabafront = pygame.image.load (os.path.join ("sprite", "butaba-front.png")).convert ()
61 self.img_butabafront.set_colorkey (pygame.Color (0, 255, 0))
62 self.img_butababack = pygame.image.load (os.path.join ("sprite", "butaba-back.png")).convert ()
63 self.img_butababack.set_colorkey (pygame.Color (0, 255, 0))
64 self.img_butabaleft = pygame.image.load (os.path.join ("sprite", "butaba-left.png")).convert ()
65 self.img_butabaleft.set_colorkey (pygame.Color (0, 255, 0))
66 self.img_butabaright = pygame.image.load (os.path.join ("sprite", "butaba-right.png")).convert ()
67 self.img_butabaright.set_colorkey (pygame.Color (0, 255, 0))
68
69 # initialize NPC graphics
70 self.img_bulisafront = pygame.image.load (os.path.join ("sprite", "bulisa-front.png")).convert ()
71 self.img_bulisafront.set_colorkey (pygame.Color (0, 255, 0))
72
73 self.img_bulisaback = pygame.image.load (os.path.join ("sprite", "bulisa-back.png")).convert ()
74 self.img_bulisaback.set_colorkey (pygame.Color (0, 255, 0))
75
76 self.img_bulisaleft = pygame.image.load (os.path.join ("sprite", "bulisa-left.png")).convert ()
77 self.img_bulisaleft.set_colorkey (pygame.Color (0, 255, 0))
78
79 self.img_bulisaright = pygame.image.load (os.path.join ("sprite", "bulisa-right.png")).convert ()
80 self.img_bulisaright.set_colorkey (pygame.Color (0, 255, 0))
81
82 self.img_mayorfront = pygame.image.load (os.path.join ("sprite", "mayor-front.png")).convert ()
83 self.img_mayorfront.set_colorkey (pygame.Color (0, 255, 0))
84
85 self.img_mayorback = pygame.image.load (os.path.join ("sprite", "mayor-back.png")).convert ()
86 self.img_mayorback.set_colorkey (pygame.Color (0, 255, 0))
87
88 self.img_mayorleft = pygame.image.load (os.path.join ("sprite", "mayor-left.png")).convert ()
89 self.img_mayorleft.set_colorkey (pygame.Color (0, 255, 0))
90
91 self.img_mayorright = pygame.image.load (os.path.join ("sprite", "mayor-right.png")).convert ()
92 self.img_mayorright.set_colorkey (pygame.Color (0, 255, 0))
93
94 # initialize portraits
95 self.img_butaba_portrait = pygame.image.load (os.path.join ("portraits", "butaba.png")).convert ()
96 self.img_bulisa_portrait = pygame.image.load (os.path.join ("portraits", "bulisa.png")).convert ()
97 self.img_mayor_portrait = pygame.image.load (os.path.join ("portraits", "mayor.png")).convert ()
98
99 # set level data
100 self.setup_levels ()
101 # set current level and position of our character
102 self.currentlevel = self.level1
103
104 # set the status message
105 self.status_message = "Game started"
106
107 self.butaba = butaba.Butaba (5,0, self.img_butabaleft,
108 self.img_butabaright, self.img_butabafront,
109 self.img_butababack, self.img_butaba_portrait, constants.RIGHT)
110
111 # set up the levels and their interactions
112 def setup_levels (self):
113 # set up the objects first
114 chest1 = gameobjects.Chest (2, 6, "chest", self.img_chest, constants.KEY_CHEST1, True)
115 chest2 = gameobjects.Chest (6, 6, "chest", self.img_chest, constants.KEY_CHEST2, True)
116 key2 = gameobjects.Key (5, 3, "a chest key", self.img_key, constants.KEY_CHEST2)
117 potion = gameobjects.HealthPotion (5, 2, self.img_redpotion)
118 gold50 = gameobjects.GoldCoins (6, 2, self.img_goldcoins, 50)
119 gold25 = gameobjects.GoldCoins (6, 2, self.img_goldcoins, 25)
120 gold10 = gameobjects.GoldCoins (6, 2, self.img_goldcoins, 10)
121 bucket = gameobjects.Bucket (6, 3, self.img_bucket)
122
123 well1 = gameobjects.Well (4, 7)
124 well2 = gameobjects.Well (5, 7)
125 well3 = gameobjects.Well (4, 8)
126 well4 = gameobjects.Well (5, 8)
127
128 npc_bulisa = npcs.Bulisa (4, 3, self.img_bulisaleft, self.img_bulisaright,
129 self.img_bulisafront, self.img_bulisaback,
130 self.img_bulisa_portrait, constants.FRONT,
131 (2, 2, 2, 2),
132 [ os.path.join ("dialogues", "bulisa1.dlg"),
133 os.path.join ("dialogues", "bulisa2.dlg"),
134 os.path.join ("dialogues", "bulisa3.dlg"),
135 os.path.join ("dialogues", "bulisa4.dlg"),
136 os.path.join ("dialogues", "bulisa5.dlg"),
137 os.path.join ("dialogues", "bulisa6.dlg"),
138 os.path.join ("dialogues", "bulisa7.dlg") ] )
139
140 npc_mayor = npcs.Mayor (5, 4, self.img_mayorleft, self.img_mayorright,
141 self.img_mayorfront, self.img_mayorback,
142 self.img_mayor_portrait, constants.FRONT,
143 (2, 2, 2, 2),
144 [ os.path.join ("dialogues", "mayor1.dlg"),
145 os.path.join ("dialogues", "mayor2.dlg"),
146 os.path.join ("dialogues", "mayor3.dlg") ])
147
148 chest1.objects = [ gold50, gold25, key2, gold10 ]
149
150 # create the levels
151 self.level1 = level.Level (cPickle.load (file (os.path.join ("levels", "level1.dat"))),
152 objects = [ chest1 ] )
153
154 self.level1w = level.Level (cPickle.load (file (os.path.join ("levels", "level1w.dat"))))
155
156 self.level1e = level.Level (cPickle.load (file (os.path.join ("levels", "level1e.dat"))),
157 objects = [ potion, chest2 ], npcs = [ npc_bulisa ])
158
159 self.level1ee = level.Level (cPickle.load (file (os.path.join ("levels", "level1ee.dat"))),
160 objects = [ well1, well2, well3, well4 ])
161
162 self.level1n = level.Level (cPickle.load (file (os.path.join ("levels", "level1n.dat"))),
163 objects = [ bucket ])
164
165 self.level1nw = level.Level (cPickle.load (file (os.path.join ("levels", "level1nw.dat"))))
166
167 self.level1ne = level.Level (cPickle.load (file (os.path.join ("levels", "level1ne.dat"))))
168
169 self.level1nee = level.Level (cPickle.load (file (os.path.join ("levels", "level1nee.dat"))))
170
171 self.level1sw = level.Level (cPickle.load (file (os.path.join ("levels", "level1sw.dat"))))
172
173 self.level1sww = level.Level (cPickle.load (file (os.path.join ("levels", "level1sww.dat"))))
174
175 self.level1ww = level.Level (cPickle.load (file (os.path.join ("levels", "level1ww.dat"))),
176 npcs = [ npc_mayor ])
177
178 # set up the interaction between levels (level exits)
179 self.level1.levelright = self.level1e
180 self.level1.levelleft = self.level1w
181 self.level1e.levelleft = self.level1
182 self.level1e.levelright = self.level1ee
183 self.level1ee.levelleft = self.level1e
184 self.level1w.levelright = self.level1
185 self.level1.leveltop = self.level1n
186 self.level1n.levelbottom = self.level1
187 self.level1n.levelleft = self.level1nw
188 self.level1w.leveltop = self.level1nw
189 self.level1nw.levelright = self.level1n
190 self.level1nw.levelbottom = self.level1w
191 self.level1n.levelright = self.level1ne
192 self.level1e.leveltop = self.level1ne
193 self.level1ne.levelleft = self.level1n
194 self.level1ne.levelbottom = self.level1e
195 self.level1ne.levelright = self.level1nee
196 self.level1ee.leveltop = self.level1nee
197 self.level1nee.levelleft = self.level1ne
198 self.level1nee.levelbottom = self.level1ee
199 self.level1sw.leveltop = self.level1w
200 self.level1w.levelbottom = self.level1sw
201 self.level1sww.levelright = self.level1sw
202 self.level1sw.levelleft = self.level1sww
203 self.level1ww.levelright = self.level1w
204 self.level1ww.levelbottom = self.level1sww
205 self.level1sww.leveltop = self.level1ww
206 self.level1w.levelleft = self.level1ww
207
208 def main_loop (self):
209 # main game loop
210 while 1:
211 self.clock.tick (25)
212 # clear screen
213 self.screen.fill (pygame.Color (0,0,0))
214 # draw the level
215 self.draw_level_background (self.currentlevel)
216 # draw level objects
217 self.draw_level_objects (self.currentlevel)
218 # draw the NPCs in the level
219 self.draw_level_npcs (self.currentlevel)
220 # draw our character
221 self.draw_butaba ()
222 # display the character's inventory
223 self.draw_inventory ()
224 # draw the status info
225 self.draw_status ()
226 # update the display
227 pygame.display.update ()
228
229 # get keyboard events
230 for event in pygame.event.get ():
231 if event.type == pygame.QUIT:
232 sys.exit (0)
233 # if keyboard event
234 if event.type == pygame.KEYDOWN:
235 if event.key == pygame.K_UP:
236 self.move_butaba_up ()
237 elif event.key == pygame.K_DOWN:
238 self.move_butaba_down ()
239 elif event.key == pygame.K_LEFT:
240 self.move_butaba_left ()
241 elif event.key == pygame.K_RIGHT:
242 self.move_butaba_right ()
243 # drinking health potion in inventory
244 elif event.key == ord ("h") or event.key == ord ("H"):
245 self.inventory_drink_health_potion ()
246 # quit the game
247 elif event.key == ord ("q") or event.key == ord ("Q"):
248 sys.exit (0)
249
250 # drink a health potion if it is in the player's inventory
251 def inventory_drink_health_potion (self):
252 # look for a health potion
253 for item in self.butaba.objects:
254 if isinstance (item, gameobjects.HealthPotion) is True:
255 self.use_object (self.butaba, item)
256 break
257
258 def move_butaba_up (self):
259 # clear any status messages
260 self.status_message = None
261 # first if butaba is not facing up, make him face up
262 if self.butaba.position <> constants.BACK:
263 self.butaba.position = constants.BACK
264 return
265
266 # if butaba is trying to move off the top of the screen
267 if self.butaba.row <= 0:
268 # if there is a level above set current level to that one
269 if self.currentlevel.leveltop is not None:
270 lastrow = len (self.currentlevel.leveltop.background) - 1
271 # interact with objects
272 if self.level_interact (self.currentlevel.leveltop, lastrow, self.butaba.col) is False:
273 return
274
275 # make sure there is no obstacle
276 if self.check_background_obstacle (self.currentlevel.leveltop, lastrow, self.butaba.col) is False:
277 self.currentlevel = self.currentlevel.leveltop
278 self.butaba.row = lastrow
279 # normal upward movement
280 else:
281 # if there is any object in that place interact with it
282 # if any object is a blocking object then avoid movement
283 if self.level_interact (self.currentlevel, self.butaba.row-1, self.butaba.col) is False:
284 return
285
286 if self.check_background_obstacle (self.currentlevel, self.butaba.row-1, self.butaba.col) is False:
287 self.butaba.row -= 1
288
289 def move_butaba_down (self):
290 # clear any status messages
291 self.status_message = None
292 # first if butaba is not facing forward, make him face forward/down
293 if self.butaba.position <> constants.FRONT:
294 self.butaba.position = constants.FRONT
295 return
296
297 # if butaba is trying to move off the bottom of the screen
298 if self.butaba.row >= len (self.currentlevel.background)-1:
299 # if there is a level below set current level to that one
300 if self.currentlevel.levelbottom is not None:
301 # interact with objects if any
302 # if any object is a blocking object then avoid movement
303 if self.level_interact (self.currentlevel.levelbottom, 0, self.butaba.col) is False:
304 return
305 # make sure there is no obstacle at that position
306 if self.check_background_obstacle (self.currentlevel.levelbottom, 0, self.butaba.col) is False:
307 self.currentlevel = self.currentlevel.levelbottom
308 self.butaba.row = 0
309 # normal downward movement
310 else:
311 # interact with objects if any
312 # if any object is a blocking object then avoid movement
313 if self.level_interact (self.currentlevel, self.butaba.row+1, self.butaba.col) is False:
314 return
315 if self.check_background_obstacle (self.currentlevel, self.butaba.row+1, self.butaba.col) is False:
316 self.butaba.row += 1
317
318 # check if a background tile is an obstacle
319 def check_background_obstacle (self, level, row, col):
320 if (level.background[row][col][2] == 1):
321 return True
322 else:
323 return False
324
325 # get and interact with objects and characters if present in a particular row/col
326 def level_interact (self, level, row, col):
327 objs = []
328 # get list of objects at current location
329 for obj in level.objects:
330 if obj.row == row and obj.col == col:
331 objs.append (obj)
332
333 notblock = self.interact_objects (level, objs)
334
335 # get npc at current location
336 current_npc = None
337 for npc in level.npcs:
338 if npc.row == row and npc.col == col:
339 current_npc = npc
340 break
341
342 # npcs always block the tile. So return false if there is an NPC
343 # at the location
344 if current_npc is not None:
345 self.interact_npc (current_npc)
346 return False
347
348 return notblock
349
350 # interaction with npcs
351 def interact_npc (self, npc):
352 # interact with NPC and get the response ID
353 # if the NPC is Bulisa
354
355 # if the NPC is dead cannot talk with the NPC
356 if npc.is_dead is True:
357 self.status_message = "%s is dead! RIP..." % npc.charname
358 return
359
360 if isinstance (npc, npcs.Bulisa):
361 # interact with Bulisa
362 self.interact_npc_bulisa (npc)
363 elif isinstance (npc, npcs.Mayor):
364 # interact with the Mayor
365 self.interact_npc_mayor (npc)
366
367 # interact with the mayor
368 def interact_npc_mayor (self, npc):
369 resp_id = None
370
371 if gamestate.flag["mission_charity_money"] is True:
372 npc.currentdialog = 2
373 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
374 elif gamestate.flag["mission_charity_informed_mayor"] is True:
375 npc.currentdialog = 1
376 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
377 else:
378 npc.currentdialog = 0
379 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
380
381
382 # deal with the respnses
383 if resp_id == "missioncharityaccepted":
384 gamestate.flag["mission_charity_money"] = True
385 # response ID is none
386 elif resp_id is None:
387 self.status_message = "You cannot initiate a conversation with %s" % npc.charname
388
389 # interact with NPC Bulisa
390 def interact_npc_bulisa (self, npc):
391 # set initial response ID to none
392 resp_id = None
393
394 # check for global game states (starting from later flags to earlier ones
395
396 # whether butaba is doing the mayor charity mission
397 if gamestate.flag["mission_charity_money"] is True:
398 npc.currentdialog = 6
399 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
400 # whether the drawing water from well mission completed
401 elif gamestate.flag["mission_charity_informed"] is True:
402 npc.currentdialog = 5
403 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
404 elif gamestate.flag['mission_bulisa_water_from_well_complete'] is True:
405 npc.currentdialog = 4
406 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
407 # whether the drawing water from well mission refused
408 elif gamestate.flag['mission_bulisa_water_from_well_refused'] is True:
409 npc.currentdialog = 2
410 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
411 # whether the drawing water from well mission is accepted
412 elif gamestate.flag ['mission_bulisa_water_from_well'] is True:
413 # check if butaba has a bucket full of water
414 for invobj in self.butaba.objects:
415 if isinstance (invobj, gameobjects.Bucket) is True:
416 # if yes, then mission is completed accordingly
417 if invobj.liquid == "water":
418 gamestate.flag["mission_bulisa_water_from_well_complete"] = True
419 self.butaba.objects.remove (invobj)
420 key1 = gameobjects.Key (5, 3, "a chest key", self.img_key2, constants.KEY_CHEST1)
421 self.butaba.objects.append (key1)
422 break
423 # water mission is not completed yet
424 if gamestate.flag["mission_bulisa_water_from_well_complete"] is False:
425 npc.currentdialog = 1
426 else:
427 npc.currentdialog = 3
428 # get the response ID
429 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
430 # Butaba hasn't been told about the initial mission yet, so tell him now
431 else:
432 npc.currentdialog = 0
433 # get the response ID
434 resp_id = utility.dialogue_play (self.screen, self.img_dialogue, npc, self.butaba.portrait, 0, 90)
435
436 # NPC RESPONSES:
437 # Check each response
438
439 # Drawing water from well mission is refused
440 if resp_id == "refusedwatermission":
441 gamestate.flag["mission_bulisa_water_from_well_refused"] = True
442 # Drawing water from well mission begins
443 elif resp_id == "acceptedwatermission":
444 gamestate.flag["mission_bulisa_water_from_well"] = True
445 gamestate.flag["mission_bulisa_water_from_well_refused"] = False
446 # Butaba is informed about the mayor's charity
447 elif resp_id == "informedcharitymission":
448 gamestate.flag["mission_charity_informed"] = True
449 # if mayor has been told about Butaba
450 elif resp_id == "mayortoldaboutbutaba":
451 gamestate.flag["mission_charity_informed_mayor"] = True
452 # if response ID is none
453 elif resp_id is None:
454 self.status_message = "You cannot initiate a conversation with %s" % npc.charname
455
456 # interaction with objects
457 def interact_objects (self, container, objs):
458 # overall flag for blocking/non-blocking objects
459 notblock = True
460
461 # now perform interaction
462 for obj in objs:
463 # run the object interact function
464 if obj.interact () is False:
465 notblock = False
466 # if object can be picked up ask
467 if obj.can_pickup is True:
468 ans = utility.ask_question (self.screen, "Found %s." % obj.text, ["Pick up", obj.use_str, "Ignore"], self.img_menu)
469 # if the answer is "pick up"
470 if ans == 1:
471 self.pickup_object (container, obj)
472 elif ans == 2:
473 # use the object according to its type
474 self.use_object (container, obj)
475 # if it cannot be picked up, try to use it anyway
476 else:
477 ans = utility.ask_question (self.screen, "Found %s." % obj.text, [obj.use_str, "Ignore"], self.img_menu)
478 if ans == 1:
479 self.use_object (container, obj)
480
481 return notblock
482
483 # transfer an object from one container to another
484 # container must have an objects list
485 def transfer_object (self, source, obj, dest):
486 # remove object from source
487 source.objects.remove (obj)
488 # add object to destination
489 dest.objects.append (obj)
490
491 # picking up an object
492 def pickup_object (self, container, obj):
493 # only if object can be picked up, pick it up or use it
494 if obj.can_pickup is True:
495 # check if the inventory is full
496 if len (self.butaba.objects) >= constants.MAXITEMS:
497 self.status_message = "Cannot pick up item. Inventory full"
498 else:
499 # add item to inventory
500 self.transfer_object (container, obj, self.butaba)
501
502 self.status_message = "You picked up %s" % obj.text
503
504 # this method uses the object first by calling the object use () method
505 # and then performing specific actions as necessary
506 def use_object (self, container, obj):
507 # if the object is a health potion
508 if isinstance (obj, gameobjects.HealthPotion) is True:
509 if self.butaba.health < constants.MAXHEALTH:
510 obj.use (self.butaba)
511 container.objects.remove (obj)
512 self.status_message = "You gained health"
513 else:
514 self.status_message = "You already have maximum health!"
515 # if the object is a chest
516 elif isinstance (obj, gameobjects.Chest) is True:
517 # if chest is locked, try to open it
518 if obj.locked is True:
519 # try opening the chest with every item 9the use () function
520 # of the chest determines if item is a key anyway
521 fittedkey = None
522 for invobj in self.butaba.objects:
523 fittedkey = obj.use (invobj)
524 # if a key fits
525 if fittedkey is not None:
526 break
527 # if no key found
528 if fittedkey is None:
529 self.status_message = "No key found to open %s" % obj.text
530 # chest successfully unlocked
531 else:
532 self.status_message = "You unlocked the %s" % obj.text
533 # remove the key from Butaba
534 self.butaba.objects.remove (fittedkey)
535 # add an experience point for unlocking chest subject
536 # to a limit of KNOWLEDGEMAX_CHEST_UNLOCK
537 if self.butaba.experience < constants.KNOWLEDGEMAX_CHEST_UNLOCK:
538 self.butaba.experience += 1
539 self.status_message += " and gained experience!"
540 # display the contents of the chest
541 else:
542 item = utility.get_container_object (self.screen, obj, self.img_chestbg, 30)
543 if item is not None:
544 self.interact_objects (obj, [ item, ])
545
546 # if the object is gold coins
547 elif isinstance (obj, gameobjects.GoldCoins) is True:
548 obj.use (self.butaba)
549 self.status_message = "You picked up %d gold." % obj.value
550 # remove the gold coins after adding it to Butaba's gold
551 container.objects.remove (obj)
552 # using a bucket means emptying it
553 elif isinstance (obj, gameobjects.Bucket) is True:
554 if obj.liquid is not None:
555 self.status_message = "You emptied the bucket of %s" % obj.liquid
556 obj.use (self.butaba)
557 else:
558 self.status_message = "Bucket is already empty."
559 # using a well
560 elif isinstance (obj, gameobjects.Well) is True:
561 # if the well is not dry, i.e. it has some liquid
562 if obj.liquid is not None:
563 # search butaba inventory for an empty bucket
564 for invobj in self.butaba.objects:
565 # bucket found, now check if it is empty
566 if isinstance (invobj, gameobjects.Bucket) is True:
567 # if empty fill it
568 if invobj.liquid is None:
569 obj.use (invobj)
570 self.status_message = "You successfully filled the %s with %s" % (invobj.text, obj.liquid)
571 if self.butaba.strength < constants.STRENGTHMAX_DRAW_WELL_WATER:
572 self.butaba.strength += 2
573 self.status_message += " and gained strength!"
574 return
575 self.status_message = "You have no empty bucket to draw %s with!" % obj.liquid
576 else:
577 self.status_message = "%s appears to be dry!" % obj.text
578
579 def move_butaba_left (self):
580 # clear any status messages
581 self.status_message = None
582
583 # first if Butaba is not facing left, make him face left
584 if self.butaba.position <> constants.LEFT:
585 self.butaba.position = constants.LEFT
586 return
587
588 # if butaba is trying to move off the left edge
589 if self.butaba.col <= 0:
590 # if there is a level to the right set current level to that one
591 if self.currentlevel.levelleft is not None:
592 # get the last column of the previous level
593 lastcol = len (self.currentlevel.levelleft.background[0]) - 1
594 # interact with objects if any
595 # if any object is a blocking object then avoid movement
596 if self.level_interact (self.currentlevel.levelleft, self.butaba.row, lastcol) is False:
597 return
598 # make sure there is no obstacle at that position of movement
599 if self.check_background_obstacle (self.currentlevel.levelleft, self.butaba.row, lastcol) is False:
600 self.currentlevel = self.currentlevel.levelleft
601 self.butaba.col = lastcol
602 # normal left movement
603 else:
604 # interact with objects if any
605 # if any object is a blocking object then avoid movement
606 if self.level_interact (self.currentlevel, self.butaba.row, self.butaba.col-1) is False:
607 return
608 if self.check_background_obstacle (self.currentlevel, self.butaba.row, self.butaba.col-1) is False:
609 self.butaba.col -= 1
610
611 def move_butaba_right (self):
612 # clear any status messages
613 self.status_message = None
614
615 # First if Butaba is not facing right make him face right
616 if self.butaba.position <> constants.RIGHT:
617 self.butaba.position = constants.RIGHT
618 return
619
620 # if butaba is trying to move off the right edge
621 if self.butaba.col >= len (self.currentlevel.background[0])-1:
622 # if there is a level to the right swap current level with that one
623 if self.currentlevel.levelright is not None:
624 # interact with objects if any
625 # if any object is a blocking object then avoid movement
626 if self.level_interact (self.currentlevel.levelright, self.butaba.row, 0) is False:
627 return
628
629 # make sure there is no obstacle at that position of movement
630 # get the last column of the previous level
631 if self.check_background_obstacle (self.currentlevel.levelright, self.butaba.row, 0) is False:
632 self.currentlevel = self.currentlevel.levelright
633 self.butaba.col = 0
634 # normal right movement
635 else:
636 # interact with objects if any
637 # if any object is a blocking object then avoid moving
638 if self.level_interact (self.currentlevel, self.butaba.row, self.butaba.col + 1) is False:
639 return
640 if self.check_background_obstacle (self.currentlevel, self.butaba.row, self.butaba.col + 1) is False:
641 self.butaba.col += 1
642
643 def draw_butaba (self):
644 if self.butaba.position == constants.FRONT:
645 self.screen.blit (self.butaba.imagefront, (self.butaba.col*48, self.butaba.row*48))
646 elif self.butaba.position == constants.BACK:
647 self.screen.blit (self.butaba.imageback, (self.butaba.col*48, self.butaba.row*48))
648 elif self.butaba.position == constants.LEFT:
649 self.screen.blit (self.butaba.imageleft, (self.butaba.col*48, self.butaba.row*48))
650 elif self.butaba.position == constants.RIGHT:
651 self.screen.blit (self.butaba.imageright, (self.butaba.col*48, self.butaba.row*48))
652
653
654 # Draw the status infodisplay
655 def draw_status (self):
656 self.screen.blit (self.img_redpotion, (485, 10))
657 utility.put_text (self.screen, 550, 25, 20, (255, 0, 0), "%d" % self.butaba.health)
658
659 self.screen.blit (self.img_lightning, (620, 10))
660 utility.put_text (self.screen, 660, 25, 20, (255,255,255), "%d" % self.butaba.strength)
661
662 self.screen.blit (self.img_wand, (485, 65))
663 utility.put_text (self.screen, 550, 75, 20, (0, 0, 255), "%d" % self.butaba.magic)
664
665 self.screen.blit (self.img_bulb, (620, 65))
666 utility.put_text (self.screen, 660, 75, 20, (0, 255, 0), "%d" % self.butaba.experience)
667
668 self.screen.blit (self.img_goldcoins, (485, 110))
669 utility.put_text (self.screen, 550, 130, 20, (255, 255, 0), "%d" % self.butaba.gold)
670
671 if self.status_message is not None:
672 utility.put_text (self.screen, 10, 485, 10, (255,255, 0), "%s" % self.status_message)
673
674 # display the inventory of the player
675 def draw_inventory (self):
676 # draw the inventory slots
677 r = 1
678 c = 1
679 utility.put_text (self.screen, 490, 170, 16, (255,255 , 0), "Inventory")
680 for i in range (constants.MAXITEMS):
681 self.screen.blit (self.img_inventory, (440+c*54, 150+r*54))
682 if c % 4 == 0:
683 r += 1
684 c = 1
685 else:
686 c += 1
687
688 r = 1
689 c = 1
690 for obj in self.butaba.objects:
691 self.screen.blit (obj.image, (440+c*54+2, 150+r*54+2))
692 if c % 4 == 0:
693 r += 1
694 c = 1
695 else:
696 c += 1
697
698 # Draw the level background tiles on surface
699 def draw_level_background (self, level):
700 i = 0
701 for row in level.background:
702 j = 0
703 for tilerow, tilecol, is_solid in row:
704 tilex = tilecol * 48
705 tiley = tilerow * 48
706 self.screen.blit (self.img_tileset, (j*48, i*48), pygame.Rect (tilex, tiley, 48, 48))
707
708 j += 1
709 i += 1
710
711 # Draw the level objects
712 def draw_level_objects (self, level):
713 for obj in level.objects:
714 if obj.image is not None:
715 self.screen.blit (obj.image, (obj.col*48, obj.row*48))
716
717
718 # Draw the NPCs in the level
719 def draw_level_npcs (self, level):
720 for npc in level.npcs:
721 # if npc is not dead then move the NPC randomly depending on their
722 # movement area
723 if npc.is_dead is False:
724 # move this turn?
725 if random.randint (1, 500) < npc.movement_speed:
726 # whether to change direction?
727 if random.randint (1, 100) < 25:
728 npc.position = random.randint (0, 3)
729 else:
730 # if left movement
731 if npc.position == constants.LEFT:
732 # cannot move beyond level and cannot move beyond the
733 # left limit area
734 if npc.col > 0 and npc.col > npc.initcol - npc.leftlimit:
735 # check if there is any background obstacle
736 if self.check_background_obstacle (level, npc.row, npc.col - 1) is False:
737 # check if there are any objects
738 objblock = False
739 npcblock = False
740 for lobj in level.objects:
741 if lobj.row == npc.row and lobj.col == npc.col - 1:
742 objblock = True
743 break
744 # check if there any any npcs blocking
745 for lnpc in level.npcs:
746 if lnpc.row == npc.row and lnpc.col == npc.col - 1:
747 npcblock = True
748 break
749 if objblock is False and npcblock is False:
750 # if butaba is not blocking
751 if self.butaba.row <> npc.row or self.butaba.col <> npc.col - 1:
752 npc.col -= 1
753 elif npc.position == constants.RIGHT:
754 # cannot move beyond level and cannot move beyond the
755 # right limit area
756 if npc.col < 9 and npc.col < npc.initcol + npc.rightlimit:
757 # check if there is any background obstacle
758 if self.check_background_obstacle (level, npc.row, npc.col + 1) is False:
759 # check if there are any objects
760 objblock = False
761 npcblock = False
762 for lobj in level.objects:
763 if lobj.row == npc.row and lobj.col == npc.col + 1:
764 objblock = True
765 break
766 # check if there any any npcs blocking
767 for lnpc in level.npcs:
768 if lnpc.row == npc.row and lnpc.col == npc.col + 1:
769 npcblock = True
770 break
771 if objblock is False and npcblock is False:
772 # if butaba is not blocking
773 if self.butaba.row <> npc.row or self.butaba.col <> npc.col + 1:
774 npc.col += 1
775 elif npc.position == constants.FRONT:
776 # cannot move beyond level and cannot move beyond the
777 # lower bottom limit area
778 if npc.row < 9 and npc.row < npc.initrow + npc.bottomlimit:
779 # check if there is any background obstacle
780 if self.check_background_obstacle (level, npc.row + 1, npc.col) is False:
781 # check if there are any objects
782 objblock = False
783 npcblock = False
784 for lobj in level.objects:
785 if lobj.row == npc.row + 1 and lobj.col == npc.col:
786 objblock = True
787 break
788 # check if there any any npcs blocking
789 for lnpc in level.npcs:
790 if lnpc.row == npc.row + 1 and lnpc.col == npc.col:
791 npcblock = True
792 break
793 if objblock is False and npcblock is False:
794 # if butaba is not blocking
795 if self.butaba.row <> npc.row + 1 or self.butaba.col <> npc.col:
796 npc.row += 1
797 elif npc.position == constants.BACK:
798 # cannot move beyond level and cannot move beyond the
799 # top upper limit area
800 if npc.row > 0 and npc.row > npc.initrow - npc.toplimit:
801 # check if there is any background obstacle
802 if self.check_background_obstacle (level, npc.row - 1, npc.col) is False:
803 # check if there are any objects
804 objblock = False
805 npcblock = False
806 for lobj in level.objects:
807 if lobj.row == npc.row - 1 and lobj.col == npc.col:
808 objblock = True
809 break
810 # check if there any any npcs blocking
811 for lnpc in level.npcs:
812 if lnpc.row == npc.row - 1 and lnpc.col == npc.col:
813 npcblock = True
814 break
815 # if no object is blocking
816 if objblock is False and npcblock is False:
817 # if butaba is not blocking
818 if self.butaba.row <> npc.row - 1 or self.butaba.col <> npc.col:
819 npc.row -= 1
820
821 if npc.position == constants.FRONT:
822 img = npc.imagefront
823 elif npc.position == constants.BACK:
824 img = npc.imageback
825 elif npc.position == constants.LEFT:
826 img = npc.imageleft
827 else:
828 img = npc.imageright
829
830 self.screen.blit (img, (npc.col*48, npc.row*48))