139 lines
4.0 KiB
GDScript
139 lines
4.0 KiB
GDScript
extends Interactable
|
|
class_name Pickupable
|
|
|
|
@export var springStrength: float = 150.0
|
|
@export var springDamping: float = 50.0
|
|
@export var rotateStrength: float = 10.0
|
|
@export var rotateDamping: float = 2.0
|
|
|
|
@export var maxLiftForce: float = 600.0
|
|
@export var maxTorque: float = 40.0
|
|
@export var maxThrowImpulse: float = 100.0
|
|
|
|
@export var throwSpeed: float = 10.0
|
|
@export var maxDistance: float = 4.0
|
|
@export var body: RigidBody3D
|
|
@export var snapAnchor: Marker3D
|
|
@export var visualMeshNode: MeshInstance3D
|
|
|
|
@export_range(0.01, 1000.0, 0.01) var bodyMass: float = 1.0
|
|
|
|
var dragging: bool = false
|
|
var rotating: bool = false
|
|
var playerRef
|
|
|
|
var holdDistance: float
|
|
var rotationOffset: Basis
|
|
var localCenterOffset: Vector3
|
|
|
|
var currentSocket: Socket = null
|
|
|
|
func _ready():
|
|
if body:
|
|
body.mass = bodyMass
|
|
|
|
func beginInteract(player, hitPosition: Vector3):
|
|
playerRef = player
|
|
dragging = true
|
|
|
|
var holdPoint = player.hand
|
|
holdDistance = holdPoint.global_position.distance_to(hitPosition)
|
|
rotationOffset = holdPoint.global_basis.inverse() * body.global_basis
|
|
localCenterOffset = body.global_basis.inverse() * (body.global_position - hitPosition)
|
|
|
|
func updateInteract(player):
|
|
if !dragging:
|
|
return
|
|
|
|
if Input.is_action_just_pressed("action") and currentSocket != null:
|
|
if currentSocket.attachedObject == self:
|
|
currentSocket.detachObject()
|
|
else:
|
|
if currentSocket.attachObject(self):
|
|
endInteract(player)
|
|
return
|
|
|
|
body.sleeping = false
|
|
|
|
# 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
|
|
|
|
if rotating:
|
|
var previewTargetBasis = holdPoint.global_basis * rotationOffset
|
|
var previewCurrentQuat = body.global_basis.get_rotation_quaternion()
|
|
var previewTargetQuat = previewTargetBasis.get_rotation_quaternion()
|
|
|
|
var previewDiff = previewTargetQuat * previewCurrentQuat.inverse()
|
|
if previewDiff.w < 0.0:
|
|
previewDiff = -previewDiff
|
|
|
|
if previewDiff.get_angle() < 1.0:
|
|
var delta = player.interactionMouseDelta
|
|
var weightPenalty = 1.0 / (1.0 + (body.mass * 0.1))
|
|
|
|
var rot_x = deg_to_rad(-delta.x * 0.35 * weightPenalty)
|
|
var rot_y = deg_to_rad(-delta.y * 0.35 * weightPenalty)
|
|
|
|
rotationOffset = Basis(Vector3.UP, rot_x) * rotationOffset
|
|
rotationOffset = Basis(Vector3.RIGHT, rot_y) * 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.w < 0.0:
|
|
diff = -diff
|
|
|
|
var angle = diff.get_angle()
|
|
if angle > 0.001:
|
|
var angularAcceleration = (diff.get_axis() * angle * rotateStrength * 10.0) - (body.angular_velocity * rotateDamping * 10.0)
|
|
var invI = body.get_inverse_inertia_tensor()
|
|
var torque = Vector3.ZERO
|
|
|
|
if abs(invI.determinant()) > 0.00001:
|
|
var I = invI.inverse()
|
|
torque = I * angularAcceleration
|
|
else:
|
|
torque = angularAcceleration * body.mass * 0.01
|
|
torque = torque.limit_length(maxTorque)
|
|
body.apply_torque(torque)
|
|
|
|
var targetGrabPos = holdPoint.global_position + (-holdPoint.global_basis.z * holdDistance)
|
|
var targetCenterPos = targetGrabPos + (targetBasis * localCenterOffset)
|
|
var posError = targetCenterPos - body.global_position
|
|
var force = (posError * springStrength) - (body.linear_velocity * springDamping)
|
|
force *= body.mass
|
|
force = force.limit_length(maxLiftForce)
|
|
body.apply_central_force(force)
|
|
|
|
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
|
|
var throwImpulse = direction * throwSpeed * body.mass
|
|
throwImpulse = throwImpulse.limit_length(maxThrowImpulse)
|
|
body.apply_impulse(throwImpulse)
|
|
endInteract(playerRef)
|