use decorator::TreeDecorator; use foliage::FoliagePlacer; use pumpkin_data::tag; use pumpkin_data::{Block, BlockState}; use pumpkin_util::{math::position::BlockPos, random::RandomGenerator}; use trunk::TrunkPlacer; use crate::generation::proto_chunk::GenerationCache; use crate::generation::{block_state_provider::BlockStateProvider, feature::size::FeatureSize}; use crate::world::BlockRegistryExt; pub mod decorator; pub mod foliage; pub mod trunk; pub struct TreeFeature { pub trunk_provider: BlockStateProvider, pub trunk_placer: TrunkPlacer, pub foliage_provider: BlockStateProvider, pub foliage_placer: FoliagePlacer, pub minimum_size: FeatureSize, pub ignore_vines: bool, pub decorators: Vec, pub below_trunk_provider: BlockStateProvider, } pub struct TreeNode { center: BlockPos, foliage_radius: i32, giant_trunk: bool, } impl TreeFeature { #[expect(clippy::too_many_arguments)] pub fn generate( &self, block_registry: &dyn BlockRegistryExt, chunk: &mut T, min_y: i8, height: u16, feature_name: &str, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> bool { // TODO let log_positions = self.generate_main( block_registry, chunk, min_y, height, feature_name, random, pos, ); for decorator in &self.decorators { decorator.generate(chunk, random, &[], &log_positions); } true } pub fn can_replace_or_log(state: &BlockState, block: u16) -> bool { Self::can_replace(state, block) || tag::Block::MINECRAFT_LOGS.1.contains(&block) } pub fn is_air_or_leaves(state: &BlockState, block: u16) -> bool { state.is_air() || tag::Block::MINECRAFT_LEAVES.1.contains(&block) } pub fn can_replace(state: &BlockState, block: u16) -> bool { state.is_air() || tag::Block::MINECRAFT_REPLACEABLE_BY_TREES .1 .contains(&block) } #[expect(clippy::too_many_arguments)] fn generate_main( &self, block_registry: &dyn BlockRegistryExt, chunk: &mut T, _min_y: i8, _height: u16, _feature_name: &str, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> Vec { let height = self.trunk_placer.get_height(random); let clipped_height = self.minimum_size.min_clipped_height; let top = self.get_top(height, chunk, pos); // TODO: roots if top < height && (clipped_height.is_none() || top < clipped_height.unwrap() as u32) { return vec![]; } let trunk_state = self.trunk_provider.get(random, pos); let (nodes, logs) = self.trunk_placer.generate( block_registry, top, pos, chunk, random, &self.below_trunk_provider, trunk_state, ); let foliage_height = self .foliage_placer .r#type .get_random_height(random, height as i32); let base_height = height as i32 - foliage_height; let foliage_radius = self.foliage_placer.get_random_radius(random, base_height); let foliage_state = self.foliage_provider.get(random, pos); for node in nodes { self.foliage_placer.generate( chunk, random, &node, foliage_height, foliage_radius, foliage_state, ); } logs } fn get_top(&self, height: u32, chunk: &T, init_pos: BlockPos) -> u32 { for y in 0..=height + 1 { let j = self.minimum_size.r#type.get_radius(height, y as i32); for x in -j..=j { for z in -j..=j { let pos = BlockPos(init_pos.0.add_raw(x, y as i32, z)); let rstate = GenerationCache::get_block_state(chunk, &pos.0); let block = rstate.to_block_id(); if Self::can_replace_or_log(rstate.to_state(), block) && (self.ignore_vines || block != Block::VINE) { continue; } return y.saturating_sub(2); } } } height } }