440 lines
16 KiB
GDScript
440 lines
16 KiB
GDScript
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()
|