extends SpringArm3D @onready var player: CharacterBody3D = $"../.." # --- Paramètres de mouvement --- @export var max_follow_speed: float = 3 @export var speed_acceleration: float = 2.5 @export var height_offset: float = 2.5 @export var wait_time: float = 0.5 # --- Paramètres de Dialogue --- @export var dialog_zoom_length: float = 3.5 @export var dialog_side_offset: float = 2.0 @export var transition_speed: float = 4.0 @onready var normal_spring_length: float = spring_length var delay_timer: float = 0.0 var current_follow_speed: float = 0.0 var current_side_offset: float = 0.0 var target_side_offset: float = 0.0 var is_in_dialog: bool = false # Variables pour le dialogue var dialog_target_position: Vector3 = Vector3.ZERO var target_dialog_rotation_y: float = 0.0 var dialog_weight: float = 0.0 # Permet une transition ultra-fluide (0.0 = Exploration, 1.0 = Dialogue) func _ready() -> void: top_level = true func set_dialog_mode(npc_position: Vector3 = Vector3.ZERO) -> void: is_in_dialog = !is_in_dialog if is_in_dialog: dialog_target_position = npc_position var dir_to_npc = (npc_position - player.global_position) dir_to_npc.y = 0 dir_to_npc = dir_to_npc.normalized() target_dialog_rotation_y = atan2(-dir_to_npc.x, -dir_to_npc.z) target_side_offset = dialog_side_offset else: target_side_offset = 0.0 func _physics_process(delta: float) -> void: if not is_instance_valid(player): return # Mise à jour progressive du poids de transition du dialogue (de 0 à 1) var target_weight = 1.0 if is_in_dialog else 0.0 dialog_weight = lerp(dialog_weight, target_weight, transition_speed * delta) # ========================================== # 1. GESTION DE LA ROTATION # ========================================== if is_in_dialog or dialog_weight > 0.01: delay_timer = 0.3 current_follow_speed = 0.5 # On interpole la rotation vers l'angle de dialogue en utilisant le poids global_rotation.y = lerp_angle(global_rotation.y, target_dialog_rotation_y, transition_speed * delta) if not is_in_dialog and dialog_weight <= 0.01: var target_rotation_y = player.global_rotation.y if player.can_play: var angle_diff = abs(angle_difference(global_rotation.y, target_rotation_y)) if angle_diff > 0.02 and player.velocity.length() > 0.1: delay_timer += delta else: delay_timer = 0.3 current_follow_speed = 0.5 if delay_timer >= wait_time: current_follow_speed = move_toward(current_follow_speed, max_follow_speed, speed_acceleration * delta) global_rotation.y = lerp_angle(global_rotation.y, target_rotation_y, current_follow_speed * delta) # ========================================== # 2. GESTION DU ZOOM, DU DECALAGE ET DE LA POSITION # ========================================== var target_length = dialog_zoom_length if is_in_dialog else normal_spring_length spring_length = lerp(spring_length, target_length, transition_speed * delta) current_side_offset = lerp(current_side_offset, target_side_offset, transition_speed * delta) var right_direction = global_transform.basis.x # Transition fluide du point de pivot (de la position du joueur vers le point intermédiaire du dialogue) var normal_base = player.global_position var dialog_base = player.global_position.lerp(dialog_target_position, 0.3) var current_base = normal_base.lerp(dialog_base, dialog_weight) global_position = current_base + Vector3(0, height_offset, 0) + (right_direction * current_side_offset)