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 '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 if self.use_portal and not self.done:
349 self.player.location = self.player.get_tile_location()
350 self.update_game_data()
351 self.state = 'transition_out'
352
353 def check_for_battle(self):
354 """
355 Check if the flag has been made true, indicating
356 to switch state to a battle.
357 """
358 if self.switch_to_battle and self.allow_battles and not self.done:
359 self.player.location = self.player.get_tile_location()
360 self.update_game_data()
361 self.next = 'battle'
362 self.state = 'transition_out'
363
364 def check_for_menu(self, keys):
365 """
366 Check if player hits enter to go to menu.
367 """
368 if keys[pg.K_RETURN] and self.allow_input:
369 if self.player.state == 'resting':
370 self.state = 'menu'
371 self.allow_input = False
372
373 if not keys[pg.K_RETURN]:
374 self.allow_input = True
375
376
377 def update_game_data(self):
378 """
379 Update the persistant game data dictionary.
380 """
381 self.game_data['last location'] = self.player.location
382 self.game_data['last direction'] = self.player.direction
383 self.game_data['last state'] = self.name
384 self.set_new_start_pos()
385
386
387 def set_new_start_pos(self):
388 """
389 Set new start position based on previous state.
390 """
391 location = copy.deepcopy(self.game_data['last location'])
392 direction = self.game_data['last direction']
393
394 if self.next == 'player menu':
395 pass
396 elif direction == 'up':
397 location[1] += 1
398 elif direction == 'down':
399 location[1] -= 1
400 elif direction == 'left':
401 location[0] += 1
402 elif direction == 'right':
403 location[0] -= 1
404
405 def handling_dialogue(self, surface, keys, current_time):
406 """
407 Update only dialogue boxes.
408 """
409 self.dialogue_handler.update(keys, current_time)
410 self.draw_level(surface)
411
412 def goto_menu(self, surface, keys, *args):
413 """
414 Go to menu screen.
415 """
416 self.menu_screen.update(surface, keys)
417 self.menu_screen.draw(surface)
418
419 def check_for_dialogue(self):
420 """
421 Check if the level needs to freeze.
422 """
423 if self.dialogue_handler.textbox:
424 self.state = 'dialogue'
425
426 def transition_out(self, surface, *args):
427 """
428 Transition level to new scene.
429 """
430 transition_image = pg.Surface(self.transition_rect.size)
431 transition_image.fill(c.TRANSITION_COLOR)
432 transition_image.set_alpha(self.transition_alpha)
433 self.draw_level(surface)
434 surface.blit(transition_image, self.transition_rect)
435 self.transition_alpha += c.TRANSITION_SPEED
436 if self.transition_alpha >= 255:
437 self.transition_alpha = 255
438 self.done = True
439
440 def transition_in(self, surface, *args):
441 """
442 Transition into level.
443 """
444 self.viewport_update()
445 transition_image = pg.Surface(self.transition_rect.size)
446 transition_image.fill(c.TRANSITION_COLOR)
447 transition_image.set_alpha(self.transition_alpha)
448 self.draw_level(surface)
449 surface.blit(transition_image, self.transition_rect)
450 self.transition_alpha -= c.TRANSITION_SPEED
451 if self.transition_alpha <= 0:
452 self.state = 'normal'
453 self.transition_alpha = 0
454
455 def update(self, surface, keys, current_time):
456 """
457 Update state.
458 """
459 state_function = self.state_dict[self.state]
460 state_function(surface, keys, current_time)
461
462 def viewport_update(self):
463 """
464 Update viewport so it stays centered on character,
465 unless at edge of map.
466 """
467 self.viewport.center = self.player.rect.center
468 self.viewport.clamp_ip(self.level_rect)
469
470 def draw_level(self, surface):
471 """
472 Blit all images to screen.
473 """
474 self.level_surface.blit(self.map_image, self.viewport, self.viewport)
475 self.level_surface.blit(self.player.image, self.player.rect)
476 self.sprites.draw(self.level_surface)
477
478 surface.blit(self.level_surface, (0, 0), self.viewport)
479 self.dialogue_handler.draw(surface)
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495