Pumpkin/pumpkin-inventory/src/brewing/brewing_screen_handler.rs
Alexander Medvedev fdd2684432 feat: add Villager trading
currently job selection is missing
2026-05-01 14:34:05 +02:00

206 lines
7.1 KiB
Rust

//! Brewing stand screen handler.
//!
//! This module implements the screen handler for brewing stands.
//! Brewing stands have 5 slots:
//! - Slots 0-2: Potion bottles (output/input)
//! - Slot 3: Brewing ingredient
//! - Slot 4: Fuel (blaze powder)
//!
//! The brewing stand has two tracked properties:
//! - Brew time (0-400): Progress of the current brewing operation
//! - Fuel time (0-20): Amount of fuel remaining
use std::{any::Any, pin::Pin, sync::Arc};
use pumpkin_data::tag::Taggable;
use pumpkin_data::{screen::WindowType, tag};
use pumpkin_world::{block::entities::PropertyDelegate, inventory::Inventory};
use crate::{
player::player_inventory::PlayerInventory,
screen_handler::{ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, ScreenProperty},
};
use pumpkin_data::item_stack::ItemStack;
/// Screen handler for the brewing stand.
///
/// Manages the brewing stand's 5 slots and tracks brewing progress
/// and fuel levels.
pub struct BrewingScreenHandler {
/// The brewing stand's inventory (5 slots: 0-2 potions, 3 ingredient, 4 fuel).
inventory: Arc<dyn Inventory>,
/// Core screen handler behavior (slots, sync ID, listeners).
behaviour: ScreenHandlerBehaviour,
/// Delegate for accessing brew time and fuel time properties.
///
/// Property 0: Brew time (0-400), Property 1: Fuel time (0-20).
_property_delegate: Arc<dyn PropertyDelegate>,
}
impl BrewingScreenHandler {
/// Creates a new brewing stand screen handler.
///
/// # Arguments
/// - `sync_id` - The sync ID for client-server matching
/// - `player_inventory` - The player's inventory
/// - `inventory` - The brewing stand's inventory (5 slots)
/// - `property_delegate` - Delegate for accessing brew time and fuel properties
pub async fn new(
sync_id: u8,
player_inventory: Arc<PlayerInventory>,
inventory: Arc<dyn Inventory>,
property_delegate: Arc<dyn PropertyDelegate>,
) -> Self {
struct BrewingScreenListener;
impl crate::screen_handler::ScreenHandlerListener for BrewingScreenListener {
fn on_property_update<'a>(
&'a self,
screen_handler: &'a ScreenHandlerBehaviour,
property: u8,
value: i32,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler
.update_property(screen_handler, i32::from(property), value)
.await;
}
})
}
}
let mut handler = Self {
inventory,
behaviour: ScreenHandlerBehaviour::new(sync_id, Some(WindowType::BrewingStand)),
_property_delegate: property_delegate.clone(),
};
// BrewTime (index 0) and Fuel (index 1)
handler.add_property(ScreenProperty::new(property_delegate.clone(), 0));
handler.add_property(ScreenProperty::new(property_delegate.clone(), 1));
handler.add_listener(Arc::new(BrewingScreenListener)).await;
// Add all 5 brewing stand slots: 0-2 = potions, 3 = ingredient, 4 = fuel
for i in 0..5 {
handler.add_slot(Arc::new(crate::slot::NormalSlot::new(
handler.inventory.clone(),
i,
)));
}
// Add player slots
let pi: Arc<dyn Inventory> = player_inventory.clone();
handler.add_player_slots(&pi);
handler
}
}
impl ScreenHandler for BrewingScreenHandler {
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn get_behaviour(&self) -> &ScreenHandlerBehaviour {
&self.behaviour
}
fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour {
&mut self.behaviour
}
fn on_closed<'a>(
&'a mut self,
player: &'a dyn crate::screen_handler::InventoryPlayer,
) -> ScreenHandlerFuture<'a, ()> {
Box::pin(async move { self.default_on_closed(player).await })
}
/// Quick move logic for brewing stand.
///
/// - Potions (0-2) -> Player inventory
/// - Ingredient (3) -> Player inventory
/// - Fuel (4) -> Player inventory
/// - From player: potions -> potion slots, fuel -> fuel slot, else -> ingredient slot
fn quick_move<'a>(
&'a mut self,
_player: &'a dyn crate::screen_handler::InventoryPlayer,
slot_index: i32,
) -> Pin<Box<dyn Future<Output = ItemStack> + Send + 'a>> {
Box::pin(async move {
let mut stack_left = ItemStack::EMPTY.clone();
let slot = self.get_behaviour().slots[slot_index as usize].clone();
if !slot.has_stack().await {
return stack_left;
}
let slot_stack_lock = slot.get_stack().await;
let mut stack = slot_stack_lock.lock().await;
stack_left = stack.clone();
let success = if slot_index < 5 {
// Moving from brewing stand to player inventory
self.insert_item(&mut stack, 5, self.get_behaviour().slots.len() as i32, true)
.await
} else {
// Moving from player inventory to brewing stand
// Check item type to determine target slot
// Check if item has potion contents (for slots 0-2)
let has_potion_contents = stack
.get_data_component::<pumpkin_data::data_component_impl::PotionContentsImpl>()
.is_some();
// Check if item is brewing fuel (for slot 4)
let is_fuel = stack.get_item().has_tag(&tag::Item::MINECRAFT_BREWING_FUEL);
if has_potion_contents {
// Try to insert into potion slots (0-2)
self.insert_item(&mut stack, 0, 3, false).await
} else if is_fuel {
// Try to insert into fuel slot (4)
self.insert_item(&mut stack, 4, 5, false).await
} else {
// Try to insert into ingredient slot (3)
self.insert_item(&mut stack, 3, 4, false).await
}
};
if !success {
return ItemStack::EMPTY.clone();
}
if stack.is_empty() {
drop(stack);
slot.set_stack(ItemStack::EMPTY.clone()).await;
} else {
slot.mark_dirty().await;
}
stack_left
})
}
}
/// Creates a new brewing stand screen handler.
///
/// Factory function used by the server when a player opens a brewing stand.
pub async fn create_brewing(
sync_id: u8,
player_inventory: Arc<PlayerInventory>,
inventory: Arc<dyn Inventory>,
property_delegate: Arc<dyn PropertyDelegate>,
) -> Option<impl ScreenHandler> {
let handler =
BrewingScreenHandler::new(sync_id, player_inventory, inventory, property_delegate).await;
Some(handler)
}