From 53d1bdb7f82472d925b2bd5c5194ef937bbff970 Mon Sep 17 00:00:00 2001 From: blujai831 Date: Tue, 13 Feb 2024 10:27:08 -0800 Subject: [PATCH] Initial untested prototype of new FX implementation. --- fx/FX.gd | 439 ++++++++++++++++++++++++++++++++++++++++ fx/FX.tscn | 167 +++++++++++++++ fx/FXConfig.gd | 36 ++++ fx/Raindrop.glb | 3 + fx/Raindrop.glb.import | 47 +++++ fx/Raindrop.res | Bin 0 -> 6008 bytes fx/Snowflake.glb | 3 + fx/Snowflake.glb.import | 47 +++++ fx/Snowflake.res | Bin 0 -> 86539 bytes fx/WeatherParticles.gd | 25 +++ 10 files changed, 767 insertions(+) create mode 100644 fx/FX.gd create mode 100644 fx/FX.tscn create mode 100644 fx/FXConfig.gd create mode 100644 fx/Raindrop.glb create mode 100644 fx/Raindrop.glb.import create mode 100644 fx/Raindrop.res create mode 100644 fx/Snowflake.glb create mode 100644 fx/Snowflake.glb.import create mode 100644 fx/Snowflake.res create mode 100644 fx/WeatherParticles.gd diff --git a/fx/FX.gd b/fx/FX.gd new file mode 100644 index 0000000..732f5e1 --- /dev/null +++ b/fx/FX.gd @@ -0,0 +1,439 @@ +extends CanvasLayer +## Exposes high-level controls for overarching visual and audio effects. + +## Default weather intensity transition time. +const DEFAULT_WEATHER_FADE: float = 10.0 +## Default screen fade transition time. +const DEFAULT_FADE: float = 1.0 +## Default tint transition time. +const DEFAULT_TINT: float = 1.0 +## Default flash duration. +const DEFAULT_FLASH: float = 1.0 +## Max turbulence influence at peak snow magnitude. +const SNOW_MAX_MAX_TURBULENCE_INFLUENCE: float = 0.375 +## Max turbulence influence at zero snow magnitude. +const SNOW_MIN_MAX_TURBULENCE_INFLUENCE: float = 0.125 +## Minimum necessary rain magnitude for lightning. +const LIGHTNING_RAIN_THRESHOLD: float = 0.8 +## Probability of a lightning strike at any given frame at peak rain magnitude. +const LIGHTNING_MAX_PROBABILITY_PER_FRAME: float = 0.005 +## Maximum duration of random lighning. +const LIGHTNING_MAX_FADE: float = 1.0 + +## Active configuration. +@export var config: FXConfig + +## Audio fader for playing music. +@onready var bgm_fader := $BGM as AudioFader +## Audio fader for playing general-purpose continuous ambience. +@onready var bgs_fader := $BGS as AudioFader +## Ambience interpolator for playing continuous ambience related to rain. +@onready var rain_bgs_interpolator := $RainBGS as AmbienceInterpolator +## Ambience interpolator for playing continuous ambience related to snow. +@onready var snow_bgs_interpolator := $SnowBGS as AmbienceInterpolator +## Having a single universal environment allows high-level manipulation. +@onready var environment := $WorldEnvironment.environment as Environment +## Tween channel for changing fog color. +@onready var fog_color_tween_channel := TweenChannel.make_replacing() +## Tween channel for changing fog density. +@onready var fog_density_tween_channel := TweenChannel.make_replacing() +## Tween channel for changing fog sky affect. +@onready var fog_sky_affect_tween_channel := TweenChannel.make_replacing() +## Sky resource for use with environment. +@onready var sky := $WorldEnvironment.environment.sky as Sky +## Tween channels for shader globals. +@onready var shader_global_tween_channels := {} +## Having a single universal light source allows high-level manipulation. +@onready var light := $DirectionalLight3D as DirectionalLight3D +## Tween channel for moving light source. +@onready var light_transform_tween_channel := TweenChannel.make_replacing() +## Tween channel for changing light source energy. +@onready var light_energy_tween_channel := TweenChannel.make_replacing() +## Particle system for rendering rain. +@onready var rain_particles := $RainParticles as WeatherParticles +## Tween channel for changing rain particle amount ratio. +@onready var rain_amount_tween_channel := TweenChannel.make_replacing() +## Particle system for rendering snow. +@onready var snow_particles := $SnowParticles as WeatherParticles +## Tween channel for changing snow particle amount ratio. +@onready var snow_amount_tween_channel := TweenChannel.make_replacing() +## Tween channel for changing snow turbulence. +@onready var snow_turbulence_tween_channel := TweenChannel.make_replacing() +## Multiplicative solid-color overlay primarily for ambient effects. +@onready var tint_rect := $Tint as ColorRect +## Tween channel for changing tint color. +@onready var tint_tween_channel := TweenChannel.make_replacing() +## Alpha-blended solid-color overlay primarily for screen transitions. +@onready var fade_rect := $Fade as ColorRect +## Tween channel for changing fade color. +@onready var fade_tween_channel := TweenChannel.make_replacing() +## Alpha-blended solid-color overlay primarily for dramatic effect. +@onready var flash_rect := $Flash as ColorRect +## Tween channel for changing flash color. +@onready var flash_tween_channel := TweenChannel.make_replacing() + +## Sets the environment to use canvas layer 1 (and below) as the background. +func enable_canvas_background() -> void: + environment.background_mode = Environment.BG_CANVAS + environment.background_canvas_max_layer = 1 + +## Sets the environment to use the sky as the background. +func disable_canvas_background() -> void: + environment.background_mode = Environment.BG_SKY + environment.sky = sky + +## Applies the background mode associated with the active config. +func restore_background_mode() -> void: + if config.canvas: + enable_canvas_background() + else: + disable_canvas_background() + +## Fades out any current music and plays the given music instead. +func play_bgm(stream: AudioStream, fade: float = 0.0) -> void: + if fade > 0.0: + await bgm_fader.fade_in(stream, fade) + else: + await bgm_fader.play(stream) + +## Fades out any current music. +func stop_bgm(fade: float = AudioFader.DEFAULT_AUDIO_FADE) -> void: + await bgm_fader.fade_out(fade) + +## Plays the music specified by the active config. +func restore_bgm(fade: float = AudioFader.DEFAULT_AUDIO_FADE) -> void: + await play_bgm(config.bgm, fade) + +## Fades out any current general-purpose ambience and plays the given instead. +func play_bgs( + stream: AudioStream, + fade: float = AudioFader.DEFAULT_AUDIO_FADE +) -> void: + await bgs_fader.crossfade(stream, fade) + +## Fades out any current general-purpose ambience. +func stop_bgs(fade: float = AudioFader.DEFAULT_AUDIO_FADE) -> void: + await bgs_fader.fade_out(fade) + +## Plays the ambience specified by the active config. +func restore_bgs(fade: float = AudioFader.DEFAULT_AUDIO_FADE) -> void: + await play_bgs(config.bgs, fade) + +## Sets fog color. Alpha is used for density and sky affect. +func set_fog(color: Color, fade: float = DEFAULT_WEATHER_FADE) -> void: + var opaque := color + opaque.a = 1.0 + var set_fog_color := func() -> void: + var tween := await fog_color_tween_channel.create_tween(self) + tween.tween_property(environment, ^'fog_light_color', opaque, fade) + await fog_color_tween_channel.sync(tween) + var set_fog_density := func() -> void: + var tween := await fog_density_tween_channel.create_tween(self) + tween.tween_property(environment, ^'fog_density', color.a, fade) + await fog_density_tween_channel.sync(tween) + var set_fog_sky_affect := func() -> void: + var tween := ( + await fog_sky_affect_tween_channel.create_tween(self) + ) + tween.tween_property(environment, ^'fog_sky_affect', color.a, fade) + await fog_sky_affect_tween_channel.sync(tween) + await Task.group([ + Task.new(set_fog_color), + Task.new(set_fog_density), + Task.new(set_fog_sky_affect) + ]).sync() + +## Sets fog density and sky affect without changing color. +func set_fog_strength( + strength: float, + fade: float = DEFAULT_WEATHER_FADE +) -> void: + var set_fog_density := func() -> void: + var tween := await fog_density_tween_channel.create_tween(self) + tween.tween_property(environment, ^'fog_density', strength, fade) + await fog_density_tween_channel.sync(tween) + var set_fog_sky_affect := func() -> void: + var tween := ( + await fog_sky_affect_tween_channel.create_tween(self) + ) + tween.tween_property(environment, ^'fog_sky_affect', strength, fade) + await fog_sky_affect_tween_channel.sync(tween) + await Task.group([ + Task.new(set_fog_density), + Task.new(set_fog_sky_affect) + ]).sync() + +## Applies the fog settings specified by the active config. +func restore_fog(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_fog(config.fog, fade) + +## Sets global shader uniform. +func set_shader_global( + what: StringName, + value: float, + fade: float = DEFAULT_WEATHER_FADE +) -> void: + if not shader_global_tween_channels.has(what): + shader_global_tween_channels[what] = TweenChannel.make_replacing() + var tween_channel := shader_global_tween_channels[what] as TweenChannel + var tween := await tween_channel.create_tween(self) + tween.tween_method( + RenderingServer.global_shader_parameter_set.bind(what), + RenderingServer.global_shader_parameter_get(what), + value, fade + ) + await tween_channel.sync(tween) + +## Sets closeness to midnight. +func set_night(value: float, fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_shader_global(&'night', value, fade) + +## Applies the night value specified by the active config. +func restore_night(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_night(config.night, fade) + +## Sets sky cloudiness. +func set_overcast(value: float, fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_shader_global(&'overcast', value, fade) + +## Applies the overcast value specified by the active config. +func restore_overcast(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_overcast(config.overcast, fade) + +## Sets environment wetness. +func set_wet(value: float, fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_shader_global(&'wet', value, fade) + +## Applies the wet value specified by the active config. +func restore_wet(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_wet(config.wet, fade) + +## Sets wind speed. +func set_wind(value: float, fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_shader_global(&'wind', value, fade) + +## Applies the wind value specified by the active config. +func restore_wind(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_wind(config.wind, fade) + +## Sets light source's position (i.e. the opposite of its facing direction). +func set_light_source( + value: Vector3, + fade: float = DEFAULT_WEATHER_FADE +) -> void: + var from := light.basis + var to := Basis.looking_at(-value) + var setter := func(weight: float) -> void: + light.basis = from.slerp(to, weight) + var tween := await light_transform_tween_channel.create_tween(self) + tween.tween_method(setter, 0.0, 1.0, fade) + await light_transform_tween_channel.sync(tween) + +## Applies the light source position specified by the active config. +func restore_light_source(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_light_source(config.light_source, fade) + +## Sets light source's energy. +func set_light_energy( + value: float, + fade: float = DEFAULT_WEATHER_FADE +) -> void: + var tween := await light_energy_tween_channel.create_tween(self) + tween.tween_property(light, ^'light_energy', value, fade) + await light_energy_tween_channel.sync(tween) + +## Applies the light source energy specified by the active config. +func restore_light_energy(fade: float = DEFAULT_WEATHER_FADE) -> void: + await set_light_energy(config.light_energy, fade) + +## Sets the weather to rainfall at the given magnitude. +func set_rain(value: float, fade: float = DEFAULT_WEATHER_FADE) -> void: + await _set_weather(value, 0.0, fade) + +## Sets the weather to snowfall at the given magnitude. +func set_snow(value: float, fade: float = DEFAULT_WEATHER_FADE) -> void: + await _set_weather(0.0, value, fade) + +## Clears any active weather. +func stop_weather(fade: float = DEFAULT_WEATHER_FADE) -> void: + await _set_weather(0.0, 0.0, fade) + +## Applies the weather specified by the active config. +func restore_weather(fade: float = DEFAULT_WEATHER_FADE) -> void: + match config.weather_type: + FXConfig.WeatherType.NONE: + await stop_weather(fade) + FXConfig.WeatherType.RAIN: + await set_rain(config.weather_magnitude, fade) + FXConfig.WeatherType.SNOW: + await set_snow(config.weather_magnitude, fade) + +## Sets screen tint. +func tint(color: Color, fade: float = DEFAULT_TINT) -> void: + var tween := await tint_tween_channel.create_tween(self) + tween.tween_property(tint_rect, ^'color', color, fade) + await tint_tween_channel.sync(tween) + +## Clears screen tint. +func clear_tint(fade: float = DEFAULT_TINT) -> void: + await tint(Color.WHITE, fade) + +## Applies the screen tint specified by the active config. +func restore_tint(fade: float = DEFAULT_TINT) -> void: + await tint(config.tint, fade) + +## Fades the screen out to the given color. +func fade_out(color: Color = Color.BLACK, fade: float = DEFAULT_FADE) -> void: + var tween := await fade_tween_channel.create_tween(self) + tween.tween_property(fade_rect, ^'color', color, fade) + await fade_tween_channel.sync(tween) + +## Fades the screen in from any current screen fade state. +func fade_in(fade: float = DEFAULT_FADE) -> void: + var transparent_equiv := fade_rect.color + transparent_equiv.a = 0.0 + await fade_out(transparent_equiv, fade) + +## Flashes the screen the given color. +func flash(color: Color, fade: float = DEFAULT_FLASH) -> void: + var transparent_equiv := color + transparent_equiv.a = 0.0 + var tween := await flash_tween_channel.create_tween(self) + flash_rect.color = color + tween.tween_property(flash_rect, ^'color', transparent_equiv, fade) + await flash_tween_channel.sync(tween) + +## Simulates lightning by rapidly shifting fog, brightness, and time of day. +func lightning(value: float = 1.0, fade: float = DEFAULT_FLASH) -> void: + var night_task := func() -> void: + RenderingServer.global_shader_parameter_set( + &'night', lerpf(config.night, 0.0, value) + ) + await restore_night(fade) + var fog_task := func() -> void: + environment.fog_density = lerpf(config.fog.a, 0.0, value) + environment.fog_sky_affect = environment.fog_density + await set_fog_strength(config.fog.a, fade) + var light_task := func() -> void: + light.light_energy = lerpf(config.light_energy, 1.0, value) + await set_light_energy(config.light_energy, fade) + await Task.group([ + Task.new(night_task), + Task.new(fog_task), + Task.new(light_task) + ]).sync() + +## Applies given as active config. Restores all associated effects. +## +## If config to apply is null or not given, restores all effects +## associated with the current active config. +func apply_conifg( + new_config: FXConfig = null, + fade: float = DEFAULT_WEATHER_FADE +) -> void: + if new_config: + config = new_config + restore_background_mode() + var bgm_task := func() -> void: + await restore_bgm(fade) + var bgs_task := func() -> void: + await restore_bgs(fade) + var fog_task := func() -> void: + await restore_fog(fade) + var night_task := func() -> void: + await restore_night(fade) + var overcast_task := func() -> void: + await restore_overcast(fade) + var wet_task := func() -> void: + await restore_wet(fade) + var wind_task := func() -> void: + await restore_wind(fade) + var light_source_task := func() -> void: + await restore_light_source(fade) + var light_energy_task := func() -> void: + await restore_light_energy(fade) + var weather_task := func() -> void: + await restore_weather(fade) + var tint_task := func() -> void: + await restore_tint(fade) + await Task.group([ + Task.new(bgm_task), + Task.new(bgs_task), + Task.new(fog_task), + Task.new(night_task), + Task.new(overcast_task), + Task.new(wet_task), + Task.new(wind_task), + Task.new(light_source_task), + Task.new(light_energy_task), + Task.new(weather_task), + Task.new(tint_task) + ]).sync() + +## Changes the weather to an arbitrary and possibly chimeric configuration. +func _set_weather( + rain: float, + snow: float, + fade: float = DEFAULT_WEATHER_FADE +) -> void: + var rain_audio_task := func() -> void: + await rain_bgs_interpolator.interpolate(rain, fade) + var rain_amount_task := func() -> void: + var tween := await rain_amount_tween_channel.create_tween(self) + tween.tween_property(rain_particles, ^'amount_ratio', rain, fade) + await rain_amount_tween_channel.sync(tween) + var snow_audio_task := func() -> void: + await snow_bgs_interpolator.interpolate(snow, fade) + var snow_amount_task := func() -> void: + var tween := await snow_amount_tween_channel.create_tween(self) + tween.tween_property(snow_particles, ^'amount_ratio', snow, fade) + await snow_amount_tween_channel.sync(tween) + var max_turbulence: float = lerpf( + SNOW_MIN_MAX_TURBULENCE_INFLUENCE, + SNOW_MAX_MAX_TURBULENCE_INFLUENCE, + snow + ) + var min_turbulence: float = max_turbulence/2.0 + var snow_max_turbulence_task := func() -> void: + var tween := await snow_turbulence_tween_channel.create_tween(self) + tween.tween_property( + snow_particles.process_material, + ^'turbulence_influence_max', + max_turbulence, + fade + ) + await snow_turbulence_tween_channel.sync(tween) + var snow_min_turbulence_task := func() -> void: + var tween := await snow_turbulence_tween_channel.create_tween(self) + tween.tween_property( + snow_particles.process_material, + ^'turbulence_influence_min', + min_turbulence, + fade + ) + await snow_turbulence_tween_channel.sync(tween) + await Task.group([ + Task.new(rain_audio_task), + Task.new(rain_amount_task), + Task.new(snow_audio_task), + Task.new(snow_amount_task), + Task.new(snow_max_turbulence_task), + Task.new(snow_min_turbulence_task) + ]).sync() + +## Lightning handling for _process. +func _do_lightning() -> void: + if rain_particles.amount_ratio > LIGHTNING_RAIN_THRESHOLD: + var roll: float = randf() + var max_power: float = ( + (rain_particles.amount_ratio - LIGHTNING_RAIN_THRESHOLD)/ + (1.0 - LIGHTNING_RAIN_THRESHOLD) + ) + var check: float = max_power*LIGHTNING_MAX_PROBABILITY_PER_FRAME + if roll <= check: + var power: float = max_power*roll/check + # Intentionally not awaited. + lightning(power, power*LIGHTNING_MAX_FADE) + +func _process(_delta: float) -> void: + _do_lightning() diff --git a/fx/FX.tscn b/fx/FX.tscn new file mode 100644 index 0000000..0604d0b --- /dev/null +++ b/fx/FX.tscn @@ -0,0 +1,167 @@ +[gd_scene load_steps=26 format=3 uid="uid://bapbn52tw4uxt"] + +[ext_resource type="Script" path="res://fx/FX.gd" id="1_o5hjr"] +[ext_resource type="Script" path="res://audio/AudioFader.gd" id="2_lpome"] +[ext_resource type="Script" path="res://audio/ambience/AmbienceInterpolator.gd" id="3_kcje8"] +[ext_resource type="Shader" path="res://shaders/sky.gdshader" id="3_qkds1"] +[ext_resource type="AudioStream" uid="uid://bs25p8gni0x5t" path="res://audio/ambience/rain_level_1.ogg" id="4_m88w1"] +[ext_resource type="ArrayMesh" uid="uid://5s6wywij7e8l" path="res://fx/Raindrop.res" id="4_n0mfc"] +[ext_resource type="ArrayMesh" uid="uid://l6okcjkm4n72" path="res://fx/Snowflake.res" id="4_x3nfi"] +[ext_resource type="Script" path="res://fx/WeatherParticles.gd" id="5_4a72t"] +[ext_resource type="AudioStream" uid="uid://cvkmicogpj178" path="res://audio/ambience/rain_level_2.ogg" id="5_qq61e"] +[ext_resource type="AudioStream" uid="uid://gmicbgcj2e2k" path="res://audio/ambience/rain_level_3.ogg" id="6_boctk"] +[ext_resource type="AudioStream" uid="uid://dskrc0ofdbnbl" path="res://audio/ambience/rain_level_4.ogg" id="7_oixwt"] +[ext_resource type="AudioStream" uid="uid://t2y240536hb2" path="res://audio/ambience/rain_level_5.ogg" id="8_my1rc"] +[ext_resource type="AudioStream" uid="uid://clxqwer2sr55e" path="res://audio/ambience/thunder.ogg" id="9_fbpi5"] +[ext_resource type="AudioStream" uid="uid://fs3rqrp3cn3b" path="res://audio/ambience/howling_wind.ogg" id="10_dbbpf"] + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_jy0wt"] + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_guaq2"] +seamless = true +noise = SubResource("FastNoiseLite_jy0wt") + +[sub_resource type="Gradient" id="Gradient_uyl8u"] +offsets = PackedFloat32Array(0.764151, 1) + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_reikm"] +frequency = 0.75 + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_ahwt5"] +width = 1024 +color_ramp = SubResource("Gradient_uyl8u") +noise = SubResource("FastNoiseLite_reikm") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_khdbh"] +shader = ExtResource("3_qkds1") +shader_parameter/cloud_map = SubResource("NoiseTexture2D_guaq2") +shader_parameter/star_map = SubResource("NoiseTexture2D_ahwt5") + +[sub_resource type="Sky" id="Sky_y43u3"] +sky_material = SubResource("ShaderMaterial_khdbh") + +[sub_resource type="Environment" id="Environment_5ktyi"] +background_mode = 2 +background_canvas_max_layer = 1 +sky = SubResource("Sky_y43u3") +fog_enabled = true +fog_light_color = Color(0.517647, 0.552941, 0.607843, 1) +fog_density = 0.0 +fog_sky_affect = 0.0 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_bqidl"] +particle_flag_align_y = true +emission_shape_offset = Vector3(0, 24, 0) +emission_shape = 6 +emission_ring_axis = Vector3(0, 1, 0) +emission_ring_height = 0.0 +emission_ring_radius = 32.0 +emission_ring_inner_radius = 16.0 +direction = Vector3(0, -1, 0) +spread = 3.0 +initial_velocity_min = 48.0 +initial_velocity_max = 48.0 +angular_velocity_min = 360.0 +angular_velocity_max = 720.0 +damping_min = 4.0 +damping_max = 4.0 +turbulence_noise_speed = Vector3(0, 0, 1) +turbulence_influence_min = 0.063 +turbulence_influence_max = 0.063 +collision_mode = 2 + +[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_x0w6v"] +particle_flag_rotate_y = true +emission_shape_offset = Vector3(0, 24, 0) +emission_shape = 6 +emission_ring_axis = Vector3(0, 1, 0) +emission_ring_height = 0.0 +emission_ring_radius = 32.0 +emission_ring_inner_radius = 16.0 +direction = Vector3(0, -1, 0) +initial_velocity_min = 3.0 +initial_velocity_max = 3.0 +angular_velocity_min = 360.0 +angular_velocity_max = 720.0 +damping_min = 4.0 +damping_max = 4.0 +turbulence_enabled = true +turbulence_noise_speed = Vector3(0, 0, 1) +turbulence_influence_min = 0.188 +turbulence_influence_max = 0.375 +collision_mode = 2 + +[sub_resource type="CanvasItemMaterial" id="CanvasItemMaterial_4csoj"] +blend_mode = 3 + +[node name="FX" type="CanvasLayer"] +layer = 2 +script = ExtResource("1_o5hjr") + +[node name="BGM" type="Node" parent="."] +script = ExtResource("2_lpome") +bus = &"Music" + +[node name="BGS" type="Node" parent="."] +script = ExtResource("2_lpome") +bus = &"Sound" + +[node name="RainBGS" type="Node" parent="."] +script = ExtResource("3_kcje8") +bus = &"Sound" +streams = [ExtResource("4_m88w1"), ExtResource("5_qq61e"), ExtResource("6_boctk"), ExtResource("7_oixwt"), [ExtResource("8_my1rc"), ExtResource("9_fbpi5")]] + +[node name="SnowBGS" type="Node" parent="."] +script = ExtResource("3_kcje8") +bus = &"Sound" +streams = [ExtResource("10_dbbpf")] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_5ktyi") + +[node name="RainParticles" type="GPUParticles3D" parent="."] +amount = 32768 +amount_ratio = 0.0 +lifetime = 0.75 +visibility_aabb = AABB(-64, -64, -64, 128, 128, 128) +process_material = SubResource("ParticleProcessMaterial_bqidl") +draw_pass_1 = ExtResource("4_n0mfc") +script = ExtResource("5_4a72t") +seconds_ahead = 1.0 + +[node name="SnowParticles" type="GPUParticles3D" parent="."] +amount = 4096 +amount_ratio = 0.0 +lifetime = 6.0 +visibility_aabb = AABB(-64, -64, -64, 128, 128, 128) +process_material = SubResource("ParticleProcessMaterial_x0w6v") +draw_pass_1 = ExtResource("4_x3nfi") +script = ExtResource("5_4a72t") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +light_energy = 0.25 +shadow_enabled = true + +[node name="Tint" type="ColorRect" parent="."] +material = SubResource("CanvasItemMaterial_4csoj") +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Fade" type="ColorRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(1, 1, 1, 0) + +[node name="Flash" type="ColorRect" parent="."] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(1, 1, 1, 0) diff --git a/fx/FXConfig.gd b/fx/FXConfig.gd new file mode 100644 index 0000000..9b3e03b --- /dev/null +++ b/fx/FXConfig.gd @@ -0,0 +1,36 @@ +class_name FXConfig extends Resource +## Stores settings to apply to FX. + +## Variety of weather condition. +enum WeatherType { + NONE, ## No particular weather condition. + RAIN, ## Rainfall. + SNOW ## Snowfall. +} + +## Whether to render a canvas layer in the background instead of sky. +@export var canvas := false +## Music that should play. +@export var bgm: AudioStream = null +## Ambience that should play. +@export var bgs: AudioStream = null +## Desired fog color. +@export var fog := Color.TRANSPARENT +## How close the sky should look to midnight. +@export var night: float = 0.0 +## How cloudy the sky should be. +@export var overcast: float = 0.25 +## How wet the environment should be. +@export var wet: float = 0.0 +## Desired wind speed. +@export var wind: float = 0.25 +## Direction the light should come from. (Opposite of actual facing direction.) +@export var light_source := Vector3.UP +## Light intensity. +@export var light_energy: float = 0.25 +## Weather that should occur. +@export var weather_type := WeatherType.NONE +## Amount of weather to occur. +@export var weather_magnitude: float = 0.0 +## Screen tint, e.g. for exceptionally hot or cold environments. +@export var tint := Color.WHITE diff --git a/fx/Raindrop.glb b/fx/Raindrop.glb new file mode 100644 index 0000000..6655d84 --- /dev/null +++ b/fx/Raindrop.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb8317ea802c829c3cb862930c803d523fe161af949b72261f983525015c69c5 +size 4736 diff --git a/fx/Raindrop.glb.import b/fx/Raindrop.glb.import new file mode 100644 index 0000000..fc67f9a --- /dev/null +++ b/fx/Raindrop.glb.import @@ -0,0 +1,47 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://bjodbd5e8vd5s" +path="res://.godot/imported/Raindrop.glb-5c9e24da8779228af34394222cad9207.scn" + +[deps] + +source_file="res://fx/Raindrop.glb" +dest_files=["res://.godot/imported/Raindrop.glb-5c9e24da8779228af34394222cad9207.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/apply_root_scale=true +nodes/root_scale=1.0 +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +import_script/path="" +_subresources={ +"meshes": { +"Raindrop_Cylinder": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/make_streamable": "", +"save_to_file/path": "res://fx/Raindrop.res" +} +} +} +gltf/naming_version=1 +gltf/embedded_image_handling=1 diff --git a/fx/Raindrop.res b/fx/Raindrop.res new file mode 100644 index 0000000000000000000000000000000000000000..7a8320b8be4beb41da26ace38ac625a5f149fe2f GIT binary patch literal 6008 zcmbVQU5p$@5iXzYvwc3>=l@(FIh&u5!~x?Ai5&;$@K10Em^1bfLxMf@?#}K_JUg?V znYHhDIbH#g}jpdCySGkwfYC-H569R2>2M^69i#n*0g zy#EKA5V{2|@zc1OlzlIZ%O>=)*h|ZP+87z!cmrs{jTvdxux0UR zJ*`w_4fu6GGhrB%WmAFh+!|$Lk?X7tFTwQ6lO~G%Py?UD&Dvz-W1Fb8LwQaC-Ce}( zXrN8M9;9gyN1h)sv=#N$F*THqfP|maroDPI%z{Q3_=y_r495*WFda+Su)LXGgipNuQ?zMiyxAa&25$Cc3BJV6J)5F zlI;t|s$tOZG85Hs;W(?Tmr|DR34KVLrL>t;QJ_qgp$fS~VCwgd zr5P$vEt}L+xM=Wf5ZOwxlv?M_9ZRUy>Q)ajtPuy~{Gq}*Jt z6vwEd-0YGmpS4-z{AQkXxIHq?6J}51R})jtibtRIpuo0?(j)6su*eo$&4scWgg6jd039pw8@{oJuoBp-F!9TNa31#$uG9E}y$9?!ul=}T%DmF4w-kh7 zDK<%dZ}S@Lvd*#q*x2z|QcL0sdav>W*KORy*=JCz!IWR|(%@URGV^9_#HhEHMTr8b zfuw{S>7F_Ut!D@;)stzjNL;6015Sh<-Xt|&vxs-B z7dNvoh!6<+#-w%gtO0$iFll?(C>oBVx1A&MS!Sv`yb80^GEyj0{d(+JW@*uz#!B_3_E_ai85B=hQAR!E;V3uLEUtUTlu8==d>f&`z6|*)+=-@-y8XL% zpF~(H*w14Ic@miL-Xor$-GAxl>rS{J=5L`AkHhc+k4w%pxf>F9QU^8a+(PFcH7Dm@ulOydCDn0rfBES zK+_E6^ET4{TYDVeE#NGWG;s#l%K028J)+~AbNS7=+-Bvs+Lk{9K@M#FMhJb4^e+XU;L*oEE9Y(uF`+i_QEV5q#Tmc{0_W}Fh6?*{= zpfSz202w-FqzzqJiz7Gb8MFkA-~26oI5?ZpCGrYCVW0<3Nbm483K;$plis7lXi~c8 z=xqPMM`sAycoak~U$b~M$MWg|A4f{Zfj{^U_`v(NS_EH@8srrn-F(&3?KvQh5abng z9Q1d_EUn`>dN(eD22MO{F`qw|&!3Y(&@q5-ImsI@HL(& zEpP6FU%=tFE$%&K@v+SdY05#v%w6*qFa6!({3VNj*}0I0981$U;-!fBimKjKMDCYX<7w3;?9DurhpspJT zd6vX+7cB87Ct0wgnsqt?1}{KJ8Mj zrFjKpoJl(be+2Yw?%!l=ST>IyZ*Z%uP-_al1y=k}ae)r|lqfbA$oV4S=oISIR zwBtX0@sYKphtBlxBD(SZ7yqey?8f0w`})L&x1Aj6fBfY78%~aN9Q^VfCr3Q`z38t_ zt~gFVi{te3r$2h`*U!Im&e$8>f%d>Cnm124mplogT;MT4|>c}}0>v%oHS^@e%%<&BHKQInw5%>3?!`7_jV#E$jIwzeXo&4}8D z>NnTZW9nPGcSF`RA-DUVZ`k7q%JDorY@23R-M4tdOpCJ#`+xKfdtY z_FS>`*e=@9@Ai-3eW6YK0=^Vl&)TTFRcz4TDmM7rDmLh96&q}|iVf>*6&v@O?E}B0 zK8ML{`lF`zcu6fsO0B|sV2!?Fw&R7ux6F3DQAY5}*oeP4M)2~uLq3fDBfuSU5B~h$ zftT?;ayMQfpF%IdQ?gf1%K`bcJS<<3qcRHXkIHA{%W_EWlN0hKxmU*IE3!wvDQDyX zd0a~Jpj72+vR}@MFZ<-HxUue+-Ev$WLcbE&FURC0`a$4&IV|Jow*j}v7v&)O+kv;q z=j9Rfp8$SRj>yN+e-?O`Ov(iMoxqPuMV>@I47^poE;aN+z)ez?C(sW7SBobGeIIa_ ud;{;FUGf-^p?j~F6vy;GPV+x+5F$QF?XURE%D{4Cd|0_>*9rWe`~Lt`<;*ew literal 0 HcmV?d00001 diff --git a/fx/Snowflake.glb b/fx/Snowflake.glb new file mode 100644 index 0000000..0c73f85 --- /dev/null +++ b/fx/Snowflake.glb @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f7fb498549465c2aa0574835226a285c4370db986bb967a226cbf5e66a19c0b +size 130776 diff --git a/fx/Snowflake.glb.import b/fx/Snowflake.glb.import new file mode 100644 index 0000000..23df66e --- /dev/null +++ b/fx/Snowflake.glb.import @@ -0,0 +1,47 @@ +[remap] + +importer="scene" +importer_version=1 +type="PackedScene" +uid="uid://bwgf3d4bhrbmf" +path="res://.godot/imported/Snowflake.glb-4fc7c590a9a455186ce9965fe6d9835b.scn" + +[deps] + +source_file="res://fx/Snowflake.glb" +dest_files=["res://.godot/imported/Snowflake.glb-4fc7c590a9a455186ce9965fe6d9835b.scn"] + +[params] + +nodes/root_type="" +nodes/root_name="" +nodes/apply_root_scale=true +nodes/root_scale=1.0 +meshes/ensure_tangents=true +meshes/generate_lods=true +meshes/create_shadow_meshes=true +meshes/light_baking=1 +meshes/lightmap_texel_size=0.2 +meshes/force_disable_compression=false +skins/use_named_skins=true +animation/import=true +animation/fps=30 +animation/trimming=false +animation/remove_immutable_tracks=true +import_script/path="" +_subresources={ +"meshes": { +"Snowflake_Circle_001": { +"generate/lightmap_uv": 0, +"generate/lods": 0, +"generate/shadow_meshes": 0, +"lods/normal_merge_angle": 60.0, +"lods/normal_split_angle": 25.0, +"save_to_file/enabled": true, +"save_to_file/make_streamable": "", +"save_to_file/path": "res://fx/Snowflake.res" +} +} +} +gltf/naming_version=1 +gltf/embedded_image_handling=1 diff --git a/fx/Snowflake.res b/fx/Snowflake.res new file mode 100644 index 0000000000000000000000000000000000000000..fcfb08f60d687e5958840958e8309f1b74f343ed GIT binary patch literal 86539 zcmeFad3Y36`u<(j0)h$z#UMI35(RM^aRC=1i7SYJV+I!prqgsfAwo#V0tD2cq98`Y zD2dTQQNU$zAvlhpqs9S65gca$l>`%F(+PnQ1STjV^n2guJ_X%4^PBJW{k_-q{`01< zb3W&J&U31(s=KSY@6>ZD?Kf%HI`*#}cnnH=l;cpYE-Va`jtCWvM_YgFU2$mV@wvIr z9Pj!5CoDnGDJX@ZqWqG=V8}l)KNy(kFV6QD1w)fUPM6reNrAkOlVCiPvO1!3KbIOP@&_i43uWay zCm-FNTjVb)%o%6aN--MebVa0fU}422g(2tEpSk?m`P|cH3Eh9*n^%;T?R0`aFH{_u zI59WqoET&Hy=`>5#d^JG>-le-{^0n)q)DNPX2%!im*k9}6vE5Ir5?8Y-U7I~Kl2`5 z0-rOKms?bnn?K1Pn#5h3W!BYmnqBD`hzJ$tl=|~ZCKl%wOw0`xnkPNnonH_t3>4=V zn)lQZ?<9Z8l#9*g9rk%^$Nf7gzc3GPQSaILMzF3Tymdq7HLwnE7+>$g+`PkM_<4eD z)|~$0+(~*<7{k7B+&{sIp+I3UKTv#lC_jFOy$)Z#!elweS#P6-fNr(YJoe#gF+{*DGek$-JaZ5kq)qdq+;)pLR6e_^Sy%6uvz%-{vTwnIY+yZ}bU{Ve~ zapvf9WSHq1m#f#u?4~1q-g^JLh1u7H>rhlOuBfCi8waRBaWRfU-W!2${iFMeig5(W zDIRY=g+J?@kUL37ihs9QZ*BVio5h-=TV8H4wh

