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"""
8
9import copy
10import pygame as pg
11from .. import tools, collision
12from .. components import person, textbox, portal
13from . import player_menu
14from .. import tilerender
15from .. import setup
16
17
18
19class LevelState(tools._State):
20 def __init__(self, name, battles=False):
21 super(LevelState, self).__init__()
22 self.name = name
23 self.tmx_map = setup.TMX[name]
24 self.allow_battles = battles
25
26 def startup(self, current_time, game_data):
27 """
28 Call when the State object is flipped to.
29 """
30 self.game_data = game_data
31 self.current_time = current_time
32 self.state = 'normal'
33 self.switch_to_battle = False
34 self.allow_input = False
35 self.cut_off_bottom_map = ['castle', 'town', 'dungeon']
36 self.renderer = tilerender.Renderer(self.tmx_map)
37 self.map_image = self.renderer.make_2x_map()
38
39 self.viewport = self.make_viewport(self.map_image)
40 self.level_surface = self.make_level_surface(self.map_image)
41 self.level_rect = self.level_surface.get_rect()
42 self.portals = None
43 self.player = self.make_player()
44 self.blockers = self.make_blockers()
45 self.sprites = self.make_sprites()
46
47 self.collision_handler = collision.CollisionHandler(self.player,
48 self.blockers,
49 self.sprites,
50 self)
51 self.dialogue_handler = textbox.TextHandler(self)
52 self.state_dict = self.make_state_dict()
53 self.portals = self.make_level_portals()
54 self.menu_screen = player_menu.Player_Menu(game_data, self)
55
56 def make_viewport(self, map_image):
57 """
58 Create the viewport to view the level through.
59 """
60 map_rect = map_image.get_rect()
61 return setup.SCREEN.get_rect(bottom=map_rect.bottom)
62
63 def make_level_surface(self, map_image):
64 """
65 Create the surface all images are blitted to.
66 """
67 map_rect = map_image.get_rect()
68 map_width = map_rect.width
69 if self.name in self.cut_off_bottom_map:
70 map_height = map_rect.height - 32
71 else:
72 map_height = map_rect.height
73 size = map_width, map_height
74
75 return pg.Surface(size).convert()
76
77 def make_player(self):
78 """
79 Make the player and sets location.
80 """
81 last_state = self.game_data['last state']
82
83
84 if last_state == 'battle':
85 player = person.Player(self.game_data['last direction'])
86 player.rect.x = self.game_data['last location'][0] * 32
87 player.rect.y = self.game_data['last location'][1] * 32
88
89 else:
90 for object in self.renderer.tmx_data.getObjects():
91 properties = object.__dict__
92 if properties['name'] == 'start point':
93 if last_state == properties['state']:
94 posx = properties['x'] * 2
95 posy = (properties['y'] * 2) - 32
96 player = person.Player(properties['direction'])
97 player.rect.x = posx
98 player.rect.y = posy
99
100 return player
101
102 def make_blockers(self):
103 """
104 Make the blockers for the level.
105 """
106 blockers = []
107
108 for object in self.renderer.tmx_data.getObjects():
109 properties = object.__dict__
110 if properties['name'] == 'blocker':
111 left = properties['x'] * 2
112 top = ((properties['y']) * 2) - 32
113 blocker = pg.Rect(left, top, 32, 32)
114 blockers.append(blocker)
115
116 return blockers
117
118 def make_sprites(self):
119 """
120 Make any sprites for the level as needed.
121 """
122 sprites = pg.sprite.Group()
123
124 for object in self.renderer.tmx_data.getObjects():
125 properties = object.__dict__
126 if properties['name'] == 'sprite':
127 if 'direction' in properties:
128 direction = properties['direction']
129 else:
130 direction = 'down'
131
132 if properties['type'] == 'soldier' and direction == 'left':
133 index = 1
134 else:
135 index = 0
136
137 if 'item' in properties:
138 item = properties['item']
139 else:
140 item = None
141
142 if 'id' in properties:
143 id = properties['id']
144 else:
145 id = None
146
147
148 x = properties['x'] * 2
149 y = ((properties['y']) * 2) - 32
150
151 sprite_dict = {'oldman': person.Person('oldman',
152 x, y, direction),
153 'bluedressgirl': person.Person('femalevillager',
154 x, y, direction,
155 'resting', 1),
156 'femalewarrior': person.Person('femvillager2',
157 x, y, direction,
158 'autoresting'),
159 'devil': person.Person('devil', x, y,
160 'down', 'autoresting'),
161 'oldmanbrother': person.Person('oldmanbrother',
162 x, y, direction),
163 'soldier': person.Person('soldier',
164 x, y, direction,
165 'resting', index),
166 'king': person.Person('king', x, y, direction),
167 'treasurechest': person.Chest(x, y, id)}
168
169 sprite = sprite_dict[properties['type']]
170 sprite.item = item
171 self.assign_dialogue(sprite, properties)
172 self.check_for_opened_chest(sprite)
173 sprites.add(sprite)
174
175 return sprites
176
177 def assign_dialogue(self, sprite, property_dict):
178 """
179 Assign dialogue from object property dictionaries in tmx maps to sprites.
180 """
181 dialogue_list = []
182 for i in range(int(property_dict['dialogue length'])):
183 dialogue_list.append(property_dict['dialogue'+str(i)])
184 sprite.dialogue = dialogue_list
185
186 def check_for_opened_chest(self, sprite):
187 if sprite.name == 'treasurechest':
188 if not self.game_data['treasure{}'.format(sprite.id)]:
189 sprite.dialogue = ['Empty.']
190 sprite.item = None
191 sprite.index = 1
192
193 def make_state_dict(self):
194 """
195 Make a dictionary of states the level can be in.
196 """
197 state_dict = {'normal': self.running_normally,
198 'dialogue': self.handling_dialogue,
199 'menu': self.goto_menu}
200
201 return state_dict
202
203 def make_level_portals(self):
204 """
205 Make the portals to switch state.
206 """
207 portal_group = pg.sprite.Group()
208
209 for object in self.renderer.tmx_data.getObjects():
210 properties = object.__dict__
211 if properties['name'] == 'portal':
212 posx = properties['x'] * 2
213 posy = (properties['y'] * 2) - 32
214 new_state = properties['type']
215 portal_group.add(portal.Portal(posx, posy, new_state))
216
217
218 return portal_group
219
220 def running_normally(self, surface, keys, current_time):
221 """
222 Update level normally.
223 """
224 self.check_for_dialogue()
225 self.player.update(keys, current_time)
226 self.sprites.update(current_time)
227 self.collision_handler.update(keys, current_time)
228 self.check_for_portals()
229 self.check_for_battle()
230 self.dialogue_handler.update(keys, current_time)
231 self.check_for_menu(keys)
232 self.viewport_update()
233 self.draw_level(surface)
234
235 def check_for_portals(self):
236 """
237 Check if the player walks into a door, requiring a level change.
238 """
239 portal = pg.sprite.spritecollideany(self.player, self.portals)
240
241 if portal and self.player.state == 'resting':
242 self.player.location = self.player.get_tile_location()
243 self.next = portal.name
244 self.update_game_data()
245 self.done = True
246
247 def check_for_battle(self):
248 """
249 Check if the flag has been made true, indicating
250 to switch state to a battle.
251 """
252 if self.switch_to_battle and self.allow_battles and not self.done:
253 self.player.location = self.player.get_tile_location()
254 self.update_game_data()
255 self.next = 'battle'
256 self.done = True
257
258 def check_for_menu(self, keys):
259 """
260 Check if player hits enter to go to menu.
261 """
262 if keys[pg.K_RETURN] and self.allow_input:
263 if self.player.state == 'resting':
264 self.state = 'menu'
265 self.allow_input = False
266
267 if not keys[pg.K_RETURN]:
268 self.allow_input = True
269
270
271 def update_game_data(self):
272 """
273 Update the persistant game data dictionary.
274 """
275 self.game_data['last location'] = self.player.location
276 self.game_data['last direction'] = self.player.direction
277 self.game_data['last state'] = self.name
278 self.set_new_start_pos()
279
280
281 def set_new_start_pos(self):
282 """
283 Set new start position based on previous state.
284 """
285 location = copy.deepcopy(self.game_data['last location'])
286 direction = self.game_data['last direction']
287
288 if self.next == 'player menu':
289 pass
290 elif direction == 'up':
291 location[1] += 1
292 elif direction == 'down':
293 location[1] -= 1
294 elif direction == 'left':
295 location[0] += 1
296 elif direction == 'right':
297 location[0] -= 1
298
299
300
301 def handling_dialogue(self, surface, keys, current_time):
302 """
303 Update only dialogue boxes.
304 """
305 self.dialogue_handler.update(keys, current_time)
306 self.draw_level(surface)
307
308
309 def goto_menu(self, surface, keys, *args):
310 """
311 Go to menu screen.
312 """
313 self.menu_screen.update(surface, keys)
314 self.menu_screen.draw(surface)
315
316
317 def check_for_dialogue(self):
318 """
319 Check if the level needs to freeze.
320 """
321 if self.dialogue_handler.textbox:
322 self.state = 'dialogue'
323
324 def update(self, surface, keys, current_time):
325 """
326 Update state.
327 """
328 state_function = self.state_dict[self.state]
329 state_function(surface, keys, current_time)
330
331 def viewport_update(self):
332 """
333 Update viewport so it stays centered on character,
334 unless at edge of map.
335 """
336 self.viewport.center = self.player.rect.center
337 self.viewport.clamp_ip(self.level_rect)
338
339 def draw_level(self, surface):
340 """
341 Blit all images to screen.
342 """
343 self.level_surface.blit(self.map_image, self.viewport, self.viewport)
344 self.level_surface.blit(self.player.image, self.player.rect)
345 self.sprites.draw(self.level_surface)
346
347 surface.blit(self.level_surface, (0, 0), self.viewport)
348 self.dialogue_handler.draw(surface)
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364