use decorator::TreeDecorator; use foliage::FoliagePlacer; use pumpkin_data::tag; use pumpkin_data::{Block, BlockState}; use pumpkin_util::{math::position::BlockPos, random::RandomGenerator}; use root::RootPlacer; use trunk::TrunkPlacer; use crate::generation::proto_chunk::GenerationCache; use crate::generation::{block_state_provider::BlockStateProvider, feature::size::FeatureSize}; use crate::world::WorldPortalExt; pub mod decorator; pub mod foliage; pub mod root; 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 root_placer: Option, } 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 WorldPortalExt, chunk: &mut T, min_y: i8, height: u16, feature_name: pumpkin_data::placed_feature::PlacedFeature, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> bool { let (log_positions, root_positions, foliage_positions) = self.generate_main( block_registry, chunk, min_y, height, feature_name, random, pos, ); for decorator in &self.decorators { decorator.generate( chunk, random, &root_positions, &log_positions, &foliage_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 WorldPortalExt, chunk: &mut T, _min_y: i8, _height: u16, _feature_name: pumpkin_data::placed_feature::PlacedFeature, // This placed feature random: &mut RandomGenerator, pos: BlockPos, ) -> (Vec, Vec, Vec) { let height = self.trunk_placer.get_height(random); let trunk_start = self .root_placer .as_ref() .map_or(pos, |placer| placer.trunk_offset(pos, random)); let clipped_height = self.minimum_size.min_clipped_height; let top = self.get_top(height, chunk, trunk_start); if top < height && top < clipped_height.map_or(u32::MAX, |h| h as u32) { return (vec![], vec![], vec![]); } let root_positions = if let Some(placer) = &self.root_placer { match placer.generate(chunk, random, pos, trunk_start) { Some(positions) => positions, None => return (vec![], vec![], vec![]), } } else { Vec::new() }; let trunk_state = self.trunk_provider.get(random, pos); let (nodes, logs) = self.trunk_placer.generate( block_registry, top, trunk_start, 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); let mut foliage_positions = Vec::new(); for node in nodes { foliage_positions.extend(self.foliage_placer.generate( chunk, random, &node, foliage_height, foliage_radius, foliage_state, )); } (logs, root_positions, foliage_positions) } 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 } }