Zw1c!uaV~9;{5j|de8memuL== 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�Wo5vN}Vzl-Mi>Jv?(l7G3qUDfWj%*%Z=#S?i^m6(1$CBe@i$`o8 zUlg5}7#k6!)g{EuFp^XZg7vJ`*v@CufSU6baq6z_icF_n_Z#k)%;SL~XX z&r>h zvb??{+pc}Wz2oh#fA`$~SIaj0U9ZJ|+Xk*LW4JwfZQ|{gIX%8-(e`_N+%MXX-hSrz zBg=4{mN_jQS+2P7qhGUlwzs@1F1%tHOLjD;jKzCjc=2Ap_q^HBCug!e`tD@t(9x)D zjeGRnN0YsE6^UT zTVHScIR3~oUh25@p3{6R35mOMS-ki8vw>%tSiJZ1+A9ZM&*H_c-L`f-ix)TGqfg#p zS=_k9&%(ADgBaT0viN@T`nW9bv-RKly)k@FF3)>@@AyKTUeEMDB&E4kj>jxo=fKC~I{edZa*e)_!6t@nKV{QRf>qnGEc zkN5oE@y2^Syw~%{@#X*Bj*I{9*z`YLK94p3r*-G{{BMp&-v0CU@Bj9AwBUzvvswPz z@o2#h3#PG@{ACVwiD*>g7EI%~lD|wD`G0#n`cKQ{Yr{T1hk1SejeR6$&T?-(z3t+- zBV!*q#XGM2|Hq&IZ;n6To_1mui}!x(w)LOCWAVOE?e4m}g2jvb+Zz{r#^S|!{r@`d zUvpW4PH-kmA9vHeEM9#7w?A9MvSRHGZ?P=;cHXy%NGq{nRiA04J^;S`TV6U|4(~g0j^ci{X5;W0i{b?q`O5Bq#Km(u0w}(mz0Xq zDXDa$gecvJfP^SrN{N6VaQB?!2;aE^es!OF|M&1bznNLH)~vN=&zg1iyJx?=GndWE z?%;GfJvGkGnd0Oe&$W9$?9+R0(wdFEuFLu3MUHi2yr0glmvMeNU2a}(PEN+@aeOy- z*Z(-YXL4WNqWDrn_F~xtwud-lm(wU-6%%xBr=5Q=Pp*iF-D6_M+1p&K8;C;QBq! z$$Nj@IEM*Inm2K9Ivo5wE2gY4)4|92l2a9CI`}yDeD_JjA@#&Gi5$F~izSB>&C(=t z@Sj**+sV1{PS))u&vWtW>^U1R;`=dPpSR=v@OjtoD}O)d_ve4&zR&M-e-Gzu`n~Mz zJ&*6lJnO#C=N-R3KF|1d^?bJ<{hs{qj@>-RjT@4uCgt{=Ew$su|^%D6dWQ3sbd z;@?lu!{r($H@0v@WWUch&d0yXH!tEpORrlSXV0yTv-MZB?{jkAPrsgiK5ndo&oBS) z=bxzk^M5w~M3i`b{)s5@qWtr7lAk6!v`R9ri9?)o?jGUEeN^W1nfE$)IeX*g7zZ!s z`TI)j-R4j-Pl?qIb@J6&>)_?pueV1yOn%(BpTn_7$ND(T+CHnKL&UarB^>INotev_ zZiQLd9U6nleI0H>+sC1G`f=4A%B5Rg)4}U>GBwWDZ0vY*lg^paANA?&X(tzNTf9i! zPN(zJ`R3%DZD-HT!}Ys)I-i|h$9Fp1TwTBWJLlT&I=R1we`Wh$)*b#8eOmX6=0K8? z$T{9Wd%v-pTXfp>J4`q;v5|wnUQX7bW8w~z9NaiR)?G7y-Paagv(drJZMau-sDsz% z`9qQpnc}cAZ`o1~UatGu?u{KDn|j*8$NRO* z*IPRHIQD%1Jf?Pv+4UX#^BXUpJHgHt4*r>pYdblw+r_EpxiNmPyom3|czxcE_rvF1 zzpwoLoZpw8@9w`G{QZ`fb9=$T-;YJVPxJS6Zm)Zu+oMjuv-u*vAM>pHKA(5|I{7@~ z*VFUeesu8b^v~y|h%hfj{KxXrK-BAB%1b^Lqvs|c!#n{S?{MPOiFywIY;JmyewUA) z=Q~YGBl$ibMW_F6K62yySZD8F%}0j{XDsRvJs-u&dcBf^%Nv(yf*vl{I67i;M5MgW zH_nIW<)dfupQYEWjkD+0#@UKa`*rjFITE_DV%5R(|2z37O27X* z?{m$Q{1j2b_x(@JKT+B5@#9uWT7~yc4pH|{{`r&V7e4mo+$a>TS@_tKbGr)Lm#%e) zdo6i1J~|SvKWsaHogJrtcCOhgy-bw<0C*d-b48ugIo}*F(*7Uk zbk29*@AW;IYt%Jze=GcbQ>0JPY`g2=b^Kl3FY?#<>13SmQS0)Lw7({f|3oJI)>*fU zA_@uaCg}F$%q~ioyGBvh-0>W?9>~8W3R^2}J+QTsAMg8pEKC67`$eI7-wB|3Umq)O zj6;WFQD<^q#_>B88&$~r;Pm-*aq^Du{dE1$;y+98++o%4I-avJci21;opwGtIQ|ov zc_Q(EBq$1JA0{4f_Msd1WNe48{XBh)U%MRZV+S9b4W>0r?vOh{?(q(u?>?(I)J{=r zhJ$~0=l?r7*6&fD zFP#my7o82K|9N~r=2Es z508ZrxCw9eckpL>bt}}_=TNU~-Q5lm+s3SO@Mn6i&$ZpznX~0|d--_V zF0b>t-81F>sNVo5=j=Gz*>HY4f89J?pW}P~-CVt|?mD?^^Yq!=U)lawbic@7HxG9v z<@|Tgyra|p8acj~adI6Kk80}R+}qII!H-*8xMWd>2l*bX zb#Tv{9lYGCyero@xaZmqPs_Xh=?CjAcJk@vk*<#KZF(K9&->}dzKHL9_$$56PLJ~g zCpkO4lMQGTo%Vh@zL#-wMU!r9?BM6;#yK=QH-55%)9K)2e95V$^&EVRR!q6Hp2LY| z$1gZM{^NOzmrFhI_yq^|oW$X2dEf8GIDO7W^z%0-@9jO0?`%J<&->%;`#s}xsNakK zsT}L~*jNaU!VUQbI?oMuKq&~>i3)+k3<9ivu&s}bD@?Q7T`N*Hw zy(k~W${K5lL-c%fsPLhU4$ubMx)J8w%3cWno=Ocgi=5pF!<)i2EpQYE?a_i&l zxb=xndp{lD%Xm5Or}NdXvyX3|SN?y?KidlAf6Kw=tEcmY>yMsaT;KEZPjr5CeUARK zFZ}H5-|P10Y5tzcpSSt`gUb)*aPa*%W7+l&{`_ps&b6f-vaQRW-@(hLg=zac_;a+| zN_BDY<7Y0bv(I71%DTH9#^xWh&cQz?Uw*9IT!#;jwwmu?AKmQl;Lq_~pL;g!XlKLe z_WCa8zr4FUOLpS-N=^L}`{{v6Kfiq3zQ zUT0@e;(?Q#o&IMAG>T4pKV84Wge2o9JGe0petvGe*WvVRIJ>d2gO6?h&a;nE|IV|2 z7ulb!c{%@XvX}GEX`;W&?Bo6Ecb{EcI=fESi}=y?d4Ifpzh``o^?UN^d$xb)9?$7_ zI-{Sp`Fp$<@n3Y0=hwmK6u*8xhy15<(9D%}c02g>{daSa)8+T2&lP?C$U%-5JqP)i zjGl*lEc#rOI%Vpa4tFozYvJH^|3eP^H}ps5i|6?6^N*X$)A{IW+RyuW`N+xpF+LCc zoqUvW^P!CnEKFG`ur8` z{c%3|_4M;{|1;)`^2>|(FUmhAiJ9y^%bFy&!uM_-KlZe1-#Qn?ds2ro zq3o@5PB#2x{O33T{*H{-{XAQLN9OO?+On&_swcXl=8-D>$?D$cExWdqNtHg;d{4VN zjXvJl&+GW@Cv7+10=c)8Ie9cn-qGHNlSfZhFOa*yDo?xXcKmi4_kH8OuAN3lwVgk% zFY?;`Q9FP9IPZ_A-B>3dJaz9Fw%vWD>-+PWTiABb<9uIK9rcda|86VF2Pflwdtx)n zmZzPJAMgAAA+AFH(=uK*YMxK#`?Re;VN6dsgzee6C$B$g6uy*apbqedqm)2Vuu&u6zGpl3#QhD~4#(aYHrJ0m(W7v-!($5r(65ja zk|DZut>39}!!}Jpj*y7{&k5-eEhCzQU*!Gsi@KLm0MHaP2*1;-Lo@&lLz6Ybul&{w zYXb;)6O;)-Km~w6X`n(NP#?S;+A9SFcnJic7)T0gh5jWAzxZ1T)CO@tJy0FQ21P(g zkQ@{T*+E^<3cLgAfXbjH$QL^I7AOJU0Qo^-kPhSpZ-W-#RgfO!1O-49@ET|ha)WlD z2gn1`fIgrt=nVRUHXtMD4GMx@AOk1_`hoVKIp_=0f@Yv6=mffe#b6*94hDlIpd)w} zECBC;`JfvZ0lI=2AOg$;-9ZO10L%r$z&tPrECL^bpFAlrRoV597kvFcC})1&jw@f(&MWuR;qm!+5puWz41>EtcDNS|ho6JZpg+tB`@u~h2iyo+z}287{1U7KyFh!G4R(Ngz)+YU zW`*rw7MKs_g00}2Z~)8=i@~>GH#iXHg+pK;ma2l)w%fMOieK-Nmfn#B1I1@I6 zGhijy2+oG%;Ct{BSQd_e)8TkH5q<;b!9{QZJOn?6Q{i5?1nz;8;bJ%mZh;@bt#Atb z2+oDO;6k_?&W8u#8u%I92|tCa;ZnE_u7&&HGPn*NfZxGYa6kM49)_FX=WsjR0awDW z;6``?o`YY)C?QIM9>5;37^(_iL2tw2s4FZA z-$1ERVe|$}g$lzw=pC3B6@j&26<81@M}<%|_!i2DQli&U3X}n*LHSS?R0O3(S<#!Q zGIyXIQ$;|1Uti@ z;c3_fo`s#@DYy(RMio)b5R1_gR23~ibwgA|bx;-b0h$n^3Yvr}pz)|ihzjUaR3BAG zAEI|qJyZ*gMa|GWR2wx%EzwjoA2mdup;@Q_dJm05EztX@4_bs)qd90HnvPbYfv6w) z2z`u3qrqql>WGG*xo8dQgnFO>XgHdQdZN$KAhZGPKtoYybO5bGU!z0l3)CC!MkCNJ z)C&zm-=IxsEjoz0pjBum+Kj$JzoWhADB6!6qA$^QbPt_CchOdK9Bo0@(Ry?PZ9^N; z9&{TWLBFDX=sx-$9Y(j%x9BAL4qZcM&>eIPokb52z(1hh(0TL-T|(#3P4o*og|48> zh~n7zXY?aVj3L%I5k~kTdKv$OUcwj9Rg@5OjByft8a+mFu*9FB!niSd1vf=+;@PMj zDvDF%Vz>iJh1;WocofQyBhX0H2<5@aaRJ-}6~bw8GMo!1#c6N~oEvAruj7I(!827;Sa98{;u89>sg5%-7H~~J2 z1&)Oe;(_=8ws-(Gcr7M)9S$(Vaq&jn2XDgh@i%xd?u|dkz3?F153j}}@jl!ikHTZ{ z7Q7!1#fR`tJOnSpU*OSr4W5k; zzJODZoA?3#1*atE@Kv0YJjO}LW&8+V!Y`8_aa?i*pTRe9LUJD`BDZjI0?BVUKDmbh z`5nJR7)eYXVoqKmX$U6Ch$N|qA%ess2?!zqvE((9gTx|)N;Jt$;*eZK zk=UdF$w<E^$7}q8%a7{7}FUQ01 zJESnFMMmH{$z&jzMEa1A$wcxl`4VR%=}88XhkS+ek}TwFoSkgOnaD$OmrNz|Lfj>f z$xQM)`7Fdt@+p}?&XY?aW{?Zv?|}VC=7yL;ekF^@9CC`xCJV`Ya+<6p_s9aWimV}5 z$!}x{xkGM{#pD?IfvhHH$R2W^AbN{DAlC??hsb_%j+`YY$U*Ww`H~zZw~3&e$qsUW zd`qsAorKaO#L|THJMtAtK{1U*U!jcdCJE?a5})oO-;m@~Q%O_OuL-A#XdD{Q5;Pf2 zOOw#IXl(j2Ekj?UWobN`p2nrcsG)Dtm#7MVk*6?CLtm$fX>pp7rly5xDw=_&qeW;o zT8h3(v(u8a9L-Ei)10&d%|~<4qVx@#iRPjC>D#m(EkLu-7PLIAPMg!pG&gNX3(^KO z7tKqX(W<=>Xb_cBBz>DD6SV&~~&Z9YsH&UFc}~F8z@9rQPWeI+%8*eQ1B$ zl}@1JXm2{6_M;!uUi2e6k#?g$lTCChnM${l&GaX-j;yCM>6i2iGJ}3j*3!LXC0$GQ zku79R_!~;==r-~>{e(`Y%jqOKi%z2}=zRJGolY0f4fHEIhi;^w(XZ(e`YByaSJByY zFf3^z1kKE7eRf$nH|iF3^WG3CqMT(rfe%y-(w_>-07DJI&4tu}Ab0EzVwL zIax85f!(A9*#laD-JtjAn=C8K%-&*`X-4)s%gu7IR;(y1&E8-wSuU2BMX>U$9m~(k zv3#sK%f_0qJS+<Go zmXIxE$LJ__lzzq%v$J$5d!H?1=jm8>jxJz`&1YvQVBgW%Y$W@H9igAH57~Qc78}7n zV58YQHi<1^W7uT2h%ILyvBhj6TfwHY32ZK#!^W{`Y$p4dtz|3ORJMlAV5`{_wu-G| z<5^tx1G`E$u%GE^_A>i{o@ATZkL)!4lKn`(W53XE*(rLPUZF?XdUlNcM31wt*%#~^ z_Bs2CZDfbo4t9cVVmsOQ>>@kBF0ei9 zJiE-cv)HT;>(08eK`dZ{Sq~PA^<{BbFIGP&#;>wp*i}}HHx6#HGW@p?H`zUQgXQG; zcuszU<>FUZCVnf#6;_--X1}t`{5E^U?ywx3@Y4J)qa5=>ycCE0E&c`vJU!3C5zo$( z@e;fOFTzXmg1jP6&6DzMJS%^Vr{Wp7;%RtMUWsd-kSF0Oc>$h?m*?sD+q^DM%MEYI z%kmn$1uw@F@H#vNug&B0S9o(?jaTNaxaC!NJzkSn=c9Qu-i9~fqj)XefPct4@^QQo z@4y@Kk^CM09?v-j;Xb?f3}Zn}5LD^FDkm|Co2>@AF=K zA|J?m^5J|K@51}>L3}Eo!~64Yd>Q|U&)`e>1m1(s<^%XA{9WFcFX2=8c)px>=acwc zK9d*b-|~lSC4ayU@i%!a{vBV-kMTHs4Uf$a@{Ifd5BRJ6ceaPG;Jf*Kc9?JEtNC8O zif`cS_&0njKh8hr+xQXwJ^zv)2g}=0v>CIhW!JS3>g3TyP|kb0`>3%;WNh{5=1izr?ZliJ#*S_*s6H zU*tgi!iBiRZ*wNJxW>7-#f`YfZ}P|d5&xOr<@fn@9!FSlhsPGb@mS(l9*CF34L+SW z6N`CVv5+?vv-o`8RwNb?;!~bTEa2~mY5Z+5k5A@{cm?sYC@((a%|!|kUsM$F#493! zs3y{iHX@-&C(?*|qP0jRT8M@sr6?^biPWO1C@flvj-ruhCF+V!qPTcnR1uX$8Bt7> z6&b}_qOs^KGKu`6ka$zn5d}mCQBu4s`il}Gvlu1XiC$u)XfN`Kexj)8EAomW;yuw_ zbP=OP7SUA<5WPiDv0RK0W5qDBO!N_h#2WFjSSyBzkHlcHRP+!_#6Zza3>Tk@F=Bxj zDprUI;sf!ScwdYcABx3drdTb;iCJQ$_*_g9tHcbkLCh1=#Uim#Ocb-le6d;V6m!L7 zaZs!iTf_nJg_tIGh&f`rm?}OI`^A@Hz4%5<5gWxWu~jq>w?%UCtw<_<5luukaZh|F zeiJ#wQITET6y?MXkyVru$;40Mkhm(6h+oAiaYXzq4vX)_F>y55caa#N?kUS}Bi?lM2U^16TBkPOw;x(yc z2AM%f`I<;2-w-L~tD>ODEs{wplggYTwKS5*Boa$4xlAEnmg%LCab-H0Q3mo=`I5{e z6UsR96`5RGnLs9%v1E3cS;m*yWFnbW#*GF`AFQ>}oa=%gj z%OB+>c~|}*zm`ACo$@F7mE0xo$aC_P{8es{XXJHxMUIduRSEe}7MID?a9K+xS5PHY zH6&2gWn9%!##c3EC;664q<)w2R56)aN%dGJQIDihNF`Q*N~N%hrBbL@6<4n)tJ0~L zlu-#(LdDd}Dz+jjy-KUJ%BbR~3`(gq>NQ2xKv`Zjl9^QlSxya>kqzZrDw8U%>dOi$uga=Qs4Oav%Av}t!m6susoqc()jKM`s-#|5)m1T7 zNR?70RX+8mDynj+TB@2VsA{Srs)j0{-d43$Zgp8MQ4eHY^_yI*uF9KorD~{Fsb6FR zbxTfF7v&UnL!OuS<#<&`eXQ=u6{@wWuO_Q{s+DS_rmK!>jcTkqsSawsTCG~Dx~V>DkQ$+8tG;TT z8l%2ayVOY4S$(5ES6kFUwN~|3JJoQtL-kVcsRQau^@TbV{<_XawOegdTh#+~R2@@? z)qS;H?NyJ}_X_C!>ZIDI?y0ZUUA0GTR!7t?>N|ByeXAa-^Xf$S%QnZ=Idw|?rmiTc ze^6J|?~3ZP>XEvvxW1__sXOYnI-{~^|Okv zg(fN%>d9;nLe{;G|hr`oCxy0Y%5+o?)ALbcLERZHDQ4ORnGQ(aCs z(fw3}uC6QUX1aoYM_19Ubv@llSJm}(JKa^+)a`X$-Ay;vb#x2eT-VTzbVFTD_t2en zZT+rppu6i@x{L0q-`2BKT&=a#iS-PXL|Z*m#nYdtSh{9#T=&(3Lmb!DgMs?69v)(# z9;OHAje19j0eYA2qrcXJLiEum^@n<>-l~V_5A+EAm7b)J>G$+xJyGx1-|6@D3H_}e zt2gK^dV=1pm+GVXoc=~1(R=h6y;?8QoAj6Z3%ypa)6?{FeL$bm)Ad}vP_NRv^*nuA zuh8f9&w81jqJPzA^;LaO|ETBa8+xU_u4n5t`mVmHzt?y4RQ-d#rZ4NCbQ*JCKhnSJ zwC0Mwty36eUNN^cG&gkub3rFGzvxT)H=Wcx)XB^Poz5r&Omg#BOM^{f6U(GB#KbnK zO(r8uN)wn2Ca$q2QTV@waueUYY%-g?<|RYT>n5YgX^NOvjWKyl9FyB<6VJS1vYPa! zuwmvklh0%~hjc45N|!KWbZc`^f1;b2GN!4SsY{y~x~}ZXBdXeydIrlu)xI+zGk&2%=k zOea&-v^5<~1+!O=H=pUQW`X|5?9=1*B-6uuY(CQOnsIuxS)xBMAM3^XQ$5CXF{8|U zJ;e+#-OV`D&Ga`t&4*^FnQVHQVP>M4VfvW~W{8{9cd z{l#oCLu@NM*nVYN+Rdi7Jz@ITug&*nkr`l{+JSb78EHG(7Iu(rZadi4wy*7ChuSu_ zs~v8~hQEL|%yze9Y){+G_OtzMJKM{?YunoQ?P%NCj856CH5n`$nG@j?JPUNuC(LrOgq`Gu=DH} zc8Z;E*V!-a47=9OwVUi`c8*z!H!-&!8@lLtdd9;kqaef?xV87DYsZIFlWxBa9I;siF7 z$;0#Tgu#9rH@Hal+B`gNkT8fF%3LHDN%CN?O%hB*yX-}hBuE}431udth-vsc0yT(gc&5qxLw*$lxYn=ZH# za_)t-^Zi~ZlQk?ag+1r(uQpTIc66rTyv-B*8rgT+{$g{4ZAa$_PTPXPFV^*aZ*SVX zVcXGpgYRw8;AUi>vt2lBJGyXi+>Q*EqRJtv293iX=3R;$JtC+Yj0_e9Bf@e?Sl0+X z2#N)xLy4+kdvr*R4(rC?gJ4ClCX!kaj1PN?g(wl!4aSAFqsN8Rw2*dvWrBvmq_B4M zq>!2u(yp&U@QBO|Ye&xvsn0^%^*NvBhqa^U2bIJA-NF2@+!xk!f-Rx!oM1;-PYX7M z`lbb6hxPbiU1)QBus*DNhMqSMeeV`D4cm(%=g~8y)&zrtE+LwR?G<6|=oKL~I2_d_ z95*277`E3%>KGhS147LmL*4y?h_JmW($0X8>K9s%2<`U?T7~T`k-qc`sXn2HtwJAr zhSvLpwez=6NRT$SY`|BEJ3H>i?F<4I|Uc*>Y#I2z80(ww%aj5wXj6Nm;eVWgRx=xYFMWYngq+k z@~CYR{BElR!^3iGSices3l@gu13N4@W)}y|!ZKyBIM`|53o3=>qp$|SvS3tLrU~oR zL5pB%Se~#gf|GVn&@wFF2=)Z82lIkmVVO9r69pB6xna4(Rt$c&U4tHBc{!}F+95&r zu>38o@7qDa(6GE4)@SY7pnX_o4AusjgO7sG!%~E`3`zwbhvgw#D)`1u4@!pROTqLY zcCa~^8kV`jI(N_^_$n;V*$zPvlno|_|$9*4OR3 zL9ej void: + var camera := get_viewport().get_camera_3d() + if camera: + # Track camera velocity. + var camera_velocity := ( + camera.global_position - _last_camera_posn + )/delta + _last_camera_posn = camera.global_position + # Reposition particle system. + global_position = ( + camera.global_position + + camera.basis.z*front_flat_distance + + camera_velocity*seconds_ahead + )