use std::sync::Arc; use crate::block::entities::{ PropertyDelegate, furnace::FurnaceBlockEntity, furnace_like_block_entity::ExperienceContainer, }; use pumpkin_data::{ block_properties::{BlockProperties, FurnaceLikeProperties}, screen::WindowType, translation, }; use pumpkin_inventory::{ furnace_like::furnace_like_screen_handler::FurnaceLikeScreenHandler, player::player_inventory::PlayerInventory, screen_handler::{BoxFuture, InventoryPlayer, ScreenHandlerFactory, SharedScreenHandler}, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::text::TextComponent; use pumpkin_world::{BlockStateId, inventory::Inventory}; use tokio::sync::Mutex; use crate::{ block::{ BlockBehaviour, BlockFuture, BrokenArgs, NormalUseArgs, OnPlaceArgs, PlacedArgs, registry::BlockActionResult, }, entity::experience_orb::ExperienceOrbEntity, }; struct FurnaceScreenFactory { inventory: Arc, property_delegate: Arc, experience_container: Arc, } impl FurnaceScreenFactory { fn new( inventory: Arc, property_delegate: Arc, experience_container: Arc, ) -> Self { Self { inventory, property_delegate, experience_container, } } } impl ScreenHandlerFactory for FurnaceScreenFactory { fn create_screen_handler<'a>( &'a self, sync_id: u8, player_inventory: &'a Arc, _player: &'a dyn InventoryPlayer, ) -> BoxFuture<'a, Option> { Box::pin(async move { let concrete_handler = FurnaceLikeScreenHandler::new( sync_id, player_inventory, self.inventory.clone(), self.property_delegate.clone(), self.experience_container.clone(), WindowType::Furnace, ) .await; let concrete_arc = Arc::new(Mutex::new(concrete_handler)); Some(concrete_arc as SharedScreenHandler) }) } fn get_display_name(&self) -> pumpkin_util::text::TextComponent { TextComponent::translate_cross( translation::java::CONTAINER_FURNACE, translation::bedrock::CONTAINER_FURNACE, &[], ) } } #[pumpkin_block("minecraft:furnace")] pub struct FurnaceBlock; impl BlockBehaviour for FurnaceBlock { fn normal_use<'a>(&'a self, args: NormalUseArgs<'a>) -> BlockFuture<'a, BlockActionResult> { Box::pin(async move { if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(inventory) = block_entity.clone().get_inventory() && let Some(property_delegate) = block_entity.clone().to_property_delegate() && let Some(experience_container) = block_entity.to_experience_container() { let furnace_screen_factory = FurnaceScreenFactory::new(inventory, property_delegate, experience_container); args.player .open_handled_screen(&furnace_screen_factory, Some(*args.position)) .await; } crate::block::registry::BlockActionResult::Consume }) } fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { Box::pin(async move { let mut props = FurnaceLikeProperties::default(args.block); props.facing = args .player .living_entity .entity .get_horizontal_facing() .opposite(); props.to_state_id(args.block) }) } fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { let furnace_block_entity = FurnaceBlockEntity::new(*args.position); args.world .add_block_entity(Arc::new(furnace_block_entity)) .await; }) } fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { Box::pin(async move { // Extract and drop accumulated XP as orbs before removing the block entity if let Some(block_entity) = args.world.get_block_entity(args.position) && let Some(experience_container) = block_entity.to_experience_container() { let xp = experience_container.extract_experience(); if xp > 0 { let pos = args.position.to_f64(); ExperienceOrbEntity::spawn(args.world, pos, xp as u32).await; } } args.world.remove_block_entity(args.position).await; }) } }