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
33 def set_music(self):
34 """
35 Set music based on name.
36 """
37 music_dict = {c.TOWN: ('town_theme', .4),
38 c.OVERWORLD: ('overworld', .4),
39 c.CASTLE: ('kings_theme', .4),
40 c.DUNGEON: ('dungeon_theme', .4),
41 c.DUNGEON2: ('dungeon_theme', .4),
42 c.DUNGEON3: ('dungeon_theme', .4),
43 c.DUNGEON4: ('dungeon_theme', .4),
44 c.DUNGEON5: ('dungeon_theme', .4),
45 c.HOUSE: ('pleasant_creek', .1),
46 c.BROTHER_HOUSE: ('pleasant_creek', .1)}
47
48
49 if self.name in music_dict:
50 music = music_dict[self.name][0]
51 volume = music_dict[self.name][1]
52 self.music_title = music
53 return setup.MUSIC[music], volume
54 else:
55 return None, None
56
57 def startup(self, current_time, game_data):
58 """
59 Call when the State object is flipped to.
60 """
61 self.game_data = game_data
62 self.current_time = current_time
63 self.state = 'transition_in'
64 self.reset_dialogue = ()
65 self.switch_to_battle = False
66 self.use_portal = False
67 self.allow_input = False
68 self.cut_off_bottom_map = ['castle', 'town', 'dungeon']
69 self.renderer = tilerender.Renderer(self.tmx_map)
70 self.map_image = self.renderer.make_2x_map()
71
72 self.viewport = self.make_viewport(self.map_image)
73 self.level_surface = self.make_level_surface(self.map_image)
74 self.level_rect = self.level_surface.get_rect()
75 self.portals = self.make_level_portals()
76 self.player = self.make_player()
77 self.blockers = self.make_blockers()
78 self.sprites = self.make_sprites()
79
80 self.collision_handler = collision.CollisionHandler(self.player,
81 self.blockers,
82 self.sprites,
83 self.portals,
84 self)
85 self.dialogue_handler = textbox.TextHandler(self)
86 self.state_dict = self.make_state_dict()
87 self.menu_screen = player_menu.Player_Menu(game_data, self)
88 self.transition_rect = setup.SCREEN.get_rect()
89 self.transition_alpha = 255
90
91 def make_viewport(self, map_image):
92 """
93 Create the viewport to view the level through.
94 """
95 map_rect = map_image.get_rect()
96 return setup.SCREEN.get_rect(bottom=map_rect.bottom)
97
98 def make_level_surface(self, map_image):
99 """
100 Create the surface all images are blitted to.
101 """
102 map_rect = map_image.get_rect()
103 map_width = map_rect.width
104 if self.name in self.cut_off_bottom_map:
105 map_height = map_rect.height - 32
106 else:
107 map_height = map_rect.height
108 size = map_width, map_height
109
110 return pg.Surface(size).convert()
111
112 def make_player(self):
113 """
114 Make the player and sets location.
115 """
116 last_state = self.previous
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 'You are the greatest warrior this world has ever known.']
285 thank_you_dialogue = ['Thank you for retrieving my crown.',
286 'My kingdom is forever in your debt.']
287
288 if self.game_data['crown quest'] and not self.game_data['delivered crown']:
289 sprite.dialogue = retrieved_crown_dialogue
290 self.game_data['delivered crown'] = True
291 self.reset_dialogue = (sprite, thank_you_dialogue)
292 elif self.game_data['delivered crown']:
293 sprite.dialogue = thank_you_dialogue
294
295
296 def check_for_opened_chest(self, sprite):
297 if sprite.name == 'treasurechest':
298 if not self.game_data['treasure{}'.format(sprite.id)]:
299 sprite.dialogue = ['Empty.']
300 sprite.item = None
301 sprite.index = 1
302
303 def make_state_dict(self):
304 """
305 Make a dictionary of states the level can be in.
306 """
307 state_dict = {'normal': self.running_normally,
308 'dialogue': self.handling_dialogue,
309 'menu': self.goto_menu,
310 'transition_in': self.transition_in,
311 'transition_out': self.transition_out}
312
313 return state_dict
314
315 def make_level_portals(self):
316 """
317 Make the portals to switch state.
318 """
319 portal_group = pg.sprite.Group()
320
321 for object in self.renderer.tmx_data.getObjects():
322 properties = object.__dict__
323 if properties['name'] == 'portal':
324 posx = properties['x'] * 2
325 posy = (properties['y'] * 2) - 32
326 new_state = properties['type']
327 portal_group.add(portal.Portal(posx, posy, new_state))
328
329
330 return portal_group
331
332 def running_normally(self, surface, keys, current_time):
333 """
334 Update level normally.
335 """
336 self.check_for_dialogue()
337 self.player.update(keys, current_time)
338 self.sprites.update(current_time)
339 self.collision_handler.update(keys, current_time)
340 self.check_for_portals()
341 self.check_for_battle()
342 self.dialogue_handler.update(keys, current_time)
343 self.check_for_menu(keys)
344 self.viewport_update()
345 self.draw_level(surface)
346
347 def check_for_portals(self):
348 """
349 Check if the player walks into a door, requiring a level change.
350 """
351 if self.use_portal and not self.done:
352 self.player.location = self.player.get_tile_location()
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