chunkgen 0.2.0

Minecraft world generation library
Documentation
use serde::Deserialize;

use crate::util::rng::RandomSource;
use crate::util::value_providers::int::IntProvider;
use crate::world::worldgen::block_predicate::BlockPredicate;
use crate::world::worldgen::feature::features::blockpos_iter::between_closed;
use crate::world::worldgen::feature::features::mark_above_for_post_processing;
use crate::world::worldgen::feature::state_provider::ProviderHolder;
use crate::world::worldgen::region::WorldGenRegion;
use shulkr::util::BlockPosition;

#[derive(Debug, Deserialize)]
pub struct DiskConfig {
    pub state_provider: ProviderHolder,
    pub target: BlockPredicate,
    pub radius: IntProvider,
    pub half_height: i32,
}

pub fn place(
    region: &WorldGenRegion<'_>,
    random: &mut RandomSource,
    origin: BlockPosition,
    config: &DiskConfig,
) -> bool {
    let mut placed_any = false;
    let top = origin.y() + config.half_height;
    let bottom = origin.y() - config.half_height - 1;
    let radius = config.radius.sample(random);

    for column in between_closed(
        origin.offset(-radius, 0, -radius),
        origin.offset(radius, 0, radius),
    ) {
        let xd = column.x() - origin.x();
        let zd = column.z() - origin.z();
        if xd * xd + zd * zd <= radius * radius {
            placed_any |= place_column(config, region, random, top, bottom, column);
        }
    }

    placed_any
}

fn place_column(
    config: &DiskConfig,
    region: &WorldGenRegion<'_>,
    random: &mut RandomSource,
    top: i32,
    bottom: i32,
    column: BlockPosition,
) -> bool {
    let mut placed_any = false;
    let mut placed_above = false;

    for y in ((bottom + 1)..=top).rev() {
        let pos = column.at_y(y);
        if config.target.test(region, pos) {
            if let Some(state) = config
                .state_provider
                .get_optional_state(region, random, pos)
            {
                region.set_block(pos, state);
                if !placed_above {
                    mark_above_for_post_processing(region, pos);
                }
                placed_any = true;
                placed_above = true;
            }
        } else {
            placed_above = false;
        }
    }

    placed_any
}