use core::time::Duration;
use crate::abi::{Header, Status, WeatherFront};
use crate::world::WorldError;
use crate::{Host, Load};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum WeatherState {
Clear,
Rain,
Snow,
Blizzard,
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct WeatherAt {
pub clear: f32,
pub rain: f32,
pub snow: f32,
pub blizzard: f32,
}
impl WeatherAt {
#[must_use]
pub fn state(&self) -> WeatherState {
[
(self.clear, WeatherState::Clear),
(self.rain, WeatherState::Rain),
(self.snow, WeatherState::Snow),
(self.blizzard, WeatherState::Blizzard),
]
.into_iter()
.fold((f32::MIN, WeatherState::Clear), |best, next| {
if next.0 > best.0 { next } else { best }
})
.1
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum WeatherPhenomenonEffect {
None,
Rain,
Blizzard,
Other(u32),
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[non_exhaustive]
pub struct Front {
pub layer: u32,
pub kind: WeatherPhenomenonEffect,
pub position: [f64; 3],
pub radius: f32,
pub remaining: Duration,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Forecast {
Clear,
Rain,
Blizzard,
Any,
}
#[derive(Clone, Copy, Debug)]
pub struct Weather {
host: Host,
}
impl Weather {
#[must_use]
pub fn at(&self, position: [f64; 3]) -> Option<WeatherAt> {
let fixed = crate::ecs::fixed(position);
let mut out = [0.0f32; 4];
let status =
(self.host.api().weather_at)(self.host.handle(), &raw const fixed, &raw mut out);
(status == Status::OK).then_some(WeatherAt {
clear: out[0],
rain: out[1],
snow: out[2],
blizzard: out[3],
})
}
#[must_use]
pub fn fronts(&self) -> Vec<Front> {
let blank = WeatherFront {
header: Header::of::<WeatherFront>(),
layer: 0,
effect: 0,
position: [0; 3],
radius: 0.0,
reserved: 0,
remaining: 0,
};
let mut fronts = vec![blank; 16];
loop {
let mut count = 0u32;
let capacity = u32::try_from(fronts.len()).unwrap_or(u32::MAX);
let status = (self.host.api().weather_fronts)(
self.host.handle(),
fronts.as_mut_ptr(),
capacity,
&raw mut count,
);
if status != Status::OK {
return Vec::new();
}
let count = count as usize;
if count <= fronts.len() {
fronts.truncate(count);
break;
}
fronts = vec![blank; count];
}
fronts
.iter()
.map(|front| Front {
layer: front.layer,
kind: match front.effect {
WeatherFront::NONE => WeatherPhenomenonEffect::None,
WeatherFront::RAIN => WeatherPhenomenonEffect::Rain,
WeatherFront::BLIZZARD => WeatherPhenomenonEffect::Blizzard,
other => WeatherPhenomenonEffect::Other(other),
},
position: crate::ecs::metres(front.position),
radius: front.radius,
remaining: Duration::from_nanos(u64::try_from(front.remaining).unwrap_or(0)),
})
.collect()
}
pub fn set(&self, forecast: Forecast, lasting: Option<Duration>) -> Result<(), WorldError> {
let effect = match forecast {
Forecast::Clear => WeatherFront::NONE,
Forecast::Rain => WeatherFront::RAIN,
Forecast::Blizzard => WeatherFront::BLIZZARD,
Forecast::Any => u32::MAX,
};
let duration = lasting.map_or(0, |d| {
i64::try_from(d.as_nanos()).unwrap_or(i64::MAX).max(1)
});
WorldError::from_status((self.host.api().set_weather)(
self.host.handle(),
effect,
duration,
))
}
}
impl Load<'_> {
#[must_use]
pub fn weather(&mut self) -> Weather {
Weather { host: self.host() }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_strongest_weight_names_the_weather() {
let now = WeatherAt {
clear: 0.2,
rain: 0.1,
snow: 0.6,
blizzard: 0.1,
};
assert_eq!(now.state(), WeatherState::Snow);
}
}