all repos — Legends-RPG @ 5a42cedfef57b4e82debdde9de1dd8a881ee0e59

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