示例#1
0
    def __init__(self, start_pos, initial_heading_vector, current_target,
                 target_position, damage, speed, explosions_sprite_sheet,
                 image_atlas, homing_radius, collision_grid, *groups):

        super().__init__(*groups)
        self.explosions_sprite_sheet = explosions_sprite_sheet
        self.original_image = image_atlas.subsurface((0, 256, 6, 12))

        self.current_vector = [
            initial_heading_vector[0], initial_heading_vector[1]
        ]
        self.target_vector = self.current_vector
        self.position = [float(start_pos[0]), float(start_pos[1])]

        facing_angle = math.atan2(-self.current_vector[0],
                                  -self.current_vector[1]) * 180 / math.pi
        self.image = pygame.transform.rotate(self.original_image, facing_angle)

        self.rect = self.image.get_rect()
        self.rect.center = start_pos

        self.collision_grid = collision_grid
        handlers_by_type = {
            GameCollisionType.MONSTER: self.collision_grid.no_handler
        }
        self.collision_shape = CollisionRect(
            self.original_image.get_rect(),
            math.atan2(-self.current_vector[0],
                       -self.current_vector[1]), handlers_by_type,
            GameCollisionType.TURRET_PROJECTILE, [GameCollisionType.MONSTER])
        self.collision_shape.set_owner(self)
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)

        self.drawable_collision_rect = DrawableCollisionRect(
            self.collision_shape)

        self.should_die = False

        self.projectile_speed = speed
        self.damage = damage

        self.target_position = target_position
        x_diff = self.target_position[0] - self.position[0]
        y_diff = self.target_position[1] - self.position[1]
        total_target_dist = math.sqrt(x_diff**2 + y_diff**2)
        self.target_distance = total_target_dist + 16.0
        self.shot_range = 600.0

        self.rotate_speed = 10.0

        self.time_to_home_in = False
        self.homing_time_acc = 0.0
        self.homing_time = 0.2
        self.homing_radius = homing_radius

        self.current_target = current_target
示例#2
0
    def __init__(self, start_pos, initial_heading_vector, damage,
                 collision_grid, arrow_image, *groups):

        super().__init__(*groups)
        self.collision_grid = collision_grid

        self.original_image = arrow_image
        self.image = self.original_image.copy()

        self.rect = self.image.get_rect()
        self.rect.center = start_pos

        self.current_vector = [
            initial_heading_vector[0], initial_heading_vector[1]
        ]

        self.position = [
            float(self.rect.center[0]),
            float(self.rect.center[1])
        ]
        self.world_position = [
            float(self.rect.center[0]),
            float(self.rect.center[1])
        ]

        self.should_die = False

        self.bullet_speed = 300.0
        self.damage = damage

        self.shot_range = 1000.0

        self.collision_rect = CollisionRect(
            self.rect, 0, {
                GameCollisionType.MONSTER: CollisionNoHandler(),
                GameCollisionType.TILE: CollisionNoHandler()
            }, GameCollisionType.PLAYER_WEAPON,
            [GameCollisionType.MONSTER, GameCollisionType.TILE])
        self.collision_rect.set_owner(self)
        self.collision_grid.add_new_shape_to_grid(self.collision_rect)

        self.drawable_rect = DrawableCollisionRect(self.collision_rect)
示例#3
0
文件: player.py 项目: MyreMylar/vania
    def update_melee_attacking(self, time_delta):
        if self.active_anim.running:
            if 3 < self.active_anim.frame_index < 10:
                if self.x_facing_direction == "left":
                    self.move_speed -= self.attack_slide_acceleration * time_delta
                    if self.move_speed < -self.attack_slide_top_speed:
                        self.move_speed = -self.attack_slide_top_speed
                    self.velocity[0] = self.move_speed
                elif self.x_facing_direction == "right":
                    self.move_speed += self.attack_slide_acceleration * time_delta
                    if self.move_speed > self.attack_slide_top_speed:
                        self.move_speed = self.attack_slide_top_speed
                    self.velocity[0] = self.move_speed
            if self.active_anim.frame_index == 3 and self.melee_collision_shape is None:
                attack_position = [
                    self.world_position[0], self.world_position[1]
                ]
                if self.x_facing_direction == "left":
                    attack_position[0] -= 64
                attack_dimensions = [64, 16]
                game_types_to_collide_with = [CollisionType.AI]
                handlers_to_use = {
                    CollisionType.AI: self.collision_grid.no_handler
                }
                self.melee_collision_shape = CollisionRect(
                    pygame.Rect(attack_position[0], attack_position[1],
                                attack_dimensions[0], attack_dimensions[1]), 0,
                    handlers_to_use, CollisionType.PLAYER_ATTACKS,
                    game_types_to_collide_with)
                self.collision_grid.add_new_shape_to_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape.owner = self

                self.melee_attack_collision_drawable_rectangle = DrawableCollisionRect(
                    self.melee_collision_shape)
            elif self.active_anim.frame_index > 3 and self.melee_collision_shape is not None:
                self.collision_grid.remove_shape_from_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape = None

        else:
            self.motion_state = "idle"
示例#4
0
    def __init__(self, moving_sprites_group, collision_grid, knife_surface,
                 start_position, throwing_direction, projectile_drawable_rects,
                 camera, is_player_weapon=True, speed=1020.0):
        super().__init__(moving_sprites_group)

        self.collision_grid = collision_grid
        self.moving_sprites_group = moving_sprites_group
        self.original_image = knife_surface
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect()
        self.knife_embed_length = (self.rect.width/9)*4
        self.facing_direction = throwing_direction[:]
        self.facing_angle = math.degrees(math.atan2(self.facing_direction[1], self.facing_direction[0]))

        self.world_position = [start_position[0], start_position[1]]
        self.screen_position = [0.0, 0.0]

        self.throw_speed = speed + random.normalvariate(0, 40)
        self.initial_throw_velocity = [self.facing_direction[0]*self.throw_speed,
                                       self.facing_direction[1]*self.throw_speed]

        self.velocity = [self.initial_throw_velocity[0], self.initial_throw_velocity[1]]

        game_types_to_collide_with = [CollisionType.WORLD_SOLID,
                                      CollisionType.WORLD_PLATFORM_EDGE,
                                      CollisionType.WORLD_JUMP_THROUGH,
                                      CollisionType.WORLD_JUMP_THROUGH_EDGE]

        handlers_to_use = {CollisionType.WORLD_SOLID: self.collision_grid.rub_handler,
                           CollisionType.WORLD_PLATFORM_EDGE: self.collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH: self.collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH_EDGE: self.collision_grid.rub_handler}

        if is_player_weapon:
            self.type = "player_projectile"
            collision_type = CollisionType.PLAYER_PROJECTILES
            game_types_to_collide_with.extend([CollisionType.AI,CollisionType.AI_PROJECTILES])
            handlers_to_use[CollisionType.AI] = self.collision_grid.no_handler
            handlers_to_use[CollisionType.AI_PROJECTILES] = self.collision_grid.no_handler
        else:
            self.type = "enemy_projectile"
            collision_type = CollisionType.AI_PROJECTILES
            game_types_to_collide_with.extend([CollisionType.PLAYER, CollisionType.PLAYER_PROJECTILES])
            handlers_to_use[CollisionType.PLAYER] = self.collision_grid.no_handler
            handlers_to_use[CollisionType.PLAYER_PROJECTILES] = self.collision_grid.no_handler

        self.collision_shape = CollisionRect(pygame.Rect(self.world_position[0],
                                                         self.world_position[1],
                                                         self.rect.width,
                                                         int(self.rect.height*0.6)),
                                             0,
                                             handlers_to_use,
                                             collision_type,
                                             game_types_to_collide_with)
        self.collision_shape.owner = self
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)

        self.collision_shape.set_position(self.world_position)

        self.projectile_drawable_rects = projectile_drawable_rects
        self.drawable_rectangle = DrawableCollisionRect(self.collision_shape)
        self.projectile_drawable_rects.append(self.drawable_rectangle)

        self.frozen = False
        self.life_time = 15.0
        self.hit_something = False

        self.update_screen_position(camera)
        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        self.should_kill = False
示例#5
0
class ThrowingKnife(Projectile):
    def __init__(self, moving_sprites_group, collision_grid, knife_surface,
                 start_position, throwing_direction, projectile_drawable_rects,
                 camera, is_player_weapon=True, speed=1020.0):
        super().__init__(moving_sprites_group)

        self.collision_grid = collision_grid
        self.moving_sprites_group = moving_sprites_group
        self.original_image = knife_surface
        self.image = self.original_image.copy()
        self.rect = self.image.get_rect()
        self.knife_embed_length = (self.rect.width/9)*4
        self.facing_direction = throwing_direction[:]
        self.facing_angle = math.degrees(math.atan2(self.facing_direction[1], self.facing_direction[0]))

        self.world_position = [start_position[0], start_position[1]]
        self.screen_position = [0.0, 0.0]

        self.throw_speed = speed + random.normalvariate(0, 40)
        self.initial_throw_velocity = [self.facing_direction[0]*self.throw_speed,
                                       self.facing_direction[1]*self.throw_speed]

        self.velocity = [self.initial_throw_velocity[0], self.initial_throw_velocity[1]]

        game_types_to_collide_with = [CollisionType.WORLD_SOLID,
                                      CollisionType.WORLD_PLATFORM_EDGE,
                                      CollisionType.WORLD_JUMP_THROUGH,
                                      CollisionType.WORLD_JUMP_THROUGH_EDGE]

        handlers_to_use = {CollisionType.WORLD_SOLID: self.collision_grid.rub_handler,
                           CollisionType.WORLD_PLATFORM_EDGE: self.collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH: self.collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH_EDGE: self.collision_grid.rub_handler}

        if is_player_weapon:
            self.type = "player_projectile"
            collision_type = CollisionType.PLAYER_PROJECTILES
            game_types_to_collide_with.extend([CollisionType.AI,CollisionType.AI_PROJECTILES])
            handlers_to_use[CollisionType.AI] = self.collision_grid.no_handler
            handlers_to_use[CollisionType.AI_PROJECTILES] = self.collision_grid.no_handler
        else:
            self.type = "enemy_projectile"
            collision_type = CollisionType.AI_PROJECTILES
            game_types_to_collide_with.extend([CollisionType.PLAYER, CollisionType.PLAYER_PROJECTILES])
            handlers_to_use[CollisionType.PLAYER] = self.collision_grid.no_handler
            handlers_to_use[CollisionType.PLAYER_PROJECTILES] = self.collision_grid.no_handler

        self.collision_shape = CollisionRect(pygame.Rect(self.world_position[0],
                                                         self.world_position[1],
                                                         self.rect.width,
                                                         int(self.rect.height*0.6)),
                                             0,
                                             handlers_to_use,
                                             collision_type,
                                             game_types_to_collide_with)
        self.collision_shape.owner = self
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)

        self.collision_shape.set_position(self.world_position)

        self.projectile_drawable_rects = projectile_drawable_rects
        self.drawable_rectangle = DrawableCollisionRect(self.collision_shape)
        self.projectile_drawable_rects.append(self.drawable_rectangle)

        self.frozen = False
        self.life_time = 15.0
        self.hit_something = False

        self.update_screen_position(camera)
        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        self.should_kill = False

    def update(self, time_delta, gravity, camera):
        self.life_time -= time_delta
        if self.life_time <= 0.0:
            self.should_kill = True

        if self.collision_shape is not None:
            if len(self.collision_shape.collided_shapes_this_frame) > 0:
                for shape in self.collision_shape.collided_shapes_this_frame:
                    if shape.game_type == CollisionType.AI or shape.game_type == CollisionType.PLAYER or shape.game_type == CollisionType.PLAYER_PROJECTILES or shape.game_type == CollisionType.AI_PROJECTILES:
                        self.should_kill = True
                    else:
                        if not self.hit_something:
                            self.hit_something = True
                            # A bunch of code here to make sure daggers stick into things in a meaty fashion
                            self.world_position[0] = self.collision_shape.x
                            self.world_position[1] = self.collision_shape.y
                            self.velocity = [0.0, 0.0]
                            embed = random.normalvariate(self.knife_embed_length, self.knife_embed_length/8)
                            self.world_position[0] += self.facing_direction[0] * embed
                            self.world_position[1] += self.facing_direction[1] * embed

                            self.collision_shape.set_position(self.world_position)

                            self.frozen = True

        if not self.frozen:
            vel_length = math.sqrt(self.velocity[0] ** 2 + self.velocity[1] ** 2)
            if vel_length == 0.0:
                vel_length = 0.0001
            self.facing_direction = [self.velocity[0] / vel_length, self.velocity[1] / vel_length]
            self.facing_angle = math.degrees(math.atan2(self.facing_direction[1], self.facing_direction[0]))
            self.image = pygame.transform.rotate(self.original_image, -self.facing_angle)
            self.rect = self.image.get_rect()
            self.collision_shape.set_rotation(math.radians(-self.facing_angle))
            self.drawable_rectangle.on_rotation()

            self.velocity[1] += gravity * time_delta

            self.world_position[0] += self.velocity[0] * time_delta
            self.world_position[1] += self.velocity[1] * time_delta

            # set the position of the collision shape
            self.collision_shape.set_position(self.world_position)

        self.update_screen_position(camera)
        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        if self.frozen and self.collision_shape is not None:
            self.collision_grid.remove_shape_from_grid(self.collision_shape)
            self.projectile_drawable_rects.remove(self.drawable_rectangle)
            self.collision_shape = None

        if self.should_kill:
            self.kill()
            if self.collision_shape is not None:
                self.collision_grid.remove_shape_from_grid(self.collision_shape)
                self.projectile_drawable_rects.remove(self.drawable_rectangle)
                self.collision_shape = None

    def update_screen_position(self, camera):
        view_top_left_position = (camera.position[0] - camera.half_width,
                                  camera.position[1] - camera.half_height)
        self.screen_position[0] = self.world_position[0] - view_top_left_position[0]
        self.screen_position[1] = self.world_position[1] - view_top_left_position[1]
