feat: implement remaining tree types
This commit is contained in:
parent
d8a124cf92
commit
32c5530d6c
@ -965,6 +965,30 @@ fn value_to_tree_feature(config: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn value_to_block_list(v: &Value) -> TokenStream {
|
||||||
|
if let Some(s) = v.as_str() {
|
||||||
|
if let Some(tag) = s.strip_prefix('#') {
|
||||||
|
let name = format!(
|
||||||
|
"MINECRAFT_{}",
|
||||||
|
tag.strip_prefix("minecraft:").unwrap_or(tag).to_uppercase()
|
||||||
|
);
|
||||||
|
let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
|
||||||
|
return quote! { &pumpkin_data::tag::Block::#ident.1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut blocks = Vec::new();
|
||||||
|
if let Some(arr) = v.as_array() {
|
||||||
|
for b in arr {
|
||||||
|
if let Some(s) = b.as_str() {
|
||||||
|
let name = s.strip_prefix("minecraft:").unwrap_or(s).to_uppercase();
|
||||||
|
let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
|
||||||
|
blocks.push(quote! { pumpkin_data::Block::#ident.id });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
quote! { &[#(#blocks),*] }
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a trunk-placer JSON object into a `TrunkPlacer` token stream.
|
/// Converts a trunk-placer JSON object into a `TrunkPlacer` token stream.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
@ -994,10 +1018,35 @@ fn value_to_trunk_placer(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"minecraft:upwards_branching_trunk_placer" => {
|
"minecraft:upwards_branching_trunk_placer" => {
|
||||||
quote! { TrunkType::UpwardsBranching(UpwardsBranchingTrunkPlacer {}) }
|
let extra_branch_steps = value_to_int_provider(&v["extra_branch_steps"]);
|
||||||
|
let place_branch_per_log_probability =
|
||||||
|
v["place_branch_per_log_probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
let extra_branch_length = value_to_int_provider(&v["extra_branch_length"]);
|
||||||
|
let can_grow_through = value_to_block_list(&v["can_grow_through"]);
|
||||||
|
quote! {
|
||||||
|
TrunkType::UpwardsBranching(UpwardsBranchingTrunkPlacer {
|
||||||
|
extra_branch_steps: #extra_branch_steps,
|
||||||
|
place_branch_per_log_probability: #place_branch_per_log_probability,
|
||||||
|
extra_branch_length: #extra_branch_length,
|
||||||
|
can_grow_through: #can_grow_through,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:cherry_trunk_placer" => {
|
"minecraft:cherry_trunk_placer" => {
|
||||||
quote! { TrunkType::Cherry(CherryTrunkPlacer {}) }
|
let branch_count = value_to_int_provider(&v["branch_count"]);
|
||||||
|
let branch_horizontal_length = value_to_int_provider(&v["branch_horizontal_length"]);
|
||||||
|
let branch_start_offset_v = &v["branch_start_offset_from_top"];
|
||||||
|
let min = branch_start_offset_v["min_inclusive"].as_i64().unwrap_or(0) as i32;
|
||||||
|
let max = branch_start_offset_v["max_inclusive"].as_i64().unwrap_or(0) as i32;
|
||||||
|
let branch_end_offset_from_top = value_to_int_provider(&v["branch_end_offset_from_top"]);
|
||||||
|
quote! {
|
||||||
|
TrunkType::Cherry(CherryTrunkPlacer {
|
||||||
|
branch_count: #branch_count,
|
||||||
|
branch_horizontal_length: #branch_horizontal_length,
|
||||||
|
branch_start_offset_from_top: UniformIntProvider { min_inclusive: #min, max_inclusive: #max },
|
||||||
|
branch_end_offset_from_top: #branch_end_offset_from_top,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => quote! { TrunkType::Straight(StraightTrunkPlacer) },
|
_ => quote! { TrunkType::Straight(StraightTrunkPlacer) },
|
||||||
};
|
};
|
||||||
|
|||||||
@ -733,8 +733,8 @@ fn build_configured_features() -> std::collections::HashMap<String, ConfiguredFe
|
|||||||
prioritize_tip: true,
|
prioritize_tip: true,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
map . insert ("cherry" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 7u8 , height_rand_a : 1u8 , height_rand_b : 0u8 , r#type : TrunkType :: Cherry (CherryTrunkPlacer { }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (4i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: Cherry (CherryFoliagePlacer { height : IntProvider :: Constant (5i32) , wide_bottom_layer_hole_chance : 0.25f32 , corner_hole_chance : 0.25f32 , hanging_leaves_chance : 0.16666667f32 , hanging_leaves_extension_chance : 0.33333334f32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 1u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [] , }))) ;
|
map . insert ("cherry" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 7u8 , height_rand_a : 1u8 , height_rand_b : 0u8 , r#type : TrunkType :: Cherry (CherryTrunkPlacer { branch_count : IntProvider :: Object (NormalIntProvider :: WeightedList (WeightedListIntProvider { distribution : vec ! [WeightedEntry { data : IntProvider :: Constant (1i32) , weight : 1i32 } , WeightedEntry { data : IntProvider :: Constant (2i32) , weight : 1i32 } , WeightedEntry { data : IntProvider :: Constant (3i32) , weight : 1i32 }] })) , branch_horizontal_length : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : 2i32 , max_inclusive : 4i32 })) , branch_start_offset_from_top : UniformIntProvider { min_inclusive : - 4i32 , max_inclusive : - 3i32 } , branch_end_offset_from_top : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : - 1i32 , max_inclusive : 0i32 })) , }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (4i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: Cherry (CherryFoliagePlacer { height : IntProvider :: Constant (5i32) , wide_bottom_layer_hole_chance : 0.25f32 , corner_hole_chance : 0.25f32 , hanging_leaves_chance : 0.16666667f32 , hanging_leaves_extension_chance : 0.33333334f32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 1u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [] , }))) ;
|
||||||
map . insert ("cherry_bees_005" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 7u8 , height_rand_a : 1u8 , height_rand_b : 0u8 , r#type : TrunkType :: Cherry (CherryTrunkPlacer { }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (4i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: Cherry (CherryFoliagePlacer { height : IntProvider :: Constant (5i32) , wide_bottom_layer_hole_chance : 0.25f32 , corner_hole_chance : 0.25f32 , hanging_leaves_chance : 0.16666667f32 , hanging_leaves_extension_chance : 0.33333334f32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 1u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [TreeDecorator :: Beehive (BeehiveTreeDecorator { probability : 0.05f32 })] , }))) ;
|
map . insert ("cherry_bees_005" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 7u8 , height_rand_a : 1u8 , height_rand_b : 0u8 , r#type : TrunkType :: Cherry (CherryTrunkPlacer { branch_count : IntProvider :: Object (NormalIntProvider :: WeightedList (WeightedListIntProvider { distribution : vec ! [WeightedEntry { data : IntProvider :: Constant (1i32) , weight : 1i32 } , WeightedEntry { data : IntProvider :: Constant (2i32) , weight : 1i32 } , WeightedEntry { data : IntProvider :: Constant (3i32) , weight : 1i32 }] })) , branch_horizontal_length : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : 2i32 , max_inclusive : 4i32 })) , branch_start_offset_from_top : UniformIntProvider { min_inclusive : - 4i32 , max_inclusive : - 3i32 } , branch_end_offset_from_top : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : - 1i32 , max_inclusive : 0i32 })) , }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: CHERRY_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (4i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: Cherry (CherryFoliagePlacer { height : IntProvider :: Constant (5i32) , wide_bottom_layer_hole_chance : 0.25f32 , corner_hole_chance : 0.25f32 , hanging_leaves_chance : 0.16666667f32 , hanging_leaves_extension_chance : 0.33333334f32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 1u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [TreeDecorator :: Beehive (BeehiveTreeDecorator { probability : 0.05f32 })] , }))) ;
|
||||||
map.insert(
|
map.insert(
|
||||||
"chorus_plant".to_string(),
|
"chorus_plant".to_string(),
|
||||||
ConfiguredFeature::ChorusPlant(
|
ConfiguredFeature::ChorusPlant(
|
||||||
@ -2520,7 +2520,7 @@ fn build_configured_features() -> std::collections::HashMap<String, ConfiguredFe
|
|||||||
})),
|
})),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
map . insert ("mangrove" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 2u8 , height_rand_a : 1u8 , height_rand_b : 4u8 , r#type : TrunkType :: UpwardsBranching (UpwardsBranchingTrunkPlacer { }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (3i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: RandomSpread (RandomSpreadFoliagePlacer { foliage_height : IntProvider :: Constant (2i32) , leaf_placement_attempts : 70i32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 2u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [TreeDecorator :: LeaveVine (LeavesVineTreeDecorator { }) , TreeDecorator :: AttachedToLeaves (AttachedToLeavesTreeDecorator { }) , TreeDecorator :: Beehive (BeehiveTreeDecorator { probability : 0.01f32 })] , }))) ;
|
map . insert ("mangrove" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 2u8 , height_rand_a : 1u8 , height_rand_b : 4u8 , r#type : TrunkType :: UpwardsBranching (UpwardsBranchingTrunkPlacer { extra_branch_steps : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : 1i32 , max_inclusive : 4i32 })) , place_branch_per_log_probability : 0.5f32 , extra_branch_length : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : 0i32 , max_inclusive : 1i32 })) , can_grow_through : & pumpkin_data :: tag :: Block :: MINECRAFT_MANGROVE_LOGS_CAN_GROW_THROUGH . 1 , }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (3i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: RandomSpread (RandomSpreadFoliagePlacer { foliage_height : IntProvider :: Constant (2i32) , leaf_placement_attempts : 70i32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 2u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [TreeDecorator :: LeaveVine (LeavesVineTreeDecorator { }) , TreeDecorator :: AttachedToLeaves (AttachedToLeavesTreeDecorator { }) , TreeDecorator :: Beehive (BeehiveTreeDecorator { probability : 0.01f32 })] , }))) ;
|
||||||
map.insert(
|
map.insert(
|
||||||
"mangrove_vegetation".to_string(),
|
"mangrove_vegetation".to_string(),
|
||||||
ConfiguredFeature::RandomSelector(RandomFeature {
|
ConfiguredFeature::RandomSelector(RandomFeature {
|
||||||
@ -3873,7 +3873,7 @@ fn build_configured_features() -> std::collections::HashMap<String, ConfiguredFe
|
|||||||
schedule_tick: None,
|
schedule_tick: None,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
map . insert ("tall_mangrove" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 4u8 , height_rand_a : 1u8 , height_rand_b : 9u8 , r#type : TrunkType :: UpwardsBranching (UpwardsBranchingTrunkPlacer { }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (3i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: RandomSpread (RandomSpreadFoliagePlacer { foliage_height : IntProvider :: Constant (2i32) , leaf_placement_attempts : 70i32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 3u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [TreeDecorator :: LeaveVine (LeavesVineTreeDecorator { }) , TreeDecorator :: AttachedToLeaves (AttachedToLeavesTreeDecorator { }) , TreeDecorator :: Beehive (BeehiveTreeDecorator { probability : 0.01f32 })] , }))) ;
|
map . insert ("tall_mangrove" . to_string () , ConfiguredFeature :: Tree (Box :: new (TreeFeature { trunk_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("axis" . to_string () , "y" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LOG , properties : Some (props) , } . get_state () } }) , trunk_placer : TrunkPlacer { base_height : 4u8 , height_rand_a : 1u8 , height_rand_b : 9u8 , r#type : TrunkType :: UpwardsBranching (UpwardsBranchingTrunkPlacer { extra_branch_steps : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : 1i32 , max_inclusive : 6i32 })) , place_branch_per_log_probability : 0.5f32 , extra_branch_length : IntProvider :: Object (NormalIntProvider :: Uniform (UniformIntProvider { min_inclusive : 0i32 , max_inclusive : 1i32 })) , can_grow_through : & pumpkin_data :: tag :: Block :: MINECRAFT_MANGROVE_LOGS_CAN_GROW_THROUGH . 1 , }) , } , foliage_provider : BlockStateProvider :: Simple (SimpleStateProvider { state : { let mut props = std :: collections :: HashMap :: new () ; props . insert ("distance" . to_string () , "7" . to_string ()) ; props . insert ("persistent" . to_string () , "false" . to_string ()) ; props . insert ("waterlogged" . to_string () , "false" . to_string ()) ; BlockStateCodec { name : & pumpkin_data :: Block :: MANGROVE_LEAVES , properties : Some (props) , } . get_state () } }) , foliage_placer : FoliagePlacer { radius : IntProvider :: Constant (3i32) , offset : IntProvider :: Constant (0i32) , r#type : FoliageType :: RandomSpread (RandomSpreadFoliagePlacer { foliage_height : IntProvider :: Constant (2i32) , leaf_placement_attempts : 70i32 , }) } , minimum_size : FeatureSize { min_clipped_height : None , r#type : FeatureSizeType :: TwoLayersFeatureSize (TwoLayersFeatureSize { limit : 3u8 , lower_size : 0u8 , upper_size : 2u8 , }) } , ignore_vines : true , below_trunk_provider : BlockStateProvider :: Rule (RuleBasedBlockStateProvider { fallback : None , rules : vec ! [BlockStateRule { if_true : BlockPredicate :: Not (NotBlockPredicate { predicate : Box :: new (BlockPredicate :: MatchingBlockTag (MatchingBlockTagPredicate { offset : OffsetBlocksBlockPredicate { offset : None } , tag : pumpkin_data :: tag :: Block :: MINECRAFT_CANNOT_REPLACE_BELOW_TREE_TRUNK , })) , }) , then : BlockStateProvider :: Simple (SimpleStateProvider { state : pumpkin_data :: Block :: DIRT . default_state }) }] , }) , decorators : vec ! [TreeDecorator :: LeaveVine (LeavesVineTreeDecorator { }) , TreeDecorator :: AttachedToLeaves (AttachedToLeavesTreeDecorator { }) , TreeDecorator :: Beehive (BeehiveTreeDecorator { probability : 0.01f32 })] , }))) ;
|
||||||
map.insert(
|
map.insert(
|
||||||
"trees_badlands".to_string(),
|
"trees_badlands".to_string(),
|
||||||
ConfiguredFeature::RandomSelector(RandomFeature {
|
ConfiguredFeature::RandomSelector(RandomFeature {
|
||||||
|
|||||||
@ -24,18 +24,18 @@ impl CherryFoliagePlacer {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
node: &TreeNode,
|
node: &TreeNode,
|
||||||
foliage_height: i32,
|
foliage_height: i32,
|
||||||
radius: i32,
|
leaf_radius: i32,
|
||||||
offset: i32,
|
offset: i32,
|
||||||
foliage_provider: &BlockState,
|
foliage_provider: &BlockState,
|
||||||
) {
|
) {
|
||||||
let pos = node.center.up_height(offset);
|
let pos = node.center.up_height(offset);
|
||||||
let radius = radius + node.foliage_radius - 1;
|
let current_radius = leaf_radius + node.foliage_radius - 1;
|
||||||
FoliagePlacer::generate_square(
|
FoliagePlacer::generate_square(
|
||||||
self,
|
self,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
pos,
|
pos,
|
||||||
radius - 2,
|
current_radius - 2,
|
||||||
foliage_height - 3,
|
foliage_height - 3,
|
||||||
node.giant_trunk,
|
node.giant_trunk,
|
||||||
foliage_provider,
|
foliage_provider,
|
||||||
@ -45,44 +45,48 @@ impl CherryFoliagePlacer {
|
|||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
pos,
|
pos,
|
||||||
radius - 1,
|
current_radius - 1,
|
||||||
foliage_height - 4,
|
foliage_height - 4,
|
||||||
node.giant_trunk,
|
node.giant_trunk,
|
||||||
foliage_provider,
|
foliage_provider,
|
||||||
);
|
);
|
||||||
for y in foliage_height - 5..0 {
|
for y in (0..=foliage_height - 5).rev() {
|
||||||
FoliagePlacer::generate_square(
|
FoliagePlacer::generate_square(
|
||||||
self,
|
self,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
pos,
|
pos,
|
||||||
radius,
|
current_radius,
|
||||||
y,
|
y,
|
||||||
node.giant_trunk,
|
node.giant_trunk,
|
||||||
foliage_provider,
|
foliage_provider,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// TODO: generateSquareWithHangingLeaves
|
|
||||||
FoliagePlacer::generate_square(
|
FoliagePlacer::generate_square_with_hanging_leaves(
|
||||||
self,
|
self,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
pos,
|
pos,
|
||||||
radius,
|
current_radius,
|
||||||
-1,
|
-1,
|
||||||
node.giant_trunk,
|
node.giant_trunk,
|
||||||
foliage_provider,
|
foliage_provider,
|
||||||
|
self.hanging_leaves_chance,
|
||||||
|
self.hanging_leaves_extension_chance,
|
||||||
);
|
);
|
||||||
// TODO: generateSquareWithHangingLeaves
|
|
||||||
FoliagePlacer::generate_square(
|
FoliagePlacer::generate_square_with_hanging_leaves(
|
||||||
self,
|
self,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
pos,
|
pos,
|
||||||
radius - 1,
|
current_radius - 1,
|
||||||
-2,
|
-2,
|
||||||
node.giant_trunk,
|
node.giant_trunk,
|
||||||
foliage_provider,
|
foliage_provider,
|
||||||
|
self.hanging_leaves_chance,
|
||||||
|
self.hanging_leaves_extension_chance,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
pub fn get_random_height(&self, random: &mut RandomGenerator) -> i32 {
|
pub fn get_random_height(&self, random: &mut RandomGenerator) -> i32 {
|
||||||
@ -97,20 +101,24 @@ impl LeaveValidator for CherryFoliagePlacer {
|
|||||||
dx: i32,
|
dx: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
dz: i32,
|
dz: i32,
|
||||||
radius: i32,
|
current_radius: i32,
|
||||||
_giant_trunk: bool,
|
_giant_trunk: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if y == -1
|
if y == -1
|
||||||
&& (dx == radius || dz == radius)
|
&& (dx == current_radius || dz == current_radius)
|
||||||
&& random.next_f32() < self.wide_bottom_layer_hole_chance
|
&& random.next_f32() < self.wide_bottom_layer_hole_chance
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let in_radius = dx == radius && dz == radius;
|
|
||||||
if radius > 2 {
|
let corner = dx == current_radius && dz == current_radius;
|
||||||
return in_radius
|
let wide_layer = current_radius > 2;
|
||||||
|| dx + dz > radius * 2 - 2 && random.next_f32() < self.corner_hole_chance;
|
|
||||||
|
if wide_layer {
|
||||||
|
corner
|
||||||
|
|| dx + dz > current_radius * 2 - 2 && random.next_f32() < self.corner_hole_chance
|
||||||
|
} else {
|
||||||
|
corner && random.next_f32() < self.corner_hole_chance
|
||||||
}
|
}
|
||||||
in_radius && random.next_f32() < self.corner_hole_chance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,10 +7,11 @@ use fancy::LargeOakFoliagePlacer;
|
|||||||
use jungle::JungleFoliagePlacer;
|
use jungle::JungleFoliagePlacer;
|
||||||
use mega_pine::MegaPineFoliagePlacer;
|
use mega_pine::MegaPineFoliagePlacer;
|
||||||
use pine::PineFoliagePlacer;
|
use pine::PineFoliagePlacer;
|
||||||
|
use pumpkin_data::BlockDirection;
|
||||||
use pumpkin_data::BlockState;
|
use pumpkin_data::BlockState;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{int_provider::IntProvider, position::BlockPos, vector3::Vector3},
|
math::{int_provider::IntProvider, position::BlockPos, vector3::Vector3},
|
||||||
random::RandomGenerator,
|
random::{RandomGenerator, RandomImpl},
|
||||||
};
|
};
|
||||||
use random_spread::RandomSpreadFoliagePlacer;
|
use random_spread::RandomSpreadFoliagePlacer;
|
||||||
|
|
||||||
@ -128,12 +129,110 @@ impl FoliagePlacer {
|
|||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
block_state: &BlockState,
|
block_state: &BlockState,
|
||||||
) {
|
) -> bool {
|
||||||
let block = GenerationCache::get_block_state(chunk, &pos.0);
|
let block = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
if !TreeFeature::can_replace(block.to_state(), block.to_block_id()) {
|
if !TreeFeature::can_replace(block.to_state(), block.to_block_id()) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
chunk.set_block_state(&pos.0, block_state);
|
chunk.set_block_state(&pos.0, block_state);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_set<T: GenerationCache>(
|
||||||
|
chunk: &T,
|
||||||
|
pos: BlockPos,
|
||||||
|
foliage_provider: &BlockState,
|
||||||
|
) -> bool {
|
||||||
|
GenerationCache::get_block_state(chunk, &pos.0).0 == foliage_provider.id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_place_extension<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
chance: f32,
|
||||||
|
log_pos: BlockPos,
|
||||||
|
pos: BlockPos,
|
||||||
|
foliage_provider: &BlockState,
|
||||||
|
) -> bool {
|
||||||
|
if pos.manhattan_distance(log_pos) >= 7 || random.next_f32() > chance {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
Self::place_foliage_block(chunk, pos, foliage_provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn generate_square_with_hanging_leaves<T: LeaveValidator, T2: GenerationCache>(
|
||||||
|
validator: &T,
|
||||||
|
chunk: &mut T2,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
center_pos: BlockPos,
|
||||||
|
radius: i32,
|
||||||
|
y: i32,
|
||||||
|
giant_trunk: bool,
|
||||||
|
foliage_provider: &BlockState,
|
||||||
|
hanging_leaves_chance: f32,
|
||||||
|
hanging_leaves_extension_chance: f32,
|
||||||
|
) {
|
||||||
|
Self::generate_square(
|
||||||
|
validator,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
center_pos,
|
||||||
|
radius,
|
||||||
|
y,
|
||||||
|
giant_trunk,
|
||||||
|
foliage_provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
let i = i32::from(giant_trunk);
|
||||||
|
let log_pos = center_pos.down();
|
||||||
|
|
||||||
|
let directions = [
|
||||||
|
BlockDirection::North,
|
||||||
|
BlockDirection::South,
|
||||||
|
BlockDirection::East,
|
||||||
|
BlockDirection::West,
|
||||||
|
];
|
||||||
|
|
||||||
|
for along_edge in directions {
|
||||||
|
let to_edge = along_edge.rotate_clockwise();
|
||||||
|
|
||||||
|
let offset_to_edge = if to_edge.positive() {
|
||||||
|
radius + i
|
||||||
|
} else {
|
||||||
|
radius
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut pos = center_pos
|
||||||
|
.add(0, y - 1, 0)
|
||||||
|
.offset_dir(to_edge.to_offset(), offset_to_edge)
|
||||||
|
.offset_dir(along_edge.to_offset(), -radius);
|
||||||
|
|
||||||
|
for _ in -radius..(radius + i) {
|
||||||
|
let leaves_above = Self::is_set(chunk, pos.up(), foliage_provider);
|
||||||
|
if leaves_above
|
||||||
|
&& Self::try_place_extension(
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
hanging_leaves_chance,
|
||||||
|
log_pos,
|
||||||
|
pos,
|
||||||
|
foliage_provider,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Self::try_place_extension(
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
hanging_leaves_extension_chance,
|
||||||
|
log_pos,
|
||||||
|
pos.down(),
|
||||||
|
foliage_provider,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pos = pos.offset_dir(along_edge.to_offset(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -31,48 +31,57 @@ impl BendingTrunkPlacer {
|
|||||||
below_trunk_provider: &BlockStateProvider,
|
below_trunk_provider: &BlockStateProvider,
|
||||||
trunk_block: &BlockState,
|
trunk_block: &BlockState,
|
||||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||||
|
let horizontal_directions = BlockDirection::horizontal();
|
||||||
|
let direction = horizontal_directions[random.next_bounded_i32(4) as usize];
|
||||||
|
let log_height = height as i32 - 1;
|
||||||
|
let mut pos = start_pos;
|
||||||
|
|
||||||
placer.set_dirt(
|
placer.set_dirt(
|
||||||
block_registry,
|
block_registry,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
&start_pos.down(),
|
&pos.down(),
|
||||||
below_trunk_provider,
|
below_trunk_provider,
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: make this random
|
|
||||||
let random_direction = BlockDirection::North;
|
|
||||||
let height = height - 1;
|
|
||||||
let mut pos = start_pos;
|
|
||||||
let mut trunk_poses = Vec::new();
|
let mut trunk_poses = Vec::new();
|
||||||
let mut nodes = Vec::new();
|
let mut nodes = Vec::new();
|
||||||
for y in 0..height {
|
|
||||||
if y + 1 >= height + random.next_bounded_i32(2) as u32 {
|
for i in 0..=log_height {
|
||||||
pos = pos.offset(random_direction.to_offset());
|
if i + 1 >= log_height + random.next_bounded_i32(2) {
|
||||||
|
pos = pos.offset(direction.to_offset());
|
||||||
}
|
}
|
||||||
|
|
||||||
if placer.place(chunk, &pos, trunk_block) {
|
if placer.place(chunk, &pos, trunk_block) {
|
||||||
trunk_poses.push(pos);
|
trunk_poses.push(pos);
|
||||||
}
|
}
|
||||||
if y >= 1 {
|
|
||||||
|
if i >= self.min_height_for_leaves as i32 {
|
||||||
nodes.push(TreeNode {
|
nodes.push(TreeNode {
|
||||||
center: pos,
|
center: pos,
|
||||||
foliage_radius: 0,
|
foliage_radius: 0,
|
||||||
giant_trunk: false,
|
giant_trunk: false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = pos.up();
|
pos = pos.up();
|
||||||
}
|
}
|
||||||
let bend = self.bend_length.get(random);
|
|
||||||
for _ in 0..bend {
|
let dir_length = self.bend_length.get(random);
|
||||||
|
|
||||||
|
for _ in 0..=dir_length {
|
||||||
if placer.place(chunk, &pos, trunk_block) {
|
if placer.place(chunk, &pos, trunk_block) {
|
||||||
trunk_poses.push(pos);
|
trunk_poses.push(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
nodes.push(TreeNode {
|
nodes.push(TreeNode {
|
||||||
center: pos,
|
center: pos,
|
||||||
foliage_radius: 0,
|
foliage_radius: 0,
|
||||||
giant_trunk: false,
|
giant_trunk: false,
|
||||||
});
|
});
|
||||||
pos = pos.offset(random_direction.to_offset());
|
pos = pos.offset(direction.to_offset());
|
||||||
}
|
}
|
||||||
|
|
||||||
(nodes, trunk_poses)
|
(nodes, trunk_poses)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,232 @@
|
|||||||
pub struct CherryTrunkPlacer {}
|
use pumpkin_data::{Block, BlockState};
|
||||||
|
use pumpkin_util::math::int_provider::{IntProvider, UniformIntProvider};
|
||||||
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||||
|
|
||||||
|
use super::TrunkPlacer;
|
||||||
|
use crate::generation::block_state_provider::BlockStateProvider;
|
||||||
|
use crate::generation::feature::features::tree::TreeNode;
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
use crate::world::BlockRegistryExt;
|
||||||
|
use pumpkin_data::block_properties::Axis;
|
||||||
|
|
||||||
|
pub struct CherryTrunkPlacer {
|
||||||
|
pub branch_count: IntProvider,
|
||||||
|
pub branch_horizontal_length: IntProvider,
|
||||||
|
pub branch_start_offset_from_top: UniformIntProvider,
|
||||||
|
pub branch_end_offset_from_top: IntProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CherryTrunkPlacer {
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
block_registry: &dyn BlockRegistryExt,
|
||||||
|
placer: &TrunkPlacer,
|
||||||
|
height: u32,
|
||||||
|
start_pos: BlockPos,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
below_trunk_provider: &BlockStateProvider,
|
||||||
|
trunk_state: &BlockState,
|
||||||
|
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||||
|
placer.set_dirt(
|
||||||
|
block_registry,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
&start_pos.down(),
|
||||||
|
below_trunk_provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut logs = Vec::new();
|
||||||
|
|
||||||
|
let first_branch_offset =
|
||||||
|
(height as i32 - 1 + self.branch_start_offset_from_top.get(random)).max(0);
|
||||||
|
let second_branch_start_offset_from_top = UniformIntProvider::new(
|
||||||
|
self.branch_start_offset_from_top.min_inclusive,
|
||||||
|
self.branch_start_offset_from_top.max_inclusive - 1,
|
||||||
|
);
|
||||||
|
let mut second_branch_offset =
|
||||||
|
(height as i32 - 1 + second_branch_start_offset_from_top.get(random)).max(0);
|
||||||
|
|
||||||
|
if second_branch_offset >= first_branch_offset {
|
||||||
|
second_branch_offset += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let branch_count = self.branch_count.get(random);
|
||||||
|
let has_middle_branch = branch_count == 3;
|
||||||
|
let has_both_side_branches = branch_count >= 2;
|
||||||
|
|
||||||
|
let trunk_height = if has_middle_branch {
|
||||||
|
height
|
||||||
|
} else if has_both_side_branches {
|
||||||
|
(first_branch_offset.max(second_branch_offset) + 1) as u32
|
||||||
|
} else {
|
||||||
|
(first_branch_offset + 1) as u32
|
||||||
|
};
|
||||||
|
|
||||||
|
for y in 0..trunk_height {
|
||||||
|
let pos = start_pos.up_height(y as i32);
|
||||||
|
if placer.place(chunk, &pos, trunk_state) {
|
||||||
|
logs.push(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
if has_middle_branch {
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: start_pos.up_height(trunk_height as i32),
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let directions = [
|
||||||
|
BlockPos::new(0, 0, -1), // North
|
||||||
|
BlockPos::new(0, 0, 1), // South
|
||||||
|
BlockPos::new(-1, 0, 0), // West
|
||||||
|
BlockPos::new(1, 0, 0), // East
|
||||||
|
];
|
||||||
|
let branch_direction = directions[random.next_bounded_i32(4) as usize];
|
||||||
|
|
||||||
|
let (node1, logs1) = self.generate_branch(
|
||||||
|
placer,
|
||||||
|
height,
|
||||||
|
start_pos,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
trunk_state,
|
||||||
|
branch_direction,
|
||||||
|
first_branch_offset,
|
||||||
|
first_branch_offset < trunk_height as i32 - 1,
|
||||||
|
);
|
||||||
|
nodes.push(node1);
|
||||||
|
logs.extend(logs1);
|
||||||
|
|
||||||
|
if has_both_side_branches {
|
||||||
|
let opposite_direction = BlockPos::new(-branch_direction.0.x, 0, -branch_direction.0.z);
|
||||||
|
let (node2, logs2) = self.generate_branch(
|
||||||
|
placer,
|
||||||
|
height,
|
||||||
|
start_pos,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
trunk_state,
|
||||||
|
opposite_direction,
|
||||||
|
second_branch_offset,
|
||||||
|
second_branch_offset < trunk_height as i32 - 1,
|
||||||
|
);
|
||||||
|
nodes.push(node2);
|
||||||
|
logs.extend(logs2);
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes, logs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
fn generate_branch<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
placer: &TrunkPlacer,
|
||||||
|
tree_height: u32,
|
||||||
|
origin: BlockPos,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
trunk_state: &BlockState,
|
||||||
|
branch_direction: BlockPos,
|
||||||
|
offset_from_origin: i32,
|
||||||
|
middle_continues_upwards: bool,
|
||||||
|
) -> (TreeNode, Vec<BlockPos>) {
|
||||||
|
let mut logs = Vec::new();
|
||||||
|
let mut log_pos = origin.up_height(offset_from_origin);
|
||||||
|
|
||||||
|
let branch_end_pos_offset =
|
||||||
|
tree_height as i32 - 1 + self.branch_end_offset_from_top.get(random);
|
||||||
|
let extend_branch_away_from_trunk =
|
||||||
|
middle_continues_upwards || branch_end_pos_offset < offset_from_origin;
|
||||||
|
let distance_to_trunk = self.branch_horizontal_length.get(random)
|
||||||
|
+ if extend_branch_away_from_trunk { 1 } else { 0 };
|
||||||
|
|
||||||
|
let branch_end_pos = origin
|
||||||
|
.add(
|
||||||
|
branch_direction.0.x * distance_to_trunk,
|
||||||
|
0,
|
||||||
|
branch_direction.0.z * distance_to_trunk,
|
||||||
|
)
|
||||||
|
.up_height(branch_end_pos_offset);
|
||||||
|
|
||||||
|
let steps_horizontally = if extend_branch_away_from_trunk { 2 } else { 1 };
|
||||||
|
|
||||||
|
let axis = if branch_direction.0.x != 0 {
|
||||||
|
Axis::X
|
||||||
|
} else {
|
||||||
|
Axis::Z
|
||||||
|
};
|
||||||
|
let sideways_state = self.get_sideways_state(trunk_state, axis);
|
||||||
|
|
||||||
|
for _ in 0..steps_horizontally {
|
||||||
|
log_pos = log_pos.add(branch_direction.0.x, 0, branch_direction.0.z);
|
||||||
|
if placer.place(chunk, &log_pos, sideways_state) {
|
||||||
|
logs.push(log_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let vertical_direction = if branch_end_pos.0.y > log_pos.0.y {
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
-1
|
||||||
|
};
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let distance = log_pos.manhattan_distance(branch_end_pos);
|
||||||
|
|
||||||
|
if distance == 0 {
|
||||||
|
return (
|
||||||
|
TreeNode {
|
||||||
|
center: branch_end_pos.up(),
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
},
|
||||||
|
logs,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let chance_to_grow_vertically =
|
||||||
|
(branch_end_pos.0.y - log_pos.0.y).abs() as f32 / distance as f32;
|
||||||
|
let grow_vertically = random.next_f32() < chance_to_grow_vertically;
|
||||||
|
|
||||||
|
if grow_vertically {
|
||||||
|
log_pos = log_pos.up_height(vertical_direction);
|
||||||
|
if placer.place(chunk, &log_pos, trunk_state) {
|
||||||
|
logs.push(log_pos);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_pos = log_pos.add(branch_direction.0.x, 0, branch_direction.0.z);
|
||||||
|
if placer.place(chunk, &log_pos, sideways_state) {
|
||||||
|
logs.push(log_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_sideways_state(&self, trunk_state: &BlockState, axis: Axis) -> &'static BlockState {
|
||||||
|
let block = Block::from_state_id(trunk_state.id);
|
||||||
|
if let Some(props_source) = block.properties(trunk_state.id) {
|
||||||
|
let mut props = props_source.to_props();
|
||||||
|
let axis_str = match axis {
|
||||||
|
Axis::X => "x",
|
||||||
|
Axis::Y => "y",
|
||||||
|
Axis::Z => "z",
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(idx) = props.iter().position(|(k, _)| *k == "axis") {
|
||||||
|
props[idx] = ("axis", axis_str);
|
||||||
|
} else {
|
||||||
|
props.push(("axis", axis_str));
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_state_id = block.from_properties(&props).to_state_id(block);
|
||||||
|
return BlockState::from_id(new_state_id);
|
||||||
|
}
|
||||||
|
BlockState::from_id(trunk_state.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -27,120 +27,100 @@ impl DarkOakTrunkPlacer {
|
|||||||
below_trunk_provider: &BlockStateProvider,
|
below_trunk_provider: &BlockStateProvider,
|
||||||
trunk_block: &BlockState,
|
trunk_block: &BlockState,
|
||||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||||
let pos = start_pos.down();
|
let below = start_pos.down();
|
||||||
placer.set_dirt(block_registry, chunk, random, &pos, below_trunk_provider);
|
placer.set_dirt(block_registry, chunk, random, &below, below_trunk_provider);
|
||||||
placer.set_dirt(
|
placer.set_dirt(
|
||||||
block_registry,
|
block_registry,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
&pos.offset(BlockDirection::East.to_offset()),
|
&below.east(),
|
||||||
below_trunk_provider,
|
below_trunk_provider,
|
||||||
);
|
);
|
||||||
placer.set_dirt(
|
placer.set_dirt(
|
||||||
block_registry,
|
block_registry,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
&pos.offset(BlockDirection::South.to_offset()),
|
&below.south(),
|
||||||
below_trunk_provider,
|
below_trunk_provider,
|
||||||
);
|
);
|
||||||
placer.set_dirt(
|
placer.set_dirt(
|
||||||
block_registry,
|
block_registry,
|
||||||
chunk,
|
chunk,
|
||||||
random,
|
random,
|
||||||
&pos.offset(BlockDirection::South.to_offset())
|
&below.south().east(),
|
||||||
.offset(BlockDirection::East.to_offset()),
|
|
||||||
below_trunk_provider,
|
below_trunk_provider,
|
||||||
);
|
);
|
||||||
let start_y = start_pos.0.y;
|
|
||||||
let y_height = start_y + height as i32 - 1;
|
let horizontal_directions = BlockDirection::horizontal();
|
||||||
let max_height = height - random.next_bounded_i32(4) as u32;
|
let lean_direction = horizontal_directions[random.next_bounded_i32(4) as usize];
|
||||||
|
let lean_height = height as i32 - random.next_bounded_i32(4);
|
||||||
|
let mut lean_steps = 2 - random.next_bounded_i32(3);
|
||||||
|
|
||||||
|
let mut tx = start_pos.0.x;
|
||||||
|
let mut tz = start_pos.0.z;
|
||||||
|
let ey = start_pos.0.y + height as i32 - 1;
|
||||||
|
|
||||||
let mut trunk_poses = Vec::new();
|
let mut trunk_poses = Vec::new();
|
||||||
let mut nodes = Vec::new();
|
for dy in 0..height as i32 {
|
||||||
let mut rand = random.next_bounded_i32(3);
|
if dy >= lean_height && lean_steps > 0 {
|
||||||
|
let offset = lean_direction.to_offset();
|
||||||
let mut x = pos.0.x;
|
tx += offset.x;
|
||||||
let mut z = pos.0.z;
|
tz += offset.z;
|
||||||
|
lean_steps -= 1;
|
||||||
// TODO: make this random
|
|
||||||
let random_direction = BlockDirection::North;
|
|
||||||
|
|
||||||
for y in 0..height {
|
|
||||||
if y >= max_height && rand > 0 {
|
|
||||||
x += random_direction.to_offset().x;
|
|
||||||
z += random_direction.to_offset().z;
|
|
||||||
rand -= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_y = start_y + y as i32;
|
let yy = start_pos.0.y + dy;
|
||||||
let pos = BlockPos::new(x, current_y, z);
|
let pos = BlockPos::new(tx, yy, tz);
|
||||||
// TODO: support multiple chunks
|
|
||||||
|
// Check if air or leaves at the main 2x2 position
|
||||||
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
if !TreeFeature::is_air_or_leaves(state.to_state(), state.to_block_id()) {
|
if TreeFeature::is_air_or_leaves(state.to_state(), state.to_block_id()) {
|
||||||
continue;
|
if placer.try_place(chunk, &pos, trunk_block) {
|
||||||
}
|
trunk_poses.push(pos);
|
||||||
if placer.try_place(chunk, &pos, trunk_block) {
|
}
|
||||||
trunk_poses.push(pos);
|
if placer.try_place(chunk, &pos.east(), trunk_block) {
|
||||||
}
|
trunk_poses.push(pos.east());
|
||||||
if placer.try_place(
|
}
|
||||||
chunk,
|
if placer.try_place(chunk, &pos.south(), trunk_block) {
|
||||||
&pos.offset(BlockDirection::East.to_offset()),
|
trunk_poses.push(pos.south());
|
||||||
trunk_block,
|
}
|
||||||
) {
|
if placer.try_place(chunk, &pos.east().south(), trunk_block) {
|
||||||
trunk_poses.push(pos.offset(BlockDirection::East.to_offset()));
|
trunk_poses.push(pos.east().south());
|
||||||
}
|
}
|
||||||
if placer.try_place(
|
|
||||||
chunk,
|
|
||||||
&pos.offset(BlockDirection::South.to_offset()),
|
|
||||||
trunk_block,
|
|
||||||
) {
|
|
||||||
trunk_poses.push(pos.offset(BlockDirection::South.to_offset()));
|
|
||||||
}
|
|
||||||
if placer.try_place(
|
|
||||||
chunk,
|
|
||||||
&pos.offset(BlockDirection::East.to_offset())
|
|
||||||
.offset(BlockDirection::South.to_offset()),
|
|
||||||
trunk_block,
|
|
||||||
) {
|
|
||||||
trunk_poses.push(
|
|
||||||
pos.offset(BlockDirection::East.to_offset())
|
|
||||||
.offset(BlockDirection::South.to_offset()),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut nodes = Vec::new();
|
||||||
nodes.push(TreeNode {
|
nodes.push(TreeNode {
|
||||||
center: BlockPos::new(x, y_height, z),
|
center: BlockPos::new(tx, ey, tz),
|
||||||
foliage_radius: 0,
|
foliage_radius: 0,
|
||||||
giant_trunk: true,
|
giant_trunk: true,
|
||||||
});
|
});
|
||||||
for xd in -1..2 {
|
|
||||||
for zd in -1..2 {
|
for ox in -1..=2 {
|
||||||
if (0..=1).contains(&xd) && (0..=1).contains(&zd) || random.next_bounded_i32(3) > 0
|
for oz in -1..=2 {
|
||||||
|
if (!(0..=1).contains(&ox) || !(0..=1).contains(&oz))
|
||||||
|
&& random.next_bounded_i32(3) <= 0
|
||||||
{
|
{
|
||||||
continue;
|
let length = random.next_bounded_i32(3) + 2;
|
||||||
}
|
|
||||||
let h = random.next_bounded_i32(3) + 2;
|
for branch_y in 0..length {
|
||||||
for height in 0..h {
|
let pos = BlockPos::new(
|
||||||
if placer.try_place(
|
start_pos.0.x + ox,
|
||||||
chunk,
|
ey - branch_y - 1,
|
||||||
&BlockPos::new(
|
start_pos.0.z + oz,
|
||||||
start_pos.0.x + xd,
|
);
|
||||||
y_height - height - 1,
|
if placer.try_place(chunk, &pos, trunk_block) {
|
||||||
start_pos.0.z + zd,
|
trunk_poses.push(pos);
|
||||||
),
|
}
|
||||||
trunk_block,
|
|
||||||
) {
|
|
||||||
trunk_poses.push(BlockPos::new(
|
|
||||||
start_pos.0.x + xd,
|
|
||||||
y_height - height - 1,
|
|
||||||
start_pos.0.z + zd,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: BlockPos::new(start_pos.0.x + ox, ey, start_pos.0.z + oz),
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
nodes.push(TreeNode {
|
|
||||||
center: BlockPos::new(start_pos.0.x + xd, y_height, start_pos.0.z + zd),
|
|
||||||
foliage_radius: 0,
|
|
||||||
giant_trunk: false,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1 +1,107 @@
|
|||||||
|
use pumpkin_data::BlockState;
|
||||||
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||||
|
|
||||||
|
use super::TrunkPlacer;
|
||||||
|
use crate::generation::block_state_provider::BlockStateProvider;
|
||||||
|
use crate::generation::feature::features::tree::TreeNode;
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
use crate::world::BlockRegistryExt;
|
||||||
|
use pumpkin_data::BlockDirection;
|
||||||
|
|
||||||
pub struct ForkingTrunkPlacer;
|
pub struct ForkingTrunkPlacer;
|
||||||
|
|
||||||
|
impl ForkingTrunkPlacer {
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
block_registry: &dyn BlockRegistryExt,
|
||||||
|
placer: &TrunkPlacer,
|
||||||
|
height: u32,
|
||||||
|
start_pos: BlockPos,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
below_trunk_provider: &BlockStateProvider,
|
||||||
|
trunk_state: &BlockState,
|
||||||
|
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||||
|
placer.set_dirt(
|
||||||
|
block_registry,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
&start_pos.down(),
|
||||||
|
below_trunk_provider,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let mut logs = Vec::new();
|
||||||
|
|
||||||
|
let horizontal_directions = BlockDirection::horizontal();
|
||||||
|
let lean_direction = horizontal_directions[random.next_bounded_i32(4) as usize];
|
||||||
|
let lean_height = height as i32 - random.next_bounded_i32(4) - 1;
|
||||||
|
let mut lean_steps = 3 - random.next_bounded_i32(3);
|
||||||
|
|
||||||
|
let mut current_tx = start_pos.0.x;
|
||||||
|
let mut current_tz = start_pos.0.z;
|
||||||
|
let mut last_y = None;
|
||||||
|
|
||||||
|
for yo in 0..height as i32 {
|
||||||
|
let yy = start_pos.0.y + yo;
|
||||||
|
if yo >= lean_height && lean_steps > 0 {
|
||||||
|
let offset = lean_direction.to_offset();
|
||||||
|
current_tx += offset.x;
|
||||||
|
current_tz += offset.z;
|
||||||
|
lean_steps -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let pos = BlockPos::new(current_tx, yy, current_tz);
|
||||||
|
if placer.place(chunk, &pos, trunk_state) {
|
||||||
|
logs.push(pos);
|
||||||
|
last_y = Some(yy + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ey) = last_y {
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: BlockPos::new(current_tx, ey, current_tz),
|
||||||
|
foliage_radius: 1,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut branch_tx = start_pos.0.x;
|
||||||
|
let mut branch_tz = start_pos.0.z;
|
||||||
|
let branch_direction = horizontal_directions[random.next_bounded_i32(4) as usize];
|
||||||
|
|
||||||
|
if branch_direction != lean_direction {
|
||||||
|
let branch_pos = lean_height - random.next_bounded_i32(2) - 1;
|
||||||
|
let mut branch_steps = 1 + random.next_bounded_i32(3);
|
||||||
|
let mut branch_last_y = None;
|
||||||
|
|
||||||
|
let mut yo = branch_pos;
|
||||||
|
while yo < height as i32 && branch_steps > 0 {
|
||||||
|
if yo >= 1 {
|
||||||
|
let yyx = start_pos.0.y + yo;
|
||||||
|
let offset = branch_direction.to_offset();
|
||||||
|
branch_tx += offset.x;
|
||||||
|
branch_tz += offset.z;
|
||||||
|
let pos = BlockPos::new(branch_tx, yyx, branch_tz);
|
||||||
|
if placer.place(chunk, &pos, trunk_state) {
|
||||||
|
logs.push(pos);
|
||||||
|
branch_last_y = Some(yyx + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yo += 1;
|
||||||
|
branch_steps -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(ey) = branch_last_y {
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: BlockPos::new(branch_tx, ey, branch_tz),
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes, logs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -142,7 +142,16 @@ impl TrunkType {
|
|||||||
below_trunk_provider,
|
below_trunk_provider,
|
||||||
trunk_state,
|
trunk_state,
|
||||||
),
|
),
|
||||||
Self::Forking(_) => (vec![], vec![]), // TODO
|
Self::Forking(_) => ForkingTrunkPlacer::generate(
|
||||||
|
block_registry,
|
||||||
|
placer,
|
||||||
|
height,
|
||||||
|
start_pos,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
below_trunk_provider,
|
||||||
|
trunk_state,
|
||||||
|
),
|
||||||
Self::Giant(_) => GiantTrunkPlacer::generate(
|
Self::Giant(_) => GiantTrunkPlacer::generate(
|
||||||
block_registry,
|
block_registry,
|
||||||
placer,
|
placer,
|
||||||
@ -193,8 +202,26 @@ impl TrunkType {
|
|||||||
below_trunk_provider,
|
below_trunk_provider,
|
||||||
trunk_state,
|
trunk_state,
|
||||||
),
|
),
|
||||||
Self::UpwardsBranching(_) => (vec![], vec![]), // TODO
|
Self::UpwardsBranching(upwards_branching) => upwards_branching.generate(
|
||||||
Self::Cherry(_) => (vec![], vec![]), // TODO
|
block_registry,
|
||||||
|
placer,
|
||||||
|
height,
|
||||||
|
start_pos,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
below_trunk_provider,
|
||||||
|
trunk_state,
|
||||||
|
),
|
||||||
|
Self::Cherry(cherry) => cherry.generate(
|
||||||
|
block_registry,
|
||||||
|
placer,
|
||||||
|
height,
|
||||||
|
start_pos,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
below_trunk_provider,
|
||||||
|
trunk_state,
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,150 @@
|
|||||||
pub struct UpwardsBranchingTrunkPlacer {}
|
use pumpkin_data::BlockState;
|
||||||
|
use pumpkin_util::math::int_provider::IntProvider;
|
||||||
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
use pumpkin_util::random::{RandomGenerator, RandomImpl};
|
||||||
|
|
||||||
|
use super::TrunkPlacer;
|
||||||
|
use crate::generation::block_state_provider::BlockStateProvider;
|
||||||
|
use crate::generation::feature::features::tree::{TreeFeature, TreeNode};
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
use crate::world::BlockRegistryExt;
|
||||||
|
use pumpkin_data::BlockDirection;
|
||||||
|
|
||||||
|
pub struct UpwardsBranchingTrunkPlacer {
|
||||||
|
pub extra_branch_steps: IntProvider,
|
||||||
|
pub place_branch_per_log_probability: f32,
|
||||||
|
pub extra_branch_length: IntProvider,
|
||||||
|
pub can_grow_through: &'static [u16],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UpwardsBranchingTrunkPlacer {
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
_block_registry: &dyn BlockRegistryExt,
|
||||||
|
placer: &TrunkPlacer,
|
||||||
|
height: u32,
|
||||||
|
start_pos: BlockPos,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
_below_trunk_provider: &BlockStateProvider,
|
||||||
|
trunk_state: &BlockState,
|
||||||
|
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||||
|
let mut nodes = Vec::new();
|
||||||
|
let mut logs = Vec::new();
|
||||||
|
|
||||||
|
for height_pos in 0..height as i32 {
|
||||||
|
let current_pos = start_pos.up_height(height_pos);
|
||||||
|
if self.place_log_special(chunk, ¤t_pos, trunk_state) {
|
||||||
|
logs.push(current_pos);
|
||||||
|
if height_pos < height as i32 - 1
|
||||||
|
&& random.next_f32() < self.place_branch_per_log_probability
|
||||||
|
{
|
||||||
|
let branch_dir =
|
||||||
|
BlockDirection::horizontal()[random.next_bounded_i32(4) as usize];
|
||||||
|
let branch_len = self.extra_branch_length.get(random);
|
||||||
|
let branch_pos = (branch_len - self.extra_branch_length.get(random) - 1).max(0);
|
||||||
|
let branch_steps = self.extra_branch_steps.get(random);
|
||||||
|
|
||||||
|
self.place_branch(
|
||||||
|
placer,
|
||||||
|
height,
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
trunk_state,
|
||||||
|
&mut nodes,
|
||||||
|
&mut logs,
|
||||||
|
current_pos,
|
||||||
|
branch_dir,
|
||||||
|
branch_pos,
|
||||||
|
branch_steps,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if height_pos == height as i32 - 1 {
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: current_pos.up(),
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(nodes, logs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
fn place_branch<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
_placer: &TrunkPlacer,
|
||||||
|
tree_height: u32,
|
||||||
|
chunk: &mut T,
|
||||||
|
_random: &mut RandomGenerator,
|
||||||
|
trunk_state: &BlockState,
|
||||||
|
nodes: &mut Vec<TreeNode>,
|
||||||
|
logs: &mut Vec<BlockPos>,
|
||||||
|
current_pos: BlockPos,
|
||||||
|
branch_dir: BlockDirection,
|
||||||
|
branch_pos: i32,
|
||||||
|
mut branch_steps: i32,
|
||||||
|
) {
|
||||||
|
let mut height_along_branch = current_pos.0.y + branch_pos;
|
||||||
|
let mut log_x = current_pos.0.x;
|
||||||
|
let mut log_z = current_pos.0.z;
|
||||||
|
let mut branch_placement_index = branch_pos;
|
||||||
|
|
||||||
|
while branch_placement_index < tree_height as i32 && branch_steps > 0 {
|
||||||
|
if branch_placement_index >= 1 {
|
||||||
|
let placement_height = current_pos.0.y + branch_placement_index;
|
||||||
|
let offset = branch_dir.to_offset();
|
||||||
|
log_x += offset.x;
|
||||||
|
log_z += offset.z;
|
||||||
|
height_along_branch = placement_height;
|
||||||
|
|
||||||
|
let pos = BlockPos::new(log_x, placement_height, log_z);
|
||||||
|
if self.place_log_special(chunk, &pos, trunk_state) {
|
||||||
|
logs.push(pos);
|
||||||
|
height_along_branch = placement_height + 1;
|
||||||
|
}
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: pos,
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
branch_placement_index += 1;
|
||||||
|
branch_steps -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if height_along_branch - current_pos.0.y > 1 {
|
||||||
|
let foliage_pos = BlockPos::new(log_x, height_along_branch, log_z);
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: foliage_pos,
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
nodes.push(TreeNode {
|
||||||
|
center: foliage_pos.down_height(2),
|
||||||
|
foliage_radius: 0,
|
||||||
|
giant_trunk: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_log_special<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
pos: &BlockPos,
|
||||||
|
trunk_state: &BlockState,
|
||||||
|
) -> bool {
|
||||||
|
let block = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
|
if TreeFeature::can_replace(block.to_state(), block.to_block_id())
|
||||||
|
|| self.can_grow_through.contains(&block.to_block_id())
|
||||||
|
{
|
||||||
|
chunk.set_block_state(&pos.0, trunk_state);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user