nature-sim/Entities/Tree/scripts/tree.gd
2025-07-02 16:17:58 +01:00

177 lines
5.1 KiB
GDScript

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