示例#6
0
class Arrow(Projectile):
    def __init__(self, start_pos, initial_heading_vector, damage,
                 collision_grid, arrow_image, *groups):

        super().__init__(*groups)
        self.collision_grid = collision_grid

        self.original_image = arrow_image
        self.image = self.original_image.copy()

        self.rect = self.image.get_rect()
        self.rect.center = start_pos

        self.current_vector = [
            initial_heading_vector[0], initial_heading_vector[1]
        ]

        self.position = [
            float(self.rect.center[0]),
            float(self.rect.center[1])
        ]
        self.world_position = [
            float(self.rect.center[0]),
            float(self.rect.center[1])
        ]

        self.should_die = False

        self.bullet_speed = 300.0
        self.damage = damage

        self.shot_range = 1000.0

        self.collision_rect = CollisionRect(
            self.rect, 0, {
                GameCollisionType.MONSTER: CollisionNoHandler(),
                GameCollisionType.TILE: CollisionNoHandler()
            }, GameCollisionType.PLAYER_WEAPON,
            [GameCollisionType.MONSTER, GameCollisionType.TILE])
        self.collision_rect.set_owner(self)
        self.collision_grid.add_new_shape_to_grid(self.collision_rect)

        self.drawable_rect = DrawableCollisionRect(self.collision_rect)

    def remove_from_grid(self):
        self.collision_grid.remove_shape_from_grid(self.collision_rect)

    def react_to_collision(self):
        if not self.should_die:
            if len(self.collision_rect.collided_shapes_this_frame) > 0:
                self.should_die = True
                for shape in self.collision_rect.collided_shapes_this_frame:
                    if shape.game_type == GameCollisionType.MONSTER:
                        shape.owner.take_damage(
                            Damage(self.damage, DamageType.PHYSICAL))

    def draw_collision_shape(self, screen):
        self.drawable_rect.on_rotation()
        self.drawable_rect.update_collided_colours()
        self.drawable_rect.draw(screen)

    def calc_facing_angle_rad(self):
        direction_magnitude = math.sqrt(self.current_vector[0]**2 +
                                        self.current_vector[1]**2)
        unit_dir_vector = [0, 0]
        if direction_magnitude > 0.0:
            unit_dir_vector = [
                self.current_vector[0] / direction_magnitude,
                self.current_vector[1] / direction_magnitude
            ]
        facing_angle = math.atan2(-unit_dir_vector[0],
                                  -unit_dir_vector[1])  # *180/math.pi
        return facing_angle

    def update(self, tiled_level, time_delta):
        if not self.should_die:
            self.shot_range -= time_delta * self.bullet_speed
            self.world_position[0] += (self.current_vector[0] * time_delta *
                                       self.bullet_speed)
            self.world_position[1] += (self.current_vector[1] * time_delta *
                                       self.bullet_speed)

            self.position[
                0] = self.world_position[0] - tiled_level.position_offset[0]
            self.position[
                1] = self.world_position[1] - tiled_level.position_offset[1]
            self.rect.center = self.position

            # calc facing angle & convert to degrees
            facing_angle = self.calc_facing_angle_rad() * 180 / math.pi

            bullet_centre_position = self.rect.center
            self.image = pygame.transform.rotate(self.original_image,
                                                 facing_angle)
            self.rect = self.image.get_rect()
            self.rect.center = bullet_centre_position
            # update collision shape position and rotation
            self.collision_rect.set_rotation(facing_angle * math.pi / 180.0)
            self.collision_rect.set_position(self.world_position)

        if self.shot_range <= 0.0:
            self.should_die = True

        if self.should_die:
            self.collision_grid.remove_shape_from_grid(self.collision_rect)
            self.kill()
示例#7
0
    def __init__(self, start_position, moving_sprites_group, ui_sprites_group, collision_grid, archer_image):
        super().__init__(moving_sprites_group)
        self.type = "enemy"
        self.moving_sprites_group = moving_sprites_group
        self.ui_sprites_group = ui_sprites_group
        self.collision_grid = collision_grid

        self.atlas_image = archer_image
        self.anim_sets = {"idle_right": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 0],
                                                frame_size=[64, 96], num_frames=8, base_speed=8,
                                                centre_offset=[0, -2]),
                          "idle_left": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 0],
                                               frame_size=[64, 96], num_frames=8, base_speed=8,
                                               centre_offset=[0, -2], x_flip=True),
                          "walk_right": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 96], frame_size=[64, 96],
                                                num_frames=6, base_speed=8, centre_offset=[0, -2]),
                          "walk_left": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 96], frame_size=[64, 96],
                                               num_frames=6, base_speed=8, centre_offset=[0, -2], x_flip=True),
                          "attack_right": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 192],
                                                  frame_size=[96, 96], num_frames=6, base_speed=8,
                                                  centre_offset=[0, -2]),
                          "attack_left": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 192],
                                                 frame_size=[96, 96], num_frames=6, base_speed=8,
                                                 centre_offset=[32, -2], x_flip=True)
                          }
        self.active_anim = self.anim_sets["idle_left"]
        self.image = self.active_anim.current_frame
        self.rect = self.image.get_rect()

        self.start_position = start_position[:]
        self.world_position = [coord for coord in self.start_position]
        self.screen_position = [self.world_position[0], self.world_position[1]]

        game_types_to_collide_with = [CollisionType.WORLD_SOLID,
                                      CollisionType.WORLD_JUMP_THROUGH,
                                      CollisionType.WORLD_PLATFORM_EDGE,
                                      CollisionType.WORLD_JUMP_THROUGH_EDGE,
                                      CollisionType.PLAYER_ATTACKS,
                                      CollisionType.PLAYER_PROJECTILES]
        handlers_to_use = {CollisionType.WORLD_SOLID: collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH: collision_grid.rub_handler,
                           CollisionType.WORLD_PLATFORM_EDGE: collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH_EDGE: collision_grid.rub_handler,
                           CollisionType.PLAYER_ATTACKS: collision_grid.no_handler,
                           CollisionType.PLAYER_PROJECTILES: collision_grid.no_handler}

        self.collision_shape_offset = [0, 0]
        self.collision_shape = CollisionRect(pygame.Rect(self.world_position[0],
                                                         self.world_position[1],
                                                         self.rect.width,
                                                         self.rect.height),
                                             0,
                                             handlers_to_use,
                                             CollisionType.AI,
                                             game_types_to_collide_with)
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)
        self.collision_shape.owner = self

        self.base_health = 100
        self.current_health = self.base_health

        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        self.velocity = [0.0, 0.0]
        self.motion_state = "idle"

        self.frames_falling = 0
        self.touching_ground = False
        self.move_speed = 0.0

        self.x_facing_direction = "left"
        self.changed_direction_recently = False

        self.health_ui = UIEnemyHealthBar(self, self.ui_sprites_group)

        # debug draw
        self.drawable_collision_rectangle = DrawableCollisionRect(self.collision_shape)

        # view cone params
        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        self.view_cone = ViewCone(self.world_position, facing_vec, fov=30.0, length=400.0)
        self.collision_grid.add_new_shape_to_grid(self.view_cone.collision_circle)
        self.visible_enemies = None
        self.closest_visible_enemy = None

        arrow_image_position = [576, 0]
        arrow_image_dimensions = [46, 7]
        arrow_image_rect = pygame.Rect(arrow_image_position, arrow_image_dimensions)
        self.arrow_image = self.atlas_image.subsurface(arrow_image_rect).convert_alpha()
        self.time_since_last_saw_enemy = 0
        self.enemy_spotted_timeout = 3.0
        self.is_time_to_fire = False
        self.fire_time = 1.0
        self.fire_timer = 0.0
        self.active_arrows = []
        self.projectile_drawable_rects = []

        self.has_moved = False
        self.disable_movement = False

        self.sprite_flash_acc = 0.0
        self.sprite_flash_time = 0.25
        self.should_flash_sprite = False
        self.active_flash_sprite = False
        self.last_impact_direction_vec = None
        self.direction_change_time = 1.0
        self.direction_change_timer = 0.0
        self.impact_velocity = None
        self.impact_time = 0.1
        self.impact_timer = 0.0

        self.last_frame_x_pos = self.world_position[0]
        self.last_collision_shape_x = self.collision_shape.x

        self.collision_print_time = 1.0
        self.collision_print_timer = 0.0

        # debuggers
        self.start_frame_collision_shape_x = self.collision_shape.x
        self.pre_collision_gets_moved_x = self.collision_shape.x
        self.post_collision_gets_moved_x = self.collision_shape.x

        self.alive = True
