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::feature::features::DIRECTIONS;
use crate::world::worldgen::region::WorldGenRegion;
use shulkr::util::BlockPosition;
use shulkr::util::Direction;
use shulkr::world::block::Block;
use shulkr::world::block::BlockState;

#[derive(Debug, Deserialize)]
pub struct DeltaConfig {
    pub contents: BlockState,
    pub rim: BlockState,
    pub size: IntProvider,
    pub rim_size: IntProvider,
}

fn cannot_replace(block: Block) -> bool {
    block == Block::BEDROCK
        || block == Block::NETHER_BRICKS
        || block == Block::NETHER_BRICK_FENCE
        || block == Block::NETHER_BRICK_STAIRS
        || block == Block::NETHER_WART
        || block == Block::CHEST
        || block == Block::SPAWNER
}

fn is_clear(region: &WorldGenRegion<'_>, pos: BlockPosition, config: &DeltaConfig) -> bool {
    let state = region.get_block(pos);
    if state.as_block() == config.contents.as_block() {
        return false;
    }
    if cannot_replace(state.as_block()) {
        return false;
    }

    for direction in DIRECTIONS {
        let is_air = region.get_block(pos.relative(direction)).is_air();
        let is_up = direction == Direction::Up;
        if (is_air && !is_up) || (!is_air && is_up) {
            return false;
        }
    }

    true
}

pub fn place(
    region: &WorldGenRegion<'_>,
    random: &mut RandomSource,
    origin: BlockPosition,
    config: &DeltaConfig,
) -> bool {
    let mut any_placed = false;
    let spawn_rim = random.next_f64() < 0.9;
    let rim_x = if spawn_rim {
        config.rim_size.sample(random)
    } else {
        0
    };
    let rim_z = if spawn_rim {
        config.rim_size.sample(random)
    } else {
        0
    };
    let has_rim = spawn_rim && rim_x != 0 && rim_z != 0;
    let radius_x = config.size.sample(random);
    let radius_z = config.size.sample(random);
    let radius_limit = radius_x.max(radius_z);

    for pos in origin.within_manhattan(radius_x, 0, radius_z) {
        let dist = (pos.x() - origin.x()).abs() + (pos.z() - origin.z()).abs();
        if dist > radius_limit {
            break;
        }

        if is_clear(region, pos, config) {
            if has_rim {
                any_placed = true;
                region.set_block(pos, config.rim);
            }

            let offset_pos = pos.offset(rim_x, 0, rim_z);
            if is_clear(region, offset_pos, config) {
                any_placed = true;
                region.set_block(offset_pos, config.contents);
            }
        }
    }

    any_placed
}