class GameScene:
    def __init__(self):
        self.__sprite_list = pygame.sprite.Group()
        self.__wall_sprite_list = pygame.sprite.Group()
        self.__obstacle_sprite_list = pygame.sprite.Group()
        self.__particles_list = pygame.sprite.Group()
        self.__slot_1 = 255
        self.__slot_2 = 330
        self.__slot_3 = 406
        self.__slot_4 = 481
        self.__slots = [self.__slot_1, self.__slot_2, self.__slot_3, self.__slot_4]
        self.__obstacle_sprites = [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "obstacle_01.png")),
                                   pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "obstacle_02.png"))]
        self.__particle_sprites = [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "explosion_01.png")),
                                   pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "explosion_02.png")),
                                   pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "explosion_03.png"))]
        self.__boulder_sprites = [[pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder.png")),
                           pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling.png")),
                           pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back.png"))],

                           [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_ML.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_ML.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_ML.png"))],

                           [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_M.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_M.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_M.png"))],

                           [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_SM.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_SM.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_SM.png"))],

                           [pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_S.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_rolling_S.png")),
                            pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "boulder_back_S.png"))]]
        self.__background = pygame.image.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "world.png"))
        # the camera's x position should never change as the boulder will never venture off the course
        self.__camera_y_offset = 0

    def start(self):
        #pygame.mixer.music.load("Assets\\level_music.ogg")
        #pygame.mixer.music.play(-1)
        self.__sprite_list.empty()
        self.__wall_sprite_list.empty()
        self.__obstacle_sprite_list.empty()
        self.__particles_list.empty()
        self._create_player()
        self._create_world()
        self.__boulder.change_velocity([0, 0.33])


    def _create_player(self):
        boulder_x_pos = GameConstants.SCREEN_WIDTH / 2
        boulder_y_pos = 0

        self.__boulder = Boulder([boulder_x_pos, boulder_y_pos],
                                   GameConstants.BOULDER_SIZE,
                                   GameConstants.BOULDER_HEALTH,
                                   self.__boulder_sprites)
        self.__sprite_list.add(self.__boulder)

    def _create_world(self):
        wall_size = [GameConstants.WALL_THICKNESS, GameConstants.MAP_HEIGHT]
        wall_sprite = pygame.Surface(wall_size)
        wall_sprite.fill(GameConstants.BLUE)
        left_wall = Wall([240, 0], wall_size, wall_sprite, LEFT_WALL_ID)
        right_wall = Wall([550, 0], wall_size, wall_sprite, RIGHT_WALL_ID)
        self.__walls = [left_wall, right_wall]

        # re-enable to see the wall boundaries
        #self.__sprite_list.add(left_wall)
        #self.__sprite_list.add(right_wall)
        self.__wall_sprite_list.add(left_wall)
        self.__wall_sprite_list.add(right_wall)

        self.__world_shift = 0

        self._create_obstacles()

    def _create_obstacles(self):
        # no obstacles can be created in the first 5 rows
        row = 6

        # 250 rows = 20 (800 x 600) screens
        while row < GameConstants.MAP_ROWS:
            # maybe make these vars to adjust difficulty
            to_create = randint(0, 1)
            number_of_obstacles = 0
            obstacles_for_row = []
            while number_of_obstacles < to_create:
                obstacles_for_row.append(self._create_obstacle(row, obstacles_for_row))
                number_of_obstacles += 1

            for obstacle in obstacles_for_row:
                self.__obstacle_sprite_list.add(obstacle)
            row += 1

    def _create_obstacle(self, row, obstacles_for_row):
        position = self._compute_unique_obstacle_coordinates(row, obstacles_for_row)
        sprite = self.__obstacle_sprites[self._get_random_obstacle_sprite_index()]
        obstacle = Obstacle(position, GameConstants.CHUNK_SIZE, GameConstants.OBSTACLE_HEALTH, sprite)
        return obstacle

    def _get_random_obstacle_sprite_index(self):
        sprite_index = randint(0, (len(self.__obstacle_sprites) - 1))
        return sprite_index

    def _compute_unique_obstacle_coordinates(self, row, obstacles_in_row):
        unique_coordinates_found = False
        coordinates = None
        if len(obstacles_in_row) < 1:
            coordinates = self._compute_obstacle_coordinates(row)
            unique_coordinates_found = True

        while not unique_coordinates_found:
            coordinates = self._compute_obstacle_coordinates(row)
            for obstacle in obstacles_in_row:
                x_diff = coordinates[0] != obstacle.get_position()[0]
                y_diff = coordinates[1] != obstacle.get_position()[1]
                if x_diff or y_diff:
                    unique_coordinates_found = True
        return coordinates

    def _compute_obstacle_coordinates(self, row):
        slot = randint(0, (len(self.__slots)-1))
        y_pos = row*GameConstants.ROW_HEIGHT  # map is broken up into 64 x 64 chunks
        x_pos = self.__slots[slot]
        return [x_pos, y_pos]

    def _check_for_collisions(self):
        self._check_for_wall_collisions()
        self._check_for_obstacle_collisions()

    def _check_for_wall_collisions(self):
        wall_collisions = pygame.sprite.spritecollide(self.__boulder, self.__wall_sprite_list, False)
        boulder_position = self.__boulder.get_position()
        boulder_direction_vector = self.__boulder.get_velocity()
        original_position = boulder_position[:]
        for wall in wall_collisions:
            # apply a speed reduction to the boulder_velocity on a collision
            x_speed_reduction = 0.75*abs(boulder_direction_vector[0])
            if boulder_direction_vector[0] < 0 and wall.get_id() == LEFT_WALL_ID:
                boulder_position[0] = wall.rect.right
                self.__boulder.update_position(boulder_position)
                boulder_direction_vector[0] += x_speed_reduction
            elif boulder_direction_vector[0] > 0 and wall.get_id() == RIGHT_WALL_ID:
                boulder_position[0] = wall.rect.left - self.__boulder.get_size()[0]
                self.__boulder.update_position(boulder_position)
                boulder_direction_vector[0] -= x_speed_reduction

            # take the velocity of the boulder and reverse the direction
            boulder_direction_vector[0] *= -1
            self.__boulder.change_velocity(boulder_direction_vector)
            self._deal_damage_to_boulder(GameConstants.WALL_DAMAGE, original_position)

    def _deal_damage_to_boulder(self, damage, position):
        particle = Particles(position, self.__particle_sprites)
        self.__particles_list.add(particle)
        self.__boulder.take_damage(damage)

    def _check_for_obstacle_collisions(self):
        obstacle_collisions = pygame.sprite.spritecollide(self.__boulder, self.__obstacle_sprite_list, False)
        for obstacle in obstacle_collisions:
            """boulder_direction_vector = self.__boulder.get_velocity()
            #self._adjust_boulder_from_collision(obstacle)
            # apply a speed reduction to the boulder_velocity on a collision
            x_speed_reduction = 0.75*abs(boulder_direction_vector[0])
            y_speed_reduction = 0.90*abs(boulder_direction_vector[1])
            if boulder_direction_vector[0] < 0:
                boulder_direction_vector[0] += x_speed_reduction
            elif boulder_direction_vector[0] > 0:
                boulder_direction_vector[0] -= x_speed_reduction

            boulder_direction_vector[1] -= y_speed_reduction

            # take the velocity of the boulder and reverse the direction
            boulder_direction_vector[0] *= -1
            self.__boulder.change_velocity(boulder_direction_vector)"""

            self._deal_damage_to_boulder(self.__boulder.get_health(), obstacle.get_position())

    def _adjust_boulder_from_collision(self, obstacle):
        boulder_x = self.__boulder.get_position()[0]
        boulder_y = self.__boulder.get_position()[1]
        intersect_on_boulder_left = (obstacle.rect.left <= self.__boulder.rect.left) and (self.__boulder.rect.left <= obstacle.rect.right)
        intersect_on_boulder_right = (obstacle.rect.left <= self.__boulder.rect.right) and (self.__boulder.rect.right <= obstacle.rect.right)
        intersect_on_boulder_top = (obstacle.rect.top <= self.__boulder.rect.top) and (self.__boulder.rect.top <= obstacle.rect.bottom)
        intersect_on_boulder_bottom = (obstacle.rect.top <= self.__boulder.rect.bottom) and (self.__boulder.rect.bottom <= obstacle.rect.bottom)

        if intersect_on_boulder_bottom and intersect_on_boulder_left:
            boulder_x = obstacle.rect.right
            boulder_y = obstacle.rect.top - self.__boulder.get_size()[1]
        elif intersect_on_boulder_top and intersect_on_boulder_left:
            boulder_x = obstacle.rect.right
            boulder_y = obstacle.rect.bottom
        elif intersect_on_boulder_bottom and intersect_on_boulder_right:
            boulder_x = obstacle.rect.left - self.__boulder.get_size()[0]
            boulder_y = obstacle.rect.top - self.__boulder.get_size()[1]
        elif intersect_on_boulder_top and intersect_on_boulder_right:
            boulder_x = obstacle.rect.left - self.__boulder.get_size()[0]
            boulder_y = obstacle.rect.bottom
        elif intersect_on_boulder_right:
            boulder_x = obstacle.rect.left - self.__boulder.get_size()[0]
        elif intersect_on_boulder_left:
            boulder_x = obstacle.rect.right
        elif intersect_on_boulder_top:
            boulder_y = obstacle.rect.bottom
        elif intersect_on_boulder_bottom:
            boulder_y = obstacle.rect.top - self.__boulder.get_size()[1]

        self.__boulder.update_position([boulder_x, boulder_y])

    def _check_particles_have_dissipated(self):
        index_list = []
        for index, particles in enumerate(self.__particles_list):
            if particles.have_dissipated():
                index_list.append(index)

        for index in index_list:
            del self.__particles_list[index]

    def draw(self, screen):

        # blue layer
        screen.fill(GameConstants.BLUE)

        # background layer
        screen.blit(self.__background, (0, 0-self.__boulder.get_position()[1]))
        for obstacle in self.__obstacle_sprite_list:
            display_coordinates = obstacle.get_position()
            display_coordinates[1] -= self.__boulder.get_velocity()[1]
            screen.blit(obstacle.image, (display_coordinates[0], display_coordinates[1]))

        # print('Number of particles: ' + str(len(self.__particles_list)))
        for particles in self.__particles_list:
            if not particles.have_dissipated():
                screen.blit(particles.image, (particles.get_position()[0], 27))

        # entities layer
        screen.blit(self.__boulder.image, (self.__boulder.get_position()[0], 25))

    def update(self):
        self.__camera_y_offset = self.__boulder.get_position()[1]

        # quirk using this method -- need to figure out how to get the most recently pressed key and use that one
        keys_pressed = pygame.key.get_pressed()
        if keys_pressed[pygame.K_LEFT]:
            self.__boulder.move_left()
        elif keys_pressed[pygame.K_RIGHT]:
            self.__boulder.move_right()
        elif keys_pressed[pygame.K_UP]:
            self.__boulder.move_up()
        elif keys_pressed[pygame.K_DOWN]:
            self.__boulder.move_down()
        elif keys_pressed[pygame.K_ESCAPE]:
            return GameStatus.STATUS_QUIT

        self._check_for_collisions()

        # temporary disabled the clean up of the dissipated particles -- optimize this later
        # self._check_particles_have_dissipated()

        if self.__boulder.is_dead():
            #pygame.mixer.music.stop()
            return GameStatus.TRANSITION_GAME_OVER  # signal the game over screen

        # has the boulder crossed the finish line
        if self.__boulder.get_position()[1] > GameConstants.MAP_HEIGHT:
            #pygame.mixer.music.stop()
            self.__boulder.stop_rolling()
            if self.__boulder.get_health() > 2*GameConstants.BOULDER_HEALTH/3:
                return GameStatus.TRANSITION_GOOD_ENDING
            else:
                return GameStatus.TRANSITION_BAD_ENDING

        self.__sprite_list.update()
        self.__obstacle_sprite_list.update()
        self.__particles_list.update()