示例#8
0
class EnemyArcher(pygame.sprite.Sprite):
    """
    An enemy type for the Vania game. This guy is going to stroll about from left to right and back again on a platform,
    then, if he sees the player, shoot an arrow in that general direction.
    """
    def __init__(self, start_position, moving_sprites_group, ui_sprites_group, collision_grid, archer_image):
        super().__init__(moving_sprites_group)
        self.type = "enemy"
        self.moving_sprites_group = moving_sprites_group
        self.ui_sprites_group = ui_sprites_group
        self.collision_grid = collision_grid

        self.atlas_image = archer_image
        self.anim_sets = {"idle_right": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 0],
                                                frame_size=[64, 96], num_frames=8, base_speed=8,
                                                centre_offset=[0, -2]),
                          "idle_left": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 0],
                                               frame_size=[64, 96], num_frames=8, base_speed=8,
                                               centre_offset=[0, -2], x_flip=True),
                          "walk_right": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 96], frame_size=[64, 96],
                                                num_frames=6, base_speed=8, centre_offset=[0, -2]),
                          "walk_left": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 96], frame_size=[64, 96],
                                               num_frames=6, base_speed=8, centre_offset=[0, -2], x_flip=True),
                          "attack_right": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 192],
                                                  frame_size=[96, 96], num_frames=6, base_speed=8,
                                                  centre_offset=[0, -2]),
                          "attack_left": AnimSet(atlas_surface=self.atlas_image, start_pos=[0, 192],
                                                 frame_size=[96, 96], num_frames=6, base_speed=8,
                                                 centre_offset=[32, -2], x_flip=True)
                          }
        self.active_anim = self.anim_sets["idle_left"]
        self.image = self.active_anim.current_frame
        self.rect = self.image.get_rect()

        self.start_position = start_position[:]
        self.world_position = [coord for coord in self.start_position]
        self.screen_position = [self.world_position[0], self.world_position[1]]

        game_types_to_collide_with = [CollisionType.WORLD_SOLID,
                                      CollisionType.WORLD_JUMP_THROUGH,
                                      CollisionType.WORLD_PLATFORM_EDGE,
                                      CollisionType.WORLD_JUMP_THROUGH_EDGE,
                                      CollisionType.PLAYER_ATTACKS,
                                      CollisionType.PLAYER_PROJECTILES]
        handlers_to_use = {CollisionType.WORLD_SOLID: collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH: collision_grid.rub_handler,
                           CollisionType.WORLD_PLATFORM_EDGE: collision_grid.rub_handler,
                           CollisionType.WORLD_JUMP_THROUGH_EDGE: collision_grid.rub_handler,
                           CollisionType.PLAYER_ATTACKS: collision_grid.no_handler,
                           CollisionType.PLAYER_PROJECTILES: collision_grid.no_handler}

        self.collision_shape_offset = [0, 0]
        self.collision_shape = CollisionRect(pygame.Rect(self.world_position[0],
                                                         self.world_position[1],
                                                         self.rect.width,
                                                         self.rect.height),
                                             0,
                                             handlers_to_use,
                                             CollisionType.AI,
                                             game_types_to_collide_with)
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)
        self.collision_shape.owner = self

        self.base_health = 100
        self.current_health = self.base_health

        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        self.velocity = [0.0, 0.0]
        self.motion_state = "idle"

        self.frames_falling = 0
        self.touching_ground = False
        self.move_speed = 0.0

        self.x_facing_direction = "left"
        self.changed_direction_recently = False

        self.health_ui = UIEnemyHealthBar(self, self.ui_sprites_group)

        # debug draw
        self.drawable_collision_rectangle = DrawableCollisionRect(self.collision_shape)

        # view cone params
        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        self.view_cone = ViewCone(self.world_position, facing_vec, fov=30.0, length=400.0)
        self.collision_grid.add_new_shape_to_grid(self.view_cone.collision_circle)
        self.visible_enemies = None
        self.closest_visible_enemy = None

        arrow_image_position = [576, 0]
        arrow_image_dimensions = [46, 7]
        arrow_image_rect = pygame.Rect(arrow_image_position, arrow_image_dimensions)
        self.arrow_image = self.atlas_image.subsurface(arrow_image_rect).convert_alpha()
        self.time_since_last_saw_enemy = 0
        self.enemy_spotted_timeout = 3.0
        self.is_time_to_fire = False
        self.fire_time = 1.0
        self.fire_timer = 0.0
        self.active_arrows = []
        self.projectile_drawable_rects = []

        self.has_moved = False
        self.disable_movement = False

        self.sprite_flash_acc = 0.0
        self.sprite_flash_time = 0.25
        self.should_flash_sprite = False
        self.active_flash_sprite = False
        self.last_impact_direction_vec = None
        self.direction_change_time = 1.0
        self.direction_change_timer = 0.0
        self.impact_velocity = None
        self.impact_time = 0.1
        self.impact_timer = 0.0

        self.last_frame_x_pos = self.world_position[0]
        self.last_collision_shape_x = self.collision_shape.x

        self.collision_print_time = 1.0
        self.collision_print_timer = 0.0

        # debuggers
        self.start_frame_collision_shape_x = self.collision_shape.x
        self.pre_collision_gets_moved_x = self.collision_shape.x
        self.post_collision_gets_moved_x = self.collision_shape.x

        self.alive = True

    def shutdown(self):
        if self.alive:
            self.kill()
            self.health_ui.kill()
            self.collision_grid.remove_shape_from_grid(self.collision_shape)
            self.collision_grid.remove_shape_from_grid(self.view_cone.collision_circle)
            self.drawable_collision_rectangle = None
            self.alive = False

    def draw_debug_info(self, screen, camera):
        if self.drawable_collision_rectangle is not None:
            self.drawable_collision_rectangle.update_collided_colours()
            self.drawable_collision_rectangle.draw(screen, camera.position, (camera.half_width,
                                                                             camera.half_height))
            self.view_cone.draw(screen, camera)

    def update_screen_position(self, camera):
        view_top_left_position = (camera.position[0] - camera.half_width,
                                  camera.position[1] - camera.half_height)
        self.screen_position[0] = self.world_position[0] - view_top_left_position[0]
        self.screen_position[1] = self.world_position[1] - view_top_left_position[1]

    def update_animation(self, time_delta):
        # set the animation name based on motion and direction
        anim_name = self.motion_state + "_"
        anim_name += self.x_facing_direction
        # if this is a new animation, run the start function
        if self.active_anim != self.anim_sets[anim_name]:
            self.active_anim = self.anim_sets[anim_name]
            self.active_anim.start()
        # update the animation & set the sprite image to the current animation frame
        self.active_anim.update(time_delta)
        self.image = self.active_anim.current_frame

    def update_movement(self, time_delta, camera):
        self.has_moved = False
        if self.closest_visible_enemy is not None and self.touching_ground:
            self.velocity[0] = 0.0
            on_left = (self.closest_visible_enemy.world_position[0] - self.world_position[0]) < 0.0
            if on_left:
                self.motion_state = "attack"
                self.x_facing_direction = "left"
            else:
                self.motion_state = "attack"
                self.x_facing_direction = "right"

            if self.is_time_to_fire:
                self.is_time_to_fire = False
                self.fire_timer = 0.0
                if self.x_facing_direction == "left":
                    firing_direction = [-0.90, -0.1]
                else:
                    firing_direction = [0.90, -0.1]

                arrow = ThrowingKnife(self.moving_sprites_group, self.collision_grid, self.arrow_image,
                                      self.world_position, firing_direction, self.projectile_drawable_rects,
                                      camera, is_player_weapon=False, speed=1500.0)
                self.active_arrows.append(arrow)
            else:
                self.fire_timer += time_delta
                if self.fire_timer >= self.fire_time:
                    self.is_time_to_fire = True
        elif self.touching_ground:
            self.has_moved = True
            if self.x_facing_direction == "left":
                self.motion_state = "walk"
                self.velocity[0] = -100.0
            elif self.x_facing_direction == "right":
                self.motion_state = "walk"
                self.velocity[0] = 100.0
        else:
            self.motion_state = "idle"

    def in_range_and_in_front(self, sprite):
        x_dist = sprite.world_position[0] - self.world_position[0]
        y_dist = sprite.world_position[1] - self.world_position[1]
        distance_squared = x_dist ** 2 + y_dist ** 2
        if distance_squared >= self.view_cone.length_squared:
            return False

        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        dot = facing_vec[0] * x_dist + facing_vec[1] * y_dist
        if dot < 0:
            return False

        vector_to_point = [x_dist, y_dist]

        cross_1 = self.view_cone.cone_extent_facings[0][0] * vector_to_point[1] - self.view_cone.cone_extent_facings[0][1] * vector_to_point[0]
        cross_2 = self.view_cone.cone_extent_facings[1][0] * vector_to_point[1] - self.view_cone.cone_extent_facings[1][1] * vector_to_point[0]

        if cross_1 < 0 < cross_2 or cross_1 > 0 > cross_2:
            return True
        else:
            return False

    def update_visible_enemies(self, time_delta):
        if self.closest_visible_enemy is not None:
            self.time_since_last_saw_enemy += time_delta
        if self.time_since_last_saw_enemy > self.enemy_spotted_timeout:
            self.closest_visible_enemy = None

        enemies = [sprite for sprite in self.moving_sprites_group.sprites()
                   if sprite.type == "player" and self.in_range_and_in_front(sprite)]

        if len(enemies) > 0 and self.has_moved:
            self.view_cone.set_position(pygame.math.Vector2(self.world_position))
            if self.x_facing_direction == "right":
                facing_vec = [1.0, 0.0]
            else:
                facing_vec = [-1.0, 0.0]
            self.view_cone.set_facing_direction(pygame.math.Vector2(facing_vec))
            self.view_cone.update()
            self.visible_enemies = [sprite for sprite in enemies if
                                    self.view_cone.is_subject_visible(pygame.math.Vector2(sprite.world_position)) and
                                    sprite.current_health > 0]
            if len(self.visible_enemies) > 0:
                self.closest_visible_enemy = self.find_closest(self.visible_enemies)
                self.time_since_last_saw_enemy = 0
        else:
            self.view_cone.clear()

    def find_closest(self, sprites):
        closest_distance = 9999999999999999.0
        closest_sprite = None
        for sprite in sprites:
            x_dist = self.world_position[0] - sprite.world_position[0]
            y_dist = self.world_position[1] - sprite.world_position[1]
            squared_dist = x_dist ** 2 + y_dist ** 2
            if squared_dist < closest_distance:
                closest_distance = squared_dist
                closest_sprite = sprite
        return closest_sprite

    def update(self, time_delta, gravity, camera):

        floor_collided_this_frame = False
        impact_velocity = None
        hit_wall = False
        mtv_vector = None

        if len(self.collision_shape.collided_shapes_this_frame) > 0:
            for shape in self.collision_shape.collided_shapes_this_frame:
                if shape.game_type == CollisionType.PLAYER_PROJECTILES:
                    if not self.should_flash_sprite:  # invincible for flash time secs (0.5) after taking damage
                        self.take_damage(15)
                        # turn to face damage source
                        self.last_impact_direction_vec = pygame.math.Vector2(shape.owner.velocity[0],
                                                                             shape.owner.velocity[1])
                        self.last_impact_direction_vec.normalize_ip()
                        impact_velocity = self.apply_knockback(shape)

                if shape.game_type == CollisionType.PLAYER_ATTACKS:
                    if not self.should_flash_sprite:  # invincible for flash time secs (0.5) after taking damage
                        self.take_damage(15)
                        # turn to face damage source
                        self.last_impact_direction_vec = pygame.math.Vector2(self.world_position[0] - shape.owner.world_position[0],
                                                                             self.world_position[1] - shape.owner.world_position[1])
                        if self.last_impact_direction_vec.length_squared() > 0:
                            self.last_impact_direction_vec.normalize_ip()
                        impact_velocity = self.apply_knockback(shape)

                if shape.game_type == CollisionType.WORLD_PLATFORM_EDGE or shape.game_type == CollisionType.WORLD_JUMP_THROUGH_EDGE:
                    # slightly tricksy bit of code here to detect when are at a left or right platform edge
                    # relies on the edges being tagged up using a special tile type and some poking at the normals
                    # to figure out if it is a left or right platform edge. This method won't work on single block
                    # platforms but that's probably OK, AI can just fall off those.
                    if not self.changed_direction_recently and self.motion_state == "walk":
                        if shape.y > self.collision_shape.aabb_rect.bottom:  # test we are walking on top of this edge
                            if self.x_facing_direction == "left":
                                if self.collision_shape.x < shape.x and not shape.normals['left'].should_skip:
                                    self.collision_shape.set_position([shape.x, self.collision_shape.y])
                                    self.changed_direction_recently = True
                                    self.x_facing_direction = "right"
                            elif self.x_facing_direction == "right":
                                if self.collision_shape.x > shape.x and not shape.normals['right'].should_skip:
                                    self.collision_shape.set_position([shape.x, self.collision_shape.y])
                                    self.changed_direction_recently = True
                                    self.x_facing_direction = "left"

                if shape.game_type == CollisionType.WORLD_SOLID or \
                        shape.game_type == CollisionType.WORLD_PLATFORM_EDGE or \
                        shape.game_type == CollisionType.WORLD_JUMP_THROUGH or \
                        shape.game_type == CollisionType.WORLD_JUMP_THROUGH_EDGE:
                    # see if we have an upwards facing mtv vector
                    mtv_vector = self.collision_shape.get_frame_mtv_vector(shape)
                    if mtv_vector is not None:
                        if abs(mtv_vector[1]) > abs(mtv_vector[0]) and mtv_vector[1] < 0:
                            floor_collided_this_frame = True
                            self.velocity[1] = 0.0
                            self.frames_falling = 0
                            self.touching_ground = True
                        # see if we have a sideways facing mtv vector
                        if abs(mtv_vector[0]) > abs(mtv_vector[1]):
                            hit_wall = True
                            self.velocity[0] = 0.0
                            # makes AI change direction if they hit a wall.
                            if not self.changed_direction_recently:
                                self.changed_direction_recently = True
                                if self.x_facing_direction == "left" and mtv_vector[0] > 0:
                                    self.x_facing_direction = "right"
                                elif self.x_facing_direction == "right" and mtv_vector[0] < 0:
                                    self.x_facing_direction = "left"

                    #if not self.touching_ground:
                    #    print("mid air collision")
                    #    self.velocity[0] = 0.0
                    #    self.velocity[1] = 0.0
                    #    self.collision_shape.x = self.world_position[0]
                    #    self.collision_shape.y = self.world_position[1]

        if not floor_collided_this_frame:
            if self.touching_ground and self.frames_falling > 10:
                self.frames_falling = 0
                self.touching_ground = False
            self.frames_falling += 1

        if len(self.collision_shape.collided_shapes_this_frame) > 0:
            self.world_position[0] = self.collision_shape.x - self.collision_shape_offset[0]
            self.world_position[1] = self.collision_shape.y - self.collision_shape_offset[1]

        if not floor_collided_this_frame:
            self.velocity[1] += gravity * time_delta

        self.update_impact_reactions(hit_wall, impact_velocity, time_delta)

        velocity_delta_vec = pygame.math.Vector2(self.velocity[0] * time_delta,
                                                 self.velocity[1] * time_delta)
        if velocity_delta_vec.length() < 32.0:
            self.world_position[0] += self.velocity[0] * time_delta
            self.world_position[1] += self.velocity[1] * time_delta
        else:
            print("clamping velocity")
            velocity_delta_vec = velocity_delta_vec.normalize_ip() * 32.0
            self.velocity[0] = velocity_delta_vec.x / time_delta
            self.velocity[1] = velocity_delta_vec.y / time_delta

        if not self.disable_movement:
            self.update_movement(time_delta, camera)
        self.update_animation(time_delta)
        self.update_sprite(time_delta)
        self.update_visible_enemies(time_delta)

        if self.changed_direction_recently:
            if self.direction_change_timer < self.direction_change_time:
                self.direction_change_timer += time_delta
            else:
                self.changed_direction_recently = False
                self.direction_change_timer = 0.0

        # set the position of the collision shape
        self.collision_shape.set_position([self.world_position[0] + self.collision_shape_offset[0],
                                           self.world_position[1] + self.collision_shape_offset[1]])

        self.update_screen_position(camera)
        self.rect.centerx = int(self.screen_position[0] - self.active_anim.centre_offset[0])
        self.rect.centery = int(self.screen_position[1] - self.active_anim.centre_offset[1])

        if self.current_health <= 0:
            self.shutdown()

    def update_impact_reactions(self, hit_wall, impact_velocity, time_delta):
        if impact_velocity is not None:
            self.touching_ground = False
            # self.disable_movement = True
            self.velocity[0] = 0.0
            self.velocity[1] = 0.0
            self.impact_velocity = impact_velocity
            self.impact_timer = 0.0
        if not hit_wall and not self.touching_ground and self.impact_velocity is not None:
            self.velocity[0] += self.impact_velocity.x * time_delta
            self.velocity[1] += self.impact_velocity.y * time_delta
            if self.impact_timer < self.impact_time:
                self.impact_timer += time_delta
            else:
                self.impact_velocity[0] *= 0.8
                self.impact_velocity[1] *= 0.8
        if self.touching_ground and self.last_impact_direction_vec is not None:
            if not self.changed_direction_recently:
                self.turn_to_face_direction(self.last_impact_direction_vec)
            self.last_impact_direction_vec = None
            # self.disable_movement = False
            self.impact_velocity = None

    def debug_knockback(self, post_collision_move_x, pre_collision_move_x):
        if abs(self.world_position[0] - self.last_frame_x_pos) > 4.0:
            print("LARGE x MOVE:", abs(self.world_position[0] - self.last_frame_x_pos))
            print("self.velocity:", self.velocity)
            print("collision shape x move:", abs(self.collision_shape.x - self.last_collision_shape_x))
            print("pre_collision_move_world_pos_x:", pre_collision_move_x)
            print("post_collision_move_world_pos_x", post_collision_move_x)
            # print("mtv_vector:", mtv_vector)
            for shape in self.collision_shape.collided_shapes_this_frame:
                print("shape:", shape.x, shape.y)
            print("\n")
            print("self.start_frame_collision_shape_x", self.start_frame_collision_shape_x)
            print("self.pre_collision_gets_moved_x", self.pre_collision_gets_moved_x)
            print("self.post_collision_gets_moved_x", self.post_collision_gets_moved_x)

    def apply_knockback(self, shape):
        impact_velocity = pygame.math.Vector2(self.last_impact_direction_vec.x * 500.0,
                                              (self.last_impact_direction_vec.y * 300.0) - 1200.0)
        return impact_velocity

    def turn_to_face_direction(self, direction):
        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        dot = facing_vec[0] * direction.x + facing_vec[1] * direction.y
        if dot > 0 and self.x_facing_direction == "right":
            self.x_facing_direction = "left"
            self.has_moved = True
        elif dot > 0 and self.x_facing_direction == "left":
            self.x_facing_direction = "right"
            self.has_moved = True

    def update_sprite(self, time_delta):
        if self.should_flash_sprite and not self.current_health <= 0:
            self.sprite_flash_acc += time_delta
            if self.sprite_flash_acc > self.sprite_flash_time:
                self.sprite_flash_acc = 0.0
                self.should_flash_sprite = False

            else:
                lerp_value = self.sprite_flash_acc / self.sprite_flash_time
                flash_alpha = self.lerp(255, 0, lerp_value)
                flash_image = self.active_anim.current_frame.copy()
                flash_sprite = self.active_anim.current_frame.copy()
                flash_image.fill((255, 255, 255), None, pygame.BLEND_RGB_ADD)
                flash_image.fill((255, 255, 255, flash_alpha), None, pygame.BLEND_RGBA_MULT)
                flash_sprite.blit(flash_image, (0, 0))
                self.image = flash_sprite

                if not self.active_flash_sprite:
                    self.active_flash_sprite = True
        else:
            self.image = self.active_anim.current_frame

    def take_damage(self, damage):
        self.current_health -= damage
        if self.current_health < 0:
            self.current_health = 0
        self.should_flash_sprite = True
        self.sprite_flash_acc = 0.0

    @staticmethod
    def lerp(a, b, c):
        return (c * b) + ((1.0 - c) * a)
