feat: more chunk gen work
This commit is contained in:
parent
3a3ba2c873
commit
015091de12
File diff suppressed because one or more lines are too long
@ -454,6 +454,10 @@ impl GenerationCache for Cache {
|
|||||||
fn is_air(&self, local_pos: &Vector3<i32>) -> bool {
|
fn is_air(&self, local_pos: &Vector3<i32>) -> bool {
|
||||||
is_air(GenerationCache::get_block_state(self, local_pos))
|
is_air(GenerationCache::get_block_state(self, local_pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_sea_level(&self) -> i32 {
|
||||||
|
self.get_center_chunk().get_sea_level()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Cache {
|
impl Cache {
|
||||||
|
|||||||
@ -38,8 +38,9 @@ impl BlockStateProvider {
|
|||||||
Self::Simple(provider) => Some(provider.get(pos)),
|
Self::Simple(provider) => Some(provider.get(pos)),
|
||||||
Self::Weighted(provider) => Some(provider.get(random)),
|
Self::Weighted(provider) => Some(provider.get(random)),
|
||||||
Self::DualNoise(provider) => Some(provider.get(pos)),
|
Self::DualNoise(provider) => Some(provider.get(pos)),
|
||||||
Self::Pillar(provider) => Some(provider.get(pos)),
|
Self::Pillar(provider) => Some(provider.get_with_random(random, pos)),
|
||||||
Self::RandomizedInt(_) | Self::Rule(_) => None,
|
Self::RandomizedInt(provider) => provider.get_for_bonemeal(random, pos),
|
||||||
|
Self::Rule(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,10 +56,8 @@ impl BlockStateProvider {
|
|||||||
Self::NoiseThreshold(provider) => provider.get(random, pos),
|
Self::NoiseThreshold(provider) => provider.get(random, pos),
|
||||||
Self::NoiseProvider(provider) => provider.get(pos),
|
Self::NoiseProvider(provider) => provider.get(pos),
|
||||||
Self::DualNoise(provider) => provider.get(pos),
|
Self::DualNoise(provider) => provider.get(pos),
|
||||||
Self::Pillar(provider) => provider.get(pos),
|
Self::Pillar(provider) => provider.get_with_random(random, pos),
|
||||||
Self::RandomizedInt(provider) => {
|
Self::RandomizedInt(provider) => provider.get_state_for_world(world, random, pos),
|
||||||
provider.source.get_state_for_world(world, random, pos)
|
|
||||||
}
|
|
||||||
Self::Rule(provider) => {
|
Self::Rule(provider) => {
|
||||||
for rule in &provider.rules {
|
for rule in &provider.rules {
|
||||||
if rule.if_true.test_world(world, None, &pos) {
|
if rule.if_true.test_world(world, None, &pos) {
|
||||||
@ -86,7 +85,7 @@ impl BlockStateProvider {
|
|||||||
Self::Simple(provider) => provider.get(pos),
|
Self::Simple(provider) => provider.get(pos),
|
||||||
Self::Weighted(provider) => provider.get(random),
|
Self::Weighted(provider) => provider.get(random),
|
||||||
Self::DualNoise(provider) => provider.get(pos),
|
Self::DualNoise(provider) => provider.get(pos),
|
||||||
Self::Pillar(provider) => provider.get(pos),
|
Self::Pillar(provider) => provider.get_with_random(random, pos),
|
||||||
Self::RandomizedInt(provider) => provider.get(random, pos, chunk, block_registry),
|
Self::RandomizedInt(provider) => provider.get(random, pos, chunk, block_registry),
|
||||||
Self::Rule(provider) => provider.get(block_registry, chunk, random, pos),
|
Self::Rule(provider) => provider.get(block_registry, chunk, random, pos),
|
||||||
}
|
}
|
||||||
@ -170,6 +169,63 @@ pub struct RandomizedIntBlockStateProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RandomizedIntBlockStateProvider {
|
impl RandomizedIntBlockStateProvider {
|
||||||
|
fn apply_property(
|
||||||
|
&self,
|
||||||
|
base_state: &'static BlockState,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
) -> &'static BlockState {
|
||||||
|
let value = self.values.get(random);
|
||||||
|
let val_str = value.to_string();
|
||||||
|
let block = Block::from_id(base_state.id.to_block_id());
|
||||||
|
block.properties(base_state.id).map_or_else(
|
||||||
|
|| {
|
||||||
|
let props = [(self.property.as_str(), val_str.as_str())];
|
||||||
|
let new_state_id = block.from_properties(&props).to_state_id(block);
|
||||||
|
BlockState::from_id(new_state_id)
|
||||||
|
},
|
||||||
|
|props_source| {
|
||||||
|
let mut props: Vec<(&str, &str)> = props_source
|
||||||
|
.to_props()
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (*k, *v))
|
||||||
|
.collect();
|
||||||
|
let mut found = false;
|
||||||
|
for (k, v) in &mut props {
|
||||||
|
if *k == self.property {
|
||||||
|
*v = &val_str;
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
props.push((&self.property, &val_str));
|
||||||
|
}
|
||||||
|
let new_state_id = block.from_properties(&props).to_state_id(block);
|
||||||
|
BlockState::from_id(new_state_id)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_for_bonemeal(
|
||||||
|
&self,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
) -> Option<&'static BlockState> {
|
||||||
|
self.source
|
||||||
|
.get_for_bonemeal(random, pos)
|
||||||
|
.map(|state| self.apply_property(state, random))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_state_for_world(
|
||||||
|
&self,
|
||||||
|
world: &dyn BlockAccessor,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
) -> &'static BlockState {
|
||||||
|
let state = self.source.get_state_for_world(world, random, pos);
|
||||||
|
self.apply_property(state, random)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get<T: GenerationCache>(
|
pub fn get<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
@ -177,8 +233,8 @@ impl RandomizedIntBlockStateProvider {
|
|||||||
chunk: &T,
|
chunk: &T,
|
||||||
block_registry: &dyn WorldPortalExt,
|
block_registry: &dyn WorldPortalExt,
|
||||||
) -> &'static BlockState {
|
) -> &'static BlockState {
|
||||||
// TODO
|
let state = self.source.get(random, pos, chunk, block_registry);
|
||||||
self.source.get(random, pos, chunk, block_registry)
|
self.apply_property(state, random)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +245,33 @@ pub struct PillarBlockStateProvider {
|
|||||||
impl PillarBlockStateProvider {
|
impl PillarBlockStateProvider {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub const fn get(&self, _pos: BlockPos) -> &'static BlockState {
|
pub const fn get(&self, _pos: BlockPos) -> &'static BlockState {
|
||||||
// TODO: random axis
|
self.state
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_with_random(
|
||||||
|
&self,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
_pos: BlockPos,
|
||||||
|
) -> &'static BlockState {
|
||||||
|
let axis_str = match random.next_bounded_i32(3) {
|
||||||
|
0 => "x",
|
||||||
|
1 => "y",
|
||||||
|
_ => "z",
|
||||||
|
};
|
||||||
|
let block = Block::from_id(self.state.id.to_block_id());
|
||||||
|
if let Some(props_source) = block.properties(self.state.id) {
|
||||||
|
let prop_list = props_source.to_props();
|
||||||
|
if prop_list.iter().any(|(k, _)| *k == "axis") {
|
||||||
|
let new_props: Vec<(&str, &str)> = prop_list
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (*k, if *k == "axis" { axis_str } else { *v }))
|
||||||
|
.collect();
|
||||||
|
let new_props_obj = block.from_properties(new_props.as_slice());
|
||||||
|
let new_state_id = new_props_obj.to_state_id(block);
|
||||||
|
return BlockState::from_id(new_state_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
self.state
|
self.state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -422,21 +422,45 @@ impl ConfiguredFeature {
|
|||||||
Self::BonusChest(_feature) => {
|
Self::BonusChest(_feature) => {
|
||||||
BonusChestFeature::generate(chunk, min_y, height, feature_name, random, pos)
|
BonusChestFeature::generate(chunk, min_y, height, feature_name, random, pos)
|
||||||
}
|
}
|
||||||
Self::DeltaFeature(_feature) => {
|
Self::DeltaFeature(feature) => {
|
||||||
DeltaFeatureFeature::generate(chunk, min_y, height, feature_name, random, pos)
|
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||||
}
|
}
|
||||||
|
Self::BlockPile(feature) => feature.generate(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
min_y,
|
||||||
|
height,
|
||||||
|
feature_name,
|
||||||
|
random,
|
||||||
|
pos,
|
||||||
|
),
|
||||||
Self::DripstoneCluster(feature) => feature.generate(chunk, pos),
|
Self::DripstoneCluster(feature) => feature.generate(chunk, pos),
|
||||||
Self::LargeDripstone(feature) => feature.generate(chunk, random, pos),
|
Self::LargeDripstone(feature) => feature.generate(chunk, random, pos),
|
||||||
Self::EndGateway(feature) => feature.generate(chunk, pos),
|
Self::EndGateway(feature) => feature.generate(chunk, pos),
|
||||||
Self::FillLayer(feature) => {
|
Self::FillLayer(feature) => {
|
||||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||||
}
|
}
|
||||||
|
Self::FallenTree(feature) => feature.generate(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
min_y,
|
||||||
|
height,
|
||||||
|
feature_name,
|
||||||
|
random,
|
||||||
|
pos,
|
||||||
|
),
|
||||||
Self::HugeBrownMushroom(feature) => {
|
Self::HugeBrownMushroom(feature) => {
|
||||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||||
}
|
}
|
||||||
Self::HugeFungus(feature) => {
|
Self::HugeFungus(feature) => feature.generate(
|
||||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
chunk,
|
||||||
}
|
block_registry,
|
||||||
|
min_y,
|
||||||
|
height,
|
||||||
|
feature_name,
|
||||||
|
random,
|
||||||
|
pos,
|
||||||
|
),
|
||||||
Self::HugeRedMushroom(feature) => {
|
Self::HugeRedMushroom(feature) => {
|
||||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||||
}
|
}
|
||||||
@ -452,7 +476,7 @@ impl ConfiguredFeature {
|
|||||||
Self::WeepingVines(feature) => {
|
Self::WeepingVines(feature) => {
|
||||||
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
feature.generate(chunk, min_y, height, feature_name, random, pos)
|
||||||
}
|
}
|
||||||
_ => false,
|
Self::NoOp => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,65 +23,88 @@ impl BambooFeature {
|
|||||||
block_registry: &dyn WorldPortalExt,
|
block_registry: &dyn WorldPortalExt,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut i = 0;
|
let mut placed = 0;
|
||||||
if chunk.is_air(&pos.0) {
|
if chunk.is_air(&pos.0)
|
||||||
if block_registry.can_place_at(&Block::BAMBOO, Block::BAMBOO.default_state, chunk, &pos)
|
&& block_registry.can_place_at(&Block::BAMBOO, Block::BAMBOO.default_state, chunk, &pos)
|
||||||
{
|
{
|
||||||
let height = random.next_bounded_i32(12) + 5;
|
let height = random.next_bounded_i32(12) + 5;
|
||||||
if random.next_f32() < self.probability {
|
if random.next_f32() < self.probability {
|
||||||
let rnd = random.next_bounded_i32(4) + 1;
|
let r = random.next_bounded_i32(4) + 1;
|
||||||
for x in pos.0.x - rnd..pos.0.x + rnd {
|
for xx in pos.0.x - r..=pos.0.x + r {
|
||||||
for z in pos.0.z - rnd..pos.0.z + rnd {
|
for zz in pos.0.z - r..=pos.0.z + r {
|
||||||
let block_below =
|
let xd = xx - pos.0.x;
|
||||||
BlockPos::new(x, chunk.top_block_height_exclusive(x, z) - 1, z);
|
let zd = zz - pos.0.z;
|
||||||
let block = GenerationCache::get_block_state(chunk, &block_below.0);
|
if xd * xd + zd * zd <= r * r {
|
||||||
if !block.to_block_id().has_tag(tag::Block::MINECRAFT_DIRT) {
|
let podzol_y = chunk.top_block_height_exclusive(xx, zz) - 1;
|
||||||
continue;
|
let podzol_pos = BlockPos::new(xx, podzol_y, zz);
|
||||||
|
let block_id = GenerationCache::get_block_state(chunk, &podzol_pos.0)
|
||||||
|
.to_block_id();
|
||||||
|
if block_id
|
||||||
|
.has_tag(tag::Block::MINECRAFT_BENEATH_BAMBOO_PODZOL_REPLACEABLE)
|
||||||
|
{
|
||||||
|
chunk.set_block_state(&podzol_pos.0, Block::PODZOL.default_state);
|
||||||
}
|
}
|
||||||
chunk.set_block_state(&block_below.0, Block::PODZOL.default_state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut bpos = pos;
|
}
|
||||||
let bamboo = Block::BAMBOO.default_state;
|
|
||||||
for _ in 0..height {
|
|
||||||
if chunk.is_air(&bpos.0) {
|
|
||||||
chunk.set_block_state(&bpos.0, bamboo);
|
|
||||||
bpos = bpos.up();
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Top block
|
|
||||||
if bpos.0.y - pos.0.y >= 3 {
|
|
||||||
let mut props = BambooLikeProperties::default(&Block::BAMBOO);
|
|
||||||
props.leaves = BambooLeaves::Large;
|
|
||||||
props.stage = 1;
|
|
||||||
|
|
||||||
chunk.set_block_state(
|
let trunk_props = BambooLikeProperties {
|
||||||
&bpos.0,
|
age: 1,
|
||||||
BlockState::from_id(props.to_state_id(&Block::BAMBOO)),
|
leaves: BambooLeaves::None,
|
||||||
);
|
stage: 0,
|
||||||
props.stage = 0;
|
};
|
||||||
|
let trunk_state = BlockState::from_id(trunk_props.to_state_id(&Block::BAMBOO));
|
||||||
|
|
||||||
chunk.set_block_state(
|
let mut bpos = pos;
|
||||||
&bpos.down().0,
|
for _ in 0..height {
|
||||||
BlockState::from_id(props.to_state_id(&Block::BAMBOO)),
|
if chunk.is_air(&bpos.0) {
|
||||||
);
|
chunk.set_block_state(&bpos.0, trunk_state);
|
||||||
props.leaves = BambooLeaves::Small;
|
bpos = bpos.up();
|
||||||
|
} else {
|
||||||
chunk.set_block_state(
|
break;
|
||||||
&bpos.down().down().0,
|
|
||||||
BlockState::from_id(props.to_state_id(&Block::BAMBOO)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
|
||||||
|
if bpos.0.y - pos.0.y >= 3 {
|
||||||
|
let final_large_props = BambooLikeProperties {
|
||||||
|
age: 1,
|
||||||
|
leaves: BambooLeaves::Large,
|
||||||
|
stage: 1,
|
||||||
|
};
|
||||||
|
chunk.set_block_state(
|
||||||
|
&bpos.down().0,
|
||||||
|
BlockState::from_id(final_large_props.to_state_id(&Block::BAMBOO)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let top_large_props = BambooLikeProperties {
|
||||||
|
age: 1,
|
||||||
|
leaves: BambooLeaves::Large,
|
||||||
|
stage: 0,
|
||||||
|
};
|
||||||
|
chunk.set_block_state(
|
||||||
|
&bpos.down().down().0,
|
||||||
|
BlockState::from_id(top_large_props.to_state_id(&Block::BAMBOO)),
|
||||||
|
);
|
||||||
|
|
||||||
|
let top_small_props = BambooLikeProperties {
|
||||||
|
age: 1,
|
||||||
|
leaves: BambooLeaves::Small,
|
||||||
|
stage: 0,
|
||||||
|
};
|
||||||
|
chunk.set_block_state(
|
||||||
|
&bpos.down().down().down().0,
|
||||||
|
BlockState::from_id(top_small_props.to_state_id(&Block::BAMBOO)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
placed += 1;
|
||||||
}
|
}
|
||||||
i > 0
|
|
||||||
|
placed > 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,6 @@ use pumpkin_util::{
|
|||||||
|
|
||||||
use crate::{generation::proto_chunk::GenerationCache, world::WorldPortalExt};
|
use crate::{generation::proto_chunk::GenerationCache, world::WorldPortalExt};
|
||||||
|
|
||||||
const LAVA_SEA_LEVEL: i32 = 32; // TODO: use getSeaLevel() instead of hardcoding this
|
|
||||||
const CLUSTERED_REACH: i32 = 5;
|
const CLUSTERED_REACH: i32 = 5;
|
||||||
const UNCLUSTERED_REACH: i32 = 8;
|
const UNCLUSTERED_REACH: i32 = 8;
|
||||||
const CLUSTERED_SIZE: i32 = 50;
|
const CLUSTERED_SIZE: i32 = 50;
|
||||||
@ -143,7 +142,7 @@ fn find_air<T: GenerationCache>(chunk: &T, pos: BlockPos, mut limit: i32) -> Opt
|
|||||||
|
|
||||||
fn is_air_or_lava_ocean<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
fn is_air_or_lava_ocean<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
is_air(state) || (state.to_block_id() == Block::LAVA.id && pos.0.y <= LAVA_SEA_LEVEL)
|
is_air(state) || (state.to_block_id() == Block::LAVA.id && pos.0.y <= chunk.get_sea_level())
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn is_cannot_place_on(id: BlockId) -> bool {
|
const fn is_cannot_place_on(id: BlockId) -> bool {
|
||||||
|
|||||||
@ -1 +1,75 @@
|
|||||||
pub struct BlockPileFeature;
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::generation::block_state_provider::BlockStateProvider;
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
use crate::world::WorldPortalExt;
|
||||||
|
|
||||||
|
pub struct BlockPileFeature {
|
||||||
|
pub state_provider: BlockStateProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockPileFeature {
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
min_y: i8,
|
||||||
|
_height: u16,
|
||||||
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
) -> bool {
|
||||||
|
if pos.0.y < min_y as i32 + 5 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xr = 2 + random.next_bounded_i32(2);
|
||||||
|
let zr = 2 + random.next_bounded_i32(2);
|
||||||
|
|
||||||
|
for block_pos in BlockPos::iterate(pos.add(-xr, 0, -zr), pos.add(xr, 1, zr)) {
|
||||||
|
let xd = pos.0.x - block_pos.0.x;
|
||||||
|
let zd = pos.0.z - block_pos.0.z;
|
||||||
|
if (xd * xd + zd * zd) as f32 <= random.next_f32() * 10.0 - random.next_f32() * 6.0
|
||||||
|
|| random.next_f32() < 0.031
|
||||||
|
{
|
||||||
|
self.try_place_block(chunk, block_registry, block_pos, random);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn may_place_on<T: GenerationCache>(
|
||||||
|
chunk: &T,
|
||||||
|
block_pos: BlockPos,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
) -> bool {
|
||||||
|
let below = block_pos.down();
|
||||||
|
let below_state = GenerationCache::get_block_state(chunk, &below.0).to_state();
|
||||||
|
if below_state.id == Block::DIRT_PATH.default_state.id {
|
||||||
|
random.next_bool()
|
||||||
|
} else {
|
||||||
|
below_state.is_side_solid(BlockDirection::Up)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_place_block<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
block_pos: BlockPos,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
) {
|
||||||
|
if chunk.is_air(&block_pos.0) && Self::may_place_on(chunk, block_pos, random) {
|
||||||
|
let state = self
|
||||||
|
.state_provider
|
||||||
|
.get(random, block_pos, chunk, block_registry);
|
||||||
|
chunk.set_block_state(&block_pos.0, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::position::BlockPos,
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
@ -6,8 +6,6 @@ use pumpkin_util::{
|
|||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
const SEA_LEVEL: i32 = 63; // TODO: use getSeaLevel() instead of hardcoding
|
|
||||||
|
|
||||||
pub struct BlueIceFeature;
|
pub struct BlueIceFeature;
|
||||||
|
|
||||||
impl BlueIceFeature {
|
impl BlueIceFeature {
|
||||||
@ -16,7 +14,7 @@ impl BlueIceFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if pos.0.y >= SEA_LEVEL - 1 {
|
if pos.0.y > chunk.get_sea_level() - 1 {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,64 +25,57 @@ impl BlueIceFeature {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut has_ice_neighbor = false;
|
let mut found_packed_ice = false;
|
||||||
for neighbor_pos in [
|
for dir in BlockDirection::all() {
|
||||||
pos.up(),
|
if dir != BlockDirection::Down {
|
||||||
pos.down(),
|
let neighbor_pos = pos.offset(dir.to_offset());
|
||||||
pos.north(),
|
if GenerationCache::get_block_state(chunk, &neighbor_pos.0).to_block_id()
|
||||||
pos.south(),
|
== Block::PACKED_ICE.id
|
||||||
pos.east(),
|
{
|
||||||
pos.west(),
|
found_packed_ice = true;
|
||||||
] {
|
break;
|
||||||
if GenerationCache::get_block_state(chunk, &neighbor_pos.0).to_state()
|
}
|
||||||
== Block::ICE.default_state
|
|
||||||
{
|
|
||||||
has_ice_neighbor = true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_ice_neighbor {
|
if !found_packed_ice {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
chunk.set_block_state(&pos.0, Block::BLUE_ICE.default_state);
|
chunk.set_block_state(&pos.0, Block::BLUE_ICE.default_state);
|
||||||
|
|
||||||
for _ in 0..200 {
|
for _ in 0..200 {
|
||||||
let offset_x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
let y_off = random.next_bounded_i32(5) - random.next_bounded_i32(6);
|
||||||
let offset_y = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
let mut xz_diff = 3;
|
||||||
let offset_z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
if y_off < 2 {
|
||||||
let target_pos = pos.add(offset_x, offset_y, offset_z);
|
xz_diff += y_off / 2;
|
||||||
|
|
||||||
if chunk.out_of_height(target_pos.0.y as i16) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let target_state = GenerationCache::get_block_state(chunk, &target_pos.0).to_state();
|
if xz_diff >= 1 {
|
||||||
if target_state.is_air()
|
let place_pos = pos.add(
|
||||||
|| target_state == Block::WATER.default_state
|
random.next_bounded_i32(xz_diff) - random.next_bounded_i32(xz_diff),
|
||||||
|| target_state == Block::ICE.default_state
|
y_off,
|
||||||
|| target_state == Block::PACKED_ICE.default_state
|
random.next_bounded_i32(xz_diff) - random.next_bounded_i32(xz_diff),
|
||||||
{
|
);
|
||||||
let mut has_blue_ice_neighbor = false;
|
if chunk.out_of_height(place_pos.0.y as i16) {
|
||||||
for neighbor_pos in [
|
continue;
|
||||||
target_pos.up(),
|
|
||||||
target_pos.down(),
|
|
||||||
target_pos.north(),
|
|
||||||
target_pos.south(),
|
|
||||||
target_pos.east(),
|
|
||||||
target_pos.west(),
|
|
||||||
] {
|
|
||||||
if GenerationCache::get_block_state(chunk, &neighbor_pos.0).to_state()
|
|
||||||
== Block::BLUE_ICE.default_state
|
|
||||||
{
|
|
||||||
has_blue_ice_neighbor = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if has_blue_ice_neighbor {
|
let place_state = GenerationCache::get_block_state(chunk, &place_pos.0).to_state();
|
||||||
chunk.set_block_state(&target_pos.0, Block::BLUE_ICE.default_state);
|
if place_state.is_air()
|
||||||
|
|| place_state == Block::WATER.default_state
|
||||||
|
|| place_state == Block::PACKED_ICE.default_state
|
||||||
|
|| place_state == Block::ICE.default_state
|
||||||
|
{
|
||||||
|
for dir in BlockDirection::all() {
|
||||||
|
let rel_pos = place_pos.offset(dir.to_offset());
|
||||||
|
if GenerationCache::get_block_state(chunk, &rel_pos.0).to_block_id()
|
||||||
|
== Block::BLUE_ICE.id
|
||||||
|
{
|
||||||
|
chunk.set_block_state(&place_pos.0, Block::BLUE_ICE.default_state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,10 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
use pumpkin_util::{
|
||||||
|
HeightMap,
|
||||||
|
math::{position::BlockPos, vector3::Vector3},
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
@ -11,28 +16,57 @@ impl BonusChestFeature {
|
|||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
_random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut block_pos = pos;
|
let chunk_min_x = (pos.0.x >> 4) << 4;
|
||||||
while chunk.is_air(&block_pos.0) && block_pos.0.y > chunk.bottom_y() as i32 + 2 {
|
let chunk_min_z = (pos.0.z >> 4) << 4;
|
||||||
block_pos = block_pos.down();
|
|
||||||
|
let mut x_poses: Vec<i32> = (chunk_min_x..=chunk_min_x + 15).collect();
|
||||||
|
let mut z_poses: Vec<i32> = (chunk_min_z..=chunk_min_z + 15).collect();
|
||||||
|
|
||||||
|
for i in (1..x_poses.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32(i as i32 + 1) as usize;
|
||||||
|
x_poses.swap(i, j);
|
||||||
|
}
|
||||||
|
for i in (1..z_poses.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32(i as i32 + 1) as usize;
|
||||||
|
z_poses.swap(i, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
let chest_pos = block_pos.up();
|
for &x in &x_poses {
|
||||||
if !chunk.is_air(&chest_pos.0) {
|
for &z in &z_poses {
|
||||||
return false;
|
let y = chunk.get_top_y(&HeightMap::MotionBlockingNoLeaves, x, z);
|
||||||
}
|
let chest_pos = Vector3::new(x, y, z);
|
||||||
|
if chunk.is_air(&chest_pos) {
|
||||||
|
chunk.set_block_state(&chest_pos, Block::CHEST.default_state);
|
||||||
|
|
||||||
chunk.set_block_state(&chest_pos.0, Block::CHEST.default_state);
|
let loot_seed = random.next_i64();
|
||||||
|
let mut chest_nbt = NbtCompound::new();
|
||||||
|
chest_nbt.put_string("id", "minecraft:chest".to_string());
|
||||||
|
chest_nbt.put_int("x", chest_pos.x);
|
||||||
|
chest_nbt.put_int("y", chest_pos.y);
|
||||||
|
chest_nbt.put_int("z", chest_pos.z);
|
||||||
|
chest_nbt.put_string(
|
||||||
|
"LootTable",
|
||||||
|
"minecraft:chests/spawn_bonus_chest".to_string(),
|
||||||
|
);
|
||||||
|
chest_nbt.put_long("LootTableSeed", loot_seed);
|
||||||
|
chunk.add_block_entity(&chest_pos, chest_nbt);
|
||||||
|
|
||||||
let torch_offsets = [(1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)];
|
for dir in BlockDirection::horizontal() {
|
||||||
for &(dx, dy, dz) in &torch_offsets {
|
let torch_pos = chest_pos.add(&dir.to_offset());
|
||||||
let t_pos = BlockPos::new(chest_pos.0.x + dx, chest_pos.0.y + dy, chest_pos.0.z + dz);
|
let below_torch = torch_pos.add(&Vector3::new(0, -1, 0));
|
||||||
if chunk.is_air(&t_pos.0) {
|
if !chunk.is_air(&below_torch) && chunk.is_air(&torch_pos) {
|
||||||
chunk.set_block_state(&t_pos.0, Block::TORCH.default_state);
|
chunk.set_block_state(&torch_pos, Block::TORCH.default_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
|
||||||
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,53 +16,69 @@ impl CoralClawFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// First lets get a random coral
|
|
||||||
let block = CoralFeature::get_random_tag_entry(tag::Block::MINECRAFT_CORAL_BLOCKS, random);
|
let block = CoralFeature::get_random_tag_entry(tag::Block::MINECRAFT_CORAL_BLOCKS, random);
|
||||||
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, pos) {
|
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, pos) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let direction = BlockDirection::random_horizontal(random);
|
let claw_dir =
|
||||||
let i = random.next_bounded_i32(2) + 2;
|
BlockDirection::from_cardinal_direction(BlockDirection::random_horizontal(random));
|
||||||
// TODO: vanilla iterates the first `i` of Util.toShuffledList([direction,
|
let n_branches = random.next_bounded_i32(2) + 2;
|
||||||
// direction.getClockWise(), direction.getCounterClockWise()], random) — the
|
let mut possible_directions = [
|
||||||
// shuffle consumes RNG draws and the opposite of `direction` is never visited.
|
claw_dir,
|
||||||
let directions = BlockDirection::horizontal_worldgen()
|
claw_dir.rotate_clockwise(),
|
||||||
.into_iter()
|
claw_dir.rotate_counter_clockwise(),
|
||||||
.take(i as usize);
|
];
|
||||||
'block0: for direction2 in directions {
|
for i in (1..possible_directions.len()).rev() {
|
||||||
let mut pos = pos;
|
let j = random.next_bounded_i32(i as i32 + 1) as usize;
|
||||||
let j = random.next_bounded_i32(2) + 1;
|
possible_directions.swap(i, j);
|
||||||
pos = pos.offset(direction2.to_offset());
|
}
|
||||||
|
|
||||||
let branch_direction;
|
for &branch_direction in possible_directions.iter().take(n_branches as usize) {
|
||||||
|
let mut mut_pos = pos;
|
||||||
|
let sideway_length = random.next_bounded_i32(2) + 1;
|
||||||
|
mut_pos = mut_pos.offset(branch_direction.to_offset());
|
||||||
|
|
||||||
let k = if direction2 == direction {
|
let (segment_direction, inway_length) = if branch_direction == claw_dir {
|
||||||
branch_direction = direction;
|
(claw_dir, random.next_bounded_i32(3) + 2)
|
||||||
random.next_bounded_i32(3) + 2
|
|
||||||
} else {
|
} else {
|
||||||
pos = pos.up();
|
mut_pos = mut_pos.up();
|
||||||
//let _directions = [direction2, BlockDirection::Up];
|
let seg_dir = if random.next_bool() {
|
||||||
branch_direction = direction2; // TODO: make this random
|
branch_direction
|
||||||
random.next_bounded_i32(3) + 5
|
} else {
|
||||||
|
BlockDirection::Up
|
||||||
|
};
|
||||||
|
(seg_dir, random.next_bounded_i32(3) + 3)
|
||||||
};
|
};
|
||||||
|
|
||||||
for _ in 0..j {
|
for _ in 0..sideway_length {
|
||||||
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, pos) {
|
if !CoralFeature::generate_coral_piece(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
block,
|
||||||
|
mut_pos,
|
||||||
|
) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos = pos.offset(branch_direction.to_offset());
|
mut_pos = mut_pos.offset(segment_direction.to_offset());
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = pos.offset(branch_direction.to_offset());
|
mut_pos = mut_pos.offset(segment_direction.opposite().to_offset());
|
||||||
pos = pos.up();
|
mut_pos = mut_pos.up();
|
||||||
|
|
||||||
for _l in 0..k {
|
for _ in 0..inway_length {
|
||||||
pos = pos.offset(direction.opposite().to_offset());
|
mut_pos = mut_pos.offset(claw_dir.to_offset());
|
||||||
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, pos) {
|
if !CoralFeature::generate_coral_piece(
|
||||||
continue 'block0;
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
block,
|
||||||
|
mut_pos,
|
||||||
|
) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if random.next_f32() < 0.25 {
|
if random.next_f32() < 0.25 {
|
||||||
pos = pos.up();
|
mut_pos = mut_pos.up();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
use crate::{generation::proto_chunk::GenerationCache, world::WorldPortalExt};
|
use crate::{generation::proto_chunk::GenerationCache, world::WorldPortalExt};
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{position::BlockPos, vector3::Vector3},
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -10,47 +10,47 @@ use super::CoralFeature;
|
|||||||
pub struct CoralMushroomFeature;
|
pub struct CoralMushroomFeature;
|
||||||
|
|
||||||
impl CoralMushroomFeature {
|
impl CoralMushroomFeature {
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
block_registry: &dyn WorldPortalExt,
|
block_registry: &dyn WorldPortalExt,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// First lets get a random coral
|
|
||||||
let block = CoralFeature::get_random_tag_entry(tag::Block::MINECRAFT_CORAL_BLOCKS, random);
|
let block = CoralFeature::get_random_tag_entry(tag::Block::MINECRAFT_CORAL_BLOCKS, random);
|
||||||
|
|
||||||
let i = random.next_bounded_i32(3) + 3;
|
let height = random.next_bounded_i32(3) + 3;
|
||||||
let j = random.next_bounded_i32(3) + 3;
|
let width = random.next_bounded_i32(3) + 3;
|
||||||
let k = random.next_bounded_i32(3) + 3;
|
let length = random.next_bounded_i32(3) + 3;
|
||||||
let l = random.next_bounded_i32(3) + 1;
|
let sink_value = random.next_bounded_i32(3) + 1;
|
||||||
|
|
||||||
for m in 0..=j {
|
for x in 0..=width {
|
||||||
for n in 0..=i {
|
for y in 0..=height {
|
||||||
for o in 0..=k {
|
for z in 0..=length {
|
||||||
let mut pos = pos;
|
let cur_pos = BlockPos::new(pos.0.x + x, pos.0.y + y - sink_value, pos.0.z + z);
|
||||||
pos = pos.offset(Vector3::new(pos.0.x + m, pos.0.y + n, pos.0.z + o));
|
|
||||||
pos = pos.down_height(l);
|
|
||||||
|
|
||||||
let condition_a = (m != 0 && m != j) || (n != 0 && n != i);
|
let condition_a = (x != 0 && x != width) || (y != 0 && y != height);
|
||||||
let condition_b = (o != 0 && o != k) || (n != 0 && n != i);
|
let condition_b = (z != 0 && z != length) || (y != 0 && y != height);
|
||||||
let condition_c = (m != 0 && m != j) || (o != 0 && o != k);
|
let condition_c = (x != 0 && x != width) || (z != 0 && z != length);
|
||||||
let condition_d = m == 0 || m == j || n == 0 || n == i || o == 0 || o == k;
|
let condition_d =
|
||||||
let random_check = random.next_f32() < 0.1f32;
|
x == 0 || x == width || y == 0 || y == height || z == 0 || z == length;
|
||||||
|
|
||||||
if !((condition_a && condition_b && condition_c && condition_d)
|
if condition_a
|
||||||
&& !random_check
|
&& condition_b
|
||||||
&& CoralFeature::generate_coral_piece(
|
&& condition_c
|
||||||
|
&& condition_d
|
||||||
|
&& random.next_f32() >= 0.1
|
||||||
|
{
|
||||||
|
CoralFeature::generate_coral_piece(
|
||||||
chunk,
|
chunk,
|
||||||
block_registry,
|
block_registry,
|
||||||
random,
|
random,
|
||||||
block,
|
block,
|
||||||
pos,
|
cur_pos,
|
||||||
))
|
);
|
||||||
{}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,49 +10,59 @@ use super::CoralFeature;
|
|||||||
pub struct CoralTreeFeature;
|
pub struct CoralTreeFeature;
|
||||||
|
|
||||||
impl CoralTreeFeature {
|
impl CoralTreeFeature {
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
block_registry: &dyn WorldPortalExt,
|
block_registry: &dyn WorldPortalExt,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
// First lets get a random coral
|
|
||||||
let block = CoralFeature::get_random_tag_entry(tag::Block::MINECRAFT_CORAL_BLOCKS, random);
|
let block = CoralFeature::get_random_tag_entry(tag::Block::MINECRAFT_CORAL_BLOCKS, random);
|
||||||
let mut pos = pos;
|
let mut cur_pos = pos;
|
||||||
let i = random.next_bounded_i32(3) + 1;
|
let trunk_height = random.next_bounded_i32(3) + 1;
|
||||||
for _ in 0..i {
|
for _ in 0..trunk_height {
|
||||||
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, pos) {
|
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, cur_pos) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
pos = pos.up();
|
cur_pos = cur_pos.up();
|
||||||
}
|
}
|
||||||
let i = random.next_bounded_i32(3) + 2;
|
|
||||||
|
|
||||||
// TODO: vanilla takes the first `i` of Plane.HORIZONTAL.shuffledCopy(random) — a
|
let trunk_top_pos = cur_pos;
|
||||||
// Fisher–Yates that must run over the horizontal_worldgen() [N, E, S, W] base
|
let n_branches = random.next_bounded_i32(3) + 2;
|
||||||
// order and consumes RNG draws.
|
|
||||||
let directions = BlockDirection::horizontal_worldgen()
|
let mut directions: Vec<BlockDirection> = BlockDirection::horizontal_worldgen()
|
||||||
.into_iter()
|
.iter()
|
||||||
.take(i as usize);
|
.copied()
|
||||||
for dir in directions {
|
.map(BlockDirection::from_cardinal_direction)
|
||||||
pos = pos.offset(dir.to_offset());
|
.collect();
|
||||||
let times = random.next_bounded_i32(5) + 2;
|
for i in (1..directions.len()).rev() {
|
||||||
let mut m = 0;
|
let j = random.next_bounded_i32(i as i32 + 1) as usize;
|
||||||
for n in 0..times {
|
directions.swap(i, j);
|
||||||
if !CoralFeature::generate_coral_piece(chunk, block_registry, random, block, pos) {
|
}
|
||||||
|
|
||||||
|
for &branch_direction in directions.iter().take(n_branches as usize) {
|
||||||
|
let mut branch_pos = trunk_top_pos.offset(branch_direction.to_offset());
|
||||||
|
let branch_height = random.next_bounded_i32(5) + 2;
|
||||||
|
let mut segment_length = 0;
|
||||||
|
|
||||||
|
for j in 0..branch_height {
|
||||||
|
if !CoralFeature::generate_coral_piece(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
block,
|
||||||
|
branch_pos,
|
||||||
|
) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
pos = pos.up();
|
segment_length += 1;
|
||||||
m += 1;
|
branch_pos = branch_pos.up();
|
||||||
if n != 0 && (m < 2 || random.next_f32() >= 0.25) {
|
if j == 0 || (segment_length >= 2 && random.next_f32() < 0.25) {
|
||||||
continue;
|
branch_pos = branch_pos.offset(branch_direction.to_offset());
|
||||||
|
segment_length = 0;
|
||||||
}
|
}
|
||||||
pos = pos.offset(dir.to_offset());
|
|
||||||
m = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|||||||
@ -24,9 +24,10 @@ impl CoralFeature {
|
|||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
|
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
|
||||||
let above_block = GenerationCache::get_block_state(chunk, &pos.up().0).to_block_id();
|
let above_pos = pos.up();
|
||||||
|
let above_block = GenerationCache::get_block_state(chunk, &above_pos.0).to_block_id();
|
||||||
|
|
||||||
if block != BlockId::WATER && !block.has_tag(tag::Block::MINECRAFT_CORALS)
|
if (block != BlockId::WATER && !block.has_tag(tag::Block::MINECRAFT_CORALS))
|
||||||
|| above_block != BlockId::WATER
|
|| above_block != BlockId::WATER
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -41,18 +42,22 @@ impl CoralFeature {
|
|||||||
Block::from_state_id(block_to_place_state.id),
|
Block::from_state_id(block_to_place_state.id),
|
||||||
block_to_place_state,
|
block_to_place_state,
|
||||||
chunk,
|
chunk,
|
||||||
&pos,
|
&above_pos,
|
||||||
) {
|
) {
|
||||||
chunk.set_block_state(&pos.0, block_to_place_state);
|
chunk.set_block_state(&above_pos.0, block_to_place_state);
|
||||||
}
|
}
|
||||||
} else if random.next_f32() < 0.05 {
|
} else if random.next_f32() < 0.05 {
|
||||||
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
||||||
props.pickles = (random.next_bounded_i32(4) as u8) + 1;
|
props.pickles = (random.next_bounded_i32(4) as u8) + 1;
|
||||||
let state_id = props.to_state_id(&Block::SEA_PICKLE);
|
let state_id = props.to_state_id(&Block::SEA_PICKLE);
|
||||||
let block_state = BlockState::from_id(state_id);
|
let block_state = BlockState::from_id(state_id);
|
||||||
if block_registry.can_place_at(Block::from_state_id(state_id), block_state, chunk, &pos)
|
if block_registry.can_place_at(
|
||||||
{
|
Block::from_state_id(state_id),
|
||||||
chunk.set_block_state(&pos.0, block_state);
|
block_state,
|
||||||
|
chunk,
|
||||||
|
&above_pos,
|
||||||
|
) {
|
||||||
|
chunk.set_block_state(&above_pos.0, block_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for dir in BlockDirection::horizontal_worldgen() {
|
for dir in BlockDirection::horizontal_worldgen() {
|
||||||
@ -68,7 +73,6 @@ impl CoralFeature {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let original_props = &properties.to_props();
|
let original_props = &properties.to_props();
|
||||||
// Set the right Axis
|
|
||||||
let props: Vec<(&str, &str)> = original_props
|
let props: Vec<(&str, &str)> = original_props
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(key, value)| {
|
.map(|(key, value)| {
|
||||||
|
|||||||
@ -1,12 +1,21 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{BlockDirection, BlockId, BlockState};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator, random::RandomImpl};
|
use pumpkin_util::{
|
||||||
|
math::{int_provider::IntProvider, position::BlockPos},
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct DeltaFeatureFeature;
|
pub struct DeltaFeatureFeature {
|
||||||
|
pub contents: &'static BlockState,
|
||||||
|
pub rim: &'static BlockState,
|
||||||
|
pub size: IntProvider,
|
||||||
|
pub rim_size: IntProvider,
|
||||||
|
}
|
||||||
|
|
||||||
impl DeltaFeatureFeature {
|
impl DeltaFeatureFeature {
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
@ -14,32 +23,81 @@ impl DeltaFeatureFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let size = random.next_bounded_i32(3) + 2;
|
let mut any_placed = false;
|
||||||
let mut generated = false;
|
let spawn_rim = random.next_f64() < 0.9;
|
||||||
|
let rim_x = if spawn_rim {
|
||||||
|
self.rim_size.get(random)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let rim_z = if spawn_rim {
|
||||||
|
self.rim_size.get(random)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
let has_rim = spawn_rim && rim_x != 0 && rim_z != 0;
|
||||||
|
let radius_x = self.size.get(random);
|
||||||
|
let radius_z = self.size.get(random);
|
||||||
|
let radius_limit = radius_x.max(radius_z);
|
||||||
|
|
||||||
for dx in -size..=size {
|
for pos_iter in BlockPos::iterate_outwards(pos, radius_x, 0, radius_z) {
|
||||||
for dy in -size..=size {
|
if pos_iter.manhattan_distance(pos) > radius_limit {
|
||||||
for dz in -size..=size {
|
break;
|
||||||
if dx * dx + dy * dy + dz * dz <= size * size {
|
}
|
||||||
let target = BlockPos::new(pos.0.x + dx, pos.0.y + dy, pos.0.z + dz);
|
|
||||||
let block_id =
|
if Self::is_clear(chunk, pos_iter, self.contents) {
|
||||||
GenerationCache::get_block_state(chunk, &target.0).to_block_id();
|
if has_rim {
|
||||||
if block_id == Block::NETHERRACK.id
|
any_placed = true;
|
||||||
|| block_id == Block::LAVA.id
|
chunk.set_block_state(&pos_iter.0, self.rim);
|
||||||
|| block_id == Block::AIR.id
|
}
|
||||||
{
|
|
||||||
let replacement = if random.next_f32() < 0.6 {
|
let pos_offset = pos_iter.add(rim_x, 0, rim_z);
|
||||||
Block::BASALT
|
if Self::is_clear(chunk, pos_offset, self.contents) {
|
||||||
} else {
|
any_placed = true;
|
||||||
Block::BLACKSTONE
|
chunk.set_block_state(&pos_offset.0, self.contents);
|
||||||
};
|
|
||||||
chunk.set_block_state(&target.0, replacement.default_state);
|
|
||||||
generated = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generated
|
|
||||||
|
any_placed
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_clear<T: GenerationCache>(
|
||||||
|
chunk: &T,
|
||||||
|
pos: BlockPos,
|
||||||
|
contents: &'static BlockState,
|
||||||
|
) -> bool {
|
||||||
|
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
|
if state.to_block_id() == contents.id.to_block_id() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_cannot_replace(state.to_block_id()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir in BlockDirection::all() {
|
||||||
|
let neighbor_state =
|
||||||
|
GenerationCache::get_block_state(chunk, &pos.offset(dir.to_offset()).0);
|
||||||
|
let is_air = neighbor_state.to_state().is_air();
|
||||||
|
if (is_air && dir != BlockDirection::Up) || (!is_air && dir == BlockDirection::Up) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn is_cannot_replace(id: BlockId) -> bool {
|
||||||
|
matches!(
|
||||||
|
id,
|
||||||
|
BlockId::BEDROCK
|
||||||
|
| BlockId::NETHER_BRICKS
|
||||||
|
| BlockId::NETHER_BRICK_FENCE
|
||||||
|
| BlockId::NETHER_BRICK_STAIRS
|
||||||
|
| BlockId::NETHER_WART
|
||||||
|
| BlockId::CHEST
|
||||||
|
| BlockId::SPAWNER
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@ -1,7 +1,8 @@
|
|||||||
use pumpkin_data::{Block, BlockDirection};
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{position::BlockPos, vector3::Vector3},
|
math::{position::BlockPos, vector3::Vector3},
|
||||||
random::RandomGenerator,
|
random::{RandomGenerator, RandomImpl},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::generation::{noise::WATER_BLOCK, proto_chunk::GenerationCache};
|
use crate::generation::{noise::WATER_BLOCK, proto_chunk::GenerationCache};
|
||||||
@ -19,8 +20,8 @@ impl DesertWellFeature {
|
|||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
_random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
const CAN_GENERATE: Block = Block::SAND;
|
const CAN_GENERATE: Block = Block::SAND;
|
||||||
@ -29,7 +30,7 @@ impl DesertWellFeature {
|
|||||||
while chunk.is_air(&block_pos.0) && block_pos.0.y > chunk.bottom_y() as i32 + 2 {
|
while chunk.is_air(&block_pos.0) && block_pos.0.y > chunk.bottom_y() as i32 + 2 {
|
||||||
block_pos = block_pos.down();
|
block_pos = block_pos.down();
|
||||||
}
|
}
|
||||||
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
|
let block = GenerationCache::get_block_state(chunk, &block_pos.0).to_block_id();
|
||||||
if CAN_GENERATE.id != block {
|
if CAN_GENERATE.id != block {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -139,6 +140,33 @@ impl DesertWellFeature {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let water_center = block_pos.0;
|
||||||
|
let water_positions = [
|
||||||
|
water_center,
|
||||||
|
water_center.add(&Vector3::new(1, 0, 0)),
|
||||||
|
water_center.add(&Vector3::new(0, 0, 1)),
|
||||||
|
water_center.add(&Vector3::new(-1, 0, 0)),
|
||||||
|
water_center.add(&Vector3::new(0, 0, -1)),
|
||||||
|
];
|
||||||
|
let pos1 =
|
||||||
|
water_positions[random.next_bounded_i32(5) as usize].add(&Vector3::new(0, -1, 0));
|
||||||
|
let pos2 =
|
||||||
|
water_positions[random.next_bounded_i32(5) as usize].add(&Vector3::new(0, -2, 0));
|
||||||
|
Self::place_sus_sand(chunk, &pos1);
|
||||||
|
Self::place_sus_sand(chunk, &pos2);
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn place_sus_sand<T: GenerationCache>(chunk: &mut T, pos: &Vector3<i32>) {
|
||||||
|
chunk.set_block_state(pos, Block::SUSPICIOUS_SAND.default_state);
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:brushable_block".to_string());
|
||||||
|
nbt.put_int("x", pos.x);
|
||||||
|
nbt.put_int("y", pos.y);
|
||||||
|
nbt.put_int("z", pos.z);
|
||||||
|
nbt.put_string("LootTable", "minecraft:archaeology/desert_well".to_string());
|
||||||
|
nbt.put_long("LootTableSeed", BlockPos(*pos).as_long());
|
||||||
|
chunk.add_block_entity(pos, nbt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::block_properties::{
|
||||||
use pumpkin_data::BlockId;
|
PointedDripstoneLikeProperties, SpeleothemThickness, VerticalDirection,
|
||||||
|
};
|
||||||
use pumpkin_data::tag;
|
use pumpkin_data::tag;
|
||||||
|
use pumpkin_data::{Block, BlockDirection, BlockId, BlockState};
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
|
|
||||||
pub mod cluster;
|
pub mod cluster;
|
||||||
@ -20,3 +22,55 @@ pub(super) fn gen_dripstone<T: GenerationCache>(chunk: &mut T, pos: BlockPos) ->
|
|||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn is_empty_or_water<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
|
let block = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
|
||||||
|
block == BlockId::AIR
|
||||||
|
|| block == BlockId::CAVE_AIR
|
||||||
|
|| block == BlockId::VOID_AIR
|
||||||
|
|| block == BlockId::WATER
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn grow_pointed_dripstone<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
start_pos: BlockPos,
|
||||||
|
tip_direction: BlockDirection,
|
||||||
|
height: i32,
|
||||||
|
merged_tip: bool,
|
||||||
|
) {
|
||||||
|
let mut cur = start_pos;
|
||||||
|
let vert_dir = match tip_direction {
|
||||||
|
BlockDirection::Down => VerticalDirection::Down,
|
||||||
|
_ => VerticalDirection::Up,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut thicknesses = Vec::with_capacity(height as usize);
|
||||||
|
if height >= 3 {
|
||||||
|
thicknesses.push(SpeleothemThickness::Base);
|
||||||
|
for _ in 0..(height - 3) {
|
||||||
|
thicknesses.push(SpeleothemThickness::Middle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if height >= 2 {
|
||||||
|
thicknesses.push(SpeleothemThickness::Frustum);
|
||||||
|
}
|
||||||
|
if height >= 1 {
|
||||||
|
thicknesses.push(if merged_tip {
|
||||||
|
SpeleothemThickness::TipMerge
|
||||||
|
} else {
|
||||||
|
SpeleothemThickness::Tip
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for thickness in thicknesses {
|
||||||
|
let is_water =
|
||||||
|
GenerationCache::get_block_state(chunk, &cur.0).to_block_id() == BlockId::WATER;
|
||||||
|
let mut props = PointedDripstoneLikeProperties::default(&Block::POINTED_DRIPSTONE);
|
||||||
|
props.thickness = thickness;
|
||||||
|
props.vertical_direction = vert_dir;
|
||||||
|
props.waterlogged = is_water;
|
||||||
|
let state_id = props.to_state_id(&Block::POINTED_DRIPSTONE);
|
||||||
|
chunk.set_block_state(&cur.0, BlockState::from_id(state_id));
|
||||||
|
cur = cur.offset(tip_direction.to_offset());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -20,9 +20,17 @@ impl SmallDripstoneFeature {
|
|||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if let Some(dir) = Self::get_direction(chunk, pos, random) {
|
if let Some(dir) = Self::get_direction(chunk, pos, random) {
|
||||||
let pos = pos.offset(dir.opposite().to_offset());
|
let root_pos = pos.offset(dir.opposite().to_offset());
|
||||||
self.gen_dripstone_blocks(chunk, pos, random);
|
self.gen_dripstone_blocks(chunk, root_pos, random);
|
||||||
// TODO
|
let next_pos = pos.offset(dir.to_offset());
|
||||||
|
let height = if random.next_f32() < self.taller_dripstone
|
||||||
|
&& super::is_empty_or_water(chunk, next_pos)
|
||||||
|
{
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
super::grow_pointed_dripstone(chunk, pos, dir, height, false);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|||||||
@ -44,7 +44,6 @@ impl EndPodiumFeature {
|
|||||||
let mut oy = origin.0.y;
|
let mut oy = origin.0.y;
|
||||||
let oz = origin.0.z;
|
let oz = origin.0.z;
|
||||||
|
|
||||||
// Podium spans [ox - 4, ox + 4] and [oz - 4, oz + 4]
|
|
||||||
if ox + 4 < min_x || ox - 4 > max_x || oz + 4 < min_z || oz - 4 > max_z {
|
if ox + 4 < min_x || ox - 4 > max_x || oz + 4 < min_z || oz - 4 > max_z {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -76,7 +75,7 @@ impl EndPodiumFeature {
|
|||||||
if y < oy {
|
if y < oy {
|
||||||
if closer_than_2_5 {
|
if closer_than_2_5 {
|
||||||
chunk.set_block_state(&target, Block::BEDROCK.default_state);
|
chunk.set_block_state(&target, Block::BEDROCK.default_state);
|
||||||
} else if self.active {
|
} else {
|
||||||
chunk.set_block_state(&target, Block::END_STONE.default_state);
|
chunk.set_block_state(&target, Block::END_STONE.default_state);
|
||||||
}
|
}
|
||||||
} else if y > oy {
|
} else if y > oy {
|
||||||
|
|||||||
@ -182,7 +182,6 @@ impl EndSpikeFeature {
|
|||||||
chunk.set_block_state(&pos.0, Block::AIR.default_state);
|
chunk.set_block_state(&pos.0, Block::AIR.default_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bedrock cap serves as the crystal base, fire sits on top of it
|
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&pumpkin_util::math::vector3::Vector3::new(
|
&pumpkin_util::math::vector3::Vector3::new(
|
||||||
spike.center_x,
|
spike.center_x,
|
||||||
@ -200,12 +199,10 @@ impl EndSpikeFeature {
|
|||||||
Block::FIRE.default_state,
|
Block::FIRE.default_state,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Iron-bar cage for guarded spikes: 5x5 walls + open top frame at dy=3.
|
|
||||||
if spike.guarded {
|
if spike.guarded {
|
||||||
for dy in 0i32..=3 {
|
for dy in 0i32..=3 {
|
||||||
for dx in -2i32..=2 {
|
for dx in -2i32..=2 {
|
||||||
for dz in -2i32..=2 {
|
for dz in -2i32..=2 {
|
||||||
// Only place on perimeter walls and the top frame
|
|
||||||
let x_wall_present = dx.abs() == 2;
|
let x_wall_present = dx.abs() == 2;
|
||||||
let z_wall_present = dz.abs() == 2;
|
let z_wall_present = dz.abs() == 2;
|
||||||
let on_top = dy == 3;
|
let on_top = dy == 3;
|
||||||
@ -213,15 +210,14 @@ impl EndSpikeFeature {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connectivity rules
|
|
||||||
let x_edge = x_wall_present || on_top;
|
let x_edge = x_wall_present || on_top;
|
||||||
let z_edge = z_wall_present || on_top;
|
let z_edge = z_wall_present || on_top;
|
||||||
|
|
||||||
let mut props = OakFenceLikeProperties::default(&Block::IRON_BARS);
|
let mut props = OakFenceLikeProperties::default(&Block::IRON_BARS);
|
||||||
props.north = x_edge && dz != 2;
|
props.north = x_edge && dz != -2;
|
||||||
props.south = x_edge && dz != -2;
|
props.south = x_edge && dz != 2;
|
||||||
props.west = z_edge && dx != 2;
|
props.west = z_edge && dx != -2;
|
||||||
props.east = z_edge && dx != -2;
|
props.east = z_edge && dx != 2;
|
||||||
|
|
||||||
let bar_state = BlockState::from_id(props.to_state_id(&Block::IRON_BARS));
|
let bar_state = BlockState::from_id(props.to_state_id(&Block::IRON_BARS));
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
|
|||||||
@ -1,29 +1,204 @@
|
|||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_data::{Block, BlockDirection, BlockState};
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::{int_provider::IntProvider, position::BlockPos},
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{ProtoChunk, generation::block_state_provider::BlockStateProvider};
|
use super::tree::{TreeFeature, decorator::TreeDecorator};
|
||||||
|
use crate::{
|
||||||
|
generation::{block_state_provider::BlockStateProvider, proto_chunk::GenerationCache},
|
||||||
|
world::WorldPortalExt,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct FallenTreeFeature {
|
pub struct FallenTreeFeature {
|
||||||
pub trunk_provider: BlockStateProvider,
|
pub trunk_provider: BlockStateProvider,
|
||||||
|
pub log_length: IntProvider,
|
||||||
|
pub stump_decorators: Vec<TreeDecorator>,
|
||||||
|
pub log_decorators: Vec<TreeDecorator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FallenTreeFeature {
|
impl FallenTreeFeature {
|
||||||
pub const fn generate(
|
#[allow(clippy::too_many_arguments)]
|
||||||
_chunk: &mut ProtoChunk,
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
_random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
_pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
false
|
self.place_stump(chunk, block_registry, random, pos);
|
||||||
|
let direction =
|
||||||
|
BlockDirection::from_cardinal_direction(BlockDirection::random_horizontal(random));
|
||||||
|
let log_length = self.log_length.get(random) - 2;
|
||||||
|
let mut log_start_pos =
|
||||||
|
pos.offset(direction.to_offset() * (2 + random.next_bounded_i32(2)));
|
||||||
|
Self::set_ground_height_for_fallen_log_start_pos(chunk, &mut log_start_pos);
|
||||||
|
if Self::can_place_entire_fallen_log(chunk, log_length, log_start_pos, direction) {
|
||||||
|
self.place_fallen_log(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
log_length,
|
||||||
|
log_start_pos,
|
||||||
|
direction,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// fn gen_stump(&self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, pos: BlockPos) {
|
fn set_ground_height_for_fallen_log_start_pos<T: GenerationCache>(
|
||||||
// // chunk.set_block_state(
|
chunk: &T,
|
||||||
// // pos.0.x,
|
log_start_pos: &mut BlockPos,
|
||||||
// // pos.0.y,
|
) {
|
||||||
// // pos.0.z,
|
*log_start_pos = log_start_pos.up();
|
||||||
// // self.trunk_provider.get(random, pos, chunk),
|
for _ in 0..6 {
|
||||||
// // );
|
if Self::may_place_on(chunk, *log_start_pos) {
|
||||||
// }
|
return;
|
||||||
|
}
|
||||||
|
*log_start_pos = log_start_pos.down();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_stump<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
stump_pos: BlockPos,
|
||||||
|
) {
|
||||||
|
let stump = self.place_log_block(chunk, block_registry, random, stump_pos, None);
|
||||||
|
Self::decorate_logs(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
&[stump],
|
||||||
|
&self.stump_decorators,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_place_entire_fallen_log<T: GenerationCache>(
|
||||||
|
chunk: &T,
|
||||||
|
log_length: i32,
|
||||||
|
mut log_start_pos: BlockPos,
|
||||||
|
direction: BlockDirection,
|
||||||
|
) -> bool {
|
||||||
|
let mut gap_in_ground = 0;
|
||||||
|
let offset = direction.to_offset();
|
||||||
|
|
||||||
|
for _ in 0..log_length {
|
||||||
|
let state = GenerationCache::get_block_state(chunk, &log_start_pos.0);
|
||||||
|
if !TreeFeature::can_replace(state.to_state(), state.to_block_id()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if Self::is_over_solid_ground(chunk, log_start_pos) {
|
||||||
|
gap_in_ground = 0;
|
||||||
|
} else {
|
||||||
|
gap_in_ground += 1;
|
||||||
|
if gap_in_ground > 2 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_start_pos = log_start_pos.offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_fallen_log<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
log_length: i32,
|
||||||
|
mut log_start_pos: BlockPos,
|
||||||
|
direction: BlockDirection,
|
||||||
|
) {
|
||||||
|
let mut fallen_log = Vec::with_capacity(log_length.max(0) as usize);
|
||||||
|
let offset = direction.to_offset();
|
||||||
|
|
||||||
|
for _ in 0..log_length {
|
||||||
|
let placed = self.place_log_block(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
log_start_pos,
|
||||||
|
Some(direction),
|
||||||
|
);
|
||||||
|
fallen_log.push(placed);
|
||||||
|
log_start_pos = log_start_pos.offset(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::decorate_logs(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
&fallen_log,
|
||||||
|
&self.log_decorators,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn may_place_on<T: GenerationCache>(chunk: &T, block_pos: BlockPos) -> bool {
|
||||||
|
let state = GenerationCache::get_block_state(chunk, &block_pos.0);
|
||||||
|
TreeFeature::can_replace(state.to_state(), state.to_block_id())
|
||||||
|
&& Self::is_over_solid_ground(chunk, block_pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_over_solid_ground<T: GenerationCache>(chunk: &T, block_pos: BlockPos) -> bool {
|
||||||
|
let below = block_pos.down();
|
||||||
|
GenerationCache::get_block_state(chunk, &below.0)
|
||||||
|
.to_state()
|
||||||
|
.is_side_solid(BlockDirection::Up)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_log_block<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
block_pos: BlockPos,
|
||||||
|
direction: Option<BlockDirection>,
|
||||||
|
) -> BlockPos {
|
||||||
|
let mut state = self
|
||||||
|
.trunk_provider
|
||||||
|
.get(random, block_pos, chunk, block_registry);
|
||||||
|
if let Some(dir) = direction {
|
||||||
|
let block = Block::from_state_id(state.id);
|
||||||
|
let axis_str = match dir {
|
||||||
|
BlockDirection::East | BlockDirection::West => "x",
|
||||||
|
BlockDirection::North | BlockDirection::South => "z",
|
||||||
|
_ => "y",
|
||||||
|
};
|
||||||
|
if let Some(props) = block.properties(state.id) {
|
||||||
|
let prop_list = props.to_props();
|
||||||
|
if prop_list.iter().any(|(k, _)| *k == "axis") {
|
||||||
|
let new_props: Vec<(&str, &str)> = prop_list
|
||||||
|
.iter()
|
||||||
|
.map(|(k, v)| (*k, if *k == "axis" { axis_str } else { *v }))
|
||||||
|
.collect();
|
||||||
|
let new_props_obj = block.from_properties(new_props.as_slice());
|
||||||
|
let new_state_id = new_props_obj.to_state_id(block);
|
||||||
|
state = BlockState::from_id(new_state_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
chunk.set_block_state(&block_pos.0, state);
|
||||||
|
block_pos
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decorate_logs<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
logs: &[BlockPos],
|
||||||
|
decorators: &[TreeDecorator],
|
||||||
|
) {
|
||||||
|
for decorator in decorators {
|
||||||
|
decorator.generate(chunk, block_registry, random, &[], logs, &[]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,25 +1,30 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::BlockState;
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct FillLayerFeature;
|
pub struct FillLayerFeature {
|
||||||
|
pub height: i32,
|
||||||
|
pub state: &'static BlockState,
|
||||||
|
}
|
||||||
|
|
||||||
impl FillLayerFeature {
|
impl FillLayerFeature {
|
||||||
#[allow(clippy::unused_self)]
|
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
_random: &mut RandomGenerator,
|
_random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
for dx in -1..=1 {
|
let y = min_y as i32 + self.height;
|
||||||
for dz in -1..=1 {
|
for dx in 0..16 {
|
||||||
let target = BlockPos::new(pos.0.x + dx, pos.0.y, pos.0.z + dz);
|
for dz in 0..16 {
|
||||||
chunk.set_block_state(&target.0, Block::SNOW.default_state);
|
let target = BlockPos::new(pos.0.x + dx, y, pos.0.z + dz);
|
||||||
|
if chunk.is_air(&target.0) {
|
||||||
|
chunk.set_block_state(&target.0, self.state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
|
|||||||
@ -4,8 +4,6 @@ use pumpkin_data::{Block, BlockState};
|
|||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::random::RandomGenerator;
|
use pumpkin_util::random::RandomGenerator;
|
||||||
|
|
||||||
const SEA_LEVEL: i32 = 63; // TODO: getSeaLevel() from the worldgen context instead of hardcoding
|
|
||||||
|
|
||||||
pub struct FreezeTopLayerFeature;
|
pub struct FreezeTopLayerFeature;
|
||||||
|
|
||||||
impl FreezeTopLayerFeature {
|
impl FreezeTopLayerFeature {
|
||||||
@ -42,9 +40,10 @@ impl FreezeTopLayerFeature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Snow check
|
// Snow check
|
||||||
let top_temp = biome
|
let top_temp =
|
||||||
.weather
|
biome
|
||||||
.compute_temperature(x as f64, y, z as f64, SEA_LEVEL);
|
.weather
|
||||||
|
.compute_temperature(x as f64, y, z as f64, chunk.get_sea_level());
|
||||||
|
|
||||||
if top_temp < 0.15 {
|
if top_temp < 0.15 {
|
||||||
let top_raw = GenerationCache::get_block_state(chunk, &top_vec);
|
let top_raw = GenerationCache::get_block_state(chunk, &top_vec);
|
||||||
|
|||||||
@ -1,56 +1,246 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{Block, BlockState};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator, random::RandomImpl};
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use super::weeping_vines::WeepingVinesFeature;
|
||||||
|
use crate::{
|
||||||
|
generation::{block_predicate::BlockPredicate, proto_chunk::GenerationCache},
|
||||||
|
world::WorldPortalExt,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct HugeFungusFeature;
|
pub struct HugeFungusFeature {
|
||||||
|
pub valid_base_block: &'static BlockState,
|
||||||
|
pub stem_state: &'static BlockState,
|
||||||
|
pub hat_state: &'static BlockState,
|
||||||
|
pub decor_state: &'static BlockState,
|
||||||
|
pub replaceable_blocks: BlockPredicate,
|
||||||
|
pub planted: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl HugeFungusFeature {
|
impl HugeFungusFeature {
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let height = random.next_bounded_i32(4) + 6;
|
let below = pos.down();
|
||||||
let is_warped = random.next_bool();
|
let below_block = GenerationCache::get_block_state(chunk, &below.0).to_block_id();
|
||||||
|
let valid_base_id = Block::from_state_id(self.valid_base_block.id).id;
|
||||||
let stem_state = if is_warped {
|
if below_block != valid_base_id {
|
||||||
Block::WARPED_STEM.default_state
|
return false;
|
||||||
} else {
|
|
||||||
Block::CRIMSON_STEM.default_state
|
|
||||||
};
|
|
||||||
let wart_state = if is_warped {
|
|
||||||
Block::WARPED_WART_BLOCK.default_state
|
|
||||||
} else {
|
|
||||||
Block::NETHER_WART_BLOCK.default_state
|
|
||||||
};
|
|
||||||
|
|
||||||
for i in 0..height {
|
|
||||||
let stem_pos = BlockPos::new(pos.0.x, pos.0.y + i, pos.0.z);
|
|
||||||
chunk.set_block_state(&stem_pos.0, stem_state);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let cap_y = pos.0.y + height - 2;
|
let mut total_height = random.next_inbetween_i32(4, 13);
|
||||||
for dy in 0..=3 {
|
if random.next_bounded_i32(12) == 0 {
|
||||||
let radius = if dy == 0 || dy == 3 { 1 } else { 2 };
|
total_height *= 2;
|
||||||
for dx in -radius..=radius {
|
}
|
||||||
for dz in -radius..=radius {
|
|
||||||
let cap_pos = BlockPos::new(pos.0.x + dx, cap_y + dy, pos.0.z + dz);
|
if !self.planted && pos.0.y + total_height + 1 >= chunk.top_y() as i32 {
|
||||||
let block_state = if (dx == 0 || dz == 0) && dy == 1 && random.next_f32() < 0.3
|
return false;
|
||||||
{
|
}
|
||||||
Block::SHROOMLIGHT.default_state
|
|
||||||
} else {
|
let is_huge = !self.planted && random.next_f32() < 0.06;
|
||||||
wart_state
|
chunk.set_block_state(&pos.0, Block::AIR.default_state);
|
||||||
};
|
self.place_stem(chunk, block_registry, random, pos, total_height, is_huge);
|
||||||
chunk.set_block_state(&cap_pos.0, block_state);
|
self.place_hat(chunk, block_registry, random, pos, total_height, is_huge);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_replaceable<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
pos: BlockPos,
|
||||||
|
check_non_replaceable_plants: bool,
|
||||||
|
) -> bool {
|
||||||
|
if chunk.is_air(&pos.0)
|
||||||
|
|| GenerationCache::get_block_state(chunk, &pos.0)
|
||||||
|
.to_state()
|
||||||
|
.is_air()
|
||||||
|
{
|
||||||
|
true
|
||||||
|
} else if check_non_replaceable_plants {
|
||||||
|
self.replaceable_blocks.test(block_registry, chunk, &pos)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_stem<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
total_height: i32,
|
||||||
|
is_huge: bool,
|
||||||
|
) {
|
||||||
|
let stem_radius = i32::from(is_huge);
|
||||||
|
|
||||||
|
for dx in -stem_radius..=stem_radius {
|
||||||
|
for dz in -stem_radius..=stem_radius {
|
||||||
|
let corner_of_huge_stem =
|
||||||
|
is_huge && dx.abs() == stem_radius && dz.abs() == stem_radius;
|
||||||
|
|
||||||
|
for dy in 0..total_height {
|
||||||
|
let block_pos = pos.add(dx, dy, dz);
|
||||||
|
if self.is_replaceable(chunk, block_registry, block_pos, true) {
|
||||||
|
if self.planted {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.stem_state);
|
||||||
|
} else if corner_of_huge_stem {
|
||||||
|
if random.next_f32() < 0.1 {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.stem_state);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.stem_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
}
|
||||||
|
|
||||||
|
fn place_hat<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
total_height: i32,
|
||||||
|
is_huge: bool,
|
||||||
|
) {
|
||||||
|
let place_vines = Block::from_state_id(self.hat_state.id).id == Block::NETHER_WART_BLOCK.id;
|
||||||
|
let hat_height = (random.next_bounded_i32(1 + total_height / 3) + 5).min(total_height);
|
||||||
|
let hat_start_y = total_height - hat_height;
|
||||||
|
|
||||||
|
for dy in hat_start_y..=total_height {
|
||||||
|
let mut radius = if hat_height > 8 && dy < hat_start_y + 4 {
|
||||||
|
3
|
||||||
|
} else if dy < total_height - random.next_bounded_i32(3) {
|
||||||
|
2
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
if is_huge {
|
||||||
|
radius += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for dx in -radius..=radius {
|
||||||
|
for dz in -radius..=radius {
|
||||||
|
let is_edge_x = dx == -radius || dx == radius;
|
||||||
|
let is_edge_z = dz == -radius || dz == radius;
|
||||||
|
let inside = !is_edge_x && !is_edge_z && dy != total_height;
|
||||||
|
let corner = is_edge_x && is_edge_z;
|
||||||
|
let is_hat_bottom = dy < hat_start_y + 3;
|
||||||
|
let block_pos = pos.add(dx, dy, dz);
|
||||||
|
|
||||||
|
if self.is_replaceable(chunk, block_registry, block_pos, false) {
|
||||||
|
if is_hat_bottom {
|
||||||
|
if !inside {
|
||||||
|
self.place_hat_drop_block(chunk, random, block_pos, place_vines);
|
||||||
|
}
|
||||||
|
} else if inside {
|
||||||
|
self.place_hat_block(
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
block_pos,
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
if place_vines { 0.1 } else { 0.0 },
|
||||||
|
);
|
||||||
|
} else if corner {
|
||||||
|
self.place_hat_block(
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
block_pos,
|
||||||
|
0.01,
|
||||||
|
0.7,
|
||||||
|
if place_vines { 0.083 } else { 0.0 },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.place_hat_block(
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
block_pos,
|
||||||
|
5.0e-4,
|
||||||
|
0.98,
|
||||||
|
if place_vines { 0.07 } else { 0.0 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_hat_block<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
block_pos: BlockPos,
|
||||||
|
decor_prob: f32,
|
||||||
|
hat_prob: f32,
|
||||||
|
vines_prob: f32,
|
||||||
|
) {
|
||||||
|
if random.next_f32() < decor_prob {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.decor_state);
|
||||||
|
} else if random.next_f32() < hat_prob {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.hat_state);
|
||||||
|
if random.next_f32() < vines_prob {
|
||||||
|
Self::try_place_weeping_vines(chunk, random, block_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_hat_drop_block<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
block_pos: BlockPos,
|
||||||
|
place_vines: bool,
|
||||||
|
) {
|
||||||
|
let below = block_pos.down();
|
||||||
|
let below_id = GenerationCache::get_block_state(chunk, &below.0).to_block_id();
|
||||||
|
let hat_id = Block::from_state_id(self.hat_state.id).id;
|
||||||
|
|
||||||
|
if below_id == hat_id {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.hat_state);
|
||||||
|
} else if random.next_f32() < 0.15 {
|
||||||
|
chunk.set_block_state(&block_pos.0, self.hat_state);
|
||||||
|
if place_vines && random.next_bounded_i32(11) == 0 {
|
||||||
|
Self::try_place_weeping_vines(chunk, random, block_pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_place_weeping_vines<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
hat_block_pos: BlockPos,
|
||||||
|
) {
|
||||||
|
let place_pos = hat_block_pos.down();
|
||||||
|
if chunk.is_air(&place_pos.0) {
|
||||||
|
let mut goal_vine_height = random.next_inbetween_i32(1, 5);
|
||||||
|
if random.next_bounded_i32(7) == 0 {
|
||||||
|
goal_vine_height *= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
WeepingVinesFeature::place_weeping_vines_column(
|
||||||
|
chunk,
|
||||||
|
random,
|
||||||
|
place_pos,
|
||||||
|
goal_vine_height,
|
||||||
|
23,
|
||||||
|
25,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,6 @@ use pumpkin_util::{
|
|||||||
|
|
||||||
use crate::{block::BlockStateCodec, generation::proto_chunk::GenerationCache};
|
use crate::{block::BlockStateCodec, generation::proto_chunk::GenerationCache};
|
||||||
|
|
||||||
const SEA_LEVEL: i32 = 63; // TODO: use getSeaLevel() instead of hardcoding
|
|
||||||
|
|
||||||
pub struct IcebergFeature {
|
pub struct IcebergFeature {
|
||||||
pub main_block: BlockStateCodec,
|
pub main_block: BlockStateCodec,
|
||||||
}
|
}
|
||||||
@ -25,7 +23,7 @@ impl IcebergFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let origin = Vector3::new(pos.0.x, SEA_LEVEL, pos.0.z);
|
let origin = Vector3::new(pos.0.x, chunk.get_sea_level(), pos.0.z);
|
||||||
let main_block = self.main_block.get_state();
|
let main_block = self.main_block.get_state();
|
||||||
|
|
||||||
let snow_on_top = random.next_f64() > 0.7;
|
let snow_on_top = random.next_f64() > 0.7;
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use crate::generation::proto_chunk::GenerationCache;
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::KelpLikeProperties, tag};
|
||||||
use pumpkin_data::{Block, BlockState};
|
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::position::BlockPos,
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct KelpFeature;
|
pub struct KelpFeature;
|
||||||
|
|
||||||
impl KelpFeature {
|
impl KelpFeature {
|
||||||
@ -19,67 +20,66 @@ impl KelpFeature {
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
let mut placed = 0;
|
let mut placed = 0;
|
||||||
|
|
||||||
// Start on the ocean floor
|
|
||||||
let y = chunk.ocean_floor_height_exclusive(pos.0.x, pos.0.z);
|
let y = chunk.ocean_floor_height_exclusive(pos.0.x, pos.0.z);
|
||||||
let mut kelp_pos = BlockPos::new(pos.0.x, y, pos.0.z);
|
let mut kelp_pos = BlockPos::new(pos.0.x, y, pos.0.z);
|
||||||
|
|
||||||
// Must be in water
|
|
||||||
if GenerationCache::get_block_state(chunk, &kelp_pos.0).to_block_id() == Block::WATER {
|
if GenerationCache::get_block_state(chunk, &kelp_pos.0).to_block_id() == Block::WATER {
|
||||||
let height_rand = 1 + random.next_bounded_i32(10);
|
let height = 1 + random.next_bounded_i32(10);
|
||||||
|
|
||||||
|
for h in 0..=height {
|
||||||
|
let above = kelp_pos.up();
|
||||||
|
let can_survive = Self::can_survive(chunk, kelp_pos);
|
||||||
|
|
||||||
// Iterate from base up to height_rand
|
|
||||||
for h in 0..=height_rand {
|
|
||||||
// Check there is water at this position and one above
|
|
||||||
if GenerationCache::get_block_state(chunk, &kelp_pos.0).to_block_id()
|
if GenerationCache::get_block_state(chunk, &kelp_pos.0).to_block_id()
|
||||||
== Block::WATER
|
== Block::WATER
|
||||||
&& GenerationCache::get_block_state(
|
&& GenerationCache::get_block_state(chunk, &above.0).to_block_id()
|
||||||
chunk,
|
|
||||||
&BlockPos::new(kelp_pos.0.x, kelp_pos.0.y + 1, kelp_pos.0.z).0,
|
|
||||||
)
|
|
||||||
.to_block_id()
|
|
||||||
== Block::WATER
|
== Block::WATER
|
||||||
|
&& can_survive
|
||||||
{
|
{
|
||||||
// If this is the last iteration place the kelp head with age
|
if h == height {
|
||||||
if h == height_rand {
|
let age = (random.next_bounded_i32(4) + 20) as u8;
|
||||||
let age = random.next_bounded_i32(4) + 20;
|
let mut props = KelpLikeProperties::default(&Block::KELP);
|
||||||
// Clamp in case it goes past available states
|
props.age = age;
|
||||||
let age = age.min((Block::KELP.states.len() - 1) as i32) as usize;
|
let state_id = props.to_state_id(&Block::KELP);
|
||||||
let state_id = Block::KELP.states[age].id;
|
chunk.set_block_state(&kelp_pos.0, BlockState::from_id(state_id));
|
||||||
let state = BlockState::from_id(state_id);
|
|
||||||
chunk.set_block_state(&kelp_pos.0, state);
|
|
||||||
placed += 1;
|
placed += 1;
|
||||||
} else {
|
} else {
|
||||||
// Place kelp plant body
|
chunk.set_block_state(&kelp_pos.0, Block::KELP_PLANT.default_state);
|
||||||
let state_id = Block::KELP_PLANT.default_state.id;
|
|
||||||
let state = BlockState::from_id(state_id);
|
|
||||||
chunk.set_block_state(&kelp_pos.0, state);
|
|
||||||
}
|
}
|
||||||
} else if h > 0 {
|
} else if h > 0 {
|
||||||
// Can't place further but we have already placed at least one segment, try to put head below
|
let below = kelp_pos.down();
|
||||||
let below = BlockPos::new(kelp_pos.0.x, kelp_pos.0.y - 1, kelp_pos.0.z);
|
let below_below = below.down();
|
||||||
// Check head block can survive and that below-below is not a kelp head
|
let below_below_id =
|
||||||
if GenerationCache::get_block_state(chunk, &below.0).to_block_id()
|
GenerationCache::get_block_state(chunk, &below_below.0).to_block_id();
|
||||||
== Block::WATER
|
if Self::can_survive(chunk, below) && below_below_id != Block::KELP.id {
|
||||||
&& GenerationCache::get_block_state(
|
let age = (random.next_bounded_i32(4) + 20) as u8;
|
||||||
chunk,
|
let mut props = KelpLikeProperties::default(&Block::KELP);
|
||||||
&BlockPos::new(below.0.x, below.0.y - 1, below.0.z).0,
|
props.age = age;
|
||||||
)
|
let state_id = props.to_state_id(&Block::KELP);
|
||||||
.to_block_id()
|
chunk.set_block_state(&below.0, BlockState::from_id(state_id));
|
||||||
!= Block::KELP
|
|
||||||
{
|
|
||||||
let age = random.next_bounded_i32(4) + 20;
|
|
||||||
let age = age.min((Block::KELP.states.len() - 1) as i32) as usize;
|
|
||||||
let state_id = Block::KELP.states[age].id;
|
|
||||||
let state = BlockState::from_id(state_id);
|
|
||||||
chunk.set_block_state(&below.0, state);
|
|
||||||
placed += 1;
|
placed += 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
kelp_pos = BlockPos::new(kelp_pos.0.x, kelp_pos.0.y + 1, kelp_pos.0.z);
|
|
||||||
|
kelp_pos = kelp_pos.up();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
placed > 0
|
placed > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn can_survive<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
|
let below = pos.down();
|
||||||
|
let below_state = GenerationCache::get_block_state(chunk, &below.0);
|
||||||
|
let below_id = below_state.to_block_id();
|
||||||
|
|
||||||
|
if below_id.has_tag(tag::Block::MINECRAFT_CANNOT_SUPPORT_KELP) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
below_id == Block::KELP.id
|
||||||
|
|| below_id == Block::KELP_PLANT.id
|
||||||
|
|| below_state.to_state().is_side_solid(BlockDirection::Up)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,9 +7,6 @@ use pumpkin_util::{
|
|||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
/// The three mob types that can appear in a dungeon spawner.
|
|
||||||
///
|
|
||||||
/// Skeleton 25%, Zombie 50%, Spider 25%.
|
|
||||||
const DUNGEON_MOBS: [&str; 4] = [
|
const DUNGEON_MOBS: [&str; 4] = [
|
||||||
"minecraft:skeleton",
|
"minecraft:skeleton",
|
||||||
"minecraft:zombie",
|
"minecraft:zombie",
|
||||||
@ -20,7 +17,6 @@ const DUNGEON_MOBS: [&str; 4] = [
|
|||||||
pub struct DungeonFeature;
|
pub struct DungeonFeature;
|
||||||
|
|
||||||
impl DungeonFeature {
|
impl DungeonFeature {
|
||||||
/// Generate a monster room (dungeon) at `pos`.
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
@ -46,13 +42,12 @@ impl DungeonFeature {
|
|||||||
let solid = !chunk.is_air(&check_pos);
|
let solid = !chunk.is_air(&check_pos);
|
||||||
|
|
||||||
if dy == -1 && !solid {
|
if dy == -1 && !solid {
|
||||||
return false; // Floor has a gap
|
return false;
|
||||||
}
|
}
|
||||||
if dy == 4 && !solid {
|
if dy == 4 && !solid {
|
||||||
return false; // Ceiling has a gap
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wall openings
|
|
||||||
let on_wall = dx == min_x || dx == max_x || dz == min_z || dz == max_z;
|
let on_wall = dx == min_x || dx == max_x || dz == min_z || dz == max_z;
|
||||||
if on_wall
|
if on_wall
|
||||||
&& dy == 0
|
&& dy == 0
|
||||||
@ -94,18 +89,15 @@ impl DungeonFeature {
|
|||||||
chunk.set_block_state(&wall_pos, Block::CAVE_AIR.default_state);
|
chunk.set_block_state(&wall_pos, Block::CAVE_AIR.default_state);
|
||||||
} else if cur_solid && !is_chest {
|
} else if cur_solid && !is_chest {
|
||||||
if dy == -1 && random.next_bounded_i32(4) != 0 {
|
if dy == -1 && random.next_bounded_i32(4) != 0 {
|
||||||
// 75% mossy cobblestone
|
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&wall_pos,
|
&wall_pos,
|
||||||
Block::MOSSY_COBBLESTONE.default_state,
|
Block::MOSSY_COBBLESTONE.default_state,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
// 25% cobblestone (or 100% if it's not the floor)
|
|
||||||
chunk.set_block_state(&wall_pos, Block::COBBLESTONE.default_state);
|
chunk.set_block_state(&wall_pos, Block::COBBLESTONE.default_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Clear interior, but preserve chests and spawners
|
|
||||||
let state = GenerationCache::get_block_state(chunk, &wall_pos);
|
let state = GenerationCache::get_block_state(chunk, &wall_pos);
|
||||||
let is_chest = state.to_block() == &Block::CHEST;
|
let is_chest = state.to_block() == &Block::CHEST;
|
||||||
let is_spawner = state.to_block() == &Block::SPAWNER;
|
let is_spawner = state.to_block() == &Block::SPAWNER;
|
||||||
@ -117,7 +109,6 @@ impl DungeonFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Chest placement attempts (rooms have 1-2 chests)
|
|
||||||
for _ in 0..2 {
|
for _ in 0..2 {
|
||||||
for _ in 0..3 {
|
for _ in 0..3 {
|
||||||
let cx = pos.0.x + random.next_bounded_i32(xr * 2 + 1) - xr;
|
let cx = pos.0.x + random.next_bounded_i32(xr * 2 + 1) - xr;
|
||||||
@ -129,20 +120,17 @@ impl DungeonFeature {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count solid horizontal neighbours
|
|
||||||
let wall_count = BlockDirection::horizontal()
|
let wall_count = BlockDirection::horizontal()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|d| !chunk.is_air(&chest_pos.add(&d.to_offset())))
|
.filter(|d| !chunk.is_air(&chest_pos.add(&d.to_offset())))
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
if wall_count == 1 {
|
if wall_count == 1 {
|
||||||
// Orient the chest toward its single solid neighbor.
|
|
||||||
let facing_dir = BlockDirection::horizontal()
|
let facing_dir = BlockDirection::horizontal()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|d| !chunk.is_air(&chest_pos.add(&d.to_offset())))
|
.find(|d| !chunk.is_air(&chest_pos.add(&d.to_offset())))
|
||||||
.copied();
|
.copied();
|
||||||
|
|
||||||
// Build the chest state: face away from the wall.
|
|
||||||
let chest_state = facing_dir.map_or(Block::CHEST.default_state, |dir| {
|
let chest_state = facing_dir.map_or(Block::CHEST.default_state, |dir| {
|
||||||
use pumpkin_data::block_properties::ChestLikeProperties;
|
use pumpkin_data::block_properties::ChestLikeProperties;
|
||||||
let mut props = ChestLikeProperties::default(&Block::CHEST);
|
let mut props = ChestLikeProperties::default(&Block::CHEST);
|
||||||
@ -153,7 +141,6 @@ impl DungeonFeature {
|
|||||||
|
|
||||||
chunk.set_block_state(&chest_pos, chest_state);
|
chunk.set_block_state(&chest_pos, chest_state);
|
||||||
|
|
||||||
// Write the block-entity NBT with a deferred loot table.
|
|
||||||
let loot_seed = random.next_i64();
|
let loot_seed = random.next_i64();
|
||||||
let mut chest_nbt = NbtCompound::new();
|
let mut chest_nbt = NbtCompound::new();
|
||||||
chest_nbt.put_string("id", "minecraft:chest".to_string());
|
chest_nbt.put_string("id", "minecraft:chest".to_string());
|
||||||
@ -170,7 +157,6 @@ impl DungeonFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: set spawner entity type
|
|
||||||
let mob = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize];
|
let mob = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize];
|
||||||
chunk.set_block_state(&pos.0, Block::SPAWNER.default_state);
|
chunk.set_block_state(&pos.0, Block::SPAWNER.default_state);
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,20 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::GlowLichenLikeProperties};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct MultifaceGrowthFeature;
|
pub struct MultifaceGrowthFeature {
|
||||||
|
pub place_block: pumpkin_data::BlockId,
|
||||||
|
pub search_range: i32,
|
||||||
|
pub can_place_on_floor: bool,
|
||||||
|
pub can_place_on_ceiling: bool,
|
||||||
|
pub can_place_on_wall: bool,
|
||||||
|
pub chance_of_spreading: f32,
|
||||||
|
pub can_be_placed_on: Vec<pumpkin_data::BlockId>,
|
||||||
|
}
|
||||||
|
|
||||||
impl MultifaceGrowthFeature {
|
impl MultifaceGrowthFeature {
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
@ -13,27 +24,133 @@ impl MultifaceGrowthFeature {
|
|||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
_random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut generated = false;
|
if !Self::is_air_or_water(chunk, pos) {
|
||||||
let directions = [
|
return false;
|
||||||
(1, 0, 0),
|
}
|
||||||
(-1, 0, 0),
|
|
||||||
(0, 1, 0),
|
let shuffled_dirs = self.get_shuffled_directions(random);
|
||||||
(0, -1, 0),
|
if self.place_growth_if_possible(chunk, pos, random, &shuffled_dirs) {
|
||||||
(0, 0, 1),
|
return true;
|
||||||
(0, 0, -1),
|
}
|
||||||
];
|
|
||||||
for &(dx, dy, dz) in &directions {
|
for search_direction in shuffled_dirs {
|
||||||
let neighbor = BlockPos::new(pos.0.x + dx, pos.0.y + dy, pos.0.z + dz);
|
let placement_directions =
|
||||||
let state = GenerationCache::get_block_state(chunk, &neighbor.0);
|
self.get_shuffled_directions_except(random, search_direction.opposite());
|
||||||
if state.to_block_id() != Block::AIR.id && chunk.is_air(&pos.0) {
|
let mut cur_pos = pos;
|
||||||
chunk.set_block_state(&pos.0, Block::GLOW_LICHEN.default_state);
|
|
||||||
generated = true;
|
for _ in 0..self.search_range {
|
||||||
break;
|
cur_pos = cur_pos.offset(search_direction.to_offset());
|
||||||
|
let block_id = GenerationCache::get_block_state(chunk, &cur_pos.0).to_block_id();
|
||||||
|
if !Self::is_air_or_water(chunk, cur_pos) && block_id != self.place_block {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.place_growth_if_possible(chunk, cur_pos, random, &placement_directions) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
generated
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_growth_if_possible<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
pos: BlockPos,
|
||||||
|
_random: &mut RandomGenerator,
|
||||||
|
placement_directions: &[BlockDirection],
|
||||||
|
) -> bool {
|
||||||
|
for &direction in placement_directions {
|
||||||
|
let neighbor_pos = pos.offset(direction.to_offset());
|
||||||
|
let neighbor_id =
|
||||||
|
GenerationCache::get_block_state(chunk, &neighbor_pos.0).to_block_id();
|
||||||
|
|
||||||
|
if self.can_be_placed_on.contains(&neighbor_id) {
|
||||||
|
let is_water = GenerationCache::get_block_state(chunk, &pos.0).to_block_id()
|
||||||
|
== Block::WATER.id;
|
||||||
|
let block = Block::from_id(self.place_block);
|
||||||
|
|
||||||
|
let mut props = GlowLichenLikeProperties {
|
||||||
|
down: direction == BlockDirection::Down,
|
||||||
|
up: direction == BlockDirection::Up,
|
||||||
|
north: direction == BlockDirection::North,
|
||||||
|
south: direction == BlockDirection::South,
|
||||||
|
west: direction == BlockDirection::West,
|
||||||
|
east: direction == BlockDirection::East,
|
||||||
|
waterlogged: is_water,
|
||||||
|
};
|
||||||
|
|
||||||
|
let cur_state = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
|
if cur_state.to_block_id() == self.place_block
|
||||||
|
&& (self.place_block == Block::GLOW_LICHEN.id
|
||||||
|
|| self.place_block == Block::SCULK_VEIN.id)
|
||||||
|
{
|
||||||
|
let cur_props = GlowLichenLikeProperties::from_state_id(cur_state);
|
||||||
|
props.down |= cur_props.down;
|
||||||
|
props.up |= cur_props.up;
|
||||||
|
props.north |= cur_props.north;
|
||||||
|
props.south |= cur_props.south;
|
||||||
|
props.west |= cur_props.west;
|
||||||
|
props.east |= cur_props.east;
|
||||||
|
}
|
||||||
|
|
||||||
|
let state_id = props.to_state_id(block);
|
||||||
|
chunk.set_block_state(&pos.0, BlockState::from_id(state_id));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_valid_directions(&self) -> Vec<BlockDirection> {
|
||||||
|
let mut dirs = Vec::with_capacity(6);
|
||||||
|
if self.can_place_on_ceiling {
|
||||||
|
dirs.push(BlockDirection::Up);
|
||||||
|
}
|
||||||
|
if self.can_place_on_floor {
|
||||||
|
dirs.push(BlockDirection::Down);
|
||||||
|
}
|
||||||
|
if self.can_place_on_wall {
|
||||||
|
dirs.extend(
|
||||||
|
BlockDirection::horizontal_worldgen().map(BlockDirection::from_cardinal_direction),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
dirs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shuffled_directions(&self, random: &mut RandomGenerator) -> Vec<BlockDirection> {
|
||||||
|
let mut dirs = self.get_valid_directions();
|
||||||
|
for i in (1..dirs.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32((i + 1) as i32) as usize;
|
||||||
|
dirs.swap(i, j);
|
||||||
|
}
|
||||||
|
dirs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_shuffled_directions_except(
|
||||||
|
&self,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
exclude: BlockDirection,
|
||||||
|
) -> Vec<BlockDirection> {
|
||||||
|
let mut dirs: Vec<BlockDirection> = self
|
||||||
|
.get_valid_directions()
|
||||||
|
.into_iter()
|
||||||
|
.filter(|&d| d != exclude)
|
||||||
|
.collect();
|
||||||
|
for i in (1..dirs.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32((i + 1) as i32) as usize;
|
||||||
|
dirs.swap(i, j);
|
||||||
|
}
|
||||||
|
dirs
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_air_or_water<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
|
let id = GenerationCache::get_block_state(chunk, &pos.0).to_block_id();
|
||||||
|
id == Block::AIR.id || id == Block::WATER.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -50,12 +50,11 @@ impl OreFeature {
|
|||||||
let q = 2 * (g.ceil() as i32 + i);
|
let q = 2 * (g.ceil() as i32 + i);
|
||||||
let r = 2 * (2 + i);
|
let r = 2 * (2 + i);
|
||||||
|
|
||||||
for _ in n..=(n + q) {
|
for xprobe in n..=(n + q) {
|
||||||
for _ in p..=(p + q) {
|
for zprobe in p..=(p + q) {
|
||||||
if o > chunk.ocean_floor_height_exclusive(pos.0.x, pos.0.z) {
|
if o <= chunk.ocean_floor_height_exclusive(xprobe, zprobe) {
|
||||||
continue;
|
return self.generate_vein_part(chunk, random, d, e, h, j, l, m, n, o, p, q, r);
|
||||||
}
|
}
|
||||||
return self.generate_vein_part(chunk, random, d, e, h, j, l, m, n, o, p, q, r);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|||||||
@ -1,22 +1,29 @@
|
|||||||
use pumpkin_data::Block;
|
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||||
|
|
||||||
|
use crate::generation::feature::features::ore::OreTarget;
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct ReplaceSingleBlockFeature;
|
pub struct ReplaceSingleBlockFeature {
|
||||||
|
pub targets: Vec<OreTarget>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ReplaceSingleBlockFeature {
|
impl ReplaceSingleBlockFeature {
|
||||||
#[allow(clippy::unused_self)]
|
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
_random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
chunk.set_block_state(&pos.0, Block::SAND.default_state);
|
let block_state = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
|
for target in &self.targets {
|
||||||
|
if target.target.test(block_state, random) {
|
||||||
|
chunk.set_block_state(&pos.0, target.state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::position::BlockPos,
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
@ -36,52 +37,156 @@ impl RootSystemFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if !self.allowed_tree_position.test(block_registry, chunk, &pos) {
|
if !chunk.is_air(&pos.0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.can_place_tree(chunk, pos) {
|
let mut working_pos = pos;
|
||||||
return false;
|
if self.place_dirt_and_tree(
|
||||||
}
|
|
||||||
|
|
||||||
if !self.feature.generate(
|
|
||||||
chunk,
|
chunk,
|
||||||
block_registry,
|
block_registry,
|
||||||
min_y,
|
min_y,
|
||||||
height,
|
height,
|
||||||
feature_name,
|
feature_name,
|
||||||
random,
|
random,
|
||||||
|
&mut working_pos,
|
||||||
pos,
|
pos,
|
||||||
) {
|
) {
|
||||||
return false;
|
self.place_roots(chunk, block_registry, random, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.place_roots(chunk, block_registry, random, pos);
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_place_tree<T: GenerationCache>(&self, chunk: &T, pos: BlockPos) -> bool {
|
fn space_for_tree<T: GenerationCache>(&self, chunk: &T, pos: BlockPos) -> bool {
|
||||||
let mut mutable_pos = pos;
|
let mut column_up = pos;
|
||||||
for i in 1..=self.required_vertical_space_for_tree {
|
for i in 1..=self.required_vertical_space_for_tree {
|
||||||
mutable_pos = mutable_pos.add(0, 1, 0);
|
column_up = column_up.up();
|
||||||
if !self.is_allowed_tree_space(chunk, mutable_pos, i) {
|
let state = GenerationCache::get_block_state(chunk, &column_up.0);
|
||||||
|
if !self.is_allowed_tree_space(state.to_block_id(), i) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_allowed_tree_space<T: GenerationCache>(
|
fn is_allowed_tree_space(
|
||||||
&self,
|
&self,
|
||||||
chunk: &T,
|
block_id: pumpkin_data::BlockId,
|
||||||
pos: BlockPos,
|
blocks_above_origin: i32,
|
||||||
vertical_space: i32,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if chunk.is_air(&pos.0) {
|
if block_id == Block::AIR.id {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// TODO: check for water.
|
let blocks_above_ground = blocks_above_origin + 1;
|
||||||
vertical_space <= self.allowed_vertical_water_for_tree
|
blocks_above_ground <= self.allowed_vertical_water_for_tree && block_id == Block::WATER.id
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
fn place_dirt_and_tree<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
min_y: i8,
|
||||||
|
height: u16,
|
||||||
|
feature_name: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
working_pos: &mut BlockPos,
|
||||||
|
pos: BlockPos,
|
||||||
|
) -> bool {
|
||||||
|
for y in 0..self.root_column_max_height {
|
||||||
|
*working_pos = working_pos.up();
|
||||||
|
if chunk.top_block_height_exclusive(working_pos.0.x, working_pos.0.z) < working_pos.0.y
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self
|
||||||
|
.allowed_tree_position
|
||||||
|
.test(block_registry, chunk, working_pos)
|
||||||
|
&& self.space_for_tree(chunk, *working_pos)
|
||||||
|
{
|
||||||
|
let below_pos = working_pos.down();
|
||||||
|
let below_state = GenerationCache::get_block_state(chunk, &below_pos.0);
|
||||||
|
if below_state.to_block_id() == Block::LAVA.id
|
||||||
|
|| !below_state.to_state().is_side_solid(BlockDirection::Up)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.feature.generate(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
min_y,
|
||||||
|
height,
|
||||||
|
feature_name,
|
||||||
|
random,
|
||||||
|
*working_pos,
|
||||||
|
) {
|
||||||
|
self.place_dirt(chunk, block_registry, random, pos, pos.0.y + y);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_dirt<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
origin: BlockPos,
|
||||||
|
target_height: i32,
|
||||||
|
) {
|
||||||
|
let origin_x = origin.0.x;
|
||||||
|
let origin_z = origin.0.z;
|
||||||
|
|
||||||
|
for y in origin.0.y..target_height {
|
||||||
|
self.place_rooted_dirt(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
origin_x,
|
||||||
|
origin_z,
|
||||||
|
BlockPos::new(origin_x, y, origin_z),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_rooted_dirt<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
origin_x: i32,
|
||||||
|
origin_z: i32,
|
||||||
|
mut working_pos: BlockPos,
|
||||||
|
) {
|
||||||
|
let root_radius = self.root_radius.max(1);
|
||||||
|
|
||||||
|
for _ in 0..self.root_placement_attempts {
|
||||||
|
working_pos = working_pos.add(
|
||||||
|
random.next_bounded_i32(root_radius) - random.next_bounded_i32(root_radius),
|
||||||
|
0,
|
||||||
|
random.next_bounded_i32(root_radius) - random.next_bounded_i32(root_radius),
|
||||||
|
);
|
||||||
|
|
||||||
|
if self
|
||||||
|
.root_replaceable
|
||||||
|
.test(block_registry, chunk, &working_pos)
|
||||||
|
{
|
||||||
|
chunk.set_block_state(
|
||||||
|
&working_pos.0,
|
||||||
|
self.root_state_provider
|
||||||
|
.get(random, working_pos, chunk, block_registry),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
working_pos.0.x = origin_x;
|
||||||
|
working_pos.0.z = origin_z;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn place_roots<T: GenerationCache>(
|
fn place_roots<T: GenerationCache>(
|
||||||
@ -91,92 +196,29 @@ impl RootSystemFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) {
|
) {
|
||||||
self.place_rooted_dirt(chunk, block_registry, random, pos);
|
let root_radius = self.hanging_root_radius.max(1);
|
||||||
self.place_hanging_roots(chunk, block_registry, random, pos);
|
let vertical_span = self.hanging_roots_vertical_span.max(1);
|
||||||
}
|
|
||||||
|
|
||||||
fn place_rooted_dirt<T: GenerationCache>(
|
|
||||||
&self,
|
|
||||||
chunk: &mut T,
|
|
||||||
block_registry: &dyn WorldPortalExt,
|
|
||||||
random: &mut RandomGenerator,
|
|
||||||
pos: BlockPos,
|
|
||||||
) {
|
|
||||||
for _ in 0..self.root_placement_attempts {
|
|
||||||
let mut mutable_pos = pos.add(
|
|
||||||
random.next_bounded_i32(self.root_radius.max(1))
|
|
||||||
- random.next_bounded_i32(self.root_radius.max(1)),
|
|
||||||
0,
|
|
||||||
random.next_bounded_i32(self.root_radius.max(1))
|
|
||||||
- random.next_bounded_i32(self.root_radius.max(1)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if self
|
|
||||||
.root_replaceable
|
|
||||||
.test(block_registry, chunk, &mutable_pos)
|
|
||||||
{
|
|
||||||
chunk.set_block_state(
|
|
||||||
&mutable_pos.0,
|
|
||||||
self.root_state_provider.get_with_context(
|
|
||||||
block_registry,
|
|
||||||
chunk,
|
|
||||||
random,
|
|
||||||
mutable_pos,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
for _ in 0..self.root_column_max_height {
|
|
||||||
mutable_pos = mutable_pos.add(0, -1, 0);
|
|
||||||
if chunk.out_of_height(mutable_pos.0.y as i16)
|
|
||||||
|| !self
|
|
||||||
.root_replaceable
|
|
||||||
.test(block_registry, chunk, &mutable_pos)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunk.set_block_state(
|
|
||||||
&mutable_pos.0,
|
|
||||||
self.root_state_provider.get_with_context(
|
|
||||||
block_registry,
|
|
||||||
chunk,
|
|
||||||
random,
|
|
||||||
mutable_pos,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn place_hanging_roots<T: GenerationCache>(
|
|
||||||
&self,
|
|
||||||
chunk: &mut T,
|
|
||||||
block_registry: &dyn WorldPortalExt,
|
|
||||||
random: &mut RandomGenerator,
|
|
||||||
pos: BlockPos,
|
|
||||||
) {
|
|
||||||
for _ in 0..self.hanging_root_placement_attempts {
|
for _ in 0..self.hanging_root_placement_attempts {
|
||||||
let mutable_pos = pos.add(
|
let working_pos = pos.add(
|
||||||
random.next_bounded_i32(self.hanging_root_radius.max(1))
|
random.next_bounded_i32(root_radius) - random.next_bounded_i32(root_radius),
|
||||||
- random.next_bounded_i32(self.hanging_root_radius.max(1)),
|
random.next_bounded_i32(vertical_span) - random.next_bounded_i32(vertical_span),
|
||||||
random.next_bounded_i32(self.hanging_roots_vertical_span.max(1))
|
random.next_bounded_i32(root_radius) - random.next_bounded_i32(root_radius),
|
||||||
- random.next_bounded_i32(self.hanging_roots_vertical_span.max(1)),
|
|
||||||
random.next_bounded_i32(self.hanging_root_radius.max(1))
|
|
||||||
- random.next_bounded_i32(self.hanging_root_radius.max(1)),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if chunk.is_air(&mutable_pos.0) {
|
if chunk.is_air(&working_pos.0) {
|
||||||
let state = self.hanging_root_state_provider.get_with_context(
|
let above = working_pos.up();
|
||||||
block_registry,
|
let above_state = GenerationCache::get_block_state(chunk, &above.0);
|
||||||
chunk,
|
if above_state.to_state().is_side_solid(BlockDirection::Down) {
|
||||||
random,
|
chunk.set_block_state(
|
||||||
mutable_pos,
|
&working_pos.0,
|
||||||
);
|
self.hanging_root_state_provider.get(
|
||||||
// In vanilla, it checks if it can survive and if the block above is sturdy.
|
random,
|
||||||
// For now, let's just check if the block above is NOT air.
|
working_pos,
|
||||||
let above = mutable_pos.add(0, 1, 0);
|
chunk,
|
||||||
if !chunk.is_air(&above.0) {
|
block_registry,
|
||||||
chunk.set_block_state(&mutable_pos.0, state);
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,12 +27,11 @@ impl ScatteredOreFeature {
|
|||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let count = random.next_bounded_i32(self.size + 1);
|
let count = random.next_bounded_i32(self.size + 1);
|
||||||
let mut placed = false;
|
for i in 0..count {
|
||||||
|
let max_dist = i.min(7);
|
||||||
for _ in 0..count {
|
let offset_x = Self::get_random_offset(random, max_dist);
|
||||||
let offset_x = self.get_random_offset(random);
|
let offset_y = Self::get_random_offset(random, max_dist);
|
||||||
let offset_y = self.get_random_offset(random);
|
let offset_z = Self::get_random_offset(random, max_dist);
|
||||||
let offset_z = self.get_random_offset(random);
|
|
||||||
|
|
||||||
let target_pos = pos.add(offset_x, offset_y, offset_z);
|
let target_pos = pos.add(offset_x, offset_y, offset_z);
|
||||||
|
|
||||||
@ -52,18 +51,17 @@ impl ScatteredOreFeature {
|
|||||||
&target_pos,
|
&target_pos,
|
||||||
) {
|
) {
|
||||||
chunk.set_block_state(&target_pos.0, target.state);
|
chunk.set_block_state(&target_pos.0, target.state);
|
||||||
placed = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
placed
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_random_offset(&self, random: &mut RandomGenerator) -> i32 {
|
fn get_random_offset(random: &mut RandomGenerator, max_dist: i32) -> i32 {
|
||||||
let f1 = random.next_f32();
|
let f1 = random.next_f32();
|
||||||
let f2 = random.next_f32();
|
let f2 = random.next_f32();
|
||||||
((f1 - f2) * self.size as f32).round() as i32
|
((f1 - f2) * max_dist as f32).round() as i32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
use crate::generation::proto_chunk::GenerationCache;
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::SeaPickleLikeProperties};
|
||||||
use pumpkin_data::{Block, BlockState, block_properties::SeaPickleLikeProperties};
|
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{int_provider::IntProvider, position::BlockPos},
|
math::{int_provider::IntProvider, position::BlockPos},
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct SeaPickleFeature {
|
pub struct SeaPickleFeature {
|
||||||
pub count: IntProvider,
|
pub count: IntProvider,
|
||||||
}
|
}
|
||||||
@ -15,28 +16,37 @@ impl SeaPickleFeature {
|
|||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut times = 0;
|
let mut placed = 0;
|
||||||
let count = self.count.get(random);
|
let count = self.count.get(random);
|
||||||
|
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||||
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||||
let y = chunk.ocean_floor_height_exclusive(pos.0.x + x, pos.0.z + z);
|
let y = chunk.ocean_floor_height_exclusive(pos.0.x + x, pos.0.z + z);
|
||||||
if GenerationCache::get_block_state(chunk, &pos.0).to_block_id() != Block::WATER {
|
let pickle_pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
||||||
continue;
|
|
||||||
|
if GenerationCache::get_block_state(chunk, &pickle_pos.0).to_block_id() == Block::WATER
|
||||||
|
&& Self::can_survive(chunk, pickle_pos)
|
||||||
|
{
|
||||||
|
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
||||||
|
props.pickles = (random.next_bounded_i32(4) as u8) + 1;
|
||||||
|
let state_id = props.to_state_id(&Block::SEA_PICKLE);
|
||||||
|
chunk.set_block_state(&pickle_pos.0, BlockState::from_id(state_id));
|
||||||
|
placed += 1;
|
||||||
}
|
}
|
||||||
let mut props = SeaPickleLikeProperties::default(&Block::SEA_PICKLE);
|
|
||||||
props.pickles = (random.next_bounded_i32(4) as u8) + 1;
|
|
||||||
let pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
|
||||||
chunk.set_block_state(
|
|
||||||
&pos.0,
|
|
||||||
BlockState::from_id(props.to_state_id(&Block::SEA_PICKLE)),
|
|
||||||
);
|
|
||||||
times += 1;
|
|
||||||
}
|
}
|
||||||
times > 0
|
|
||||||
|
placed > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_survive<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
|
let below = pos.down();
|
||||||
|
GenerationCache::get_block_state(chunk, &below.0)
|
||||||
|
.to_state()
|
||||||
|
.is_side_solid(BlockDirection::Up)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,15 @@
|
|||||||
use crate::generation::proto_chunk::GenerationCache;
|
|
||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockState,
|
Block, BlockDirection, BlockState,
|
||||||
block_properties::{DoubleBlockHalf, TallSeagrassLikeProperties},
|
block_properties::{DoubleBlockHalf, TallSeagrassLikeProperties},
|
||||||
|
tag,
|
||||||
};
|
};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::position::BlockPos,
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct SeagrassFeature {
|
pub struct SeagrassFeature {
|
||||||
pub probability: f32,
|
pub probability: f32,
|
||||||
}
|
}
|
||||||
@ -18,34 +20,51 @@ impl SeagrassFeature {
|
|||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
_min_y: i8,
|
_min_y: i8,
|
||||||
_height: u16,
|
_height: u16,
|
||||||
_feature: pumpkin_data::placed_feature::PlacedFeature, // This placed feature
|
_feature: pumpkin_data::placed_feature::PlacedFeature,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let mut placed_any = false;
|
||||||
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
let x = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||||
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
let z = random.next_bounded_i32(8) - random.next_bounded_i32(8);
|
||||||
let y = chunk.ocean_floor_height_exclusive(pos.0.x + x, pos.0.z + z);
|
let y = chunk.ocean_floor_height_exclusive(pos.0.x + x, pos.0.z + z);
|
||||||
let top_pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
let grass_pos = BlockPos::new(pos.0.x + x, y, pos.0.z + z);
|
||||||
if GenerationCache::get_block_state(chunk, &top_pos.0).to_block_id() == Block::WATER {
|
|
||||||
let tall = random.next_f64() < self.probability as f64;
|
if GenerationCache::get_block_state(chunk, &grass_pos.0).to_block_id() == Block::WATER
|
||||||
if tall {
|
&& Self::can_survive(chunk, grass_pos)
|
||||||
let tall_pos = top_pos.up();
|
{
|
||||||
if GenerationCache::get_block_state(chunk, &tall_pos.0).to_block_id()
|
let is_tall = random.next_f64() < self.probability as f64;
|
||||||
== Block::WATER
|
if is_tall {
|
||||||
{
|
let above = grass_pos.up();
|
||||||
let mut props = TallSeagrassLikeProperties::default(&Block::TALL_SEAGRASS);
|
if GenerationCache::get_block_state(chunk, &above.0).to_block_id() == Block::WATER {
|
||||||
props.half = DoubleBlockHalf::Upper;
|
let mut upper_props =
|
||||||
chunk.set_block_state(&top_pos.0, Block::TALL_SEAGRASS.default_state);
|
TallSeagrassLikeProperties::default(&Block::TALL_SEAGRASS);
|
||||||
chunk.set_block_state(
|
upper_props.half = DoubleBlockHalf::Upper;
|
||||||
&tall_pos.0,
|
let upper_state =
|
||||||
BlockState::from_id(props.to_state_id(&Block::TALL_SEAGRASS)),
|
BlockState::from_id(upper_props.to_state_id(&Block::TALL_SEAGRASS));
|
||||||
);
|
|
||||||
|
chunk.set_block_state(&grass_pos.0, Block::TALL_SEAGRASS.default_state);
|
||||||
|
chunk.set_block_state(&above.0, upper_state);
|
||||||
|
placed_any = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chunk.set_block_state(&top_pos.0, Block::SEAGRASS.default_state);
|
chunk.set_block_state(&grass_pos.0, Block::SEAGRASS.default_state);
|
||||||
|
placed_any = true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
false
|
|
||||||
|
placed_any
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_survive<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
|
let below = pos.down();
|
||||||
|
let below_state = GenerationCache::get_block_state(chunk, &below.0);
|
||||||
|
let below_id = below_state.to_block_id();
|
||||||
|
|
||||||
|
if below_id.has_tag(tag::Block::MINECRAFT_CANNOT_SUPPORT_SEAGRASS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
below_state.to_state().is_side_solid(BlockDirection::Up)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,9 +27,7 @@ impl SimpleBlockFeature {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check things..
|
|
||||||
chunk.set_block_state(&pos.0, state);
|
chunk.set_block_state(&pos.0, state);
|
||||||
// TODO: schedule tick when needed
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,92 @@
|
|||||||
pub struct AlterGroundTreeDecorator;
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::TreeDecorator;
|
||||||
|
use crate::{
|
||||||
|
generation::{block_state_provider::BlockStateProvider, proto_chunk::GenerationCache},
|
||||||
|
world::WorldPortalExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct AlterGroundTreeDecorator {
|
||||||
|
pub provider: BlockStateProvider,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlterGroundTreeDecorator {
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
root_positions: &[BlockPos],
|
||||||
|
log_positions: &[BlockPos],
|
||||||
|
) {
|
||||||
|
let list = TreeDecorator::get_leaf_litter_positions(root_positions, log_positions);
|
||||||
|
let Some(first) = list.first() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let min_y = list.iter().map(|p| p.0.y).min().unwrap_or(first.0.y);
|
||||||
|
|
||||||
|
for pos in list.iter().filter(|p| p.0.y == min_y) {
|
||||||
|
self.place_circle(chunk, block_registry, random, pos.west().north());
|
||||||
|
self.place_circle(chunk, block_registry, random, pos.east().east().north());
|
||||||
|
self.place_circle(chunk, block_registry, random, pos.west().south().south());
|
||||||
|
self.place_circle(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
random,
|
||||||
|
pos.east().east().south().south(),
|
||||||
|
);
|
||||||
|
|
||||||
|
for _ in 0..5 {
|
||||||
|
let placement = random.next_bounded_i32(64);
|
||||||
|
let xx = placement % 8;
|
||||||
|
let zz = placement / 8;
|
||||||
|
if xx == 0 || xx == 7 || zz == 0 || zz == 7 {
|
||||||
|
self.place_circle(chunk, block_registry, random, pos.add(-3 + xx, 0, -3 + zz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_circle<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
) {
|
||||||
|
for xx in -2..=2i32 {
|
||||||
|
for zz in -2..=2i32 {
|
||||||
|
if xx.abs() != 2 || zz.abs() != 2 {
|
||||||
|
self.place_block_at(chunk, block_registry, random, pos.add(xx, 0, zz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_block_at<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
pos: BlockPos,
|
||||||
|
) {
|
||||||
|
for dy in (-3..=2).rev() {
|
||||||
|
let cursor = pos.up_height(dy);
|
||||||
|
if let Some(replace_with) =
|
||||||
|
self.provider
|
||||||
|
.get_optional(block_registry, chunk, random, cursor)
|
||||||
|
{
|
||||||
|
chunk.set_block_state(&cursor.0, replace_with);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !chunk.is_air(&cursor.0) && dy < 0 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,84 @@
|
|||||||
pub struct AttachedToLeavesTreeDecorator;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use pumpkin_data::BlockDirection;
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
generation::{block_state_provider::BlockStateProvider, proto_chunk::GenerationCache},
|
||||||
|
world::WorldPortalExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct AttachedToLeavesTreeDecorator {
|
||||||
|
pub probability: f32,
|
||||||
|
pub exclusion_radius_xz: i32,
|
||||||
|
pub exclusion_radius_y: i32,
|
||||||
|
pub block_provider: BlockStateProvider,
|
||||||
|
pub required_empty_blocks: i32,
|
||||||
|
pub directions: Vec<BlockDirection>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AttachedToLeavesTreeDecorator {
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
block_registry: &dyn WorldPortalExt,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
foliage_positions: &[BlockPos],
|
||||||
|
) {
|
||||||
|
if self.directions.is_empty() || foliage_positions.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut blacklist: HashSet<BlockPos> = HashSet::new();
|
||||||
|
|
||||||
|
let mut shuffled = foliage_positions.to_vec();
|
||||||
|
for i in (1..shuffled.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32((i + 1) as i32) as usize;
|
||||||
|
shuffled.swap(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
for leaf_pos in shuffled {
|
||||||
|
let dir_idx = random.next_bounded_i32(self.directions.len() as i32) as usize;
|
||||||
|
let direction = self.directions[dir_idx];
|
||||||
|
let placement_pos = leaf_pos.offset(direction.to_offset());
|
||||||
|
|
||||||
|
if !blacklist.contains(&placement_pos)
|
||||||
|
&& random.next_f32() < self.probability
|
||||||
|
&& self.has_required_empty_blocks(chunk, leaf_pos, direction)
|
||||||
|
{
|
||||||
|
for dx in -self.exclusion_radius_xz..=self.exclusion_radius_xz {
|
||||||
|
for dy in -self.exclusion_radius_y..=self.exclusion_radius_y {
|
||||||
|
for dz in -self.exclusion_radius_xz..=self.exclusion_radius_xz {
|
||||||
|
blacklist.insert(placement_pos.add(dx, dy, dz));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.set_block_state(
|
||||||
|
&placement_pos.0,
|
||||||
|
self.block_provider
|
||||||
|
.get(random, placement_pos, chunk, block_registry),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_required_empty_blocks<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &T,
|
||||||
|
leaf_pos: BlockPos,
|
||||||
|
direction: BlockDirection,
|
||||||
|
) -> bool {
|
||||||
|
let offset = direction.to_offset();
|
||||||
|
for i in 1..=self.required_empty_blocks {
|
||||||
|
let check_pos = leaf_pos.offset(offset * i);
|
||||||
|
if !chunk.is_air(&check_pos.0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -21,21 +21,28 @@ impl AttachedToLogsTreeDecorator {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
log_positions: &[BlockPos],
|
log_positions: &[BlockPos],
|
||||||
) {
|
) {
|
||||||
// TODO: shuffle
|
if self.directions.is_empty() || log_positions.is_empty() {
|
||||||
for pos in log_positions {
|
return;
|
||||||
// TODO: random
|
}
|
||||||
let pos = pos.offset(self.directions[0].to_offset());
|
|
||||||
if random.next_f32() > self.probability
|
let mut shuffled = log_positions.to_vec();
|
||||||
|| !GenerationCache::get_block_state(chunk, &pos.0)
|
for i in (1..shuffled.len()).rev() {
|
||||||
.to_state()
|
let j = random.next_bounded_i32((i + 1) as i32) as usize;
|
||||||
.is_air()
|
shuffled.swap(i, j);
|
||||||
{
|
}
|
||||||
continue;
|
|
||||||
|
for log_pos in shuffled {
|
||||||
|
let dir_idx = random.next_bounded_i32(self.directions.len() as i32) as usize;
|
||||||
|
let direction = self.directions[dir_idx];
|
||||||
|
let placement_pos = log_pos.offset(direction.to_offset());
|
||||||
|
|
||||||
|
if random.next_f32() <= self.probability && chunk.is_air(&placement_pos.0) {
|
||||||
|
chunk.set_block_state(
|
||||||
|
&placement_pos.0,
|
||||||
|
self.block_provider
|
||||||
|
.get(random, placement_pos, chunk, block_registry),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
chunk.set_block_state(
|
|
||||||
&pos.0,
|
|
||||||
self.block_provider.get(random, pos, chunk, block_registry),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,70 @@
|
|||||||
|
use pumpkin_data::{
|
||||||
|
Block, BlockDirection, BlockState,
|
||||||
|
block_properties::{BeehiveProperties, HorizontalFacing},
|
||||||
|
};
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct BeehiveTreeDecorator {
|
pub struct BeehiveTreeDecorator {
|
||||||
pub probability: f32,
|
pub probability: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BeehiveTreeDecorator {
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
log_positions: &[BlockPos],
|
||||||
|
foliage_positions: &[BlockPos],
|
||||||
|
) {
|
||||||
|
if log_positions.is_empty() || random.next_f32() >= self.probability {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let worldgen_facing = BlockDirection::South;
|
||||||
|
let spawn_directions = [
|
||||||
|
BlockDirection::East,
|
||||||
|
BlockDirection::West,
|
||||||
|
BlockDirection::South,
|
||||||
|
];
|
||||||
|
|
||||||
|
let hive_y = foliage_positions.first().map_or_else(
|
||||||
|
|| {
|
||||||
|
let last_log_y = log_positions.last().map_or(0, |p| p.0.y);
|
||||||
|
(log_positions[0].0.y + 1 + random.next_bounded_i32(3)).min(last_log_y)
|
||||||
|
},
|
||||||
|
|first_leaf| (first_leaf.0.y - 1).max(log_positions[0].0.y + 1),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut hive_placements: Vec<BlockPos> = Vec::new();
|
||||||
|
for log_pos in log_positions.iter().filter(|p| p.0.y == hive_y) {
|
||||||
|
for dir in spawn_directions {
|
||||||
|
hive_placements.push(log_pos.offset(dir.to_offset()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hive_placements.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in (1..hive_placements.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32((i + 1) as i32) as usize;
|
||||||
|
hive_placements.swap(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos in hive_placements {
|
||||||
|
let in_front = pos.offset(worldgen_facing.to_offset());
|
||||||
|
if chunk.is_air(&pos.0) && chunk.is_air(&in_front.0) {
|
||||||
|
let mut props = BeehiveProperties::default(&Block::BEE_NEST);
|
||||||
|
props.facing = HorizontalFacing::South;
|
||||||
|
let state_id = props.to_state_id(&Block::BEE_NEST);
|
||||||
|
chunk.set_block_state(&pos.0, BlockState::from_id(state_id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,43 @@
|
|||||||
pub struct CocoaTreeDecorator;
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::CocoaLikeProperties};
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
|
pub struct CocoaTreeDecorator {
|
||||||
|
pub probability: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CocoaTreeDecorator {
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
log_positions: &[BlockPos],
|
||||||
|
) {
|
||||||
|
if log_positions.is_empty() || random.next_f32() >= self.probability {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tree_y = log_positions[0].0.y;
|
||||||
|
for pos in log_positions.iter().filter(|p| p.0.y - tree_y <= 2) {
|
||||||
|
for facing in BlockDirection::horizontal_worldgen() {
|
||||||
|
if random.next_f32() <= 0.25 {
|
||||||
|
let dir = BlockDirection::from_cardinal_direction(facing);
|
||||||
|
let opposite = dir.opposite();
|
||||||
|
let cocoa_pos = pos.offset(opposite.to_offset());
|
||||||
|
if chunk.is_air(&cocoa_pos.0) {
|
||||||
|
let props = CocoaLikeProperties {
|
||||||
|
age: random.next_bounded_i32(3) as u8,
|
||||||
|
facing,
|
||||||
|
};
|
||||||
|
let state_id = props.to_state_id(&Block::COCOA);
|
||||||
|
chunk.set_block_state(&cocoa_pos.0, BlockState::from_id(state_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1 +1,57 @@
|
|||||||
pub struct CreakingHeartTreeDecorator;
|
use pumpkin_data::{
|
||||||
|
Block, BlockDirection, BlockState,
|
||||||
|
block_properties::{Axis, CreakingHeartLikeProperties, CreakingHeartState},
|
||||||
|
tag,
|
||||||
|
};
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
|
pub struct CreakingHeartTreeDecorator {
|
||||||
|
pub probability: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CreakingHeartTreeDecorator {
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
log_positions: &[BlockPos],
|
||||||
|
) {
|
||||||
|
if log_positions.is_empty() || random.next_f32() >= self.probability {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut shuffled = log_positions.to_vec();
|
||||||
|
for i in (1..shuffled.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32((i + 1) as i32) as usize;
|
||||||
|
shuffled.swap(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
for pos in shuffled {
|
||||||
|
let mut all_logs = true;
|
||||||
|
for dir in BlockDirection::all() {
|
||||||
|
let neighbor = pos.offset(dir.to_offset());
|
||||||
|
let block_id = GenerationCache::get_block_state(chunk, &neighbor.0).to_block_id();
|
||||||
|
if !block_id.has_tag(tag::Block::MINECRAFT_LOGS) {
|
||||||
|
all_logs = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if all_logs {
|
||||||
|
let props = CreakingHeartLikeProperties {
|
||||||
|
r#axis: Axis::Y,
|
||||||
|
r#creaking_heart_state: CreakingHeartState::Dormant,
|
||||||
|
r#natural: true,
|
||||||
|
};
|
||||||
|
let state_id = props.to_state_id(&Block::CREAKING_HEART);
|
||||||
|
chunk.set_block_state(&pos.0, BlockState::from_id(state_id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -53,15 +53,27 @@ impl TreeDecorator {
|
|||||||
TrunkVineTreeDecorator::generate(chunk, random, log_positions);
|
TrunkVineTreeDecorator::generate(chunk, random, log_positions);
|
||||||
}
|
}
|
||||||
Self::LeaveVine(decorator) => decorator.generate(chunk, random, foliage_positions),
|
Self::LeaveVine(decorator) => decorator.generate(chunk, random, foliage_positions),
|
||||||
Self::PaleMoss(_decorator) => {}
|
Self::PaleMoss(decorator) => {
|
||||||
Self::CreakingHeart(_decorator) => {}
|
decorator.generate(chunk, random, log_positions, foliage_positions);
|
||||||
Self::Cocoa(_decorator) => {}
|
}
|
||||||
Self::Beehive(_decorator) => {}
|
Self::CreakingHeart(decorator) => {
|
||||||
Self::AlterGround(_decorator) => {}
|
decorator.generate(chunk, random, log_positions);
|
||||||
|
}
|
||||||
|
Self::Cocoa(decorator) => {
|
||||||
|
decorator.generate(chunk, random, log_positions);
|
||||||
|
}
|
||||||
|
Self::Beehive(decorator) => {
|
||||||
|
decorator.generate(chunk, random, log_positions, foliage_positions);
|
||||||
|
}
|
||||||
|
Self::AlterGround(decorator) => {
|
||||||
|
decorator.generate(chunk, block_registry, random, root_positions, log_positions);
|
||||||
|
}
|
||||||
Self::PlaceOnGround(decorator) => {
|
Self::PlaceOnGround(decorator) => {
|
||||||
decorator.generate(chunk, block_registry, random, root_positions, log_positions);
|
decorator.generate(chunk, block_registry, random, root_positions, log_positions);
|
||||||
}
|
}
|
||||||
Self::AttachedToLeaves(_decorator) => {}
|
Self::AttachedToLeaves(decorator) => {
|
||||||
|
decorator.generate(chunk, block_registry, random, foliage_positions);
|
||||||
|
}
|
||||||
Self::AttachedToLogs(decorator) => {
|
Self::AttachedToLogs(decorator) => {
|
||||||
decorator.generate(chunk, block_registry, random, log_positions);
|
decorator.generate(chunk, block_registry, random, log_positions);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1,62 @@
|
|||||||
pub struct PaleMossTreeDecorator;
|
use pumpkin_data::{Block, BlockState, block_properties::PaleHangingMossLikeProperties};
|
||||||
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
|
pub struct PaleMossTreeDecorator {
|
||||||
|
pub leaves_probability: f32,
|
||||||
|
pub trunk_probability: f32,
|
||||||
|
pub ground_probability: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PaleMossTreeDecorator {
|
||||||
|
pub fn generate<T: GenerationCache>(
|
||||||
|
&self,
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
log_positions: &[BlockPos],
|
||||||
|
foliage_positions: &[BlockPos],
|
||||||
|
) {
|
||||||
|
if log_positions.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for &pos in log_positions {
|
||||||
|
if random.next_f32() < self.trunk_probability {
|
||||||
|
let down = pos.down();
|
||||||
|
if chunk.is_air(&down.0) {
|
||||||
|
Self::add_moss_hanger(chunk, random, down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for &pos in foliage_positions {
|
||||||
|
if random.next_f32() < self.leaves_probability {
|
||||||
|
let down = pos.down();
|
||||||
|
if chunk.is_air(&down.0) {
|
||||||
|
Self::add_moss_hanger(chunk, random, down);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_moss_hanger<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
mut pos: BlockPos,
|
||||||
|
) {
|
||||||
|
while chunk.is_air(&pos.down().0) && random.next_f32() >= 0.5 {
|
||||||
|
let props = PaleHangingMossLikeProperties { tip: false };
|
||||||
|
let state_id = props.to_state_id(&Block::PALE_HANGING_MOSS);
|
||||||
|
chunk.set_block_state(&pos.0, BlockState::from_id(state_id));
|
||||||
|
pos = pos.down();
|
||||||
|
}
|
||||||
|
|
||||||
|
let props = PaleHangingMossLikeProperties { tip: true };
|
||||||
|
let state_id = props.to_state_id(&Block::PALE_HANGING_MOSS);
|
||||||
|
chunk.set_block_state(&pos.0, BlockState::from_id(state_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -66,18 +66,16 @@ impl PlaceOnGroundTreeDecorator {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
) {
|
) {
|
||||||
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
let state = GenerationCache::get_block_state(chunk, &pos.0);
|
||||||
let up_pos = pos.up();
|
let ground_pos = pos.down();
|
||||||
let up_state = GenerationCache::get_block_state(chunk, &up_pos.0);
|
let ground_state = GenerationCache::get_block_state(chunk, &ground_pos.0);
|
||||||
// TODO
|
if (state.to_state().is_air() || state.to_block_id() == Block::VINE)
|
||||||
if (up_state.to_state().is_air() || up_state.to_block_id() == Block::VINE)
|
&& ground_state.to_state().is_full_cube()
|
||||||
&& state.to_state().is_full_cube()
|
&& !ground_state.to_block_id().has_tag(MINECRAFT_LEAVES)
|
||||||
&& !state.to_block_id().has_tag(MINECRAFT_LEAVES)
|
|
||||||
// TODO: using heightmap seems not to work
|
|
||||||
{
|
{
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(
|
||||||
&up_pos.0,
|
&pos.0,
|
||||||
self.block_state_provider
|
self.block_state_provider
|
||||||
.get(random, up_pos, chunk, block_registry),
|
.get(random, pos, chunk, block_registry),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{Block, BlockState, block_properties::KelpLikeProperties};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::position::BlockPos,
|
math::position::BlockPos,
|
||||||
random::{RandomGenerator, RandomImpl},
|
random::{RandomGenerator, RandomImpl},
|
||||||
@ -22,34 +22,31 @@ impl TwistingVinesFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
if Self::is_invalid_location(chunk, &pos) {
|
if Self::is_invalid_placement_location(chunk, pos) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut placed = false;
|
let mut placed = false;
|
||||||
|
|
||||||
for _ in 0..self.spread_width * self.spread_width {
|
for _ in 0..self.spread_width * self.spread_width {
|
||||||
let offset_x = random.next_bounded_i32(self.spread_width)
|
let offset_x = random.next_inbetween_i32(-self.spread_width, self.spread_width);
|
||||||
- random.next_bounded_i32(self.spread_width);
|
let offset_y = random.next_inbetween_i32(-self.spread_height, self.spread_height);
|
||||||
let offset_y = random.next_bounded_i32(self.spread_height)
|
let offset_z = random.next_inbetween_i32(-self.spread_width, self.spread_width);
|
||||||
- random.next_bounded_i32(self.spread_height);
|
|
||||||
let offset_z = random.next_bounded_i32(self.spread_width)
|
|
||||||
- random.next_bounded_i32(self.spread_width);
|
|
||||||
|
|
||||||
let mut mutable_pos = pos.add(offset_x, offset_y, offset_z);
|
let mut place_pos = pos.add(offset_x, offset_y, offset_z);
|
||||||
|
|
||||||
if self.find_target_y(chunk, &mut mutable_pos)
|
if Self::find_first_air_block_above_ground(chunk, &mut place_pos)
|
||||||
&& !Self::is_invalid_location(chunk, &mutable_pos)
|
&& !Self::is_invalid_placement_location(chunk, place_pos)
|
||||||
{
|
{
|
||||||
let mut height = random.next_bounded_i32(self.max_height) + 1;
|
let mut vine_height = random.next_inbetween_i32(1, self.max_height);
|
||||||
if random.next_bounded_i32(6) == 0 {
|
if random.next_bounded_i32(6) == 0 {
|
||||||
height *= 2;
|
vine_height *= 2;
|
||||||
}
|
}
|
||||||
if random.next_bounded_i32(10) == 0 {
|
if random.next_bounded_i32(5) == 0 {
|
||||||
height = 1;
|
vine_height = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::generate_column(chunk, random, &mutable_pos, height);
|
Self::place_twisting_vines_column(chunk, random, place_pos, vine_height, 17, 25);
|
||||||
placed = true;
|
placed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -57,75 +54,59 @@ impl TwistingVinesFeature {
|
|||||||
placed
|
placed
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_column<T: GenerationCache>(
|
fn find_first_air_block_above_ground<T: GenerationCache>(
|
||||||
|
chunk: &T,
|
||||||
|
place_pos: &mut BlockPos,
|
||||||
|
) -> bool {
|
||||||
|
loop {
|
||||||
|
*place_pos = place_pos.down();
|
||||||
|
if place_pos.0.y <= chunk.bottom_y() as i32 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if !chunk.is_air(&place_pos.0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*place_pos = place_pos.up();
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn place_twisting_vines_column<T: GenerationCache>(
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: &BlockPos,
|
mut place_pos: BlockPos,
|
||||||
height: i32,
|
total_height: i32,
|
||||||
|
min_age: i32,
|
||||||
|
max_age: i32,
|
||||||
) {
|
) {
|
||||||
let mut current_pos = *pos;
|
for height in 1..=total_height {
|
||||||
for i in 0..height {
|
if chunk.is_air(&place_pos.0) {
|
||||||
if !GenerationCache::get_block_state(chunk, ¤t_pos.0)
|
if height == total_height || !chunk.is_air(&place_pos.up().0) {
|
||||||
.to_state()
|
let age = random.next_inbetween_i32(min_age, max_age);
|
||||||
.is_air()
|
let mut props = KelpLikeProperties::default(&Block::TWISTING_VINES);
|
||||||
{
|
props.age = age as u8;
|
||||||
break;
|
let state_id = props.to_state_id(&Block::TWISTING_VINES);
|
||||||
|
chunk.set_block_state(&place_pos.0, BlockState::from_id(state_id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.set_block_state(&place_pos.0, Block::TWISTING_VINES_PLANT.default_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
if i == height - 1
|
place_pos = place_pos.up();
|
||||||
|| !GenerationCache::get_block_state(chunk, ¤t_pos.up().0)
|
|
||||||
.to_state()
|
|
||||||
.is_air()
|
|
||||||
{
|
|
||||||
// Top part
|
|
||||||
let _age = 17 + random.next_bounded_i32(25 - 17 + 1);
|
|
||||||
// We should set the age property here, but Pumpkin's BlockState might not support it easily yet or we just use default
|
|
||||||
// For now, let's just use the block.
|
|
||||||
// TODO: Set age property
|
|
||||||
chunk.set_block_state(¤t_pos.0, Block::TWISTING_VINES.default_state);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
chunk.set_block_state(¤t_pos.0, Block::TWISTING_VINES_PLANT.default_state);
|
|
||||||
current_pos = current_pos.up();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_invalid_location<T: GenerationCache>(chunk: &T, pos: &BlockPos) -> bool {
|
fn is_invalid_placement_location<T: GenerationCache>(chunk: &T, pos: BlockPos) -> bool {
|
||||||
if !GenerationCache::get_block_state(chunk, &pos.0)
|
if !chunk.is_air(&pos.0) {
|
||||||
.to_state()
|
|
||||||
.is_air()
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let block_below = GenerationCache::get_block_state(chunk, &pos.down().0).to_state();
|
let below = pos.down();
|
||||||
block_below != Block::WARPED_NYLIUM.default_state
|
let below_id = GenerationCache::get_block_state(chunk, &below.0).to_block_id();
|
||||||
&& block_below != Block::WARPED_WART_BLOCK.default_state
|
below_id != Block::NETHERRACK.id
|
||||||
&& block_below != Block::TWISTING_VINES.default_state
|
&& below_id != Block::WARPED_NYLIUM.id
|
||||||
&& block_below != Block::TWISTING_VINES_PLANT.default_state
|
&& below_id != Block::WARPED_WART_BLOCK.id
|
||||||
}
|
|
||||||
|
|
||||||
fn find_target_y<T: GenerationCache>(&self, chunk: &T, pos: &mut BlockPos) -> bool {
|
|
||||||
// Try to find a valid floor by looking down
|
|
||||||
let mut current = *pos;
|
|
||||||
for _ in 0..self.spread_height {
|
|
||||||
if GenerationCache::get_block_state(chunk, ¤t.0)
|
|
||||||
.to_state()
|
|
||||||
.is_air()
|
|
||||||
{
|
|
||||||
let below = current.down();
|
|
||||||
let block_below = GenerationCache::get_block_state(chunk, &below.0).to_state();
|
|
||||||
if block_below == Block::WARPED_NYLIUM.default_state
|
|
||||||
|| block_below == Block::WARPED_WART_BLOCK.default_state
|
|
||||||
|| block_below == Block::TWISTING_VINES.default_state
|
|
||||||
|| block_below == Block::TWISTING_VINES_PLANT.default_state
|
|
||||||
{
|
|
||||||
*pos = current;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
current = current.down();
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use pumpkin_data::{Block, BlockDirection, BlockState};
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::VineLikeProperties};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
@ -19,17 +19,19 @@ impl VinesFeature {
|
|||||||
if !chunk.is_air(&pos.0) {
|
if !chunk.is_air(&pos.0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for dir in BlockDirection::all() {
|
for dir in BlockDirection::all() {
|
||||||
// TODO
|
if dir == BlockDirection::Down {
|
||||||
if dir == BlockDirection::Down
|
|
||||||
|| !GenerationCache::get_block_state(chunk, &pos.offset(dir.to_offset()).0)
|
|
||||||
.to_state()
|
|
||||||
.is_full_cube()
|
|
||||||
{
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut vine =
|
|
||||||
pumpkin_data::block_properties::VineLikeProperties::default(&Block::VINE);
|
let neighbor = pos.offset(dir.to_offset());
|
||||||
|
let neighbor_state = GenerationCache::get_block_state(chunk, &neighbor.0);
|
||||||
|
if !neighbor_state.to_state().is_side_solid(dir.opposite()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut vine = VineLikeProperties::default(&Block::VINE);
|
||||||
vine.north = dir == BlockDirection::North;
|
vine.north = dir == BlockDirection::North;
|
||||||
vine.east = dir == BlockDirection::East;
|
vine.east = dir == BlockDirection::East;
|
||||||
vine.south = dir == BlockDirection::South;
|
vine.south = dir == BlockDirection::South;
|
||||||
@ -38,6 +40,7 @@ impl VinesFeature {
|
|||||||
chunk.set_block_state(&pos.0, BlockState::from_id(vine.to_state_id(&Block::VINE)));
|
chunk.set_block_state(&pos.0, BlockState::from_id(vine.to_state_id(&Block::VINE)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +1,14 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
use pumpkin_util::{
|
||||||
|
math::{position::BlockPos, vector3::Vector3},
|
||||||
|
random::RandomGenerator,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
pub struct VoidStartPlatformFeature;
|
pub struct VoidStartPlatformFeature;
|
||||||
|
|
||||||
impl VoidStartPlatformFeature {
|
impl VoidStartPlatformFeature {
|
||||||
#[allow(clippy::unused_self)]
|
|
||||||
pub fn generate<T: GenerationCache>(
|
pub fn generate<T: GenerationCache>(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut T,
|
chunk: &mut T,
|
||||||
@ -16,12 +18,33 @@ impl VoidStartPlatformFeature {
|
|||||||
_random: &mut RandomGenerator,
|
_random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
for dx in -1..=1 {
|
let chunk_x = pos.0.x >> 4;
|
||||||
for dz in -1..=1 {
|
let chunk_z = pos.0.z >> 4;
|
||||||
let target = BlockPos::new(pos.0.x + dx, pos.0.y, pos.0.z + dz);
|
|
||||||
chunk.set_block_state(&target.0, Block::OBSIDIAN.default_state);
|
if chunk_x.abs().max(chunk_z.abs()) > 1 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let platform_y = pos.0.y + 3;
|
||||||
|
let min_x = chunk_x * 16;
|
||||||
|
let max_x = min_x + 15;
|
||||||
|
let min_z = chunk_z * 16;
|
||||||
|
let max_z = min_z + 15;
|
||||||
|
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
if (8 - x).abs().max((8 - z).abs()) <= 16 {
|
||||||
|
let target = Vector3::new(x, platform_y, z);
|
||||||
|
let state = if x == 8 && z == 8 {
|
||||||
|
Block::COBBLESTONE.default_state
|
||||||
|
} else {
|
||||||
|
Block::STONE.default_state
|
||||||
|
};
|
||||||
|
chunk.set_block_state(&target, state);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,8 @@
|
|||||||
use pumpkin_data::Block;
|
use pumpkin_data::{Block, BlockDirection, BlockState, block_properties::KelpLikeProperties};
|
||||||
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator, random::RandomImpl};
|
use pumpkin_util::{
|
||||||
|
math::position::BlockPos,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::generation::proto_chunk::GenerationCache;
|
use crate::generation::proto_chunk::GenerationCache;
|
||||||
|
|
||||||
@ -16,21 +19,109 @@ impl WeepingVinesFeature {
|
|||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let ceiling_pos = pos.up();
|
if !chunk.is_air(&pos.0) {
|
||||||
let ceiling_block = GenerationCache::get_block_state(chunk, &ceiling_pos.0).to_block_id();
|
|
||||||
if ceiling_block == Block::AIR.id {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let length = random.next_bounded_i32(4) + 3;
|
let above = pos.up();
|
||||||
for i in 0..length {
|
let above_id = GenerationCache::get_block_state(chunk, &above.0).to_block_id();
|
||||||
let target = BlockPos::new(pos.0.x, pos.0.y - i, pos.0.z);
|
if above_id != Block::NETHERRACK.id && above_id != Block::NETHER_WART_BLOCK.id {
|
||||||
if chunk.is_air(&target.0) {
|
return false;
|
||||||
chunk.set_block_state(&target.0, Block::WEEPING_VINES.default_state);
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::place_roof_nether_wart(chunk, random, pos);
|
||||||
|
Self::place_roof_weeping_vines(chunk, random, pos);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn place_roof_nether_wart<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
origin: BlockPos,
|
||||||
|
) {
|
||||||
|
chunk.set_block_state(&origin.0, Block::NETHER_WART_BLOCK.default_state);
|
||||||
|
|
||||||
|
for _ in 0..200 {
|
||||||
|
let place_pos = origin.add(
|
||||||
|
random.next_bounded_i32(6) - random.next_bounded_i32(6),
|
||||||
|
random.next_bounded_i32(2) - random.next_bounded_i32(5),
|
||||||
|
random.next_bounded_i32(6) - random.next_bounded_i32(6),
|
||||||
|
);
|
||||||
|
|
||||||
|
if chunk.is_air(&place_pos.0) {
|
||||||
|
let mut neighbours = 0;
|
||||||
|
for dir in BlockDirection::all() {
|
||||||
|
let neighbour_pos = place_pos.offset(dir.to_offset());
|
||||||
|
let id =
|
||||||
|
GenerationCache::get_block_state(chunk, &neighbour_pos.0).to_block_id();
|
||||||
|
if id == Block::NETHERRACK.id || id == Block::NETHER_WART_BLOCK.id {
|
||||||
|
neighbours += 1;
|
||||||
|
}
|
||||||
|
if neighbours > 1 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if neighbours == 1 {
|
||||||
|
chunk.set_block_state(&place_pos.0, Block::NETHER_WART_BLOCK.default_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place_roof_weeping_vines<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
origin: BlockPos,
|
||||||
|
) {
|
||||||
|
for _ in 0..100 {
|
||||||
|
let place_pos = origin.add(
|
||||||
|
random.next_bounded_i32(8) - random.next_bounded_i32(8),
|
||||||
|
random.next_bounded_i32(2) - random.next_bounded_i32(7),
|
||||||
|
random.next_bounded_i32(8) - random.next_bounded_i32(8),
|
||||||
|
);
|
||||||
|
|
||||||
|
if chunk.is_air(&place_pos.0) {
|
||||||
|
let above = place_pos.up();
|
||||||
|
let above_id = GenerationCache::get_block_state(chunk, &above.0).to_block_id();
|
||||||
|
if above_id == Block::NETHERRACK.id || above_id == Block::NETHER_WART_BLOCK.id {
|
||||||
|
let mut vine_height = random.next_bounded_i32(8) + 1;
|
||||||
|
if random.next_bounded_i32(6) == 0 {
|
||||||
|
vine_height *= 2;
|
||||||
|
}
|
||||||
|
if random.next_bounded_i32(5) == 0 {
|
||||||
|
vine_height = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::place_weeping_vines_column(chunk, random, place_pos, vine_height, 17, 25);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn place_weeping_vines_column<T: GenerationCache>(
|
||||||
|
chunk: &mut T,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
mut place_pos: BlockPos,
|
||||||
|
total_height: i32,
|
||||||
|
min_age: i32,
|
||||||
|
max_age: i32,
|
||||||
|
) {
|
||||||
|
for height in 0..=total_height {
|
||||||
|
if chunk.is_air(&place_pos.0) {
|
||||||
|
if height == total_height || !chunk.is_air(&place_pos.down().0) {
|
||||||
|
let age = random.next_inbetween_i32(min_age, max_age);
|
||||||
|
let mut props = KelpLikeProperties::default(&Block::WEEPING_VINES);
|
||||||
|
props.age = age as u8;
|
||||||
|
let state_id = props.to_state_id(&Block::WEEPING_VINES);
|
||||||
|
chunk.set_block_state(&place_pos.0, BlockState::from_id(state_id));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.set_block_state(&place_pos.0, Block::WEEPING_VINES_PLANT.default_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
place_pos = place_pos.down();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,6 +96,9 @@ pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
|||||||
chunk_x: i32,
|
chunk_x: i32,
|
||||||
chunk_z: i32,
|
chunk_z: i32,
|
||||||
) -> Option<&crate::generation::blender::blending_data::BlendingData>;
|
) -> Option<&crate::generation::blender::blending_data::BlendingData>;
|
||||||
|
fn get_sea_level(&self) -> i32 {
|
||||||
|
63
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const AIR_BLOCK: Block = Block::AIR;
|
const AIR_BLOCK: Block = Block::AIR;
|
||||||
@ -184,6 +187,11 @@ impl TerrainCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ProtoChunk {
|
impl ProtoChunk {
|
||||||
|
#[must_use]
|
||||||
|
pub const fn get_sea_level(&self) -> i32 {
|
||||||
|
63
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn has_structure(&self, key: StructureKeys) -> bool {
|
pub(crate) fn has_structure(&self, key: StructureKeys) -> bool {
|
||||||
self.structure_starts.contains_key(&key)
|
self.structure_starts.contains_key(&key)
|
||||||
@ -1717,4 +1725,7 @@ impl GenerationCache for ProtoChunk {
|
|||||||
) -> Option<&crate::generation::blender::blending_data::BlendingData> {
|
) -> Option<&crate::generation::blender::blending_data::BlendingData> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
fn get_sea_level(&self) -> i32 {
|
||||||
|
Self::get_sea_level(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@ use pumpkin_util::{
|
|||||||
math::floor_div,
|
math::floor_div,
|
||||||
random::{
|
random::{
|
||||||
RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, legacy_rand::LegacyRand,
|
RandomGenerator, RandomImpl, get_carver_seed, get_region_seed, legacy_rand::LegacyRand,
|
||||||
xoroshiro128::Xoroshiro,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::f64::consts::PI;
|
use std::f64::consts::PI;
|
||||||
@ -250,8 +249,8 @@ fn should_generate_frequency(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
match method {
|
match method {
|
||||||
FrequencyReductionMethod::Default => {
|
FrequencyReductionMethod::Default => {
|
||||||
let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, salt);
|
let region_seed = get_region_seed(seed as u64, salt as i32, chunk_x, chunk_z as u32);
|
||||||
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed));
|
let mut random = LegacyRand::from_seed(region_seed);
|
||||||
random.next_f32() < frequency
|
random.next_f32() < frequency
|
||||||
}
|
}
|
||||||
FrequencyReductionMethod::LegacyType1 => {
|
FrequencyReductionMethod::LegacyType1 => {
|
||||||
@ -263,12 +262,12 @@ fn should_generate_frequency(
|
|||||||
}
|
}
|
||||||
FrequencyReductionMethod::LegacyType2 => {
|
FrequencyReductionMethod::LegacyType2 => {
|
||||||
let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, 10387320);
|
let region_seed = get_region_seed(seed as u64, chunk_x, chunk_z, 10387320);
|
||||||
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(region_seed));
|
let mut random = LegacyRand::from_seed(region_seed);
|
||||||
random.next_f32() < frequency
|
random.next_f32() < frequency
|
||||||
}
|
}
|
||||||
FrequencyReductionMethod::LegacyType3 => {
|
FrequencyReductionMethod::LegacyType3 => {
|
||||||
let carver_seed = get_carver_seed(seed as u64, chunk_x, chunk_z);
|
let carver_seed = get_carver_seed(seed as u64, chunk_x, chunk_z);
|
||||||
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
let mut random = LegacyRand::from_seed(carver_seed);
|
||||||
random.next_f64() < f64::from(frequency)
|
random.next_f64() < f64::from(frequency)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,20 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::{Block, BlockDirection, BlockId};
|
use pumpkin_data::Block;
|
||||||
use pumpkin_nbt::compound::NbtCompound;
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
HeightMap,
|
BlockDirection, HeightMap,
|
||||||
math::{block_box::BlockBox, position::BlockPos},
|
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
|
||||||
random::{RandomGenerator, RandomImpl, hash_block_pos, legacy_rand::LegacyRand},
|
random::{RandomGenerator, RandomImpl, hash_block_pos, legacy_rand::LegacyRand},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ProtoChunk,
|
ProtoChunk,
|
||||||
generation::{
|
generation::structure::{
|
||||||
positions::chunk_pos::{get_center_x, get_center_z},
|
piece::StructurePieceType,
|
||||||
structure::{
|
structures::{
|
||||||
piece::StructurePieceType,
|
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
||||||
structures::{
|
StructurePiecesCollector, StructurePosition, WorldPortalExt,
|
||||||
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
|
||||||
StructurePiecesCollector, StructurePosition, WorldPortalExt,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -29,13 +26,12 @@ impl StructureGenerator for BuriedTreasureGenerator {
|
|||||||
&self,
|
&self,
|
||||||
context: StructureGeneratorContext<'_>,
|
context: StructureGeneratorContext<'_>,
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
let x = get_center_x(context.chunk_x);
|
let x = (context.chunk_x << 4) + 9;
|
||||||
let z = get_center_z(context.chunk_z);
|
let z = (context.chunk_z << 4) + 9;
|
||||||
|
|
||||||
let bounding_box = BlockBox::new(x, -64, z, x, 320, z);
|
let bounding_box = BlockBox::new(x, 90, z, x, 90, z);
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
let mut collector = StructurePiecesCollector::default();
|
||||||
|
|
||||||
collector.add_piece(Box::new(BuriedTreasurePiece {
|
collector.add_piece(Box::new(BuriedTreasurePiece {
|
||||||
piece: StructurePiece::new(StructurePieceType::BuriedTreasure, bounding_box, 0),
|
piece: StructurePiece::new(StructurePieceType::BuriedTreasure, bounding_box, 0),
|
||||||
}));
|
}));
|
||||||
@ -55,88 +51,6 @@ impl StructurePieceBase for BuriedTreasurePiece {
|
|||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
fn place(
|
|
||||||
&mut self,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
_block_registry: &dyn WorldPortalExt,
|
|
||||||
_random: &mut RandomGenerator,
|
|
||||||
_seed: i64,
|
|
||||||
_chunk_box: &BlockBox,
|
|
||||||
) {
|
|
||||||
let boundingbox = self.bounding_box();
|
|
||||||
let y = chunk.get_top_y(
|
|
||||||
&HeightMap::OceanFloorWg,
|
|
||||||
boundingbox.min.x,
|
|
||||||
boundingbox.min.z,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut pos = BlockPos::new(boundingbox.min.x, y, boundingbox.min.z);
|
|
||||||
let bottom_y = chunk.bottom_y() as i32;
|
|
||||||
|
|
||||||
for _ in (bottom_y..=y).rev() {
|
|
||||||
let state = chunk.get_block_state(&pos.0);
|
|
||||||
let down_pos = pos.down();
|
|
||||||
let down_raw_state = chunk.get_block_state(&down_pos.0);
|
|
||||||
let down_block = down_raw_state.to_block_id();
|
|
||||||
|
|
||||||
if down_block == Block::SANDSTONE
|
|
||||||
|| down_block == Block::STONE
|
|
||||||
|| down_block == Block::ANDESITE
|
|
||||||
|| down_block == Block::GRANITE
|
|
||||||
|| down_block == Block::DIORITE
|
|
||||||
{
|
|
||||||
for dir in BlockDirection::all() {
|
|
||||||
let offset_pos = pos.offset(dir.to_offset());
|
|
||||||
let dir_state = chunk.get_block_state(&offset_pos.0);
|
|
||||||
|
|
||||||
if !dir_state.to_state().is_air() && !Self::is_liquid(dir_state.to_block_id()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let down_offset_pos = offset_pos.down();
|
|
||||||
let down_offset_state = chunk.get_block_state(&down_offset_pos.0);
|
|
||||||
|
|
||||||
if (down_offset_state.to_state().is_air()
|
|
||||||
|| Self::is_liquid(down_offset_state.to_block_id()))
|
|
||||||
&& dir != BlockDirection::Up
|
|
||||||
{
|
|
||||||
chunk.set_block_state(
|
|
||||||
offset_pos.0.x,
|
|
||||||
offset_pos.0.y,
|
|
||||||
offset_pos.0.z,
|
|
||||||
down_raw_state.to_state(),
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let state1 =
|
|
||||||
if state.to_state().is_air() || Self::is_liquid(state.to_block_id()) {
|
|
||||||
Block::SAND.default_state
|
|
||||||
} else {
|
|
||||||
state.to_state()
|
|
||||||
};
|
|
||||||
chunk.set_block_state(offset_pos.0.x, offset_pos.0.y, offset_pos.0.z, state1);
|
|
||||||
}
|
|
||||||
|
|
||||||
chunk.set_block_state(pos.0.x, pos.0.y, pos.0.z, Block::CHEST.default_state);
|
|
||||||
|
|
||||||
let mut chest_nbt = NbtCompound::new();
|
|
||||||
chest_nbt.put_string("id", "minecraft:chest".to_string());
|
|
||||||
chest_nbt.put_int("x", pos.0.x);
|
|
||||||
chest_nbt.put_int("y", pos.0.y);
|
|
||||||
chest_nbt.put_int("z", pos.0.z);
|
|
||||||
chest_nbt.put_string("LootTable", "minecraft:chests/buried_treasure".to_string());
|
|
||||||
|
|
||||||
let mut random =
|
|
||||||
LegacyRand::from_seed(hash_block_pos(pos.0.x, pos.0.y, pos.0.z) as u64);
|
|
||||||
chest_nbt.put_long("LootTableSeed", random.next_i64());
|
|
||||||
|
|
||||||
chunk.add_block_entity(chest_nbt);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
pos = pos.down();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_structure_piece(&self) -> &StructurePiece {
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
&self.piece
|
&self.piece
|
||||||
@ -145,10 +59,88 @@ impl StructurePieceBase for BuriedTreasurePiece {
|
|||||||
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
&mut self.piece
|
&mut self.piece
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl BuriedTreasurePiece {
|
fn place(
|
||||||
fn is_liquid(id: BlockId) -> bool {
|
&mut self,
|
||||||
id == BlockId::WATER || id == BlockId::LAVA
|
chunk: &mut ProtoChunk,
|
||||||
|
_block_registry: &dyn WorldPortalExt,
|
||||||
|
_random: &mut RandomGenerator,
|
||||||
|
_seed: i64,
|
||||||
|
chunk_box: &BlockBox,
|
||||||
|
) {
|
||||||
|
let bb = self.bounding_box();
|
||||||
|
let y = chunk.get_top_y(&HeightMap::OceanFloorWg, bb.min.x, bb.min.z);
|
||||||
|
let min_y = chunk.bottom_y() as i32;
|
||||||
|
let mut cur_y = y;
|
||||||
|
|
||||||
|
while cur_y > min_y {
|
||||||
|
let pos = Vector3::new(bb.min.x, cur_y, bb.min.z);
|
||||||
|
let below_pos = Vector3::new(bb.min.x, cur_y - 1, bb.min.z);
|
||||||
|
|
||||||
|
let current_state = chunk.get_block_state(&pos).to_state();
|
||||||
|
let below_state = chunk.get_block_state(&below_pos).to_state();
|
||||||
|
let below_block = Block::from_state_id(below_state.id);
|
||||||
|
|
||||||
|
if *below_block == Block::SANDSTONE
|
||||||
|
|| *below_block == Block::STONE
|
||||||
|
|| *below_block == Block::ANDESITE
|
||||||
|
|| *below_block == Block::GRANITE
|
||||||
|
|| *below_block == Block::DIORITE
|
||||||
|
{
|
||||||
|
let soft_state = if !current_state.is_air() && !current_state.is_liquid() {
|
||||||
|
current_state
|
||||||
|
} else {
|
||||||
|
Block::SAND.default_state
|
||||||
|
};
|
||||||
|
|
||||||
|
for dir in [
|
||||||
|
BlockDirection::Down,
|
||||||
|
BlockDirection::Up,
|
||||||
|
BlockDirection::North,
|
||||||
|
BlockDirection::South,
|
||||||
|
BlockDirection::West,
|
||||||
|
BlockDirection::East,
|
||||||
|
] {
|
||||||
|
let offset = dir.to_vector();
|
||||||
|
let rel_pos = pos + offset;
|
||||||
|
let rel_state = chunk.get_block_state(&rel_pos).to_state();
|
||||||
|
|
||||||
|
if rel_state.is_air() || rel_state.is_liquid() {
|
||||||
|
let below_rel_pos = rel_pos - Vector3::new(0, 1, 0);
|
||||||
|
let below_rel_state = chunk.get_block_state(&below_rel_pos).to_state();
|
||||||
|
|
||||||
|
if (below_rel_state.is_air() || below_rel_state.is_liquid())
|
||||||
|
&& dir != BlockDirection::Up
|
||||||
|
{
|
||||||
|
chunk.set_block_state(rel_pos.x, rel_pos.y, rel_pos.z, below_state);
|
||||||
|
} else {
|
||||||
|
chunk.set_block_state(rel_pos.x, rel_pos.y, rel_pos.z, soft_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.piece.bounding_box = BlockBox::new(pos.x, pos.y, pos.z, pos.x, pos.y, pos.z);
|
||||||
|
|
||||||
|
if chunk_box.contains_pos(&pos) {
|
||||||
|
chunk.set_block_state(pos.x, pos.y, pos.z, Block::CHEST.default_state);
|
||||||
|
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:chest".to_string());
|
||||||
|
nbt.put_int("x", pos.x);
|
||||||
|
nbt.put_int("y", pos.y);
|
||||||
|
nbt.put_int("z", pos.z);
|
||||||
|
nbt.put_string("LootTable", "minecraft:chests/buried_treasure".to_string());
|
||||||
|
|
||||||
|
let mut random =
|
||||||
|
LegacyRand::from_seed(hash_block_pos(pos.x, pos.y, pos.z) as u64);
|
||||||
|
nbt.put_long("LootTableSeed", random.next_i64());
|
||||||
|
|
||||||
|
chunk.add_block_entity(nbt);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur_y -= 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,9 +103,19 @@ impl DesertPyramidPiece {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn place_sand(&self, chunk: &mut ProtoChunk, bb: &BlockBox, x: i32, y: i32, z: i32) {
|
fn place_sand(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
bb: &BlockBox,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
potential_positions: &mut Vec<pumpkin_util::math::vector3::Vector3<i32>>,
|
||||||
|
) {
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Block::SAND.default_state, x, y, z, bb);
|
.add_block(chunk, Block::SAND.default_state, x, y, z, bb);
|
||||||
|
let world_pos = self.piece.offset_pos(x, y, z);
|
||||||
|
potential_positions.push(world_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
#[expect(clippy::too_many_arguments)]
|
||||||
@ -119,11 +129,12 @@ impl DesertPyramidPiece {
|
|||||||
x1: i32,
|
x1: i32,
|
||||||
y1: i32,
|
y1: i32,
|
||||||
z1: i32,
|
z1: i32,
|
||||||
|
potential_positions: &mut Vec<pumpkin_util::math::vector3::Vector3<i32>>,
|
||||||
) {
|
) {
|
||||||
for y in y0..=y1 {
|
for y in y0..=y1 {
|
||||||
for x in x0..=x1 {
|
for x in x0..=x1 {
|
||||||
for z in z0..=z1 {
|
for z in z0..=z1 {
|
||||||
self.place_sand(chunk, bb, x, y, z);
|
self.place_sand(chunk, bb, x, y, z, potential_positions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -158,14 +169,17 @@ impl DesertPyramidPiece {
|
|||||||
z0: i32,
|
z0: i32,
|
||||||
x1: i32,
|
x1: i32,
|
||||||
z1: i32,
|
z1: i32,
|
||||||
|
potential_positions: &mut Vec<pumpkin_util::math::vector3::Vector3<i32>>,
|
||||||
) {
|
) {
|
||||||
for x in x0..=x1 {
|
for x in x0..=x1 {
|
||||||
for z in z0..=z1 {
|
for z in z0..=z1 {
|
||||||
self.place_collapsed_roof_piece(chunk, bb, random, x, y0, z);
|
self.place_collapsed_roof_piece(chunk, bb, random, x, y0, z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Pick a random collapsed roof position for sus sand placement in
|
let rx = x0 + random.next_bounded_i32(x1 - x0 + 1);
|
||||||
// the structure-level afterPlace pass when brushable support exists.
|
let rz = z0 + random.next_bounded_i32(z1 - z0 + 1);
|
||||||
|
let sus_pos = self.piece.offset_pos(rx, y0, rz);
|
||||||
|
potential_positions.push(sus_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_place_chest(
|
fn try_place_chest(
|
||||||
@ -205,8 +219,36 @@ impl DesertPyramidPiece {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn add_cellar(&self, chunk: &mut ProtoChunk, bb: &BlockBox, random: &mut RandomGenerator) {
|
fn add_cellar(&self, chunk: &mut ProtoChunk, bb: &BlockBox, random: &mut RandomGenerator) {
|
||||||
self.add_cellar_stairs(chunk, bb, random);
|
let mut potential_positions = Vec::new();
|
||||||
self.add_cellar_room(chunk, bb, random);
|
self.add_cellar_stairs(chunk, bb, random, &mut potential_positions);
|
||||||
|
self.add_cellar_room(chunk, bb, random, &mut potential_positions);
|
||||||
|
|
||||||
|
for i in (1..potential_positions.len()).rev() {
|
||||||
|
let j = random.next_bounded_i32(i as i32 + 1) as usize;
|
||||||
|
potential_positions.swap(i, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
let sus_count =
|
||||||
|
(random.next_bounded_i32(5) + 4).min(potential_positions.len() as i32) as usize;
|
||||||
|
for pos in potential_positions.into_iter().take(sus_count) {
|
||||||
|
if bb.contains_pos(&pos) {
|
||||||
|
chunk.set_block_state(pos.x, pos.y, pos.z, Block::SUSPICIOUS_SAND.default_state);
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:brushable_block".to_string());
|
||||||
|
nbt.put_int("x", pos.x);
|
||||||
|
nbt.put_int("y", pos.y);
|
||||||
|
nbt.put_int("z", pos.z);
|
||||||
|
nbt.put_string(
|
||||||
|
"LootTable",
|
||||||
|
"minecraft:archaeology/desert_pyramid".to_string(),
|
||||||
|
);
|
||||||
|
nbt.put_long(
|
||||||
|
"LootTableSeed",
|
||||||
|
BlockPos::new(pos.x, pos.y, pos.z).as_long(),
|
||||||
|
);
|
||||||
|
chunk.add_block_entity(nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_cellar_stairs(
|
fn add_cellar_stairs(
|
||||||
@ -214,6 +256,7 @@ impl DesertPyramidPiece {
|
|||||||
chunk: &mut ProtoChunk,
|
chunk: &mut ProtoChunk,
|
||||||
bb: &BlockBox,
|
bb: &BlockBox,
|
||||||
random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
|
potential_positions: &mut Vec<pumpkin_util::math::vector3::Vector3<i32>>,
|
||||||
) {
|
) {
|
||||||
let west_stairs = Self::sandstone_stairs(HorizontalFacing::West);
|
let west_stairs = Self::sandstone_stairs(HorizontalFacing::West);
|
||||||
self.piece.add_block(chunk, west_stairs, 13, -1, 17, bb);
|
self.piece.add_block(chunk, west_stairs, 13, -1, 17, bb);
|
||||||
@ -225,12 +268,12 @@ impl DesertPyramidPiece {
|
|||||||
let (x, y, z) = (16, -4, 13);
|
let (x, y, z) = (16, -4, 13);
|
||||||
let variant = random.next_bool();
|
let variant = random.next_bool();
|
||||||
|
|
||||||
self.piece.add_block(chunk, sand, x - 4, y + 4, z + 4, bb);
|
self.place_sand(chunk, bb, x - 4, y + 4, z + 4, potential_positions);
|
||||||
self.piece.add_block(chunk, sand, x - 3, y + 4, z + 4, bb);
|
self.place_sand(chunk, bb, x - 3, y + 4, z + 4, potential_positions);
|
||||||
self.piece.add_block(chunk, sand, x - 2, y + 4, z + 4, bb);
|
self.place_sand(chunk, bb, x - 2, y + 4, z + 4, potential_positions);
|
||||||
self.piece.add_block(chunk, sand, x - 1, y + 4, z + 4, bb);
|
self.place_sand(chunk, bb, x - 1, y + 4, z + 4, potential_positions);
|
||||||
self.piece.add_block(chunk, sand, x, y + 4, z + 4, bb);
|
self.place_sand(chunk, bb, x, y + 4, z + 4, potential_positions);
|
||||||
self.piece.add_block(chunk, sand, x - 2, y + 3, z + 4, bb);
|
self.place_sand(chunk, bb, x - 2, y + 3, z + 4, potential_positions);
|
||||||
self.piece.add_block(
|
self.piece.add_block(
|
||||||
chunk,
|
chunk,
|
||||||
if variant { sand } else { sandstone },
|
if variant { sand } else { sandstone },
|
||||||
@ -247,13 +290,19 @@ impl DesertPyramidPiece {
|
|||||||
z + 4,
|
z + 4,
|
||||||
bb,
|
bb,
|
||||||
);
|
);
|
||||||
self.piece.add_block(chunk, sand, x - 1, y + 2, z + 4, bb);
|
self.place_sand(chunk, bb, x - 1, y + 2, z + 4, potential_positions);
|
||||||
self.piece.add_block(chunk, sandstone, x, y + 2, z + 4, bb);
|
self.piece.add_block(chunk, sandstone, x, y + 2, z + 4, bb);
|
||||||
self.piece.add_block(chunk, sand, x, y + 1, z + 4, bb);
|
self.place_sand(chunk, bb, x, y + 1, z + 4, potential_positions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::too_many_lines)]
|
#[expect(clippy::too_many_lines)]
|
||||||
fn add_cellar_room(&self, chunk: &mut ProtoChunk, bb: &BlockBox, random: &mut RandomGenerator) {
|
fn add_cellar_room(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
bb: &BlockBox,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
potential_positions: &mut Vec<pumpkin_util::math::vector3::Vector3<i32>>,
|
||||||
|
) {
|
||||||
let (x, y, z) = (16, -4, 13);
|
let (x, y, z) = (16, -4, 13);
|
||||||
let cut = Block::CUT_SANDSTONE.default_state;
|
let cut = Block::CUT_SANDSTONE.default_state;
|
||||||
let chiseled = Block::CHISELED_SANDSTONE.default_state;
|
let chiseled = Block::CHISELED_SANDSTONE.default_state;
|
||||||
@ -416,8 +465,28 @@ impl DesertPyramidPiece {
|
|||||||
cut,
|
cut,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.place_sand_box(chunk, bb, x - 2, y + 1, z - 2, x + 2, y + 3, z + 2);
|
self.place_sand_box(
|
||||||
self.place_collapsed_roof(chunk, bb, random, x - 2, y + 4, z - 2, x + 2, z + 2);
|
chunk,
|
||||||
|
bb,
|
||||||
|
x - 2,
|
||||||
|
y + 1,
|
||||||
|
z - 2,
|
||||||
|
x + 2,
|
||||||
|
y + 3,
|
||||||
|
z + 2,
|
||||||
|
potential_positions,
|
||||||
|
);
|
||||||
|
self.place_collapsed_roof(
|
||||||
|
chunk,
|
||||||
|
bb,
|
||||||
|
random,
|
||||||
|
x - 2,
|
||||||
|
y + 4,
|
||||||
|
z - 2,
|
||||||
|
x + 2,
|
||||||
|
z + 2,
|
||||||
|
potential_positions,
|
||||||
|
);
|
||||||
self.piece.add_block(chunk, blue, x, y, z, bb);
|
self.piece.add_block(chunk, blue, x, y, z, bb);
|
||||||
self.piece.add_block(chunk, orange, x + 1, y, z - 1, bb);
|
self.piece.add_block(chunk, orange, x + 1, y, z - 1, bb);
|
||||||
self.piece.add_block(chunk, orange, x + 1, y, z + 1, bb);
|
self.piece.add_block(chunk, orange, x + 1, y, z + 1, bb);
|
||||||
@ -429,24 +498,24 @@ impl DesertPyramidPiece {
|
|||||||
self.piece.add_block(chunk, orange, x, y, z - 2, bb);
|
self.piece.add_block(chunk, orange, x, y, z - 2, bb);
|
||||||
|
|
||||||
self.piece.add_block(chunk, orange, x + 3, y, z, bb);
|
self.piece.add_block(chunk, orange, x + 3, y, z, bb);
|
||||||
self.place_sand(chunk, bb, x + 3, y + 1, z);
|
self.place_sand(chunk, bb, x + 3, y + 1, z, potential_positions);
|
||||||
self.place_sand(chunk, bb, x + 3, y + 2, z);
|
self.place_sand(chunk, bb, x + 3, y + 2, z, potential_positions);
|
||||||
self.piece.add_block(chunk, cut, x + 4, y + 1, z, bb);
|
self.piece.add_block(chunk, cut, x + 4, y + 1, z, bb);
|
||||||
self.piece.add_block(chunk, chiseled, x + 4, y + 2, z, bb);
|
self.piece.add_block(chunk, chiseled, x + 4, y + 2, z, bb);
|
||||||
|
|
||||||
self.piece.add_block(chunk, orange, x - 3, y, z, bb);
|
self.piece.add_block(chunk, orange, x - 3, y, z, bb);
|
||||||
self.place_sand(chunk, bb, x - 3, y + 1, z);
|
self.place_sand(chunk, bb, x - 3, y + 1, z, potential_positions);
|
||||||
self.place_sand(chunk, bb, x - 3, y + 2, z);
|
self.place_sand(chunk, bb, x - 3, y + 2, z, potential_positions);
|
||||||
self.piece.add_block(chunk, cut, x - 4, y + 1, z, bb);
|
self.piece.add_block(chunk, cut, x - 4, y + 1, z, bb);
|
||||||
self.piece.add_block(chunk, chiseled, x - 4, y + 2, z, bb);
|
self.piece.add_block(chunk, chiseled, x - 4, y + 2, z, bb);
|
||||||
|
|
||||||
self.piece.add_block(chunk, orange, x, y, z + 3, bb);
|
self.piece.add_block(chunk, orange, x, y, z + 3, bb);
|
||||||
self.place_sand(chunk, bb, x, y + 1, z + 3);
|
self.place_sand(chunk, bb, x, y + 1, z + 3, potential_positions);
|
||||||
self.place_sand(chunk, bb, x, y + 2, z + 3);
|
self.place_sand(chunk, bb, x, y + 2, z + 3, potential_positions);
|
||||||
|
|
||||||
self.piece.add_block(chunk, orange, x, y, z - 3, bb);
|
self.piece.add_block(chunk, orange, x, y, z - 3, bb);
|
||||||
self.place_sand(chunk, bb, x, y + 1, z - 3);
|
self.place_sand(chunk, bb, x, y + 1, z - 3, potential_positions);
|
||||||
self.place_sand(chunk, bb, x, y + 2, z - 3);
|
self.place_sand(chunk, bb, x, y + 2, z - 3, potential_positions);
|
||||||
self.piece.add_block(chunk, cut, x, y + 1, z - 4, bb);
|
self.piece.add_block(chunk, cut, x, y + 1, z - 4, bb);
|
||||||
self.piece.add_block(chunk, chiseled, x, -2, z - 4, bb);
|
self.piece.add_block(chunk, chiseled, x, -2, z - 4, bb);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ use crate::generation::structure::template::{
|
|||||||
use pumpkin_util::math::block_box::BlockBox;
|
use pumpkin_util::math::block_box::BlockBox;
|
||||||
use pumpkin_util::math::position::BlockPos;
|
use pumpkin_util::math::position::BlockPos;
|
||||||
use pumpkin_util::math::vector3::Vector3;
|
use pumpkin_util::math::vector3::Vector3;
|
||||||
use pumpkin_util::random::RandomImpl;
|
use pumpkin_util::random::{RandomDeriverImpl, RandomImpl};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@ -880,7 +880,11 @@ impl StructureGenerator for JigsawGenerator {
|
|||||||
&[]
|
&[]
|
||||||
};
|
};
|
||||||
|
|
||||||
let pool_alias_lookup = PoolAliasLookup::from_bindings(pool_aliases, &mut context.random);
|
let mut alias_random =
|
||||||
|
pumpkin_util::random::legacy_rand::LegacyRand::from_seed(context.seed as u64)
|
||||||
|
.next_splitter()
|
||||||
|
.split_pos(start_pos.0.x, start_pos.0.y, start_pos.0.z);
|
||||||
|
let pool_alias_lookup = PoolAliasLookup::from_bindings(pool_aliases, &mut alias_random);
|
||||||
|
|
||||||
let start_pool = if self.start_pool.is_empty() {
|
let start_pool = if self.start_pool.is_empty() {
|
||||||
structure
|
structure
|
||||||
|
|||||||
@ -56,10 +56,10 @@ impl StructureGenerator for JungleTempleGenerator {
|
|||||||
collector.add_piece(Box::new(JungleTemplePiece {
|
collector.add_piece(Box::new(JungleTemplePiece {
|
||||||
piece,
|
piece,
|
||||||
height_adjusted: false,
|
height_adjusted: false,
|
||||||
placed_main_chest: true,
|
placed_main_chest: false,
|
||||||
placed_hidden_chest: true,
|
placed_hidden_chest: false,
|
||||||
placed_trap_1: true,
|
placed_trap_1: false,
|
||||||
placed_trap_2: true,
|
placed_trap_2: false,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
@ -508,18 +508,18 @@ impl StructurePieceBase for JungleTemplePiece {
|
|||||||
);
|
);
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 3, -3, 1, bb);
|
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 3, -3, 1, bb);
|
||||||
/*if !self.placed_trap_1 {
|
if !self.placed_trap_1 {
|
||||||
self.placed_trap_1 = this.createDispenser(
|
self.placed_trap_1 = self.piece.add_dispenser(
|
||||||
chunk,
|
chunk,
|
||||||
bb,
|
bb,
|
||||||
random,
|
random,
|
||||||
3,
|
3,
|
||||||
-2,
|
-2,
|
||||||
1,
|
1,
|
||||||
Direction.NORTH,
|
Facing::North,
|
||||||
BuiltInLootTables.JUNGLE_TEMPLE_DISPENSER,
|
"minecraft:chests/jungle_temple_dispenser",
|
||||||
);
|
);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Self::vine_facing(Facing::South), 3, -2, 2, bb);
|
.add_block(chunk, Self::vine_facing(Facing::South), 3, -2, 2, bb);
|
||||||
@ -592,27 +592,34 @@ impl StructurePieceBase for JungleTemplePiece {
|
|||||||
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 9, -3, 4, bb);
|
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 9, -3, 4, bb);
|
||||||
};
|
};
|
||||||
self.piece.add_block(chunk, redstone_wire_ns, 9, -2, 4, bb);
|
self.piece.add_block(chunk, redstone_wire_ns, 9, -2, 4, bb);
|
||||||
/*if !self.placed_trap_2 {
|
if !self.placed_trap_2 {
|
||||||
self.placed_trap_2 = this.createDispenser(
|
self.placed_trap_2 = self.piece.add_dispenser(
|
||||||
chunk,
|
chunk,
|
||||||
bb,
|
bb,
|
||||||
random,
|
random,
|
||||||
9,
|
9,
|
||||||
-2,
|
-2,
|
||||||
3,
|
3,
|
||||||
Direction.WEST,
|
Facing::West,
|
||||||
BuiltInLootTables.JUNGLE_TEMPLE_DISPENSER,
|
"minecraft:chests/jungle_temple_dispenser",
|
||||||
);
|
);
|
||||||
}*/
|
}
|
||||||
|
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Self::vine_facing(Facing::East), 8, -1, 3, bb);
|
.add_block(chunk, Self::vine_facing(Facing::East), 8, -1, 3, bb);
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Self::vine_facing(Facing::East), 8, -2, 3, bb);
|
.add_block(chunk, Self::vine_facing(Facing::East), 8, -2, 3, bb);
|
||||||
/*if !self.placed_main_chest {
|
if !self.placed_main_chest {
|
||||||
self.placed_main_chest =
|
self.placed_main_chest = self.piece.add_chest(
|
||||||
this.createChest(chunk, bb, random, 8, -3, 3, BuiltInLootTables.JUNGLE_TEMPLE);
|
chunk,
|
||||||
}*/
|
bb,
|
||||||
|
random,
|
||||||
|
8,
|
||||||
|
-3,
|
||||||
|
3,
|
||||||
|
"minecraft:chests/jungle_temple",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
self.piece
|
self.piece
|
||||||
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 9, -3, 2, bb);
|
.add_block(chunk, Block::MOSSY_COBBLESTONE.default_state, 9, -3, 2, bb);
|
||||||
@ -738,17 +745,17 @@ impl StructurePieceBase for JungleTemplePiece {
|
|||||||
BlockState::from_id(props.to_state_id(&Block::REPEATER))
|
BlockState::from_id(props.to_state_id(&Block::REPEATER))
|
||||||
};
|
};
|
||||||
self.piece.add_block(chunk, repeater_state, 10, -2, 10, bb);
|
self.piece.add_block(chunk, repeater_state, 10, -2, 10, bb);
|
||||||
/*if !self.placed_hidden_chest {
|
if !self.placed_hidden_chest {
|
||||||
self.placed_hidden_chest = this.createChest(
|
self.placed_hidden_chest = self.piece.add_chest(
|
||||||
chunk,
|
chunk,
|
||||||
bb,
|
bb,
|
||||||
random,
|
random,
|
||||||
9,
|
9,
|
||||||
-3,
|
-3,
|
||||||
10,
|
10,
|
||||||
BuiltInLootTables.JUNGLE_TEMPLE,
|
"minecraft:chests/jungle_temple",
|
||||||
);
|
);
|
||||||
}*/
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const fn set_side_dir(props: &mut RedstoneWireLikeProperties, facing: HorizontalFacing) {
|
const fn set_side_dir(props: &mut RedstoneWireLikeProperties, facing: HorizontalFacing) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -74,7 +74,6 @@ pub trait StructurePieceBase: Send + Sync {
|
|||||||
&self,
|
&self,
|
||||||
_start: &StructurePiece,
|
_start: &StructurePiece,
|
||||||
_random: &mut RandomGenerator,
|
_random: &mut RandomGenerator,
|
||||||
// TODO: this is only for Stronghold and should not be here
|
|
||||||
_weights: &mut Vec<crate::generation::structure::structures::stronghold::PieceWeight>,
|
_weights: &mut Vec<crate::generation::structure::structures::stronghold::PieceWeight>,
|
||||||
_last_piece_type: &mut Option<StrongholdPieceType>,
|
_last_piece_type: &mut Option<StrongholdPieceType>,
|
||||||
_has_portal_room: &mut bool,
|
_has_portal_room: &mut bool,
|
||||||
@ -388,15 +387,16 @@ impl StructurePiece {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
pub const fn is_under_sea_level(
|
pub const fn is_under_sea_level(
|
||||||
&self,
|
&self,
|
||||||
chunk: &mut ProtoChunk,
|
chunk: &ProtoChunk,
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
z: i32,
|
z: i32,
|
||||||
box_limit: &BlockBox,
|
box_limit: &BlockBox,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let block_pos = self.offset_pos(x, y, z);
|
let block_pos = self.offset_pos(x, y + 1, z);
|
||||||
|
|
||||||
if !box_limit.contains_pos(&block_pos) {
|
if !box_limit.contains_pos(&block_pos) {
|
||||||
return false;
|
return false;
|
||||||
@ -503,6 +503,43 @@ impl StructurePiece {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
|
pub fn add_dispenser(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
bb: &BlockBox,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
facing: pumpkin_data::block_properties::Facing,
|
||||||
|
loot_table: &str,
|
||||||
|
) -> bool {
|
||||||
|
use pumpkin_data::block_properties::DispenserLikeProperties;
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
|
|
||||||
|
let world_pos = self.offset_pos(x, y, z);
|
||||||
|
if !bb.contains_pos(&world_pos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut props = DispenserLikeProperties::default(&Block::DISPENSER);
|
||||||
|
props.facing = facing;
|
||||||
|
let state = BlockState::from_id(props.to_state_id(&Block::DISPENSER));
|
||||||
|
chunk.set_block_state(world_pos.x, world_pos.y, world_pos.z, state);
|
||||||
|
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:dispenser".to_string());
|
||||||
|
nbt.put_int("x", world_pos.x);
|
||||||
|
nbt.put_int("y", world_pos.y);
|
||||||
|
nbt.put_int("z", world_pos.z);
|
||||||
|
nbt.put_string("LootTable", loot_table.to_string());
|
||||||
|
nbt.put_long("LootTableSeed", random.next_i64());
|
||||||
|
chunk.add_block_entity(nbt);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePieceBase for StructurePiece {
|
impl StructurePieceBase for StructurePiece {
|
||||||
@ -625,6 +662,24 @@ impl StructurePiecesCollector {
|
|||||||
k
|
k
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_inside_heights(
|
||||||
|
&mut self,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
lowest_allowed: i32,
|
||||||
|
highest_allowed: i32,
|
||||||
|
) {
|
||||||
|
let bounding_box = self.get_bounding_box();
|
||||||
|
let y_span = bounding_box.max.y - bounding_box.min.y + 1;
|
||||||
|
let height_span = highest_allowed - lowest_allowed + 1 - y_span;
|
||||||
|
let y0_pos = if height_span > 1 {
|
||||||
|
lowest_allowed + random.next_bounded_i32(height_span)
|
||||||
|
} else {
|
||||||
|
lowest_allowed
|
||||||
|
};
|
||||||
|
let dy = y0_pos - bounding_box.min.y;
|
||||||
|
self.shift(dy);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_bounding_box(&mut self) -> BlockBox {
|
pub fn get_bounding_box(&mut self) -> BlockBox {
|
||||||
if let Some(bbox) = self.cached_box {
|
if let Some(bbox) = self.cached_box {
|
||||||
return bbox;
|
return bbox;
|
||||||
|
|||||||
@ -223,11 +223,14 @@ impl StructurePieceBase for CorridorExitPiece {
|
|||||||
p.add_block(chunk, nb, 6, 0, 6, &bb);
|
p.add_block(chunk, nb, 6, 0, 6, &bb);
|
||||||
p.add_block(chunk, Block::LAVA.default_state, 6, 5, 6, &bb);
|
p.add_block(chunk, Block::LAVA.default_state, 6, 5, 6, &bb);
|
||||||
|
|
||||||
// Schedule lava fluid tick
|
let lava_pos = p.offset_pos(6, 5, 6);
|
||||||
// TODO
|
if bb.contains_pos(&lava_pos) {
|
||||||
// let lava_pos = p.offset_pos(6, 5, 6);
|
chunk.schedule_fluid_tick(
|
||||||
// if bb.contains_pos(&lava_pos) {
|
lava_pos.x,
|
||||||
// chunk.schedule_fluid_tick(&lava_pos, 0);
|
lava_pos.y,
|
||||||
// }
|
lava_pos.z,
|
||||||
|
&pumpkin_data::fluid::Fluid::LAVA,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -83,6 +83,8 @@ impl StructureGenerator for NetherFortressGenerator {
|
|||||||
collector.add_piece(piece);
|
collector.add_piece(piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collector.move_inside_heights(&mut random, 48, 70);
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
start_pos: BlockPos::new(
|
start_pos: BlockPos::new(
|
||||||
section_coords::section_to_block(context.chunk_x),
|
section_coords::section_to_block(context.chunk_x),
|
||||||
|
|||||||
@ -1,43 +1,28 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::block_rotation::Rotation;
|
use pumpkin_data::{
|
||||||
|
Block, BlockState, Mirror, Rotation,
|
||||||
|
block_properties::{ChestLikeProperties, HorizontalFacing},
|
||||||
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
math::{block_box::BlockBox, position::BlockPos},
|
HeightMap,
|
||||||
random::{RandomGenerator, RandomImpl},
|
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
|
||||||
|
random::{RandomGenerator, RandomImpl, hash_block_pos, legacy_rand::LegacyRand},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ProtoChunk,
|
ProtoChunk,
|
||||||
generation::{
|
generation::structure::{
|
||||||
positions::chunk_pos::{get_center_x, get_center_z},
|
piece::StructurePieceType,
|
||||||
structure::{
|
structures::{
|
||||||
piece::StructurePieceType,
|
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
||||||
structures::{
|
StructurePiecesCollector, StructurePosition, WorldPortalExt,
|
||||||
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
|
||||||
StructurePiecesCollector, StructurePosition, WorldPortalExt,
|
|
||||||
},
|
|
||||||
template::{StructureTemplate, get_template, place_template},
|
|
||||||
},
|
},
|
||||||
|
template::{BlockStateResolver, StructurePlaceSettings, StructureTemplate, get_template},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const COLD_RUINS: &[&str] = &[
|
|
||||||
"underwater_ruin/brick_1",
|
|
||||||
"underwater_ruin/brick_2",
|
|
||||||
"underwater_ruin/brick_3",
|
|
||||||
"underwater_ruin/brick_4",
|
|
||||||
"underwater_ruin/brick_5",
|
|
||||||
"underwater_ruin/brick_6",
|
|
||||||
"underwater_ruin/brick_7",
|
|
||||||
"underwater_ruin/brick_8",
|
|
||||||
"underwater_ruin/cracked_1",
|
|
||||||
"underwater_ruin/cracked_2",
|
|
||||||
"underwater_ruin/cracked_3",
|
|
||||||
"underwater_ruin/mossy_1",
|
|
||||||
"underwater_ruin/mossy_2",
|
|
||||||
"underwater_ruin/mossy_3",
|
|
||||||
];
|
|
||||||
|
|
||||||
const WARM_RUINS: &[&str] = &[
|
const WARM_RUINS: &[&str] = &[
|
||||||
"underwater_ruin/warm_1",
|
"underwater_ruin/warm_1",
|
||||||
"underwater_ruin/warm_2",
|
"underwater_ruin/warm_2",
|
||||||
@ -47,8 +32,67 @@ const WARM_RUINS: &[&str] = &[
|
|||||||
"underwater_ruin/warm_6",
|
"underwater_ruin/warm_6",
|
||||||
"underwater_ruin/warm_7",
|
"underwater_ruin/warm_7",
|
||||||
"underwater_ruin/warm_8",
|
"underwater_ruin/warm_8",
|
||||||
|
];
|
||||||
|
|
||||||
|
const BIG_WARM_RUINS: &[&str] = &[
|
||||||
"underwater_ruin/big_warm_4",
|
"underwater_ruin/big_warm_4",
|
||||||
"underwater_ruin/big_warm_5",
|
"underwater_ruin/big_warm_5",
|
||||||
|
"underwater_ruin/big_warm_6",
|
||||||
|
"underwater_ruin/big_warm_7",
|
||||||
|
];
|
||||||
|
|
||||||
|
const RUINS_BRICK: &[&str] = &[
|
||||||
|
"underwater_ruin/brick_1",
|
||||||
|
"underwater_ruin/brick_2",
|
||||||
|
"underwater_ruin/brick_3",
|
||||||
|
"underwater_ruin/brick_4",
|
||||||
|
"underwater_ruin/brick_5",
|
||||||
|
"underwater_ruin/brick_6",
|
||||||
|
"underwater_ruin/brick_7",
|
||||||
|
"underwater_ruin/brick_8",
|
||||||
|
];
|
||||||
|
|
||||||
|
const RUINS_CRACKED: &[&str] = &[
|
||||||
|
"underwater_ruin/cracked_1",
|
||||||
|
"underwater_ruin/cracked_2",
|
||||||
|
"underwater_ruin/cracked_3",
|
||||||
|
"underwater_ruin/cracked_4",
|
||||||
|
"underwater_ruin/cracked_5",
|
||||||
|
"underwater_ruin/cracked_6",
|
||||||
|
"underwater_ruin/cracked_7",
|
||||||
|
"underwater_ruin/cracked_8",
|
||||||
|
];
|
||||||
|
|
||||||
|
const RUINS_MOSSY: &[&str] = &[
|
||||||
|
"underwater_ruin/mossy_1",
|
||||||
|
"underwater_ruin/mossy_2",
|
||||||
|
"underwater_ruin/mossy_3",
|
||||||
|
"underwater_ruin/mossy_4",
|
||||||
|
"underwater_ruin/mossy_5",
|
||||||
|
"underwater_ruin/mossy_6",
|
||||||
|
"underwater_ruin/mossy_7",
|
||||||
|
"underwater_ruin/mossy_8",
|
||||||
|
];
|
||||||
|
|
||||||
|
const BIG_RUINS_BRICK: &[&str] = &[
|
||||||
|
"underwater_ruin/big_brick_1",
|
||||||
|
"underwater_ruin/big_brick_2",
|
||||||
|
"underwater_ruin/big_brick_3",
|
||||||
|
"underwater_ruin/big_brick_8",
|
||||||
|
];
|
||||||
|
|
||||||
|
const BIG_RUINS_CRACKED: &[&str] = &[
|
||||||
|
"underwater_ruin/big_cracked_1",
|
||||||
|
"underwater_ruin/big_cracked_2",
|
||||||
|
"underwater_ruin/big_cracked_3",
|
||||||
|
"underwater_ruin/big_cracked_8",
|
||||||
|
];
|
||||||
|
|
||||||
|
const BIG_RUINS_MOSSY: &[&str] = &[
|
||||||
|
"underwater_ruin/big_mossy_1",
|
||||||
|
"underwater_ruin/big_mossy_2",
|
||||||
|
"underwater_ruin/big_mossy_3",
|
||||||
|
"underwater_ruin/big_mossy_8",
|
||||||
];
|
];
|
||||||
|
|
||||||
pub struct OceanRuinGenerator {
|
pub struct OceanRuinGenerator {
|
||||||
@ -60,81 +104,566 @@ impl StructureGenerator for OceanRuinGenerator {
|
|||||||
&self,
|
&self,
|
||||||
mut context: StructureGeneratorContext<'_>,
|
mut context: StructureGeneratorContext<'_>,
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
let chunk_center_x = get_center_x(context.chunk_x);
|
let start_x = context.chunk_x << 4;
|
||||||
let chunk_center_z = get_center_z(context.chunk_z);
|
let start_z = context.chunk_z << 4;
|
||||||
|
let position = Vector3::new(start_x, 90, start_z);
|
||||||
|
|
||||||
let rotation_idx = context.random.next_bounded_i32(4) as u8;
|
let rotation_idx = context.random.next_bounded_i32(4) as u8;
|
||||||
let rotation = Rotation::from_index(rotation_idx);
|
let rotation = Rotation::from_index(rotation_idx);
|
||||||
|
|
||||||
let pool = if self.is_warm { WARM_RUINS } else { COLD_RUINS };
|
let mut collector = StructurePiecesCollector::default();
|
||||||
let template_idx = context.random.next_bounded_i32(pool.len() as i32) as usize;
|
add_pieces(
|
||||||
let template_name = pool[template_idx];
|
&mut collector,
|
||||||
let template = get_template(template_name)?;
|
position,
|
||||||
|
rotation,
|
||||||
let size = template.size;
|
&mut context.random,
|
||||||
let bounding_box = BlockBox::new(
|
self.is_warm,
|
||||||
chunk_center_x - size.x / 2,
|
0.3,
|
||||||
context.min_y,
|
0.7,
|
||||||
chunk_center_z - size.z / 2,
|
|
||||||
chunk_center_x + size.x / 2,
|
|
||||||
256,
|
|
||||||
chunk_center_z + size.z / 2,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
|
||||||
collector.add_piece(Box::new(OceanRuinPiece {
|
|
||||||
piece: StructurePiece::new(StructurePieceType::OceanTemple, bounding_box, 0),
|
|
||||||
template,
|
|
||||||
rotation,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
start_pos: BlockPos::new(chunk_center_x, 64, chunk_center_z),
|
start_pos: BlockPos::new(start_x + 8, 64, start_z + 8),
|
||||||
collector: Arc::new(collector.into()),
|
collector: Arc::new(collector.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OceanRuinPiece {
|
fn add_pieces(
|
||||||
piece: StructurePiece,
|
collector: &mut StructurePiecesCollector,
|
||||||
template: Arc<StructureTemplate>,
|
position: Vector3<i32>,
|
||||||
rotation: Rotation,
|
rotation: Rotation,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
is_warm: bool,
|
||||||
|
large_probability: f32,
|
||||||
|
cluster_probability: f32,
|
||||||
|
) {
|
||||||
|
let is_large = random.next_f32() <= large_probability;
|
||||||
|
let base_integrity = if is_large { 0.9 } else { 0.8 };
|
||||||
|
add_piece(
|
||||||
|
collector,
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
random,
|
||||||
|
is_warm,
|
||||||
|
is_large,
|
||||||
|
base_integrity,
|
||||||
|
);
|
||||||
|
if is_large && random.next_f32() <= cluster_probability {
|
||||||
|
add_cluster_ruins(collector, random, rotation, position, is_warm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_cluster_ruins(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
rotation: Rotation,
|
||||||
|
p: Vector3<i32>,
|
||||||
|
is_warm: bool,
|
||||||
|
) {
|
||||||
|
let parent_pos = Vector3::new(p.x, 90, p.z);
|
||||||
|
let parent_corner = StructureTemplate::transform_block_pos(
|
||||||
|
Vector3::new(15, 0, 15),
|
||||||
|
Mirror::None,
|
||||||
|
rotation,
|
||||||
|
Vector3::new(0, 0, 0),
|
||||||
|
) + parent_pos;
|
||||||
|
|
||||||
|
let parent_bb = BlockBox::new(
|
||||||
|
parent_pos.x.min(parent_corner.x),
|
||||||
|
parent_pos.y.min(parent_corner.y),
|
||||||
|
parent_pos.z.min(parent_corner.z),
|
||||||
|
parent_pos.x.max(parent_corner.x),
|
||||||
|
parent_pos.y.max(parent_corner.y),
|
||||||
|
parent_pos.z.max(parent_corner.z),
|
||||||
|
);
|
||||||
|
|
||||||
|
let parent_bottom_left = Vector3::new(
|
||||||
|
parent_pos.x.min(parent_corner.x),
|
||||||
|
parent_pos.y,
|
||||||
|
parent_pos.z.min(parent_corner.z),
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut all_positions = generate_cluster_positions(random, parent_bottom_left);
|
||||||
|
let ruins_count = random.next_bounded_i32(5) + 4;
|
||||||
|
|
||||||
|
for _ in 0..ruins_count {
|
||||||
|
if all_positions.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let idx = random.next_bounded_i32(all_positions.len() as i32) as usize;
|
||||||
|
let pos = all_positions.swap_remove(idx);
|
||||||
|
let next_rotation = Rotation::from_index(random.next_bounded_i32(4) as u8);
|
||||||
|
let next_corner = StructureTemplate::transform_block_pos(
|
||||||
|
Vector3::new(5, 0, 6),
|
||||||
|
Mirror::None,
|
||||||
|
next_rotation,
|
||||||
|
Vector3::new(0, 0, 0),
|
||||||
|
) + pos;
|
||||||
|
|
||||||
|
let next_bb = BlockBox::new(
|
||||||
|
pos.x.min(next_corner.x),
|
||||||
|
pos.y.min(next_corner.y),
|
||||||
|
pos.z.min(next_corner.z),
|
||||||
|
pos.x.max(next_corner.x),
|
||||||
|
pos.y.max(next_corner.y),
|
||||||
|
pos.z.max(next_corner.z),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !next_bb.intersects(&parent_bb) {
|
||||||
|
add_piece(collector, pos, next_rotation, random, is_warm, false, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_cluster_positions(
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
origin: Vector3<i32>,
|
||||||
|
) -> Vec<Vector3<i32>> {
|
||||||
|
vec![
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
-16 + random.next_bounded_i32(8) + 1,
|
||||||
|
0,
|
||||||
|
16 + random.next_bounded_i32(7) + 1,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
-16 + random.next_bounded_i32(8) + 1,
|
||||||
|
0,
|
||||||
|
random.next_bounded_i32(7) + 1,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
-16 + random.next_bounded_i32(8) + 1,
|
||||||
|
0,
|
||||||
|
-16 + random.next_bounded_i32(5) + 4,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
random.next_bounded_i32(7) + 1,
|
||||||
|
0,
|
||||||
|
16 + random.next_bounded_i32(7) + 1,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
random.next_bounded_i32(7) + 1,
|
||||||
|
0,
|
||||||
|
-16 + random.next_bounded_i32(3) + 4,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
16 + random.next_bounded_i32(7) + 1,
|
||||||
|
0,
|
||||||
|
16 + random.next_bounded_i32(6) + 3,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
16 + random.next_bounded_i32(7) + 1,
|
||||||
|
0,
|
||||||
|
random.next_bounded_i32(7) + 1,
|
||||||
|
),
|
||||||
|
origin
|
||||||
|
+ Vector3::new(
|
||||||
|
16 + random.next_bounded_i32(7) + 1,
|
||||||
|
0,
|
||||||
|
-16 + random.next_bounded_i32(5) + 4,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_piece(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
position: Vector3<i32>,
|
||||||
|
rotation: Rotation,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
is_warm: bool,
|
||||||
|
is_large: bool,
|
||||||
|
base_integrity: f32,
|
||||||
|
) {
|
||||||
|
if is_warm {
|
||||||
|
let pool = if is_large { BIG_WARM_RUINS } else { WARM_RUINS };
|
||||||
|
let template_idx = random.next_bounded_i32(pool.len() as i32) as usize;
|
||||||
|
let template_name = pool[template_idx];
|
||||||
|
if let Some(template) = get_template(template_name) {
|
||||||
|
collector.add_piece(Box::new(OceanRuinPiece::new(
|
||||||
|
template,
|
||||||
|
template_name.to_string(),
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
base_integrity,
|
||||||
|
is_warm,
|
||||||
|
is_large,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (bricks, cracked, mossy) = if is_large {
|
||||||
|
(BIG_RUINS_BRICK, BIG_RUINS_CRACKED, BIG_RUINS_MOSSY)
|
||||||
|
} else {
|
||||||
|
(RUINS_BRICK, RUINS_CRACKED, RUINS_MOSSY)
|
||||||
|
};
|
||||||
|
let idx = random.next_bounded_i32(bricks.len() as i32) as usize;
|
||||||
|
if let Some(template) = get_template(bricks[idx]) {
|
||||||
|
collector.add_piece(Box::new(OceanRuinPiece::new(
|
||||||
|
template,
|
||||||
|
bricks[idx].to_string(),
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
base_integrity,
|
||||||
|
is_warm,
|
||||||
|
is_large,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if let Some(template) = get_template(cracked[idx]) {
|
||||||
|
collector.add_piece(Box::new(OceanRuinPiece::new(
|
||||||
|
template,
|
||||||
|
cracked[idx].to_string(),
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
0.7,
|
||||||
|
is_warm,
|
||||||
|
is_large,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if let Some(template) = get_template(mossy[idx]) {
|
||||||
|
collector.add_piece(Box::new(OceanRuinPiece::new(
|
||||||
|
template,
|
||||||
|
mossy[idx].to_string(),
|
||||||
|
position,
|
||||||
|
rotation,
|
||||||
|
0.5,
|
||||||
|
is_warm,
|
||||||
|
is_large,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OceanRuinPiece {
|
||||||
|
pub piece: StructurePiece,
|
||||||
|
pub template: Arc<StructureTemplate>,
|
||||||
|
pub template_name: String,
|
||||||
|
pub template_position: Vector3<i32>,
|
||||||
|
pub rotation: Rotation,
|
||||||
|
pub integrity: f32,
|
||||||
|
pub is_warm: bool,
|
||||||
|
pub is_large: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OceanRuinPiece {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
template: Arc<StructureTemplate>,
|
||||||
|
template_name: String,
|
||||||
|
position: Vector3<i32>,
|
||||||
|
rotation: Rotation,
|
||||||
|
integrity: f32,
|
||||||
|
is_warm: bool,
|
||||||
|
is_large: bool,
|
||||||
|
) -> Self {
|
||||||
|
let place_settings = StructurePlaceSettings::new()
|
||||||
|
.set_rotation(rotation)
|
||||||
|
.set_mirror(Mirror::None);
|
||||||
|
let bounding_box = template.get_bounding_box(&place_settings, position);
|
||||||
|
Self {
|
||||||
|
piece: StructurePiece::new(StructurePieceType::OceanTemple, bounding_box, 0),
|
||||||
|
template,
|
||||||
|
template_name,
|
||||||
|
template_position: position,
|
||||||
|
rotation,
|
||||||
|
integrity,
|
||||||
|
is_warm,
|
||||||
|
is_large,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn calculate_height(pos: Vector3<i32>, corner: Vector3<i32>, chunk: &ProtoChunk) -> i32 {
|
||||||
|
let mut new_y = pos.y;
|
||||||
|
let mut min_y = 512;
|
||||||
|
let top_y = new_y - 1;
|
||||||
|
let mut area = 0;
|
||||||
|
let min_x = pos.x.min(corner.x);
|
||||||
|
let max_x = pos.x.max(corner.x);
|
||||||
|
let min_z = pos.z.min(corner.z);
|
||||||
|
let max_z = pos.z.max(corner.z);
|
||||||
|
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
let floor_y = chunk.get_top_y(&HeightMap::OceanFloorWg, x, z) - 1;
|
||||||
|
min_y = min_y.min(floor_y);
|
||||||
|
if floor_y < top_y - 2 {
|
||||||
|
area += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = (pos.x - corner.x).abs();
|
||||||
|
if top_y - min_y > 2 && area > width - 2 {
|
||||||
|
new_y = min_y + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_y
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePieceBase for OceanRuinPiece {
|
impl StructurePieceBase for OceanRuinPiece {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_structure_piece(&self) -> &StructurePiece {
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
&self.piece
|
&self.piece
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
&mut self.piece
|
&mut self.piece
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn translate(&mut self, x: i32, y: i32, z: i32) {
|
||||||
|
self.piece.translate(x, y, z);
|
||||||
|
self.template_position += Vector3::new(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn place(
|
fn place(
|
||||||
&mut self,
|
&mut self,
|
||||||
chunk: &mut ProtoChunk,
|
chunk: &mut ProtoChunk,
|
||||||
_block_registry: &dyn WorldPortalExt,
|
_block_registry: &dyn WorldPortalExt,
|
||||||
_random: &mut RandomGenerator,
|
random: &mut RandomGenerator,
|
||||||
_seed: i64,
|
_seed: i64,
|
||||||
chunk_box: &BlockBox,
|
chunk_box: &BlockBox,
|
||||||
) {
|
) {
|
||||||
let origin = self.piece.bounding_box.min;
|
let height = chunk.get_top_y(
|
||||||
let sample_y = chunk.get_top_y(&pumpkin_util::HeightMap::OceanFloorWg, origin.x, origin.z);
|
&HeightMap::OceanFloorWg,
|
||||||
let target_y = sample_y - 2; // slightly buried on ocean floor
|
self.template_position.x,
|
||||||
let mut final_origin = origin;
|
self.template_position.z,
|
||||||
final_origin.y = target_y;
|
|
||||||
|
|
||||||
place_template(
|
|
||||||
chunk,
|
|
||||||
&self.template,
|
|
||||||
final_origin,
|
|
||||||
(0, 0),
|
|
||||||
self.rotation,
|
|
||||||
true, // skip air
|
|
||||||
true, // apply waterlogging
|
|
||||||
&[],
|
|
||||||
Some(chunk_box),
|
|
||||||
);
|
);
|
||||||
|
let mut pos = Vector3::new(self.template_position.x, height, self.template_position.z);
|
||||||
|
let size = self.template.size;
|
||||||
|
let corner = StructureTemplate::transform_block_pos(
|
||||||
|
Vector3::new(size.x - 1, 0, size.z - 1),
|
||||||
|
Mirror::None,
|
||||||
|
self.rotation,
|
||||||
|
Vector3::new(0, 0, 0),
|
||||||
|
) + pos;
|
||||||
|
|
||||||
|
pos.y = Self::calculate_height(pos, corner, chunk);
|
||||||
|
self.template_position = pos;
|
||||||
|
let place_settings = StructurePlaceSettings::new()
|
||||||
|
.set_rotation(self.rotation)
|
||||||
|
.set_mirror(Mirror::None);
|
||||||
|
self.piece.bounding_box = self
|
||||||
|
.template
|
||||||
|
.get_bounding_box(&place_settings, self.template_position);
|
||||||
|
let box_limit = self.piece.bounding_box;
|
||||||
|
|
||||||
|
let mut suspicious_count = 0;
|
||||||
|
|
||||||
|
for block in &self.template.blocks {
|
||||||
|
let palette_entry = &self.template.palette[block.state as usize];
|
||||||
|
|
||||||
|
if palette_entry.name == "minecraft:structure_void"
|
||||||
|
|| palette_entry.name == "structure_void"
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rel_pos = StructureTemplate::transform_block_pos(
|
||||||
|
block.pos,
|
||||||
|
Mirror::None,
|
||||||
|
self.rotation,
|
||||||
|
Vector3::new(0, 0, 0),
|
||||||
|
);
|
||||||
|
let world_pos = self.template_position + rel_pos;
|
||||||
|
|
||||||
|
if !box_limit.contains_pos(&world_pos) || !chunk_box.contains_pos(&world_pos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if palette_entry.name == "minecraft:structure_block" {
|
||||||
|
if let Some(nbt) = &block.nbt {
|
||||||
|
let mode = nbt.get_string("mode").unwrap_or("");
|
||||||
|
if mode == "DATA" {
|
||||||
|
let metadata = nbt.get_string("metadata").unwrap_or("");
|
||||||
|
if metadata == "chest" {
|
||||||
|
let is_water =
|
||||||
|
chunk.get_block_state(&world_pos).to_block_id() == Block::WATER.id;
|
||||||
|
let mut props = ChestLikeProperties::default(&Block::CHEST);
|
||||||
|
props.facing = HorizontalFacing::North;
|
||||||
|
props.waterlogged = is_water;
|
||||||
|
let chest_state = BlockState::from_id(props.to_state_id(&Block::CHEST));
|
||||||
|
chunk.set_block_state(
|
||||||
|
world_pos.x,
|
||||||
|
world_pos.y,
|
||||||
|
world_pos.z,
|
||||||
|
chest_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut chest_nbt = NbtCompound::new();
|
||||||
|
chest_nbt.put_string("id", "minecraft:chest".to_string());
|
||||||
|
chest_nbt.put_int("x", world_pos.x);
|
||||||
|
chest_nbt.put_int("y", world_pos.y);
|
||||||
|
chest_nbt.put_int("z", world_pos.z);
|
||||||
|
let loot_table = if self.is_large {
|
||||||
|
"minecraft:chests/underwater_ruin_big"
|
||||||
|
} else {
|
||||||
|
"minecraft:chests/underwater_ruin_small"
|
||||||
|
};
|
||||||
|
chest_nbt.put_string("LootTable", loot_table.to_string());
|
||||||
|
let mut rng = LegacyRand::from_seed(hash_block_pos(
|
||||||
|
world_pos.x,
|
||||||
|
world_pos.y,
|
||||||
|
world_pos.z,
|
||||||
|
)
|
||||||
|
as u64);
|
||||||
|
chest_nbt.put_long("LootTableSeed", rng.next_i64());
|
||||||
|
chunk.add_block_entity(chest_nbt);
|
||||||
|
} else if metadata == "drowned" {
|
||||||
|
let replacement = if world_pos.y > chunk.get_sea_level() {
|
||||||
|
Block::AIR.default_state
|
||||||
|
} else {
|
||||||
|
Block::WATER.default_state
|
||||||
|
};
|
||||||
|
chunk.set_block_state(
|
||||||
|
world_pos.x,
|
||||||
|
world_pos.y,
|
||||||
|
world_pos.z,
|
||||||
|
replacement,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.integrity < 0.999 && random.next_f32() > self.integrity {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if palette_entry.name == "minecraft:air" || palette_entry.name == "air" {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_warm
|
||||||
|
&& palette_entry.name == "minecraft:sand"
|
||||||
|
&& suspicious_count < 5
|
||||||
|
&& random.next_f32() < 0.45
|
||||||
|
{
|
||||||
|
suspicious_count += 1;
|
||||||
|
chunk.set_block_state(
|
||||||
|
world_pos.x,
|
||||||
|
world_pos.y,
|
||||||
|
world_pos.z,
|
||||||
|
Block::SUSPICIOUS_SAND.default_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:brushable_block".to_string());
|
||||||
|
nbt.put_int("x", world_pos.x);
|
||||||
|
nbt.put_int("y", world_pos.y);
|
||||||
|
nbt.put_int("z", world_pos.z);
|
||||||
|
nbt.put_string(
|
||||||
|
"LootTable",
|
||||||
|
"minecraft:archaeology/ocean_ruin_warm".to_string(),
|
||||||
|
);
|
||||||
|
let mut rng =
|
||||||
|
LegacyRand::from_seed(
|
||||||
|
hash_block_pos(world_pos.x, world_pos.y, world_pos.z) as u64
|
||||||
|
);
|
||||||
|
nbt.put_long("LootTableSeed", rng.next_i64());
|
||||||
|
chunk.add_block_entity(nbt);
|
||||||
|
continue;
|
||||||
|
} else if !self.is_warm
|
||||||
|
&& palette_entry.name == "minecraft:gravel"
|
||||||
|
&& suspicious_count < 5
|
||||||
|
&& random.next_f32() < 0.45
|
||||||
|
{
|
||||||
|
suspicious_count += 1;
|
||||||
|
chunk.set_block_state(
|
||||||
|
world_pos.x,
|
||||||
|
world_pos.y,
|
||||||
|
world_pos.z,
|
||||||
|
Block::SUSPICIOUS_GRAVEL.default_state,
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:brushable_block".to_string());
|
||||||
|
nbt.put_int("x", world_pos.x);
|
||||||
|
nbt.put_int("y", world_pos.y);
|
||||||
|
nbt.put_int("z", world_pos.z);
|
||||||
|
nbt.put_string(
|
||||||
|
"LootTable",
|
||||||
|
"minecraft:archaeology/ocean_ruin_cold".to_string(),
|
||||||
|
);
|
||||||
|
let mut rng =
|
||||||
|
LegacyRand::from_seed(
|
||||||
|
hash_block_pos(world_pos.x, world_pos.y, world_pos.z) as u64
|
||||||
|
);
|
||||||
|
nbt.put_long("LootTableSeed", rng.next_i64());
|
||||||
|
chunk.add_block_entity(nbt);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(mut state) =
|
||||||
|
BlockStateResolver::resolve(palette_entry, self.rotation, Mirror::None)
|
||||||
|
else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if chunk.get_block_state(&world_pos).to_block_id() == Block::WATER.id {
|
||||||
|
let mut modified_entry = palette_entry.clone();
|
||||||
|
if let Some((_, waterlogged)) = modified_entry
|
||||||
|
.properties
|
||||||
|
.iter_mut()
|
||||||
|
.find(|(name, _)| name == "waterlogged")
|
||||||
|
{
|
||||||
|
*waterlogged = "true".to_string();
|
||||||
|
if let Some(waterlogged_state) =
|
||||||
|
BlockStateResolver::resolve(&modified_entry, self.rotation, Mirror::None)
|
||||||
|
{
|
||||||
|
state = waterlogged_state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.set_block_state(world_pos.x, world_pos.y, world_pos.z, state);
|
||||||
|
|
||||||
|
let final_block = Block::from_id(state.id.to_block_id());
|
||||||
|
let block_entity_id =
|
||||||
|
crate::generation::structure::template::get_block_entity_id(final_block.name);
|
||||||
|
if block.nbt.is_some() || block_entity_id.is_some() {
|
||||||
|
let fallback_id = block_entity_id.unwrap_or(final_block.name);
|
||||||
|
let mut placed_nbt = NbtCompound::new();
|
||||||
|
|
||||||
|
placed_nbt.put_string("id", fallback_id.to_string());
|
||||||
|
placed_nbt.put_int("x", world_pos.x);
|
||||||
|
placed_nbt.put_int("y", world_pos.y);
|
||||||
|
placed_nbt.put_int("z", world_pos.z);
|
||||||
|
|
||||||
|
if let Some(template_nbt) = &block.nbt {
|
||||||
|
for (key, value) in &template_nbt.child_tags {
|
||||||
|
if key.as_ref() != "x"
|
||||||
|
&& key.as_ref() != "y"
|
||||||
|
&& key.as_ref() != "z"
|
||||||
|
&& key.as_ref() != "id"
|
||||||
|
{
|
||||||
|
placed_nbt.child_tags.insert(key.clone(), value.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if placed_nbt.get_string("LootTable").is_some()
|
||||||
|
&& placed_nbt.get_long("LootTableSeed").is_none()
|
||||||
|
{
|
||||||
|
let mut rng =
|
||||||
|
LegacyRand::from_seed(
|
||||||
|
hash_block_pos(world_pos.x, world_pos.y, world_pos.z) as u64,
|
||||||
|
);
|
||||||
|
placed_nbt.put_long("LootTableSeed", rng.next_i64());
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.add_block_entity(placed_nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,10 +63,16 @@ impl StructureGenerator for StrongholdGenerator {
|
|||||||
context: StructureGeneratorContext<'_>,
|
context: StructureGeneratorContext<'_>,
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
let mut collector = StructurePiecesCollector::default();
|
let mut collector = StructurePiecesCollector::default();
|
||||||
let mut random = context.random;
|
let mut tries: i64 = 0;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
collector.clear();
|
collector.clear();
|
||||||
|
let mut random = crate::generation::structure::structures::create_chunk_random(
|
||||||
|
context.seed.wrapping_add(tries),
|
||||||
|
context.chunk_x,
|
||||||
|
context.chunk_z,
|
||||||
|
);
|
||||||
|
tries += 1;
|
||||||
let mut weights = get_initial_weights();
|
let mut weights = get_initial_weights();
|
||||||
let mut last_piece_type: Option<StrongholdPieceType> = None;
|
let mut last_piece_type: Option<StrongholdPieceType> = None;
|
||||||
let mut has_portal_room = false;
|
let mut has_portal_room = false;
|
||||||
|
|||||||
@ -81,7 +81,6 @@ impl StructurePieceBase for PortalRoomPiece {
|
|||||||
&self,
|
&self,
|
||||||
_start: &StructurePiece,
|
_start: &StructurePiece,
|
||||||
_random: &mut RandomGenerator,
|
_random: &mut RandomGenerator,
|
||||||
// TODO: this is only for Stronghold and should not be here
|
|
||||||
_weights: &mut Vec<PieceWeight>,
|
_weights: &mut Vec<PieceWeight>,
|
||||||
_last_piece_type: &mut Option<StrongholdPieceType>,
|
_last_piece_type: &mut Option<StrongholdPieceType>,
|
||||||
has_portal_room: &mut bool,
|
has_portal_room: &mut bool,
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::{
|
||||||
|
Block, BlockState,
|
||||||
|
block_properties::{HorizontalFacing, OakStairsLikeProperties, StairsShape},
|
||||||
|
};
|
||||||
|
use pumpkin_nbt::compound::NbtCompound;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
BlockDirection,
|
BlockDirection,
|
||||||
math::{block_box::BlockBox, position::BlockPos},
|
math::{block_box::BlockBox, position::BlockPos},
|
||||||
@ -11,13 +15,13 @@ use serde::Deserialize;
|
|||||||
use crate::{
|
use crate::{
|
||||||
ProtoChunk,
|
ProtoChunk,
|
||||||
generation::{
|
generation::{
|
||||||
positions::chunk_pos::{get_center_x, get_center_z},
|
positions::chunk_pos::{start_block_x, start_block_z},
|
||||||
structure::{
|
structure::{
|
||||||
piece::StructurePieceType,
|
piece::StructurePieceType,
|
||||||
shiftable_piece::ShiftableStructurePiece,
|
shiftable_piece::ShiftableStructurePiece,
|
||||||
structures::{
|
structures::{
|
||||||
StructureGenerator, StructureGeneratorContext, StructurePieceBase,
|
StructureGenerator, StructureGeneratorContext, StructurePieceBase,
|
||||||
StructurePiecesCollector, StructurePosition,
|
StructurePiecesCollector, StructurePosition, WorldPortalExt,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -31,21 +35,27 @@ impl StructureGenerator for SwampHutGenerator {
|
|||||||
&self,
|
&self,
|
||||||
mut context: StructureGeneratorContext<'_>,
|
mut context: StructureGeneratorContext<'_>,
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
let x = get_center_x(context.chunk_x);
|
let x = start_block_x(context.chunk_x);
|
||||||
let z = get_center_z(context.chunk_z);
|
let z = start_block_z(context.chunk_z);
|
||||||
|
|
||||||
|
let facing = BlockDirection::get_random_horizontal_direction(&mut context.random);
|
||||||
|
let mut shiftable_piece = ShiftableStructurePiece::new(
|
||||||
|
StructurePieceType::SwampHut,
|
||||||
|
x,
|
||||||
|
64,
|
||||||
|
z,
|
||||||
|
7,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
facing.get_axis(),
|
||||||
|
);
|
||||||
|
shiftable_piece.piece.set_facing(Some(facing));
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
let mut collector = StructurePiecesCollector::default();
|
||||||
collector.add_piece(Box::new(SwampHutPiece {
|
collector.add_piece(Box::new(SwampHutPiece {
|
||||||
shiftable_structure_piece: ShiftableStructurePiece::new(
|
shiftable_structure_piece: shiftable_piece,
|
||||||
StructurePieceType::SwampHut,
|
spawned_witch: false,
|
||||||
x,
|
spawned_cat: false,
|
||||||
64,
|
|
||||||
z,
|
|
||||||
7,
|
|
||||||
7,
|
|
||||||
9,
|
|
||||||
BlockDirection::get_random_horizontal_direction(&mut context.random).get_axis(),
|
|
||||||
),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
@ -57,17 +67,72 @@ impl StructureGenerator for SwampHutGenerator {
|
|||||||
|
|
||||||
pub struct SwampHutPiece {
|
pub struct SwampHutPiece {
|
||||||
shiftable_structure_piece: ShiftableStructurePiece,
|
shiftable_structure_piece: ShiftableStructurePiece,
|
||||||
|
spawned_witch: bool,
|
||||||
|
spawned_cat: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwampHutPiece {
|
||||||
|
fn spruce_stairs(facing: HorizontalFacing, shape: StairsShape) -> &'static BlockState {
|
||||||
|
let mut props = OakStairsLikeProperties::default(&Block::SPRUCE_STAIRS);
|
||||||
|
props.facing = facing;
|
||||||
|
props.shape = shape;
|
||||||
|
BlockState::from_id(props.to_state_id(&Block::SPRUCE_STAIRS))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_witch(&mut self, chunk: &mut ProtoChunk, chunk_box: &BlockBox) {
|
||||||
|
if !self.spawned_witch {
|
||||||
|
let pos = self.shiftable_structure_piece.piece.offset_pos(2, 2, 5);
|
||||||
|
if chunk_box.contains_pos(&pos) {
|
||||||
|
self.spawned_witch = true;
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:witch".to_string());
|
||||||
|
nbt.put_list(
|
||||||
|
"Pos",
|
||||||
|
vec![
|
||||||
|
(f64::from(pos.x) + 0.5).into(),
|
||||||
|
f64::from(pos.y).into(),
|
||||||
|
(f64::from(pos.z) + 0.5).into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
nbt.put_bool("PersistenceRequired", true);
|
||||||
|
chunk.add_structure_entity(nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn spawn_cat(&mut self, chunk: &mut ProtoChunk, chunk_box: &BlockBox) {
|
||||||
|
if !self.spawned_cat {
|
||||||
|
let pos = self.shiftable_structure_piece.piece.offset_pos(2, 2, 5);
|
||||||
|
if chunk_box.contains_pos(&pos) {
|
||||||
|
self.spawned_cat = true;
|
||||||
|
let mut nbt = NbtCompound::new();
|
||||||
|
nbt.put_string("id", "minecraft:cat".to_string());
|
||||||
|
nbt.put_list(
|
||||||
|
"Pos",
|
||||||
|
vec![
|
||||||
|
(f64::from(pos.x) + 0.5).into(),
|
||||||
|
f64::from(pos.y).into(),
|
||||||
|
(f64::from(pos.z) + 0.5).into(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
nbt.put_bool("PersistenceRequired", true);
|
||||||
|
nbt.put_string("variant", "minecraft:all_black".to_string());
|
||||||
|
chunk.add_structure_entity(nbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePieceBase for SwampHutPiece {
|
impl StructurePieceBase for SwampHutPiece {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn place(
|
fn place(
|
||||||
&mut self,
|
&mut self,
|
||||||
chunk: &mut ProtoChunk,
|
chunk: &mut ProtoChunk,
|
||||||
_block_registry: &dyn crate::world::WorldPortalExt,
|
block_registry: &dyn WorldPortalExt,
|
||||||
_random: &mut RandomGenerator,
|
_random: &mut RandomGenerator,
|
||||||
_seed: i64,
|
_seed: i64,
|
||||||
chunk_box: &BlockBox,
|
chunk_box: &BlockBox,
|
||||||
@ -210,20 +275,60 @@ impl StructurePieceBase for SwampHutPiece {
|
|||||||
p.add_block(chunk, oak_fence, 1, 2, 1, &box_limit);
|
p.add_block(chunk, oak_fence, 1, 2, 1, &box_limit);
|
||||||
p.add_block(chunk, oak_fence, 5, 2, 1, &box_limit);
|
p.add_block(chunk, oak_fence, 5, 2, 1, &box_limit);
|
||||||
|
|
||||||
// let stairs_n = Block::SPRUCE_STAIRS.default_state.with("facing", "north");
|
let stairs_n = Self::spruce_stairs(HorizontalFacing::North, StairsShape::Straight);
|
||||||
// let stairs_e = Block::SPRUCE_STAIRS.default_state.with("facing", "east");
|
let stairs_e = Self::spruce_stairs(HorizontalFacing::East, StairsShape::Straight);
|
||||||
// let stairs_w = Block::SPRUCE_STAIRS.default_state.with("facing", "west");
|
let stairs_w = Self::spruce_stairs(HorizontalFacing::West, StairsShape::Straight);
|
||||||
// let stairs_s = Block::SPRUCE_STAIRS.default_state.with("facing", "south");
|
let stairs_s = Self::spruce_stairs(HorizontalFacing::South, StairsShape::Straight);
|
||||||
|
|
||||||
// p.fill_with_outline(chunk, &box_limit, false, 0, 4, 1, 6, 4, 1, &stairs_n, &stairs_n);
|
p.fill_with_outline(
|
||||||
// p.fill_with_outline(chunk, &box_limit, false, 0, 4, 2, 0, 4, 7, &stairs_e, &stairs_e);
|
chunk, &box_limit, false, 0, 4, 1, 6, 4, 1, stairs_n, stairs_n,
|
||||||
// p.fill_with_outline(chunk, &box_limit, false, 6, 4, 2, 6, 4, 7, &stairs_w, &stairs_w);
|
);
|
||||||
// p.fill_with_outline(chunk, &box_limit, false, 0, 4, 8, 6, 4, 8, &stairs_s, &stairs_s);
|
p.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 0, 4, 2, 0, 4, 7, stairs_e, stairs_e,
|
||||||
|
);
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 6, 4, 2, 6, 4, 7, stairs_w, stairs_w,
|
||||||
|
);
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 0, 4, 8, 6, 4, 8, stairs_s, stairs_s,
|
||||||
|
);
|
||||||
|
|
||||||
// p.add_block(chunk, &stairs_n.with("shape", "outer_right"), 0, 4, 1, &box_limit);
|
p.place_block(
|
||||||
// p.add_block(chunk, &stairs_n.with("shape", "outer_left"), 6, 4, 1, &box_limit);
|
chunk,
|
||||||
// p.add_block(chunk, &stairs_s.with("shape", "outer_left"), 0, 4, 8, &box_limit);
|
block_registry,
|
||||||
// p.add_block(chunk, &stairs_s.with("shape", "outer_right"), 6, 4, 8, &box_limit);
|
Self::spruce_stairs(HorizontalFacing::North, StairsShape::OuterRight),
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
p.place_block(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
Self::spruce_stairs(HorizontalFacing::North, StairsShape::OuterLeft),
|
||||||
|
6,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
p.place_block(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
Self::spruce_stairs(HorizontalFacing::South, StairsShape::OuterLeft),
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
p.place_block(
|
||||||
|
chunk,
|
||||||
|
block_registry,
|
||||||
|
Self::spruce_stairs(HorizontalFacing::South, StairsShape::OuterRight),
|
||||||
|
6,
|
||||||
|
4,
|
||||||
|
8,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
for i in [2, 7] {
|
for i in [2, 7] {
|
||||||
for j in [1, 5] {
|
for j in [1, 5] {
|
||||||
@ -231,15 +336,10 @@ impl StructurePieceBase for SwampHutPiece {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if !self.has_witch {
|
self.spawn_witch(chunk, &box_limit);
|
||||||
// let world_coords = p.get_world_coords(2, 2, 5);
|
self.spawn_cat(chunk, &box_limit);
|
||||||
// if box_limit.contains(world_coords.0, world_coords.1, world_coords.2) {
|
|
||||||
// self.has_witch = true;
|
|
||||||
// // TODO: chunk.add_entity(Witch, world_coords)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// TODO: self.spawn_cat(chunk, &box_limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_structure_piece(&self) -> &super::StructurePiece {
|
fn get_structure_piece(&self) -> &super::StructurePiece {
|
||||||
&self.shiftable_structure_piece.piece
|
&self.shiftable_structure_piece.piece
|
||||||
}
|
}
|
||||||
|
|||||||
@ -232,7 +232,7 @@ impl PosRuleTest {
|
|||||||
let dist = dx + dy + dz;
|
let dist = dx + dy + dz;
|
||||||
let chance =
|
let chance =
|
||||||
calculate_linear_chance(dist, *min_dist, *max_dist, *min_chance, *max_chance);
|
calculate_linear_chance(dist, *min_dist, *max_dist, *min_chance, *max_chance);
|
||||||
rng.next_f32() < chance
|
rng.next_f32() <= chance
|
||||||
}
|
}
|
||||||
Self::AxisAlignedLinearPos {
|
Self::AxisAlignedLinearPos {
|
||||||
axis,
|
axis,
|
||||||
@ -248,7 +248,7 @@ impl PosRuleTest {
|
|||||||
};
|
};
|
||||||
let chance =
|
let chance =
|
||||||
calculate_linear_chance(dist, *min_dist, *max_dist, *min_chance, *max_chance);
|
calculate_linear_chance(dist, *min_dist, *max_dist, *min_chance, *max_chance);
|
||||||
rng.next_f32() < chance
|
rng.next_f32() <= chance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -526,6 +526,19 @@ fn replace_preserving_properties(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_random_facing_stairs(
|
||||||
|
rng: &mut impl RandomImpl,
|
||||||
|
stair_block: &'static Block,
|
||||||
|
) -> &'static BlockState {
|
||||||
|
let facings = ["north", "south", "east", "west"];
|
||||||
|
let halves = ["bottom", "top"];
|
||||||
|
let facing = facings[rng.next_bounded_i32(4) as usize];
|
||||||
|
let half = halves[rng.next_bounded_i32(2) as usize];
|
||||||
|
let props = [("facing", facing), ("half", half)];
|
||||||
|
let state_id = stair_block.from_properties(&props).to_state_id(stair_block);
|
||||||
|
BlockState::from_id(state_id)
|
||||||
|
}
|
||||||
|
|
||||||
fn process_block_age(
|
fn process_block_age(
|
||||||
state: &'static BlockState,
|
state: &'static BlockState,
|
||||||
mossiness: f32,
|
mossiness: f32,
|
||||||
@ -537,45 +550,50 @@ fn process_block_age(
|
|||||||
block_id,
|
block_id,
|
||||||
BlockId::STONE_BRICKS
|
BlockId::STONE_BRICKS
|
||||||
| BlockId::STONE
|
| BlockId::STONE
|
||||||
| BlockId::CRACKED_STONE_BRICKS
|
|
||||||
| BlockId::CHISELED_STONE_BRICKS
|
| BlockId::CHISELED_STONE_BRICKS
|
||||||
| BlockId::INFESTED_STONE
|
| BlockId::INFESTED_STONE
|
||||||
| BlockId::INFESTED_STONE_BRICKS
|
| BlockId::INFESTED_STONE_BRICKS
|
||||||
| BlockId::INFESTED_CRACKED_STONE_BRICKS
|
|
||||||
| BlockId::INFESTED_CHISELED_STONE_BRICKS
|
| BlockId::INFESTED_CHISELED_STONE_BRICKS
|
||||||
) {
|
) {
|
||||||
if rng.next_f32() < 0.5 {
|
if rng.next_f32() < 0.5 {
|
||||||
|
let non_mossy = [
|
||||||
|
Block::CRACKED_STONE_BRICKS.default_state,
|
||||||
|
get_random_facing_stairs(rng, &Block::STONE_BRICK_STAIRS),
|
||||||
|
];
|
||||||
|
let mossy = [
|
||||||
|
Block::MOSSY_STONE_BRICKS.default_state,
|
||||||
|
get_random_facing_stairs(rng, &Block::MOSSY_STONE_BRICK_STAIRS),
|
||||||
|
];
|
||||||
|
let idx = rng.next_bounded_i32(2) as usize;
|
||||||
if rng.next_f32() < mossiness {
|
if rng.next_f32() < mossiness {
|
||||||
return Block::MOSSY_STONE_BRICKS.default_state;
|
return mossy[idx];
|
||||||
}
|
}
|
||||||
return Block::CRACKED_STONE_BRICKS.default_state;
|
return non_mossy[idx];
|
||||||
}
|
}
|
||||||
} else if block_id == BlockId::STONE_BRICK_STAIRS
|
} else if check_block_has_tag(block_id, "stairs") {
|
||||||
|| block_id == BlockId::INFESTED_MOSSY_STONE_BRICKS
|
if rng.next_f32() < 0.5 {
|
||||||
{
|
let non_mossy = [
|
||||||
if rng.next_f32() < 0.5 && rng.next_f32() < mossiness {
|
Block::STONE_SLAB.default_state,
|
||||||
return replace_preserving_properties(state, &Block::MOSSY_STONE_BRICK_STAIRS);
|
Block::STONE_BRICK_SLAB.default_state,
|
||||||
|
];
|
||||||
|
let mossy = [
|
||||||
|
replace_preserving_properties(state, &Block::MOSSY_STONE_BRICK_STAIRS),
|
||||||
|
Block::MOSSY_STONE_BRICK_SLAB.default_state,
|
||||||
|
];
|
||||||
|
let idx = rng.next_bounded_i32(2) as usize;
|
||||||
|
if rng.next_f32() < mossiness {
|
||||||
|
return mossy[idx];
|
||||||
|
}
|
||||||
|
return non_mossy[idx];
|
||||||
}
|
}
|
||||||
} else if block_id == BlockId::STONE_BRICK_SLAB {
|
} else if check_block_has_tag(block_id, "slabs") {
|
||||||
if rng.next_f32() < 0.5 && rng.next_f32() < mossiness {
|
if rng.next_f32() < mossiness {
|
||||||
return replace_preserving_properties(state, &Block::MOSSY_STONE_BRICK_SLAB);
|
return replace_preserving_properties(state, &Block::MOSSY_STONE_BRICK_SLAB);
|
||||||
}
|
}
|
||||||
} else if block_id == BlockId::STONE_BRICK_WALL {
|
} else if check_block_has_tag(block_id, "walls") {
|
||||||
if rng.next_f32() < 0.5 && rng.next_f32() < mossiness {
|
if rng.next_f32() < mossiness {
|
||||||
return replace_preserving_properties(state, &Block::MOSSY_STONE_BRICK_WALL);
|
return replace_preserving_properties(state, &Block::MOSSY_STONE_BRICK_WALL);
|
||||||
}
|
}
|
||||||
} else if block_id == BlockId::COBBLESTONE_STAIRS {
|
|
||||||
if rng.next_f32() < 0.5 && rng.next_f32() < mossiness {
|
|
||||||
return replace_preserving_properties(state, &Block::MOSSY_COBBLESTONE_STAIRS);
|
|
||||||
}
|
|
||||||
} else if block_id == BlockId::COBBLESTONE_SLAB {
|
|
||||||
if rng.next_f32() < 0.5 && rng.next_f32() < mossiness {
|
|
||||||
return replace_preserving_properties(state, &Block::MOSSY_COBBLESTONE_SLAB);
|
|
||||||
}
|
|
||||||
} else if block_id == BlockId::COBBLESTONE_WALL {
|
|
||||||
if rng.next_f32() < 0.5 && rng.next_f32() < mossiness {
|
|
||||||
return replace_preserving_properties(state, &Block::MOSSY_COBBLESTONE_WALL);
|
|
||||||
}
|
|
||||||
} else if block_id == BlockId::OBSIDIAN && rng.next_f32() < 0.15 {
|
} else if block_id == BlockId::OBSIDIAN && rng.next_f32() < 0.15 {
|
||||||
return Block::CRYING_OBSIDIAN.default_state;
|
return Block::CRYING_OBSIDIAN.default_state;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -372,9 +372,21 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
"minecraft:fallen_tree" => {
|
"minecraft:fallen_tree" => {
|
||||||
let trunk = value_to_block_state_provider(&config["trunk_provider"]);
|
let trunk = value_to_block_state_provider(&config["trunk_provider"]);
|
||||||
|
let log_length = value_to_int_provider(&config["log_length"]);
|
||||||
|
let stump_decorators: Vec<TokenStream> = config["stump_decorators"]
|
||||||
|
.as_array()
|
||||||
|
.map(|arr| arr.iter().map(value_to_tree_decorator).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
|
let log_decorators: Vec<TokenStream> = config["log_decorators"]
|
||||||
|
.as_array()
|
||||||
|
.map(|arr| arr.iter().map(value_to_tree_decorator).collect())
|
||||||
|
.unwrap_or_default();
|
||||||
quote! {
|
quote! {
|
||||||
ConfiguredFeature::FallenTree(FallenTreeFeature {
|
ConfiguredFeature::FallenTree(FallenTreeFeature {
|
||||||
trunk_provider: #trunk,
|
trunk_provider: #trunk,
|
||||||
|
log_length: #log_length,
|
||||||
|
stump_decorators: vec![#(#stump_decorators),*],
|
||||||
|
log_decorators: vec![#(#log_decorators),*],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -810,7 +822,42 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"minecraft:multiface_growth" => {
|
"minecraft:multiface_growth" => {
|
||||||
quote! { ConfiguredFeature::MultifaceGrowth(crate::generation::feature::features::multiface_growth::MultifaceGrowthFeature {}) }
|
let block_name = config["block"].as_str().unwrap_or("minecraft:glow_lichen");
|
||||||
|
let stripped = block_name.strip_prefix("minecraft:").unwrap_or(block_name);
|
||||||
|
let block_ident =
|
||||||
|
quote::format_ident!("{}", stripped.to_uppercase().replace([':', '-'], "_"));
|
||||||
|
let search_range = config["search_range"].as_i64().unwrap_or(10) as i32;
|
||||||
|
let can_place_on_floor = config["can_place_on_floor"].as_bool().unwrap_or(false);
|
||||||
|
let can_place_on_ceiling = config["can_place_on_ceiling"].as_bool().unwrap_or(false);
|
||||||
|
let can_place_on_wall = config["can_place_on_wall"].as_bool().unwrap_or(false);
|
||||||
|
let chance_of_spreading = config["chance_of_spreading"].as_f64().unwrap_or(0.5) as f32;
|
||||||
|
let can_be_placed_on: Vec<TokenStream> = config["can_be_placed_on"]
|
||||||
|
.as_array()
|
||||||
|
.map(|arr| {
|
||||||
|
arr.iter()
|
||||||
|
.filter_map(|v| v.as_str())
|
||||||
|
.map(|name| {
|
||||||
|
let stripped = name.strip_prefix("minecraft:").unwrap_or(name);
|
||||||
|
let ident = quote::format_ident!(
|
||||||
|
"{}",
|
||||||
|
stripped.to_uppercase().replace([':', '-'], "_")
|
||||||
|
);
|
||||||
|
quote! { pumpkin_data::BlockId::#ident }
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
quote! {
|
||||||
|
ConfiguredFeature::MultifaceGrowth(crate::generation::feature::features::multiface_growth::MultifaceGrowthFeature {
|
||||||
|
place_block: pumpkin_data::BlockId::#block_ident,
|
||||||
|
search_range: #search_range,
|
||||||
|
can_place_on_floor: #can_place_on_floor,
|
||||||
|
can_place_on_ceiling: #can_place_on_ceiling,
|
||||||
|
can_place_on_wall: #can_place_on_wall,
|
||||||
|
chance_of_spreading: #chance_of_spreading,
|
||||||
|
can_be_placed_on: vec![#(#can_be_placed_on),*],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:blue_ice" => {
|
"minecraft:blue_ice" => {
|
||||||
quote! { ConfiguredFeature::BlueIce(crate::generation::feature::features::blue_ice::BlueIceFeature {}) }
|
quote! { ConfiguredFeature::BlueIce(crate::generation::feature::features::blue_ice::BlueIceFeature {}) }
|
||||||
@ -842,7 +889,22 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
|||||||
quote! { ConfiguredFeature::CoralClaw(crate::generation::feature::features::coral::coral_claw::CoralClawFeature) }
|
quote! { ConfiguredFeature::CoralClaw(crate::generation::feature::features::coral::coral_claw::CoralClawFeature) }
|
||||||
}
|
}
|
||||||
"minecraft:huge_fungus" => {
|
"minecraft:huge_fungus" => {
|
||||||
quote! { ConfiguredFeature::HugeFungus(crate::generation::feature::features::huge_fungus::HugeFungusFeature {}) }
|
let valid_base_block = value_to_block_state(&config["valid_base_block"]);
|
||||||
|
let stem_state = value_to_block_state(&config["stem_state"]);
|
||||||
|
let hat_state = value_to_block_state(&config["hat_state"]);
|
||||||
|
let decor_state = value_to_block_state(&config["decor_state"]);
|
||||||
|
let replaceable_blocks = value_to_block_predicate(&config["replaceable_blocks"]);
|
||||||
|
let planted = config["planted"].as_bool().unwrap_or(false);
|
||||||
|
quote! {
|
||||||
|
ConfiguredFeature::HugeFungus(crate::generation::feature::features::huge_fungus::HugeFungusFeature {
|
||||||
|
valid_base_block: #valid_base_block,
|
||||||
|
stem_state: #stem_state,
|
||||||
|
hat_state: #hat_state,
|
||||||
|
decor_state: #decor_state,
|
||||||
|
replaceable_blocks: #replaceable_blocks,
|
||||||
|
planted: #planted,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:weeping_vines" => {
|
"minecraft:weeping_vines" => {
|
||||||
quote! { ConfiguredFeature::WeepingVines(crate::generation::feature::features::weeping_vines::WeepingVinesFeature {}) }
|
quote! { ConfiguredFeature::WeepingVines(crate::generation::feature::features::weeping_vines::WeepingVinesFeature {}) }
|
||||||
@ -860,10 +922,28 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"minecraft:delta_feature" => {
|
"minecraft:delta_feature" => {
|
||||||
quote! { ConfiguredFeature::DeltaFeature(crate::generation::feature::features::delta_feature::DeltaFeatureFeature {}) }
|
let contents = value_to_block_state(&config["contents"]);
|
||||||
|
let rim = value_to_block_state(&config["rim"]);
|
||||||
|
let size = value_to_int_provider(&config["size"]);
|
||||||
|
let rim_size = value_to_int_provider(&config["rim_size"]);
|
||||||
|
quote! {
|
||||||
|
ConfiguredFeature::DeltaFeature(crate::generation::feature::features::delta_feature::DeltaFeatureFeature {
|
||||||
|
contents: #contents,
|
||||||
|
rim: #rim,
|
||||||
|
size: #size,
|
||||||
|
rim_size: #rim_size,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:fill_layer" => {
|
"minecraft:fill_layer" => {
|
||||||
quote! { ConfiguredFeature::FillLayer(crate::generation::feature::features::fill_layer::FillLayerFeature {}) }
|
let height = config["height"].as_i64().unwrap_or(0) as i32;
|
||||||
|
let state = value_to_block_state(&config["state"]);
|
||||||
|
quote! {
|
||||||
|
ConfiguredFeature::FillLayer(crate::generation::feature::features::fill_layer::FillLayerFeature {
|
||||||
|
height: #height,
|
||||||
|
state: #state,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:bonus_chest" => {
|
"minecraft:bonus_chest" => {
|
||||||
quote! { ConfiguredFeature::BonusChest(crate::generation::feature::features::bonus_chest::BonusChestFeature {}) }
|
quote! { ConfiguredFeature::BonusChest(crate::generation::feature::features::bonus_chest::BonusChestFeature {}) }
|
||||||
@ -898,10 +978,31 @@ pub fn value_to_configured_feature(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"minecraft:block_pile" => {
|
"minecraft:block_pile" => {
|
||||||
quote! { ConfiguredFeature::BlockPile(crate::generation::feature::features::block_pile::BlockPileFeature {}) }
|
let provider = value_to_block_state_provider(&config["state_provider"]);
|
||||||
|
quote! {
|
||||||
|
ConfiguredFeature::BlockPile(crate::generation::feature::features::block_pile::BlockPileFeature {
|
||||||
|
state_provider: #provider,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:replace_single_block" => {
|
"minecraft:replace_single_block" => {
|
||||||
quote! { ConfiguredFeature::ReplaceSingleBlock(crate::generation::feature::features::replace_single_block::ReplaceSingleBlockFeature {}) }
|
let targets: Vec<TokenStream> = config["targets"]
|
||||||
|
.as_array()
|
||||||
|
.map(|arr| {
|
||||||
|
arr.iter()
|
||||||
|
.map(|t| {
|
||||||
|
let rule = value_to_rule_test(&t["target"]);
|
||||||
|
let state = value_to_block_state(&t["state"]);
|
||||||
|
quote! { OreTarget { target: #rule, state: #state } }
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
quote! {
|
||||||
|
ConfiguredFeature::ReplaceSingleBlock(crate::generation::feature::features::replace_single_block::ReplaceSingleBlockFeature {
|
||||||
|
targets: vec![#(#targets),*],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:void_start_platform" => {
|
"minecraft:void_start_platform" => {
|
||||||
quote! { ConfiguredFeature::VoidStartPlatform(crate::generation::feature::features::void_start_platform::VoidStartPlatformFeature {}) }
|
quote! { ConfiguredFeature::VoidStartPlatform(crate::generation::feature::features::void_start_platform::VoidStartPlatformFeature {}) }
|
||||||
@ -1506,13 +1607,17 @@ fn value_to_tree_decorator(v: &Value) -> TokenStream {
|
|||||||
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
quote! { TreeDecorator::LeaveVine(LeavesVineTreeDecorator { probability: #prob }) }
|
quote! { TreeDecorator::LeaveVine(LeavesVineTreeDecorator { probability: #prob }) }
|
||||||
}
|
}
|
||||||
"minecraft:cocoa" => quote! { TreeDecorator::Cocoa(CocoaTreeDecorator {}) },
|
"minecraft:cocoa" => {
|
||||||
|
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
quote! { TreeDecorator::Cocoa(CocoaTreeDecorator { probability: #prob }) }
|
||||||
|
}
|
||||||
"minecraft:beehive" => {
|
"minecraft:beehive" => {
|
||||||
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
quote! { TreeDecorator::Beehive(BeehiveTreeDecorator { probability: #prob }) }
|
quote! { TreeDecorator::Beehive(BeehiveTreeDecorator { probability: #prob }) }
|
||||||
}
|
}
|
||||||
"minecraft:alter_ground" => {
|
"minecraft:alter_ground" => {
|
||||||
quote! { TreeDecorator::AlterGround(AlterGroundTreeDecorator {}) }
|
let provider = value_to_block_state_provider(&v["provider"]);
|
||||||
|
quote! { TreeDecorator::AlterGround(AlterGroundTreeDecorator { provider: #provider }) }
|
||||||
}
|
}
|
||||||
"minecraft:attached_to_logs" => {
|
"minecraft:attached_to_logs" => {
|
||||||
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
@ -1534,7 +1639,29 @@ fn value_to_tree_decorator(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"minecraft:attached_to_leaves" => {
|
"minecraft:attached_to_leaves" => {
|
||||||
quote! { TreeDecorator::AttachedToLeaves(AttachedToLeavesTreeDecorator {}) }
|
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
let exclusion_radius_xz = v["exclusion_radius_xz"].as_i64().unwrap_or(0) as i32;
|
||||||
|
let exclusion_radius_y = v["exclusion_radius_y"].as_i64().unwrap_or(0) as i32;
|
||||||
|
let bp = value_to_block_state_provider(&v["block_provider"]);
|
||||||
|
let required_empty_blocks = v["required_empty_blocks"].as_i64().unwrap_or(1) as i32;
|
||||||
|
let dirs: Vec<TokenStream> = v["directions"]
|
||||||
|
.as_array()
|
||||||
|
.map(|arr| {
|
||||||
|
arr.iter()
|
||||||
|
.filter_map(|d| d.as_str().map(value_to_block_direction))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
quote! {
|
||||||
|
TreeDecorator::AttachedToLeaves(AttachedToLeavesTreeDecorator {
|
||||||
|
probability: #prob,
|
||||||
|
exclusion_radius_xz: #exclusion_radius_xz,
|
||||||
|
exclusion_radius_y: #exclusion_radius_y,
|
||||||
|
block_provider: #bp,
|
||||||
|
required_empty_blocks: #required_empty_blocks,
|
||||||
|
directions: vec![#(#dirs),*],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"minecraft:place_on_ground" => {
|
"minecraft:place_on_ground" => {
|
||||||
let tries = v["tries"].as_i64().unwrap_or(1) as i32;
|
let tries = v["tries"].as_i64().unwrap_or(1) as i32;
|
||||||
@ -1551,10 +1678,20 @@ fn value_to_tree_decorator(v: &Value) -> TokenStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"minecraft:creaking_heart" => {
|
"minecraft:creaking_heart" => {
|
||||||
quote! { TreeDecorator::CreakingHeart(CreakingHeartTreeDecorator {}) }
|
let prob = v["probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
quote! { TreeDecorator::CreakingHeart(CreakingHeartTreeDecorator { probability: #prob }) }
|
||||||
}
|
}
|
||||||
"minecraft:pale_moss" => {
|
"minecraft:pale_moss" => {
|
||||||
quote! { TreeDecorator::PaleMoss(PaleMossTreeDecorator {}) }
|
let leaves_probability = v["leaves_probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
let trunk_probability = v["trunk_probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
let ground_probability = v["ground_probability"].as_f64().unwrap_or(0.0) as f32;
|
||||||
|
quote! {
|
||||||
|
TreeDecorator::PaleMoss(PaleMossTreeDecorator {
|
||||||
|
leaves_probability: #leaves_probability,
|
||||||
|
trunk_probability: #trunk_probability,
|
||||||
|
ground_probability: #ground_probability,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
other => {
|
other => {
|
||||||
let msg = format!("unknown tree decorator: {other}");
|
let msg = format!("unknown tree decorator: {other}");
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user