all repos — Legends-RPG @ eee4466ba0c28babec1e401af0b6e30c2faa7cf7

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