示例#9
0
class Missile(Projectile):
    def __init__(self, start_pos, initial_heading_vector, current_target,
                 target_position, damage, speed, explosions_sprite_sheet,
                 image_atlas, homing_radius, collision_grid, *groups):

        super().__init__(*groups)
        self.explosions_sprite_sheet = explosions_sprite_sheet
        self.original_image = image_atlas.subsurface((0, 256, 6, 12))

        self.current_vector = [
            initial_heading_vector[0], initial_heading_vector[1]
        ]
        self.target_vector = self.current_vector
        self.position = [float(start_pos[0]), float(start_pos[1])]

        facing_angle = math.atan2(-self.current_vector[0],
                                  -self.current_vector[1]) * 180 / math.pi
        self.image = pygame.transform.rotate(self.original_image, facing_angle)

        self.rect = self.image.get_rect()
        self.rect.center = start_pos

        self.collision_grid = collision_grid
        handlers_by_type = {
            GameCollisionType.MONSTER: self.collision_grid.no_handler
        }
        self.collision_shape = CollisionRect(
            self.original_image.get_rect(),
            math.atan2(-self.current_vector[0],
                       -self.current_vector[1]), handlers_by_type,
            GameCollisionType.TURRET_PROJECTILE, [GameCollisionType.MONSTER])
        self.collision_shape.set_owner(self)
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)

        self.drawable_collision_rect = DrawableCollisionRect(
            self.collision_shape)

        self.should_die = False

        self.projectile_speed = speed
        self.damage = damage

        self.target_position = target_position
        x_diff = self.target_position[0] - self.position[0]
        y_diff = self.target_position[1] - self.position[1]
        total_target_dist = math.sqrt(x_diff**2 + y_diff**2)
        self.target_distance = total_target_dist + 16.0
        self.shot_range = 600.0

        self.rotate_speed = 10.0

        self.time_to_home_in = False
        self.homing_time_acc = 0.0
        self.homing_time = 0.2
        self.homing_radius = homing_radius

        self.current_target = current_target

    def update_sprite(self, all_bullet_sprites):
        all_bullet_sprites.add(self)
        return all_bullet_sprites

    def draw_collision_rect(self, screen):
        self.drawable_collision_rect.update_collided_colours()
        self.drawable_collision_rect.draw(screen)

    def react_to_collision(self):
        for shape in self.collision_shape.collided_shapes_this_frame:
            if shape.game_type == GameCollisionType.MONSTER:
                self.should_die = True

    def update_movement_and_collision(self, monsters, time_delta,
                                      new_explosions, explosions):
        if self.homing_time_acc < self.homing_time:
            self.homing_time_acc += time_delta
        else:
            self.time_to_home_in = True

        if self.time_to_home_in:
            self.homing_time_acc = 0.0
            self.time_to_home_in = False
            if self.current_target is None or self.current_target.should_die:
                self.current_target, self.target_distance = self.get_closest_monster_in_radius(
                    monsters)
            if self.current_target is not None:
                self.target_distance = self.calc_distance_to_target(
                    self.current_target)
                self.target_vector = self.calculate_aiming_vector(
                    self.current_target, self.target_distance)

        relative_angle = self.rotate_current_angle_to_target(time_delta)

        self.shot_range -= time_delta * self.projectile_speed
        self.position[0] += (self.current_vector[0] * time_delta *
                             self.projectile_speed)
        self.position[1] += (self.current_vector[1] * time_delta *
                             self.projectile_speed)
        self.rect.center = (int(self.position[0]), int(self.position[1]))

        self.collision_shape.set_position(self.position)

        if self.shot_range <= 0.0:
            self.should_die = True

        if relative_angle != 0.0:
            direction_magnitude = math.sqrt(self.current_vector[0]**2 +
                                            self.current_vector[1]**2)
            unit_dir_vector = [0, 0]
            if direction_magnitude > 0.0:
                unit_dir_vector = [
                    self.current_vector[0] / direction_magnitude,
                    self.current_vector[1] / direction_magnitude
                ]
            facing_angle = math.atan2(-unit_dir_vector[0],
                                      -unit_dir_vector[1]) * 180 / math.pi

            bullet_centre_position = self.rect.center
            self.image = pygame.transform.rotate(self.original_image,
                                                 facing_angle)
            self.rect = self.image.get_rect()
            self.rect.center = bullet_centre_position

            self.collision_shape.set_rotation(
                math.atan2(-unit_dir_vector[0], -unit_dir_vector[1]))

        if self.should_die:
            self.collision_grid.remove_shape_from_grid(self.collision_shape)
            # explode at front of missile
            explosion_pos = [0.0, 0.0]
            explosion_pos[0] = self.position[0] + (self.current_vector[0] *
                                                   7.0)
            explosion_pos[1] = self.position[1] + (self.current_vector[1] *
                                                   7.0)
            new_explosion = Explosion(self.position,
                                      self.explosions_sprite_sheet, 12,
                                      self.damage, DamageType.MISSILE,
                                      self.collision_grid)
            new_explosions.append(new_explosion)
            explosions.append(new_explosion)

    def calc_distance_to_target(self, target):
        x_dist = self.position[0] - target.position[0]
        y_dist = self.position[1] - target.position[1]
        current_dist = math.sqrt((x_dist**2) + (y_dist**2))
        # re-adjust distance to our anticipated position when projectiles reach target
        time_to_reach_target = current_dist / self.projectile_speed
        guess_position = target.guess_position_at_time(time_to_reach_target)
        x_dist = guess_position[0] - self.position[0]
        y_dist = guess_position[1] - self.position[1]
        guess_dist = math.sqrt((x_dist**2) + (y_dist**2))
        return guess_dist

    def get_closest_monster_in_radius(self, monsters):
        closest_monster_distance = 100000.0
        closest_monster_in_radius = None
        for monster in monsters:
            guess_dist = self.calc_distance_to_target(monster)
            if guess_dist < self.homing_radius:
                if guess_dist < closest_monster_distance:
                    closest_monster_distance = guess_dist
                    closest_monster_in_radius = monster
        return closest_monster_in_radius, closest_monster_distance

    def calculate_aiming_vector(self, target, distance):
        time_to_reach_target = distance / self.projectile_speed
        guess_position = target.guess_position_at_time(time_to_reach_target)
        x_direction = guess_position[0] - self.position[0]
        y_direction = guess_position[1] - self.position[1]
        distance = math.sqrt((x_direction**2) + (y_direction**2))
        return [x_direction / distance, y_direction / distance]

    def rotate_current_angle_to_target(self, time_delta):
        relative_angle = 0.0
        if self.target_vector[0] != self.current_vector[
                0] or self.target_vector[0] != self.current_vector[0]:
            current_angle = math.atan2(self.current_vector[1],
                                       self.current_vector[0])
            target_angle = math.atan2(self.target_vector[1],
                                      self.target_vector[0])
            relative_angle = target_angle - current_angle

            if abs(relative_angle) < 0.01:
                self.current_vector[0] = self.target_vector[0]
                self.current_vector[1] = self.target_vector[1]
            else:
                if relative_angle > math.pi:
                    relative_angle = relative_angle - (2 * math.pi)
                if relative_angle < -math.pi:
                    relative_angle = relative_angle + (2 * math.pi)

                if relative_angle > 0:
                    current_angle += (time_delta * self.rotate_speed)
                else:
                    current_angle -= (time_delta * self.rotate_speed)

                self.current_vector[0] = math.cos(current_angle)
                self.current_vector[1] = math.sin(current_angle)

        return relative_angle
