netfishing/world/world_weather_service.gd

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