Beispiel #1
0
class MapCamera:
    def __init__(self, x_init_world, y_init_world, width_view_camera, height_view_camera):

        # rect that represents the surface visible of the camera
        self.rect_view_camera = Rect(x_init_world, y_init_world, width_view_camera, height_view_camera)

        self.pos_dest_screen = (0, 0)  # Position to draw the map captured by this Camera
        self.map_handler = None  # Controller of the map

        self.map_tile_surface = None  # Surface Buffer that contains the map drawn
        self.rect_hidden_camera = None  # Rect of the surface buffer with pos on map

        self.following = None
        self.can_follow = {"left": False, "top": False, "bottom": False, "right": True}

    # Start the camera setting buffers
    def init(self):
        assert self.map_handler is not None, "There's not a " + str(MapHandler.__class__) + " instance "
        assert self.map_handler.has_map_loaded(), "MapHandler has no map loaded yet!"
        self.__create_buffer()

    def __create_buffer(self):

        self.rect_hidden_camera = create_camera_rect(self.rect_view_camera, self.map_handler.get_tile_size())
        self.map_tile_surface = self.map_handler.create_image(self.rect_hidden_camera)

    def set_pos_screen(self, x_screen, y_screen):  # Set position to draw the map on screen
        self.pos_dest_screen = (x_screen, y_screen)

    def set_maphandler(self, map_handler):
        self.map_handler = map_handler

    # Move camera x y pixels
    def move(self, x_world, y_world):

        rect_moved = self.rect_view_camera.move(x_world, y_world)

        if self.map_handler.is_rect_out_of_map_bounds(rect_moved):
            return

        self.rect_view_camera.move_ip(x_world, y_world)

        if not self.rect_hidden_camera.contains(self.rect_view_camera):
            # If new pos is out of the buffer, we create a new one!
            self.__create_buffer()

    def draw(self, screen):

        rect_area_visible = Rect(self.rect_view_camera.x - self.rect_hidden_camera.x,
                                 self.rect_view_camera.y - self.rect_hidden_camera.y,
                                 self.rect_view_camera.width, self.rect_view_camera.height)
        screen.blit(self.map_tile_surface, self.pos_dest_screen, rect_area_visible)

    def get_x(self):
        return self.rect_view_camera.x

    def get_y(self):
        return self.rect_view_camera.y

    def follow(self, player):
        self.following = player

    def update(self):
        if self.following:
            x_p, y_p = self.following.x, \
                       self.following.y

            if self.can_follow['right']:
                if x_p > self.rect_view_camera.x + self.rect_view_camera.width / 4:
                    rect_moved = self.rect_view_camera.copy()
                    rect_moved.x = x_p - self.rect_view_camera.width / 4

                    if self.map_handler.is_rect_out_of_map_bounds(rect_moved):
                        return

                    self.rect_view_camera = rect_moved

                    if not self.rect_hidden_camera.contains(self.rect_view_camera):
                        # If new pos is out of the buffer, we create a new one!
                        self.__create_buffer()

    def is_rect_out_of_camera_bounds(self, rect):
        return not self.rect_view_camera.contains(rect)
