use std::sync::Arc; use crate::block::entities::{ PropertyDelegate, blasting_furnace::BlastingFurnaceBlockEntity, 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 BlastingFurnaceScreenFactory { inventory: Arc, property_delegate: Arc, experience_container: Arc, } impl BlastingFurnaceScreenFactory { fn new( inventory: Arc, property_delegate: Arc, experience_container: Arc, ) -> Self { Self { inventory, property_delegate, experience_container, } } } impl ScreenHandlerFactory for BlastingFurnaceScreenFactory { 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::BlastFurnace, ) .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_BLAST_FURNACE, translation::bedrock::TILE_BLAST_FURNACE_NAME, &[], ) } } #[pumpkin_block("minecraft:blast_furnace")] pub struct BlastFurnaceBlock; impl BlockBehaviour for BlastFurnaceBlock { 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() { args.player .increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::InteractWithBlastFurnace as i32, 1, ) .await; let blasting_furnace_screen_factory = BlastingFurnaceScreenFactory::new( inventory, property_delegate, experience_container, ); args.player .open_handled_screen(&blasting_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 blasting_furnace_block_entity = BlastingFurnaceBlockEntity::new(*args.position); args.world .add_block_entity(Arc::new(blasting_furnace_block_entity)); }) } 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); }) } }