Pumpkin/crates/pumpkin/src/block/blocks/shulker_box.rs
2026-09-02 18:10:50 +02:00

118 lines
4.0 KiB
Rust

use std::sync::Arc;
use std::sync::Mutex;
use crate::block::{
GetComparatorOutputArgs, GetScreenHandlerFactoryArgs, OnPlaceArgs, OnSyncedBlockEventArgs,
PlacedArgs,
};
use crate::block::{
registry::BlockActionResult,
{BlockBehaviour, NormalUseArgs},
};
use crate::block::entities::shulker_box::ShulkerBoxBlockEntity;
use pumpkin_data::BlockStateId;
use pumpkin_data::translation;
use pumpkin_inventory::generic_container_screen_handler::create_generic_9x3;
use pumpkin_inventory::player::player_inventory::PlayerInventory;
use pumpkin_inventory::screen_handler::{
InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler,
};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::text::TextComponent;
use pumpkin_world::inventory::Inventory;
struct ShulkerBoxScreenFactory(Arc<dyn Inventory>);
impl ScreenHandlerFactory for ShulkerBoxScreenFactory {
fn create_screen_handler(
&self,
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
player: &dyn InventoryPlayer,
) -> Option<SharedScreenHandler> {
let handler = create_generic_9x3(sync_id, player_inventory, self.0.clone(), player);
let screen_handler_arc = Arc::new(Mutex::new(handler));
Some(screen_handler_arc as SharedScreenHandler)
}
fn get_display_name(&self) -> TextComponent {
pumpkin_macros::translate_cross!(
translation::java::CONTAINER_SHULKERBOX,
translation::bedrock::CONTAINER_SHULKERBOX
)
}
}
#[pumpkin_block_from_tag("minecraft:shulker_boxes")]
pub struct ShulkerBoxBlock;
type EndRodLikeProperties = pumpkin_data::block_properties::EndRodLikeProperties;
impl BlockBehaviour for ShulkerBoxBlock {
fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut props = EndRodLikeProperties::default(args.block);
props.facing = args.direction.to_facing().opposite();
props.to_state_id(args.block)
}
fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool {
// On the server, we don't need the Animation steps for now, because the client is responsible for that.
// TODO: Do not open the shulker box when it is currently closing
args.r#type == Self::OPEN_ANIMATION_EVENT_TYPE
}
fn placed(&self, args: PlacedArgs<'_>) {
{
let barrel_block_entity = ShulkerBoxBlockEntity::new(*args.position);
args.world.add_block_entity(Arc::new(barrel_block_entity));
}
}
fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult {
if let Some(factory) = self.get_screen_handler_factory(GetScreenHandlerFactoryArgs {
server: args.server,
world: args.world,
block: args.block,
position: args.position,
player: args.player,
}) {
args.player.increment_stat(
pumpkin_data::statistic::StatisticCategory::Custom,
pumpkin_data::statistic::CustomStatistic::OpenShulkerBox as i32,
1,
);
args.player
.open_handled_screen(factory.as_ref(), Some(*args.position));
}
BlockActionResult::Success
}
fn get_screen_handler_factory(
&self,
args: GetScreenHandlerFactoryArgs<'_>,
) -> Option<Box<dyn ScreenHandlerFactory>> {
let block_entity = args.world.get_block_entity(args.position)?;
let inventory = block_entity.get_inventory()?;
Some(Box::new(ShulkerBoxScreenFactory(inventory)))
}
fn get_comparator_output(&self, args: GetComparatorOutputArgs<'_>) -> Option<u8> {
if let Some(block_entity) = args.world.get_block_entity(args.position)
&& let Some(inventory) = block_entity.get_inventory()
{
Some(crate::block::calculate_comparator_output(
inventory.as_ref(),
))
} else {
None
}
}
}
impl ShulkerBoxBlock {
pub const OPEN_ANIMATION_EVENT_TYPE: u8 = 1;
}