mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-31 08:22:33 +00:00
feat: Spawners now work for generated Structures
This commit is contained in:
@@ -36,7 +36,7 @@ impl MobSpawnerBlockEntity {
|
||||
pub const DEFAULT_SPAWN_RANGE: i32 = 4;
|
||||
|
||||
#[must_use]
|
||||
pub const fn new(position: BlockPos) -> Self {
|
||||
pub const fn new(position: BlockPos, entity_type: Option<&'static EntityType>) -> Self {
|
||||
Self {
|
||||
position,
|
||||
delay: AtomicI32::new(Self::DEFAULT_DELAY),
|
||||
@@ -44,7 +44,26 @@ impl MobSpawnerBlockEntity {
|
||||
min_delay: Self::DEFAULT_MIN_SPAWN_DELAY,
|
||||
spawn_count: Self::DEFAULT_SPAWN_COUNT,
|
||||
spawn_range: Self::DEFAULT_SPAWN_RANGE,
|
||||
entity_type: AtomicCell::new(None),
|
||||
entity_type: AtomicCell::new(entity_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_nbt<'a>(&'a self, nbt: &'a mut NbtCompound) {
|
||||
// TODO: this is ugly af
|
||||
nbt.put_string("id", self.resource_location().to_string());
|
||||
let position = self.get_position();
|
||||
nbt.put_int("x", position.0.x);
|
||||
nbt.put_int("y", position.0.y);
|
||||
nbt.put_int("z", position.0.z);
|
||||
if let Some(entity_type) = self.entity_type.load() {
|
||||
let mut spawn_entry = NbtCompound::new();
|
||||
|
||||
let mut entity_nbt = NbtCompound::new();
|
||||
entity_nbt.put_string("id", format!("minecraft:{}", entity_type.resource_name));
|
||||
|
||||
spawn_entry.put_compound("entity", entity_nbt);
|
||||
|
||||
nbt.put_compound("SpawnData", spawn_entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -151,7 +170,15 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
||||
let spawn_range = nbt
|
||||
.get_int("SpawnRange")
|
||||
.unwrap_or(Self::DEFAULT_SPAWN_RANGE);
|
||||
let _spawn_entry = nbt.get_compound("SpawnData");
|
||||
|
||||
let entity_type = nbt
|
||||
.get_compound("SpawnData")
|
||||
.and_then(|data| data.get_compound("entity"))
|
||||
.and_then(|entity| entity.get_string("id"))
|
||||
.and_then(|id| {
|
||||
let name = id.strip_prefix("minecraft:").unwrap_or(id);
|
||||
EntityType::from_name(name)
|
||||
});
|
||||
|
||||
Self {
|
||||
position,
|
||||
@@ -160,7 +187,7 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
||||
min_delay,
|
||||
spawn_count,
|
||||
spawn_range,
|
||||
entity_type: AtomicCell::new(None), // TODO
|
||||
entity_type: AtomicCell::new(entity_type),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,12 +196,7 @@ impl BlockEntity for MobSpawnerBlockEntity {
|
||||
nbt: &'a mut NbtCompound,
|
||||
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
|
||||
Box::pin(async move {
|
||||
if let Some(entity_type) = self.entity_type.load() {
|
||||
let mut entity_nbt = NbtCompound::new();
|
||||
entity_nbt.put_string("id", format!("minecraft:{}", entity_type.resource_name));
|
||||
|
||||
nbt.put_compound("entity", entity_nbt);
|
||||
}
|
||||
self.write_nbt(nbt);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use pumpkin_data::chunk_gen_settings::GenerationSettings;
|
||||
use pumpkin_data::dimension::Dimension;
|
||||
use pumpkin_data::fluid::{Fluid, FluidState};
|
||||
use pumpkin_data::{Block, BlockState};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::HeightMap;
|
||||
use pumpkin_util::math::position::BlockPos;
|
||||
use pumpkin_util::math::vector3::Vector3;
|
||||
@@ -205,6 +206,29 @@ impl GenerationCache for Cache {
|
||||
}
|
||||
}
|
||||
|
||||
fn add_block_entity(&mut self, pos: &Vector3<i32>, nbt: NbtCompound) {
|
||||
let dx = (pos.x >> 4) - self.x;
|
||||
let dz = (pos.z >> 4) - self.z;
|
||||
// debug_assert!(dx < self.size && dz < self.size);
|
||||
// debug_assert!(dx >= 0 && dz >= 0);
|
||||
if !(dx < self.size && dz < self.size && dx >= 0 && dz >= 0) {
|
||||
// breakpoint here
|
||||
debug!(
|
||||
"illegal set_block_state {pos:?} cache pos ({}, {}) size {}",
|
||||
self.x, self.z, self.size
|
||||
);
|
||||
return;
|
||||
}
|
||||
match &mut self.chunks[(dx * self.size + dz) as usize] {
|
||||
Chunk::Level(_data) => {
|
||||
todo!()
|
||||
}
|
||||
Chunk::Proto(data) => {
|
||||
data.add_block_entity(nbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_top_y(&self, heightmap: &HeightMap, x: i32, z: i32) -> i32 {
|
||||
match heightmap {
|
||||
HeightMap::WorldSurfaceWg => self.top_block_height_exclusive(x, z),
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
use pumpkin_data::{Block, BlockDirection};
|
||||
use pumpkin_data::{Block, BlockDirection, entity::EntityType};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::{
|
||||
math::{position::BlockPos, vector3::Vector3},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::generation::proto_chunk::GenerationCache;
|
||||
use crate::{
|
||||
block::entities::mob_spawner::MobSpawnerBlockEntity, generation::proto_chunk::GenerationCache,
|
||||
};
|
||||
|
||||
/// The three mob types that can appear in a dungeon spawner.
|
||||
///
|
||||
@@ -143,8 +146,12 @@ impl DungeonFeature {
|
||||
}
|
||||
|
||||
// TODO: set spawner entity type
|
||||
let _ = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize];
|
||||
let mob = DUNGEON_MOBS[random.next_bounded_i32(DUNGEON_MOBS.len() as i32) as usize];
|
||||
chunk.set_block_state(&pos.0, Block::SPAWNER.default_state);
|
||||
let spawner_block_entity = MobSpawnerBlockEntity::new(pos, EntityType::from_name(mob));
|
||||
let mut entity_nbt = NbtCompound::new();
|
||||
spawner_block_entity.write_nbt(&mut entity_nbt);
|
||||
chunk.add_block_entity(&pos.0, entity_nbt);
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
||||
fn get_block_state(&self, pos: &Vector3<i32>) -> RawBlockState;
|
||||
fn get_fluid_and_fluid_state(&self, position: &Vector3<i32>) -> (Fluid, FluidState);
|
||||
fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState);
|
||||
fn add_block_entity(&mut self, pos: &Vector3<i32>, nbt: NbtCompound);
|
||||
fn top_motion_blocking_block_height_exclusive(&self, x: i32, z: i32) -> i32;
|
||||
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, x: i32, z: i32) -> i32;
|
||||
fn get_top_y(&self, heightmap: &HeightMap, x: i32, z: i32) -> i32;
|
||||
@@ -350,7 +351,7 @@ impl ProtoChunk {
|
||||
/// Adds a pending block entity to be created when the chunk is finalized.
|
||||
///
|
||||
/// The NBT compound should include the block entity's position (x, y, z) and id fields.
|
||||
pub fn add_pending_block_entity(&mut self, nbt: NbtCompound) {
|
||||
pub fn add_block_entity(&mut self, nbt: NbtCompound) {
|
||||
self.pending_block_entities.push(nbt);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
use pumpkin_data::{
|
||||
Block,
|
||||
block_properties::{BlockProperties, OakFenceLikeProperties},
|
||||
entity::EntityType,
|
||||
};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::{
|
||||
BlockDirection,
|
||||
math::{block_box::BlockBox, position::BlockPos},
|
||||
random::RandomGenerator,
|
||||
};
|
||||
use pumpkin_util::{BlockDirection, math::block_box::BlockBox, random::RandomGenerator};
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
block::entities::mob_spawner::MobSpawnerBlockEntity,
|
||||
generation::structure::{
|
||||
piece::StructurePieceType,
|
||||
structures::{
|
||||
@@ -140,7 +147,11 @@ impl StructurePieceBase for BridgePlatformPiece {
|
||||
spawner_pos.z,
|
||||
Block::SPAWNER.default_state,
|
||||
);
|
||||
// TODO: set spawner entity type to Blaze via block entity data
|
||||
let spawner_block_entity =
|
||||
MobSpawnerBlockEntity::new(BlockPos(spawner_pos), Some(&EntityType::BLAZE));
|
||||
let mut entity_nbt = NbtCompound::new();
|
||||
spawner_block_entity.write_nbt(&mut entity_nbt);
|
||||
chunk.add_block_entity(entity_nbt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,15 +4,18 @@ use pumpkin_data::{
|
||||
BlockProperties, EndPortalFrameLikeProperties, HorizontalFacing, OakFenceLikeProperties,
|
||||
OakStairsLikeProperties,
|
||||
},
|
||||
entity::EntityType,
|
||||
};
|
||||
use pumpkin_nbt::compound::NbtCompound;
|
||||
use pumpkin_util::{
|
||||
BlockDirection,
|
||||
math::block_box::BlockBox,
|
||||
math::{block_box::BlockBox, position::BlockPos},
|
||||
random::{RandomGenerator, RandomImpl},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
ProtoChunk,
|
||||
block::entities::mob_spawner::MobSpawnerBlockEntity,
|
||||
generation::structure::{
|
||||
piece::StructurePieceType,
|
||||
structures::{
|
||||
@@ -355,8 +358,11 @@ impl StructurePieceBase for PortalRoomPiece {
|
||||
self.spawner_placed = true;
|
||||
let spawner = Block::SPAWNER.default_state;
|
||||
inner.add_block(chunk, spawner, 5, 3, 6, &box_limit);
|
||||
// Note: You must check your engine's API to set the BlockEntity (Silverfish).
|
||||
// Example: chunk.set_block_entity(pos, BlockEntity::Spawner(EntityType::Silverfish));
|
||||
let spawner_block_entity =
|
||||
MobSpawnerBlockEntity::new(BlockPos(pos), Some(&EntityType::SILVERFISH));
|
||||
let mut entity_nbt = NbtCompound::new();
|
||||
spawner_block_entity.write_nbt(&mut entity_nbt);
|
||||
chunk.add_block_entity(entity_nbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,10 +96,10 @@ pub fn place_template(
|
||||
let block_entity_id = block_entity_id.unwrap_or(&palette_entry.name);
|
||||
let mut block_entity_nbt = NbtCompound::new();
|
||||
|
||||
block_entity_nbt.put_string("id", block_entity_id.to_string());
|
||||
block_entity_nbt.put_int("x", wx);
|
||||
block_entity_nbt.put_int("y", wy);
|
||||
block_entity_nbt.put_int("z", wz);
|
||||
block_entity_nbt.put_string("id", block_entity_id.to_string());
|
||||
|
||||
if let Some(template_nbt) = &block.nbt {
|
||||
for (key, value) in &template_nbt.child_tags {
|
||||
@@ -111,7 +111,7 @@ pub fn place_template(
|
||||
}
|
||||
}
|
||||
|
||||
chunk.add_pending_block_entity(block_entity_nbt);
|
||||
chunk.add_block_entity(block_entity_nbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct SpawnerBlock;
|
||||
impl BlockBehaviour for SpawnerBlock {
|
||||
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let hopper_block_entity = MobSpawnerBlockEntity::new(*args.position);
|
||||
let hopper_block_entity = MobSpawnerBlockEntity::new(*args.position, None);
|
||||
args.world
|
||||
.add_block_entity(Arc::new(hopper_block_entity))
|
||||
.await;
|
||||
|
||||
Reference in New Issue
Block a user