diff --git a/pumpkin-inventory/src/screen_handler.rs b/pumpkin-inventory/src/screen_handler.rs index d7303f288..ab76e704b 100644 --- a/pumpkin-inventory/src/screen_handler.rs +++ b/pumpkin-inventory/src/screen_handler.rs @@ -1,5 +1,3 @@ -use std::{any::Any, collections::HashMap, sync::Arc}; - use async_trait::async_trait; use log::warn; use pumpkin_data::screen::WindowType; @@ -14,6 +12,8 @@ use pumpkin_protocol::{ use pumpkin_util::text::TextComponent; use pumpkin_world::inventory::{ComparableInventory, Inventory}; use pumpkin_world::item::ItemStack; +use std::cmp::max; +use std::{any::Any, collections::HashMap, sync::Arc}; use tokio::sync::Mutex; use crate::{ @@ -45,6 +45,7 @@ impl ScreenProperty { pub trait InventoryPlayer: Send + Sync { async fn drop_item(&self, item: ItemStack, retain_ownership: bool); fn get_inventory(&self) -> Arc; + fn has_infinite_materials(&self) -> bool; async fn enqueue_inventory_packet(&self, packet: &CSetContainerContent); async fn enqueue_slot_packet(&self, packet: &CSetContainerSlot); async fn enqueue_cursor_packet(&self, packet: &CSetCursorItem); @@ -466,8 +467,150 @@ pub trait ScreenHandler: Send + Sync { action_type: SlotActionType, player: &dyn InventoryPlayer, ) { - //TODO: Implement quickcraft, Clone, PickupAll, Throw - if (action_type == SlotActionType::Pickup || action_type == SlotActionType::QuickMove) + if action_type == SlotActionType::PickupAll && button == 0 { + let behavior = self.get_behaviour_mut(); + let mut cursor_stack = behavior.cursor_stack.lock().await; + let mut to_pick_up = cursor_stack.get_max_stack_size() - cursor_stack.item_count; + + for slot in behavior.slots.iter() { + if to_pick_up == 0 { + break; + } + + let stack_lock = slot.get_stack().await; + let item_stack = stack_lock.lock().await; + if !item_stack.are_items_and_components_equal(&cursor_stack) { + continue; + } + + if !slot.can_take_items(player).await { + continue; + } + + let take = item_stack.item_count.min(to_pick_up); + to_pick_up -= take; + + if item_stack.item_count == take { + drop(item_stack); + slot.set_stack(ItemStack::EMPTY).await; + } else { + let mut stack_clone = *item_stack; + drop(item_stack); + stack_clone.decrement(take); + slot.set_stack(stack_clone).await; + } + cursor_stack.increment(take); + } + } else if action_type == SlotActionType::QuickCraft { + let drag_type = button & 3; + let drag_button = (button >> 2) & 3; + let behaviour = self.get_behaviour_mut(); + if drag_type == 0 { + behaviour.drag_slots.clear(); + } else if drag_type == 1 { + if slot_index < 0 { + warn!("Invalid slot index for drag action: {slot_index}. Must be >= 0"); + return; + } + let cursor_stack = behaviour.cursor_stack.lock().await; + + let slot = &behaviour.slots[slot_index as usize]; + let stack_lock = slot.get_stack().await; + let stack = stack_lock.lock().await; + if !cursor_stack.is_empty() + && slot.can_insert(&cursor_stack).await + && (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + && slot.get_max_item_count_for_stack(&stack).await > stack.item_count + { + behaviour.drag_slots.push(slot_index as u32); + } + } else if drag_type == 2 && !behaviour.drag_slots.is_empty() { + // process drag end + if behaviour.drag_slots.len() == 1 { + let slot = behaviour.drag_slots[0] as i32; + behaviour.drag_slots.clear(); + let _ = behaviour; + self.internal_on_slot_click(slot, drag_button, SlotActionType::Pickup, player) + .await; + + return; + } + if drag_button == 2 && !player.has_infinite_materials() { + return; // Only creative + } + + let mut cursor_stack = behaviour.cursor_stack.lock().await; + let initial_count = cursor_stack.item_count; + for slot_index in behaviour.drag_slots.iter() { + let slot = behaviour.slots[*slot_index as usize].clone(); + let stack_lock = slot.get_stack().await; + let stack = stack_lock.lock().await; + + if (stack.are_items_and_components_equal(&cursor_stack) || stack.is_empty()) + && slot.can_insert(&cursor_stack).await + { + let mut inserting_count = if drag_button == 0 { + initial_count / behaviour.drag_slots.len() as u8 + } else if drag_button == 1 { + 1 + } else if drag_button == 2 { + cursor_stack.get_max_stack_size() + } else { + panic!("Invalid drag button: {drag_button}"); + }; + inserting_count = inserting_count + .min(max( + 0, + slot.get_max_item_count_for_stack(&stack).await - stack.item_count, + )) + .min(cursor_stack.item_count); + if inserting_count > 0 { + let mut stack_clone = *stack; + drop(stack); + if stack_clone.is_empty() { + stack_clone = cursor_stack.copy_with_count(0); + } + stack_clone.increment(inserting_count); + slot.set_stack(stack_clone).await; + if drag_button != 2 { + cursor_stack.decrement(inserting_count); + } + if cursor_stack.is_empty() { + break; + } + } + } + } + + behaviour.drag_slots.clear(); + } + } else if action_type == SlotActionType::Throw { + if slot_index >= 0 && self.get_behaviour().cursor_stack.lock().await.is_empty() { + let slot = self.get_behaviour().slots[slot_index as usize].clone(); + let stack_lock = slot.get_stack().await; + let mut target_stack = stack_lock.lock().await; + if !target_stack.is_empty() { + if button == 1 { + let stack_clone = *target_stack; + player.drop_item(stack_clone, true).await; + drop(target_stack); + slot.set_stack(ItemStack::EMPTY).await; + } else { + player.drop_item(target_stack.split(1), true).await; + } + } + } + } else if action_type == SlotActionType::Clone { + if player.has_infinite_materials() && slot_index >= 0 { + let behaviour = self.get_behaviour_mut(); + let slot = behaviour.slots[slot_index as usize].clone(); + let stack_lock = slot.get_stack().await; + let stack = stack_lock.lock().await; + let mut cursor_stack = behaviour.cursor_stack.lock().await; + *cursor_stack = stack.copy_with_count(stack.get_max_stack_size()); + } + } else if (action_type == SlotActionType::Pickup + || action_type == SlotActionType::QuickMove) && (button == 0 || button == 1) { let click_type = if button == 0 { @@ -538,7 +681,6 @@ pub trait ScreenHandler: Send + Sync { if slot_stack.is_empty() { if !cursor_stack.is_empty() { - //println!("Cursor -> Slot"); let transfer_count = if click_type == MouseClick::Left { cursor_stack.item_count } else { @@ -549,7 +691,6 @@ pub trait ScreenHandler: Send + Sync { } } else if slot.can_take_items(player).await { if cursor_stack.is_empty() { - //println!("Slot -> Cursor"); let take_count = if click_type == MouseClick::Left { slot_stack.item_count } else { @@ -717,6 +858,7 @@ pub struct ScreenHandlerBehaviour { pub properties: Vec, pub tracked_property_values: Vec, pub window_type: Option, + pub drag_slots: Vec, } impl ScreenHandlerBehaviour { @@ -735,6 +877,7 @@ impl ScreenHandlerBehaviour { properties: Vec::new(), tracked_property_values: Vec::new(), window_type, + drag_slots: Vec::new(), } } diff --git a/pumpkin/src/entity/player.rs b/pumpkin/src/entity/player.rs index 66d3565c5..8b784ff30 100644 --- a/pumpkin/src/entity/player.rs +++ b/pumpkin/src/entity/player.rs @@ -2481,6 +2481,10 @@ impl InventoryPlayer for Player { self.drop_item(item).await; } + fn has_infinite_materials(&self) -> bool { + self.gamemode.load() == GameMode::Creative + } + fn get_inventory(&self) -> Arc { self.inventory.clone() }