feat: Nether Fortress

Co-Authored-By: Richard Marshall <29306641+Purdze@users.noreply.github.com>
This commit is contained in:
Alexander Medvedev 2026-03-06 09:42:54 +01:00
parent e125ca19be
commit 3f4cdd0695
37 changed files with 3181 additions and 276 deletions

View File

@ -7,7 +7,7 @@ pub use difficulty::Difficulty;
pub use gamemode::GameMode; pub use gamemode::GameMode;
pub use permission::PermissionLvl; pub use permission::PermissionLvl;
use crate::math::vector3::Axis; use crate::{math::vector3::Axis, random::RandomImpl};
pub mod biome; pub mod biome;
pub mod difficulty; pub mod difficulty;
@ -131,6 +131,15 @@ impl BlockDirection {
Self::East | Self::West => Axis::X, Self::East | Self::West => Axis::X,
} }
} }
pub fn get_random_horizontal_direction(random: &mut impl RandomImpl) -> Self {
match random.next_bounded_i32(4) {
0 => Self::North,
1 => Self::East,
2 => Self::South,
_ => Self::West,
}
}
} }
/// A mutable slice split into three parts: the element at the split index, the start, and the end. /// A mutable slice split into three parts: the element at the split index, the start, and the end.

View File

@ -90,8 +90,14 @@ impl GenerationCache for Cache {
let dx = chunk_x - self.x; let dx = chunk_x - self.x;
let dz = chunk_z - self.z; let dz = chunk_z - self.z;
(dx >= 0 && dx < self.size && dz >= 0 && dz < self.size) if dx < 0 || dx >= self.size || dz < 0 || dz >= self.size {
.then(|| self.chunks[(dx * self.size + dz) as usize].get_proto_chunk()) return None;
}
match &self.chunks[(dx * self.size + dz) as usize] {
Chunk::Proto(chunk) => Some(chunk),
Chunk::Level(_) => None,
}
} }
fn try_get_proto_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk> { fn try_get_proto_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk> {

View File

@ -1022,14 +1022,10 @@ impl ProtoChunk {
match instance { match instance {
StructureInstance::Start(pos) => tasks.push(pos.collector.clone()), StructureInstance::Start(pos) => tasks.push(pos.collector.clone()),
StructureInstance::Reference(origin_block_pos) => { StructureInstance::Reference(collector) => {
let origin_chunk_x = origin_block_pos.0.x >> 4; let collector_arc = collector.clone();
let origin_chunk_z = origin_block_pos.0.z >> 4; if !tasks.iter().any(|t| Arc::ptr_eq(t, &collector_arc)) {
if let Some(neighbor) = cache.get_chunk(origin_chunk_x, origin_chunk_z) tasks.push(collector_arc);
&& let Some(StructureInstance::Start(pos)) =
neighbor.structure_starts.get(id)
{
tasks.push(pos.collector.clone());
} }
} }
} }
@ -1069,29 +1065,10 @@ impl ProtoChunk {
} }
} }
} }
StructureInstance::Reference(origin_block_pos) => { StructureInstance::Reference(collector) => {
let origin_chunk_x = origin_block_pos.0.x >> 4; let collector_arc = collector.clone();
let origin_chunk_z = origin_block_pos.0.z >> 4; if !tasks.iter().any(|t| Arc::ptr_eq(t, &collector_arc)) {
if let Some(origin_neighbor) = tasks.push(collector_arc);
cache.try_get_proto_chunk(origin_chunk_x, origin_chunk_z)
&& let Some(StructureInstance::Start(pos)) =
origin_neighbor.structure_starts.get(id)
{
let start_x = chunk_pos::start_block_x(center_x);
let start_z = chunk_pos::start_block_z(center_z);
let end_x = start_x + 15;
let end_z = start_z + 15;
if pos
.get_bounding_box()
.intersects_raw_xz(start_x, start_z, end_x, end_z)
{
let collector_arc = pos.collector.clone();
if !tasks.iter().any(|t| Arc::ptr_eq(t, &collector_arc))
{
tasks.push(collector_arc);
}
}
} }
} }
} }
@ -1218,7 +1195,7 @@ impl ProtoChunk {
.get_bounding_box() .get_bounding_box()
.intersects_raw_xz(start_x, start_z, end_x, end_z) .intersects_raw_xz(start_x, start_z, end_x, end_z)
{ {
references.push((*key, start_data.start_pos)); references.push((*key, start_data.collector.clone()));
} }
} }
} }

View File