示例#10
0
文件: player.py 项目: MyreMylar/vania
    def update_jumping(self, time_delta, camera):
        if self.motion_state == "jump_attack":
            if self.active_anim.frame_index == 3 and self.melee_collision_shape is None:
                attack_position = [
                    self.world_position[0], self.world_position[1]
                ]
                if self.x_facing_direction == "left":
                    attack_position[0] -= 64
                attack_dimensions = [64, 16]
                game_types_to_collide_with = [CollisionType.AI]
                handlers_to_use = {
                    CollisionType.AI: self.collision_grid.no_handler
                }
                self.melee_collision_shape = CollisionRect(
                    pygame.Rect(attack_position[0], attack_position[1],
                                attack_dimensions[0], attack_dimensions[1]), 0,
                    handlers_to_use, CollisionType.PLAYER_ATTACKS,
                    game_types_to_collide_with)
                self.collision_grid.add_new_shape_to_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape.owner = self

                self.melee_attack_collision_drawable_rectangle = DrawableCollisionRect(
                    self.melee_collision_shape)
            elif self.active_anim.frame_index > 3 and self.melee_collision_shape is not None:
                self.collision_grid.remove_shape_from_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape = None
            if not self.active_anim.running:
                self.motion_state = "jump"
        elif self.motion_state == "jump_throw":
            if self.active_anim.frame_index == 3 and not self.thrown_knife:
                self.thrown_knife = True
                if self.closest_visible_enemy is not None:
                    x_dir = self.closest_visible_enemy.world_position[
                        0] - self.world_position[0]
                    y_dir = self.closest_visible_enemy.world_position[
                        1] - self.world_position[1]
                    distance = math.sqrt(x_dir**2 + y_dir**2)
                    throwing_direction = [x_dir / distance, y_dir / distance]
                    # knives travel at roughly 1000.0 pixels a second, gravity is 800 pixels down per second
                    # so we need to correct the direction vector based on how far away the target is.
                    # after 1000 distance we will have descended roughly 800 from our target position

                    # calculated by assuming traveling in a straight line to the right to a point 1000.0 away
                    gravity_offset_vec = [0.0, -0.625 * distance / 1000.0]
                    throwing_direction = [
                        throwing_direction[0] + gravity_offset_vec[0],
                        throwing_direction[1] + gravity_offset_vec[1]
                    ]
                    # renormalise direction
                    throwing_dir_len = math.sqrt(throwing_direction[0]**2 +
                                                 throwing_direction[1]**2)
                    throwing_direction = [
                        throwing_direction[0] / throwing_dir_len,
                        throwing_direction[1] / throwing_dir_len
                    ]

                else:
                    if self.x_facing_direction == 'left':
                        throwing_direction = [
                            -0.98, -0.19 + random.normalvariate(0, 0.02)
                        ]
                    else:
                        throwing_direction = [
                            0.98, -0.19 + random.normalvariate(0, 0.02)
                        ]
                knife = ThrowingKnife(self.moving_sprites_group,
                                      self.collision_grid, self.knife_image,
                                      self.world_position, throwing_direction,
                                      self.projectile_drawable_rects, camera)
                self.active_knives.append(knife)
            if not self.active_anim.running:
                self.thrown_knife = False
                self.motion_state = "jump"
        else:
            self.motion_state = "jump"
        if self.move_left:
            self.move_speed -= self.air_acceleration * time_delta
            if self.move_speed < -self.air_top_speed:
                self.move_speed = -self.air_top_speed

            self.velocity[0] = self.move_speed
            self.x_facing_direction = "left"
            speed_factor = abs(self.move_speed / self.air_top_speed)
            self.active_anim.set_speed_factor(speed_factor)
        elif self.move_right:
            self.move_speed += self.air_acceleration * time_delta
            if self.move_speed > self.air_top_speed:
                self.move_speed = self.air_top_speed
            self.velocity[0] = self.move_speed
            self.x_facing_direction = "right"
            speed_factor = abs(self.move_speed / self.air_top_speed)
            self.active_anim.set_speed_factor(speed_factor)
示例#11
0
文件: player.py 项目: MyreMylar/vania
    def __init__(self, moving_sprites_group, ui_sprites_group, fonts,
                 collision_grid, camera):
        super().__init__(moving_sprites_group)
        self.type = "player"
        self.moving_sprites_group = moving_sprites_group
        self.ui_sprites_group = ui_sprites_group
        self.fonts = fonts
        self.collision_grid = collision_grid
        self.camera = camera
        self.atlas_image = pygame.image.load("images/player.png")
        self.anim_sets = {
            "idle_right":
            AnimSet(self.atlas_image, [0, 96], [64, 96], 10, 10, [0, -1]),
            "idle_left":
            AnimSet(self.atlas_image, [0, 96], [64, 96],
                    10,
                    10, [0, -1],
                    looping=True,
                    x_flip=True),
            "run_right":
            AnimSet(self.atlas_image, [0, 0], [64, 96], 10, 30, [0, -1]),
            "run_left":
            AnimSet(self.atlas_image, [0, 0], [64, 96],
                    10,
                    30, [0, -1],
                    looping=True,
                    x_flip=True),
            "jump_right":
            AnimSet(self.atlas_image, [0, 192], [64, 96],
                    10,
                    8, [0, -1],
                    looping=False,
                    x_flip=False),
            "jump_left":
            AnimSet(self.atlas_image, [0, 192], [64, 96],
                    10,
                    8, [0, -1],
                    looping=False,
                    x_flip=True),
            "jump_attack_right":
            AnimSet(self.atlas_image, [0, 770], [96, 96],
                    10,
                    40, [0, -1],
                    looping=False,
                    x_flip=False),
            "jump_attack_left":
            AnimSet(self.atlas_image, [0, 770], [96, 96],
                    10,
                    40, [32, -1],
                    looping=False,
                    x_flip=True),
            "jump_throw_right":
            AnimSet(self.atlas_image, [0, 866], [96, 96],
                    10,
                    40, [0, -1],
                    looping=False,
                    x_flip=False),
            "jump_throw_left":
            AnimSet(self.atlas_image, [0, 866], [96, 96],
                    10,
                    40, [32, -1],
                    looping=False,
                    x_flip=True),
            "throw_right":
            AnimSet(self.atlas_image, [0, 288], [64, 96], 10, 40, [0, -1],
                    False, False),
            "throw_left":
            AnimSet(self.atlas_image, [0, 288], [64, 96], 10, 40, [0, -1],
                    False, True),
            "attack_right":
            AnimSet(self.atlas_image, [0, 384], [96, 96], 10, 60, [0, -1],
                    False, False),
            "attack_left":
            AnimSet(self.atlas_image, [0, 384], [96, 96], 10, 60, [32, -1],
                    False, True),
            "die_right":
            AnimSet(self.atlas_image, [0, 480], [96, 96], 10, 20, [0, -1],
                    False, False),
            "die_left":
            AnimSet(self.atlas_image, [0, 480], [96, 96], 10, 20, [32, -1],
                    False, True),
            "slide_right":
            AnimSet(self.atlas_image, [0, 576], [96, 96], 10, 15, [0, -1],
                    False, False),
            "slide_left":
            AnimSet(self.atlas_image, [0, 576], [96, 96], 10, 15, [32, -1],
                    False, True),
            "climb_up":
            AnimSet(self.atlas_image, [0, 672], [64, 96], 10, 30, [0, -1]),
            "climb_down":
            AnimSet(self.atlas_image, [0, 672], [64, 96], 10, 30, [0, -1],
                    True, True)
        }

        # load the knife image
        knife_image_position = [0, 960]
        knife_image_dimensions = [30, 6]
        knife_image_rect = pygame.Rect(knife_image_position,
                                       knife_image_dimensions)
        self.knife_image = self.atlas_image.subsurface(
            knife_image_rect).convert_alpha()

        self.active_anim = self.anim_sets["idle_right"]
        self.image = self.active_anim.current_frame

        self.rect = self.image.get_rect()

        self.start_position = (316.0, 596.0)
        self.world_position = [coord for coord in self.start_position]
        self.screen_position = [self.world_position[0], self.world_position[1]]

        game_types_to_collide_with = [
            CollisionType.WORLD_SOLID, CollisionType.WORLD_PLATFORM_EDGE,
            CollisionType.WORLD_JUMP_THROUGH,
            CollisionType.WORLD_JUMP_THROUGH_EDGE, CollisionType.AI_ATTACKS,
            CollisionType.AI_PROJECTILES
        ]
        handlers_to_use = {
            CollisionType.WORLD_SOLID: collision_grid.rub_handler,
            CollisionType.WORLD_PLATFORM_EDGE: collision_grid.rub_handler,
            CollisionType.WORLD_JUMP_THROUGH: collision_grid.no_handler,
            CollisionType.WORLD_JUMP_THROUGH_EDGE: collision_grid.no_handler,
            CollisionType.AI_ATTACKS: collision_grid.no_handler,
            CollisionType.AI_PROJECTILES: collision_grid.no_handler
        }

        self.collision_shape_offset = [0, 0]
        self.collision_shape = CollisionRect(
            pygame.Rect(self.world_position[0], self.world_position[1],
                        self.rect.width, self.rect.height), 0, handlers_to_use,
            CollisionType.PLAYER, game_types_to_collide_with)
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)
        self.collision_shape.owner = self

        self.ladder_collider_offset = [0.0, 0.0]
        self.triggers_collision_shape = CollisionRect(
            pygame.Rect(self.world_position[0], self.world_position[1],
                        self.rect.width / 3, self.rect.height), 0, {
                            CollisionType.LADDERS: collision_grid.no_handler,
                            CollisionType.DOOR: collision_grid.no_handler,
                            CollisionType.WATER: collision_grid.no_handler
                        }, CollisionType.PLAYER_LADDER,
            [CollisionType.LADDERS, CollisionType.DOOR, CollisionType.WATER])
        self.collision_grid.add_new_shape_to_grid(
            self.triggers_collision_shape)

        self.velocity = [0.0, 0.0]

        self.move_speed = 0.0

        # player speed values
        self.ground_top_speed = 500.0
        self.ground_acceleration = 1000.0
        self.slide_top_speed = 700.0
        self.slide_acceleration = 1500.0
        self.attack_top_speed = 300.0
        self.attack_acceleration = 3000.0
        self.air_top_speed = 250.0
        self.air_acceleration = 500.0
        self.attack_slide_top_speed = 1400.0
        self.attack_slide_acceleration = 3000.0
        self.speed_multiplier = 1.0

        self.climb_acceleration = 600.0
        self.climb_top_speed = 300.0
        self.climb_speed = 0.0

        self.jump_height = -600.0

        self.base_health = 100
        self.current_health = self.base_health

        self.action_to_start = ""
        self.motion_state = "idle"
        self.x_facing_direction = "right"
        self.y_facing_direction = "up"

        # directions
        self.move_left = False
        self.move_right = False
        self.climb_up = False
        self.climb_down = False

        # world position information
        self.in_climb_position = True
        self.touching_ground = False

        # weapons
        self.thrown_knife = False
        self.active_knives = []

        # collision shape drawing
        self.projectile_drawable_rects = []
        self.player_drawable_rectangle = DrawableCollisionRect(
            self.collision_shape)
        self.player_ladder_drawable_rectangle = DrawableCollisionRect(
            self.triggers_collision_shape)
        self.melee_attack_collision_drawable_rectangle = None
        self.frames_falling = 0

        self.found_ladder_position = [0.0, 0.0]

        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        self.respawning = False
        self.respawn_time = 5.0
        self.respawn_timer = self.respawn_time
        self.should_respawn = False

        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        self.view_cone = ViewCone(self.world_position,
                                  facing_vec,
                                  fov=145.0,
                                  length=450.0)
        self.collision_grid.add_new_shape_to_grid(
            self.view_cone.collision_circle)
        self.visible_enemies = None
        self.closest_visible_enemy = None

        # melee attack
        self.melee_collision_shape = None

        # handling level exit doors
        self.in_exit_door_position = False
        self.has_exited_level = False
        self.exit_door_hint = ExitDoorHint(self.fonts, self)
