diff --git a/game/scenes/Interaction/mushroom.gd b/game/scenes/Interaction/mushroom.gd index 0803236..0be1d27 100644 --- a/game/scenes/Interaction/mushroom.gd +++ b/game/scenes/Interaction/mushroom.gd @@ -6,11 +6,20 @@ extends Node3D @export var max_mushrooms_in_radius := 3 @export var check_radius := 1.5 @export var max_total_mushrooms := 50 +@export var interact_radius := 2.0 +@export var look_at_threshold := 0.95 static var total_mushrooms := 0 @onready var spread_timer: Timer = $SpreadTimer @onready var ground_ray: RayCast3D = $GroundRay +@onready var cap: MeshInstance3D = $Cap +@onready var stem: MeshInstance3D = $Stem + +var _player_in_range := false +var _is_highlighted := false +var highlight_mat: StandardMaterial3D +var _audio_player: AudioStreamPlayer3D func _ready() -> void: total_mushrooms += 1 @@ -19,6 +28,111 @@ func _ready() -> void: # Randomize initial scale for variety scale = Vector3.ONE * randf_range(0.8, 1.2) rotation.y = randf_range(0, TAU) + + # Setup interaction area + var area = Area3D.new() + add_child(area) + var shape = CollisionShape3D.new() + var sphere = SphereShape3D.new() + sphere.radius = interact_radius + shape.shape = sphere + area.add_child(shape) + + area.body_entered.connect(_on_body_entered) + area.body_exited.connect(_on_body_exited) + + highlight_mat = StandardMaterial3D.new() + highlight_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA + highlight_mat.albedo_color = Color(1.0, 1.0, 1.0, 0.3) + highlight_mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED + + _audio_player = AudioStreamPlayer3D.new() + _audio_player.stream = preload("res://assets/audio/pop.ogg") + add_child(_audio_player) + +func _process(_delta: float) -> void: + if not _player_in_range: + _set_highlight(false) + return + + var cam = get_viewport().get_camera_3d() + if not cam: + _set_highlight(false) + return + + var dir_to_mushroom = (global_position - cam.global_position).normalized() + var forward = -cam.global_transform.basis.z + var my_dot = forward.dot(dir_to_mushroom) + + if my_dot > look_at_threshold: + # Check if we are the most directly looked-at mushroom in range + var is_best = true + for mushroom in get_tree().get_nodes_in_group("mushrooms"): + if mushroom == self or not mushroom.get("_player_in_range"): + continue + + var m_dir = (mushroom.global_position - cam.global_position).normalized() + var m_dot = forward.dot(m_dir) + + if m_dot > my_dot: + is_best = false + break + elif m_dot == my_dot and mushroom.get_instance_id() > get_instance_id(): + # Tie breaker + is_best = false + break + + _set_highlight(is_best) + else: + _set_highlight(false) + +func _set_highlight(value: bool) -> void: + if _is_highlighted == value: return + _is_highlighted = value + + if value: + if cap: cap.material_overlay = highlight_mat + if stem: stem.material_overlay = highlight_mat + else: + if cap: cap.material_overlay = null + if stem: stem.material_overlay = null + +func _on_body_entered(body: Node3D) -> void: + if body.is_in_group("player"): + _player_in_range = true + +func _on_body_exited(body: Node3D) -> void: + if body.is_in_group("player"): + _player_in_range = false + _set_highlight(false) + +func _unhandled_input(event: InputEvent) -> void: + if _is_highlighted and event.is_action_pressed("interact"): + get_viewport().set_input_as_handled() + _collect_mushroom() + +func _collect_mushroom() -> void: + # Disable interaction and visuals + _is_highlighted = false + _set_highlight(false) + _player_in_range = false + + if cap: cap.visible = false + if stem: stem.visible = false + + # Stop spreading logic + spread_timer.stop() + + # Disable processing so it can't be interacted with again + set_process_unhandled_input(false) + set_process(false) + + # Play sound + _audio_player.play() + + # Wait for sound to finish, then free + await _audio_player.finished + queue_free() func _setup_timer() -> void: spread_timer.wait_time = randf_range(spread_interval_min, spread_interval_max)