172 lines
5.3 KiB
Rust
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
|
|
}
|
|
}
|