152 lines
3.5 KiB
GDScript
152 lines
3.5 KiB
GDScript
class_name WorldWeatherService
|
|
extends Node
|
|
|
|
signal weather_changed(weather: Weather, seconds_remaining: float)
|
|
|
|
enum Weather {
|
|
SUNNY,
|
|
CLOUDY,
|
|
RAINY,
|
|
FOGGY,
|
|
}
|
|
|
|
const DEFAULT_WEATHER: Weather = Weather.SUNNY
|
|
const SUNNY_DURATION_RANGE := Vector2(480.0, 900.0)
|
|
const CLOUDY_DURATION_RANGE := Vector2(300.0, 720.0)
|
|
const RAINY_DURATION_RANGE := Vector2(300.0, 600.0)
|
|
const FOGGY_DURATION_RANGE := Vector2(300.0, 600.0)
|
|
|
|
var _weather: Weather = DEFAULT_WEATHER
|
|
var _seconds_remaining: float = SUNNY_DURATION_RANGE.x
|
|
var _running_authority: bool = false
|
|
var _rng := RandomNumberGenerator.new()
|
|
|
|
|
|
func _ready() -> void:
|
|
set_process(false)
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
advance_weather(delta)
|
|
|
|
|
|
func begin_authoritative_session(seed_value: int) -> void:
|
|
_rng.seed = seed_value
|
|
_running_authority = true
|
|
set_process(true)
|
|
_set_weather(DEFAULT_WEATHER, _roll_duration(DEFAULT_WEATHER), true)
|
|
|
|
|
|
func begin_remote_session() -> void:
|
|
_running_authority = false
|
|
set_process(false)
|
|
_set_weather(DEFAULT_WEATHER, SUNNY_DURATION_RANGE.x, true)
|
|
|
|
|
|
func end_session() -> void:
|
|
_running_authority = false
|
|
set_process(false)
|
|
_set_weather(DEFAULT_WEATHER, SUNNY_DURATION_RANGE.x, true)
|
|
|
|
|
|
func advance_weather(real_seconds: float) -> void:
|
|
if not _running_authority or real_seconds <= 0.0:
|
|
return
|
|
_seconds_remaining -= real_seconds
|
|
while _seconds_remaining <= 0.0:
|
|
var overrun: float = -_seconds_remaining
|
|
var next_weather: Weather = _choose_next_weather()
|
|
_set_weather(next_weather, _roll_duration(next_weather), true)
|
|
_seconds_remaining -= overrun
|
|
|
|
|
|
func apply_authoritative_snapshot(
|
|
weather: Weather,
|
|
seconds_remaining: float,
|
|
) -> void:
|
|
if not is_valid_weather(int(weather)) or not is_finite(seconds_remaining):
|
|
return
|
|
_set_weather(weather, maxf(seconds_remaining, 0.0), false)
|
|
|
|
|
|
func get_weather() -> Weather:
|
|
return _weather
|
|
|
|
|
|
func get_seconds_remaining() -> float:
|
|
return _seconds_remaining
|
|
|
|
|
|
func is_raining() -> bool:
|
|
return _weather == Weather.RAINY
|
|
|
|
|
|
func is_foggy() -> bool:
|
|
return _weather == Weather.FOGGY
|
|
|
|
|
|
func get_weather_name() -> String:
|
|
return weather_name(_weather)
|
|
|
|
|
|
static func is_valid_weather(value: int) -> bool:
|
|
return value >= Weather.SUNNY and value <= Weather.FOGGY
|
|
|
|
|
|
static func weather_name(weather: Weather) -> String:
|
|
match weather:
|
|
Weather.SUNNY:
|
|
return "sunny"
|
|
Weather.CLOUDY:
|
|
return "cloudy"
|
|
Weather.RAINY:
|
|
return "rainy"
|
|
Weather.FOGGY:
|
|
return "foggy"
|
|
return "unknown"
|
|
|
|
|
|
func _set_weather(
|
|
weather: Weather,
|
|
seconds_remaining: float,
|
|
force_emit: bool,
|
|
) -> void:
|
|
var changed: bool = weather != _weather
|
|
_weather = weather
|
|
_seconds_remaining = maxf(seconds_remaining, 0.0)
|
|
if force_emit or changed:
|
|
weather_changed.emit(_weather, _seconds_remaining)
|
|
|
|
|
|
func _choose_next_weather() -> Weather:
|
|
match _weather:
|
|
Weather.SUNNY:
|
|
return Weather.CLOUDY
|
|
Weather.RAINY, Weather.FOGGY:
|
|
return Weather.CLOUDY
|
|
Weather.CLOUDY:
|
|
var roll: float = _rng.randf()
|
|
if roll < 0.45:
|
|
return Weather.SUNNY
|
|
if roll < 0.80:
|
|
return Weather.RAINY
|
|
return Weather.FOGGY
|
|
return Weather.SUNNY
|
|
|
|
|
|
func _roll_duration(weather: Weather) -> float:
|
|
var duration_range: Vector2 = _duration_range(weather)
|
|
return _rng.randf_range(duration_range.x, duration_range.y)
|
|
|
|
|
|
func _duration_range(weather: Weather) -> Vector2:
|
|
match weather:
|
|
Weather.SUNNY:
|
|
return SUNNY_DURATION_RANGE
|
|
Weather.CLOUDY:
|
|
return CLOUDY_DURATION_RANGE
|
|
Weather.RAINY:
|
|
return RAINY_DURATION_RANGE
|
|
Weather.FOGGY:
|
|
return FOGGY_DURATION_RANGE
|
|
return SUNNY_DURATION_RANGE
|