grunk/src/player/hold_component.gd

124 lines
3.2 KiB
GDScript3
Raw Normal View History

2025-07-04 14:56:35 -06:00
class_name HoldComponent extends Node3D
## Component for managing physics object holding
signal held
signal dropped
2025-07-07 12:05:45 -06:00
const ROTATION_SCALE_FACTOR := 10.0 * TAU
## Held object position lerp acceleration.
@export var hold_accel := 20.0
## Held object position velocity scale.
@export var hold_speed := 12.0
## The maximum distance the object may be from the hold point before breaking the hold.
@export var max_distance := 3.0
## Temporary linear damping for held objects
@export var hold_damping := 8.0
2025-07-07 11:18:56 -06:00
## Impulse force when throwing an object
@export var throw_force := 100.0
## Temporary collision layer.
@export_flags_3d_physics var hold_collision_layer := 0b01000000
## Temporary collision mask.
@export_flags_3d_physics var hold_collision_physics := 0b01000001
## The object currently being held.
var _held_object: RigidBody3D
var _original_damping: float
var _original_collision_layer: int
var _original_collision_mask: int
@onready var interact_ray: RayCast3D = %InteractRay
@onready var hold_point: Marker3D = %HoldPoint
func attach(prop: RigidBody3D, hold_distance: float) -> void:
hold_point.position = Vector3(0, 0, -hold_distance)
2025-07-07 11:18:56 -06:00
hold_point.global_basis = global_basis
_held_object = prop
_original_damping = prop.linear_damp
prop.linear_damp = hold_damping
_original_collision_layer = prop.collision_layer
prop.collision_layer = hold_collision_layer
_original_collision_mask = prop.collision_mask
prop.collision_mask = hold_collision_physics
held.emit()
2025-07-04 14:56:35 -06:00
func drop() -> void:
_held_object.linear_damp = _original_damping
_held_object.collision_layer = _original_collision_layer
_held_object.collision_mask = _original_collision_mask
_held_object = null
dropped.emit()
2025-07-04 14:56:35 -06:00
2025-07-07 11:18:56 -06:00
func throw() -> void:
var impulse := -global_basis.z.normalized() * throw_force
_held_object.apply_central_impulse(impulse)
drop()
2025-07-07 12:05:45 -06:00
func rotate_object(relative_motion: Vector2) -> void:
hold_point.rotate(
Vector3.UP,
relative_motion.x * GameSettings.object_rotation_sensitivity_x / ROTATION_SCALE_FACTOR
)
hold_point.rotate(
Vector3.RIGHT,
relative_motion.y * GameSettings.object_rotation_sensitivity_y / ROTATION_SCALE_FACTOR
)
func holding_object() -> bool:
return !!_held_object
2025-07-07 11:18:56 -06:00
func _unhandled_input(event: InputEvent) -> void:
2025-07-07 12:05:45 -06:00
var mouse_motion := event as InputEventMouseMotion
if (
mouse_motion
and Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED
and holding_object()
and Input.is_action_pressed("rotate_object")
):
rotate_object(mouse_motion.relative)
get_viewport().set_input_as_handled()
2025-07-07 11:18:56 -06:00
func _process_hold_controls() -> void:
if Input.is_action_just_pressed("interact"):
drop()
elif Input.is_action_just_pressed("fire"):
throw()
func _physics_process(delta: float) -> void:
# Object hold action logic
2025-07-07 11:18:56 -06:00
if holding_object():
_process_hold_controls()
# Held object logic
2025-07-04 14:56:35 -06:00
if not holding_object():
return
var diff := hold_point.global_position - (_held_object.global_position)
if diff.length() > max_distance:
drop()
return
var weight := 1 - exp(-hold_accel * delta)
_held_object.linear_velocity = _held_object.linear_velocity.lerp(diff * hold_speed, weight)
2025-07-07 11:18:56 -06:00
_held_object.global_basis = _held_object.global_basis.slerp(hold_point.global_basis, weight)