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
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)