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