all repos — Legends-RPG @ 7f3c4199a540d84af6027570cea264e34480d310

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, 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']
 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