예제 #1
0
class Sword:
    """
    Main sword weapon.
    """
    def __init__(self,
                 parent_rect: pygame.Rect,
                 depth: int = 20,
                 slash_range: int = 40,
                 extent: int = 10):
        # TODO: These Default parameters should be set in the configuration file
        self.parent_rect = parent_rect
        self.depth = depth
        self.slash_range = slash_range
        self.extent = extent
        self.horizontal_rect = pygame.Rect(0, 0, self.slash_range, self.depth)
        self.vertical_rect = pygame.Rect(0, 0, self.depth, self.slash_range)
        self.current_rect = self.horizontal_rect
        self.image = None
        self.action = Action(PlayerSprite.ATTACK_FRAME_DURATION *
                             PlayerSprite.FRAME_NUMBER)
        self._update_current_rect_and_direction(Direction.DOWN)

    def attack(self, direction: Direction):
        if self.action.in_progress():
            return
        self._update_current_rect_and_direction(direction)
        self.action.restart()

    def has_hit(self, enemy: Enemy):
        if self.action.in_progress():
            return self.current_rect.colliderect(enemy.hitbox.rect)
        else:
            return False

    def update(self):
        self.action.update()

    def _update_current_rect_and_direction(self, direction: Direction):
        if direction in (Direction.DOWN, Direction.UP):
            self.current_rect = self.horizontal_rect
        else:
            self.current_rect = self.vertical_rect
        relative_position = self.extent * direction.value
        self.current_rect.center = self.parent_rect.center + relative_position
        self.set_image(BLUE)

    def set_image(self, color):
        self.image = pygame.Surface(self.current_rect.size)
        self.image.fill(color)
        self.image.set_alpha(100)
예제 #2
0
class Bow:
    """
    Bow weapon and arrows
    """
    SHOOT_COOLDOWN = 20

    def __init__(self, parent_rect: pygame.Rect, parent_group: pygame.sprite.Group):
        self.parent_rect = parent_rect
        self.action = Action(self.SHOOT_COOLDOWN)
        self.quiver = pygame.sprite.Group()
        self.parent_group = parent_group
        self.proyectile_image = pygame.image.load('assets/sprites/arrow.png').convert_alpha()

    def attack(self, direction: Direction):
        if self.action.in_progress():
            return
        arrow = Arrow(self.parent_rect.center, direction, self.proyectile_image)
        arrow.add(self.parent_group, self.quiver)
        self.action.restart()

    def update(self, delta: float, physical_obstacles: List[pygame.Rect]):
        self.action.update()
        self.quiver.update(delta, physical_obstacles)
예제 #3
0
class Enemy:
    THINK_TIME = 70
    INVISIBLE_TIME = 25
    MAX_VISUAL_FIELD = 6000
    MAX_VELOCITY = cfg.VELOCITY * 0.5
    MAX_FRAME_WAIT = 10
    IDLE_TIME = 25

    def __init__(self, position: Vector2, direction: Direction, life: int,
                 sprite: EnititySprite):
        self.group = None
        self.life = life
        self.position = Vector2(position)
        self.velocity = Vector2(0, 0)
        self.direction = direction
        self.sprite = sprite
        self.state = sprite.state
        self.hitbox = Hitbox(sprite.rect)
        self.cooldown = Action(self.INVISIBLE_TIME)
        # self.think_counter = self.THINK_TIME
        self.force_frame_count = self.MAX_FRAME_WAIT  # TODO: Move to AI
        self.idle_counter = 0  # TODO: Move to Action
        self.hit_sound = pygame.mixer.Sound("assets/sounds/hit.wav")

    def update(self, delta, physical_objects, player_position: Vector2):
        self.cooldown.update()
        if self.cooldown.is_idle():
            #     self.animation.next_frame("walk")
            #     self.image = self.animation.current_sprite
            self.handle_ai(player_position)
        # elif self.cooldown.in_progress():
        #     self.blink()
        #     self.cooldown.update()
        # if self.idle_counter > 0:
        #     self.idle_counter -= 1
        #     self.velocity[:] = 0, 0
        self.handle_collisions_with_objects(delta, physical_objects)
        self.move(delta)

    def get_hit(self, direction: Direction):
        if self.cooldown.is_idle():
            self.cooldown.restart()
            self.hit_sound.play()
            self.velocity = cfg.VELOCITY * 2 * direction.value
            self.life -= 1
            if self.life == 0:
                self.kill()

    def handle_ai(self, player_position: Vector2):
        distance_to_player = self.position.distance_squared_to(player_position)
        if distance_to_player < self.MAX_VISUAL_FIELD:
            vec_difference = player_position - self.position
            desired_direction = Direction.closest_direction(vec_difference)
            if desired_direction != self.direction:
                self.force_frame_count -= 1
                if self.force_frame_count == 0:
                    self.force_frame_count = self.MAX_FRAME_WAIT
                    self.direction = desired_direction

            self.velocity = self.MAX_VELOCITY * self.direction.value
        else:
            self.velocity[:] = 0, 0
        # elif self.think_counter == 0:
        #     self.think_counter = choice((self.THINK_TIME, 5))

        # if self.think_counter == self.THINK_TIME:
        #     self.direction.py = self.DIRECTION_VECTOR[randint(0, 3)]

        # self.velocity = cfg.VELOCITY * 0.5 * self.direction.py
        # self.think_counter -= 1

    def handle_collisions_with_objects(self, delta: float, physical_objects):
        for idx, vec in enumerate((Vector2(1, 0), Vector2(0, 1))):
            self.hitbox.move_with_respect_to_parent(
                self.position + delta * (self.velocity.elementwise() * vec))
            if self.hitbox.has_collided_with_rects(*physical_objects):
                self.velocity[idx] = 0

    def stay_idle(self):
        self.idle_counter = self.IDLE_TIME

    def move(self, delta):
        self.position.update(self.position + delta * self.velocity)
        self.sprite.update(self.direction, self.position, self.velocity,
                           self.state)

    def kill(self):
        self.group.remove_enemy(self)

    def add(self, group):
        """Add to an enemy group"""
        self.group = group
