extends RigidBody3D ## If this heart were to exist for longer than 1 minute for some reason, destroy it. ## The heart should also disappear if it falls low enough. var lifetime := 60.0 # 1 minute ## If this heart has impacted another object already. ## Prevents repeated impact sounds and sticking. var impacted := false ## Relative size of the heart. Affects audio pitch. ## Used later to decide how impactful a collision was. var size := 1.0 ## Whether the heart will stick to avatars when colliding. var sticky := false ## How long the heart will be sticking before falling off again. @onready var sticky_timer := randf_range(3, 6) # 3 to 6 seconds # Remember the original parent this node was created under, so if the # heart sticks to a character, it can return here once it unsticks. @onready var original_parent: Node = get_parent() static var stream_normal: AudioStream = load("res://Mods/copyThrower/Resources/normal_randomizer.tres") static var stream_sticky: AudioStream = load("res://Mods/copyThrower/Resources/sticky_randomizer.tres") func _ready() -> void: body_entered.connect(on_body_entered) # Give it a little bit of random spin. var random_vector := Vector3(randf() - 0.5, randf() - 0.5, randf() - 0.5).normalized() angular_velocity = random_vector * randf_range(0, 25) * TAU angular_damp = 5.0 func set_size(value: float) -> void: size = value $Model.scale *= value $CollisionShape3D.scale *= value $AudioStreamPlayer3D.pitch_scale = 1 / lerpf(1, value, 0.5) func set_sticky(value: bool) -> void: sticky = value if value: $AudioStreamPlayer3D.stream = stream_sticky else: $AudioStreamPlayer3D.stream = stream_normal func set_material(material: StandardMaterial3D) -> void: $Model/Heart.material_override = material func _process(delta: float) -> void: # Kill heart if it ever lives too long. lifetime -= delta if lifetime <= 0: queue_free(); return # Kill heart if it falls too low. if global_position.y < -10: queue_free(); return # Unsticky heart after some time. if get_parent() is CharacterBody3D: sticky_timer -= delta if sticky_timer <= 0: set_physics_active(true) reparent(original_parent) func on_body_entered(body: Node) -> void: if not (body is CharacterBody3D): return if impacted: return impacted = true $AudioStreamPlayer3D.play() original_parent.on_collide(self, body) 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)