19 self
.screen
= pygame
.display
.set_mode ((720, 512))
20 pygame
.display
.set_caption ("The Adventures of Butaba")
22 # initalize background graphics
23 self
.img_tileset
= pygame
.image
.load (os
.path
.join ("background", "tileset.png")).convert ()
25 self
.img_menu
= pygame
.image
.load (os
.path
.join ("background", "menu_screen.png")).convert ()
27 self
.img_inventory
= pygame
.image
.load (os
.path
.join ("background", "inventory.png")).convert ()
29 self
.img_chestbg
= pygame
.image
.load (os
.path
.join ("background", "chestcontent.png")).convert ()
31 self
.img_dialogue
= pygame
.image
.load (os
.path
.join ("background", "dialog_screen.png")).convert ()
32 self
.img_dialogue
.set_colorkey (pygame
.Color (0, 255, 0))
35 # initialize object graphics
36 self
.img_redpotion
= pygame
.image
.load (os
.path
.join ("objects", "red-potion.png")).convert ()
37 self
.img_redpotion
.set_colorkey (pygame
.Color (0, 255, 0))
38 self
.img_goldcoins
= pygame
.image
.load (os
.path
.join ("objects", "gold-coins.png")).convert ()
39 self
.img_goldcoins
.set_colorkey (pygame
.Color (0, 255, 0))
40 self
.img_wand
= pygame
.image
.load (os
.path
.join ("objects", "wand.png")).convert ()
41 self
.img_wand
.set_colorkey (pygame
.Color (0, 255, 0))
42 self
.img_bulb
= pygame
.image
.load (os
.path
.join ("objects", "bulb.png")).convert ()
43 self
.img_bulb
.set_colorkey (pygame
.Color (0, 255, 0))
44 self
.img_lightning
= pygame
.image
.load (os
.path
.join ("objects", "lightning.png")).convert ()
45 self
.img_lightning
.set_colorkey (pygame
.Color (0, 255, 0))
46 self
.img_key
= pygame
.image
.load (os
.path
.join ("objects", "key.png")).convert ()
47 self
.img_key
.set_colorkey (pygame
.Color (0, 255, 0))
48 self
.img_key2
= pygame
.image
.load (os
.path
.join ("objects", "key2.png")).convert ()
49 self
.img_key2
.set_colorkey (pygame
.Color (0, 255, 0))
50 self
.img_chest
= pygame
.image
.load (os
.path
.join ("objects", "chest.png")).convert ()
51 self
.img_chest
.set_colorkey (pygame
.Color (0, 255, 0))
53 # initialize player graphics
54 self
.img_butabafront
= pygame
.image
.load (os
.path
.join ("sprite", "butaba-front.png")).convert ()
55 self
.img_butabafront
.set_colorkey (pygame
.Color (0, 255, 0))
56 self
.img_butababack
= pygame
.image
.load (os
.path
.join ("sprite", "butaba-back.png")).convert ()
57 self
.img_butababack
.set_colorkey (pygame
.Color (0, 255, 0))
58 self
.img_butabaleft
= pygame
.image
.load (os
.path
.join ("sprite", "butaba-left.png")).convert ()
59 self
.img_butabaleft
.set_colorkey (pygame
.Color (0, 255, 0))
60 self
.img_butabaright
= pygame
.image
.load (os
.path
.join ("sprite", "butaba-right.png")).convert ()
61 self
.img_butabaright
.set_colorkey (pygame
.Color (0, 255, 0))
63 # initialize NPC graphics
64 self
.img_bulisa
= pygame
.image
.load (os
.path
.join ("sprite", "bulisa.png")).convert ()
65 self
.img_bulisa
.set_colorkey (pygame
.Color (0, 255, 0))
67 # initialize portraits
68 self
.img_butaba_portrait
= pygame
.image
.load (os
.path
.join ("portraits", "butaba.png")).convert ()
69 self
.img_bulisa_portrait
= pygame
.image
.load (os
.path
.join ("portraits", "bulisa.png")).convert ()
73 # set current level and position of our character
74 self
.currentlevel
= self
.level1
76 # set the status message
77 self
.status_message
= "Game started"
79 self
.butaba
= butaba
.Butaba (5,0, butaba
.Butaba
.RIGHT
)
81 # set up the levels and their interactions
82 def setup_levels (self
):
83 # set up the objects first
84 chest1
= gameobjects
.Chest (2, 6, "chest", self
.img_chest
, constants
.KEY_CHEST1
, True)
85 chest2
= gameobjects
.Chest (6, 6, "chest", self
.img_chest
, constants
.KEY_CHEST2
, True)
86 key1
= gameobjects
.Key (5, 3, "a chest key", self
.img_key2
, constants
.KEY_CHEST1
)
87 key2
= gameobjects
.Key (5, 3, "a chest key", self
.img_key
, constants
.KEY_CHEST2
)
88 potion
= gameobjects
.HealthPotion (5, 2, self
.img_redpotion
)
89 gold50
= gameobjects
.GoldCoins (6, 2, self
.img_goldcoins
, 50)
90 gold25
= gameobjects
.GoldCoins (6, 2, self
.img_goldcoins
, 25)
91 gold10
= gameobjects
.GoldCoins (6, 2, self
.img_goldcoins
, 10)
92 potion2
= gameobjects
.HealthPotion (5, 2, self
.img_redpotion
)
93 potion3
= gameobjects
.HealthPotion (5, 2, self
.img_redpotion
)
95 npc_bulisa
= npcs
.Bulisa (4, 3, self
.img_bulisa
, self
.img_bulisa_portrait
,
96 [ os
.path
.join ("dialogues", "bulisa1.dlg") ])
98 chest1
.objects
= [ gold50
, gold25
, potion2
, potion3
, key2
, gold10
]
101 self
.level1
= level
.Level (cPickle
.load (file ("levels/level1.dat")),
102 objects
= [ chest1
] )
104 self
.level1w
= level
.Level (cPickle
.load (file ("levels/level1w.dat")))
106 self
.level1e
= level
.Level (cPickle
.load (file ("levels/level1e.dat")),
107 objects
= [ key1
, potion
, chest2
], npcs
= [ npc_bulisa
])
109 # set up the interaction between levels
110 self
.level1
.levelright
= self
.level1e
111 self
.level1
.levelleft
= self
.level1w
112 self
.level1e
.levelleft
= self
.level1
113 self
.level1w
.levelright
= self
.level1
115 def main_loop (self
):
119 self
.screen
.fill (pygame
.Color (0,0,0))
121 self
.draw_level_background (self
.currentlevel
)
123 self
.draw_level_objects (self
.currentlevel
)
124 # draw the NPCs in the level
125 self
.draw_level_npcs (self
.currentlevel
)
128 # display the character's inventory
129 self
.draw_inventory ()
130 # draw the status info
133 pygame
.display
.update ()
135 # get keyboard events
136 for event
in pygame
.event
.get ():
137 if event
.type == pygame
.QUIT
:
140 if event
.type == pygame
.KEYDOWN
:
141 if event
.key
== pygame
.K_UP
:
142 self
.move_butaba_up ()
143 elif event
.key
== pygame
.K_DOWN
:
144 self
.move_butaba_down ()
145 elif event
.key
== pygame
.K_LEFT
:
146 self
.move_butaba_left ()
147 elif event
.key
== pygame
.K_RIGHT
:
148 self
.move_butaba_right ()
149 # drinking health potion in inventory
150 elif event
.key
== ord ("h") or event
.key
== ord ("H"):
151 self
.inventory_drink_health_potion ()
153 elif event
.key
== ord ("q") or event
.key
== ord ("Q"):
156 # drink a health potion if it is in the player's inventory
157 def inventory_drink_health_potion (self
):
158 # look for a health potion
159 for item
in self
.butaba
.objects
:
160 if isinstance (item
, gameobjects
.HealthPotion
) is True:
161 self
.use_object (self
.butaba
, item
)
164 def move_butaba_up (self
):
165 # clear any status messages
166 self
.status_message
= None
167 # first if butaba is not facing up, make him face up
168 if self
.butaba
.position
<> butaba
.Butaba
.BACK
:
169 self
.butaba
.position
= butaba
.Butaba
.BACK
172 # if butaba is trying to move off the top of the screen
173 if self
.butaba
.row
<= 0:
174 # if there is a level above set current level to that one
175 if self
.currentlevel
.leveltop
is not None:
176 lastrow
= len (self
.currentlevel
.leveltop
.background
) - 1
177 # interact with objects
178 if self
.level_interact (self
.currentlevel
.leveltop
, lastrow
, self
.butaba
.col
) is False:
181 # make sure there is no obstacle
182 if self
.check_background_obstacle (self
.currentlevel
.leveltop
, lastrow
, self
.butaba
.col
) is False:
183 self
.currentlevel
= self
.currentlevel
.leveltop
184 self
.butaba
.row
= lastrow
185 # normal upward movement
187 # if there is any object in that place interact with it
188 # if any object is a blocking object then avoid movement
189 if self
.level_interact (self
.currentlevel
, self
.butaba
.row
-1, self
.butaba
.col
) is False:
192 if self
.check_background_obstacle (self
.currentlevel
, self
.butaba
.row
-1, self
.butaba
.col
) is False:
195 def move_butaba_down (self
):
196 # clear any status messages
197 self
.status_message
= None
198 # first if butaba is not facing forward, make him face forward/down
199 if self
.butaba
.position
<> butaba
.Butaba
.FRONT
:
200 self
.butaba
.position
= butaba
.Butaba
.FRONT
203 # if butaba is trying to move off the bottom of the screen
204 if self
.butaba
.row
>= len (self
.currentlevel
.background
)-1:
205 # if there is a level below set current level to that one
206 if self
.currentlevel
.levelbottom
is not None:
207 # interact with objects if any
208 # if any object is a blocking object then avoid movement
209 if self
.level_interact (self
.currentlevel
.levelbottom
, 0, self
.butaba
.col
) is False:
211 # make sure there is no obstacle at that position
212 if self
.check_background_obstacle (self
.currentlevel
.levelbottom
, 0, self
.butaba
.col
) is False:
213 self
.currentlevel
= self
.currentlevel
.levelbottom
215 # normal downward movement
217 # interact with objects if any
218 # if any object is a blocking object then avoid movement
219 if self
.level_interact (self
.currentlevel
, self
.butaba
.row
+1, self
.butaba
.col
) is False:
221 if self
.check_background_obstacle (self
.currentlevel
, self
.butaba
.row
+1, self
.butaba
.col
) is False:
224 # check if a background tile is an obstacle
225 def check_background_obstacle (self
, level
, row
, col
):
226 if (level
.background
[row
][col
][2] == 1):
231 # get and interact with objects and characters if present in a particular row/col
232 def level_interact (self
, level
, row
, col
):
234 # get list of objects at current location
235 for obj
in level
.objects
:
236 if obj
.row
== row
and obj
.col
== col
:
239 notblock
= self
.interact_objects (level
, objs
)
241 # get npc at current location
243 for npc
in level
.npcs
:
244 if npc
.row
== row
and npc
.col
== col
:
248 # npcs always block the tile. So return false if there is an NPC
250 if current_npc
is not None:
251 self
.interact_npc (current_npc
)
256 # interaction with npcs
257 def interact_npc (self
, npc
):
258 # interact with NPC and get the response ID
259 resp_id
= utility
.dialogue_play (self
.screen
, self
.img_dialogue
, npc
, self
.img_butaba_portrait
, 0, 90)
263 self
.status_message
= "You cannot initiate a conversation with %s" % npc
.charname
265 # interaction with objects
266 def interact_objects (self
, container
, objs
):
267 # overall flag for blocking/non-blocking objects
270 # now perform interaction
272 # run the object interact function
273 if obj
.interact () is False:
275 # if object can be picked up ask
276 if obj
.can_pickup
is True:
277 ans
= utility
.ask_question (self
.screen
, "Found %s." % obj
.text
, ["Pick up", obj
.use_str
, "Ignore"], self
.img_menu
)
278 # if the answer is "pick up"
280 self
.pickup_object (container
, obj
)
282 # use the object according to its type
283 self
.use_object (container
, obj
)
284 # if it cannot be picked up, try to use it anyway
286 ans
= utility
.ask_question (self
.screen
, "Found %s." % obj
.text
, [obj
.use_str
, "Ignore"], self
.img_menu
)
288 self
.use_object (container
, obj
)
292 # transfer an object from one container to another
293 # container must have an objects list
294 def transfer_object (self
, source
, obj
, dest
):
295 # remove object from source
296 source
.objects
.remove (obj
)
297 # add object to destination
298 dest
.objects
.append (obj
)
300 # picking up an object
301 def pickup_object (self
, container
, obj
):
302 # only if object can be picked up, pick it up or use it
303 if obj
.can_pickup
is True:
304 # check if the inventory is full
305 if len (self
.butaba
.objects
) >= butaba
.Butaba
.MAXITEMS
:
306 self
.status_message
= "Cannot pick up item. Inventory full"
308 # add item to inventory
309 self
.transfer_object (container
, obj
, self
.butaba
)
311 self
.status_message
= "You picked up %s" % obj
.text
313 # this method uses the object first by calling the object use () method
314 # and then performing specific actions as necessary
315 def use_object (self
, container
, obj
):
316 # if the object is a health potion
317 if isinstance (obj
, gameobjects
.HealthPotion
) is True:
318 if self
.butaba
.health
< butaba
.Butaba
.MAXHEALTH
:
319 obj
.use (self
.butaba
)
320 container
.objects
.remove (obj
)
321 self
.status_message
= "You gained health"
323 self
.status_message
= "You already have maximum health!"
324 # if the object is a chest
325 elif isinstance (obj
, gameobjects
.Chest
) is True:
326 # if chest is locked, try to open it
327 if obj
.locked
is True:
328 # try opening the chest with every item 9the use () function
329 # of the chest determines if item is a key anyway
331 for invobj
in self
.butaba
.objects
:
332 fittedkey
= obj
.use (invobj
)
334 if fittedkey
is not None:
337 if fittedkey
is None:
338 self
.status_message
= "No key found to open %s" % obj
.text
339 # chest successfully unlocked
341 self
.status_message
= "You unlocked the %s" % obj
.text
342 # remove the key from Butaba
343 self
.butaba
.objects
.remove (fittedkey
)
344 # add an experience point for unlocking chest subject
345 # to a limit of KNOWLEDGEMAX_CHEST_UNLOCK
346 if self
.butaba
.experience
< constants
.KNOWLEDGEMAX_CHEST_UNLOCK
:
347 self
.butaba
.experience
+= 1
348 self
.status_message
+= " and gained experience!"
349 # display the contents of the chest
351 item
= utility
.get_container_object (self
.screen
, obj
, self
.img_chestbg
, 30)
353 self
.interact_objects (obj
, [ item
, ])
355 # if the object is gold coins
356 elif isinstance (obj
, gameobjects
.GoldCoins
) is True:
357 obj
.use (self
.butaba
)
358 self
.status_message
= "You picked up %d gold." % obj
.value
359 # remove the gold coins after adding it to Butaba's gold
360 container
.objects
.remove (obj
)
362 def move_butaba_left (self
):
363 # clear any status messages
364 self
.status_message
= None
366 # first if Butaba is not facing left, make him face left
367 if self
.butaba
.position
<> butaba
.Butaba
.LEFT
:
368 self
.butaba
.position
= butaba
.Butaba
.LEFT
371 # if butaba is trying to move off the left edge
372 if self
.butaba
.col
<= 0:
373 # if there is a level to the right set current level to that one
374 if self
.currentlevel
.levelleft
is not None:
375 # get the last column of the previous level
376 lastcol
= len (self
.currentlevel
.levelleft
.background
[0]) - 1
377 # interact with objects if any
378 # if any object is a blocking object then avoid movement
379 if self
.level_interact (self
.currentlevel
.levelleft
, self
.butaba
.row
, lastcol
) is False:
381 # make sure there is no obstacle at that position of movement
382 if self
.check_background_obstacle (self
.currentlevel
.levelleft
, self
.butaba
.row
, lastcol
) is False:
383 self
.currentlevel
= self
.currentlevel
.levelleft
384 self
.butaba
.col
= lastcol
385 # normal left movement
387 # interact with objects if any
388 # if any object is a blocking object then avoid movement
389 if self
.level_interact (self
.currentlevel
, self
.butaba
.row
, self
.butaba
.col
-1) is False:
391 if self
.check_background_obstacle (self
.currentlevel
, self
.butaba
.row
, self
.butaba
.col
-1) is False:
394 def move_butaba_right (self
):
395 # clear any status messages
396 self
.status_message
= None
398 # First if Butaba is not facing right make him face right
399 if self
.butaba
.position
<> butaba
.Butaba
.RIGHT
:
400 self
.butaba
.position
= butaba
.Butaba
.RIGHT
403 # if butaba is trying to move off the right edge
404 if self
.butaba
.col
>= len (self
.currentlevel
.background
[0])-1:
405 # if there is a level to the right swap current level with that one
406 if self
.currentlevel
.levelright
is not None:
407 # interact with objects if any
408 # if any object is a blocking object then avoid movement
409 if self
.level_interact (self
.currentlevel
.levelright
, self
.butaba
.row
, 0) is False:
412 # make sure there is no obstacle at that position of movement
413 # get the last column of the previous level
414 if self
.check_background_obstacle (self
.currentlevel
.levelright
, self
.butaba
.row
, 0) is False:
415 self
.currentlevel
= self
.currentlevel
.levelright
417 # normal right movement
419 # interact with objects if any
420 # if any object is a blocking object then avoid moving
421 if self
.level_interact (self
.currentlevel
, self
.butaba
.row
, self
.butaba
.col
+ 1) is False:
423 if self
.check_background_obstacle (self
.currentlevel
, self
.butaba
.row
, self
.butaba
.col
+ 1) is False:
426 def draw_butaba (self
):
427 if self
.butaba
.position
== butaba
.Butaba
.FRONT
:
428 self
.screen
.blit (self
.img_butabafront
, (self
.butaba
.col
*48, self
.butaba
.row
*48))
429 elif self
.butaba
.position
== butaba
.Butaba
.BACK
:
430 self
.screen
.blit (self
.img_butababack
, (self
.butaba
.col
*48, self
.butaba
.row
*48))
431 elif self
.butaba
.position
== butaba
.Butaba
.LEFT
:
432 self
.screen
.blit (self
.img_butabaleft
, (self
.butaba
.col
*48, self
.butaba
.row
*48))
433 elif self
.butaba
.position
== butaba
.Butaba
.RIGHT
:
434 self
.screen
.blit (self
.img_butabaright
, (self
.butaba
.col
*48, self
.butaba
.row
*48))
437 # Draw the status infodisplay
438 def draw_status (self
):
439 self
.screen
.blit (self
.img_redpotion
, (485, 10))
440 utility
.put_text (self
.screen
, 550, 25, 20, (255, 0, 0), "%d" % self
.butaba
.health
)
442 self
.screen
.blit (self
.img_lightning
, (620, 10))
443 utility
.put_text (self
.screen
, 660, 25, 20, (255,255,255), "%d" % self
.butaba
.strength
)
445 self
.screen
.blit (self
.img_wand
, (485, 65))
446 utility
.put_text (self
.screen
, 550, 75, 20, (0, 0, 255), "%d" % self
.butaba
.magic
)
448 self
.screen
.blit (self
.img_bulb
, (620, 65))
449 utility
.put_text (self
.screen
, 660, 75, 20, (0, 255, 0), "%d" % self
.butaba
.experience
)
451 self
.screen
.blit (self
.img_goldcoins
, (485, 110))
452 utility
.put_text (self
.screen
, 550, 130, 20, (255, 255, 0), "%d" % self
.butaba
.gold
)
454 if self
.status_message
is not None:
455 utility
.put_text (self
.screen
, 10, 485, 10, (255,255, 0), "%s" % self
.status_message
)
457 # display the inventory of the player
458 def draw_inventory (self
):
459 # draw the inventory slots
462 utility
.put_text (self
.screen
, 490, 170, 16, (255,255 , 0), "Inventory")
463 for i
in range (butaba
.Butaba
.MAXITEMS
):
464 self
.screen
.blit (self
.img_inventory
, (440+c
*54, 150+r
*54))
473 for obj
in self
.butaba
.objects
:
474 self
.screen
.blit (obj
.image
, (440+c
*54+2, 150+r
*54+2))
481 # Draw the level background tiles on surface
482 def draw_level_background (self
, level
):
484 for row
in level
.background
:
486 for tilerow
, tilecol
, is_solid
in row
:
489 self
.screen
.blit (self
.img_tileset
, (j
*48, i
*48), pygame
.Rect (tilex
, tiley
, 48, 48))
494 # Draw the level objects
495 def draw_level_objects (self
, level
):
496 for obj
in level
.objects
:
497 if obj
.image
is not None:
498 self
.screen
.blit (obj
.image
, (obj
.col
*48, obj
.row
*48))
501 # Draw the NPCs in the level
502 def draw_level_npcs (self
, level
):
503 for npc
in level
.npcs
:
504 if npc
.image
is not None:
505 self
.screen
.blit (npc
.image
, (npc
.col
*48, npc
.row
*48))