mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
174 lines
6.0 KiB
Rust
174 lines
6.0 KiB
Rust
use std::{any::Any, pin::Pin, sync::Arc};
|
|
|
|
use pumpkin_data::{fuels::is_fuel, item_stack::ItemStack, screen::WindowType};
|
|
use pumpkin_world::{
|
|
block::entities::{PropertyDelegate, furnace_like_block_entity::ExperienceContainer},
|
|
inventory::Inventory,
|
|
};
|
|
|
|
use crate::{
|
|
player::player_inventory::PlayerInventory,
|
|
screen_handler::{
|
|
InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour,
|
|
ScreenHandlerFuture, ScreenHandlerListener, ScreenProperty,
|
|
},
|
|
};
|
|
use tracing::debug;
|
|
|
|
use super::furnace_like_slot::{FurnaceLikeSlot, FurnaceLikeSlotType, FurnaceOutputSlot};
|
|
|
|
pub struct FurnaceLikeScreenHandler {
|
|
pub inventory: Arc<dyn Inventory>,
|
|
experience_container: Arc<dyn ExperienceContainer>,
|
|
behaviour: ScreenHandlerBehaviour,
|
|
}
|
|
|
|
impl FurnaceLikeScreenHandler {
|
|
pub async fn new(
|
|
sync_id: u8,
|
|
player_inventory: &Arc<PlayerInventory>,
|
|
inventory: Arc<dyn Inventory>,
|
|
property_delegate: Arc<dyn PropertyDelegate>,
|
|
experience_container: Arc<dyn ExperienceContainer>,
|
|
window_type: WindowType,
|
|
) -> Self {
|
|
struct FurnaceLikeScreenListener;
|
|
impl ScreenHandlerListener for FurnaceLikeScreenListener {
|
|
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,
|
|
experience_container,
|
|
behaviour: ScreenHandlerBehaviour::new(sync_id, Some(window_type)),
|
|
};
|
|
|
|
// 0: Fire icon (fuel left) counting from fuel burn time down to 0 (in-game ticks)
|
|
// 1: Maximum fuel burn time fuel burn time or 0 (in-game ticks)
|
|
// 2: Progress arrow counting from 0 to maximum progress (in-game ticks)
|
|
// 3: Maximum progress always 200 on the vanilla server
|
|
for i in 0..4 {
|
|
handler.add_property(ScreenProperty::new(property_delegate.clone(), i));
|
|
}
|
|
|
|
handler
|
|
.add_listener(Arc::new(FurnaceLikeScreenListener))
|
|
.await;
|
|
handler.add_inventory_slots();
|
|
let player_inventory: Arc<dyn Inventory> = player_inventory.clone();
|
|
handler.add_player_slots(&player_inventory);
|
|
|
|
handler
|
|
}
|
|
|
|
fn add_inventory_slots(&mut self) {
|
|
self.add_slot(Arc::new(FurnaceLikeSlot::new(
|
|
self.inventory.clone(),
|
|
FurnaceLikeSlotType::Top,
|
|
)));
|
|
self.add_slot(Arc::new(FurnaceLikeSlot::new(
|
|
self.inventory.clone(),
|
|
FurnaceLikeSlotType::Bottom,
|
|
)));
|
|
// Output slot awards experience when items are taken
|
|
self.add_slot(Arc::new(FurnaceOutputSlot::new(
|
|
self.inventory.clone(),
|
|
self.experience_container.clone(),
|
|
)));
|
|
}
|
|
}
|
|
|
|
impl ScreenHandler for FurnaceLikeScreenHandler {
|
|
fn on_closed<'a>(&'a mut self, player: &'a dyn InventoryPlayer) -> ScreenHandlerFuture<'a, ()> {
|
|
Box::pin(async move {
|
|
self.default_on_closed(player).await;
|
|
// TODO: self.inventory.on_closed(player).await;
|
|
})
|
|
}
|
|
|
|
fn as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn get_behaviour(&self) -> &ScreenHandlerBehaviour {
|
|
&self.behaviour
|
|
}
|
|
|
|
fn get_behaviour_mut(&mut self) -> &mut ScreenHandlerBehaviour {
|
|
&mut self.behaviour
|
|
}
|
|
|
|
fn quick_move<'a>(
|
|
&'a mut self,
|
|
player: &'a dyn InventoryPlayer,
|
|
slot_index: i32,
|
|
) -> ItemStackFuture<'a> {
|
|
Box::pin(async move {
|
|
const FUEL_SLOT: i32 = 1; // Note: Slots 0, 1, 2 are Furnace slots.
|
|
const OUTPUT_SLOT: i32 = 2;
|
|
|
|
debug!("FurnaceLikeScreenHandler::quick_move slot_index={slot_index}");
|
|
|
|
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;
|
|
|
|
// Acquire the lock to read/clone the stack
|
|
let mut stack = slot_stack_lock.lock().await;
|
|
stack_left = stack.clone();
|
|
|
|
let success = if slot_index < 3 {
|
|
// If clicked slot is one of the Furnace slots (0, 1, 2):
|
|
// Try to move to player inventory (slots 3 onwards, starting from the end)
|
|
self.insert_item(&mut stack, 3, self.get_behaviour().slots.len() as i32, true)
|
|
.await
|
|
} else if is_fuel(stack.item.id) {
|
|
// If clicked slot is in the player inventory (3+) and contains fuel:
|
|
// Try to move to the Furnace's Fuel slot (slot 1)
|
|
self.insert_item(&mut stack, FUEL_SLOT, 3, false).await
|
|
} else {
|
|
// If clicked slot is in the player inventory (3+) and NOT fuel (must be a smeltable item):
|
|
// Try to move to the Furnace's Input/Smelting slot (slot 0)
|
|
self.insert_item(&mut stack, 0, 3, false).await
|
|
};
|
|
|
|
if !success {
|
|
return ItemStack::EMPTY.clone();
|
|
}
|
|
|
|
if stack.is_empty() {
|
|
drop(stack); // Release lock before awaiting
|
|
slot.set_stack(ItemStack::EMPTY.clone()).await;
|
|
} else {
|
|
slot.mark_dirty().await;
|
|
}
|
|
|
|
// Award XP when taking from output slot (slot 2)
|
|
if slot_index == OUTPUT_SLOT {
|
|
debug!("quick_move: taking from output slot, calling on_take_item");
|
|
slot.on_take_item(player, &stack_left).await;
|
|
}
|
|
|
|
stack_left
|
|
})
|
|
}
|
|
}
|