extends CharacterBody3D @export var speed: float = 6.0 @export var jumpVelocity: float = 5.0 @export var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity") as float @export var acceleration: float = 20.0 @export var airAcceleration: float = 4.0 @export var friction: float = 10.0 var mouseBaseSensitivity: float = 0.004 var mouseSensitivity: float = 0.0 @export var mouseSensitivityMultiplier: float = 0.2 @export var head: Node3D @export var camera: Camera3D @export var raycast: RayCast3D @export var hand: Marker3D @export var crosshair: CanvasLayer var lateJumpTime: float = 0.15 var lateJumpTimer: float = 0.0 var currentInteractable: Interactable var dragging: bool = false var pitch: float = 0.0 var playerId: String var displayName: String var mouseDelta: Vector2 = Vector2.ZERO var interactionMouseDelta: Vector2 = Vector2.ZERO var mouseHorizontalFactor: float = 1.0 var mouseVerticalFactor: float = 1.0 var targetMouseHorizontalFactor: float = 1.0 var targetMouseVerticalFactor: float = 1.0 @export var mouseFactorLerpSpeed: float = 2.0 func _ready(): mouseSensitivity = mouseBaseSensitivity * mouseSensitivityMultiplier Input.mouse_mode = Input.MOUSE_MODE_CAPTURED func _input(event: InputEvent) -> void: if event.is_action_pressed("ui_cancel"): if Input.mouse_mode == Input.MOUSE_MODE_CAPTURED: Input.mouse_mode = Input.MOUSE_MODE_VISIBLE else: Input.mouse_mode = Input.MOUSE_MODE_CAPTURED func _unhandled_input(event: InputEvent) -> void: if Input.mouse_mode != Input.MOUSE_MODE_CAPTURED: return if event is InputEventMouseMotion: if dragging and currentInteractable: interactionMouseDelta += event.relative mouseDelta += event.relative func _physics_process(delta): mouseHorizontalFactor = lerp(mouseHorizontalFactor, targetMouseHorizontalFactor, delta * mouseFactorLerpSpeed) mouseVerticalFactor = lerp(mouseVerticalFactor, targetMouseVerticalFactor, delta * mouseFactorLerpSpeed) if mouseDelta != Vector2.ZERO: rotate_y(-mouseDelta.x * mouseSensitivity * mouseHorizontalFactor) pitch -= mouseDelta.y * mouseSensitivity * mouseVerticalFactor pitch = clamp(pitch, deg_to_rad(-90), deg_to_rad(90)) head.rotation.x = pitch mouseDelta = Vector2.ZERO if !is_on_floor(): velocity.y -= gravity * delta lateJumpTimer -= delta else: lateJumpTimer = lateJumpTime # Interaction search if !dragging: if raycast.is_colliding(): currentInteractable = Interactable.find(raycast.get_collider()) crosshair.setHover(true) else: currentInteractable = null crosshair.setHover(false) # Start interaction if !dragging and currentInteractable: if Input.is_action_just_pressed("interact"): dragging = true currentInteractable.beginInteract(self, raycast.get_collision_point()) # Update interaction if dragging and currentInteractable: if Input.is_action_just_pressed("throw"): if currentInteractable is Pickupable: currentInteractable.throw() stopInteraction() return currentInteractable.updateInteract(self) interactionMouseDelta = Vector2.ZERO # End interaction if dragging and currentInteractable: if Input.is_action_just_released("interact"): stopInteraction() # Late jump buffering if Input.is_action_just_pressed("jump") and lateJumpTimer > 0.0: velocity.y = jumpVelocity # Movement var inputDir := Input.get_vector("left", "right", "forward", "backward") var direction := (transform.basis * Vector3(inputDir.x, 0, inputDir.y)).normalized() if is_on_floor(): if direction != Vector3.ZERO: velocity.x = direction.x * speed velocity.z = direction.z * speed else: velocity.x = lerp(velocity.x, 0.0, delta * friction) velocity.z = lerp(velocity.z, 0.0, delta * friction) else: velocity.x = lerp(velocity.x, direction.x * speed, delta * airAcceleration) velocity.z = lerp(velocity.z, direction.z * speed, delta * airAcceleration) move_and_slide() func setMouseFactors(horizontal: float, vertical: float): targetMouseHorizontalFactor = clamp(horizontal, 0.0, 1.0) targetMouseVerticalFactor = clamp(vertical, 0.0, 1.0) func stopInteraction(): if currentInteractable: currentInteractable.endInteract(self) currentInteractable = null dragging = false interactionMouseDelta = Vector2.ZERO