Pumpkin/crates/pumpkin-inventory/src/brewing/brewing_screen_handler.rs
2026-08-26 20:33:08 +02:00

194 lines
6.3 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, 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, 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 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(
&self,
screen_handler: &ScreenHandlerBehaviour,
property: u8,
value: i32,
) {
if let Some(sync_handler) = screen_handler.sync_handler.as_ref() {
sync_handler.update_property(screen_handler, i32::from(property), value);
}
}
}
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));
// 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(&mut self, player: &dyn crate::screen_handler::InventoryPlayer) {
self.default_on_closed(player);
}
/// 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(
&mut self,
_player: &dyn crate::screen_handler::InventoryPlayer,
slot_index: i32,
) -> ItemStack {
let mut stack_left = ItemStack::EMPTY.clone();
let slot = self.get_behaviour().slots[slot_index as usize].clone();
if !slot.has_stack() {
return stack_left;
}
let mut stack = slot.get_stack();
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)
} 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)
} else if is_fuel {
// Try to insert into fuel slot (4)
self.insert_item(&mut stack, 4, 5, false)
} else {
// Try to insert into ingredient slot (3)
self.insert_item(&mut stack, 3, 4, false)
}
};
if !success {
return ItemStack::EMPTY.clone();
}
if stack.is_empty() {
slot.set_stack(ItemStack::EMPTY.clone());
} else {
slot.set_stack(stack);
}
stack_left
}
}
/// Creates a new brewing stand screen handler.
///
/// Factory function used by the server when a player opens a brewing stand.
pub fn create_brewing(
sync_id: u8,
player_inventory: &Arc<PlayerInventory>,
inventory: Arc<dyn Inventory>,
property_delegate: &Arc<dyn PropertyDelegate>,
) -> Option<BrewingScreenHandler> {
let handler =
BrewingScreenHandler::new(sync_id, player_inventory, inventory, property_delegate);
Some(handler)
}