feat(chunk-gen): add RootSystem feature
This commit is contained in:
parent
ed12db09ce
commit
c9c05bb6ec
@ -632,7 +632,49 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
||||
quote! { ConfiguredFeature::Vines(crate::generation::feature::features::vines::VinesFeature) }
|
||||
}
|
||||
"minecraft:root_system" => {
|
||||
quote! { ConfiguredFeature::RootSystem(crate::generation::feature::features::root_system::RootSystemFeature {}) }
|
||||
let feature = value_to_inline_placed_feature(&config["feature"]);
|
||||
let required_vertical_space_for_tree = config["required_vertical_space_for_tree"]
|
||||
.as_i64()
|
||||
.unwrap_or(0) as i32;
|
||||
let root_radius = config["root_radius"].as_i64().unwrap_or(0) as i32;
|
||||
let root_replaceable = value_to_block_predicate(&config["root_replaceable"]);
|
||||
let root_state_provider = value_to_block_state_provider(&config["root_state_provider"]);
|
||||
let root_placement_attempts = config["root_placement_attempts"].as_i64().unwrap_or(0)
|
||||
as i32;
|
||||
let root_column_max_height = config["root_column_max_height"].as_i64().unwrap_or(0)
|
||||
as i32;
|
||||
let hanging_root_radius = config["hanging_root_radius"].as_i64().unwrap_or(0) as i32;
|
||||
let hanging_roots_vertical_span = config["hanging_roots_vertical_span"]
|
||||
.as_i64()
|
||||
.or(config["hanging_root_vertical_span"].as_i64())
|
||||
.unwrap_or(0) as i32;
|
||||
let hanging_root_state_provider =
|
||||
value_to_block_state_provider(&config["hanging_root_state_provider"]);
|
||||
let hanging_root_placement_attempts = config["hanging_root_placement_attempts"]
|
||||
.as_i64()
|
||||
.unwrap_or(0) as i32;
|
||||
let allowed_vertical_water_for_tree = config["allowed_vertical_water_for_tree"]
|
||||
.as_i64()
|
||||
.unwrap_or(0) as i32;
|
||||
let allowed_tree_position = value_to_block_predicate(&config["allowed_tree_position"]);
|
||||
|
||||
quote! {
|
||||
ConfiguredFeature::RootSystem(crate::generation::feature::features::root_system::RootSystemFeature {
|
||||
feature: Box::new(#feature),
|
||||
required_vertical_space_for_tree: #required_vertical_space_for_tree,
|
||||
root_radius: #root_radius,
|
||||
root_replaceable: #root_replaceable,
|
||||
root_state_provider: #root_state_provider,
|
||||
root_placement_attempts: #root_placement_attempts,
|
||||
root_column_max_height: #root_column_max_height,
|
||||
hanging_root_radius: #hanging_root_radius,
|
||||
hanging_roots_vertical_span: #hanging_roots_vertical_span,
|
||||
hanging_root_state_provider: #hanging_root_state_provider,
|
||||
hanging_root_placement_attempts: #hanging_root_placement_attempts,
|
||||
allowed_vertical_water_for_tree: #allowed_vertical_water_for_tree,
|
||||
allowed_tree_position: #allowed_tree_position,
|
||||
})
|
||||
}
|
||||
}
|
||||
"minecraft:multiface_growth" => {
|
||||
quote! { ConfiguredFeature::MultifaceGrowth(crate::generation::feature::features::multiface_growth::MultifaceGrowthFeature {}) }
|
||||
|
||||
@ -3595,7 +3595,60 @@ fn build_configured_features() -> std::collections::HashMap<String, ConfiguredFe
|
||||
map.insert(
|
||||
"rooted_azalea_tree".to_string(),
|
||||
ConfiguredFeature::RootSystem(
|
||||
crate::generation::feature::features::root_system::RootSystemFeature {},
|
||||
crate::generation::feature::features::root_system::RootSystemFeature {
|
||||
feature: Box::new(PlacedFeature {
|
||||
feature: Feature::Named("azalea_tree".to_string()),
|
||||
placement: vec![],
|
||||
}),
|
||||
required_vertical_space_for_tree: 3i32,
|
||||
root_radius: 3i32,
|
||||
root_replaceable: BlockPredicate::MatchingBlockTag(MatchingBlockTagPredicate {
|
||||
offset: OffsetBlocksBlockPredicate { offset: None },
|
||||
tag: pumpkin_data::tag::Block::MINECRAFT_AZALEA_ROOT_REPLACEABLE,
|
||||
}),
|
||||
root_state_provider: BlockStateProvider::Simple(SimpleStateProvider {
|
||||
state: pumpkin_data::Block::ROOTED_DIRT.default_state,
|
||||
}),
|
||||
root_placement_attempts: 20i32,
|
||||
root_column_max_height: 100i32,
|
||||
hanging_root_radius: 3i32,
|
||||
hanging_roots_vertical_span: 2i32,
|
||||
hanging_root_state_provider: BlockStateProvider::Simple(SimpleStateProvider {
|
||||
state: {
|
||||
let mut props = std::collections::HashMap::new();
|
||||
props.insert("waterlogged".to_string(), "false".to_string());
|
||||
BlockStateCodec {
|
||||
name: &pumpkin_data::Block::HANGING_ROOTS,
|
||||
properties: Some(props),
|
||||
}
|
||||
.get_state()
|
||||
},
|
||||
}),
|
||||
hanging_root_placement_attempts: 20i32,
|
||||
allowed_vertical_water_for_tree: 2i32,
|
||||
allowed_tree_position: BlockPredicate::AllOf(AllOfBlockPredicate {
|
||||
predicates: vec![
|
||||
BlockPredicate::AnyOf(AnyOfBlockPredicate {
|
||||
predicates: vec![
|
||||
BlockPredicate::MatchingBlockTag(MatchingBlockTagPredicate {
|
||||
offset: OffsetBlocksBlockPredicate { offset: None },
|
||||
tag: pumpkin_data::tag::Block::MINECRAFT_AIR,
|
||||
}),
|
||||
BlockPredicate::MatchingBlockTag(MatchingBlockTagPredicate {
|
||||
offset: OffsetBlocksBlockPredicate { offset: None },
|
||||
tag: pumpkin_data::tag::Block::MINECRAFT_REPLACEABLE_BY_TREES,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
BlockPredicate::MatchingBlockTag(MatchingBlockTagPredicate {
|
||||
offset: OffsetBlocksBlockPredicate {
|
||||
offset: Some(Vector3::new(0i32, -1i32, 0i32)),
|
||||
},
|
||||
tag: pumpkin_data::tag::Block::MINECRAFT_AZALEA_GROWS_ON,
|
||||
}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
),
|
||||
);
|
||||
map.insert(
|
||||
|
||||
@ -387,6 +387,15 @@ impl ConfiguredFeature {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::SculkPatch(feature) => feature.generate(block_registry, chunk, random, pos),
|
||||
Self::RootSystem(feature) => feature.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
),
|
||||
_ => false, // TODO
|
||||
}
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ impl LakeFeature {
|
||||
if Block::from_state_id(fluid_state.id).id == Block::WATER.id {
|
||||
for xx in 0..16 {
|
||||
for zz in 0..16 {
|
||||
let freeze_pos = origin.add(xx as i32, 4, zz as i32);
|
||||
let freeze_pos = origin.add(xx, 4, zz);
|
||||
let biome = chunk.get_biome_for_terrain_gen(
|
||||
freeze_pos.0.x,
|
||||
freeze_pos.0.y,
|
||||
|
||||
@ -1,3 +1,184 @@
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::generation::block_predicate::BlockPredicate;
|
||||
use crate::generation::block_state_provider::BlockStateProvider;
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::world::BlockRegistryExt;
|
||||
|
||||
pub struct RootSystemFeature {
|
||||
// TODO
|
||||
pub feature: Box<crate::generation::feature::placed_features::PlacedFeature>,
|
||||
pub required_vertical_space_for_tree: i32,
|
||||
pub root_radius: i32,
|
||||
pub root_replaceable: BlockPredicate,
|
||||
pub root_state_provider: BlockStateProvider,
|
||||
pub root_placement_attempts: i32,
|
||||
pub root_column_max_height: i32,
|
||||
pub hanging_root_radius: i32,
|
||||
pub hanging_roots_vertical_span: i32,
|
||||
pub hanging_root_state_provider: BlockStateProvider,
|
||||
pub hanging_root_placement_attempts: i32,
|
||||
pub allowed_vertical_water_for_tree: i32,
|
||||
pub allowed_tree_position: BlockPredicate,
|
||||
}
|
||||
|
||||
impl RootSystemFeature {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn generate<T: GenerationCache>(
|
||||
&self,
|
||||
chunk: &mut T,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
feature_name: &str,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
if !self.allowed_tree_position.test(block_registry, chunk, &pos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.can_place_tree(chunk, pos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !self.feature.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.place_roots(chunk, block_registry, random, pos);
|
||||
true
|
||||
}
|
||||
|
||||
fn can_place_tree<T: GenerationCache>(&self, chunk: &T, pos: BlockPos) -> bool {
|
||||
let mut mutable_pos = pos;
|
||||
for i in 1..=self.required_vertical_space_for_tree {
|
||||
mutable_pos = mutable_pos.add(0, 1, 0);
|
||||
if !self.is_allowed_tree_space(chunk, mutable_pos, i) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn is_allowed_tree_space<T: GenerationCache>(
|
||||
&self,
|
||||
chunk: &T,
|
||||
pos: BlockPos,
|
||||
vertical_space: i32,
|
||||
) -> bool {
|
||||
if chunk.is_air(&pos.0) {
|
||||
return true;
|
||||
}
|
||||
// TODO: check for water.
|
||||
vertical_space <= self.allowed_vertical_water_for_tree
|
||||
}
|
||||
|
||||
fn place_roots<T: GenerationCache>(
|
||||
&self,
|
||||
chunk: &mut T,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) {
|
||||
self.place_rooted_dirt(chunk, block_registry, random, pos);
|
||||
self.place_hanging_roots(chunk, block_registry, random, pos);
|
||||
}
|
||||
|
||||
fn place_rooted_dirt<T: GenerationCache>(
|
||||
&self,
|
||||
chunk: &mut T,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) {
|
||||
for _ in 0..self.root_placement_attempts {
|
||||
let mut mutable_pos = pos.add(
|
||||
random.next_bounded_i32(self.root_radius.max(1))
|
||||
- random.next_bounded_i32(self.root_radius.max(1)),
|
||||
0,
|
||||
random.next_bounded_i32(self.root_radius.max(1))
|
||||
- random.next_bounded_i32(self.root_radius.max(1)),
|
||||
);
|
||||
|
||||
if self
|
||||
.root_replaceable
|
||||
.test(block_registry, chunk, &mutable_pos)
|
||||
{
|
||||
chunk.set_block_state(
|
||||
&mutable_pos.0,
|
||||
self.root_state_provider.get_with_context(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
mutable_pos,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
for _ in 0..self.root_column_max_height {
|
||||
mutable_pos = mutable_pos.add(0, -1, 0);
|
||||
if chunk.out_of_height(mutable_pos.0.y as i16)
|
||||
|| !self
|
||||
.root_replaceable
|
||||
.test(block_registry, chunk, &mutable_pos)
|
||||
{
|
||||
break;
|
||||
}
|
||||
chunk.set_block_state(
|
||||
&mutable_pos.0,
|
||||
self.root_state_provider.get_with_context(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
mutable_pos,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn place_hanging_roots<T: GenerationCache>(
|
||||
&self,
|
||||
chunk: &mut T,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) {
|
||||
for _ in 0..self.hanging_root_placement_attempts {
|
||||
let mutable_pos = pos.add(
|
||||
random.next_bounded_i32(self.hanging_root_radius.max(1))
|
||||
- random.next_bounded_i32(self.hanging_root_radius.max(1)),
|
||||
random.next_bounded_i32(self.hanging_roots_vertical_span.max(1))
|
||||
- random.next_bounded_i32(self.hanging_roots_vertical_span.max(1)),
|
||||
random.next_bounded_i32(self.hanging_root_radius.max(1))
|
||||
- random.next_bounded_i32(self.hanging_root_radius.max(1)),
|
||||
);
|
||||
|
||||
if chunk.is_air(&mutable_pos.0) {
|
||||
let state = self.hanging_root_state_provider.get_with_context(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
mutable_pos,
|
||||
);
|
||||
// In vanilla, it checks if it can survive and if the block above is sturdy.
|
||||
// For now, let's just check if the block above is NOT air.
|
||||
let above = mutable_pos.add(0, 1, 0);
|
||||
if !chunk.is_air(&above.0) {
|
||||
chunk.set_block_state(&mutable_pos.0, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user