Ejemplo n.º 2
0
class GameScene(Scene):
    def __init__(self, controller):
        super(GameScene, self).__init__(controller)
        self.__sprite_list = pygame.sprite.Group()
        self.__wall_sprite_list = pygame.sprite.Group()
        self.__obstacle_sprite_list = pygame.sprite.Group()
        self.__particles_list = pygame.sprite.Group()
        self.__slot_1 = 255
        self.__slot_2 = 330
        self.__slot_3 = 406
        self.__slot_4 = 481
        self.__slots = [self.__slot_1, self.__slot_2, self.__slot_3, self.__slot_4]
        self.__background = Images.WORLD_01
        self.__obstacleTypes = [Rock(), TwinRock()]
        # the camera's x position should never change as the boulder will never venture off the course
        self.__camera_y_offset = 0
        self.__boulder_rolling_sound = Sounds.ROCK_ROLLING
        self.__rock_breaking_sound = Sounds.ROCK_BREAKING

    def reset(self):
        super(GameScene, self).reset()
        pygame.mixer.music.load(os.path.join(GameConstants.ASSETS_DIRECTORY, "level_music.ogg"))
        pygame.mixer.music.play(-1)
        self.__sprite_list.empty()
        self.__wall_sprite_list.empty()
        self.__obstacle_sprite_list.empty()
        self.__particles_list.empty()
        self.__create_player()
        self.__create_world()
        self.__boulder.velocity = Boulder.INITIAL_VELOCITY.copy()
        self.__boulder_rolling_sound.play(-1)

    def __create_player(self):
        self.__boulder = Boulder(Vector((GameConstants.SCREEN_WIDTH - Boulder.INITIAL_SIZE.x)//2, 0))
        self.__sprite_list.add(self.__boulder)

    def __create_world(self):
        left_wall = Wall(Vector(240, 0), LEFT_WALL_ID)
        right_wall = Wall(Vector(550, 0), RIGHT_WALL_ID)
        self.__walls = [left_wall, right_wall]
        self.__wall_sprite_list.add(left_wall)
        self.__wall_sprite_list.add(right_wall)

        self.__world_shift = 0

        self.__create_obstacles()

    def __create_obstacles(self):
        # no obstacles can be created in the first 5 rows
        row = 6

        # 250 rows = 20 (800 x 600) screens
        while row < GameConstants.MAP_ROWS:
            # maybe make these vars to adjust difficulty
            to_create = randint(0, 2)
            number_of_obstacles = 0
            obstacles_for_row = []
            while number_of_obstacles < to_create:
                obstacles_for_row.append(self.__create_obstacle(row, obstacles_for_row))
                number_of_obstacles += 1

            for obstacle in obstacles_for_row:
                self.__sprite_list.add(obstacle)
                self.__obstacle_sprite_list.add(obstacle)
            row += 1

    def __create_obstacle(self, row, obstacles_for_row):
        position = self.__compute_unique_obstacle_coordinates(row, obstacles_for_row)
        obstacle_type = self.__obstacleTypes[self.__get_random_obstacle_sprite_index()]
        obstacle = Obstacle(position, obstacle_type, "obstacle_not_unique")
        return obstacle

    def __get_random_obstacle_sprite_index(self):
        sprite_index = randint(0, len(self.__obstacleTypes) - 1)
        return sprite_index

    def __compute_unique_obstacle_coordinates(self, row, obstacles_in_row):
        unique_coordinates_found = False
        coordinates = None
        if len(obstacles_in_row) < 1:
            coordinates = self.__compute_obstacle_coordinates(row)
            unique_coordinates_found = True

        while not unique_coordinates_found:
            coordinates = self.__compute_obstacle_coordinates(row)
            for obstacle in obstacles_in_row:
                if coordinates != obstacle.position:
                    unique_coordinates_found = True
        return coordinates

    def __compute_obstacle_coordinates(self, row):
        slot = randint(0, (len(self.__slots)-1))
        y_pos = row*GameConstants.ROW_HEIGHT  # map is broken up into 64 x 64 chunks
        x_pos = self.__slots[slot]
        return Vector(x_pos, y_pos, 0)

    def __check_for_collisions(self):
        if self.__check_for_wall_collisions() or self.__check_for_obstacle_collisions():
            self.__rock_breaking_sound.play(0)

    def __check_for_wall_collisions(self):
        wall_collisions = pygame.sprite.spritecollide(self.__boulder, self.__wall_sprite_list, False)
        original_position = self.__boulder.position.copy()
        for wall in wall_collisions:
            # apply a speed reduction to the boulder_velocity on a collision
            x_speed_reduction = 0.75*abs(self.__boulder.velocity.x)

            if self.__boulder.velocity.x < 0 and wall.get_object_id() == LEFT_WALL_ID:
                self.__boulder.position.x = wall.rect.right
                self.__boulder.velocity.x += x_speed_reduction
                self.__particles_list.add(Particles(Vector(wall.rect.right-Boulder.INITIAL_SIZE.x//4, original_position.y), "particles_not_unique"))

            elif self.__boulder.velocity.x > 0 and wall.get_object_id() == RIGHT_WALL_ID:
                self.__boulder.position.x = wall.rect.left - self.__boulder.get_size().x
                self.__boulder.velocity.x -= x_speed_reduction
                self.__particles_list.add(Particles(Vector(wall.rect.left-Boulder.INITIAL_SIZE.x//4, original_position.y), "particles_not_unique"))

            # take the velocity of the boulder and reverse the direction
            self.__boulder.velocity.x *= -1
            self.__deal_damage_to_boulder(Wall.DAMAGE)
            return True
        return False

    def __deal_damage_to_boulder(self, damage):
        self.__boulder.take_damage(damage)

    def __check_for_obstacle_collisions(self):
        obstacle_collisions = pygame.sprite.spritecollide(self.__boulder, self.__obstacle_sprite_list, True, self.__is_boulder_obstacle_collision)
        collision_count = len(obstacle_collisions)
        if collision_count > 0:
            self.__particles_list.add(Particles(self.__boulder.position.copy(), "particles_not_unique"))
            self.__deal_damage_to_boulder(5*collision_count)
            return True

        return False

    def __is_boulder_obstacle_collision(self, boulder, obstacle):
        return rectangle_intersection(boulder.rect, obstacle.get_bounding_box())

    def __check_particles_have_dissipated(self):
        to_remove = []
        for particles in self.__particles_list:
            if particles.have_dissipated():
                to_remove.append(particles)

        for particles in to_remove:
            self.__particles_list.remove(particles)

    def __get_object_display_coordinates(self, game_object):
        display_coordinates = game_object.position.copy()
        display_coordinates.y -= (self.__boulder.position.y - GameConstants.BOULDER_DRAW_OFFSET)
        return display_coordinates.x, display_coordinates.y

    def draw(self, screen):
        super(GameScene, self).draw(screen)

        screen.blit(self.__background, (0, 0-self.__boulder.position.y))

        # sort the game_objects based on y-position so that they render properly
        game_objects = [self.__boulder] + [obstacle for obstacle in self.__obstacle_sprite_list]
        game_objects = sorted(game_objects, key=lambda game_object: game_object.rect.centery)

        for sprite in game_objects:
            screen.blit(sprite.image, (self.__get_object_display_coordinates(sprite)))

            """
            # -- DEBUG -- display obstacle bounding boxes
            bounding_box = sprite.get_bounding_box()
            collision_box = pygame.Surface([bounding_box.width, bounding_box.height])
            collision_box.fill(GameConstants.BLUE)
            collision_box_position_x = bounding_box.x
            collision_box_position_y = bounding_box.y - self.__boulder.position.y + 25
            screen.blit(collision_box, (collision_box_position_x, collision_box_position_y))"""

        for particles in self.__particles_list:
            if not particles.have_dissipated():
                screen.blit(particles.image, (particles.position.x, GameConstants.BOULDER_DRAW_OFFSET + 2))

        """
        # -- DEBUG --
        screen.blit(self.__boulder.image, (self.__boulder.position.x, 25))

        for wall in self.__wall_sprite_list:
            display_coordinates = wall.position
            display_coordinates.y -= self.__boulder.velocity.y
            screen.blit(wall.image, (display_coordinates.x, display_coordinates.y))
        """

    def __apply_friction(self):
        x_friction = 0.005
        # -- apply vertical friction
        if self.__boulder.velocity.y >= Boulder.MIN_Y_VELOCITY:
            self.__boulder.velocity.y -= 0.005

        if (self.__boulder.velocity.x > 0 and self.__boulder.velocity.x - x_friction < 0) or \
            (self.__boulder.velocity.x < 0 and self.__boulder.velocity.x + x_friction > 0):
            self.__boulder.velocity.x = 0
        elif self.__boulder.velocity.x > 0:
            self.__boulder.velocity.x -= x_friction
        elif self.__boulder.velocity.x < 0:
            self.__boulder.velocity.x += x_friction

    def __update_boulder(self):
        if self._controller.raise_move_backward():
            self.__boulder.decelerate()

        if self._controller.raise_move_forward():
            self.__boulder.accelerate()

        if self._controller.raise_move_left():
            self.__boulder.move_left()

        if self._controller.raise_move_right():
            self.__boulder.move_right()

    def update(self, events):
        super(GameScene, self).update(events)
        
        self.__camera_y_offset = self.__boulder.position.y

        # testing the boulder with some friction
        self.__apply_friction()

        if self._controller.raise_transition_escape():
            return GameStatus.TRANSITION_MAIN_MENU

        self.__update_boulder()

        self.__check_for_collisions()

        # temporary disabled the clean up of the dissipated particles -- optimize this later
        self.__check_particles_have_dissipated()

        if self.__boulder.is_dead():
            pygame.mixer.music.stop()
            self.__boulder_rolling_sound.stop()
            return GameStatus.TRANSITION_GAME_OVER  # signal the game over screen

        # has the boulder crossed the finish line
        if self.__boulder.position.y > GameConstants.MAP_HEIGHT:
            pygame.mixer.music.stop()
            self.__boulder_rolling_sound.stop()
            if self.__boulder.get_health() > 2*Boulder.INITIAL_HEALTH/3:
                return GameStatus.TRANSITION_GOOD_ENDING
            else:
                return GameStatus.TRANSITION_BAD_ENDING

        self.__sprite_list.update()
        self.__obstacle_sprite_list.update()
        self.__particles_list.update()