fish_lib/game/systems/
weather_system.rs1use crate::game::systems::weather_system::attributes::WeatherAttributes;
2use crate::game::systems::weather_system::config::WeatherSystemConfig;
3use crate::game::systems::weather_system::weather::Weather;
4use crate::utils::math::float_interpolate;
5use chrono::{DateTime, Timelike};
6use chrono_tz::Tz;
7use noise::{NoiseFn, Perlin};
8
9pub mod attributes;
10pub mod config;
11pub mod weather;
12
13pub struct WeatherSystem {
14 cloudiness: Perlin,
15 cloud_brightness: Perlin,
16 moisture: Perlin,
17 wind_presence: Perlin,
18 wind_strength: Perlin,
19 temperature: Perlin,
20 rain_intensity: Perlin,
21 config: WeatherSystemConfig,
22}
23
24impl WeatherSystem {
25 pub fn new(config: WeatherSystemConfig) -> Self {
26 let seed = config.location_data.weather_seed;
27 Self {
28 cloudiness: Perlin::new(seed),
29 cloud_brightness: Perlin::new(seed * 2),
30 moisture: Perlin::new(seed * 3),
31 wind_presence: Perlin::new(seed * 4),
32 wind_strength: Perlin::new(seed * 5),
33 temperature: Perlin::new(seed * 6),
34 rain_intensity: Perlin::new(seed * 7),
35 config,
36 }
37 }
38
39 fn time_to_noise_input(time: DateTime<Tz>) -> f64 {
40 time.timestamp() as f64 / 1_000_000.0
41 }
42
43 fn normalize_noise(noise: f64) -> f32 {
44 (noise as f32 + 1.0) / 2.0
45 }
46
47 pub fn light_level(time: DateTime<Tz>) -> f32 {
49 let hour = time.hour() as f32 + (time.minute() as f32 / 60.0);
50 let multiplier = (((hour - 6.0) * std::f32::consts::PI / 12.0).sin() * 0.45) + 0.55;
51 multiplier.clamp(0.1, 1.0)
52 }
53
54 pub fn get_weather_attributes(&self, time: DateTime<Tz>) -> WeatherAttributes {
55 let t = Self::time_to_noise_input(time);
56
57 let cloudiness_noise = self.cloudiness.get([t * 5.5, 0.0]);
58 let cloud_brightness_noise = self.cloud_brightness.get([t * 2.5, 1.0]);
59 let moisture_noise = self.moisture.get([t * 3.25, 1_000_000.0]);
60 let wind_presence_noise = self.wind_presence.get([t * 40.0, t * 50.0]);
61 let wind_strength_noise = self.wind_strength.get([t * 4.5, 5_000_000.0]);
62 let temperature_noise = self.temperature.get([t, 2_000_000.0]);
63 let rain_intensity_noise = self.rain_intensity.get([t, 0.0]);
64
65 let moisture = Self::normalize_noise(moisture_noise);
66 let rain_intensity = Self::normalize_noise(rain_intensity_noise);
67
68 let wind_strength = Self::normalize_noise(wind_strength_noise);
69 let wind_presence = Self::normalize_noise(wind_presence_noise);
70
71 let cloudiness = Self::normalize_noise(cloudiness_noise);
72 let cloud_brightness_raw = Self::normalize_noise(cloud_brightness_noise);
73
74 let cloud_brightness =
76 (cloud_brightness_raw * (1.0 - cloudiness) + 1.0 * (1.0 - cloudiness)).clamp(0.0, 1.0);
77
78 let raw_light = Self::light_level(time);
79
80 let cloud_light_blocking =
81 cloudiness * (1.0 - cloud_brightness * self.config.cloud_brightness_light_block_factor);
82 let light = raw_light * (1.0 - cloud_light_blocking);
83 let temperature = Self::normalize_noise(temperature_noise) * raw_light;
84
85 WeatherAttributes {
86 cloudiness,
87 cloud_brightness,
88 moisture,
89 wind_presence,
90 wind_strength,
91 temperature,
92 light,
93 rain_intensity,
94 }
95 }
96
97 pub fn get_weather(&self, time: DateTime<Tz>, time_multiplier: f32) -> Weather {
98 let attributes = self.get_weather_attributes(time);
99 let (season_data, season, season_progress) = self
100 .config
101 .location_data
102 .full_season_information(time, time_multiplier);
103
104 let raining_rain_intensity_met =
105 attributes.rain_intensity > season_data.rain_intensity_raining_threshold;
106 let raining_moisture_met = attributes.moisture > season_data.moisture_raining_threshold;
107 let raining_cloudiness_met =
108 attributes.cloudiness > season_data.cloudiness_raining_threshold;
109 let is_raining =
110 raining_cloudiness_met && raining_moisture_met && raining_rain_intensity_met;
111 let rain_strength = if is_raining {
112 (attributes.rain_intensity - season_data.rain_intensity_raining_threshold)
113 / (1.0 - season_data.rain_intensity_raining_threshold)
114 } else {
115 0.0
116 };
117
118 Weather {
119 location_name: self.config.location_data.name.clone(),
120 time,
121 season,
122 season_progress,
123 temperature_c: float_interpolate(
124 season_data.min_temp_c,
125 season_data.max_temp_c,
126 attributes.temperature,
127 ),
128 min_possible_temp_c: season_data.min_temp_c,
129 max_possible_temp_c: season_data.max_temp_c,
130 humidity: attributes.moisture,
131 light_level: attributes.light,
132 cloudiness: attributes.cloudiness,
133 cloud_brightness: attributes.cloud_brightness,
134 is_raining,
135 rain_strength,
136 }
137 }
138}