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::state_provider::ProviderHolder;
use crate::world::worldgen::region::WorldGenRegion;
use shulkr::util::BlockPosition;
use shulkr::util::Direction;

#[derive(Debug, Deserialize)]
pub struct Layer {
    pub height: IntProvider,
    #[serde(rename = "provider")]
    pub state: ProviderHolder,
}

#[derive(Debug, Deserialize)]
pub struct BlockColumnConfig {
    pub layers: Vec<Layer>,
    pub direction: Direction,
    pub allowed_placement: BlockPredicate,
    pub prioritize_tip: bool,
}

pub fn place(
    region: &WorldGenRegion<'_>,
    random: &mut RandomSource,
    origin: BlockPosition,
    config: &BlockColumnConfig,
) -> bool {
    let mut layer_heights: Vec<i32> = Vec::with_capacity(config.layers.len());
    let mut total_height = 0;

    for layer in &config.layers {
        let height = layer.height.sample(random);
        layer_heights.push(height);
        total_height += height;
    }

    if total_height == 0 {
        return false;
    }

    let mut place_pos = origin;
    let mut next_pos = place_pos.relative(config.direction);

    for y in 0..total_height {
        if !config.allowed_placement.test(region, next_pos) {
            truncate(&mut layer_heights, total_height, y, config.prioritize_tip);
            break;
        }
        next_pos = next_pos.relative(config.direction);
    }

    for (index, layer) in config.layers.iter().enumerate() {
        for _ in 0..layer_heights[index] {
            let state = layer.state.get_state(region, random, place_pos);
            region.set_block(place_pos, state);
            place_pos = place_pos.relative(config.direction);
        }
    }

    true
}

fn truncate(layer_heights: &mut [i32], total_height: i32, new_height: i32, prioritize_tip: bool) {
    let mut amount_to_remove = total_height - new_height;
    let indices: Vec<usize> = if prioritize_tip {
        (0..layer_heights.len()).collect()
    } else {
        (0..layer_heights.len()).rev().collect()
    };

    for index in indices {
        if amount_to_remove <= 0 {
            break;
        }
        let to_remove = layer_heights[index].min(amount_to_remove);
        amount_to_remove -= to_remove;
        layer_heights[index] -= to_remove;
    }
}