示例#12
0
文件: player.py 项目: MyreMylar/vania
class Player(pygame.sprite.Sprite):
    def __init__(self, moving_sprites_group, ui_sprites_group, fonts,
                 collision_grid, camera):
        super().__init__(moving_sprites_group)
        self.type = "player"
        self.moving_sprites_group = moving_sprites_group
        self.ui_sprites_group = ui_sprites_group
        self.fonts = fonts
        self.collision_grid = collision_grid
        self.camera = camera
        self.atlas_image = pygame.image.load("images/player.png")
        self.anim_sets = {
            "idle_right":
            AnimSet(self.atlas_image, [0, 96], [64, 96], 10, 10, [0, -1]),
            "idle_left":
            AnimSet(self.atlas_image, [0, 96], [64, 96],
                    10,
                    10, [0, -1],
                    looping=True,
                    x_flip=True),
            "run_right":
            AnimSet(self.atlas_image, [0, 0], [64, 96], 10, 30, [0, -1]),
            "run_left":
            AnimSet(self.atlas_image, [0, 0], [64, 96],
                    10,
                    30, [0, -1],
                    looping=True,
                    x_flip=True),
            "jump_right":
            AnimSet(self.atlas_image, [0, 192], [64, 96],
                    10,
                    8, [0, -1],
                    looping=False,
                    x_flip=False),
            "jump_left":
            AnimSet(self.atlas_image, [0, 192], [64, 96],
                    10,
                    8, [0, -1],
                    looping=False,
                    x_flip=True),
            "jump_attack_right":
            AnimSet(self.atlas_image, [0, 770], [96, 96],
                    10,
                    40, [0, -1],
                    looping=False,
                    x_flip=False),
            "jump_attack_left":
            AnimSet(self.atlas_image, [0, 770], [96, 96],
                    10,
                    40, [32, -1],
                    looping=False,
                    x_flip=True),
            "jump_throw_right":
            AnimSet(self.atlas_image, [0, 866], [96, 96],
                    10,
                    40, [0, -1],
                    looping=False,
                    x_flip=False),
            "jump_throw_left":
            AnimSet(self.atlas_image, [0, 866], [96, 96],
                    10,
                    40, [32, -1],
                    looping=False,
                    x_flip=True),
            "throw_right":
            AnimSet(self.atlas_image, [0, 288], [64, 96], 10, 40, [0, -1],
                    False, False),
            "throw_left":
            AnimSet(self.atlas_image, [0, 288], [64, 96], 10, 40, [0, -1],
                    False, True),
            "attack_right":
            AnimSet(self.atlas_image, [0, 384], [96, 96], 10, 60, [0, -1],
                    False, False),
            "attack_left":
            AnimSet(self.atlas_image, [0, 384], [96, 96], 10, 60, [32, -1],
                    False, True),
            "die_right":
            AnimSet(self.atlas_image, [0, 480], [96, 96], 10, 20, [0, -1],
                    False, False),
            "die_left":
            AnimSet(self.atlas_image, [0, 480], [96, 96], 10, 20, [32, -1],
                    False, True),
            "slide_right":
            AnimSet(self.atlas_image, [0, 576], [96, 96], 10, 15, [0, -1],
                    False, False),
            "slide_left":
            AnimSet(self.atlas_image, [0, 576], [96, 96], 10, 15, [32, -1],
                    False, True),
            "climb_up":
            AnimSet(self.atlas_image, [0, 672], [64, 96], 10, 30, [0, -1]),
            "climb_down":
            AnimSet(self.atlas_image, [0, 672], [64, 96], 10, 30, [0, -1],
                    True, True)
        }

        # load the knife image
        knife_image_position = [0, 960]
        knife_image_dimensions = [30, 6]
        knife_image_rect = pygame.Rect(knife_image_position,
                                       knife_image_dimensions)
        self.knife_image = self.atlas_image.subsurface(
            knife_image_rect).convert_alpha()

        self.active_anim = self.anim_sets["idle_right"]
        self.image = self.active_anim.current_frame

        self.rect = self.image.get_rect()

        self.start_position = (316.0, 596.0)
        self.world_position = [coord for coord in self.start_position]
        self.screen_position = [self.world_position[0], self.world_position[1]]

        game_types_to_collide_with = [
            CollisionType.WORLD_SOLID, CollisionType.WORLD_PLATFORM_EDGE,
            CollisionType.WORLD_JUMP_THROUGH,
            CollisionType.WORLD_JUMP_THROUGH_EDGE, CollisionType.AI_ATTACKS,
            CollisionType.AI_PROJECTILES
        ]
        handlers_to_use = {
            CollisionType.WORLD_SOLID: collision_grid.rub_handler,
            CollisionType.WORLD_PLATFORM_EDGE: collision_grid.rub_handler,
            CollisionType.WORLD_JUMP_THROUGH: collision_grid.no_handler,
            CollisionType.WORLD_JUMP_THROUGH_EDGE: collision_grid.no_handler,
            CollisionType.AI_ATTACKS: collision_grid.no_handler,
            CollisionType.AI_PROJECTILES: collision_grid.no_handler
        }

        self.collision_shape_offset = [0, 0]
        self.collision_shape = CollisionRect(
            pygame.Rect(self.world_position[0], self.world_position[1],
                        self.rect.width, self.rect.height), 0, handlers_to_use,
            CollisionType.PLAYER, game_types_to_collide_with)
        self.collision_grid.add_new_shape_to_grid(self.collision_shape)
        self.collision_shape.owner = self

        self.ladder_collider_offset = [0.0, 0.0]
        self.triggers_collision_shape = CollisionRect(
            pygame.Rect(self.world_position[0], self.world_position[1],
                        self.rect.width / 3, self.rect.height), 0, {
                            CollisionType.LADDERS: collision_grid.no_handler,
                            CollisionType.DOOR: collision_grid.no_handler,
                            CollisionType.WATER: collision_grid.no_handler
                        }, CollisionType.PLAYER_LADDER,
            [CollisionType.LADDERS, CollisionType.DOOR, CollisionType.WATER])
        self.collision_grid.add_new_shape_to_grid(
            self.triggers_collision_shape)

        self.velocity = [0.0, 0.0]

        self.move_speed = 0.0

        # player speed values
        self.ground_top_speed = 500.0
        self.ground_acceleration = 1000.0
        self.slide_top_speed = 700.0
        self.slide_acceleration = 1500.0
        self.attack_top_speed = 300.0
        self.attack_acceleration = 3000.0
        self.air_top_speed = 250.0
        self.air_acceleration = 500.0
        self.attack_slide_top_speed = 1400.0
        self.attack_slide_acceleration = 3000.0
        self.speed_multiplier = 1.0

        self.climb_acceleration = 600.0
        self.climb_top_speed = 300.0
        self.climb_speed = 0.0

        self.jump_height = -600.0

        self.base_health = 100
        self.current_health = self.base_health

        self.action_to_start = ""
        self.motion_state = "idle"
        self.x_facing_direction = "right"
        self.y_facing_direction = "up"

        # directions
        self.move_left = False
        self.move_right = False
        self.climb_up = False
        self.climb_down = False

        # world position information
        self.in_climb_position = True
        self.touching_ground = False

        # weapons
        self.thrown_knife = False
        self.active_knives = []

        # collision shape drawing
        self.projectile_drawable_rects = []
        self.player_drawable_rectangle = DrawableCollisionRect(
            self.collision_shape)
        self.player_ladder_drawable_rectangle = DrawableCollisionRect(
            self.triggers_collision_shape)
        self.melee_attack_collision_drawable_rectangle = None
        self.frames_falling = 0

        self.found_ladder_position = [0.0, 0.0]

        self.rect.centerx = int(self.screen_position[0])
        self.rect.centery = int(self.screen_position[1])

        self.respawning = False
        self.respawn_time = 5.0
        self.respawn_timer = self.respawn_time
        self.should_respawn = False

        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        self.view_cone = ViewCone(self.world_position,
                                  facing_vec,
                                  fov=145.0,
                                  length=450.0)
        self.collision_grid.add_new_shape_to_grid(
            self.view_cone.collision_circle)
        self.visible_enemies = None
        self.closest_visible_enemy = None

        # melee attack
        self.melee_collision_shape = None

        # handling level exit doors
        self.in_exit_door_position = False
        self.has_exited_level = False
        self.exit_door_hint = ExitDoorHint(self.fonts, self)

    def respawn(self):
        self.motion_state = "idle"
        self.current_health = self.base_health
        self.should_respawn = False
        self.world_position = [coord for coord in self.start_position]
        self.collision_shape.set_position([
            self.world_position[0] + self.collision_shape_offset[0],
            self.world_position[1] + self.collision_shape_offset[1]
        ])

        self.collision_shape.set_position([
            self.world_position[0] + self.collision_shape_offset[0],
            self.world_position[1] + self.collision_shape_offset[1]
        ])

        self.triggers_collision_shape.set_position([
            self.world_position[0] + self.ladder_collider_offset[0],
            self.world_position[1] + self.ladder_collider_offset[1]
        ])

    def process_events(self, event):
        if event.type == KEYDOWN:
            if event.key == K_LEFT:
                self.move_left = True
            if event.key == K_RIGHT:
                self.move_right = True
            if event.key == K_UP:
                if self.in_climb_position:
                    self.climb_up = True
                elif self.in_exit_door_position:
                    self.has_exited_level = True
                else:
                    self.camera.process_input(event)
            if event.key == K_DOWN:
                if self.in_climb_position:
                    self.climb_down = True
                else:
                    self.camera.process_input(event)
            if event.key == K_SPACE:
                if self.motion_state == "idle" or self.motion_state == "run" and self.touching_ground:
                    self.action_to_start = "jump"
            if event.key == K_LCTRL or event.key == K_RCTRL:
                if self.motion_state == "idle" or self.motion_state == "run" and self.touching_ground:
                    self.action_to_start = "throw"
                elif not self.touching_ground:
                    self.action_to_start = "jump_throw"
            if event.key == K_z or event.key == K_LSHIFT or event.key == K_RSHIFT:
                if self.motion_state == "idle" or self.motion_state == "run" and self.touching_ground:
                    self.action_to_start = "attack"
                elif not self.touching_ground:
                    self.action_to_start = "jump_attack"
            if event.key == K_x:
                if self.motion_state == "idle" or self.motion_state == "run" and self.touching_ground:
                    self.action_to_start = "slide"

            if event.key == K_ESCAPE:
                self.should_respawn = True
        if event.type == KEYUP:
            if event.key == K_LEFT:
                self.move_left = False
            if event.key == K_RIGHT:
                self.move_right = False
            if event.key == K_UP:
                if self.in_climb_position:
                    self.climb_up = False
                else:
                    self.camera.process_input(event)
            if event.key == K_DOWN:
                if self.in_climb_position:
                    self.climb_down = False
                else:
                    self.camera.process_input(event)

    def lose_health(self, health):
        self.current_health -= health
        if self.current_health < 0:
            self.current_health = 0

    def update(self, time_delta, gravity, camera):
        # react to collision stuff
        self.in_climb_position = False
        self.speed_multiplier = 1.0
        floor_collided_this_frame = False
        in_exit_door_position = False
        if len(self.triggers_collision_shape.collided_shapes_this_frame) > 0:
            for shape in self.triggers_collision_shape.collided_shapes_this_frame:
                if shape.game_type == CollisionType.WATER:
                    self.speed_multiplier = 0.5
                elif shape.game_type == CollisionType.LADDERS:
                    self.in_climb_position = True
                    self.found_ladder_position = [shape.x, shape.y]

                elif shape.game_type == CollisionType.DOOR:
                    in_exit_door_position = True
                    if not self.in_exit_door_position:
                        self.in_exit_door_position = True
                        self.add_exit_door_hint()

        if len(self.collision_shape.collided_shapes_this_frame) > 0:
            for shape in self.collision_shape.collided_shapes_this_frame:
                if shape.game_type == CollisionType.AI_PROJECTILES:
                    self.lose_health(10)
                if shape.game_type == CollisionType.WORLD_JUMP_THROUGH or\
                        shape.game_type == CollisionType.WORLD_JUMP_THROUGH_EDGE:
                    # moderately complicated handling for platforms that you can jump and climb through
                    # essentially they only act like platforms if you fall down on top of them
                    # I treat them like collision shapes with no handling and then only apply the mtv vector
                    # if a bunch of conditions are met (falling, not climbing or starting a jump & above the platform)
                    mtv_vector = self.collision_shape.get_frame_mtv_vector(
                        shape)
                    if self.climb_down:
                        self.touching_ground = False
                    else:
                        if mtv_vector is not None and not (
                                self.action_to_start
                                == "jump") and self.motion_state != "climb":
                            if abs(mtv_vector[1]) > abs(
                                    mtv_vector[0]
                            ) and mtv_vector[1] < 0 and self.velocity[1] > 0.0:
                                if abs(mtv_vector[1]) > 0.5:
                                    self.collision_shape.set_position([
                                        self.collision_shape.x + mtv_vector[0],
                                        self.collision_shape.y + mtv_vector[1]
                                    ])
                                floor_collided_this_frame = True
                                self.velocity[1] = 0.0
                                self.frames_falling = 0
                                self.touching_ground = True
                                if self.motion_state == "jump" or self.motion_state == "jump_throw":
                                    self.thrown_knife = False

                        elif mtv_vector is None and self.motion_state != "climb":
                            if self.touching_ground:
                                floor_collided_this_frame = True

                if shape.game_type == CollisionType.WORLD_SOLID or \
                        shape.game_type == CollisionType.WORLD_PLATFORM_EDGE:
                    # see if we have an upwards facing mtv vector
                    mtv_vector = self.collision_shape.get_frame_mtv_vector(
                        shape)
                    if mtv_vector is not None and not (self.action_to_start
                                                       == "jump"):
                        if abs(mtv_vector[1]) > abs(
                                mtv_vector[0]) and mtv_vector[1] < 0:
                            floor_collided_this_frame = True
                            self.velocity[1] = 0.0
                            self.frames_falling = 0
                            self.touching_ground = True
                            if self.motion_state == "jump" or self.motion_state == "jump_throw":
                                self.thrown_knife = False
                        # see if we have a sideways facing mtv vector
                        if abs(mtv_vector[0]) - abs(mtv_vector[1]) > 0.1:
                            self.move_speed = 0.0
                            self.motion_state = "idle"
                    elif mtv_vector is None:
                        if self.touching_ground:
                            floor_collided_this_frame = True

        if not in_exit_door_position:
            if self.in_exit_door_position:
                self.in_exit_door_position = False
                self.remove_exit_door_hint()

        if not floor_collided_this_frame:
            if self.touching_ground and self.frames_falling > 10:
                self.frames_falling = 0
                self.touching_ground = False
            self.frames_falling += 1

        if len(self.collision_shape.collided_shapes_this_frame) > 0:
            self.world_position[
                0] = self.collision_shape.x - self.collision_shape_offset[0]
            self.world_position[
                1] = self.collision_shape.y - self.collision_shape_offset[1]

        self.update_climbing(time_delta)

        if self.motion_state != "climb":
            if self.touching_ground or self.motion_state == "die":
                if self.motion_state == "throw":
                    self.update_throwing(camera)
                elif self.motion_state == "attack":
                    self.update_melee_attacking(time_delta)
                elif self.motion_state == "slide":
                    self.update_sliding(time_delta)
                elif self.motion_state == "die":
                    self.update_dying()
                else:
                    self.update_running(time_delta)
            else:
                self.update_jumping(time_delta, camera)

            self.velocity[
                0] = self.move_speed * self.speed_multiplier  # A good place to add a move speed multiplier

        # if attack is interrupted make sure we kill attack shape
        if not (self.motion_state == "attack"
                or self.motion_state == "jump_attack"):
            if self.melee_collision_shape is not None:
                self.collision_grid.remove_shape_from_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape = None

        # if slide interrupted make sure we restore normal collision shape dimensions
        if not self.motion_state == "slide":
            if self.collision_shape.width == 96 and self.collision_shape.height == 32:
                self.collision_shape.set_dimensions(64, 96)
                self.collision_shape_offset = [0, 0]
                self.player_drawable_rectangle.on_change_dimensions()

        if self.action_to_start != "":
            if self.action_to_start == "jump":
                self.touching_ground = False
                self.velocity[1] = self.jump_height
                self.world_position[1] -= 10.0
            if self.action_to_start == "slide":
                self.collision_shape.set_dimensions(96, 32)
                self.collision_shape_offset = [0, 32]
                self.player_drawable_rectangle.on_change_dimensions()
            self.motion_state = self.action_to_start
            self.action_to_start = ""

        if self.motion_state == "climb":
            pass
        else:
            if not floor_collided_this_frame:
                self.velocity[1] += gravity * time_delta

        self.world_position[0] += self.velocity[0] * time_delta
        self.world_position[1] += self.velocity[1] * time_delta

        self.update_animation(time_delta)

        # set the position of the collision shapes
        self.collision_shape.set_position([
            self.world_position[0] + self.collision_shape_offset[0],
            self.world_position[1] + self.collision_shape_offset[1]
        ])

        self.triggers_collision_shape.set_position([
            self.world_position[0] + self.ladder_collider_offset[0],
            self.world_position[1] + self.ladder_collider_offset[1]
        ])

        self.update_visible_enemies()

        #  set the sprite's centre position based of the current
        #  position and the animation's 'centre point' offset
        self.update_screen_position(camera)
        self.rect.centerx = int(self.screen_position[0] -
                                self.active_anim.centre_offset[0])
        self.rect.centery = int(self.screen_position[1] -
                                self.active_anim.centre_offset[1])

        if self.should_respawn:
            self.respawn()

        if self.current_health <= 0 and not self.respawning:
            self.action_to_start = "die"
            self.respawning = True
            self.respawn_timer = self.respawn_time

        if self.respawning:
            if self.respawn_timer <= 0.0:
                self.should_respawn = True
                self.respawning = False
            else:
                self.respawn_timer -= time_delta

    def in_range_and_in_front(self, sprite):
        x_dist = sprite.world_position[0] - self.world_position[0]
        y_dist = sprite.world_position[1] - self.world_position[1]
        distance_squared = x_dist**2 + y_dist**2
        if distance_squared >= self.view_cone.length_squared:
            return False

        if self.x_facing_direction == "right":
            facing_vec = [1.0, 0.0]
        else:
            facing_vec = [-1.0, 0.0]
        dot = facing_vec[0] * x_dist + facing_vec[1] * y_dist
        if dot < 0:
            return False

        return True

    def update_visible_enemies(self):
        enemies = [
            sprite for sprite in self.moving_sprites_group.sprites()
            if sprite.type == "enemy" and self.in_range_and_in_front(sprite)
        ]

        if len(enemies
               ) > 0 and self.collision_shape.moved_since_last_collision_test:
            self.view_cone.set_position(
                pygame.math.Vector2(self.world_position))
            if self.x_facing_direction == "right":
                facing_vec = [1.0, 0.0]
            else:
                facing_vec = [-1.0, 0.0]
            self.view_cone.set_facing_direction(
                pygame.math.Vector2(facing_vec))
            self.view_cone.update()
            self.visible_enemies = [
                sprite for sprite in enemies
                if self.view_cone.is_subject_visible(
                    pygame.math.Vector2(sprite.world_position))
            ]
            if len(self.visible_enemies) > 0:
                self.closest_visible_enemy = self.find_closest(
                    self.visible_enemies)
            else:
                self.closest_visible_enemy = None
        else:
            self.view_cone.clear()
            self.closest_visible_enemy = None

    def find_closest(self, sprites):
        closest_distance = 9999999999999999.0
        closest_sprite = None
        for sprite in sprites:
            x_dist = self.world_position[0] - sprite.world_position[0]
            y_dist = self.world_position[1] - sprite.world_position[1]
            squared_dist = x_dist**2 + y_dist**2
            if squared_dist < closest_distance:
                closest_distance = squared_dist
                closest_sprite = sprite
        return closest_sprite

    def update_screen_position(self, camera):
        view_top_left_position = (camera.position[0] - camera.half_width,
                                  camera.position[1] - camera.half_height)
        self.screen_position[
            0] = self.world_position[0] - view_top_left_position[0]
        self.screen_position[
            1] = self.world_position[1] - view_top_left_position[1]

    def update_animation(self, time_delta):
        # set the animation name based on motion and direction
        anim_name = self.motion_state + "_"
        if self.motion_state == "climb":
            anim_name += self.y_facing_direction
        else:
            anim_name += self.x_facing_direction
        # if this is a new animation, run the start function
        if self.active_anim != self.anim_sets[anim_name]:
            self.active_anim = self.anim_sets[anim_name]
            self.active_anim.start()
        # update the animation & set the sprite image to the current animation frame
        self.active_anim.update(time_delta)
        self.image = self.active_anim.current_frame

    def update_jumping(self, time_delta, camera):
        if self.motion_state == "jump_attack":
            if self.active_anim.frame_index == 3 and self.melee_collision_shape is None:
                attack_position = [
                    self.world_position[0], self.world_position[1]
                ]
                if self.x_facing_direction == "left":
                    attack_position[0] -= 64
                attack_dimensions = [64, 16]
                game_types_to_collide_with = [CollisionType.AI]
                handlers_to_use = {
                    CollisionType.AI: self.collision_grid.no_handler
                }
                self.melee_collision_shape = CollisionRect(
                    pygame.Rect(attack_position[0], attack_position[1],
                                attack_dimensions[0], attack_dimensions[1]), 0,
                    handlers_to_use, CollisionType.PLAYER_ATTACKS,
                    game_types_to_collide_with)
                self.collision_grid.add_new_shape_to_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape.owner = self

                self.melee_attack_collision_drawable_rectangle = DrawableCollisionRect(
                    self.melee_collision_shape)
            elif self.active_anim.frame_index > 3 and self.melee_collision_shape is not None:
                self.collision_grid.remove_shape_from_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape = None
            if not self.active_anim.running:
                self.motion_state = "jump"
        elif self.motion_state == "jump_throw":
            if self.active_anim.frame_index == 3 and not self.thrown_knife:
                self.thrown_knife = True
                if self.closest_visible_enemy is not None:
                    x_dir = self.closest_visible_enemy.world_position[
                        0] - self.world_position[0]
                    y_dir = self.closest_visible_enemy.world_position[
                        1] - self.world_position[1]
                    distance = math.sqrt(x_dir**2 + y_dir**2)
                    throwing_direction = [x_dir / distance, y_dir / distance]
                    # knives travel at roughly 1000.0 pixels a second, gravity is 800 pixels down per second
                    # so we need to correct the direction vector based on how far away the target is.
                    # after 1000 distance we will have descended roughly 800 from our target position

                    # calculated by assuming traveling in a straight line to the right to a point 1000.0 away
                    gravity_offset_vec = [0.0, -0.625 * distance / 1000.0]
                    throwing_direction = [
                        throwing_direction[0] + gravity_offset_vec[0],
                        throwing_direction[1] + gravity_offset_vec[1]
                    ]
                    # renormalise direction
                    throwing_dir_len = math.sqrt(throwing_direction[0]**2 +
                                                 throwing_direction[1]**2)
                    throwing_direction = [
                        throwing_direction[0] / throwing_dir_len,
                        throwing_direction[1] / throwing_dir_len
                    ]

                else:
                    if self.x_facing_direction == 'left':
                        throwing_direction = [
                            -0.98, -0.19 + random.normalvariate(0, 0.02)
                        ]
                    else:
                        throwing_direction = [
                            0.98, -0.19 + random.normalvariate(0, 0.02)
                        ]
                knife = ThrowingKnife(self.moving_sprites_group,
                                      self.collision_grid, self.knife_image,
                                      self.world_position, throwing_direction,
                                      self.projectile_drawable_rects, camera)
                self.active_knives.append(knife)
            if not self.active_anim.running:
                self.thrown_knife = False
                self.motion_state = "jump"
        else:
            self.motion_state = "jump"
        if self.move_left:
            self.move_speed -= self.air_acceleration * time_delta
            if self.move_speed < -self.air_top_speed:
                self.move_speed = -self.air_top_speed

            self.velocity[0] = self.move_speed
            self.x_facing_direction = "left"
            speed_factor = abs(self.move_speed / self.air_top_speed)
            self.active_anim.set_speed_factor(speed_factor)
        elif self.move_right:
            self.move_speed += self.air_acceleration * time_delta
            if self.move_speed > self.air_top_speed:
                self.move_speed = self.air_top_speed
            self.velocity[0] = self.move_speed
            self.x_facing_direction = "right"
            speed_factor = abs(self.move_speed / self.air_top_speed)
            self.active_anim.set_speed_factor(speed_factor)

    def update_running(self, time_delta):
        if self.move_left:
            self.motion_state = "run"
            if self.x_facing_direction == "right":
                self.move_speed = 0.0
            self.move_speed -= self.ground_acceleration * time_delta
            if self.move_speed < -self.ground_top_speed:
                self.move_speed = -self.ground_top_speed
            self.velocity[0] = self.move_speed
            self.x_facing_direction = "left"
            speed_factor = abs(self.move_speed / self.ground_top_speed)
            self.active_anim.set_speed_factor(speed_factor)

        elif self.move_right:
            self.motion_state = "run"
            if self.x_facing_direction == "left":
                self.move_speed = 0.0
            self.move_speed += self.ground_acceleration * time_delta
            if self.move_speed > self.ground_top_speed:
                self.move_speed = self.ground_top_speed
            self.velocity[0] = self.move_speed
            self.x_facing_direction = "right"
            speed_factor = abs(self.move_speed / self.ground_top_speed)
            self.active_anim.set_speed_factor(speed_factor)
        else:
            self.move_speed = 0.0
            self.velocity[0] = self.move_speed
            self.motion_state = "idle"

    def update_dying(self):
        if not self.active_anim.running:
            pass
            # self.dying = False

    def update_melee_attacking(self, time_delta):
        if self.active_anim.running:
            if 3 < self.active_anim.frame_index < 10:
                if self.x_facing_direction == "left":
                    self.move_speed -= self.attack_slide_acceleration * time_delta
                    if self.move_speed < -self.attack_slide_top_speed:
                        self.move_speed = -self.attack_slide_top_speed
                    self.velocity[0] = self.move_speed
                elif self.x_facing_direction == "right":
                    self.move_speed += self.attack_slide_acceleration * time_delta
                    if self.move_speed > self.attack_slide_top_speed:
                        self.move_speed = self.attack_slide_top_speed
                    self.velocity[0] = self.move_speed
            if self.active_anim.frame_index == 3 and self.melee_collision_shape is None:
                attack_position = [
                    self.world_position[0], self.world_position[1]
                ]
                if self.x_facing_direction == "left":
                    attack_position[0] -= 64
                attack_dimensions = [64, 16]
                game_types_to_collide_with = [CollisionType.AI]
                handlers_to_use = {
                    CollisionType.AI: self.collision_grid.no_handler
                }
                self.melee_collision_shape = CollisionRect(
                    pygame.Rect(attack_position[0], attack_position[1],
                                attack_dimensions[0], attack_dimensions[1]), 0,
                    handlers_to_use, CollisionType.PLAYER_ATTACKS,
                    game_types_to_collide_with)
                self.collision_grid.add_new_shape_to_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape.owner = self

                self.melee_attack_collision_drawable_rectangle = DrawableCollisionRect(
                    self.melee_collision_shape)
            elif self.active_anim.frame_index > 3 and self.melee_collision_shape is not None:
                self.collision_grid.remove_shape_from_grid(
                    self.melee_collision_shape)
                self.melee_collision_shape = None

        else:
            self.motion_state = "idle"

    def update_throwing(self, camera):
        if self.active_anim.frame_index == 3 and not self.thrown_knife:
            self.thrown_knife = True
            if self.closest_visible_enemy is not None:
                x_dir = self.closest_visible_enemy.world_position[
                    0] - self.world_position[0]
                y_dir = self.closest_visible_enemy.world_position[
                    1] - self.world_position[1]
                distance = math.sqrt(x_dir**2 + y_dir**2)
                throwing_direction = [x_dir / distance, y_dir / distance]
                # knives travel at roughly 1000.0 pixels a second, gravity is 800 pixels down per second
                # so we need to correct the direction vector based on how far away the target is.
                # after 1000 distance we will have descended roughly 800 from our target position

                # calculated by assuming traveling in a straight line to the right to a point 1000.0 away
                gravity_offset_vec = [0.0, -0.625 * distance / 1000.0]
                throwing_direction = [
                    throwing_direction[0] + gravity_offset_vec[0],
                    throwing_direction[1] + gravity_offset_vec[1]
                ]
                # renormalise direction
                throwing_dir_len = math.sqrt(throwing_direction[0]**2 +
                                             throwing_direction[1]**2)
                throwing_direction = [
                    throwing_direction[0] / throwing_dir_len,
                    throwing_direction[1] / throwing_dir_len
                ]

            else:
                if self.x_facing_direction == 'left':
                    throwing_direction = [
                        -0.98, -0.19 + random.normalvariate(0, 0.02)
                    ]
                else:
                    throwing_direction = [
                        0.98, -0.19 + random.normalvariate(0, 0.02)
                    ]
            knife = ThrowingKnife(self.moving_sprites_group,
                                  self.collision_grid, self.knife_image,
                                  self.world_position, throwing_direction,
                                  self.projectile_drawable_rects, camera)
            self.active_knives.append(knife)
        if not self.active_anim.running:
            self.motion_state = "idle"
            self.thrown_knife = False

    def update_sliding(self, time_delta):
        if self.active_anim.running:
            if self.x_facing_direction == "left":
                self.move_speed -= self.slide_acceleration * time_delta
                if self.move_speed < -self.slide_top_speed:
                    self.move_speed = -self.slide_top_speed
                self.velocity[0] = self.move_speed
            elif self.x_facing_direction == "right":
                self.move_speed += self.slide_acceleration * time_delta
                if self.move_speed > self.slide_top_speed:
                    self.move_speed = self.slide_top_speed
                self.velocity[0] = self.move_speed
        else:
            self.collision_shape.set_dimensions(64, 96)
            self.collision_shape_offset = [0, 0]
            self.player_drawable_rectangle.on_change_dimensions()
            self.motion_state = "idle"

    def update_climbing(self, time_delta):
        if self.motion_state == "climb" and not self.in_climb_position:
            self.motion_state = "idle"
        if self.in_climb_position:
            if self.climb_up:
                self.world_position[0] = self.found_ladder_position[0]
                if self.touching_ground:
                    self.world_position[1] -= 10.0

                self.motion_state = "climb"
                self.y_facing_direction = "up"
                self.velocity[1] = 0.0
                self.move_speed = 0.0
                self.touching_ground = False
                self.climb_speed -= self.climb_acceleration * time_delta
                if self.climb_speed < -self.climb_top_speed:
                    self.climb_speed = -self.climb_top_speed
                self.world_position[1] += (self.climb_speed * time_delta)
                speed_factor = abs(self.climb_speed / self.climb_top_speed)
                self.active_anim.set_speed_factor(speed_factor)
            elif self.climb_down:
                self.world_position[0] = self.found_ladder_position[0]
                if self.touching_ground:
                    self.motion_state = "idle"
                    self.climb_speed = 0.0
                else:
                    self.motion_state = "climb"
                    self.y_facing_direction = "down"
                    self.velocity[1] = 0.0
                    self.move_speed = 0.0
                    self.touching_ground = False
                    self.climb_speed += self.climb_acceleration * time_delta
                    if self.climb_speed > self.climb_top_speed:
                        self.climb_speed = self.climb_top_speed
                    self.world_position[1] += (self.climb_speed * time_delta)
                    speed_factor = abs(self.climb_speed / self.climb_top_speed)
                    self.active_anim.set_speed_factor(speed_factor)
            elif self.motion_state == "climb":
                self.touching_ground = False
                self.velocity[1] = 0.0
                self.climb_speed = 0.0
                self.active_anim.set_speed_factor(0.0)
        else:
            self.climb_up = False
            self.climb_down = False
        if self.motion_state != "climb":
            self.climb_speed = 0.0

    def draw_collision_shapes(self, screen, camera):
        self.player_drawable_rectangle.update_collided_colours()
        self.player_drawable_rectangle.draw(
            screen, camera.position, (camera.half_width, camera.half_height))

        self.player_ladder_drawable_rectangle.update_collided_colours()
        self.player_ladder_drawable_rectangle.draw(
            screen, camera.position, (camera.half_width, camera.half_height))

        for rect in self.projectile_drawable_rects:
            rect.update_collided_colours()
            rect.draw(screen, camera.position,
                      (camera.half_width, camera.half_height))

        self.view_cone.draw(screen, camera)

        if self.melee_collision_shape is not None:
            self.melee_attack_collision_drawable_rectangle.update_collided_colours(
            )
            self.melee_attack_collision_drawable_rectangle.draw(
                screen, camera.position,
                (camera.half_width, camera.half_height))

    def add_exit_door_hint(self):
        self.ui_sprites_group.add(self.exit_door_hint)

    def remove_exit_door_hint(self):
        self.ui_sprites_group.remove(self.exit_door_hint)
