feat(block): implement archaeology brushable blocks

This commit is contained in:
Alexander Medvedev 2026-08-12 20:46:43 +02:00
parent 75200d79ae
commit 22daa1e5d0
3 changed files with 107 additions and 0 deletions

View File

@ -0,0 +1,104 @@
use std::sync::Arc;
use pumpkin_data::block_properties::{BlockProperties, SuspiciousSandLikeProperties};
use pumpkin_data::sound::{Sound, SoundCategory};
use pumpkin_data::{Block, BlockId, BlockStateId};
use pumpkin_world::world::BlockFlags;
use crate::block::entities::brushable_block::BrushableBlockBlockEntity;
use crate::block::{
BlockBehaviour, BlockFuture, BlockMetadata, BrokenArgs, OnPlaceArgs, PlacedArgs,
};
pub struct BrushableBlock;
impl BlockMetadata for BrushableBlock {
fn ids() -> Box<[BlockId]> {
[BlockId::SUSPICIOUS_SAND, BlockId::SUSPICIOUS_GRAVEL].into()
}
}
impl BrushableBlock {
pub async fn brush(
world: &Arc<crate::world::World>,
pos: &pumpkin_util::math::position::BlockPos,
block: &Block,
) {
let state = world.get_block_state(pos);
let mut props = SuspiciousSandLikeProperties::from_state_id(state.id, block);
let is_gravel = block.id == BlockId::SUSPICIOUS_GRAVEL;
if let Some(be) = world.get_block_entity(pos)
&& let Some(brush_be) = be.as_any().downcast_ref::<BrushableBlockBlockEntity>()
{
let mut hits = brush_be.hits.lock().await;
*hits += 1;
if *hits >= 4 {
let item = brush_be.item.lock().await.take();
if let Some(item_stack) = item {
world.drop_stack(pos, item_stack).await;
}
let target_block = if is_gravel {
&Block::GRAVEL
} else {
&Block::SAND
};
world
.set_block_state(pos, target_block.default_state.id, BlockFlags::NOTIFY_ALL)
.await;
let sound = if is_gravel {
Sound::ItemBrushBrushingGravelComplete
} else {
Sound::ItemBrushBrushingSandComplete
};
world.play_sound(sound, SoundCategory::Blocks, &pos.to_f64());
} else {
props.dusted = (*hits as u8).min(3);
world
.set_block_state(pos, props.to_state_id(block), BlockFlags::NOTIFY_ALL)
.await;
let sound = if is_gravel {
Sound::ItemBrushBrushingGravel
} else {
Sound::ItemBrushBrushingSand
};
world.play_sound(sound, SoundCategory::Blocks, &pos.to_f64());
}
}
}
}
impl BlockBehaviour for BrushableBlock {
fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> {
Box::pin(async move {
let props = SuspiciousSandLikeProperties::default(args.block);
props.to_state_id(args.block)
})
}
fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
let entity = BrushableBlockBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(entity));
})
}
fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> {
Box::pin(async move {
if let Some(be) = args.world.get_block_entity(args.position)
&& let Some(brush_be) = be.as_any().downcast_ref::<BrushableBlockBlockEntity>()
&& let Some(contained) = brush_be.item.lock().await.take()
{
args.world.drop_stack(args.position, contained).await;
}
})
}
}

View File

@ -110,6 +110,7 @@ pub mod coral;
pub mod abstract_wall_mounting;
pub mod beacon;
pub mod brushable_block;
pub mod creaking_heart;
pub mod decorated_pot;
pub mod sniffer_egg;

View File

@ -129,6 +129,7 @@ use crate::block::blocks::signs::SignBlock;
use crate::block::blocks::slabs::SlabBlock;
use crate::block::blocks::slime::SlimeBlock;
use crate::block::blocks::snow::LayeredSnowBlock;
use crate::block::blocks::brushable_block::BrushableBlock;
use crate::block::blocks::sniffer_egg::SnifferEggBlock;
use crate::block::blocks::spawner::SpawnerBlock;
use crate::block::blocks::sponge::{SpongeBlock, WetSpongeBlock};
@ -258,6 +259,7 @@ pub fn default_registry() -> Arc<BlockRegistry> {
manager.register(DecoratedPotBlock);
manager.register(CreakingHeartBlock);
manager.register(SnifferEggBlock);
manager.register(BrushableBlock);
manager.register(BushBlock);
manager.register(FlowerBlock);
manager.register(PotatoBlock);