class MapHandler:
    def __init__(self, tile_size):

        self.matrix_tiles = None
        self.rect_full_map = None
        self.tileset_surface = None
        self.is_map_loaded = False
        self.n_rows = 0
        self.n_cols = 0
        self.n_tiles = 0
        self.path_to_tileset = None
        self.tile_size = tile_size

    # Load a file containing tile positioning
    def load_map(self, path_to_file_level):

        # Tile s positioning data
        file_level = open(path_to_file_level)

        self.matrix_tiles = []
        # lines = file_level.readlines()
        # n_lines = len(lines)
        self.n_rows, self.n_cols, self.n_tiles = read_n_rows_columns_num_tiles(file_level)

        if self.is_map_loaded:
            self.check_loaded_tileset()  # We check that we can cover the tiles with our current
        # tile set

        i = 0
        while i < self.n_rows:
            j = 0
            line = list(file_level.readline().strip())
            while j < self.n_cols:
                try:
                    line[j] = int(line[j])
                    if line[j] < 0 or line[j] >= self.n_tiles:
                        raise AssertionError("Tile Value at ({0},{1}) must be in range {2}-{3}. '{4}' not permitted".
                                             format(i + 2, j + 1, 0, self.n_tiles - 1, line[j]))

                except ValueError:
                    raise AssertionError("Tile Value at ({0},{1}) must be an Integer. '{2}' not permitted".
                                         format(i + 2, j + 1, line[j]))
                except IndexError:
                    raise AssertionError("Wrong number of tiles at line {0}. {1} tiles are missing".
                                         format(i + 2, self.n_cols - j))

                j += 1

            self.matrix_tiles.append(line)
            i += 1

        file_level.close()
        self.rect_full_map = Rect(0, 0, self.n_cols * self.tile_size, self.n_rows * self.tile_size)
        self.is_map_loaded = True

    def clamp_rect(self, rect):
        return rect.clamp(self.rect_full_map)

    def has_map_loaded(self):
        return self.is_map_loaded

    def load_tileset(self, path_to_file_image):

        # Tile s image
        self.tileset_surface = pygame.image.load(path_to_file_image)

        self.check_loaded_tileset()

        self.tileset_surface.convert()
        self.path_to_tileset = path_to_file_image

    def is_rect_out_of_map_bounds(self, rect):
        return not self.rect_full_map.contains(rect)

    # Create a Surface that contains/draws all the visible tiles that are contained in rect_cut
    def create_image(self, rect_camera_map):
        # pre: rect_image must be created <TILE_SIZE*rows>X<TILE_SIZE*columns>
        assert self.is_map_loaded, "There's no map loaded yet!"

        image_map_tile = pygame.Surface(rect_camera_map.size, SRCALPHA)

        n_rows = rect_camera_map.height // self.tile_size
        n_columns = rect_camera_map.width // self.tile_size

        i_init_matriz = rect_camera_map.y // self.tile_size
        j_init_matriz = rect_camera_map.x // self.tile_size

        src_srfc_tile_rect = Rect(0, 0, self.tile_size, self.tile_size)

        for i in range(n_rows):

            i_matrix = i_init_matriz + i
            if i_matrix >= self.n_rows:
                continue

            for j in range(n_columns):

                j_matrix = j_init_matriz + j

                if j_matrix >= self.n_cols:
                    continue

                tile_index = self.matrix_tiles[i_matrix][j_matrix]

                if tile_index != 0:
                    src_srfc_tile_rect.x = self.tile_size * (tile_index - 1)
                    image_map_tile.blit(self.tileset_surface, (
                        (j_matrix - j_init_matriz) * self.tile_size, (i_matrix - i_init_matriz) * self.tile_size),
                                        src_srfc_tile_rect)

        return image_map_tile

    def check_loaded_tileset(self):
        if normalize(self.tileset_surface.get_width(), self.tile_size) != self.tileset_surface.get_width() or \
                                self.tileset_surface.get_width() // self.tile_size < (
                            self.n_tiles - 1) or self.tileset_surface.get_height() != self.tile_size:
            raise AssertionError(
                'Wrong tileset, {0} must be width>={1} , height={2} and contain {3} different aligned tiles.'.format(
                    self.path_to_tileset, self.n_tiles * self.tile_size, self.tile_size, self.n_tiles - 1))

    def collide_map(self, rect):
        assert self.is_map_loaded, "There's no map loaded yet!"

        rect_collide = create_camera_rect(rect, self.tile_size)

        n_rows = rect_collide.height // self.tile_size
        n_columns = rect_collide.width // self.tile_size

        i_init_matriz = rect_collide.y // self.tile_size
        j_init_matriz = rect_collide.x // self.tile_size

        collide_list = []
        for i in range(n_rows):

            i_matrix = i_init_matriz + i
            if i_matrix >= self.n_rows:
                continue

            for j in range(n_columns):

                j_matrix = j_init_matriz + j

                if j_matrix >= self.n_cols:
                    continue

                tile_index = self.matrix_tiles[i_matrix][j_matrix]

                if tile_index != 0:
                    collide_list.append((tile_index, (i, j)))

        return collide_list

    def get_tile_size(self):
        return self.tile_size

    def get_floor_dist(self, x, y, max_dist):
        """Obtiene la distancia entre el punto (x, y) y el proximo bloque solido hacia abajo"""

        for dy in range(max_dist):
            if (y + dy) % self.tile_size == 0 and self.matrix_tiles[int((y + dy) // self.tile_size)][
                        x // self.tile_size] != 0:
                return dy

        return max_dist

    def get_nearest_bottom_tile(self, rect, max_deep):
        """"Regresa el primer tile que se encuentre hacia abajo"""

        rect_collide = create_camera_rect(rect, self.tile_size)

        n_columns = rect_collide.width // self.tile_size

        i_init_matriz = rect_collide.bottom // self.tile_size
        j_init_matriz = rect_collide.x // self.tile_size

        for i in range(max_deep):

            i_matrix = i_init_matriz + i
            if i_matrix >= self.n_rows:
                return None

            for j in range(n_columns):

                j_matrix = j_init_matriz + j

                if j_matrix >= self.n_cols:
                    break

                tile_index = self.matrix_tiles[i_matrix][j_matrix]

                if tile_index != 0:
                    return tile_index, (i_matrix, j_matrix), rect_collide.bottom - rect.bottom + i * self.tile_size

        return None