mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
chore: finish 26.1 chunk generation port
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -43,7 +43,7 @@ pub fn build() -> TokenStream {
|
||||
};
|
||||
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
||||
use pumpkin_util::math::int_provider::{
|
||||
BiasedToBottomIntProvider, ClampedIntProvider, ClampedNormalIntProvider,
|
||||
BiasedToBottomIntProvider, ClampedIntProvider, TrapezoidIntProvider, ClampedNormalIntProvider,
|
||||
ConstantIntProvider, IntProvider, NormalIntProvider, UniformIntProvider,
|
||||
WeightedEntry, WeightedListIntProvider,
|
||||
};
|
||||
@@ -566,6 +566,9 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
||||
"minecraft:fossil" => {
|
||||
quote! { ConfiguredFeature::Fossil(crate::generation::feature::features::fossil::FossilFeature {}) }
|
||||
}
|
||||
"minecraft:fossil" => {
|
||||
quote! { ConfiguredFeature::Fossil(crate::generation::feature::features::fossil::FossilFeature {}) }
|
||||
}
|
||||
"minecraft:lake" => {
|
||||
quote! { ConfiguredFeature::Lake(crate::generation::feature::features::lake::LakeFeature {}) }
|
||||
}
|
||||
@@ -575,7 +578,7 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
||||
"minecraft:huge_red_mushroom" => {
|
||||
quote! { ConfiguredFeature::HugeRedMushroom(crate::generation::feature::features::huge_red_mushroom::HugeRedMushroomFeature {}) }
|
||||
}
|
||||
"minecraft:ice_spike" => {
|
||||
"minecraft:spike" => {
|
||||
quote! { ConfiguredFeature::IceSpike(crate::generation::feature::features::ice_spike::IceSpikeFeature {}) }
|
||||
}
|
||||
"minecraft:freeze_top_layer" => {
|
||||
@@ -596,7 +599,7 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
||||
"minecraft:iceberg" => {
|
||||
quote! { ConfiguredFeature::Iceberg(crate::generation::feature::features::iceberg::IcebergFeature {}) }
|
||||
}
|
||||
"minecraft:forest_rock" => {
|
||||
"minecraft:block_blob" => {
|
||||
quote! { ConfiguredFeature::ForestRock(crate::generation::feature::features::forest_rock::ForestRockFeature {}) }
|
||||
}
|
||||
"minecraft:end_platform" => {
|
||||
@@ -783,8 +786,14 @@ fn value_to_block_state_provider(v: &Value) -> TokenStream {
|
||||
})
|
||||
}
|
||||
}
|
||||
_ if !v["fallback"].is_null() => {
|
||||
let fallback = value_to_block_state_provider(&v["fallback"]);
|
||||
"minecraft:rule_based_state_provider" => {
|
||||
let fallback = if !v["fallback"].is_null() {
|
||||
let provider = value_to_block_state_provider(&v["fallback"]);
|
||||
quote! { Some(Box::new(#provider))}
|
||||
} else {
|
||||
quote! { None }
|
||||
};
|
||||
|
||||
let rules: Vec<TokenStream> = v["rules"]
|
||||
.as_array()
|
||||
.map(|arr| {
|
||||
@@ -799,7 +808,7 @@ fn value_to_block_state_provider(v: &Value) -> TokenStream {
|
||||
.unwrap_or_default();
|
||||
quote! {
|
||||
BlockStateProvider::Rule(RuleBasedBlockStateProvider {
|
||||
fallback: Box::new(#fallback),
|
||||
fallback: #fallback,
|
||||
rules: vec![#(#rules),*],
|
||||
})
|
||||
}
|
||||
@@ -859,7 +868,7 @@ fn value_to_rule_test(v: &Value) -> TokenStream {
|
||||
"minecraft:always_true" | "" => quote! { RuleTest::AlwaysTrue },
|
||||
"minecraft:block_match" => {
|
||||
let block = v["block"].as_str().unwrap_or("minecraft:stone");
|
||||
let name_stripped = block.strip_prefix("minecraft:").unwrap_or(block);
|
||||
let name_stripped = block.strip_prefix("minecraft:").unwrap_or(block);
|
||||
let block_ident =
|
||||
quote::format_ident!("{}", name_stripped.to_uppercase().replace([':', '-'], "_"));
|
||||
quote! { RuleTest::BlockMatch(BlockMatchRuleTest { block: pumpkin_data::Block::#block_ident }) }
|
||||
@@ -870,12 +879,13 @@ fn value_to_rule_test(v: &Value) -> TokenStream {
|
||||
}
|
||||
"minecraft:tag_match" => {
|
||||
let tag = v["tag"].as_str().unwrap_or("");
|
||||
quote! { RuleTest::TagMatch(TagMatchRuleTest { tag: #tag.to_string() }) }
|
||||
let tag_ident = quote::format_ident!("{}", tag.to_uppercase().replace([':', '-'], "_"));
|
||||
quote! { RuleTest::TagMatch(TagMatchRuleTest { tag: pumpkin_data::tag::Block::#tag_ident }) }
|
||||
}
|
||||
"minecraft:random_block_match" => {
|
||||
let block = v["block"].as_str().unwrap_or("minecraft:stone");
|
||||
let prob = v["probability"].as_f64().unwrap_or(0.5) as f32;
|
||||
let name_stripped = block.strip_prefix("minecraft:").unwrap_or(block);
|
||||
let name_stripped = block.strip_prefix("minecraft:").unwrap_or(block);
|
||||
let block_ident =
|
||||
quote::format_ident!("{}", block.to_uppercase().replace([':', '-'], "_"));
|
||||
quote! { RuleTest::RandomBlockMatch(RandomBlockMatchRuleTest { block: pumpkin_data::Block::#block_ident, probability: #prob }) }
|
||||
@@ -918,28 +928,26 @@ fn value_to_block_wrapper(v: &Value) -> TokenStream {
|
||||
/// # Arguments
|
||||
/// – `config` – the `"config"` sub-object of a `minecraft:tree` configured feature JSON entry.
|
||||
fn value_to_tree_feature(config: &Value) -> TokenStream {
|
||||
let dirt = value_to_block_state_provider(&config["dirt_provider"]);
|
||||
let trunk = value_to_block_state_provider(&config["trunk_provider"]);
|
||||
let trunk_placer = value_to_trunk_placer(&config["trunk_placer"]);
|
||||
let foliage = value_to_block_state_provider(&config["foliage_provider"]);
|
||||
let foliage_placer = value_to_foliage_placer(&config["foliage_placer"]);
|
||||
let min_size = value_to_feature_size(&config["minimum_size"]);
|
||||
let ignore_vines = config["ignore_vines"].as_bool().unwrap_or(true);
|
||||
let force_dirt = config["force_dirt"].as_bool().unwrap_or(false);
|
||||
let below_trunk_provider = value_to_block_state_provider(&config["below_trunk_provider"]);
|
||||
let decorators: Vec<TokenStream> = config["decorators"]
|
||||
.as_array()
|
||||
.map(|arr| arr.iter().map(value_to_tree_decorator).collect())
|
||||
.unwrap_or_default();
|
||||
quote! {
|
||||
TreeFeature {
|
||||
dirt_provider: #dirt,
|
||||
trunk_provider: #trunk,
|
||||
trunk_placer: #trunk_placer,
|
||||
foliage_provider: #foliage,
|
||||
foliage_placer: #foliage_placer,
|
||||
minimum_size: #min_size,
|
||||
ignore_vines: #ignore_vines,
|
||||
force_dirt: #force_dirt,
|
||||
below_trunk_provider: #below_trunk_provider,
|
||||
decorators: vec![#(#decorators),*],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ pub fn build() -> TokenStream {
|
||||
};
|
||||
use pumpkin_util::y_offset::{AboveBottom, Absolute, BelowTop, YOffset};
|
||||
use pumpkin_util::math::int_provider::{
|
||||
BiasedToBottomIntProvider, ClampedIntProvider, ClampedNormalIntProvider,
|
||||
BiasedToBottomIntProvider, ClampedIntProvider, TrapezoidIntProvider, ClampedNormalIntProvider,
|
||||
ConstantIntProvider, IntProvider, NormalIntProvider, UniformIntProvider,
|
||||
WeightedEntry, WeightedListIntProvider,
|
||||
};
|
||||
@@ -92,7 +92,7 @@ fn value_to_feature(v: &Value) -> TokenStream {
|
||||
let cf = value_to_inline_configured_feature(v);
|
||||
quote! { Feature::Inlined(Box::new(#cf)) }
|
||||
}
|
||||
_ => quote! { Feature::Named(String::new()) },
|
||||
_ => panic!("Wrong feature value"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,10 +273,11 @@ pub fn value_to_block_predicate(v: &Value) -> TokenStream {
|
||||
// "true" (or any non-# string) is AlwaysTrue.
|
||||
if let Some(s) = v.as_str() {
|
||||
if let Some(tag) = s.strip_prefix('#') {
|
||||
let tag_ident = quote::format_ident!("{}", tag.to_uppercase().replace([':', '-'], "_"));
|
||||
return quote! {
|
||||
BlockPredicate::MatchingBlockTag(MatchingBlockTagPredicate {
|
||||
offset: OffsetBlocksBlockPredicate { offset: None },
|
||||
tag: #tag.to_string(),
|
||||
tag: pumpkin_data::tag::Block::#tag_ident,
|
||||
})
|
||||
};
|
||||
}
|
||||
@@ -299,10 +300,11 @@ pub fn value_to_block_predicate(v: &Value) -> TokenStream {
|
||||
"minecraft:matching_block_tag" => {
|
||||
let offset = value_to_offset_predicate(&v["offset"]);
|
||||
let tag = v["tag"].as_str().unwrap_or("");
|
||||
let tag_ident = quote::format_ident!("{}", tag.to_uppercase().replace([':', '-'], "_"));
|
||||
quote! {
|
||||
BlockPredicate::MatchingBlockTag(MatchingBlockTagPredicate {
|
||||
offset: #offset,
|
||||
tag: #tag.to_string(),
|
||||
tag: pumpkin_data::tag::Block::#tag_ident,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -526,6 +528,12 @@ pub fn value_to_int_provider(v: &Value) -> TokenStream {
|
||||
let src = value_to_int_provider(&v["source"]);
|
||||
quote! { IntProvider::Object(NormalIntProvider::Clamped(ClampedIntProvider { source: Box::new(#src), min_inclusive: #min, max_inclusive: #max })) }
|
||||
}
|
||||
"minecraft:trapezoid" => {
|
||||
let min = v["min"].as_i64().unwrap_or(0) as i32;
|
||||
let max = v["max"].as_i64().unwrap_or(0) as i32;
|
||||
let plateau = v["plateau"].as_i64().unwrap_or(0) as i32;
|
||||
quote! { IntProvider::Object(NormalIntProvider::Trapezoid(TrapezoidIntProvider { min_inclusive: #min, max_inclusive: #max, plateau: #plateau })) }
|
||||
}
|
||||
"minecraft:clamped_normal" => {
|
||||
let mean = v["mean"].as_f64().unwrap_or(0.0) as f32;
|
||||
let dev = v["deviation"].as_f64().unwrap_or(1.0) as f32;
|
||||
@@ -549,12 +557,11 @@ pub fn value_to_int_provider(v: &Value) -> TokenStream {
|
||||
quote! { IntProvider::Object(NormalIntProvider::WeightedList(WeightedListIntProvider { distribution: vec![#(#entries),*] })) }
|
||||
}
|
||||
_ => {
|
||||
let val = v["value"].as_i64().unwrap_or(0) as i32;
|
||||
quote! { IntProvider::Constant(#val) }
|
||||
panic!("Unknown Int Provider, Seems like Mojang added a new one")
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => quote! { IntProvider::Constant(0) },
|
||||
_ => panic!("Unknown Int Provider, Seems like Mojang added a new one"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,7 +580,7 @@ pub fn value_to_height_map(s: &str) -> TokenStream {
|
||||
"OCEAN_FLOOR" => quote! { HeightMap::OceanFloor },
|
||||
"MOTION_BLOCKING" => quote! { HeightMap::MotionBlocking },
|
||||
"MOTION_BLOCKING_NO_LEAVES" => quote! { HeightMap::MotionBlockingNoLeaves },
|
||||
_ => quote! { HeightMap::MotionBlocking },
|
||||
_ => panic!("Unknown Height map"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -592,7 +599,7 @@ pub fn value_to_block_direction(s: &str) -> TokenStream {
|
||||
"south" => quote! { BlockDirection::South },
|
||||
"west" => quote! { BlockDirection::West },
|
||||
"east" => quote! { BlockDirection::East },
|
||||
_ => quote! { BlockDirection::Down },
|
||||
_ => panic!("Unknown Block direction"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@@ -789,94 +789,94 @@ mod tests {
|
||||
|
||||
// ── weapon category predicates ───────────────────────────────────
|
||||
|
||||
// /// 2-durability combat weapons (axes/pickaxes/shovels/hoes) must match their category predicate.
|
||||
// #[test]
|
||||
// fn weapon_categories_identify_2_cost_items() {
|
||||
// // Items that should have is_axe / is_pickaxe / is_shovel / is_hoe = true.
|
||||
// let axes: &[&Item] = &[
|
||||
// &Item::WOODEN_AXE,
|
||||
// &Item::STONE_AXE,
|
||||
// &Item::IRON_AXE,
|
||||
// &Item::GOLDEN_AXE,
|
||||
// &Item::DIAMOND_AXE,
|
||||
// &Item::NETHERITE_AXE,
|
||||
// ];
|
||||
// let pickaxes: &[&Item] = &[
|
||||
// &Item::WOODEN_PICKAXE,
|
||||
// &Item::STONE_PICKAXE,
|
||||
// &Item::IRON_PICKAXE,
|
||||
// &Item::GOLDEN_PICKAXE,
|
||||
// &Item::DIAMOND_PICKAXE,
|
||||
// &Item::NETHERITE_PICKAXE,
|
||||
// ];
|
||||
// let shovels: &[&Item] = &[
|
||||
// &Item::WOODEN_SHOVEL,
|
||||
// &Item::STONE_SHOVEL,
|
||||
// &Item::IRON_SHOVEL,
|
||||
// &Item::GOLDEN_SHOVEL,
|
||||
// &Item::DIAMOND_SHOVEL,
|
||||
// &Item::NETHERITE_SHOVEL,
|
||||
// ];
|
||||
// let hoes: &[&Item] = &[
|
||||
// &Item::WOODEN_HOE,
|
||||
// &Item::STONE_HOE,
|
||||
// &Item::IRON_HOE,
|
||||
// &Item::GOLDEN_HOE,
|
||||
// &Item::DIAMOND_HOE,
|
||||
// &Item::NETHERITE_HOE,
|
||||
// ];
|
||||
/// 2-durability combat weapons (axes/pickaxes/shovels/hoes) must match their category predicate.
|
||||
#[test]
|
||||
fn weapon_categories_identify_2_cost_items() {
|
||||
// Items that should have is_axe / is_pickaxe / is_shovel / is_hoe = true.
|
||||
let axes: &[&Item] = &[
|
||||
&Item::WOODEN_AXE,
|
||||
&Item::STONE_AXE,
|
||||
&Item::IRON_AXE,
|
||||
&Item::GOLDEN_AXE,
|
||||
&Item::DIAMOND_AXE,
|
||||
&Item::NETHERITE_AXE,
|
||||
];
|
||||
let pickaxes: &[&Item] = &[
|
||||
&Item::WOODEN_PICKAXE,
|
||||
&Item::STONE_PICKAXE,
|
||||
&Item::IRON_PICKAXE,
|
||||
&Item::GOLDEN_PICKAXE,
|
||||
&Item::DIAMOND_PICKAXE,
|
||||
&Item::NETHERITE_PICKAXE,
|
||||
];
|
||||
let shovels: &[&Item] = &[
|
||||
&Item::WOODEN_SHOVEL,
|
||||
&Item::STONE_SHOVEL,
|
||||
&Item::IRON_SHOVEL,
|
||||
&Item::GOLDEN_SHOVEL,
|
||||
&Item::DIAMOND_SHOVEL,
|
||||
&Item::NETHERITE_SHOVEL,
|
||||
];
|
||||
let hoes: &[&Item] = &[
|
||||
&Item::WOODEN_HOE,
|
||||
&Item::STONE_HOE,
|
||||
&Item::IRON_HOE,
|
||||
&Item::GOLDEN_HOE,
|
||||
&Item::DIAMOND_HOE,
|
||||
&Item::NETHERITE_HOE,
|
||||
];
|
||||
|
||||
// for item in axes {
|
||||
// let stack = ItemStack::new(1, item);
|
||||
// assert!(stack.is_axe(), "{} should be an axe", item.registry_key);
|
||||
// assert!(
|
||||
// !stack.is_sword(),
|
||||
// "{} should not be a sword",
|
||||
// item.registry_key
|
||||
// );
|
||||
// }
|
||||
// for item in pickaxes {
|
||||
// let stack = ItemStack::new(1, item);
|
||||
// assert!(
|
||||
// stack.is_pickaxe(),
|
||||
// "{} should be a pickaxe",
|
||||
// item.registry_key
|
||||
// );
|
||||
// }
|
||||
// for item in shovels {
|
||||
// let stack = ItemStack::new(1, item);
|
||||
// assert!(
|
||||
// stack.is_shovel(),
|
||||
// "{} should be a shovel",
|
||||
// item.registry_key
|
||||
// );
|
||||
// }
|
||||
// for item in hoes {
|
||||
// let stack = ItemStack::new(1, item);
|
||||
// assert!(stack.is_hoe(), "{} should be a hoe", item.registry_key);
|
||||
// }
|
||||
for item in axes {
|
||||
let stack = ItemStack::new(1, item);
|
||||
assert!(stack.is_axe(), "{} should be an axe", item.registry_key);
|
||||
assert!(
|
||||
!stack.is_sword(),
|
||||
"{} should not be a sword",
|
||||
item.registry_key
|
||||
);
|
||||
}
|
||||
for item in pickaxes {
|
||||
let stack = ItemStack::new(1, item);
|
||||
assert!(
|
||||
stack.is_pickaxe(),
|
||||
"{} should be a pickaxe",
|
||||
item.registry_key
|
||||
);
|
||||
}
|
||||
for item in shovels {
|
||||
let stack = ItemStack::new(1, item);
|
||||
assert!(
|
||||
stack.is_shovel(),
|
||||
"{} should be a shovel",
|
||||
item.registry_key
|
||||
);
|
||||
}
|
||||
for item in hoes {
|
||||
let stack = ItemStack::new(1, item);
|
||||
assert!(stack.is_hoe(), "{} should be a hoe", item.registry_key);
|
||||
}
|
||||
|
||||
// // Swords should cost 1, so they must NOT match any 2-cost predicate.
|
||||
// let swords: &[&Item] = &[
|
||||
// &Item::IRON_SWORD,
|
||||
// &Item::DIAMOND_SWORD,
|
||||
// &Item::NETHERITE_SWORD,
|
||||
// ];
|
||||
// for item in swords {
|
||||
// let stack = ItemStack::new(1, item);
|
||||
// assert!(stack.is_sword(), "{} should be a sword", item.registry_key);
|
||||
// assert!(
|
||||
// !stack.is_axe(),
|
||||
// "{} should not be an axe",
|
||||
// item.registry_key
|
||||
// );
|
||||
// assert!(
|
||||
// !stack.is_pickaxe(),
|
||||
// "{} should not be a pickaxe",
|
||||
// item.registry_key
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
// Swords should cost 1, so they must NOT match any 2-cost predicate.
|
||||
let swords: &[&Item] = &[
|
||||
&Item::IRON_SWORD,
|
||||
&Item::DIAMOND_SWORD,
|
||||
&Item::NETHERITE_SWORD,
|
||||
];
|
||||
for item in swords {
|
||||
let stack = ItemStack::new(1, item);
|
||||
assert!(stack.is_sword(), "{} should be a sword", item.registry_key);
|
||||
assert!(
|
||||
!stack.is_axe(),
|
||||
"{} should not be an axe",
|
||||
item.registry_key
|
||||
);
|
||||
assert!(
|
||||
!stack.is_pickaxe(),
|
||||
"{} should not be a pickaxe",
|
||||
item.registry_key
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Unbreaking (statistical) ─────────────────────────────────────
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ pub enum NormalIntProvider {
|
||||
/// Wraps another provider and clamps its output to a specified range.
|
||||
#[serde(rename = "minecraft:clamped")]
|
||||
Clamped(ClampedIntProvider),
|
||||
#[serde(rename = "minecraft:trapezoid")]
|
||||
Trapezoid(TrapezoidIntProvider),
|
||||
/// Returns values from a normal (Gaussian) distribution, clamped to a specified inclusive range.
|
||||
#[serde(rename = "minecraft:clamped_normal")]
|
||||
ClampedNormal(ClampedNormalIntProvider),
|
||||
@@ -56,6 +58,11 @@ impl ToTokens for NormalIntProvider {
|
||||
NormalIntProvider::ClampedNormal(#clamped_normal)
|
||||
});
|
||||
}
|
||||
Self::Trapezoid(trapezoid) => {
|
||||
tokens.extend(quote! {
|
||||
NormalIntProvider::Trapezoid(#trapezoid)
|
||||
});
|
||||
}
|
||||
Self::WeightedList(weighted_list) => {
|
||||
tokens.extend(quote! {
|
||||
NormalIntProvider::WeightedList(#weighted_list)
|
||||
@@ -103,6 +110,7 @@ impl IntProvider {
|
||||
NormalIntProvider::Uniform(uniform) => uniform.get_min(),
|
||||
NormalIntProvider::BiasedToBottom(biased) => biased.get_min(),
|
||||
NormalIntProvider::Clamped(clamped) => clamped.get_min(),
|
||||
NormalIntProvider::Trapezoid(trapezoid) => trapezoid.get_min(),
|
||||
NormalIntProvider::ClampedNormal(clamped_normal) => clamped_normal.get_min(),
|
||||
NormalIntProvider::WeightedList(weighted_list) => weighted_list.get_min(),
|
||||
},
|
||||
@@ -126,6 +134,9 @@ impl IntProvider {
|
||||
NormalIntProvider::Clamped(clamped) => clamped.get(random),
|
||||
NormalIntProvider::ClampedNormal(clamped_normal) => clamped_normal.get(random),
|
||||
NormalIntProvider::WeightedList(weighted_list) => weighted_list.get(random),
|
||||
NormalIntProvider::Trapezoid(trapezoid_int_provider) => {
|
||||
trapezoid_int_provider.get(random)
|
||||
}
|
||||
},
|
||||
Self::Constant(i) => *i,
|
||||
}
|
||||
@@ -145,6 +156,9 @@ impl IntProvider {
|
||||
NormalIntProvider::Clamped(clamped) => clamped.get_max(),
|
||||
NormalIntProvider::ClampedNormal(clamped_normal) => clamped_normal.get_max(),
|
||||
NormalIntProvider::WeightedList(weighted_list) => weighted_list.get_max(),
|
||||
NormalIntProvider::Trapezoid(trapezoid_int_provider) => {
|
||||
trapezoid_int_provider.get_max()
|
||||
}
|
||||
},
|
||||
Self::Constant(i) => *i,
|
||||
}
|
||||
@@ -357,6 +371,91 @@ impl ClampedIntProvider {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct TrapezoidIntProvider {
|
||||
/// The minimum value (inclusive) to clamp to.
|
||||
pub min_inclusive: i32,
|
||||
/// The maximum value (inclusive) to clamp to.
|
||||
pub max_inclusive: i32,
|
||||
pub plateau: i32,
|
||||
}
|
||||
|
||||
impl ToTokens for TrapezoidIntProvider {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let min_inclusive = LitInt::new(&self.min_inclusive.to_string(), Span::call_site());
|
||||
let max_inclusive = LitInt::new(&self.max_inclusive.to_string(), Span::call_site());
|
||||
let plateau = LitInt::new(&self.plateau.to_string(), Span::call_site());
|
||||
tokens.extend(quote! {
|
||||
TrapezoidIntProvider {
|
||||
min_inclusive: #min_inclusive,
|
||||
max_inclusive: #max_inclusive,
|
||||
plateau: #plateau
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl TrapezoidIntProvider {
|
||||
/// Creates a new clamped provider with the specified source and range.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `min_inclusive` – The minimum value (inclusive) to clamp to.
|
||||
/// - `max_inclusive` – The maximum value (inclusive) to clamp to.
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `ClampedIntProvider` instance.
|
||||
#[must_use]
|
||||
pub const fn new(min_inclusive: i32, max_inclusive: i32, plateau: i32) -> Self {
|
||||
Self {
|
||||
min_inclusive,
|
||||
max_inclusive,
|
||||
plateau,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the minimum value after clamping.
|
||||
///
|
||||
/// # Returns
|
||||
/// The larger of the source's minimum and the clamp minimum.
|
||||
#[must_use]
|
||||
pub const fn get_min(&self) -> i32 {
|
||||
self.min_inclusive
|
||||
}
|
||||
|
||||
/// Generates a random value from the source and clamps it to the configured range.
|
||||
///
|
||||
/// # Arguments
|
||||
/// - `random` – The random number generator to use.
|
||||
///
|
||||
/// # Returns
|
||||
/// A random integer from the source provider, clamped to [`min_inclusive`, `max_inclusive`].
|
||||
pub fn get(&self, random: &mut impl RandomImpl) -> i32 {
|
||||
if self.plateau == 0 && self.max_inclusive == -self.min_inclusive {
|
||||
return random.next_bounded_i32(self.max_inclusive + 1)
|
||||
- random.next_bounded_i32(self.max_inclusive + 1);
|
||||
}
|
||||
let range = self.max_inclusive - self.min_inclusive;
|
||||
if self.plateau == range {
|
||||
return random.next_bounded_i32(self.max_inclusive - self.min_inclusive + 1)
|
||||
+ self.min_inclusive;
|
||||
}
|
||||
let plateau_start = (range - self.plateau) / 2;
|
||||
let plateau_end = range - plateau_start;
|
||||
self.min_inclusive
|
||||
+ random.next_bounded_i32(plateau_end + 1)
|
||||
+ random.next_bounded_i32(plateau_start + 1)
|
||||
}
|
||||
|
||||
/// Returns the maximum value after clamping.
|
||||
///
|
||||
/// # Returns
|
||||
/// The smaller of the source's maximum and the clamp maximum.
|
||||
#[must_use]
|
||||
pub const fn get_max(&self) -> i32 {
|
||||
self.max_inclusive
|
||||
}
|
||||
}
|
||||
|
||||
/// An integer provider that generates values from a clamped normal distribution.
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct ClampedNormalIntProvider {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use itertools::Itertools;
|
||||
use pumpkin_data::fluid::{Fluid, FluidState};
|
||||
use pumpkin_data::tag::{RegistryKey, get_tag_ids};
|
||||
use pumpkin_data::tag::{self};
|
||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||
use pumpkin_util::math::{position::BlockPos, vector3::Vector3};
|
||||
|
||||
@@ -102,15 +102,13 @@ impl MatchingFluidsBlockPredicate {
|
||||
|
||||
pub struct MatchingBlockTagPredicate {
|
||||
pub offset: OffsetBlocksBlockPredicate,
|
||||
pub tag: String,
|
||||
pub tag: tag::Tag,
|
||||
}
|
||||
|
||||
impl MatchingBlockTagPredicate {
|
||||
pub fn test<T: GenerationCache>(&self, chunk: &T, pos: &BlockPos) -> bool {
|
||||
let block = self.offset.get_raw(chunk, pos);
|
||||
get_tag_ids(RegistryKey::Block, &self.tag)
|
||||
.unwrap()
|
||||
.contains(&block.to_block_id())
|
||||
let state = self.offset.get_raw(chunk, pos);
|
||||
self.tag.1.contains(&state.to_block_id())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ impl BlockStateProvider {
|
||||
Self::Pillar(provider) => provider.get(pos),
|
||||
Self::RandomizedInt(provider) => provider.get(random, pos),
|
||||
// Without chunk context, fall through to fallback (rules cannot be evaluated)
|
||||
Self::Rule(provider) => provider.fallback.get(random, pos),
|
||||
Self::Rule(_provider) => todo!(), //provider.get(random, pos),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,10 +53,23 @@ impl BlockStateProvider {
|
||||
_ => self.get(random, pos),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_optional<T: GenerationCache>(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &T,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> Option<&'static BlockState> {
|
||||
match self {
|
||||
Self::Rule(provider) => provider.get_optional(block_registry, chunk, random, pos),
|
||||
_ => Some(self.get(random, pos)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RuleBasedBlockStateProvider {
|
||||
pub fallback: Box<BlockStateProvider>,
|
||||
pub fallback: Option<Box<BlockStateProvider>>,
|
||||
pub rules: Vec<BlockStateRule>,
|
||||
}
|
||||
|
||||
@@ -68,14 +81,27 @@ impl RuleBasedBlockStateProvider {
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> &'static BlockState {
|
||||
if let Some(optional) = self.get_optional(block_registry, chunk, random, pos) {
|
||||
return optional;
|
||||
}
|
||||
GenerationCache::get_block_state(chunk, &pos.0).to_state()
|
||||
}
|
||||
pub fn get_optional<T: GenerationCache>(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &T,
|
||||
random: &mut RandomGenerator,
|
||||
pos: BlockPos,
|
||||
) -> Option<&'static BlockState> {
|
||||
for rule in &self.rules {
|
||||
if rule.if_true.test(block_registry, chunk, &pos) {
|
||||
return rule
|
||||
.then
|
||||
.get_with_context(block_registry, chunk, random, pos);
|
||||
return Some(
|
||||
rule.then
|
||||
.get_with_context(block_registry, chunk, random, pos),
|
||||
);
|
||||
}
|
||||
}
|
||||
self.fallback.get(random, pos)
|
||||
self.fallback.as_ref().map(|f| f.get(random, pos))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -278,9 +278,15 @@ impl ConfiguredFeature {
|
||||
random,
|
||||
pos,
|
||||
),
|
||||
Self::Tree(feature) => {
|
||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||
}
|
||||
Self::Tree(feature) => feature.generate(
|
||||
block_registry,
|
||||
chunk,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
),
|
||||
Self::RandomSelector(feature) => feature.generate(
|
||||
chunk,
|
||||
block_registry,
|
||||
|
||||
@@ -8,21 +8,21 @@ use trunk::TrunkPlacer;
|
||||
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::generation::{block_state_provider::BlockStateProvider, feature::size::FeatureSize};
|
||||
use crate::world::BlockRegistryExt;
|
||||
|
||||
pub mod decorator;
|
||||
pub mod foliage;
|
||||
pub mod trunk;
|
||||
|
||||
pub struct TreeFeature {
|
||||
pub dirt_provider: BlockStateProvider,
|
||||
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 force_dirt: bool,
|
||||
pub decorators: Vec<TreeDecorator>,
|
||||
pub below_trunk_provider: BlockStateProvider,
|
||||
}
|
||||
|
||||
pub struct TreeNode {
|
||||
@@ -32,8 +32,10 @@ pub struct TreeNode {
|
||||
}
|
||||
|
||||
impl TreeFeature {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn generate<T: GenerationCache>(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &mut T,
|
||||
min_y: i8,
|
||||
height: u16,
|
||||
@@ -42,7 +44,15 @@ impl TreeFeature {
|
||||
pos: BlockPos,
|
||||
) -> bool {
|
||||
// TODO
|
||||
let log_positions = self.generate_main(chunk, min_y, height, feature_name, random, pos);
|
||||
let log_positions = self.generate_main(
|
||||
block_registry,
|
||||
chunk,
|
||||
min_y,
|
||||
height,
|
||||
feature_name,
|
||||
random,
|
||||
pos,
|
||||
);
|
||||
|
||||
for decorator in &self.decorators {
|
||||
decorator.generate(chunk, random, &[], &log_positions);
|
||||
@@ -65,8 +75,10 @@ impl TreeFeature {
|
||||
.contains(&block)
|
||||
}
|
||||
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
fn generate_main<T: GenerationCache>(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &mut T,
|
||||
_min_y: i8,
|
||||
_height: u16,
|
||||
@@ -82,15 +94,14 @@ impl TreeFeature {
|
||||
return vec![];
|
||||
}
|
||||
let trunk_state = self.trunk_provider.get(random, pos);
|
||||
let dirt_state = self.dirt_provider.get(random, pos);
|
||||
|
||||
let (nodes, logs) = self.trunk_placer.generate(
|
||||
block_registry,
|
||||
top,
|
||||
pos,
|
||||
chunk,
|
||||
random,
|
||||
self.force_dirt,
|
||||
dirt_state,
|
||||
&self.below_trunk_provider,
|
||||
trunk_state,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,8 +4,14 @@ use pumpkin_util::{
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::generation::feature::features::tree::{TreeNode, trunk::TrunkPlacer};
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::{
|
||||
generation::{
|
||||
block_state_provider::BlockStateProvider,
|
||||
feature::features::tree::{TreeNode, trunk::TrunkPlacer},
|
||||
},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
pub struct BendingTrunkPlacer {
|
||||
pub min_height_for_leaves: u32,
|
||||
@@ -16,16 +22,22 @@ impl BendingTrunkPlacer {
|
||||
#[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,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_block: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
placer.set_dirt(chunk, &start_pos.down(), force_dirt, dirt_state);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&start_pos.down(),
|
||||
below_trunk_provider,
|
||||
);
|
||||
|
||||
// TODO: make this random
|
||||
let random_direction = BlockDirection::North;
|
||||
|
||||
@@ -4,43 +4,52 @@ use pumpkin_util::{
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::generation::feature::features::tree::{TreeFeature, TreeNode, trunk::TrunkPlacer};
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::{
|
||||
generation::{
|
||||
block_state_provider::BlockStateProvider,
|
||||
feature::features::tree::{TreeFeature, TreeNode, trunk::TrunkPlacer},
|
||||
},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
pub struct DarkOakTrunkPlacer;
|
||||
|
||||
impl DarkOakTrunkPlacer {
|
||||
#[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,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_block: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
let pos = start_pos.down();
|
||||
placer.set_dirt(chunk, &pos, force_dirt, dirt_state);
|
||||
placer.set_dirt(block_registry, chunk, random, &pos, below_trunk_provider);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.offset(BlockDirection::East.to_offset()),
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.offset(BlockDirection::South.to_offset()),
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.offset(BlockDirection::South.to_offset())
|
||||
.offset(BlockDirection::East.to_offset()),
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
);
|
||||
let start_y = start_pos.0.y;
|
||||
let y_height = start_y + height as i32 - 1;
|
||||
|
||||
@@ -10,27 +10,39 @@ use pumpkin_util::{
|
||||
};
|
||||
|
||||
use super::TrunkPlacer;
|
||||
use crate::generation::feature::features::tree::{TreeFeature, TreeNode};
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::{
|
||||
generation::{
|
||||
block_state_provider::BlockStateProvider,
|
||||
feature::features::tree::{TreeFeature, TreeNode},
|
||||
},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
pub struct FancyTrunkPlacer;
|
||||
|
||||
impl FancyTrunkPlacer {
|
||||
#[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,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_block: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
let j = height as i32 + 2;
|
||||
let k = ((j as f64) * 0.618).floor() as i32;
|
||||
|
||||
placer.set_dirt(chunk, &start_pos.down(), force_dirt, dirt_state);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&start_pos.down(),
|
||||
below_trunk_provider,
|
||||
);
|
||||
|
||||
let l = ((1.382 + (1.0 * (j as f64) / 13.0).powf(2.0)).floor() as i32).min(1);
|
||||
let m = start_pos.0.y + k;
|
||||
|
||||
@@ -4,43 +4,52 @@ use pumpkin_util::{
|
||||
random::RandomGenerator,
|
||||
};
|
||||
|
||||
use crate::generation::feature::features::tree::{TreeNode, trunk::TrunkPlacer};
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::{
|
||||
generation::{
|
||||
block_state_provider::BlockStateProvider,
|
||||
feature::features::tree::{TreeNode, trunk::TrunkPlacer},
|
||||
},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
pub struct GiantTrunkPlacer;
|
||||
|
||||
impl GiantTrunkPlacer {
|
||||
#[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,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
random: &mut RandomGenerator,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_block: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
let pos = start_pos.down();
|
||||
placer.set_dirt(chunk, &pos, force_dirt, dirt_state);
|
||||
placer.set_dirt(block_registry, chunk, random, &pos, below_trunk_provider);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.offset(BlockDirection::East.to_offset()),
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.offset(BlockDirection::South.to_offset()),
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&pos.offset(BlockDirection::South.to_offset())
|
||||
.offset(BlockDirection::South.to_offset()),
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
);
|
||||
|
||||
let mut trunk_poses = Vec::new();
|
||||
|
||||
@@ -6,34 +6,40 @@ use pumpkin_util::{
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::generation::feature::features::tree::{
|
||||
TreeNode,
|
||||
trunk::{TrunkPlacer, giant::GiantTrunkPlacer},
|
||||
};
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::{
|
||||
generation::{
|
||||
block_state_provider::BlockStateProvider,
|
||||
feature::features::tree::{
|
||||
TreeNode,
|
||||
trunk::{TrunkPlacer, giant::GiantTrunkPlacer},
|
||||
},
|
||||
},
|
||||
world::BlockRegistryExt,
|
||||
};
|
||||
|
||||
pub struct MegaJungleTrunkPlacer;
|
||||
|
||||
impl MegaJungleTrunkPlacer {
|
||||
#[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,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_block: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
let (mut nodes, mut trunk_poses) = GiantTrunkPlacer::generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_block,
|
||||
);
|
||||
let mut i = height as i32 - 2 - random.next_bounded_i32(4);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use fancy::FancyTrunkPlacer;
|
||||
use pumpkin_data::tag;
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::{
|
||||
math::position::BlockPos,
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
@@ -9,12 +8,14 @@ use pumpkin_util::{
|
||||
use straight::StraightTrunkPlacer;
|
||||
|
||||
use super::{TreeFeature, TreeNode};
|
||||
use crate::generation::block_state_provider::BlockStateProvider;
|
||||
use crate::generation::feature::features::tree::trunk::{
|
||||
bending::BendingTrunkPlacer, cherry::CherryTrunkPlacer, dark_oak::DarkOakTrunkPlacer,
|
||||
forking::ForkingTrunkPlacer, giant::GiantTrunkPlacer, mega_jungle::MegaJungleTrunkPlacer,
|
||||
upwards_branching::UpwardsBranchingTrunkPlacer,
|
||||
};
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::world::BlockRegistryExt;
|
||||
|
||||
pub mod bending;
|
||||
pub mod cherry;
|
||||
@@ -42,18 +43,15 @@ impl TrunkPlacer {
|
||||
|
||||
pub fn set_dirt<T: GenerationCache>(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
chunk: &mut T,
|
||||
random: &mut RandomGenerator,
|
||||
pos: &BlockPos,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
) {
|
||||
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
|
||||
if force_dirt
|
||||
|| !(tag::Block::MINECRAFT_DIRT.1.contains(&block)
|
||||
&& block != Block::GRASS_BLOCK
|
||||
&& block != Block::MYCELIUM)
|
||||
if let Some(state) = below_trunk_provider.get_optional(block_registry, chunk, random, *pos)
|
||||
{
|
||||
chunk.set_block_state(&pos.0, dirt_state);
|
||||
chunk.set_block_state(&pos.0, state);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,22 +85,22 @@ impl TrunkPlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn generate<T: GenerationCache>(
|
||||
&self,
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
height: u32,
|
||||
start_pos: BlockPos,
|
||||
chunk: &mut T,
|
||||
random: &mut RandomGenerator,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_state: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
self.r#type.generate(
|
||||
block_registry,
|
||||
self,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
)
|
||||
}
|
||||
@@ -124,74 +122,75 @@ impl TrunkType {
|
||||
#[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,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_state: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
match self {
|
||||
Self::Straight(_) => StraightTrunkPlacer::generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
random,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
),
|
||||
Self::Forking(_) => (vec![], vec![]), // TODO
|
||||
Self::Giant(_) => GiantTrunkPlacer::generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
),
|
||||
Self::MegaJungle(_) => MegaJungleTrunkPlacer::generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
),
|
||||
Self::DarkOak(_) => DarkOakTrunkPlacer::generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
),
|
||||
Self::Fancy(_) => FancyTrunkPlacer::generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
),
|
||||
Self::Bending(bending) => bending.generate(
|
||||
block_registry,
|
||||
placer,
|
||||
height,
|
||||
start_pos,
|
||||
chunk,
|
||||
random,
|
||||
force_dirt,
|
||||
dirt_state,
|
||||
below_trunk_provider,
|
||||
trunk_state,
|
||||
),
|
||||
Self::UpwardsBranching(_) => (vec![], vec![]), // TODO
|
||||
|
||||
@@ -1,23 +1,34 @@
|
||||
use pumpkin_data::BlockState;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::random::RandomGenerator;
|
||||
|
||||
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;
|
||||
|
||||
pub struct StraightTrunkPlacer;
|
||||
|
||||
impl StraightTrunkPlacer {
|
||||
#[expect(clippy::too_many_arguments)]
|
||||
pub fn generate<T: GenerationCache>(
|
||||
block_registry: &dyn BlockRegistryExt,
|
||||
placer: &TrunkPlacer,
|
||||
height: u32,
|
||||
start_pos: BlockPos,
|
||||
chunk: &mut T,
|
||||
force_dirt: bool,
|
||||
dirt_state: &BlockState,
|
||||
random: &mut RandomGenerator,
|
||||
below_trunk_provider: &BlockStateProvider,
|
||||
trunk_state: &BlockState,
|
||||
) -> (Vec<TreeNode>, Vec<BlockPos>) {
|
||||
placer.set_dirt(chunk, &start_pos.down(), force_dirt, dirt_state);
|
||||
placer.set_dirt(
|
||||
block_registry,
|
||||
chunk,
|
||||
random,
|
||||
&start_pos.down(),
|
||||
below_trunk_provider,
|
||||
);
|
||||
let mut logs = Vec::new();
|
||||
for i in 0..height {
|
||||
let pos = start_pos.up_height(i as i32);
|
||||
|
||||
@@ -1,90 +1,90 @@
|
||||
// use pumpkin_data::noise_router::FindTopSurfaceData;
|
||||
// use pumpkin_util::math::vector3::Vector3;
|
||||
use pumpkin_data::noise_router::FindTopSurfaceData;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
|
||||
// use crate::generation::noise::router::{
|
||||
// chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
// chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl},
|
||||
// density_function::NoiseFunctionComponentRange,
|
||||
// };
|
||||
use crate::generation::noise::router::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
chunk_noise_router::{ChunkNoiseFunctionComponent, StaticChunkNoiseFunctionComponentImpl},
|
||||
density_function::NoiseFunctionComponentRange,
|
||||
};
|
||||
|
||||
// #[derive(Clone)]
|
||||
// pub struct FindTopSurface {
|
||||
// density_index: usize,
|
||||
// upper_bound_index: usize,
|
||||
// min_value: f64,
|
||||
// max_value: f64,
|
||||
// data: &'static FindTopSurfaceData,
|
||||
// }
|
||||
#[derive(Clone)]
|
||||
pub struct FindTopSurface {
|
||||
density_index: usize,
|
||||
upper_bound_index: usize,
|
||||
min_value: f64,
|
||||
max_value: f64,
|
||||
data: &'static FindTopSurfaceData,
|
||||
}
|
||||
|
||||
// impl FindTopSurface {
|
||||
// pub const fn new(
|
||||
// density_index: usize,
|
||||
// upper_bound_index: usize,
|
||||
// min_value: f64,
|
||||
// max_value: f64,
|
||||
// data: &'static FindTopSurfaceData,
|
||||
// ) -> Self {
|
||||
// Self {
|
||||
// density_index,
|
||||
// upper_bound_index,
|
||||
// min_value,
|
||||
// max_value,
|
||||
// data,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
impl FindTopSurface {
|
||||
pub const fn new(
|
||||
density_index: usize,
|
||||
upper_bound_index: usize,
|
||||
min_value: f64,
|
||||
max_value: f64,
|
||||
data: &'static FindTopSurfaceData,
|
||||
) -> Self {
|
||||
Self {
|
||||
density_index,
|
||||
upper_bound_index,
|
||||
min_value,
|
||||
max_value,
|
||||
data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// impl NoiseFunctionComponentRange for FindTopSurface {
|
||||
// #[inline]
|
||||
// fn min(&self) -> f64 {
|
||||
// self.min_value
|
||||
// }
|
||||
impl NoiseFunctionComponentRange for FindTopSurface {
|
||||
#[inline]
|
||||
fn min(&self) -> f64 {
|
||||
self.min_value
|
||||
}
|
||||
|
||||
// #[inline]
|
||||
// fn max(&self) -> f64 {
|
||||
// self.max_value
|
||||
// }
|
||||
// }
|
||||
#[inline]
|
||||
fn max(&self) -> f64 {
|
||||
self.max_value
|
||||
}
|
||||
}
|
||||
|
||||
// impl StaticChunkNoiseFunctionComponentImpl for FindTopSurface {
|
||||
// fn sample(
|
||||
// &self,
|
||||
// component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
// pos: &Vector3<i32>,
|
||||
// sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
// ) -> f64 {
|
||||
// let upper = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
// &mut component_stack[..=self.upper_bound_index],
|
||||
// pos,
|
||||
// sample_options,
|
||||
// );
|
||||
impl StaticChunkNoiseFunctionComponentImpl for FindTopSurface {
|
||||
fn sample(
|
||||
&self,
|
||||
component_stack: &mut [ChunkNoiseFunctionComponent],
|
||||
pos: &Vector3<i32>,
|
||||
sample_options: &ChunkNoiseFunctionSampleOptions,
|
||||
) -> f64 {
|
||||
let upper = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut component_stack[..=self.upper_bound_index],
|
||||
pos,
|
||||
sample_options,
|
||||
);
|
||||
|
||||
// let cell_height = self.data.cell_height;
|
||||
// let lower_bound = self.data.lower_bound;
|
||||
let cell_height = self.data.cell_height;
|
||||
let lower_bound = self.data.lower_bound;
|
||||
|
||||
// // Snap upper bound down to nearest cell boundary, matching Java:
|
||||
// // int topY = Mth.floor(this.upperBound.compute(context) / this.cellHeight) * this.cellHeight
|
||||
// let top_y = (upper / cell_height as f64).floor() as i32 * cell_height;
|
||||
// Snap upper bound down to nearest cell boundary, matching Java:
|
||||
// int topY = Mth.floor(this.upperBound.compute(context) / this.cellHeight) * this.cellHeight
|
||||
let top_y = (upper / cell_height as f64).floor() as i32 * cell_height;
|
||||
|
||||
// if top_y <= lower_bound {
|
||||
// return lower_bound as f64;
|
||||
// }
|
||||
if top_y <= lower_bound {
|
||||
return lower_bound as f64;
|
||||
}
|
||||
|
||||
// // Walk downward in cellHeight steps, return the first Y where density > 0.0
|
||||
// let mut y = top_y;
|
||||
// while y >= lower_bound {
|
||||
// let sample_pos = Vector3::new(pos.x, y, pos.z);
|
||||
// let density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
// &mut component_stack[..=self.density_index],
|
||||
// &sample_pos,
|
||||
// sample_options,
|
||||
// );
|
||||
// if density > 0.0 {
|
||||
// return y as f64;
|
||||
// }
|
||||
// y -= cell_height;
|
||||
// }
|
||||
// Walk downward in cellHeight steps, return the first Y where density > 0.0
|
||||
let mut y = top_y;
|
||||
while y >= lower_bound {
|
||||
let sample_pos = Vector3::new(pos.x, y, pos.z);
|
||||
let density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut component_stack[..=self.density_index],
|
||||
&sample_pos,
|
||||
sample_options,
|
||||
);
|
||||
if density > 0.0 {
|
||||
return y as f64;
|
||||
}
|
||||
y -= cell_height;
|
||||
}
|
||||
|
||||
// lower_bound as f64
|
||||
// }
|
||||
// }
|
||||
lower_bound as f64
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,12 @@ use pumpkin_data::{
|
||||
};
|
||||
use pumpkin_util::random::xoroshiro128::XoroshiroSplitter;
|
||||
|
||||
use crate::{GlobalRandomConfig, generation::noise::perlin::DoublePerlinNoiseSampler};
|
||||
use crate::{
|
||||
GlobalRandomConfig,
|
||||
generation::noise::{
|
||||
perlin::DoublePerlinNoiseSampler, router::find_top_surface::FindTopSurface,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
chunk_density_function::ChunkNoiseFunctionSampleOptions,
|
||||
@@ -45,6 +50,7 @@ pub enum DependentProtoNoiseFunctionComponent {
|
||||
Binary(Binary),
|
||||
ShiftedNoise(ShiftedNoise),
|
||||
WeirdScaled(WeirdScaled),
|
||||
FindTopSurface(FindTopSurface),
|
||||
Clamp(Clamp),
|
||||
RangeChoice(RangeChoice),
|
||||
Spline(SplineFunction),
|
||||
@@ -151,6 +157,24 @@ impl ProtoNoiseRouters {
|
||||
)),
|
||||
)
|
||||
}
|
||||
BaseNoiseFunctionComponent::FindTopSurface {
|
||||
density_index,
|
||||
upper_bound_index,
|
||||
data,
|
||||
} => {
|
||||
let min_value = data.lower_bound as f64;
|
||||
let max_value = stack[*upper_bound_index].max().max(min_value);
|
||||
|
||||
ProtoNoiseFunctionComponent::Dependent(
|
||||
DependentProtoNoiseFunctionComponent::FindTopSurface(FindTopSurface::new(
|
||||
*density_index,
|
||||
*upper_bound_index,
|
||||
min_value,
|
||||
max_value,
|
||||
data,
|
||||
)),
|
||||
)
|
||||
}
|
||||
BaseNoiseFunctionComponent::EndIslands => ProtoNoiseFunctionComponent::Independent(
|
||||
IndependentProtoNoiseFunctionComponent::EndIsland(EndIsland::new(
|
||||
random_config.seed,
|
||||
@@ -387,6 +411,13 @@ impl ProtoNoiseRouters {
|
||||
| UnaryOperation::Cube
|
||||
| UnaryOperation::QuarterNegative
|
||||
| UnaryOperation::HalfNegative => (applied_min_value, applied_max_value),
|
||||
UnaryOperation::Invert => {
|
||||
if arg1_min < 0.0 && arg1_max > 0.0 {
|
||||
(f64::NEG_INFINITY, f64::INFINITY)
|
||||
} else {
|
||||
(applied_max_value, applied_min_value)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ProtoNoiseFunctionComponent::Dependent(
|
||||
|
||||
@@ -79,33 +79,25 @@ impl<'a> SurfaceHeightEstimateSampler<'a> {
|
||||
}
|
||||
|
||||
fn calculate_height_estimate(&mut self, aligned_x: i32, aligned_z: i32) -> i32 {
|
||||
let mut low = self.minimum_y;
|
||||
let mut high = self.maximum_y;
|
||||
let mut result = i32::MAX;
|
||||
|
||||
let sample_options =
|
||||
ChunkNoiseFunctionSampleOptions::new(false, SampleAction::SkipCellCaches, 0, 0, 0);
|
||||
|
||||
while low <= high {
|
||||
let mid = low + ((high - low) / 2);
|
||||
let stepped_mid = mid - (mid % self.y_level_step_count as i32);
|
||||
// preliminarySurfaceLevel (FindTopSurface) returns the surface Y directly.
|
||||
// Sample at y=0 — FindTopSurface ignores the incoming Y and computes its own.
|
||||
let pos = Vector3::new(aligned_x, 0, aligned_z);
|
||||
let surface_y = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut self.component_stack,
|
||||
&pos,
|
||||
&sample_options,
|
||||
);
|
||||
|
||||
let pos = Vector3::new(aligned_x, stepped_mid, aligned_z);
|
||||
let density = ChunkNoiseFunctionComponent::sample_from_stack(
|
||||
&mut self.component_stack,
|
||||
&pos,
|
||||
&sample_options,
|
||||
);
|
||||
|
||||
if density > Self::NOTCHIAN_SAMPLE_CUTOFF {
|
||||
result = stepped_mid;
|
||||
low = stepped_mid + self.y_level_step_count as i32;
|
||||
} else {
|
||||
high = stepped_mid - self.y_level_step_count as i32;
|
||||
}
|
||||
// FindTopSurface returns lowerBound as f64 when no solid block found,
|
||||
// which is below minimum_y — treat that as "no surface"
|
||||
if surface_y <= self.minimum_y as f64 {
|
||||
i32::MAX
|
||||
} else {
|
||||
surface_y as i32
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use pumpkin_data::tag::{RegistryKey, get_tag_ids};
|
||||
use pumpkin_data::tag::{self};
|
||||
|
||||
use crate::block::RawBlockState;
|
||||
|
||||
pub struct TagMatchRuleTest {
|
||||
pub tag: String,
|
||||
pub tag: tag::Tag,
|
||||
}
|
||||
|
||||
impl TagMatchRuleTest {
|
||||
#[must_use]
|
||||
pub fn test(&self, state: RawBlockState) -> bool {
|
||||
let values = get_tag_ids(RegistryKey::Block, &self.tag).unwrap();
|
||||
values.contains(&state.to_block_id())
|
||||
self.tag.1.contains(&state.to_block_id())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use pumpkin_data::block_properties::{
|
||||
BambooLeaves, BambooLikeProperties, BlockProperties, EnumVariants, Integer0To1,
|
||||
};
|
||||
use pumpkin_data::item::Item;
|
||||
use pumpkin_data::tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON;
|
||||
use pumpkin_data::tag::Block::MINECRAFT_SUPPORTS_BAMBOO;
|
||||
use pumpkin_data::tag::Taggable;
|
||||
use pumpkin_data::tag::{self};
|
||||
use pumpkin_data::{Block, BlockDirection};
|
||||
@@ -34,7 +34,7 @@ impl BlockBehaviour for BambooBlock {
|
||||
.get_block_and_state_id(&args.position.down())
|
||||
.await;
|
||||
|
||||
if block_below.has_tag(&MINECRAFT_BAMBOO_PLANTABLE_ON) {
|
||||
if block_below.has_tag(&MINECRAFT_SUPPORTS_BAMBOO) {
|
||||
let mut props = BambooLikeProperties::from_state_id(
|
||||
Block::BAMBOO.default_state.id,
|
||||
&Block::BAMBOO,
|
||||
@@ -277,7 +277,7 @@ impl PlantBlockBase for BambooBlock {
|
||||
pos: &pumpkin_util::math::position::BlockPos,
|
||||
) -> bool {
|
||||
let block = block_accessor.get_block(pos).await;
|
||||
block.has_tag(&tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
|
||||
block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_BAMBOO)
|
||||
}
|
||||
|
||||
async fn can_place_at(&self, block_accessor: &dyn BlockAccessor, block_pos: &BlockPos) -> bool {
|
||||
|
||||
@@ -114,6 +114,6 @@ impl PlantBlockBase for BambooSaplingBlock {
|
||||
|
||||
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
|
||||
let block = block_accessor.get_block(pos).await;
|
||||
block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_BAMBOO_PLANTABLE_ON)
|
||||
block.has_tag(&pumpkin_data::tag::Block::MINECRAFT_SUPPORTS_BAMBOO)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,6 @@ impl PlantBlockBase for DryVegetationBlock {
|
||||
block_pos: &BlockPos,
|
||||
) -> bool {
|
||||
let block_below = block_accessor.get_block(block_pos).await;
|
||||
block_below.has_tag(&tag::Block::MINECRAFT_DRY_VEGETATION_MAY_PLACE_ON)
|
||||
block_below.has_tag(&tag::Block::MINECRAFT_SUPPORTS_DRY_VEGETATION)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ pub mod wither_rose;
|
||||
trait PlantBlockBase {
|
||||
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
|
||||
let block = block_accessor.get_block(pos).await;
|
||||
block.has_tag(&tag::Block::MINECRAFT_DIRT) || block == &Block::FARMLAND
|
||||
block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_VEGETATION)
|
||||
}
|
||||
|
||||
async fn get_state_for_neighbor_update(
|
||||
|
||||
@@ -42,7 +42,7 @@ impl BlockBehaviour for MushroomPlantBlock {
|
||||
impl PlantBlockBase for MushroomPlantBlock {
|
||||
async fn can_plant_on_top(&self, block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool {
|
||||
let block = block_accessor.get_block(pos).await;
|
||||
block.has_tag(&tag::Block::MINECRAFT_MUSHROOM_GROW_BLOCK)
|
||||
block.has_tag(&tag::Block::MINECRAFT_OVERRIDES_MUSHROOM_LIGHT_REQUIREMENT)
|
||||
// TODO: Check light level and isOpaqueFullCube
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user