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