Pumpkin/pumpkin-world/src/generation/feature/features/tree/mod.rs
2026-05-19 20:08:58 +02:00

172 lines
5.3 KiB
Rust

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<TreeDecorator>,
pub below_trunk_provider: BlockStateProvider,
pub root_placer: Option<RootPlacer>,
}
pub struct TreeNode {
center: BlockPos,
foliage_radius: i32,
giant_trunk: bool,
}
impl TreeFeature {
#[expect(clippy::too_many_arguments)]
pub fn generate<T: GenerationCache>(
&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<T: GenerationCache>(
&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<BlockPos>, Vec<BlockPos>, Vec<BlockPos>) {
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<T: GenerationCache>(&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
}
}