示例#13
0
def test_cone():
    pygame.init()
    screen = pygame.display.set_mode((800, 600))

    background = pygame.Surface((800, 600))
    background.fill(pygame.Color("#000000"))

    running = True
    view_cone = ViewCone([400, 300], [1.0, 0.0], fov=60, length=200)

    test_rect = CollisionRect(pygame.Rect((310, 400), (30, 30)), 0,
                              {CollisionType.VIEW_CONE: CollisionNoHandler()},
                              CollisionType.WORLD_SOLID,
                              [CollisionType.VIEW_CONE]
                              )
    drawable_test_rect = DrawableCollisionRect(test_rect)

    test_rect_2 = CollisionRect(pygame.Rect((340, 400), (30, 30)), 0,
                               {CollisionType.VIEW_CONE: CollisionNoHandler()},
                               CollisionType.WORLD_SOLID,
                               [CollisionType.VIEW_CONE]
                               )
    drawable_test_rect_2 = DrawableCollisionRect(test_rect_2)

    drawable_test_circle = DrawableCollisionCircle(view_cone.collision_circle)

    test_rect.normals["right"].should_skip = True
    test_rect_2.normals["left"].should_skip = True

    test_points = []
    for _ in range(0, 1000):
        test_points.append(pygame.math.Vector2(float(random.randint(10, 790)),
                                               float(random.randint(10, 590))))

    clock = pygame.time.Clock()
    while running:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    mouse_pos = pygame.mouse.get_pos()
                    vec_to_mouse = [mouse_pos[0] - view_cone.origin_centre_position[0],
                                    mouse_pos[1] - view_cone.origin_centre_position[1]]
                    length = math.sqrt(vec_to_mouse[0] ** 2 + vec_to_mouse[1] ** 2)
                    if length > 0.0:
                        vec_to_mouse_norm = Vector2(vec_to_mouse[0] / length, vec_to_mouse[1] / length)

                        view_cone.set_facing_direction(vec_to_mouse_norm)

                if event.button == 3:
                    view_cone.set_position(Vector2(pygame.mouse.get_pos()))

        screen.blit(background, (0, 0))

        view_cone.update()

        for point in test_points:
            if view_cone.is_subject_visible(point):
                pygame.draw.line(screen, pygame.Color("#0000FF"), point, [point.x + 1, point.y + 1], 2)
            else:
                pygame.draw.line(screen, pygame.Color("#FF0000"), point, [point.x + 1, point.y + 1], 2)

        if collide_circle_with_rotated_rectangle(view_cone.collision_circle, test_rect):
            test_rect.add_frame_collided_shape(view_cone.collision_circle)
            view_cone.collision_circle.add_frame_collided_shape(test_rect)
        else:
            test_rect.clear_frame_collided_shapes()
            view_cone.collision_circle.clear_frame_collided_shapes()

        if collide_circle_with_rotated_rectangle(view_cone.collision_circle, test_rect_2):
            test_rect_2.add_frame_collided_shape(view_cone.collision_circle)
            view_cone.collision_circle.add_frame_collided_shape(test_rect_2)
        else:
            test_rect_2.clear_frame_collided_shapes()
            view_cone.collision_circle.clear_frame_collided_shapes()

        drawable_test_rect.update_collided_colours()
        drawable_test_rect_2.update_collided_colours()
        drawable_test_circle.update_collided_colours()
        drawable_test_rect.draw(screen)
        drawable_test_rect_2.draw(screen)
        # drawable_test_circle.draw(screen)
        view_cone.draw(screen)
        pygame.display.flip()