예제 #4
0
class Player:
    HITBOX_DEFLATION = -16
    DAMAGE_MOMENTUM = 2.5
    COOLDOWN_TIME = 10

    def __init__(self):
        self.life = 10
        self.position = Vector2((cfg.DIS_WIDTH // 2, cfg.DIS_HEIGHT // 2))
        self.velocity = Vector2(0, 0)
        self.state = State.IDLE
        self.direction = Direction.DOWN
        self.sprite_group = pygame.sprite.Group()
        self.sprite = PlayerSprite(self.position, self.direction, self.state,
                                   self.sprite_group)
        self.hitbox = Hitbox(self.sprite.rect, self.HITBOX_DEFLATION,
                             self.HITBOX_DEFLATION)
        self.cooldown = Action(self.COOLDOWN_TIME)
        self.sword = Sword(self.sprite.rect)
        self.bow = Bow(self.sprite.rect, self.sprite_group)

    def update(self, delta: float, control: Control,
               objects_group: List[pygame.Rect], enemy_group: EnemyGroup):
        self.bow.update(delta, objects_group)
        self.sword.update()
        self.cooldown.update()
        if self.cooldown.is_idle() and self.sword.action.is_idle(
        ) and self.bow.action.is_idle():
            self.handle_input(control)
        self.handle_collision_with_enemy(enemy_group)
        self.handle_collision_with_objects(delta, objects_group)
        self.move(delta)

    def handle_input(self, control: Control):
        self.velocity[:] = 0, 0

        if control.attack:
            self.sword.attack(self.direction)
            self.state = State.ATTACK
            return
        if control.shoot:
            self.bow.attack(self.direction)
            self.state = State.IDLE
            return
        if control.up:
            self.velocity.y = -cfg.VELOCITY
        if control.down:
            self.velocity.y = cfg.VELOCITY
        if control.left:
            self.velocity.x = -cfg.VELOCITY
        if control.right:
            self.velocity.x = cfg.VELOCITY

        if self.velocity.elementwise() != 0:
            self.velocity = 0.7071 * self.velocity

        if self.velocity.length_squared() == 0:
            self.state = State.IDLE
        else:
            self.state = State.WALK
            self.update_direction()

    def update_direction(self):
        if self.velocity.y == 0:
            if self.velocity.x > 0:
                self.direction = Direction.RIGHT
            else:
                self.direction = Direction.LEFT
        elif self.velocity.x == 0:
            if self.velocity.y > 0:
                self.direction = Direction.DOWN
            else:
                self.direction = Direction.UP

    def handle_collision_with_objects(self, delta: float,
                                      physical_objects: List[pygame.Rect]):
        for idx, vec in enumerate((Vector2(1, 0), Vector2(0, 1))):
            self.hitbox.move_with_respect_to_parent(
                self.position + delta * (self.velocity.elementwise() * vec))
            if self.hitbox.has_collided_with_rects(*physical_objects):
                self.velocity[idx] = 0

    def handle_collision_with_enemy(self, enemy_group: EnemyGroup):
        enemy: Enemy
        for enemy in enemy_group:
            if self.cooldown.is_idle() and self.hitbox.has_collided_with(
                    enemy.hitbox):
                self.get_hit(enemy.position)
                enemy.stay_idle()
            if self.sword.has_hit(enemy):
                enemy.get_hit(self.direction)
            for arrow in self.bow.quiver:
                if arrow.has_hit(enemy):
                    enemy.get_hit(arrow.direction)
                    arrow.kill()

    def get_hit(self, enemy_position: Vector2):
        self.cooldown.restart()
        self.life -= 1
        vec_difference = self.position - enemy_position
        direction = Direction.closest_direction(vec_difference)
        self.velocity = cfg.VELOCITY * self.DAMAGE_MOMENTUM * direction.value

    def move(self, delta: float):
        self.position.update(self.position + delta * self.velocity)
        self.sprite.update(self.direction, self.position, self.velocity,
                           self.state)