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