chunkgen 0.2.0

Minecraft world generation library
Documentation
use serde::{Deserialize, Deserializer};

use crate::world::worldgen::block_predicate::HolderSet;
use crate::world::worldgen::region::WorldGenRegion;
use shulkr::util::BlockPosition;
use shulkr::world::block::BlockState;

#[derive(Debug, Deserialize)]
pub struct SpringConfig {
    #[serde(deserialize_with = "fluid_as_block_state")]
    pub state: BlockState,
    #[serde(default = "requires_block_below_default")]
    pub requires_block_below: bool,
    #[serde(default = "rock_count_default")]
    pub rock_count: i32,
    #[serde(default = "hole_count_default")]
    pub hole_count: i32,
    pub valid_blocks: HolderSet,
}

const fn requires_block_below_default() -> bool {
    true
}

const fn rock_count_default() -> i32 {
    4
}

const fn hole_count_default() -> i32 {
    1
}

fn fluid_as_block_state<'de, D: Deserializer<'de>>(
    deserializer: D,
) -> Result<BlockState, D::Error> {
    let state = BlockState::deserialize(deserializer)?;
    Ok(state.as_block().default_state())
}

pub fn place(region: &WorldGenRegion<'_>, origin: BlockPosition, config: &SpringConfig) -> bool {
    if !is_valid(region, config, origin.above()) {
        return false;
    }
    if config.requires_block_below && !is_valid(region, config, origin.below()) {
        return false;
    }

    let current = region.get_block(origin);
    if !current.is_air() && !config.valid_blocks.contains_block(current) {
        return false;
    }

    let mut rock_count = 0;
    for neighbor in [
        origin.west(),
        origin.east(),
        origin.north(),
        origin.south(),
        origin.below(),
    ] {
        if is_valid(region, config, neighbor) {
            rock_count += 1;
        }
    }

    let mut hole_count = 0;
    for neighbor in [
        origin.west(),
        origin.east(),
        origin.north(),
        origin.south(),
        origin.below(),
    ] {
        if region.get_block(neighbor).is_air() {
            hole_count += 1;
        }
    }

    if rock_count == config.rock_count && hole_count == config.hole_count {
        region.set_block(origin, config.state);
        return true;
    }

    false
}

fn is_valid(region: &WorldGenRegion<'_>, config: &SpringConfig, pos: BlockPosition) -> bool {
    config.valid_blocks.contains_block(region.get_block(pos))
}