extends Node3D @export_group("Luce Direzionale e Ambiente") @export var color_mattina: Color = Color(1.0, 0.9, 0.8, 1.0) @export var color_pomeriggio: Color = Color(1.0, 0.6, 0.2, 1.0) @export var color_notte: Color = Color(0.1, 0.1, 0.3, 1.0) @export_group("Rotazione Sole (Gradi)") @export var rotazione_sole_mattina: Vector3 = Vector3(-15.0, 45.0, 0.0) @export var rotazione_sole_pomeriggio: Vector3 = Vector3(-75.0, 10.0, 0.0) @export var rotazione_sole_notte: Vector3 = Vector3(-35.0, -120.0, 0.0) @export_group("Esposizione Generale") @export var esposizione_mattina: float = 1.0 @export var esposizione_pomeriggio: float = 0.95 @export var esposizione_notte: float = 0.4 @export_group("Colori Cielo - Alto (Top)") @export var sky_top_mattina: Color = Color(0.35, 0.65, 0.9, 1.0) @export var sky_top_pomeriggio: Color = Color(0.7, 0.45, 0.2, 1.0) @export var sky_top_notte: Color = Color(0.05, 0.05, 0.15, 1.0) @export_group("Colori Cielo - Orizzonte (Horizon)") @export var sky_horizon_mattina: Color = Color(0.75, 0.85, 0.95, 1.0) @export var sky_horizon_pomeriggio: Color = Color(0.95, 0.65, 0.35, 1.0) @export var sky_horizon_notte: Color = Color(0.15, 0.1, 0.25, 1.0) @export_group("Gradiente Globale Shader") @export_subgroup("Colore Top (Alto)") @export var grad_top_mattina: Color = Color(0.8, 0.4, 0.2, 1.0) @export var grad_top_pomeriggio: Color = Color(0.2, 0.3, 0.5, 1.0) @export var grad_top_notte: Color = Color(0.05, 0.05, 0.1, 1.0) @export_subgroup("Colore Bottom (Basso)") @export var grad_bot_mattina: Color = Color(0.5, 0.2, 0.1, 1.0) @export var grad_bot_pomeriggio: Color = Color(0.1, 0.15, 0.25, 1.0) @export var grad_bot_notte: Color = Color(0.01, 0.01, 0.05, 1.0) @export_subgroup("Intensità Gradiente") @export var intensita_grad_mattina: float = 0.5 @export var intensita_grad_pomeriggio: float = 0.3 @export var intensita_grad_notte: float = 0.8 @export_group("Colori Acqua (Deep)") @export var acqua_deep_mattina: Color = Color(0.0, 0.1, 0.2, 1.0) @export var acqua_deep_pomeriggio: Color = Color(0.0, 0.15, 0.25, 1.0) @export var acqua_deep_notte: Color = Color(0.0, 0.02, 0.05, 1.0) @export_range(0.0, 1.0) var oscuramento_acqua_pioggia: float = 0.6 # <--- SLIDER OSCURAMENTO PIOGGIA @export_group("Nebbia (Fog)") @export var colore_nebbia_mattina: Color = Color(0.75, 0.85, 0.95, 1.0) @export var colore_nebbia_pomeriggio: Color = Color(0.95, 0.65, 0.35, 1.0) @export var colore_nebbia_notte: Color = Color(0.15, 0.1, 0.25, 1.0) @export var densita_nebbia_mattina: float = 0.005 @export var densita_nebbia_pomeriggio: float = 0.002 @export var densita_nebbia_notte: float = 0.015 @export_group("Bloom (Glow)") @export var bloom_mattina: float = 1.2 @export var bloom_pomeriggio: float = 0.8 @export var bloom_notte: float = 2.0 @export_group("Ciclo Temporale") @export_range(1.0, 3.0) var tempo_giorno: float = 1.0 @export var durata_transizione: float = 3.0 @export_range(0.0, 1.0) var energia_luce_notte: float = 0.3 @export_group("Materiali Extra") @export var materiale_nebbia: ShaderMaterial @export var materiale_pioggia_gocce: StandardMaterial3D @export var materiale_nuvole: ShaderMaterial @export var nodo_polvere: ColorRect @export var materiale_acqua: ShaderMaterial @export var materiale_fili_luce: ShaderMaterial @export_group("Meteo (Pioggia & Fulmini)") @export var colore_mood_pioggia: Color = Color(0.5, 0.6, 0.7, 1.0) @export var tempo_min_fulmine: float = 5.0 @export var tempo_max_fulmine: float = 15.0 @export var colore_fulmine: Color = Color(1.0, 1.0, 0.8, 1.0) @export var fulmini_min: int = 1 @export var fulmini_max: int = 3 @export var scala_min_fulmine: float = 1.0 @export var scala_max_fulmine: float = 3.0 @export_group("Cinematic Mode Ambiente") @export var intervallo_eventi_meteo: float = 8.0 @export_group("God Rays") @export var godray_scene: PackedScene @export var max_rain_godrays: int = 8 @export var godray_spawn_radius: float = 15.0 @export var godray_spawn_offset: Vector3 = Vector3(15.0, 5.0, -15.0) @export var godray_rotation_degrees: Vector3 = Vector3(-45.0, 45.0, -30.0) @export var godray_spawn_height: float = 5.0 @export var godray_scale: Vector3 = Vector3(2.0, 6.0, 2.0) @export_group("Lucciole") @export var particelle_lucciole: GPUParticles3D @export var quantita_lucciole: int = 60 @export var raggio_spawn_lucciole: float = 20.0 @export var altezza_spawn_lucciole: float = 3.0 @export_group("Vento") @export var particelle_vento: GPUParticles3D @export var quantita_vento: int = 100 @export var raggio_spawn_vento: float = 25.0 @export var altezza_spawn_vento: float = 5.0 @export_range(0.0, 5.0) var forza_vento_fili_base: float = 1.0 @export_group("Neve") @export var bottone_neve: Button @export var bottone_nevicata: Button @export var overlay_nebbia_schermo: ColorRect @export var particelle_neve: GPUParticles3D @export var quantita_neve: int = 2000 @export var tempo_transizione_neve: float = 10.0 @onready var sole: DirectionalLight3D = $DirectionalLight3D @onready var ambiente: WorldEnvironment = $WorldEnvironment @onready var menu_giornata: OptionButton = $CanvasLayer/OptionButton @onready var bottone_pioggia: CheckButton = $CanvasLayer/CheckButtonPioggia @onready var particelle_pioggia: GPUParticles3D = $SubViewportContainer/SubViewport/World/Camere/PioggiaCadente @onready var nodo_camere: Node3D = $SubViewportContainer/SubViewport/World/Camere @onready var luce_lampo: DirectionalLight3D = $LuceLampo @onready var sprite_fulmine: Sprite3D = $SubViewportContainer/SubViewport/World/Camere/SpriteFulmine var tween_giorno: Tween var is_raining: bool = false var intensita_pioggia: float = 0.0 var timer_prossimo_fulmine: float = 0.0 var is_cinematic_mode: bool = false var timer_cinematic_env: float = 0.0 var sto_cambiando_in_cinematic: bool = false var is_snow_accumulated: bool = false var is_actively_snowing: bool = false var snow_tween: Tween var cold_tween: Tween var actual_snow_amount: float = 0.0 func _ready() -> void: if menu_giornata: menu_giornata.clear() menu_giornata.add_item("1 - Mattina") menu_giornata.add_item("2 - Pomeriggio") menu_giornata.add_item("3 - Notte") menu_giornata.selected = int(tempo_giorno) - 1 menu_giornata.item_selected.connect(_on_giornata_selezionata) if bottone_pioggia: bottone_pioggia.toggled.connect(_on_pioggia_toggled) if particelle_pioggia: particelle_pioggia.emitting = false if particelle_lucciole: particelle_lucciole.emitting = false particelle_lucciole.amount = quantita_lucciole var proc_mat = particelle_lucciole.process_material as ParticleProcessMaterial if proc_mat: proc_mat.emission_box_extents = Vector3(raggio_spawn_lucciole, altezza_spawn_lucciole, raggio_spawn_lucciole) if particelle_vento: particelle_vento.emitting = false particelle_vento.amount = quantita_vento var proc_mat_vento = particelle_vento.process_material as ParticleProcessMaterial if proc_mat_vento: proc_mat_vento.emission_box_extents = Vector3(raggio_spawn_vento, altezza_spawn_vento, raggio_spawn_vento) set_snow_amount(0.0) if particelle_neve: particelle_neve.emitting = false particelle_neve.amount = quantita_neve if bottone_neve: bottone_neve.pressed.connect(_on_snow_button_pressed) if bottone_nevicata: bottone_nevicata.pressed.connect(_on_nevicata_button_pressed) bottone_nevicata.disabled = true _resetta_timer_fulmine() func _input(event: InputEvent) -> void: if event is InputEventKey and event.pressed and not event.echo: if event.keycode == KEY_C: is_cinematic_mode = !is_cinematic_mode if is_cinematic_mode: timer_cinematic_env = intervallo_eventi_meteo print("Ambiente Cinematic: ATTIVATA") else: print("Ambiente Cinematic: DISATTIVATA") return if event.keycode in [KEY_1, KEY_2, KEY_3, KEY_4, KEY_5]: if is_cinematic_mode: is_cinematic_mode = false print("Ambiente Cinematic: DISATTIVATA (Cambio camera manuale)") func _on_giornata_selezionata(index: int) -> void: if not sto_cambiando_in_cinematic and is_cinematic_mode: is_cinematic_mode = false print("Ambiente Cinematic: DISATTIVATA (Orario cambiato manualmente)") var target_tempo = float(index + 1) if tween_giorno and tween_giorno.is_valid(): tween_giorno.kill() tween_giorno = create_tween() tween_giorno.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) tween_giorno.tween_property(self, "tempo_giorno", target_tempo, durata_transizione) if index == 0: trigger_morning() func _on_pioggia_toggled(toggled_on: bool) -> void: if is_actively_snowing and toggled_on: if bottone_pioggia: bottone_pioggia.set_pressed_no_signal(false) return if not sto_cambiando_in_cinematic and is_cinematic_mode: is_cinematic_mode = false print("Ambiente Cinematic: DISATTIVATA (Pioggia cambiata manualmente)") var stava_piovendo = is_raining is_raining = toggled_on if particelle_pioggia: particelle_pioggia.emitting = is_raining if stava_piovendo and not is_raining: trigger_after_rain() func _process(delta: float) -> void: if is_cinematic_mode: timer_cinematic_env -= delta if timer_cinematic_env <= 0.0: _genera_evento_meteo_casuale() timer_cinematic_env = randf_range(intervallo_eventi_meteo * 0.8, intervallo_eventi_meteo * 1.5) var base_tint: Color var base_sky_top: Color var base_sky_horizon: Color var base_fog_color: Color var base_fog_density: float var base_bloom: float var base_esposizione: float var base_grad_top: Color var base_grad_bot: Color var base_grad_intensity: float var base_rotazione_sole: Vector3 var base_acqua_deep: Color var tempesta_attiva = is_raining or is_actively_snowing if tempesta_attiva: intensita_pioggia = move_toward(intensita_pioggia, 1.0, delta * 0.5) if is_raining: timer_prossimo_fulmine -= delta if timer_prossimo_fulmine <= 0.0: _scatena_fulmine() _resetta_timer_fulmine() else: intensita_pioggia = move_toward(intensita_pioggia, 0.0, delta * 0.5) if materiale_nuvole: var densita_attuale = lerp(0.4, 1.0, intensita_pioggia) materiale_nuvole.set_shader_parameter("cloud_density", densita_attuale) var sharpness_attuale = lerp(0.14, 0.0, intensita_pioggia) materiale_nuvole.set_shader_parameter("center_sharpness", sharpness_attuale) if tempo_giorno <= 2.0: var t = tempo_giorno - 1.0 base_tint = color_mattina.lerp(color_pomeriggio, t) base_sky_top = sky_top_mattina.lerp(sky_top_pomeriggio, t) base_sky_horizon = sky_horizon_mattina.lerp(sky_horizon_pomeriggio, t) base_fog_color = colore_nebbia_mattina.lerp(colore_nebbia_pomeriggio, t) base_fog_density = lerp(densita_nebbia_mattina, densita_nebbia_pomeriggio, t) base_bloom = lerp(bloom_mattina, bloom_pomeriggio, t) base_esposizione = lerp(esposizione_mattina, esposizione_pomeriggio, t) base_grad_top = grad_top_mattina.lerp(grad_top_pomeriggio, t) base_grad_bot = grad_bot_mattina.lerp(grad_bot_pomeriggio, t) base_grad_intensity = lerp(intensita_grad_mattina, intensita_grad_pomeriggio, t) base_rotazione_sole = rotazione_sole_mattina.lerp(rotazione_sole_pomeriggio, t) base_acqua_deep = acqua_deep_mattina.lerp(acqua_deep_pomeriggio, t) else: var t = tempo_giorno - 2.0 base_tint = color_pomeriggio.lerp(color_notte, t) base_sky_top = sky_top_pomeriggio.lerp(sky_top_notte, t) base_sky_horizon = sky_horizon_pomeriggio.lerp(sky_horizon_notte, t) base_fog_color = colore_nebbia_pomeriggio.lerp(colore_nebbia_notte, t) base_fog_density = lerp(densita_nebbia_pomeriggio, densita_nebbia_notte, t) base_bloom = lerp(bloom_pomeriggio, bloom_notte, t) base_esposizione = lerp(esposizione_pomeriggio, esposizione_notte, t) base_grad_top = grad_top_pomeriggio.lerp(grad_top_notte, t) base_grad_bot = grad_bot_pomeriggio.lerp(grad_bot_notte, t) base_grad_intensity = lerp(intensita_grad_pomeriggio, intensita_grad_notte, t) base_rotazione_sole = rotazione_sole_pomeriggio.lerp(rotazione_sole_notte, t) base_acqua_deep = acqua_deep_pomeriggio.lerp(acqua_deep_notte, t) var final_tint = base_tint.lerp(base_tint * colore_mood_pioggia, intensita_pioggia) var final_sky_top = base_sky_top.lerp(base_sky_top * colore_mood_pioggia, intensita_pioggia) var final_sky_horizon = base_sky_horizon.lerp(base_sky_horizon * colore_mood_pioggia, intensita_pioggia) var final_fog_color = base_fog_color.lerp(base_fog_color * colore_mood_pioggia, intensita_pioggia) var final_fog_density = lerp(base_fog_density, base_fog_density * 4.0, intensita_pioggia) var final_grad_top = base_grad_top.lerp(base_grad_top * colore_mood_pioggia, intensita_pioggia) var final_grad_bot = base_grad_bot.lerp(base_grad_bot * colore_mood_pioggia, intensita_pioggia) # Applico lo scurimento personalizzato all'acqua in base allo slider var final_acqua_deep = base_acqua_deep.lerp(base_acqua_deep.darkened(oscuramento_acqua_pioggia), intensita_pioggia) RenderingServer.global_shader_parameter_set("global_gradient_color_top", final_grad_top) RenderingServer.global_shader_parameter_set("global_gradient_color_bot", final_grad_bot) RenderingServer.global_shader_parameter_set("global_gradient_intensity", base_grad_intensity) # Assegnazione parametri allo shader dell'acqua if materiale_acqua: var target_ripples = lerp(0.02, 1.0, intensita_pioggia) materiale_acqua.set_shader_parameter("ripple_density", target_ripples) var ripple_speed = lerp(0.8, 1.6, intensita_pioggia) materiale_acqua.set_shader_parameter("ripple_speed", ripple_speed) materiale_acqua.set_shader_parameter("deep_water_color", final_acqua_deep) # --- GESTIONE VENTO DEI FILI DELLA LUCE --- if materiale_fili_luce: # Interpoliamo tra la forza base (es. 1.0) e una bufera (5.0) in base alla pioggia var vento_attuale = lerp(forza_vento_fili_base, 5.0, intensita_pioggia) materiale_fili_luce.set_shader_parameter("forza_vento", vento_attuale) var night_val = clamp(tempo_giorno - 2.0, 0.0, 1.0) var attenuazione_luce_neve = lerp(1.0, 0.55, actual_snow_amount * (1.0 - night_val)) var attenuazione_bloom_neve = lerp(1.0, 0.5, actual_snow_amount) var final_bloom = lerp(base_bloom, base_bloom * 0.5, intensita_pioggia) * attenuazione_bloom_neve var final_esposizione = base_esposizione * attenuazione_luce_neve var colore_riflesso = Color(0.4, 0.5, 0.6, 1.0) var colore_gocce = final_sky_horizon.lerp(colore_riflesso, 0.15) colore_gocce = colore_gocce.lerp(colore_gocce * 0.4, night_val) if materiale_pioggia_gocce: materiale_pioggia_gocce.albedo_color = colore_gocce if sole: sole.light_color = final_tint sole.rotation_degrees = base_rotazione_sole var target_energy = lerp(1.0, energia_luce_notte, night_val) sole.light_energy = lerp(target_energy, target_energy * 0.4, intensita_pioggia) if ambiente and ambiente.environment: ambiente.environment.ambient_light_color = final_tint ambiente.environment.fog_enabled = true ambiente.environment.fog_light_color = final_fog_color ambiente.environment.fog_density = final_fog_density ambiente.environment.glow_enabled = true ambiente.environment.glow_intensity = final_bloom ambiente.environment.tonemap_exposure = final_esposizione var sky_mat = ambiente.environment.sky.sky_material as ShaderMaterial if sky_mat: sky_mat.set_shader_parameter("sky_top_color", final_sky_top) sky_mat.set_shader_parameter("sky_horizon_color", final_sky_horizon) sky_mat.set_shader_parameter("sun_color", final_tint) sky_mat.set_shader_parameter("night_intensity", night_val) if materiale_nebbia: materiale_nebbia.set_shader_parameter("night_intensity", night_val) materiale_nebbia.set_shader_parameter("sun_color", final_tint) var e_notte = tempo_giorno >= 2.5 if nodo_polvere: nodo_polvere.visible = (not tempesta_attiva and not e_notte) if particelle_lucciole: particelle_lucciole.emitting = (not tempesta_attiva and e_notte) if particelle_vento: particelle_vento.emitting = (not tempesta_attiva and not e_notte) func _genera_evento_meteo_casuale() -> void: sto_cambiando_in_cinematic = true var scelta = randi() % 3 if scelta == 0: var nuovo_orario = randi() % 3 if menu_giornata: menu_giornata.selected = nuovo_orario _on_giornata_selezionata(nuovo_orario) elif scelta == 1: if not is_actively_snowing: var piove = randf() > 0.5 if bottone_pioggia: bottone_pioggia.set_pressed_no_signal(piove) _on_pioggia_toggled(piove) elif scelta == 2: if not is_actively_snowing: if not is_raining: if bottone_pioggia: bottone_pioggia.set_pressed_no_signal(true) _on_pioggia_toggled(true) _scatena_fulmine() sto_cambiando_in_cinematic = false func _resetta_timer_fulmine() -> void: timer_prossimo_fulmine = randf_range(tempo_min_fulmine, tempo_max_fulmine) func _scatena_fulmine() -> void: if luce_lampo: var tween_luce = create_tween() luce_lampo.light_energy = 5.0 tween_luce.tween_interval(0.05) tween_luce.tween_callback(func(): luce_lampo.light_energy = 0.0) tween_luce.tween_interval(0.05) tween_luce.tween_callback(func(): luce_lampo.light_energy = 2.0) tween_luce.tween_method(func(val): luce_lampo.light_energy = val, 2.0, 0.0, 0.2) var numero_scariche = randi_range(fulmini_min, fulmini_max) for i in numero_scariche: if sprite_fulmine: var angolo_casuale = randf() * TAU var distanza = randf_range(10.0, 40.0) sprite_fulmine.position = Vector3(cos(angolo_casuale) * distanza, 10.0, sin(angolo_casuale) * distanza) sprite_fulmine.modulate = colore_fulmine sprite_fulmine.modulate.a = 1.0 var s = randf_range(scala_min_fulmine, scala_max_fulmine) sprite_fulmine.scale = Vector3(s, s, s) var tween_sprite = create_tween() tween_sprite.tween_interval(0.2) tween_sprite.tween_property(sprite_fulmine, "modulate:a", 0.0, 0.4) if numero_scariche > 1: await get_tree().create_timer(randf_range(0.05, 0.15)).timeout func trigger_morning() -> void: spawn_single_godray() func trigger_after_rain() -> void: if tempo_giorno < 2.5: for i in range(max_rain_godrays): spawn_single_godray() func spawn_single_godray() -> void: if godray_scene == null: print("Attenzione: Manca la scena del Godray nell'Inspector!") return if nodo_camere == null: print("Attenzione: Non trovo il nodo Camere per agganciare il Godray!") return var ray = godray_scene.instantiate() var nodo_mondo = nodo_camere.get_parent() nodo_mondo.add_child(ray) var random_x = randf_range(-godray_spawn_radius, godray_spawn_radius) var random_z = randf_range(-godray_spawn_radius, godray_spawn_radius) ray.global_position = nodo_camere.global_position + Vector3(random_x, godray_spawn_height, random_z) ray.rotation_degrees = godray_rotation_degrees ray.scale = godray_scale func _on_snow_button_pressed(): is_snow_accumulated = !is_snow_accumulated if bottone_nevicata: bottone_nevicata.disabled = !is_snow_accumulated if is_snow_accumulated and not is_actively_snowing: _on_nevicata_button_pressed() elif not is_snow_accumulated and is_actively_snowing: _on_nevicata_button_pressed() var target_snow: float = 1.0 if is_snow_accumulated else 0.0 if snow_tween and snow_tween.is_valid(): snow_tween.kill() snow_tween = create_tween() snow_tween.tween_method(set_snow_amount, actual_snow_amount, target_snow, tempo_transizione_neve) func _on_nevicata_button_pressed(): if not is_snow_accumulated: return is_actively_snowing = !is_actively_snowing if bottone_pioggia: bottone_pioggia.disabled = is_actively_snowing if is_actively_snowing and is_raining: bottone_pioggia.set_pressed_no_signal(false) _on_pioggia_toggled(false) if particelle_neve: particelle_neve.emitting = is_actively_snowing if overlay_nebbia_schermo and overlay_nebbia_schermo.material: var target_opacity: float = 0.5 if is_actively_snowing else 0.1 var target_radius: float = 0.350 if is_actively_snowing else 0.465 if cold_tween and cold_tween.is_valid(): cold_tween.kill() cold_tween = create_tween() cold_tween.set_trans(Tween.TRANS_SINE).set_ease(Tween.EASE_IN_OUT) cold_tween.set_parallel(true) cold_tween.tween_property(overlay_nebbia_schermo.material, "shader_parameter/max_opacity", target_opacity, 4.0) cold_tween.tween_property(overlay_nebbia_schermo.material, "shader_parameter/clear_center_radius", target_radius, 4.0) func set_snow_amount(value: float): actual_snow_amount = value RenderingServer.global_shader_parameter_set("global_snow_amount", value)