class_name ChairInteraction extends Area3D ## Sit-down interaction for the treatment chair. ## Shows a prompt when the player is in range, sits them on `interact`, ## smoothly lerps the camera to a seated pose, locks movement, and emits ## `player_seated`. Standing again unlocks movement and emits `player_stood`. signal player_seated signal player_stood @export var seat_transform_path: NodePath @export var lerp_duration: float = 0.6 @onready var prompt_label: Label = $PromptLayer/PromptLabel @onready var seat_marker: Node3D = get_node_or_null(seat_transform_path) var _player: PlayerController = null var _player_in_range: bool = false var _is_seated: bool = false var _stand_transform: Transform3D var _tween: Tween = null func _ready() -> void: if prompt_label: prompt_label.visible = false body_entered.connect(_on_body_entered) body_exited.connect(_on_body_exited) func _on_body_entered(body: Node3D) -> void: if body is PlayerController: _player = body _player_in_range = true _update_prompt() func _on_body_exited(body: Node3D) -> void: if body == _player and not _is_seated: _player_in_range = false _player = null _update_prompt() func _unhandled_input(event: InputEvent) -> void: if not event.is_action_pressed("interact"): return if _is_seated: _stand_up() elif _player_in_range and _player != null: _sit_down() func _update_prompt() -> void: if not prompt_label: return if _is_seated: prompt_label.text = "Press E to stand" prompt_label.visible = true elif _player_in_range: prompt_label.text = "Press E to sit" prompt_label.visible = true else: prompt_label.visible = false func _sit_down() -> void: if seat_marker == null: push_warning("ChairInteraction: no seat marker assigned; cannot sit.") return _is_seated = true _stand_transform = _player.global_transform _player.set_movement_locked(true) _update_prompt() _lerp_player_to(seat_marker.global_transform, func() -> void: player_seated.emit() ) func _stand_up() -> void: _is_seated = false _update_prompt() _lerp_player_to(_stand_transform, func() -> void: if _player: _player.set_movement_locked(false) player_stood.emit() # Refresh range state after standing. _player_in_range = overlaps_body(_player) if _player else false _update_prompt() ) func _lerp_player_to(target: Transform3D, on_done: Callable) -> void: if _tween and _tween.is_running(): _tween.kill() _tween = create_tween() _tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) _tween.tween_method(_apply_player_transform.bind(_player.global_transform, target), 0.0, 1.0, lerp_duration) _tween.tween_callback(on_done) func _apply_player_transform(t: float, from_xform: Transform3D, to_xform: Transform3D) -> void: if _player: _player.global_transform = from_xform.interpolate_with(to_xform, t)