extends Interactable class_name Pickupable @export var springStrength: float = 150.0 @export var springDamping: float = 50.0 @export var rotateStrength: float = 5.0 @export var rotateDamping: float = 5.0 @export var throwForce: float = 10.0 @export var maxDistance: float = 4.0 @export var body: RigidBody3D @export var snapAnchor: Marker3D @export_range(0.01, 1000.0, 0.01) var bodyMass: float = 1.0 var dragging: bool = false var rotating: bool = false var playerRef var grabLocal: Vector3 var holdDistance: float var rotationOffset: Basis func _ready(): if body: body.mass = bodyMass func beginInteract(player, hitPosition: Vector3): playerRef = player dragging = true grabLocal = body.to_local(hitPosition) var holdPoint = player.hand holdDistance = holdPoint.global_position.distance_to(hitPosition) rotationOffset = holdPoint.global_basis.inverse() * body.global_basis func updateInteract(player): if !dragging: return # Toggle rotation mode if Input.is_action_just_pressed("rotate"): rotating = true playerRef.setMouseFactors(0.0, 0.0) if Input.is_action_just_released("rotate"): rotating = false playerRef.setMouseFactors(1.0, 1.0) var holdPoint = player.hand # Release if too far away if body.global_position.distance_to(player.global_position) > maxDistance: endInteract(player) return # Target position var targetPosition = holdPoint.global_position + (-holdPoint.global_basis.z * holdDistance) var offset = targetPosition - body.global_position # Linear spring body.apply_force(offset * springStrength - body.linear_velocity * springDamping, body.to_global(grabLocal) - body.global_position) if rotating: var delta = player.interactionMouseDelta rotationOffset = Basis(Vector3.UP, deg_to_rad(-delta.x * 0.35)) * rotationOffset rotationOffset = Basis(Vector3.RIGHT, deg_to_rad(delta.y * 0.35)) * rotationOffset # Angular spring var targetBasis = holdPoint.global_basis * rotationOffset var currentQuat = body.global_basis.get_rotation_quaternion() var targetQuat = targetBasis.get_rotation_quaternion() var diff = targetQuat * currentQuat.inverse() if diff.dot(Quaternion.IDENTITY) < 0: diff = -diff var angle = diff.get_angle() if angle > 0.001: body.apply_torque(diff.get_axis() * angle * rotateStrength - body.angular_velocity * rotateDamping) func endInteract(player): dragging = false rotating = false player.setMouseFactors(1.0, 1.0) playerRef = null func throw(): if !dragging: return var direction = -playerRef.camera.global_basis.z body.apply_impulse(direction * throwForce)