@ -10,8 +10,8 @@ use crate::{
structure::structures::{ structure::structures::{
StructureGenerator, StructureGeneratorContext, StructurePosition, StructureGenerator, StructureGeneratorContext, StructurePosition,
buried_treasure::BuriedTreasureGenerator, create_chunk_random, igloo::IglooGenerator, buried_treasure::BuriedTreasureGenerator, create_chunk_random, igloo::IglooGenerator,
nether_fossil::NetherFossilGenerator, stronghold::StrongholdGenerator, nether_fortress::NetherFortressGenerator, nether_fossil::NetherFossilGenerator,
swamp_hut::SwampHutGenerator, stronghold::StrongholdGenerator, swamp_hut::SwampHutGenerator,
}, },
}, },
}; };
@ -49,6 +49,9 @@ pub fn try_generate_structure(
StructureKeys::Stronghold => { StructureKeys::Stronghold => {
StrongholdGenerator::get_structure_position(&StrongholdGenerator, context) StrongholdGenerator::get_structure_position(&StrongholdGenerator, context)
} }
StructureKeys::Fortress => {
NetherFortressGenerator::get_structure_position(&NetherFortressGenerator, context)
}
StructureKeys::NetherFossil => { StructureKeys::NetherFossil => {
NetherFossilGenerator::get_structure_position(&NetherFossilGenerator, context) NetherFossilGenerator::get_structure_position(&NetherFossilGenerator, context)
} }

View File

@ -56,7 +56,13 @@ impl StructurePieceBase for BuriedTreasurePiece {
Box::new((*self).clone()) Box::new((*self).clone())
} }
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
_chunk_box: &BlockBox,
) {
let boundingbox = self.bounding_box(); let boundingbox = self.bounding_box();
let y = chunk.get_top_y( let y = chunk.get_top_y(
&HeightMap::OceanFloorWg, &HeightMap::OceanFloorWg,

View File

@ -8,7 +8,10 @@
use std::sync::Arc; use std::sync::Arc;
use pumpkin_util::{math::position::BlockPos, math::vector3::Vector3, random::RandomGenerator}; use pumpkin_util::{
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
random::RandomGenerator,
};
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
@ -142,7 +145,13 @@ impl StructurePieceBase for IglooPiece {
Box::new(self.clone()) Box::new(self.clone())
} }
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
_chunk_box: &BlockBox,
) {
let origin = self.shiftable_structure_piece.piece.bounding_box.min; let origin = self.shiftable_structure_piece.piece.bounding_box.min;
// Vanilla samples height at the entrance position (3, 0, 5 in template space) // Vanilla samples height at the entrance position (3, 0, 5 in template space)

View File

@ -10,7 +10,6 @@ use pumpkin_util::{
}; };
use tracing::debug; use tracing::debug;
use crate::generation::structure::structures::stronghold::PieceWeight;
use crate::generation::structure::structures::stronghold::StrongholdPieceType; use crate::generation::structure::structures::stronghold::StrongholdPieceType;
use crate::{ use crate::{
ProtoChunk, ProtoChunk,
@ -49,7 +48,13 @@ pub trait StructurePieceBase: Send + Sync {
fn clone_box(&self) -> Box<dyn StructurePieceBase>; fn clone_box(&self) -> Box<dyn StructurePieceBase>;
/// Places the blocks for this piece into the chunk. /// Places the blocks for this piece into the chunk.
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, seed: i64); fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
seed: i64,
_chunk_box: &BlockBox,
);
#[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_arguments)]
fn fill_openings( fn fill_openings(
@ -57,7 +62,7 @@ pub trait StructurePieceBase: Send + Sync {
_start: &StructurePiece, _start: &StructurePiece,
_random: &mut RandomGenerator, _random: &mut RandomGenerator,
// TODO: this is only for Stronghold and should not be here // TODO: this is only for Stronghold and should not be here
_weights: &mut Vec<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,
@ -65,6 +70,21 @@ pub trait StructurePieceBase: Send + Sync {
_pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>, _pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) { ) {
} }
fn fill_openings_nether(
&self,
_start: &StructurePiece,
_random: &mut RandomGenerator,
_bridge_pieces: &mut Vec<
crate::generation::structure::structures::nether_fortress::PieceWeight,
>,
_corridor_pieces: &mut Vec<
crate::generation::structure::structures::nether_fortress::PieceWeight,
>,
_collector: &mut StructurePiecesCollector,
_pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
}
} }
#[derive(Clone)] #[derive(Clone)]
@ -366,15 +386,6 @@ impl StructurePiece {
// world.mark_block_for_post_processing(&block_pos); // world.mark_block_for_post_processing(&block_pos);
// } // }
} }
pub fn get_random_horizontal_direction(random: &mut impl RandomImpl) -> BlockDirection {
match random.next_bounded_i32(4) {
0 => BlockDirection::North,
1 => BlockDirection::East,
2 => BlockDirection::South,
_ => BlockDirection::West,
}
}
} }
impl StructurePieceBase for StructurePiece { impl StructurePieceBase for StructurePiece {
@ -382,7 +393,14 @@ impl StructurePieceBase for StructurePiece {
Box::new(self.clone()) Box::new(self.clone())
} }
fn place(&mut self, _chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) {} fn place(
&mut self,
_chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
_chunk_box: &BlockBox,
) {
}
fn translate(&mut self, x: i32, y: i32, z: i32) { fn translate(&mut self, x: i32, y: i32, z: i32) {
self.bounding_box.move_pos(x, y, z); self.bounding_box.move_pos(x, y, z);
@ -451,7 +469,7 @@ impl StructurePiecesCollector {
for piece in &mut self.pieces { for piece in &mut self.pieces {
if piece.bounding_box().intersects(&chunk_box) { if piece.bounding_box().intersects(&chunk_box) {
piece.place(chunk, random, seed); piece.place(chunk, random, seed, &chunk_box);
} }
} }
} }
@ -552,5 +570,5 @@ pub enum StructureInstance {
Start(StructurePosition), Start(StructurePosition),
/// This chunk just contains a piece of a structure starting elsewhere. /// This chunk just contains a piece of a structure starting elsewhere.
/// Stores the `BlockPos` of the 'Start' so you can look it up. /// Stores the `BlockPos` of the 'Start' so you can look it up.
Reference(BlockPos), Reference(Arc<Mutex<StructurePiecesCollector>>),
} }

View File

@ -1,188 +0,0 @@
// use std::sync::Arc;
// use pumpkin_data::Block;
// use pumpkin_util::{
// math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
// random::RandomGenerator,
// };
// use serde::Deserialize;
// use crate::{
// ProtoChunk,
// generation::{
// positions::chunk_pos,
// structure::structures::{
// StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
// StructurePiecesCollector, StructurePosition,
// },
// },
// };
// #[derive(Deserialize, Clone, Debug)]
// pub struct NetherFortressGenerator;
// impl StructureGenerator for NetherFortressGenerator {
// fn get_structure_position(
// &self,
// context: StructureGeneratorContext,
// ) -> Option<StructurePosition> {
// let start_x = chunk_pos::start_block_x(context.chunk_x);
// let start_z = chunk_pos::start_block_z(context.chunk_z);
// let start_y = 64;
// let mut collector = StructurePiecesCollector::default();
// // Add a Bridge Platform piece
// let box_min = BlockPos::new(start_x, start_y, start_z);
// let box_max = BlockPos::new(start_x + 8, start_y + 10, start_z + 8);
// todo!();
// Some(StructurePosition {
// start_pos: box_min,
// collector: Arc::new(collector.into()),
// })
// }
// }
// #[derive(Debug, Clone)]
// pub enum NetherFortressPieceType {
// BridgePlatform,
// SmallCorridor,
// }
// struct NetherFortressPiece {
// piece: StructurePiece,
// piece_type: NetherFortressPieceType,
// }
// impl StructurePieceBase for NetherFortressPiece {
// fn place(&mut self, chunk: &mut ProtoChunk, _seed: i64) {
// let bounding_box = self.piece.bounding_box;
// let origin_x = bounding_box.min.x;
// let origin_y = bounding_box.min.y;
// let origin_z = bounding_box.min.z;
// let offset = |x, y, z| Vector3::new(origin_x + x, origin_y + y, origin_z + z);
// let fill_rel = |min_x, min_y, min_z, max_x, max_y, max_z, block, c: &mut ProtoChunk| {
// fill(
// origin_x + min_x,
// origin_y + min_y,
// origin_z + min_z,
// origin_x + max_x,
// origin_y + max_y,
// origin_z + max_z,
// block,
// c,
// )
// };
// let fill_outline_rel =
// |min_x, min_y, min_z, max_x, max_y, max_z, outline, inside, c: &mut ProtoChunk| {
// fill_with_outline(
// origin_x + min_x,
// origin_y + min_y,
// origin_z + min_z,
// origin_x + max_x,
// origin_y + max_y,
// origin_z + max_z,
// outline,
// inside,
// c,
// )
// };
// match self.piece_type {
// NetherFortressPieceType::BridgePlatform => {
// fill_rel(0, 2, 0, 6, 7, 7, Block::AIR.default_state, chunk);
// fill_rel(1, 0, 0, 5, 1, 7, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(1, 2, 1, 5, 2, 7, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(1, 3, 2, 5, 3, 7, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(1, 4, 3, 5, 4, 7, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(1, 2, 0, 1, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(5, 2, 0, 5, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(1, 5, 2, 1, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(5, 5, 2, 5, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(0, 5, 3, 0, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(6, 5, 3, 6, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(1, 5, 8, 5, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
// let fence = &Block::NETHER_BRICK_FENCE.default_state;
// chunk.set_block_state(&offset(1, 6, 3), fence);
// chunk.set_block_state(&offset(5, 6, 3), fence);
// chunk.set_block_state(&offset(0, 6, 3), fence);
// chunk.set_block_state(&offset(6, 6, 3), fence);
// fill_outline_rel(0, 6, 4, 0, 6, 7, fence, fence, chunk);
// fill_outline_rel(6, 6, 4, 6, 6, 7, fence, fence, chunk);
// chunk.set_block_state(&offset(0, 6, 8), fence);
// chunk.set_block_state(&offset(6, 6, 8), fence);
// fill_outline_rel(1, 6, 8, 5, 6, 8, fence, fence, chunk);
// chunk.set_block_state(&offset(1, 7, 8), fence);
// fill_outline_rel(2, 7, 8, 4, 7, 8, fence, fence, chunk);
// chunk.set_block_state(&offset(5, 7, 8), fence);
// chunk.set_block_state(&offset(2, 8, 8), fence);
// chunk.set_block_state(&offset(3, 8, 8), fence);
// chunk.set_block_state(&offset(4, 8, 8), fence);
// // Blaze Spawner
// chunk.set_block_state(&offset(3, 5, 5), Block::SPAWNER.default_state);
// // Pillars down
// for x in 0..6 {
// for z in 0..6 {
// fill_downwards(
// origin_x + x,
// origin_y - 1,
// origin_z + z,
// Block::NETHER_BRICKS.default_state,
// chunk,
// );
// }
// }
// }
// NetherFortressPieceType::SmallCorridor => {
// fill_rel(0, 0, 0, 4, 1, 4, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(0, 2, 0, 4, 5, 4, Block::AIR.default_state, chunk);
// let fence = &Block::NETHER_BRICK_FENCE.default_state;
// fill_rel(0, 2, 0, 0, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(4, 2, 0, 4, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
// fill_rel(0, 3, 1, 0, 4, 1, fence, chunk);
// fill_rel(0, 3, 3, 0, 4, 3, fence, chunk);
// fill_rel(4, 3, 1, 4, 4, 1, fence, chunk);
// fill_rel(4, 3, 3, 4, 4, 3, fence, chunk);
// fill_rel(0, 6, 0, 4, 6, 4, Block::NETHER_BRICKS.default_state, chunk);
// for i in 0..=4 {
// for j in 0..=4 {
// fill_downwards(
// origin_x + i,
// origin_y - 1,
// origin_z + j,
// Block::NETHER_BRICKS.default_state,
// chunk,
// );
// }
// }
// }
// }
// }
// fn get_structure_piece(&self) -> &super::StructurePiece {
// &self.piece
// }
// fn get_structure_piece_mut(&mut self) -> &mut super::StructurePiece {
// &mut self.piece
// }
// }

View File

@ -0,0 +1,155 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Straight exterior bridge segment (5 × 10 × 19).
#[derive(Clone)]
pub struct BridgePiece {
pub piece: NetherFortressPiece,
}
impl BridgePiece {
pub fn create(
collector: &mut StructurePiecesCollector,
_random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -1, -3, 0, 5, 10, 19, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece =
NetherFortressPiece::new(StructurePieceType::NetherFortressBridge, chain_length, bbox);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for BridgePiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last_piece: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last_piece,
1,
3,
false,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
p.fill_with_outline(chunk, &bb, false, 0, 3, 0, 4, 4, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 5, 0, 3, 7, 18, air, air);
p.fill_with_outline(chunk, &bb, false, 0, 5, 0, 0, 5, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 5, 0, 4, 5, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 4, 2, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 13, 4, 2, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 4, 1, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 0, 15, 4, 1, 18, nb, nb);
for i in 0..=4i32 {
for j in 0..=2i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
p.fill_downwards(chunk, nb, i, -1, 18 - j, &bb);
}
}
// North-South fence (connects along X axis on left/right walls)
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.north = true;
props.south = true;
let _fence_ns =
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE));
// fence_ns + east (left wall)
props.east = true;
props.north = true;
props.south = true;
let fence_east =
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE));
// fence_ns + west (right wall)
props.east = false;
props.west = true;
let fence_west =
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE));
p.fill_with_outline(chunk, &bb, false, 0, 1, 1, 0, 4, 1, fence_east, fence_east);
p.fill_with_outline(chunk, &bb, false, 0, 3, 4, 0, 4, 4, fence_east, fence_east);
p.fill_with_outline(
chunk, &bb, false, 0, 3, 14, 0, 4, 14, fence_east, fence_east,
);
p.fill_with_outline(
chunk, &bb, false, 0, 1, 17, 0, 4, 17, fence_east, fence_east,
);
p.fill_with_outline(chunk, &bb, false, 4, 1, 1, 4, 4, 1, fence_west, fence_west);
p.fill_with_outline(chunk, &bb, false, 4, 3, 4, 4, 4, 4, fence_west, fence_west);
p.fill_with_outline(
chunk, &bb, false, 4, 3, 14, 4, 4, 14, fence_west, fence_west,
);
p.fill_with_outline(
chunk, &bb, false, 4, 1, 17, 4, 4, 17, fence_west, fence_west,
);
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,189 @@
use pumpkin_data::Block;
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// 4-way exterior bridge crossing (19 × 10 × 19).
/// Also serves as the generation start piece.
#[derive(Clone)]
pub struct BridgeCrossingPiece {
pub piece: NetherFortressPiece,
}
impl BridgeCrossingPiece {
/// Create as the very first (start) piece at a fixed y=64.
pub fn new_start(random: &mut impl RandomImpl, x: i32, z: i32) -> Self {
let orientation = BlockDirection::get_random_horizontal_direction(random);
let bbox = BlockBox::create_box(x, 64, z, orientation.get_axis(), 19, 10, 19);
let mut piece =
NetherFortressPiece::new(StructurePieceType::NetherFortressBridgeCrossing, 0, bbox);
piece.piece.set_facing(Some(orientation));
Self { piece }
}
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -8, -3, 0, 19, 10, 19, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressBridgeCrossing,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
pub fn fill_openings(
&self,
start: &StructurePiece,
random: &mut impl RandomImpl,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last_piece: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last_piece,
8,
3,
false,
pieces_to_process,
);
self.piece.fill_nw_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last_piece,
3,
8,
false,
pieces_to_process,
);
self.piece.fill_se_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last_piece,
3,
8,
false,
pieces_to_process,
);
}
}
impl StructurePieceBase for BridgeCrossingPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
self.fill_openings(
start,
random,
bridge_pieces,
corridor_pieces,
collector,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
p.fill_with_outline(chunk, &bb, false, 7, 3, 0, 11, 4, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 3, 7, 18, 4, 11, nb, nb);
p.fill_with_outline(chunk, &bb, false, 8, 5, 0, 10, 7, 18, air, air);
p.fill_with_outline(chunk, &bb, false, 0, 5, 8, 18, 7, 10, air, air);
p.fill_with_outline(chunk, &bb, false, 7, 5, 0, 7, 5, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 7, 5, 11, 7, 5, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 11, 5, 0, 11, 5, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 11, 5, 11, 11, 5, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 5, 7, 7, 5, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 11, 5, 7, 18, 5, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 5, 11, 7, 5, 11, nb, nb);
p.fill_with_outline(chunk, &bb, false, 11, 5, 11, 18, 5, 11, nb, nb);
p.fill_with_outline(chunk, &bb, false, 7, 2, 0, 11, 2, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 7, 2, 13, 11, 2, 18, nb, nb);
p.fill_with_outline(chunk, &bb, false, 7, 0, 0, 11, 1, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 7, 0, 15, 11, 1, 18, nb, nb);
for i in 7..=11i32 {
for j in 0..=2i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
p.fill_downwards(chunk, nb, i, -1, 18 - j, &bb);
}
}
p.fill_with_outline(chunk, &bb, false, 0, 2, 7, 5, 2, 11, nb, nb);
p.fill_with_outline(chunk, &bb, false, 13, 2, 7, 18, 2, 11, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 0, 7, 3, 1, 11, nb, nb);
p.fill_with_outline(chunk, &bb, false, 15, 0, 7, 18, 1, 11, nb, nb);
for i in 0..=2i32 {
for j in 7..=11i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
p.fill_downwards(chunk, nb, i, -1, 18 - j, &bb); // Note: Java uses `18 - i` but iterates j; faithful copy
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,120 @@
use pumpkin_data::Block;
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, PieceWeight},
},
},
};
/// Dead-end / terminator piece for the exterior bridge (5 × 10 × 8).
/// Generated randomly on each side and used as a fallback when no valid piece
/// can be placed.
#[derive(Clone)]
pub struct BridgeEndPiece {
pub piece: NetherFortressPiece,
/// Seed used so the crumbling geometry is deterministic per world seed.
pub seed: u32,
}
impl BridgeEndPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -1, -3, 0, 5, 10, 8, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let seed = random.next_i32() as u32;
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressBridgeEnd,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece, seed }))
}
}
impl StructurePieceBase for BridgeEndPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
// BridgeEnd is a terminal piece – it never spawns children.
fn fill_openings_nether(
&self,
_start: &StructurePiece,
_random: &mut RandomGenerator,
_bridge_pieces: &mut Vec<PieceWeight>,
_corridor_pieces: &mut Vec<PieceWeight>,
_collector: &mut StructurePiecesCollector,
_pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
// Use a seeded local random so geometry is deterministic.
let mut rng = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed(self.seed as u64);
for i in 0..=4i32 {
for j in 3..=4i32 {
let k = rng.next_bounded_i32(8);
p.fill_with_outline(chunk, &bb, false, i, j, 0, i, j, k, nb, nb);
}
}
let k0 = rng.next_bounded_i32(8);
p.fill_with_outline(chunk, &bb, false, 0, 5, 0, 0, 5, k0, nb, nb);
let k1 = rng.next_bounded_i32(8);
p.fill_with_outline(chunk, &bb, false, 4, 5, 0, 4, 5, k1, nb, nb);
for i in 0..=4i32 {
let j = rng.next_bounded_i32(5);
p.fill_with_outline(chunk, &bb, false, i, 2, 0, i, 2, j, nb, nb);
}
for i in 0..=4i32 {
for j in 0..=1i32 {
let k = rng.next_bounded_i32(3);
p.fill_with_outline(chunk, &bb, false, i, j, 0, i, j, k, nb, nb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,157 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, PieceWeight},
},
},
};
/// Exterior platform with a Blaze mob-spawner (7 × 8 × 9).
#[derive(Clone)]
pub struct BridgePlatformPiece {
pub piece: NetherFortressPiece,
pub has_blaze_spawner: bool,
}
impl BridgePlatformPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
chain_length: u32,
orientation: BlockDirection,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -2, 0, 0, 7, 8, 9, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressBridgePlatform,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self {
piece,
has_blaze_spawner: false,
}))
}
}
impl StructurePieceBase for BridgePlatformPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
// Terminal piece – no children.
fn fill_openings_nether(
&self,
_start: &StructurePiece,
_random: &mut RandomGenerator,
_bridge_pieces: &mut Vec<PieceWeight>,
_corridor_pieces: &mut Vec<PieceWeight>,
_collector: &mut StructurePiecesCollector,
_pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 6, 7, 7, air, air);
p.fill_with_outline(chunk, &bb, false, 1, 0, 0, 5, 1, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 2, 1, 5, 2, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 3, 2, 5, 3, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 4, 3, 5, 4, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 2, 0, 1, 4, 2, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 2, 0, 5, 4, 2, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 5, 2, 1, 5, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 5, 2, 5, 5, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 5, 3, 0, 5, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 5, 3, 6, 5, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 5, 8, 5, 5, 8, nb, nb);
// Fence helpers
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
p.add_block(chunk, fence(true, false, false, false), 1, 6, 3, &bb);
p.add_block(chunk, fence(false, true, false, false), 5, 6, 3, &bb);
p.add_block(chunk, fence(false, true, true, false), 0, 6, 3, &bb);
p.add_block(chunk, fence(true, false, true, false), 6, 6, 3, &bb);
p.fill_with_outline(chunk, &bb, false, 0, 6, 4, 0, 6, 7, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 6, 6, 4, 6, 6, 7, f_ns, f_ns);
p.add_block(chunk, fence(false, true, false, true), 0, 6, 8, &bb);
p.add_block(chunk, fence(true, false, false, true), 6, 6, 8, &bb);
p.fill_with_outline(chunk, &bb, false, 1, 6, 8, 5, 6, 8, f_ew, f_ew);
p.add_block(chunk, fence(false, true, false, false), 1, 7, 8, &bb);
p.fill_with_outline(chunk, &bb, false, 2, 7, 8, 4, 7, 8, f_ew, f_ew);
p.add_block(chunk, fence(true, false, false, false), 5, 7, 8, &bb);
p.add_block(chunk, fence(false, true, false, false), 2, 8, 8, &bb);
p.add_block(chunk, f_ew, 3, 8, 8, &bb);
p.add_block(chunk, fence(true, false, false, false), 4, 8, 8, &bb);
// Blaze spawner (placed once)
if !self.has_blaze_spawner {
let spawner_pos = p.offset_pos(3, 5, 5);
if bb.contains_pos(&spawner_pos) {
self.has_blaze_spawner = true;
chunk.set_block_state(
spawner_pos.x,
spawner_pos.y,
spawner_pos.z,
Block::SPAWNER.default_state,
);
// TODO: set spawner entity type to Blaze via block entity data
}
}
for i in 0..=6i32 {
for j in 0..=6i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,163 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Small 3-way crossing for the exterior bridge network (7 × 9 × 7).
#[derive(Clone)]
pub struct BridgeSmallCrossingPiece {
pub piece: NetherFortressPiece,
}
impl BridgeSmallCrossingPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -2, 0, 0, 7, 9, 7, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressBridgeSmallCrossing,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for BridgeSmallCrossingPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
2,
0,
false,
pieces_to_process,
);
self.piece.fill_nw_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
2,
false,
pieces_to_process,
);
self.piece.fill_se_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
2,
false,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 6, 1, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 6, 7, 6, air, air);
// Walls
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 1, 6, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 6, 1, 6, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 2, 0, 6, 6, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 2, 6, 6, 6, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 0, 6, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 5, 0, 6, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 2, 0, 6, 6, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 2, 5, 6, 6, 6, nb, nb);
// Fence helpers
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
// Battlements: top of the four open sides
p.fill_with_outline(chunk, &bb, false, 2, 6, 0, 4, 6, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 0, 4, 5, 0, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 2, 6, 6, 4, 6, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 6, 4, 5, 6, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 0, 6, 2, 0, 6, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 5, 2, 0, 5, 4, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 6, 6, 2, 6, 6, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 5, 2, 6, 5, 4, f_ns, f_ns);
for i in 0..=6i32 {
for j in 0..=6i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,149 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Stairway that transitions from bridge level to interior level (7 × 11 × 7).
#[derive(Clone)]
pub struct BridgeStairsPiece {
pub piece: NetherFortressPiece,
}
impl BridgeStairsPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
chain_length: u32,
orientation: BlockDirection,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -2, 0, 0, 7, 11, 7, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressBridgeStairs,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for BridgeStairsPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
// SE opening connects into the corridor network (inside = true)
self.piece.fill_se_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
6,
2,
false,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 6, 1, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 6, 10, 6, air, air);
// Walls
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 1, 8, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 2, 0, 6, 8, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 1, 0, 8, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 2, 1, 6, 8, 6, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 2, 6, 5, 8, 6, nb, nb);
// Fence helpers
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
// Fence railings
p.fill_with_outline(chunk, &bb, false, 0, 3, 2, 0, 5, 4, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 6, 3, 2, 6, 5, 2, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 6, 3, 4, 6, 5, 4, f_ns, f_ns);
// Stair steps (bottom to top, going from z=5 → z=1)
p.add_block(chunk, nb, 5, 2, 5, &bb);
p.fill_with_outline(chunk, &bb, false, 4, 2, 5, 4, 3, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 3, 2, 5, 3, 4, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 2, 5, 2, 5, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 2, 5, 1, 6, 5, nb, nb);
// Upper floor platform
p.fill_with_outline(chunk, &bb, false, 1, 7, 1, 5, 7, 4, nb, nb);
// Open the SE exit gap
p.fill_with_outline(chunk, &bb, false, 6, 8, 2, 6, 8, 4, air, air);
// Battlement above forward opening
p.fill_with_outline(chunk, &bb, false, 2, 6, 0, 4, 8, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 0, 4, 5, 0, f_ew, f_ew);
for i in 0..=6i32 {
for j in 0..=6i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,171 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Interior corridor piece with a balcony overlook (9 × 7 × 9).
#[derive(Clone)]
pub struct CorridorBalconyPiece {
pub piece: NetherFortressPiece,
}
impl CorridorBalconyPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -3, 0, 0, 9, 7, 9, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorBalcony,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for CorridorBalconyPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
// In Java: `i` is 1 for EAST/SOUTH facing, 5 for WEST/NORTH.
let i = match self.piece.piece.facing {
Some(BlockDirection::West) | Some(BlockDirection::North) => 5,
_ => 1,
};
let nw_blocked = random.next_bounded_i32(8) > 0;
let se_blocked = random.next_bounded_i32(8) > 0;
self.piece.fill_nw_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
i,
nw_blocked,
pieces_to_process,
);
self.piece.fill_se_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
i,
se_blocked,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ns = fence(false, false, true, true);
let f_ew = fence(true, true, false, false);
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 8, 1, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 8, 5, 8, air, air);
p.fill_with_outline(chunk, &bb, false, 0, 6, 0, 8, 6, 5, nb, nb);
// Front wall (z=0) openings
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 2, 5, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 2, 0, 8, 5, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 3, 0, 1, 4, 0, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 7, 3, 0, 7, 4, 0, f_ew, f_ew);
// Back floor and openings
p.fill_with_outline(chunk, &bb, false, 0, 2, 4, 8, 2, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 1, 4, 2, 2, 4, air, air);
p.fill_with_outline(chunk, &bb, false, 6, 1, 4, 7, 2, 4, air, air);
// Back fence railing
p.fill_with_outline(chunk, &bb, false, 1, 3, 8, 7, 3, 8, f_ew, f_ew);
p.add_block(chunk, fence(false, true, false, true), 0, 3, 8, &bb);
p.add_block(chunk, fence(true, false, false, true), 8, 3, 8, &bb);
p.fill_with_outline(chunk, &bb, false, 0, 3, 6, 0, 3, 7, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 8, 3, 6, 8, 3, 7, f_ns, f_ns);
// Side wall pillars and fences
p.fill_with_outline(chunk, &bb, false, 0, 3, 4, 0, 5, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 8, 3, 4, 8, 5, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 3, 5, 2, 5, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 3, 5, 7, 5, 5, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 4, 5, 1, 5, 5, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 7, 4, 5, 7, 5, 5, f_ew, f_ew);
for i in 0..=5i32 {
for j in 0..=8i32 {
p.fill_downwards(chunk, nb, j, -1, i, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,137 @@
use pumpkin_data::Block;
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// 4-way interior corridor crossing (5 × 7 × 5).
#[derive(Clone)]
pub struct CorridorCrossingPiece {
pub piece: NetherFortressPiece,
}
impl CorridorCrossingPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -1, 0, 0, 5, 7, 5, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorCrossing,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for CorridorCrossingPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
1,
0,
true,
pieces_to_process,
);
self.piece.fill_nw_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
1,
true,
pieces_to_process,
);
self.piece.fill_se_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
1,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 4, 1, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 4, 5, 4, air, air);
// Four corner pillars
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 0, 5, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 2, 0, 4, 5, 0, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 4, 0, 5, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 2, 4, 4, 5, 4, nb, nb);
// Ceiling
p.fill_with_outline(chunk, &bb, false, 0, 6, 0, 4, 6, 4, nb, nb);
for i in 0..=4i32 {
for j in 0..=4i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,236 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Large entrance tower connecting the bridge network to the interior (13 × 14 × 13).
#[derive(Clone)]
pub struct CorridorExitPiece {
pub piece: NetherFortressPiece,
}
impl CorridorExitPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
_random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -5, -3, 0, 13, 14, 13, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorExit,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for CorridorExitPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
5,
3,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
// Main shell
p.fill_with_outline(chunk, &bb, false, 0, 3, 0, 12, 4, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 5, 0, 12, 13, 12, air, air);
// Outer walls
p.fill_with_outline(chunk, &bb, false, 0, 5, 0, 1, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 11, 5, 0, 12, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 11, 4, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 8, 5, 11, 10, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 9, 11, 7, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 0, 4, 12, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 8, 5, 0, 10, 12, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 9, 0, 7, 12, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 11, 2, 10, 12, 10, nb, nb);
// Gate arch fence
p.fill_with_outline(
chunk,
&bb,
false,
5,
8,
0,
7,
8,
0,
Block::NETHER_BRICK_FENCE.default_state,
Block::NETHER_BRICK_FENCE.default_state,
);
// Battlement fences and corner blocks along top
let mut i = 1i32;
while i <= 11 {
p.fill_with_outline(chunk, &bb, false, i, 10, 0, i, 11, 0, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, i, 10, 12, i, 11, 12, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 0, 10, i, 0, 11, i, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 12, 10, i, 12, 11, i, f_ns, f_ns);
p.add_block(chunk, nb, i, 13, 0, &bb);
p.add_block(chunk, nb, i, 13, 12, &bb);
p.add_block(chunk, nb, 0, 13, i, &bb);
p.add_block(chunk, nb, 12, 13, i, &bb);
if i != 11 {
p.add_block(chunk, f_ew, i + 1, 13, 0, &bb);
p.add_block(chunk, f_ew, i + 1, 13, 12, &bb);
p.add_block(chunk, f_ns, 0, 13, i + 1, &bb);
p.add_block(chunk, f_ns, 12, 13, i + 1, &bb);
}
i += 2;
}
// Corner fence pieces
p.add_block(chunk, fence(false, true, true, false), 0, 13, 0, &bb);
p.add_block(chunk, fence(false, true, false, true), 0, 13, 12, &bb);
p.add_block(chunk, fence(true, false, false, true), 12, 13, 12, &bb);
p.add_block(chunk, fence(true, false, true, false), 12, 13, 0, &bb);
// Interior buttress fences
let mut i = 3i32;
while i <= 9 {
p.fill_with_outline(
chunk,
&bb,
false,
1,
7,
i,
1,
8,
i,
fence(false, false, true, true), // + west connection
fence(false, false, true, true),
);
p.fill_with_outline(
chunk,
&bb,
false,
11,
7,
i,
11,
8,
i,
fence(false, false, true, true), // + east connection
fence(false, false, true, true),
);
i += 2;
}
// Exterior bridge supports
p.fill_with_outline(chunk, &bb, false, 4, 2, 0, 8, 2, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 4, 12, 2, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 0, 0, 8, 1, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 0, 9, 8, 1, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 0, 4, 3, 1, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 9, 0, 4, 12, 1, 8, nb, nb);
for i in 4..=8i32 {
for j in 0..=2i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
p.fill_downwards(chunk, nb, i, -1, 12 - j, &bb);
}
}
for i in 0..=2i32 {
for j in 4..=8i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
p.fill_downwards(chunk, nb, 12 - i, -1, j, &bb);
}
}
// Central lava well
p.fill_with_outline(chunk, &bb, false, 5, 5, 5, 7, 5, 7, nb, nb);
p.fill_with_outline(chunk, &bb, false, 6, 1, 6, 6, 4, 6, air, air);
p.add_block(chunk, nb, 6, 0, 6, &bb);
p.add_block(chunk, Block::LAVA.default_state, 6, 5, 6, &bb);
// Schedule lava fluid tick
// TODO
// let lava_pos = p.offset_pos(6, 5, 6);
// if bb.contains_pos(&lava_pos) {
// chunk.schedule_fluid_tick(&lava_pos, 0);
// }
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,154 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Interior corridor that turns left (NW), optionally containing a chest (5 × 7 × 5).
#[derive(Clone)]
pub struct CorridorLeftTurnPiece {
pub piece: NetherFortressPiece,
pub contains_chest: bool,
}
impl CorridorLeftTurnPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -1, 0, 0, 5, 7, 5, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let contains_chest = random.next_bounded_i32(3) == 0;
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorLeftTurn,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self {
piece,
contains_chest,
}))
}
}
impl StructurePieceBase for CorridorLeftTurnPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_nw_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
1,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 4, 1, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 4, 5, 4, air, air);
// Right wall (closed)
p.fill_with_outline(chunk, &bb, false, 4, 2, 0, 4, 5, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 3, 1, 4, 4, 1, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 4, 3, 3, 4, 4, 3, f_ns, f_ns);
// Front wall (closed)
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 0, 5, 0, nb, nb);
// Back wall
p.fill_with_outline(chunk, &bb, false, 0, 2, 4, 3, 5, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 3, 4, 1, 4, 4, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 3, 3, 4, 3, 4, 4, f_ew, f_ew);
// Optional chest
// TODO
// if self.contains_chest {
// let chest_pos = p.offset_pos(3, 2, 3);
// if bb.contains_pos(&chest_pos) {
// self.contains_chest = false;
// p.add_chest(chunk, &bb, random, 3, 2, 3, "nether_bridge");
// }
// }
// Ceiling
p.fill_with_outline(chunk, &bb, false, 0, 6, 0, 4, 6, 4, nb, nb);
for i in 0..=4i32 {
for j in 0..=4i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,281 @@
use pumpkin_data::{
Block,
block_properties::{
BlockProperties, HorizontalFacing, OakFenceLikeProperties, OakStairsLikeProperties,
},
};
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Large interior room with nether wart crops (13 × 14 × 13).
#[derive(Clone)]
pub struct CorridorNetherWartsRoomPiece {
pub piece: NetherFortressPiece,
}
impl CorridorNetherWartsRoomPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -5, -3, 0, 13, 14, 13, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorNetherWartsRoom,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for CorridorNetherWartsRoomPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
// Two forward exits (one at the back, one even further)
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
5,
3,
true,
pieces_to_process,
);
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
5,
11,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
let f_ns_west = fence(true, false, true, true);
let f_ns_east = fence(false, true, true, true);
// Shell (identical to CorridorExit outer shape)
p.fill_with_outline(chunk, &bb, false, 0, 3, 0, 12, 4, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 5, 0, 12, 13, 12, air, air);
p.fill_with_outline(chunk, &bb, false, 0, 5, 0, 1, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 11, 5, 0, 12, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 11, 4, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 8, 5, 11, 10, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 9, 11, 7, 12, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 0, 4, 12, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 8, 5, 0, 10, 12, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 5, 9, 0, 7, 12, 1, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 11, 2, 10, 12, 10, nb, nb);
// Battlements
let mut i = 1i32;
while i <= 11 {
p.fill_with_outline(chunk, &bb, false, i, 10, 0, i, 11, 0, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, i, 10, 12, i, 11, 12, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 0, 10, i, 0, 11, i, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 12, 10, i, 12, 11, i, f_ns, f_ns);
p.add_block(chunk, nb, i, 13, 0, &bb);
p.add_block(chunk, nb, i, 13, 12, &bb);
p.add_block(chunk, nb, 0, 13, i, &bb);
p.add_block(chunk, nb, 12, 13, i, &bb);
if i != 11 {
p.add_block(chunk, f_ew, i + 1, 13, 0, &bb);
p.add_block(chunk, f_ew, i + 1, 13, 12, &bb);
p.add_block(chunk, f_ns, 0, 13, i + 1, &bb);
p.add_block(chunk, f_ns, 12, 13, i + 1, &bb);
}
i += 2;
}
p.add_block(chunk, fence(false, true, true, false), 0, 13, 0, &bb);
p.add_block(chunk, fence(false, true, false, true), 0, 13, 12, &bb);
p.add_block(chunk, fence(true, false, false, true), 12, 13, 12, &bb);
p.add_block(chunk, fence(true, false, true, false), 12, 13, 0, &bb);
// Interior buttress fences
let mut i = 3i32;
while i <= 9 {
p.fill_with_outline(chunk, &bb, false, 1, 7, i, 1, 8, i, f_ns_west, f_ns_west);
p.fill_with_outline(chunk, &bb, false, 11, 7, i, 11, 8, i, f_ns_east, f_ns_east);
i += 2;
}
// Staircase going up (north-facing stairs, going from z=4 up to z=10)
let mut stair_props = OakStairsLikeProperties::default(&Block::NETHER_BRICK_STAIRS);
stair_props.facing = HorizontalFacing::North;
let stair_n =
pumpkin_data::BlockState::from_id(stair_props.to_state_id(&Block::NETHER_BRICK_STAIRS));
stair_props.facing = HorizontalFacing::East;
let stair_e =
pumpkin_data::BlockState::from_id(stair_props.to_state_id(&Block::NETHER_BRICK_STAIRS));
stair_props.facing = HorizontalFacing::West;
let stair_w =
pumpkin_data::BlockState::from_id(stair_props.to_state_id(&Block::NETHER_BRICK_STAIRS));
for j in 0..=6i32 {
let k = j + 4;
for l in 5..=7i32 {
p.add_block(chunk, stair_n, l, 5 + j, k, &bb);
}
if (5..=8).contains(&k) {
p.fill_with_outline(chunk, &bb, false, 5, 5, k, 7, j + 4, k, nb, nb);
} else if (9..=10).contains(&k) {
p.fill_with_outline(chunk, &bb, false, 5, 8, k, 7, j + 4, k, nb, nb);
}
if j >= 1 {
p.fill_with_outline(chunk, &bb, false, 5, 6 + j, k, 7, 9 + j, k, air, air);
}
}
for l in 5..=7i32 {
p.add_block(chunk, stair_n, l, 12, 11, &bb);
}
// Stair balcony fences
p.fill_with_outline(chunk, &bb, false, 5, 6, 7, 5, 7, 7, f_ns_east, f_ns_east);
p.fill_with_outline(chunk, &bb, false, 7, 6, 7, 7, 7, 7, f_ns_west, f_ns_west);
// Clear the arch above the top stair exit
p.fill_with_outline(chunk, &bb, false, 5, 13, 12, 7, 13, 12, air, air);
// Wart beds (raised platforms of soul sand with nether wart on top)
p.fill_with_outline(chunk, &bb, false, 2, 5, 2, 3, 5, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 9, 3, 5, 10, nb, nb);
p.fill_with_outline(chunk, &bb, false, 2, 5, 4, 2, 5, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 9, 5, 2, 10, 5, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 9, 5, 9, 10, 5, 10, nb, nb);
p.fill_with_outline(chunk, &bb, false, 10, 5, 4, 10, 5, 8, nb, nb);
// Stair edgings for the wart beds
p.add_block(chunk, stair_w, 4, 5, 2, &bb);
p.add_block(chunk, stair_w, 4, 5, 3, &bb);
p.add_block(chunk, stair_w, 4, 5, 9, &bb);
p.add_block(chunk, stair_w, 4, 5, 10, &bb);
p.add_block(chunk, stair_e, 8, 5, 2, &bb);
p.add_block(chunk, stair_e, 8, 5, 3, &bb);
p.add_block(chunk, stair_e, 8, 5, 9, &bb);
p.add_block(chunk, stair_e, 8, 5, 10, &bb);
let soul_sand = Block::SOUL_SAND.default_state;
let nether_wart = Block::NETHER_WART.default_state;
p.fill_with_outline(chunk, &bb, false, 3, 4, 4, 4, 4, 8, soul_sand, soul_sand);
p.fill_with_outline(chunk, &bb, false, 8, 4, 4, 9, 4, 8, soul_sand, soul_sand);
p.fill_with_outline(
chunk,
&bb,
false,
3,
5,
4,
4,
5,
8,
nether_wart,
nether_wart,
);
p.fill_with_outline(
chunk,
&bb,
false,
8,
5,
4,
9,
5,
8,
nether_wart,
nether_wart,
);
// Bridge support legs
p.fill_with_outline(chunk, &bb, false, 4, 2, 0, 8, 2, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 4, 12, 2, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 0, 0, 8, 1, 3, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 0, 9, 8, 1, 12, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 0, 4, 3, 1, 8, nb, nb);
p.fill_with_outline(chunk, &bb, false, 9, 0, 4, 12, 1, 8, nb, nb);
for l in 4..=8i32 {
for m in 0..=2i32 {
p.fill_downwards(chunk, nb, l, -1, m, &bb);
p.fill_downwards(chunk, nb, l, -1, 12 - m, &bb);
}
}
for l in 0..=2i32 {
for m in 4..=8i32 {
p.fill_downwards(chunk, nb, l, -1, m, &bb);
p.fill_downwards(chunk, nb, 12 - l, -1, m, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,154 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{
BlockDirection,
math::block_box::BlockBox,
random::{RandomGenerator, RandomImpl},
};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Interior corridor that turns right (SE), optionally containing a chest (5 × 7 × 5).
#[derive(Clone)]
pub struct CorridorRightTurnPiece {
pub piece: NetherFortressPiece,
pub contains_chest: bool,
}
impl CorridorRightTurnPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -1, 0, 0, 5, 7, 5, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let contains_chest = random.next_bounded_i32(3) == 0;
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorRightTurn,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self {
piece,
contains_chest,
}))
}
}
impl StructurePieceBase for CorridorRightTurnPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_se_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
0,
1,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
let fence = |west: bool, east: bool, north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.west = west;
props.east = east;
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ew = fence(true, true, false, false);
let f_ns = fence(false, false, true, true);
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 4, 1, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 4, 5, 4, air, air);
// Left wall (closed)
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 0, 5, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 3, 1, 0, 4, 1, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 0, 3, 3, 0, 4, 3, f_ns, f_ns);
// Front wall (closed)
p.fill_with_outline(chunk, &bb, false, 4, 2, 0, 4, 5, 0, nb, nb);
// Back wall (SE opening side)
p.fill_with_outline(chunk, &bb, false, 1, 2, 4, 4, 5, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 1, 3, 4, 1, 4, 4, f_ew, f_ew);
p.fill_with_outline(chunk, &bb, false, 3, 3, 4, 3, 4, 4, f_ew, f_ew);
// Optional chest
// TODO
// if self.contains_chest {
// let chest_pos = p.offset_pos(1, 2, 3);
// if bb.contains_pos(&chest_pos) {
// self.contains_chest = false;
// p.add_chest(chunk, &bb, random, 1, 2, 3, "nether_bridge");
// }
// }
// Ceiling
p.fill_with_outline(chunk, &bb, false, 0, 6, 0, 4, 6, 4, nb, nb);
for i in 0..=4i32 {
for j in 0..=4i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,152 @@
use pumpkin_data::{
Block,
block_properties::{
BlockProperties, HorizontalFacing, OakFenceLikeProperties, OakStairsLikeProperties,
},
};
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Descending interior corridor staircase (5 × 14 × 10).
#[derive(Clone)]
pub struct CorridorStairsPiece {
pub piece: NetherFortressPiece,
}
impl CorridorStairsPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
// Offset: -1 left/right, -7 height (deep stairs), 14 tall, 10 long
let bbox = BlockBox::rotated(x, y, z, -1, -7, 0, 5, 14, 10, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressCorridorStairs,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for CorridorStairsPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
1,
0,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
// Stair block facing south (going deeper into the structure)
let mut stair_props = OakStairsLikeProperties::default(&Block::NETHER_BRICK_STAIRS);
stair_props.facing = HorizontalFacing::South;
let stair =
pumpkin_data::BlockState::from_id(stair_props.to_state_id(&Block::NETHER_BRICK_STAIRS));
let fence = |north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ns = fence(true, true);
for i in 0..=9i32 {
let j = 1.max(7 - i);
let k = (j + 5).min(14 - i).min(13);
let l = i;
// Solid base
p.fill_with_outline(chunk, &bb, false, 0, 0, i, 4, j, i, nb, nb);
// Air interior
p.fill_with_outline(chunk, &bb, false, 1, j + 1, i, 3, k - 1, i, air, air);
// Stair treads (only for the descending section)
if i <= 6 {
p.add_block(chunk, stair, 1, j + 1, i, &bb);
p.add_block(chunk, stair, 2, j + 1, i, &bb);
p.add_block(chunk, stair, 3, j + 1, i, &bb);
}
// Ceiling row
p.fill_with_outline(chunk, &bb, false, 0, k, i, 4, k, i, nb, nb);
// Side walls
p.fill_with_outline(chunk, &bb, false, 0, j + 1, i, 0, k - 1, i, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, j + 1, i, 4, k - 1, i, nb, nb);
// Fence railings every 2 slices
if (i & 1) == 0 {
p.fill_with_outline(chunk, &bb, false, 0, j + 2, i, 0, j + 3, i, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 4, j + 2, i, 4, j + 3, i, f_ns, f_ns);
}
// Fill downwards beneath each slice
for m in 0..=4i32 {
p.fill_downwards(chunk, nb, m, -1, l, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -0,0 +1,624 @@
use std::sync::{Arc, Mutex};
use pumpkin_util::{
BlockDirection,
math::{block_box::BlockBox, position::BlockPos},
random::RandomImpl,
};
use serde::Deserialize;
use crate::generation::{
section_coords,
structure::structures::{
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePiecesCollector,
StructurePosition, nether_fortress::bridge_crossing::BridgeCrossingPiece,
},
};
pub mod bridge;
pub mod bridge_crossing;
pub mod bridge_end;
pub mod bridge_platform;
pub mod bridge_small_crossing;
pub mod bridge_stairs;
pub mod corridor_balcony;
pub mod corridor_crossing;
pub mod corridor_exit;
pub mod corridor_left_turn;
pub mod corridor_nether_warts_room;
pub mod corridor_right_turn;
pub mod corridor_stairs;
pub mod small_corridor;
/// Great reference: <https://minecraft.wiki/w/Nether_fortress>
#[derive(Deserialize)]
pub struct NetherFortressGenerator;
impl StructureGenerator for NetherFortressGenerator {
fn get_structure_position(
&self,
context: StructureGeneratorContext,
) -> Option<StructurePosition> {
let mut collector = StructurePiecesCollector::default();
let mut random = context.random;
let start_x = section_coords::section_to_block(context.chunk_x) + 2;
let start_z = section_coords::section_to_block(context.chunk_z) + 2;
let start_piece = BridgeCrossingPiece::new_start(&mut random, start_x, start_z);
let start_box: Box<dyn crate::generation::structure::structures::StructurePieceBase> =
Box::new(start_piece.clone());
let mut pieces_to_process: Vec<
Box<dyn crate::generation::structure::structures::StructurePieceBase>,
> = Vec::new();
let mut bridge_pieces = get_bridge_piece_weights();
let mut corridor_pieces = get_corridor_piece_weights();
collector.add_piece(start_box);
start_piece.fill_openings(
&start_piece.piece.piece,
&mut random,
&mut bridge_pieces,
&mut corridor_pieces,
&mut collector,
&mut pieces_to_process,
);
while !pieces_to_process.is_empty() {
let idx = random.next_bounded_i32(pieces_to_process.len() as i32) as usize;
let piece = pieces_to_process.remove(idx);
piece.fill_openings_nether(
&start_piece.piece.piece,
&mut random,
&mut bridge_pieces,
&mut corridor_pieces,
&mut collector,
&mut pieces_to_process,
);
}
Some(StructurePosition {
start_pos: BlockPos::new(
section_coords::section_to_block(context.chunk_x),
64,
section_coords::section_to_block(context.chunk_z),
),
collector: Arc::new(Mutex::new(collector)),
})
}
}
/// Weights for the bridge (exterior) pieces.
#[derive(Clone, Debug)]
pub struct PieceWeight {
pub piece_type: NetherFortressPieceType,
pub weight: i32,
pub limit: u32,
pub generated_count: u32,
pub repeatable: bool,
}
impl PieceWeight {
const fn new(
piece_type: NetherFortressPieceType,
weight: i32,
limit: u32,
repeatable: bool,
) -> Self {
Self {
piece_type,
weight,
limit,
generated_count: 0,
repeatable,
}
}
pub const fn can_generate(&self) -> bool {
self.limit == 0 || self.generated_count < self.limit
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum NetherFortressPieceType {
// Bridge pieces
Bridge,
BridgeCrossing,
BridgeSmallCrossing,
BridgeStairs,
BridgePlatform,
CorridorExit,
// Corridor pieces
SmallCorridor,
CorridorCrossing,
CorridorRightTurn,
CorridorLeftTurn,
CorridorStairs,
CorridorBalcony,
CorridorNetherWartsRoom,
}
pub fn get_bridge_piece_weights() -> Vec<PieceWeight> {
vec![
PieceWeight::new(NetherFortressPieceType::Bridge, 30, 0, true),
PieceWeight::new(NetherFortressPieceType::BridgeCrossing, 10, 4, false),
PieceWeight::new(NetherFortressPieceType::BridgeSmallCrossing, 10, 4, false),
PieceWeight::new(NetherFortressPieceType::BridgeStairs, 10, 3, false),
PieceWeight::new(NetherFortressPieceType::BridgePlatform, 5, 2, false),
PieceWeight::new(NetherFortressPieceType::CorridorExit, 5, 1, false),
]
}
pub fn get_corridor_piece_weights() -> Vec<PieceWeight> {
vec![
PieceWeight::new(NetherFortressPieceType::SmallCorridor, 25, 0, true),
PieceWeight::new(NetherFortressPieceType::CorridorCrossing, 15, 5, false),
PieceWeight::new(NetherFortressPieceType::CorridorRightTurn, 5, 10, false),
PieceWeight::new(NetherFortressPieceType::CorridorLeftTurn, 5, 10, false),
PieceWeight::new(NetherFortressPieceType::CorridorStairs, 10, 3, true),
PieceWeight::new(NetherFortressPieceType::CorridorBalcony, 7, 2, false),
PieceWeight::new(
NetherFortressPieceType::CorridorNetherWartsRoom,
5,
2,
false,
),
]
}
/// The shared base state for every nether fortress piece.
#[derive(Clone)]
pub struct NetherFortressPiece {
pub piece: StructurePiece,
}
impl NetherFortressPiece {
#[must_use]
pub const fn new(
r#type: crate::generation::structure::piece::StructurePieceType,
chain_length: u32,
bbox: BlockBox,
) -> Self {
Self {
piece: StructurePiece::new(r#type, bbox, chain_length),
}
}
pub const fn is_in_bounds(bb: &BlockBox) -> bool {
bb.min.y > 10
}
/// Attempt to pick a random piece from `weights` up to 5 times.
/// Falls back to `BridgeEnd` if nothing succeeds.
#[allow(clippy::too_many_arguments)]
pub fn pick_piece(
weights: &mut Vec<PieceWeight>,
last_piece: &mut Option<NetherFortressPieceType>,
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn crate::generation::structure::structures::StructurePieceBase>> {
let total_weight = Self::check_remaining_pieces(weights);
if total_weight < 0 {
return bridge_end::BridgeEndPiece::create(
collector,
random,
x,
y,
z,
orientation,
chain_length,
);
}
let mut attempt = 0;
while attempt < 5 {
attempt += 1;
let mut k = random.next_bounded_i32(total_weight);
for i in 0..weights.len() {
k -= weights[i].weight;
if k < 0 {
let same_as_last =
Some(weights[i].piece_type) == *last_piece && !weights[i].repeatable;
if !weights[i].can_generate() || same_as_last {
break;
}
if let Some(p) = Self::create_piece(
weights[i].piece_type,
collector,
random,
x,
y,
z,
orientation,
chain_length,
) {
weights[i].generated_count += 1;
*last_piece = Some(weights[i].piece_type);
if !weights[i].can_generate() {
weights.remove(i);
}
return Some(p);
}
break;
}
}
}
bridge_end::BridgeEndPiece::create(collector, random, x, y, z, orientation, chain_length)
}
fn check_remaining_pieces(weights: &[PieceWeight]) -> i32 {
let mut has_limited = false;
let mut total = 0i32;
for w in weights {
if w.limit > 0 && w.generated_count < w.limit {
has_limited = true;
}
total += w.weight;
}
if has_limited { total } else { -1 }
}
#[allow(clippy::too_many_arguments)]
pub fn create_piece(
piece_type: NetherFortressPieceType,
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn crate::generation::structure::structures::StructurePieceBase>> {
match piece_type {
NetherFortressPieceType::Bridge => {
bridge::BridgePiece::create(collector, random, x, y, z, orientation, chain_length)
}
NetherFortressPieceType::BridgeCrossing => {
bridge_crossing::BridgeCrossingPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::BridgeSmallCrossing => {
bridge_small_crossing::BridgeSmallCrossingPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::BridgeStairs => bridge_stairs::BridgeStairsPiece::create(
collector,
x,
y,
z,
chain_length,
orientation,
),
NetherFortressPieceType::BridgePlatform => {
bridge_platform::BridgePlatformPiece::create(
collector,
x,
y,
z,
chain_length,
orientation,
)
}
NetherFortressPieceType::CorridorExit => corridor_exit::CorridorExitPiece::create(
collector,
random,
x,
y,
z,
orientation,
chain_length,
),
NetherFortressPieceType::SmallCorridor => small_corridor::SmallCorridorPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
),
NetherFortressPieceType::CorridorCrossing => {
corridor_crossing::CorridorCrossingPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::CorridorRightTurn => {
corridor_right_turn::CorridorRightTurnPiece::create(
collector,
random,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::CorridorLeftTurn => {
corridor_left_turn::CorridorLeftTurnPiece::create(
collector,
random,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::CorridorStairs => {
corridor_stairs::CorridorStairsPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::CorridorBalcony => {
corridor_balcony::CorridorBalconyPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
)
}
NetherFortressPieceType::CorridorNetherWartsRoom => {
corridor_nether_warts_room::CorridorNetherWartsRoomPiece::create(
collector,
x,
y,
z,
orientation,
chain_length,
)
}
}
}
// ---- opening helpers --------------------------------------------------
#[allow(clippy::too_many_arguments)]
pub fn fill_forward_opening(
&self,
start: &StructurePiece,
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
last_piece: &mut Option<NetherFortressPieceType>,
left_right_offset: i32,
height_offset: i32,
inside: bool,
pieces_to_process: &mut Vec<
Box<dyn crate::generation::structure::structures::StructurePieceBase>,
>,
) {
if let Some(facing) = self.piece.facing {
let bb = self.piece.bounding_box;
let (nx, ny, nz) = match facing {
BlockDirection::North => (
bb.min.x + left_right_offset,
bb.min.y + height_offset,
bb.min.z - 1,
),
BlockDirection::South => (
bb.min.x + left_right_offset,
bb.min.y + height_offset,
bb.max.z + 1,
),
BlockDirection::West => (
bb.min.x - 1,
bb.min.y + height_offset,
bb.min.z + left_right_offset,
),
BlockDirection::East => (
bb.max.x + 1,
bb.min.y + height_offset,
bb.min.z + left_right_offset,
),
_ => return,
};
self.piece_generator(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
last_piece,
nx,
ny,
nz,
facing,
inside,
pieces_to_process,
);
}
}
#[allow(clippy::too_many_arguments)]
pub fn fill_nw_opening(
&self,
start: &StructurePiece,
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
last_piece: &mut Option<NetherFortressPieceType>,
height_offset: i32,
left_right_offset: i32,
inside: bool,
pieces_to_process: &mut Vec<
Box<dyn crate::generation::structure::structures::StructurePieceBase>,
>,
) {
if let Some(facing) = self.piece.facing {
let bb = self.piece.bounding_box;
let (nx, ny, nz, next_facing) = match facing {
BlockDirection::North | BlockDirection::South => (
bb.min.x - 1,
bb.min.y + height_offset,
bb.min.z + left_right_offset,
BlockDirection::West,
),
BlockDirection::West | BlockDirection::East => (
bb.min.x + left_right_offset,
bb.min.y + height_offset,
bb.min.z - 1,
BlockDirection::North,
),
_ => return,
};
self.piece_generator(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
last_piece,
nx,
ny,
nz,
next_facing,
inside,
pieces_to_process,
);
}
}
#[allow(clippy::too_many_arguments)]
pub fn fill_se_opening(
&self,
start: &StructurePiece,
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
last_piece: &mut Option<NetherFortressPieceType>,
height_offset: i32,
left_right_offset: i32,
inside: bool,
pieces_to_process: &mut Vec<
Box<dyn crate::generation::structure::structures::StructurePieceBase>,
>,
) {
if let Some(facing) = self.piece.facing {
let bb = self.piece.bounding_box;
let (nx, ny, nz, next_facing) = match facing {
BlockDirection::North | BlockDirection::South => (
bb.max.x + 1,
bb.min.y + height_offset,
bb.min.z + left_right_offset,
BlockDirection::East,
),
BlockDirection::West | BlockDirection::East => (
bb.min.x + left_right_offset,
bb.min.y + height_offset,
bb.max.z + 1,
BlockDirection::South,
),
_ => return,
};
self.piece_generator(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
last_piece,
nx,
ny,
nz,
next_facing,
inside,
pieces_to_process,
);
}
}
#[allow(clippy::too_many_arguments)]
fn piece_generator(
&self,
start: &StructurePiece,
collector: &mut StructurePiecesCollector,
random: &mut impl RandomImpl,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
last_piece: &mut Option<NetherFortressPieceType>,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
inside: bool,
pieces_to_process: &mut Vec<
Box<dyn crate::generation::structure::structures::StructurePieceBase>,
>,
) {
let start_box = start.bounding_box;
if (x - start_box.min.x).abs() > 112 || (z - start_box.min.z).abs() > 112 {
if let Some(p) = bridge_end::BridgeEndPiece::create(
collector,
random,
x,
y,
z,
orientation,
self.piece.chain_length,
) {
collector.add_piece(p)
}
return;
}
let weights = if inside {
corridor_pieces
} else {
bridge_pieces
};
if let Some(p) = Self::pick_piece(
weights,
last_piece,
collector,
random,
x,
y,
z,
orientation,
self.piece.chain_length + 1,
) {
collector.add_piece(p.clone_box());
pieces_to_process.push(p);
}
}
}

View File

@ -0,0 +1,129 @@
use pumpkin_data::{
Block,
block_properties::{BlockProperties, OakFenceLikeProperties},
};
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
use crate::{
ProtoChunk,
generation::structure::{
piece::StructurePieceType,
structures::{
StructurePiece, StructurePieceBase, StructurePiecesCollector,
nether_fortress::{NetherFortressPiece, NetherFortressPieceType, PieceWeight},
},
},
};
/// Straight interior corridor segment (5 × 7 × 5).
/// This is also the fallback piece returned by the generation logic.
#[derive(Clone)]
pub struct SmallCorridorPiece {
pub piece: NetherFortressPiece,
}
impl SmallCorridorPiece {
pub fn create(
collector: &mut StructurePiecesCollector,
x: i32,
y: i32,
z: i32,
orientation: BlockDirection,
chain_length: u32,
) -> Option<Box<dyn StructurePieceBase>> {
let bbox = BlockBox::rotated(x, y, z, -1, 0, 0, 5, 7, 5, &orientation);
if !NetherFortressPiece::is_in_bounds(&bbox) || collector.get_intersecting(&bbox).is_some()
{
return None;
}
let mut piece = NetherFortressPiece::new(
StructurePieceType::NetherFortressSmallCorridor,
chain_length,
bbox,
);
piece.piece.set_facing(Some(orientation));
Some(Box::new(Self { piece }))
}
}
impl StructurePieceBase for SmallCorridorPiece {
fn get_structure_piece(&self) -> &StructurePiece {
&self.piece.piece
}
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
&mut self.piece.piece
}
fn fill_openings_nether(
&self,
start: &StructurePiece,
random: &mut RandomGenerator,
bridge_pieces: &mut Vec<PieceWeight>,
corridor_pieces: &mut Vec<PieceWeight>,
collector: &mut StructurePiecesCollector,
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
) {
let mut last: Option<NetherFortressPieceType> = None;
self.piece.fill_forward_opening(
start,
collector,
random,
bridge_pieces,
corridor_pieces,
&mut last,
1,
0,
true,
pieces_to_process,
);
}
fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let bb = *chunk_box;
let p = &self.piece.piece;
let nb = Block::NETHER_BRICKS.default_state;
let air = Block::AIR.default_state;
let fence = |north: bool, south: bool| {
let mut props = OakFenceLikeProperties::default(&Block::NETHER_BRICK_FENCE);
props.north = north;
props.south = south;
pumpkin_data::BlockState::from_id(props.to_state_id(&Block::NETHER_BRICK_FENCE))
};
let f_ns = fence(true, true);
p.fill_with_outline(chunk, &bb, false, 0, 0, 0, 4, 1, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 4, 5, 4, air, air);
// Side walls
p.fill_with_outline(chunk, &bb, false, 0, 2, 0, 0, 5, 4, nb, nb);
p.fill_with_outline(chunk, &bb, false, 4, 2, 0, 4, 5, 4, nb, nb);
// Fence railings on side walls
p.fill_with_outline(chunk, &bb, false, 0, 3, 1, 0, 4, 1, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 0, 3, 3, 0, 4, 3, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 4, 3, 1, 4, 4, 1, f_ns, f_ns);
p.fill_with_outline(chunk, &bb, false, 4, 3, 3, 4, 4, 3, f_ns, f_ns);
// Ceiling
p.fill_with_outline(chunk, &bb, false, 0, 6, 0, 4, 6, 4, nb, nb);
for i in 0..=4i32 {
for j in 0..=4i32 {
p.fill_downwards(chunk, nb, i, -1, j, &bb);
}
}
}
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
Box::new(self.clone())
}
}

View File

@ -2,7 +2,7 @@ use std::sync::Arc;
use pumpkin_data::{Block, BlockDirection, BlockState}; use pumpkin_data::{Block, BlockDirection, BlockState};
use pumpkin_util::{ use pumpkin_util::{
math::{position::BlockPos, vector3::Vector3}, math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
random::{RandomDeriverImpl, RandomGenerator, RandomImpl}, random::{RandomDeriverImpl, RandomGenerator, RandomImpl},
}; };
use tracing::debug; use tracing::debug;
@ -166,7 +166,13 @@ impl StructurePieceBase for NetherFossilPiece {
Box::new(self.clone()) Box::new(self.clone())
} }
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
seed: i64,
_chunk_box: &BlockBox,
) {
// Vanilla column scan: find air above soul sand or solid block // Vanilla column scan: find air above soul sand or solid block
let Some(placement_y) = self.find_placement_y(chunk) else { let Some(placement_y) = self.find_placement_y(chunk) else {
debug!( debug!(

View File

@ -91,9 +91,15 @@ impl StructurePieceBase for ChestCorridorPiece {
); );
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;

View File

@ -119,9 +119,15 @@ impl StructurePieceBase for CorridorPiece {
} }
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;
let air = Block::AIR.default_state; let air = Block::AIR.default_state;
@ -246,8 +252,14 @@ impl StructurePieceBase for SmallCorridorPiece {
&mut self.piece.piece &mut self.piece.piece
} }
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) { fn place(
let box_limit = self.piece.piece.bounding_box; &mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let box_limit = *chunk_box;
let p = &self.piece.piece; let p = &self.piece.piece;
let stone = Block::STONE_BRICKS.default_state; let stone = Block::STONE_BRICKS.default_state;

View File

@ -159,9 +159,15 @@ impl StructurePieceBase for FiveWayCrossingPiece {
} }
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;
let air = Block::AIR.default_state; let air = Block::AIR.default_state;

View File

@ -101,9 +101,15 @@ impl StructurePieceBase for LeftTurnPiece {
} }
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;
let air = Block::AIR.default_state; let air = Block::AIR.default_state;

View File

@ -71,9 +71,15 @@ impl StructurePieceBase for LibraryPiece {
&mut self.piece.piece &mut self.piece.piece
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;

View File

@ -87,9 +87,15 @@ impl StructurePieceBase for PortalRoomPiece {
*has_portal_room = true; *has_portal_room = true;
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;

View File

@ -92,9 +92,15 @@ impl StructurePieceBase for PrisonHallPiece {
); );
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;
let air = Block::AIR.default_state; let air = Block::AIR.default_state;

View File

@ -102,9 +102,15 @@ impl StructurePieceBase for RightTurnPiece {
} }
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;

View File

@ -46,7 +46,7 @@ impl Clone for SpiralStaircasePiece {
impl SpiralStaircasePiece { impl SpiralStaircasePiece {
/// Matches Start(Random random, int i, int j) /// Matches Start(Random random, int i, int j)
pub fn new_start(random: &mut impl RandomImpl, x: i32, z: i32) -> Self { pub fn new_start(random: &mut impl RandomImpl, x: i32, z: i32) -> Self {
let orientation = StructurePiece::get_random_horizontal_direction(random); let orientation = BlockDirection::get_random_horizontal_direction(random);
let bounding_box = BlockBox::create_box(x, 64, z, orientation.get_axis(), 5, 11, 5); let bounding_box = BlockBox::create_box(x, 64, z, orientation.get_axis(), 5, 11, 5);
let mut piece = StrongholdPiece::new(StructurePieceType::StrongholdStart, 0, bounding_box); let mut piece = StrongholdPiece::new(StructurePieceType::StrongholdStart, 0, bounding_box);
piece.piece.set_facing(Some(orientation)); piece.piece.set_facing(Some(orientation));
@ -138,9 +138,15 @@ impl StructurePieceBase for SpiralStaircasePiece {
); );
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;

View File

@ -123,9 +123,15 @@ impl StructurePieceBase for SquareRoomPiece {
); );
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;
let air = Block::AIR.default_state; let air = Block::AIR.default_state;

View File

@ -90,9 +90,15 @@ impl StructurePieceBase for StairsPiece {
); );
} }
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
let randomizer = StoneBrickRandomizer; let randomizer = StoneBrickRandomizer;
let box_limit = self.piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.piece; let p = &self.piece;
let inner = &p.piece; let inner = &p.piece;

View File

@ -1,7 +1,11 @@
use std::sync::Arc; use std::sync::Arc;
use pumpkin_data::Block; use pumpkin_data::Block;
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator}; use pumpkin_util::{
BlockDirection,
math::{block_box::BlockBox, position::BlockPos},
random::RandomGenerator,
};
use serde::Deserialize; use serde::Deserialize;
use crate::{ use crate::{
@ -12,7 +16,7 @@ use crate::{
piece::StructurePieceType, piece::StructurePieceType,
shiftable_piece::ShiftableStructurePiece, shiftable_piece::ShiftableStructurePiece,
structures::{ structures::{
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase, StructureGenerator, StructureGeneratorContext, StructurePieceBase,
StructurePiecesCollector, StructurePosition, StructurePiecesCollector, StructurePosition,
}, },
}, },
@ -40,7 +44,7 @@ impl StructureGenerator for SwampHutGenerator {
7, 7,
7, 7,
9, 9,
StructurePiece::get_random_horizontal_direction(&mut context.random).get_axis(), BlockDirection::get_random_horizontal_direction(&mut context.random).get_axis(),
), ),
})); }));
@ -61,7 +65,13 @@ impl StructurePieceBase for SwampHutPiece {
Box::new(self.clone()) Box::new(self.clone())
} }
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
chunk_box: &BlockBox,
) {
if !self if !self
.shiftable_structure_piece .shiftable_structure_piece
.adjust_to_average_height(chunk) .adjust_to_average_height(chunk)
@ -69,7 +79,7 @@ impl StructurePieceBase for SwampHutPiece {
return; return;
} }
let box_limit = self.shiftable_structure_piece.piece.bounding_box; let box_limit = *chunk_box;
let p = &self.shiftable_structure_piece.piece; let p = &self.shiftable_structure_piece.piece;
let spruce_planks = Block::SPRUCE_PLANKS.default_state; let spruce_planks = Block::SPRUCE_PLANKS.default_state;

View File

@ -163,7 +163,13 @@ impl StructurePieceBase for TemplatePiece {
Box::new(self.clone()) Box::new(self.clone())
} }
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) { fn place(
&mut self,
chunk: &mut ProtoChunk,
_random: &mut RandomGenerator,
_seed: i64,
_chunk_box: &BlockBox,
) {
self.place_blocks(chunk); self.place_blocks(chunk);
// TODO: Spawn entities from template // TODO: Spawn entities from template