use std::ops::{Deref, DerefMut};
use bevy_app::prelude::*;
use bevy_ecs::prelude::*;
use serde::{Deserialize, Serialize};
use tracing::error;
use valence_ident::{ident, Ident};
use valence_nbt::serde::CompoundSerializer;
use crate::codec::{RegistryCodec, RegistryValue};
use crate::{Registry, RegistryIdx, RegistrySet};
pub struct BiomePlugin;
impl Plugin for BiomePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<BiomeRegistry>()
.add_systems(PreStartup, load_default_biomes)
.add_systems(PostUpdate, update_biome_registry.before(RegistrySet));
}
}
fn load_default_biomes(mut reg: ResMut<BiomeRegistry>, codec: Res<RegistryCodec>) {
let mut helper = move || -> anyhow::Result<()> {
for value in codec.registry(BiomeRegistry::KEY) {
let biome = Biome::deserialize(value.element.clone())?;
reg.insert(value.name.clone(), biome);
}
reg.swap_to_front(ident!("plains"));
Ok(())
};
if let Err(e) = helper() {
error!("failed to load default biomes from registry codec: {e:#}");
}
}
fn update_biome_registry(reg: Res<BiomeRegistry>, mut codec: ResMut<RegistryCodec>) {
if reg.is_changed() {
let biomes = codec.registry_mut(BiomeRegistry::KEY);
biomes.clear();
biomes.extend(reg.iter().map(|(_, name, biome)| {
RegistryValue {
name: name.into(),
element: biome
.serialize(CompoundSerializer)
.expect("failed to serialize biome"),
}
}));
}
}
#[derive(Resource, Default, Debug)]
pub struct BiomeRegistry {
reg: Registry<BiomeId, Biome>,
}
impl BiomeRegistry {
pub const KEY: Ident<&'static str> = ident!("worldgen/biome");
}
impl Deref for BiomeRegistry {
type Target = Registry<BiomeId, Biome>;
fn deref(&self) -> &Self::Target {
&self.reg
}
}
impl DerefMut for BiomeRegistry {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.reg
}
}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Debug)]
pub struct BiomeId(u32);
impl BiomeId {
pub const DEFAULT: Self = BiomeId(0);
}
impl RegistryIdx for BiomeId {
const MAX: usize = u32::MAX as usize;
#[inline]
fn to_index(self) -> usize {
self.0 as usize
}
#[inline]
fn from_index(idx: usize) -> Self {
Self(idx as u32)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Biome {
pub downfall: f32,
pub effects: BiomeEffects,
pub has_precipitation: bool,
pub temperature: f32,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct BiomeEffects {
pub fog_color: u32,
pub sky_color: u32,
pub water_color: u32,
pub water_fog_color: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub grass_color: Option<u32>,
}
impl Default for Biome {
fn default() -> Self {
Self {
downfall: 0.4,
effects: BiomeEffects::default(),
has_precipitation: true,
temperature: 0.8,
}
}
}
impl Default for BiomeEffects {
fn default() -> Self {
Self {
fog_color: 12638463,
sky_color: 7907327,
water_color: 4159204,
water_fog_color: 329011,
grass_color: None,
}
}
}