data/states/levels.py (view raw)
1"""
2This is the base class for all level states (i.e. states
3where the player can move around the screen). Levels are
4differentiated by self.name and self.tmx_map.
5This class inherits from the generic state class
6found in the tools.py module.
7"""
8import copy, sys
9import pickle
10import pygame as pg
11from .. import tools, collision
12from .. import constants as c
13from .. components import person, textbox, portal
14from . import player_menu
15from .. import tilerender
16from .. import setup
17
18
19#Python 2/3 compatibility.
20if sys.version_info[0] == 2:
21 import cPickle
22 pickle = cPickle
23 range = xrange
24
25
26class LevelState(tools._State):
27 def __init__(self, name, battles=False):
28 super(LevelState, self).__init__()
29 self.name = name
30 self.tmx_map = setup.TMX[name]
31 self.allow_battles = battles
32 self.music, self.volume = self.set_music()
33
34 def set_music(self):
35 """
36 Set music based on name.
37 """
38 music_dict = {c.TOWN: ('town_theme', .4),
39 c.OVERWORLD: ('overworld', .4),
40 c.CASTLE: ('kings_theme', .4),
41 c.DUNGEON: ('dungeon_theme', .4),
42 c.DUNGEON2: ('dungeon_theme', .4),
43 c.DUNGEON3: ('dungeon_theme', .4),
44 c.DUNGEON4: ('dungeon_theme', .4),
45 c.DUNGEON5: ('dungeon_theme', .4),
46 c.HOUSE: ('pleasant_creek', .1),
47 c.BROTHER_HOUSE: ('pleasant_creek', .1)}
48
49 if self.name in music_dict:
50 music = music_dict[self.name][0]
51 volume = music_dict[self.name][1]
52 return setup.MUSIC[music], volume
53 else:
54 return None, None
55
56 def startup(self, current_time, game_data):
57 """
58 Call when the State object is flipped to.
59 """
60 self.game_data = game_data
61 self.current_time = current_time
62 self.state = 'transition_in'
63 self.reset_dialogue = ()
64 self.switch_to_battle = False
65 self.use_portal = False
66 self.allow_input = False
67 self.cut_off_bottom_map = ['castle', 'town', 'dungeon']
68 self.renderer = tilerender.Renderer(self.tmx_map)
69 self.map_image = self.renderer.make_2x_map()
70
71 self.viewport = self.make_viewport(self.map_image)
72 self.level_surface = self.make_level_surface(self.map_image)
73 self.level_rect = self.level_surface.get_rect()
74 self.portals = self.make_level_portals()
75 self.player = self.make_player()
76 self.blockers = self.make_blockers()
77 self.sprites = self.make_sprites()
78
79 self.collision_handler = collision.CollisionHandler(self.player,
80 self.blockers,
81 self.sprites,
82 self.portals,
83 self)
84 self.dialogue_handler = textbox.TextHandler(self)
85 self.state_dict = self.make_state_dict()
86 self.menu_screen = player_menu.Player_Menu(game_data, self)
87 self.transition_rect = setup.SCREEN.get_rect()
88 self.transition_alpha = 255
89
90 def make_viewport(self, map_image):
91 """
92 Create the viewport to view the level through.
93 """
94 map_rect = map_image.get_rect()
95 return setup.SCREEN.get_rect(bottom=map_rect.bottom)
96
97 def make_level_surface(self, map_image):
98 """
99 Create the surface all images are blitted to.
100 """
101 map_rect = map_image.get_rect()
102 map_width = map_rect.width
103 if self.name in self.cut_off_bottom_map:
104 map_height = map_rect.height - 32
105 else:
106 map_height = map_rect.height
107 size = map_width, map_height
108
109 return pg.Surface(size).convert()
110
111 def make_player(self):
112 """
113 Make the player and sets location.
114 """
115 last_state = self.game_data['last state']
116
117
118 if last_state == 'battle':
119 player = person.Player(self.game_data['last direction'], self.game_data)
120 player.rect.x = self.game_data['last location'][0] * 32
121 player.rect.y = self.game_data['last location'][1] * 32
122
123 else:
124 for object in self.renderer.tmx_data.getObjects():
125 properties = object.__dict__
126 if properties['name'] == 'start point':
127 if last_state == properties['state']:
128 posx = properties['x'] * 2
129 posy = (properties['y'] * 2) - 32
130 player = person.Player(properties['direction'],
131 self.game_data)
132 player.rect.x = posx
133 player.rect.y = posy
134
135 return player
136
137 def make_blockers(self):
138 """
139 Make the blockers for the level.
140 """
141 blockers = []
142
143 for object in self.renderer.tmx_data.getObjects():
144 properties = object.__dict__
145 if properties['name'] == 'blocker':
146 left = properties['x'] * 2
147 top = ((properties['y']) * 2) - 32
148 blocker = pg.Rect(left, top, 32, 32)
149 blockers.append(blocker)
150
151 return blockers
152
153 def make_sprites(self):
154 """
155 Make any sprites for the level as needed.
156 """
157 sprites = pg.sprite.Group()
158
159 for object in self.renderer.tmx_data.getObjects():
160 properties = object.__dict__
161 if properties['name'] == 'sprite':
162 if 'direction' in properties:
163 direction = properties['direction']
164 else:
165 direction = 'down'
166
167 if properties['type'] == 'soldier' and direction == 'left':
168 index = 1
169 else:
170 index = 0
171
172 if 'item' in properties:
173 item = properties['item']
174 else:
175 item = None
176
177 if 'id' in properties:
178 id = properties['id']
179 else:
180 id = None
181
182 if 'battle' in properties:
183 battle = properties['battle']
184 else:
185 battle = None
186
187 if 'state' in properties:
188 sprite_state = properties['state']
189 else:
190 sprite_state = None
191
192
193 x = properties['x'] * 2
194 y = ((properties['y']) * 2) - 32
195
196 sprite_dict = {'oldman': person.Person('oldman',
197 x, y, direction),
198 'bluedressgirl': person.Person('femalevillager',
199 x, y, direction,
200 'resting', 1),
201 'femalewarrior': person.Person('femvillager2',
202 x, y, direction,
203 'autoresting'),
204 'devil': person.Person('devil', x, y,
205 'down', 'autoresting'),
206 'oldmanbrother': person.Person('oldmanbrother',
207 x, y, direction),
208 'soldier': person.Person('soldier',
209 x, y, direction,
210 'resting', index),
211 'king': person.Person('king', x, y, direction),
212 'evilwizard': person.Person('evilwizard', x, y, direction),
213 'treasurechest': person.Chest(x, y, id)}
214
215 sprite = sprite_dict[properties['type']]
216 if sprite_state:
217 sprite.state = sprite_state
218
219 if sprite.name == 'oldman':
220 if self.game_data['old man gift'] and not self.game_data['elixir received']:
221 sprite.item = self.game_data['old man gift']
222 else:
223 sprite.item = item
224 elif sprite.name == 'king':
225 if not self.game_data['talked to king']:
226 sprite.item = self.game_data['king item']
227 else:
228 sprite.item = item
229 sprite.battle = battle
230 self.assign_dialogue(sprite, properties)
231 self.check_for_opened_chest(sprite)
232 if sprite.name == 'evilwizard' and self.game_data['crown quest']:
233 pass
234 else:
235 sprites.add(sprite)
236
237 return sprites
238
239 def assign_dialogue(self, sprite, property_dict):
240 """
241 Assign dialogue from object property dictionaries in tmx maps to sprites.
242 """
243 dialogue_list = []
244 for i in range(int(property_dict['dialogue length'])):
245 dialogue_list.append(property_dict['dialogue'+str(i)])
246 sprite.dialogue = dialogue_list
247
248 if sprite.name == 'oldman':
249 quest_in_process_dialogue = ['Hurry to the NorthEast Shores!',
250 'I do not have much time left.']
251
252 if self.game_data['has brother elixir']:
253 if self.game_data['elixir received']:
254 sprite.dialogue = ['My good health is thanks to you.',
255 'I will be forever in your debt.']
256 else:
257 sprite.dialogue = ['Thank you for reaching my brother.',
258 'This ELIXIR will cure my ailment.',
259 'As a reward, I will teach you a magic spell.',
260 'Use it wisely.',
261 'You learned FIRE BLAST.']
262
263 elif self.game_data['talked to sick brother']:
264 sprite.dialogue = quest_in_process_dialogue
265
266 elif not self.game_data['talked to sick brother']:
267 self.reset_dialogue = (sprite, quest_in_process_dialogue)
268 elif sprite.name == 'oldmanbrother':
269 if self.game_data['has brother elixir']:
270 if self.game_data['elixir received']:
271 sprite.dialogue = ['I am glad my brother is doing well.',
272 'You have a wise and generous spirit.']
273 else:
274 sprite.dialogue = ['Hurry! There is precious little time.']
275 elif self.game_data['talked to sick brother']:
276 sprite.dialogue = ['My brother is sick?!?',
277 'I have not seen him in years. I had no idea he was not well.',
278 'Quick, take this ELIXIR to him immediately.']
279 elif sprite.name == 'king':
280 retrieved_crown_dialogue = ['My crown! You recovered my stolen crown!!!',
281 'I can not believe what I see before my eyes.',
282 'You are truly a brave and noble warrior.',
283 'Henceforth, I name thee Grand Protector of this Town!',
284 'Go forth and be recognized.',
285 'You are the greatest warrior this world has ever known.']
286 thank_you_dialogue = ['Thank you for retrieving my crown.',
287 'My kingdom is forever in your debt.']
288
289 if self.game_data['crown quest'] and not self.game_data['delivered crown']:
290 sprite.dialogue = retrieved_crown_dialogue
291 self.game_data['delivered crown'] = True
292 self.reset_dialogue = (sprite, thank_you_dialogue)
293 elif self.game_data['delivered crown']:
294 sprite.dialogue = thank_you_dialogue
295
296
297 def check_for_opened_chest(self, sprite):
298 if sprite.name == 'treasurechest':
299 if not self.game_data['treasure{}'.format(sprite.id)]:
300 sprite.dialogue = ['Empty.']
301 sprite.item = None
302 sprite.index = 1
303
304 def make_state_dict(self):
305 """
306 Make a dictionary of states the level can be in.
307 """
308 state_dict = {'normal': self.running_normally,
309 'dialogue': self.handling_dialogue,
310 'menu': self.goto_menu,
311 'transition_in': self.transition_in,
312 'transition_out': self.transition_out}
313
314 return state_dict
315
316 def make_level_portals(self):
317 """
318 Make the portals to switch state.
319 """
320 portal_group = pg.sprite.Group()
321
322 for object in self.renderer.tmx_data.getObjects():
323 properties = object.__dict__
324 if properties['name'] == 'portal':
325 posx = properties['x'] * 2
326 posy = (properties['y'] * 2) - 32
327 new_state = properties['type']
328 portal_group.add(portal.Portal(posx, posy, new_state))
329
330
331 return portal_group
332
333 def running_normally(self, surface, keys, current_time):
334 """
335 Update level normally.
336 """
337 self.check_for_dialogue()
338 self.player.update(keys, current_time)
339 self.sprites.update(current_time)
340 self.collision_handler.update(keys, current_time)
341 self.check_for_portals()
342 self.check_for_battle()
343 self.dialogue_handler.update(keys, current_time)
344 self.check_for_menu(keys)
345 self.viewport_update()
346 self.draw_level(surface)
347
348 def check_for_portals(self):
349 """
350 Check if the player walks into a door, requiring a level change.
351 """
352 if self.use_portal and not self.done:
353 self.player.location = self.player.get_tile_location()
354 self.update_game_data()
355 self.state = 'transition_out'
356
357 def check_for_battle(self):
358 """
359 Check if the flag has been made true, indicating
360 to switch state to a battle.
361 """
362 if self.switch_to_battle and self.allow_battles and not self.done:
363 self.player.location = self.player.get_tile_location()
364 self.update_game_data()
365 self.next = 'battle'
366 self.state = 'transition_out'
367
368 def check_for_menu(self, keys):
369 """
370 Check if player hits enter to go to menu.
371 """
372 if keys[pg.K_RETURN] and self.allow_input:
373 if self.player.state == 'resting':
374 self.state = 'menu'
375 self.allow_input = False
376
377 if not keys[pg.K_RETURN]:
378 self.allow_input = True
379
380
381 def update_game_data(self):
382 """
383 Update the persistant game data dictionary.
384 """
385 self.game_data['last location'] = self.player.location
386 self.game_data['last direction'] = self.player.direction
387 self.game_data['last state'] = self.name
388 self.set_new_start_pos()
389
390
391 def set_new_start_pos(self):
392 """
393 Set new start position based on previous state.
394 """
395 location = copy.deepcopy(self.game_data['last location'])
396 direction = self.game_data['last direction']
397
398 if self.next == 'player menu':
399 pass
400 elif direction == 'up':
401 location[1] += 1
402 elif direction == 'down':
403 location[1] -= 1
404 elif direction == 'left':
405 location[0] += 1
406 elif direction == 'right':
407 location[0] -= 1
408
409 def handling_dialogue(self, surface, keys, current_time):
410 """
411 Update only dialogue boxes.
412 """
413 self.dialogue_handler.update(keys, current_time)
414 self.draw_level(surface)
415
416 def goto_menu(self, surface, keys, *args):
417 """
418 Go to menu screen.
419 """
420 self.menu_screen.update(surface, keys)
421 self.menu_screen.draw(surface)
422
423 def check_for_dialogue(self):
424 """
425 Check if the level needs to freeze.
426 """
427 if self.dialogue_handler.textbox:
428 self.state = 'dialogue'
429
430 def transition_out(self, surface, *args):
431 """
432 Transition level to new scene.
433 """
434 transition_image = pg.Surface(self.transition_rect.size)
435 transition_image.fill(c.TRANSITION_COLOR)
436 transition_image.set_alpha(self.transition_alpha)
437 self.draw_level(surface)
438 surface.blit(transition_image, self.transition_rect)
439 self.transition_alpha += c.TRANSITION_SPEED
440 if self.transition_alpha >= 255:
441 self.transition_alpha = 255
442 self.done = True
443
444 def transition_in(self, surface, *args):
445 """
446 Transition into level.
447 """
448 self.viewport_update()
449 transition_image = pg.Surface(self.transition_rect.size)
450 transition_image.fill(c.TRANSITION_COLOR)
451 transition_image.set_alpha(self.transition_alpha)
452 self.draw_level(surface)
453 surface.blit(transition_image, self.transition_rect)
454 self.transition_alpha -= c.TRANSITION_SPEED
455 if self.transition_alpha <= 0:
456 self.state = 'normal'
457 self.transition_alpha = 0
458
459 def update(self, surface, keys, current_time):
460 """
461 Update state.
462 """
463 state_function = self.state_dict[self.state]
464 state_function(surface, keys, current_time)
465
466 def viewport_update(self):
467 """
468 Update viewport so it stays centered on character,
469 unless at edge of map.
470 """
471 self.viewport.center = self.player.rect.center
472 self.viewport.clamp_ip(self.level_rect)
473
474 def draw_level(self, surface):
475 """
476 Blit all images to screen.
477 """
478 self.level_surface.blit(self.map_image, self.viewport, self.viewport)
479 self.level_surface.blit(self.player.image, self.player.rect)
480 self.sprites.draw(self.level_surface)
481
482 surface.blit(self.level_surface, (0, 0), self.viewport)
483 self.dialogue_handler.draw(surface)
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499