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