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