class_name PoliceCar extends CharacterBody3D const FRAME_IDXS := 10 @export var dot_curve: Curve @export var max_speed := 8. @export var health := 10. @export var knockback_mul := 1. @export var invulnerable := false var frame_idx := 0 var frame_count := 0 var knockback := Vector3.ZERO var default_movement := true var stunned := false func set_rotation_to_velocity() -> void: rotation.y = Vector2(velocity.x, -velocity.z).angle() func exp_lerp(a: Variant, b: Variant, decay: float, dt: float) -> Variant: return lerp(a, b, 1 - exp(-decay * dt)) func _ready() -> void: frame_idx = randi() % FRAME_IDXS func _update_nav_agent() -> void: %NavAgent.target_position = Player.instance.global_position func _process_movement(delta: float) -> void: var dir := global_position.direction_to(%NavAgent.get_next_path_position()) dir.y = 0 var dot := dir.dot(velocity.normalized()) var dot_power := dot_curve.sample(dot) velocity += dir * 8. * delta * dot_power velocity = velocity.limit_length(max_speed) func _process(delta: float) -> void: if not default_movement: return if stunned: return frame_count = (frame_count + 1) % FRAME_IDXS if frame_count == frame_idx: _update_nav_agent() if knockback.length() < 0.1: _process_movement(delta) set_rotation_to_velocity() position.y = 0.01 else: velocity = knockback knockback = exp_lerp(knockback, Vector3.ZERO, 15., delta) move_and_slide() func hit(proj: Node3D, damage: float) -> bool: if invulnerable: return true health -= damage if health <= 0: queue_free() else: if proj is PlayerProjectile: knockback = proj.velocity * 2. * knockback_mul * proj.knockback_mul return true func _on_hurtbox_body_entered(body: Node3D) -> void: if body is Player: body.damage(self)