class_name TreeNode extends Node3D var tree_data: TreeDataResource var model_instance: Node3D var mesh_instances: Array[MeshInstance3D] = [] var original_materials: Array[Material] = [] var outline_material: Material var is_invisible: bool = false var interact_range: bool = false var fade_tween: Tween @onready var area: Area3D = $InteractRange @onready var last_blocked: Timer = $LastBlocked func _ready(): setup_outline_material() area.body_entered.connect(_on_player_entered) area.body_exited.connect(_on_player_exited) last_blocked.timeout.connect(_on_fade_timer_timeout) func set_tree_data(data: TreeDataResource): tree_data = data spawn_model() func spawn_model(): if not tree_data or not tree_data.model: Log.pr("No tree data or model provided") return # Clear any existing model if model_instance: model_instance.queue_free() mesh_instances.clear() original_materials.clear() # Instantiate the model from the TreeDataResource model_instance = tree_data.model.instantiate() tree_data.apply_seasonal_colors(model_instance, Season.current) add_child(model_instance) # Re-scan for mesh instances in the new model find_all_mesh_instances(model_instance) # Apply any additional properties from tree_data apply_tree_properties() func apply_tree_properties(): if not model_instance or not tree_data: return # Apply scale variations if defined in tree_data if tree_data.has_method("get_random_scale"): model_instance.scale *= tree_data.get_random_scale() # Apply random scale based on tree properties if tree_data.max_height > 0: var scale_variation = randf_range(1.5, 2) model_instance.scale *= scale_variation func setup_outline_material() -> void: # Create outline material with your shader outline_material = ShaderMaterial.new() outline_material.shader = preload("res://outline.gdshader") outline_material.set_shader_parameter("color", Vector3(0.702, 0.557, 0.259)) func find_all_mesh_instances(node: Node): if node is MeshInstance3D: mesh_instances.append(node) for child in node.get_children(): find_all_mesh_instances(child) func fade_out_tree_simple(duration: float = 0.25): # Reset the timer last_blocked.stop() last_blocked.start() if is_invisible or interact_range: return is_invisible = true # Kill any existing fade tween if fade_tween: fade_tween.kill() for mesh_instance in mesh_instances: fade_mesh_materials(mesh_instance, 1.0, 0.1, duration) # This gets called when the timer times out func _on_fade_timer_timeout(): fade_in_tree_immediate() func fade_in_tree_immediate(duration: float = 0.25): if not is_invisible: return # Kill any existing fade tween if fade_tween: fade_tween.kill() for mesh_instance in mesh_instances: fade_mesh_materials(mesh_instance, 0.1, 1.0, duration) is_invisible = false func fade_mesh_materials(mesh_instance: MeshInstance3D, from_alpha: float, to_alpha: float, duration: float): if not mesh_instance.mesh: return var surface_count = mesh_instance.mesh.get_surface_count() for surface_id in range(surface_count): var material = mesh_instance.get_surface_override_material(surface_id) # Always ensure we have a unique material instance if not material: # No override material - duplicate from base material var base_material = mesh_instance.mesh.surface_get_material(surface_id) if base_material: material = base_material.duplicate() mesh_instance.set_surface_override_material(surface_id, material) else: # Override material exists - duplicate it to ensure uniqueness material = material.duplicate() mesh_instance.set_surface_override_material(surface_id, material) # Only work with StandardMaterial3D if material and material is StandardMaterial3D: var std_mat = material as StandardMaterial3D # Set transparency mode based on target alpha if to_alpha < 1.0: std_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA # Force shadow casting mesh_instance.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON # Set initial alpha std_mat.albedo_color.a = from_alpha # Create and store the tween fade_tween = create_tween() fade_tween.tween_method( func(alpha: float): std_mat.albedo_color.a = alpha, from_alpha, to_alpha, duration ) # When fading to full opacity, reset transparency mode if to_alpha >= 1.0: fade_tween.tween_callback( func(): std_mat.transparency = BaseMaterial3D.TRANSPARENCY_DISABLED ) func toggle_tree_visibility(duration: float = 1.0): if is_invisible: fade_in_tree_immediate(duration) else: fade_out_tree_simple(duration) func _on_player_entered(body: Node3D): if body.is_in_group("player"): # Apply outline to all mesh instances interact_range = true if not is_invisible: for mesh in mesh_instances: mesh.material_overlay = outline_material func _on_player_exited(body: Node3D): if body.is_in_group("player"): interact_range = false # Remove outline from all mesh instances if not is_invisible: # Reset the material overlay to null for mesh in mesh_instances: mesh.material_overlay = null