Pumpkin/pumpkin/src/block/blocks/furnace.rs
2026-05-14 12:28:43 +02:00

142 lines
4.8 KiB
Rust

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<dyn Inventory>,
property_delegate: Arc<dyn PropertyDelegate>,
experience_container: Arc<dyn ExperienceContainer>,
}
impl FurnaceScreenFactory {
fn new(
inventory: Arc<dyn Inventory>,
property_delegate: Arc<dyn PropertyDelegate>,
experience_container: Arc<dyn ExperienceContainer>,
) -> 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<PlayerInventory>,
_player: &'a dyn InventoryPlayer,
) -> BoxFuture<'a, Option<SharedScreenHandler>> {
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;
})
}
}