279 lines
8.0 KiB
GDScript
279 lines
8.0 KiB
GDScript
@tool
|
|
class_name WeatherController
|
|
extends Node
|
|
|
|
signal weather_state_changed(old_state: WeatherProfile.WeatherState, new_state: WeatherProfile.WeatherState)
|
|
signal weather_transition_started(from: WeatherProfile.WeatherState, to: WeatherProfile.WeatherState)
|
|
signal weather_transition_completed(state: WeatherProfile.WeatherState)
|
|
signal weather_state_entered(state: WeatherProfile.WeatherState)
|
|
signal weather_state_exited(state: WeatherProfile.WeatherState)
|
|
|
|
@export_group("Weather Profiles")
|
|
@export var weather_profiles: Array[WeatherProfile] = []
|
|
@export var current_weather: WeatherProfile.WeatherState = WeatherProfile.WeatherState.CLEAR:
|
|
set(value):
|
|
if value != current_weather:
|
|
_request_weather_change(value)
|
|
|
|
@export_group("Transition Settings")
|
|
@export var default_transition_duration: float = 5.0
|
|
@export var transition_curve: Curve
|
|
|
|
@export_group("Random Weather")
|
|
@export var enable_random_weather: bool = false
|
|
@export var min_weather_duration: float = 60.0
|
|
@export var max_weather_duration: float = 300.0
|
|
@export var snow_enabled: bool = true
|
|
|
|
@export_group("Scripted Timeline")
|
|
@export var use_scripted_timeline: bool = false
|
|
@export var weather_timeline: Array[Dictionary] = []
|
|
|
|
var _current_profile: WeatherProfile
|
|
var _target_profile: WeatherProfile
|
|
var _transition_weight: float = 1.0
|
|
var _is_transitioning: bool = false
|
|
var _transition_duration: float = 0.0
|
|
var _transition_elapsed: float = 0.0
|
|
var _random_weather_timer: float = 0.0
|
|
var _next_weather_change: float = 0.0
|
|
var _timeline_index: int = 0
|
|
var _timeline_timer: float = 0.0
|
|
|
|
|
|
func _ready() -> void:
|
|
if weather_profiles.is_empty():
|
|
_initialize_default_profiles()
|
|
|
|
if transition_curve == null:
|
|
transition_curve = Curve.new()
|
|
transition_curve.add_point(Vector2(0.0, 0.0))
|
|
transition_curve.add_point(Vector2(0.5, 0.5))
|
|
transition_curve.add_point(Vector2(1.0, 1.0))
|
|
|
|
_current_profile = get_profile_for_state(current_weather)
|
|
_target_profile = _current_profile
|
|
_schedule_next_random_weather()
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
if _is_transitioning:
|
|
_process_transition(delta)
|
|
|
|
if enable_random_weather and not use_scripted_timeline:
|
|
_process_random_weather(delta)
|
|
|
|
if use_scripted_timeline:
|
|
_process_scripted_timeline(delta)
|
|
|
|
|
|
func _initialize_default_profiles() -> void:
|
|
weather_profiles.clear()
|
|
weather_profiles.append(WeatherProfile.create_clear())
|
|
weather_profiles.append(WeatherProfile.create_cloudy())
|
|
weather_profiles.append(WeatherProfile.create_overcast())
|
|
weather_profiles.append(WeatherProfile.create_rain())
|
|
weather_profiles.append(WeatherProfile.create_storm())
|
|
weather_profiles.append(WeatherProfile.create_snow())
|
|
|
|
|
|
func _process_transition(delta: float) -> void:
|
|
_transition_elapsed += delta
|
|
var t := clampf(_transition_elapsed / _transition_duration, 0.0, 1.0)
|
|
|
|
if transition_curve:
|
|
_transition_weight = transition_curve.sample(t)
|
|
else:
|
|
_transition_weight = t
|
|
|
|
if t >= 1.0:
|
|
_complete_transition()
|
|
|
|
|
|
func _complete_transition() -> void:
|
|
_is_transitioning = false
|
|
_transition_weight = 1.0
|
|
_current_profile = _target_profile
|
|
var new_state := _current_profile.weather_state
|
|
weather_transition_completed.emit(new_state)
|
|
weather_state_entered.emit(new_state)
|
|
|
|
|
|
func _process_random_weather(delta: float) -> void:
|
|
_random_weather_timer += delta
|
|
|
|
if _random_weather_timer >= _next_weather_change:
|
|
_random_weather_timer = 0.0
|
|
_trigger_random_weather_change()
|
|
_schedule_next_random_weather()
|
|
|
|
|
|
func _schedule_next_random_weather() -> void:
|
|
_next_weather_change = randf_range(min_weather_duration, max_weather_duration)
|
|
|
|
|
|
func _trigger_random_weather_change() -> void:
|
|
var available_states: Array[WeatherProfile.WeatherState] = []
|
|
for state in WeatherProfile.WeatherState.values():
|
|
if state == current_weather:
|
|
continue
|
|
if state == WeatherProfile.WeatherState.SNOW and not snow_enabled:
|
|
continue
|
|
available_states.append(state)
|
|
|
|
if available_states.is_empty():
|
|
return
|
|
|
|
var new_state: WeatherProfile.WeatherState = available_states.pick_random()
|
|
set_weather(new_state)
|
|
|
|
|
|
func _process_scripted_timeline(delta: float) -> void:
|
|
if weather_timeline.is_empty():
|
|
return
|
|
|
|
_timeline_timer += delta
|
|
|
|
if _timeline_index < weather_timeline.size():
|
|
var entry: Dictionary = weather_timeline[_timeline_index]
|
|
var trigger_time: float = entry.get("time", 0.0)
|
|
|
|
if _timeline_timer >= trigger_time:
|
|
var state_name: String = entry.get("state", "CLEAR")
|
|
var duration: float = entry.get("duration", default_transition_duration)
|
|
|
|
if WeatherProfile.WeatherState.has(state_name):
|
|
var state: WeatherProfile.WeatherState = WeatherProfile.WeatherState.get(state_name)
|
|
set_weather(state, duration)
|
|
|
|
_timeline_index += 1
|
|
|
|
|
|
func _request_weather_change(new_state: WeatherProfile.WeatherState) -> void:
|
|
var profile := get_profile_for_state(new_state)
|
|
if profile == null:
|
|
push_warning("WeatherController: No profile for state %s" % WeatherProfile.WeatherState.keys()[new_state])
|
|
return
|
|
|
|
set_weather(new_state, profile.default_transition_duration)
|
|
|
|
|
|
func get_profile_for_state(state: WeatherProfile.WeatherState) -> WeatherProfile:
|
|
for profile in weather_profiles:
|
|
if profile.weather_state == state:
|
|
return profile
|
|
return null
|
|
|
|
|
|
func set_weather(state: WeatherProfile.WeatherState, transition_duration: float = -1.0) -> void:
|
|
var profile := get_profile_for_state(state)
|
|
if profile == null:
|
|
push_warning("WeatherController: No profile for state %s" % WeatherProfile.WeatherState.keys()[state])
|
|
return
|
|
|
|
if _current_profile:
|
|
weather_state_exited.emit(_current_profile.weather_state)
|
|
|
|
var old_state := current_weather
|
|
current_weather = state
|
|
_target_profile = profile
|
|
|
|
if transition_duration < 0:
|
|
transition_duration = profile.default_transition_duration
|
|
|
|
_transition_duration = transition_duration
|
|
_transition_elapsed = 0.0
|
|
_is_transitioning = true
|
|
_transition_weight = 0.0
|
|
|
|
weather_transition_started.emit(old_state, state)
|
|
weather_state_changed.emit(old_state, state)
|
|
|
|
|
|
func set_weather_immediate(state: WeatherProfile.WeatherState) -> void:
|
|
var profile := get_profile_for_state(state)
|
|
if profile == null:
|
|
return
|
|
|
|
if _current_profile:
|
|
weather_state_exited.emit(_current_profile.weather_state)
|
|
|
|
var old_state := current_weather
|
|
current_weather = state
|
|
_current_profile = profile
|
|
_target_profile = profile
|
|
_is_transitioning = false
|
|
_transition_weight = 1.0
|
|
|
|
weather_state_changed.emit(old_state, state)
|
|
weather_state_entered.emit(state)
|
|
|
|
|
|
func get_current_profile() -> WeatherProfile:
|
|
if _is_transitioning and _current_profile and _target_profile:
|
|
return _current_profile.lerp_to(_target_profile, _transition_weight)
|
|
return _current_profile
|
|
|
|
|
|
func get_cloud_density() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.cloud_density if profile else 0.2
|
|
|
|
|
|
func get_cloud_opacity() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.cloud_opacity if profile else 0.7
|
|
|
|
|
|
func get_cloud_coverage() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.cloud_coverage if profile else 0.3
|
|
|
|
|
|
func get_cloud_color() -> Color:
|
|
var profile := get_current_profile()
|
|
return profile.cloud_color if profile else Color.WHITE
|
|
|
|
|
|
func get_wind_speed() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.wind_speed if profile else 1.0
|
|
|
|
|
|
func get_wind_direction() -> Vector2:
|
|
var profile := get_current_profile()
|
|
return profile.wind_direction if profile else Vector2(1.0, 0.0)
|
|
|
|
|
|
func get_rain_intensity() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.rain_intensity if profile else 0.0
|
|
|
|
|
|
func get_snow_intensity() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.snow_intensity if profile else 0.0
|
|
|
|
|
|
func is_lightning_active() -> bool:
|
|
var profile := get_current_profile()
|
|
return profile.lightning_enabled if profile else false
|
|
|
|
|
|
func get_lightning_frequency() -> float:
|
|
var profile := get_current_profile()
|
|
return profile.lightning_frequency if profile else 0.0
|
|
|
|
|
|
func get_transition_progress() -> float:
|
|
return _transition_weight
|
|
|
|
|
|
func is_transitioning() -> bool:
|
|
return _is_transitioning
|
|
|
|
|
|
func reset_timeline() -> void:
|
|
_timeline_index = 0
|
|
_timeline_timer = 0.0
|