chunkgen 0.2.0

Minecraft world generation library
Documentation
use std::sync::OnceLock;

use serde::Deserialize;
use shulkr::util::Key;

use crate::world::chunk::heightmap::HeightmapType;
use shulkr::util::BlockPosition;

use crate::util::rng::RandomSource;
use crate::util::shuffle;
use crate::world::worldgen::structure::pools::structure_pool_element::StructurePoolElement;
use crate::world::worldgen::structure::templatesystem::structure_processor::StructureProcessor;
use shulkr::world::block::Rotation;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
pub enum Projection {
    #[serde(rename = "terrain_matching")]
    TerrainMatching,
    #[serde(rename = "rigid")]
    Rigid,
}

impl Projection {
    pub fn processors(self) -> Vec<StructureProcessor> {
        match self {
            Self::TerrainMatching => vec![StructureProcessor::Gravity {
                heightmap: HeightmapType::WorldSurfaceWg,
                offset: -1,
            }],
            Self::Rigid => Vec::new(),
        }
    }
}

#[derive(Debug)]
pub struct StructureTemplatePool {
    templates: Vec<&'static StructurePoolElement>,
    fallback: Key,
    max_size: OnceLock<i32>,
}

impl StructureTemplatePool {
    pub fn new(fallback: Key, templates: Vec<(&'static StructurePoolElement, i32)>) -> Self {
        let mut expanded = Vec::new();
        for (element, weight) in templates {
            for _ in 0..weight {
                expanded.push(element);
            }
        }
        Self {
            templates: expanded,
            fallback,
            max_size: OnceLock::new(),
        }
    }

    pub fn get_max_size(&self) -> i32 {
        *self.max_size.get_or_init(|| {
            self.templates
                .iter()
                .filter(|element| !matches!(element, StructurePoolElement::Empty))
                .map(|element| {
                    element
                        .get_bounding_box(BlockPosition::new(0, 0, 0), Rotation::None)
                        .y_span()
                })
                .max()
                .unwrap_or(0)
        })
    }

    pub const fn fallback(&self) -> &Key {
        &self.fallback
    }

    pub fn get_random_template(&self, random: &mut RandomSource) -> &'static StructurePoolElement {
        if self.templates.is_empty() {
            return StructurePoolElement::empty();
        }
        self.templates[random.next_i32_bounded(self.templates.len() as i32) as usize]
    }

    pub fn get_shuffled_templates(
        &self,
        random: &mut RandomSource,
    ) -> Vec<&'static StructurePoolElement> {
        let mut templates = self.templates.clone();
        shuffle(&mut templates, random);
        templates
    }

    pub const fn size(&self) -> usize {
        self.templates.len()
    }
}