chunkgen 0.2.0

Minecraft world generation library
Documentation
use std::f64::consts::PI;

use serde::Deserialize;

use crate::util::math;
use crate::util::rng::RandomSource;
use crate::world::worldgen::feature::features::tree::TreeConfig;
use crate::world::worldgen::feature::foliageplacers::FoliageAttachment;
use crate::world::worldgen::region::WorldGenRegion;
use shulkr::util::BlockPosition;
use shulkr::world::block::BlockState;

#[derive(Debug, Deserialize)]
pub struct FancyTrunkPlacer {
    pub base_height: i32,
    pub height_rand_a: i32,
    pub height_rand_b: i32,
}

impl FancyTrunkPlacer {
    #[expect(
        clippy::too_many_arguments,
        reason = "mirrors vanilla FancyTrunkPlacer.placeTrunk"
    )]
    pub fn place_trunk(
        &self,
        placer: &super::TrunkPlacer,
        region: &WorldGenRegion<'_>,
        trunk_setter: &mut dyn FnMut(BlockPosition, BlockState),
        random: &mut RandomSource,
        tree_height: i32,
        origin: BlockPosition,
        config: &TreeConfig,
    ) -> Vec<FoliageAttachment> {
        let height = tree_height + 2;
        let trunk_height = math::fast_floor(f64::from(height) * 0.618);
        super::place_below_trunk_block(region, trunk_setter, random, origin.below(), config);
        let clusters_per_y = 1.min(math::fast_floor(
            1.382 + (f64::from(height) / 13.0).powf(2.0),
        ));
        let trunk_top = origin.y() + trunk_height;
        let mut foliage_coords: Vec<(BlockPosition, i32)> =
            vec![(origin.above_n(height - 5), trunk_top)];

        for relative_y in (0..=height - 5).rev() {
            let shape = super::tree_shape(height, relative_y);
            if shape < 0.0 {
                continue;
            }
            for _ in 0..clusters_per_y {
                let radius = f64::from(shape) * (f64::from(random.next_f32()) + 0.328);
                let angle = f64::from(random.next_f32() * 2.0) * PI;
                let x = radius * angle.sin() + 0.5;
                let z = radius * angle.cos() + 0.5;
                let check_start = BlockPosition::new(
                    origin.x() + math::fast_floor(x),
                    origin.y() + relative_y - 1,
                    origin.z() + math::fast_floor(z),
                );
                let check_end = check_start.above_n(5);
                if !super::make_limb(
                    placer,
                    region,
                    trunk_setter,
                    random,
                    check_start,
                    check_end,
                    false,
                    config,
                ) {
                    continue;
                }
                let dx = origin.x() - check_start.x();
                let dz = origin.z() - check_start.z();
                let branch_height =
                    f64::from(check_start.y()) - (f64::from(dx * dx + dz * dz)).sqrt() * 0.381;
                let branch_top = if branch_height > f64::from(trunk_top) {
                    trunk_top
                } else {
                    branch_height as i32
                };
                let check_branch_base = origin.at_y(branch_top);
                if super::make_limb(
                    placer,
                    region,
                    trunk_setter,
                    random,
                    check_branch_base,
                    check_start,
                    false,
                    config,
                ) {
                    foliage_coords.push((check_start, check_branch_base.y()));
                }
            }
        }

        super::make_limb(
            placer,
            region,
            trunk_setter,
            random,
            origin,
            origin.above_n(trunk_height),
            true,
            config,
        );

        for &(pos, branch_base) in &foliage_coords {
            let base_coord = origin.at_y(branch_base);
            if base_coord != pos && super::trim_branches(height, branch_base - origin.y()) {
                super::make_limb(
                    placer,
                    region,
                    trunk_setter,
                    random,
                    base_coord,
                    pos,
                    true,
                    config,
                );
            }
        }

        foliage_coords
            .iter()
            .filter(|(_, branch_base)| super::trim_branches(height, branch_base - origin.y()))
            .map(|&(pos, _)| FoliageAttachment {
                pos,
                radius_offset: 0,
                double_trunk: false,
            })
            .collect()
    }
}