From 6f18f6fa2b8d47c422e8087616f80d1a1edf68cd Mon Sep 17 00:00:00 2001 From: copygirl Date: Fri, 4 Jul 2025 20:17:37 +0200 Subject: [PATCH] Allow grabbing (and throwing) hearts --- Resources/heart.gd | 19 ++------------ Resources/heart.tscn | 1 + copyThrower.gd | 60 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 17 deletions(-) diff --git a/Resources/heart.gd b/Resources/heart.gd index 4c31d9d..0ffd2a5 100644 --- a/Resources/heart.gd +++ b/Resources/heart.gd @@ -86,8 +86,8 @@ func _process(delta: float) -> void: if get_parent() is CharacterBody3D: sticky_timer -= delta if sticky_timer <= 0: - set_physics_active(true) reparent(original_parent) + freeze = false func on_body_entered(body: Node) -> void: if not (body is CharacterBody3D): return @@ -99,20 +99,5 @@ func on_body_entered(body: Node) -> void: if sticky: await get_tree().process_frame; - set_physics_active(false) - set_gravity_scale(0.0) reparent(body) - - -func set_physics_active(active: bool) -> void: - if active: - sleeping = false - collision_mask = 1 - set_gravity_scale(1.0) - else: - sleeping = true - linear_velocity = Vector3.ZERO - angular_velocity = Vector3.ZERO - collision_mask = 0 - collision_layer = 0 - set_gravity_scale(0.0) + freeze = true diff --git a/Resources/heart.tscn b/Resources/heart.tscn index 0c99273..4200921 100644 --- a/Resources/heart.tscn +++ b/Resources/heart.tscn @@ -10,6 +10,7 @@ height = 0.04 radius = 0.055 [node name="Heart" type="RigidBody3D"] +collision_layer = 0 contact_monitor = true max_contacts_reported = 1 script = ExtResource("1_h4gh1") diff --git a/copyThrower.gd b/copyThrower.gd index 782b147..0071df7 100644 --- a/copyThrower.gd +++ b/copyThrower.gd @@ -48,9 +48,30 @@ var triggers = { "TransgenderPride" : [ 0.35, pride("transgender") ], } +const HAND_CLOSE_THRESHOLD : float = 12.0 +const HAND_OPEN_TRHESHOLD : float = 8.0 +var hands := { + RightHand = { closed = false, collider = null }, + LeftHand = { closed = false, collider = null }, +} + var queue: Array[RigidBody3D] = [] var queue_delay := 0.0 +func scene_init() -> void: + # Reset collider values, in case hands aren't found. + for hand in hands.values(): hand.collider = null + + var skeleton := get_skeleton() + if not skeleton: return + + for child in skeleton.get_children(): + var collider := child as AvatarCollider + if not collider: continue + var hand = hands.get(collider.bone_name) + if not hand: continue + hand.collider = collider + func handle_channel_chat_message( _cheerer_username: String, _cheerer_display_name: String, @@ -90,6 +111,7 @@ func handle_channel_chat_message( func _process(delta: float) -> void: _apply_bumping(delta) _throw_hearts_in_queue(delta) + _grab_with_hands() ## Applies body and head "bumping" that causes ## the avatar to "shake" in response to being hit. @@ -156,6 +178,44 @@ func _throw_hearts_in_queue(delta: float) -> void: object.global_position = pos object.linear_velocity = vel + object.add_to_group("copyThrower/objects") + +func _grab_with_hands() -> void: + for hand in hands.values(): + var closedness = _hand_closedness(hand.collider) + # When hand has just been closed, grab nearby hearts and reparent. + if (not hand.closed) and (closedness > HAND_CLOSE_THRESHOLD): + var body: CharacterBody3D = hand.collider.get_node("CharacterBody3D") + var hand_center = body.to_global(Vector3(0, 0, 0.08)) + for object: RigidBody3D in get_tree().get_nodes_in_group("copyThrower/objects"): + if object.global_position.distance_to(hand_center) < 0.16: + object.reparent(hand.collider) + object.global_position = hand_center + object.freeze = true + hand.closed = true + # When hand has just been opened, throw any grabbed hearts. + elif hand.closed and (closedness < HAND_OPEN_TRHESHOLD): + for hand_child in hand.collider.get_children(): + var object := hand_child as RigidBody3D + if not object: continue + object.reparent(object.original_parent) + object.linear_velocity = Vector3(0, 1.5, 3.0) * hand.collider.global_basis + Vector3.UP + object.freeze = false + hand.closed = false + +static func _hand_closedness(collider: AvatarCollider) -> float: + var total := 0.0 + var skeleton := collider.get_skeleton() + var fingers := skeleton.get_bone_children(collider.bone_idx) + for finger in fingers: total += _get_total_curl(skeleton, finger) + return total + +static func _get_total_curl(skeleton: Skeleton3D, bone: int, current := 0.0) -> float: + current += skeleton.get_bone_pose_rotation(bone).get_euler().x + var children := skeleton.get_bone_children(bone) + if children.size() == 1: + return _get_total_curl(skeleton, children[0], current) + else: return current func on_collide(object: RigidBody3D, body: CharacterBody3D) -> void: