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.previous
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 'You are the greatest warrior this world has ever known.']
281 thank_you_dialogue = ['Thank you for retrieving my crown.',
282 'My kingdom is forever in your debt.']
283
284 if self.game_data['crown quest'] and not self.game_data['delivered crown']:
285 sprite.dialogue = retrieved_crown_dialogue
286 self.game_data['delivered crown'] = True
287 self.reset_dialogue = (sprite, thank_you_dialogue)
288 elif self.game_data['delivered crown']:
289 sprite.dialogue = thank_you_dialogue
290
291
292 def check_for_opened_chest(self, sprite):
293 if sprite.name == 'treasurechest':
294 if not self.game_data['treasure{}'.format(sprite.id)]:
295 sprite.dialogue = ['Empty.']
296 sprite.item = None
297 sprite.index = 1
298
299 def make_state_dict(self):
300 """
301 Make a dictionary of states the level can be in.
302 """
303 state_dict = {'normal': self.running_normally,
304 'dialogue': self.handling_dialogue,
305 'menu': self.goto_menu,
306 'transition_in': self.transition_in,
307 'transition_out': self.transition_out}
308
309 return state_dict
310
311 def make_level_portals(self):
312 """
313 Make the portals to switch state.
314 """
315 portal_group = pg.sprite.Group()
316
317 for object in self.renderer.tmx_data.getObjects():
318 properties = object.__dict__
319 if properties['name'] == 'portal':
320 posx = properties['x'] * 2
321 posy = (properties['y'] * 2) - 32
322 new_state = properties['type']
323 portal_group.add(portal.Portal(posx, posy, new_state))
324
325
326 return portal_group
327
328 def running_normally(self, surface, keys, current_time):
329 """
330 Update level normally.
331 """
332 self.check_for_dialogue()
333 self.player.update(keys, current_time)
334 self.sprites.update(current_time)
335 self.collision_handler.update(keys, current_time)
336 self.check_for_portals()
337 self.check_for_battle()
338 self.dialogue_handler.update(keys, current_time)
339 self.check_for_menu(keys)
340 self.viewport_update()
341 self.draw_level(surface)
342
343 def check_for_portals(self):
344 """
345 Check if the player walks into a door, requiring a level change.
346 """
347 if self.use_portal and not self.done:
348 self.player.location = self.player.get_tile_location()
349 self.update_game_data()
350 self.state = 'transition_out'
351
352 def check_for_battle(self):
353 """
354 Check if the flag has been made true, indicating
355 to switch state to a battle.
356 """
357 if self.switch_to_battle and self.allow_battles and not self.done:
358 self.player.location = self.player.get_tile_location()
359 self.update_game_data()
360 self.next = 'battle'
361 self.state = 'transition_out'
362
363 def check_for_menu(self, keys):
364 """
365 Check if player hits enter to go to menu.
366 """
367 if keys[pg.K_RETURN] and self.allow_input:
368 if self.player.state == 'resting':
369 self.state = 'menu'
370 self.allow_input = False
371
372 if not keys[pg.K_RETURN]:
373 self.allow_input = True
374
375
376 def update_game_data(self):
377 """
378 Update the persistant game data dictionary.
379 """
380 self.game_data['last location'] = self.player.location
381 self.game_data['last direction'] = self.player.direction
382 self.game_data['last state'] = self.name
383 self.set_new_start_pos()
384
385
386 def set_new_start_pos(self):
387 """
388 Set new start position based on previous state.
389 """
390 location = copy.deepcopy(self.game_data['last location'])
391 direction = self.game_data['last direction']
392
393 if self.next == 'player menu':
394 pass
395 elif direction == 'up':
396 location[1] += 1
397 elif direction == 'down':
398 location[1] -= 1
399 elif direction == 'left':
400 location[0] += 1
401 elif direction == 'right':
402 location[0] -= 1
403
404 def handling_dialogue(self, surface, keys, current_time):
405 """
406 Update only dialogue boxes.
407 """
408 self.dialogue_handler.update(keys, current_time)
409 self.draw_level(surface)
410
411 def goto_menu(self, surface, keys, *args):
412 """
413 Go to menu screen.
414 """
415 self.menu_screen.update(surface, keys)
416 self.menu_screen.draw(surface)
417
418 def check_for_dialogue(self):
419 """
420 Check if the level needs to freeze.
421 """
422 if self.dialogue_handler.textbox:
423 self.state = 'dialogue'
424
425 def transition_out(self, surface, *args):
426 """
427 Transition level to new scene.
428 """
429 transition_image = pg.Surface(self.transition_rect.size)
430 transition_image.fill(c.TRANSITION_COLOR)
431 transition_image.set_alpha(self.transition_alpha)
432 self.draw_level(surface)
433 surface.blit(transition_image, self.transition_rect)
434 self.transition_alpha += c.TRANSITION_SPEED
435 if self.transition_alpha >= 255:
436 self.transition_alpha = 255
437 self.done = True
438
439 def transition_in(self, surface, *args):
440 """
441 Transition into level.
442 """
443 self.viewport_update()
444 transition_image = pg.Surface(self.transition_rect.size)
445 transition_image.fill(c.TRANSITION_COLOR)
446 transition_image.set_alpha(self.transition_alpha)
447 self.draw_level(surface)
448 surface.blit(transition_image, self.transition_rect)
449 self.transition_alpha -= c.TRANSITION_SPEED
450 if self.transition_alpha <= 0:
451 self.state = 'normal'
452 self.transition_alpha = 0
453
454 def update(self, surface, keys, current_time):
455 """
456 Update state.
457 """
458 state_function = self.state_dict[self.state]
459 state_function(surface, keys, current_time)
460
461 def viewport_update(self):
462 """
463 Update viewport so it stays centered on character,
464 unless at edge of map.
465 """
466 self.viewport.center = self.player.rect.center
467 self.viewport.clamp_ip(self.level_rect)
468
469 def draw_level(self, surface):
470 """
471 Blit all images to screen.
472 """
473 self.level_surface.blit(self.map_image, self.viewport, self.viewport)
474 self.level_surface.blit(self.player.image, self.player.rect)
475 self.sprites.draw(self.level_surface)
476
477 surface.blit(self.level_surface, (0, 0), self.viewport)
478 self.dialogue_handler.draw(surface)
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494