96 lines
2.7 KiB
GDScript
96 lines
2.7 KiB
GDScript
extends RigidBody3D
|
|
|
|
class_name Grabbable_Object
|
|
|
|
@export var grabbable: bool = true
|
|
var is_grabbed: bool = false
|
|
var original_parent: Node = null
|
|
@export var grab_offset: Vector3 = Vector3(0.0, 0.0, -1.0)
|
|
@export var drop_forward_offset: float = 1.0
|
|
@export var weight: float = 5.0
|
|
|
|
@export var max_linear_speed: float = 15.0
|
|
@export var max_angular_speed: float = 10.0
|
|
|
|
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
|
|
# Limit linear speed
|
|
if state.linear_velocity.length() > max_linear_speed:
|
|
state.linear_velocity = state.linear_velocity.normalized() * max_linear_speed
|
|
|
|
# Limit speed of rotation (optional)
|
|
if state.angular_velocity.length() > max_angular_speed:
|
|
state.angular_velocity = state.angular_velocity.normalized() * max_angular_speed
|
|
|
|
func grab(player: Node3D):
|
|
if not grabbable or is_grabbed or player.grabbed_item:
|
|
return
|
|
is_grabbed = true
|
|
original_parent = get_parent()
|
|
|
|
# Disable physics
|
|
gravity_scale = 0.0
|
|
linear_velocity = Vector3.ZERO
|
|
angular_velocity = Vector3.ZERO
|
|
freeze = true
|
|
$CollisionShape3D.disabled = true
|
|
|
|
# Reparent under player
|
|
var current_transform = global_transform
|
|
get_parent().remove_child(self)
|
|
player.add_child(self)
|
|
player.grabbed_item = self
|
|
print(player.grabbed_item)
|
|
global_transform = current_transform
|
|
|
|
# Move above player's head
|
|
position = grab_offset
|
|
rotation = Vector3(0,0,0)
|
|
|
|
func can_drop(player: Node3D) -> bool:
|
|
var space_state = get_world_3d().direct_space_state
|
|
|
|
var start = player.global_position + Vector3(0, 2, 0)
|
|
var forward_dir = -player.global_transform.basis.z.normalized()
|
|
var end = start + forward_dir * drop_forward_offset
|
|
|
|
var query = PhysicsRayQueryParameters3D.create(start, end)
|
|
query.exclude = [player]
|
|
|
|
var result = space_state.intersect_ray(query)
|
|
|
|
return result.is_empty()
|
|
|
|
func release(player: Node3D, throw := false):
|
|
if not is_grabbed or not can_drop(player):
|
|
return
|
|
is_grabbed = false
|
|
|
|
# Re-enable physics
|
|
$CollisionShape3D.disabled = false
|
|
freeze = false
|
|
gravity_scale = 1.0
|
|
|
|
# Reparent back to world
|
|
var current_transform = global_transform
|
|
get_parent().remove_child(self)
|
|
original_parent.add_child(self)
|
|
global_transform = current_transform
|
|
|
|
# Place in front of player
|
|
var forward_dir = -player.global_transform.basis.z.normalized()
|
|
|
|
var player_velocity = player.velocity if player is CharacterBody3D else Vector3.ZERO
|
|
var throw_dir = Vector3(0,2.0,0) + forward_dir + (player_velocity * 0.25)
|
|
|
|
global_position = player.global_position + Vector3(0, 1.5, 0) + forward_dir * drop_forward_offset
|
|
|
|
# Give it a small push forward (optional)
|
|
if throw:
|
|
apply_impulse(throw_dir * 0.01)
|
|
else:
|
|
apply_impulse(forward_dir * 0.001)
|
|
player.grabbed_item = null
|
|
|
|
func get_weight():
|
|
return weight
|