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