all repos — Legends-RPG @ d47f67121715333a19dfc87e93a7adc8a681e540

A fantasy mini-RPG built with Python and Pygame.

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