From bd7a1a2126ca829587c3dcaa82b84760e355d28b Mon Sep 17 00:00:00 2001 From: yhypno Date: Fri, 31 Jul 2026 08:38:38 +0530 Subject: [PATCH] fix(inventory): enchanting table offers and lapis validation (#2374) * fix(inventory): match applied enchantments with table preview * fix(inventory): require lapis for enchanting purchases --- .../enchanting/enchanting_screen_handler.rs | 83 +++++++++++++++---- 1 file changed, 67 insertions(+), 16 deletions(-) diff --git a/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs b/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs index 718ddff34..7f44e533a 100644 --- a/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs +++ b/pumpkin-inventory/src/enchanting/enchanting_screen_handler.rs @@ -8,7 +8,7 @@ use pumpkin_data::item_stack::ItemStack; use pumpkin_data::screen::WindowType; use pumpkin_data::statistic::{CustomStatistic, StatisticCategory}; use pumpkin_data::tag::{Enchantment as EnchantmentTag, Taggable}; -use pumpkin_util::random::RandomImpl; +use pumpkin_util::random::{RandomImpl, legacy_rand::LegacyRand}; use pumpkin_world::inventory::Inventory; use crate::{ @@ -17,10 +17,44 @@ use crate::{ InventoryPlayer, ItemStackFuture, ScreenHandler, ScreenHandlerBehaviour, ScreenHandlerFuture, offer_or_drop_stack, }, - slot::NormalSlot, + slot::{BoxFuture, NormalSlot, Slot}, window_property::{EnchantmentTable, WindowProperty}, }; +struct LapisSlot(NormalSlot); + +fn is_lapis(stack: &ItemStack) -> bool { + stack.item == &Item::LAPIS_LAZULI +} + +impl LapisSlot { + fn new(inventory: Arc) -> Self { + Self(NormalSlot::new(inventory, 1)) + } +} + +impl Slot for LapisSlot { + fn get_inventory(&self) -> Arc { + self.0.get_inventory() + } + + fn get_index(&self) -> usize { + self.0.get_index() + } + + fn set_id(&self, id: usize) { + self.0.set_id(id); + } + + fn can_insert<'a>(&'a self, stack: &'a ItemStack) -> BoxFuture<'a, bool> { + Box::pin(async move { is_lapis(stack) }) + } + + fn mark_dirty(&self) -> BoxFuture<'_, ()> { + self.0.mark_dirty() + } +} + pub struct EnchantingTableScreenHandler { pub inventory: Arc, behaviour: ScreenHandlerBehaviour, @@ -51,7 +85,7 @@ impl EnchantingTableScreenHandler { // Enchanting slots: 0 is item, 1 is lapis handler.add_slot(Arc::new(NormalSlot::new(inventory.clone(), 0))); - handler.add_slot(Arc::new(NormalSlot::new(inventory.clone(), 1))); + handler.add_slot(Arc::new(LapisSlot::new(inventory.clone()))); let player_inventory: Arc = player_inventory.clone(); handler.add_player_slots(&player_inventory); @@ -81,9 +115,7 @@ impl EnchantingTableScreenHandler { self.enchantment_level[i] = -1; } } else { - let mut random = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed( - self.enchantment_seed as u64, - ); + let mut random = LegacyRand::from_seed(self.enchantment_seed as u64); for i in 0..3 { let level = self.calculate_level_requirement(&mut random, i, enchantability); @@ -92,18 +124,22 @@ impl EnchantingTableScreenHandler { for i in 0..3 { if self.level_requirements[i] > 0 { + let mut random = self.create_enchantment_random(i); let enchantments = Self::get_enchantment_list( &mut random, &item, i, self.level_requirements[i], ); - if let Some(first) = enchantments.first() { - self.enchantment_id[i] = first.0.id as i32; - self.enchantment_level[i] = first.1; - } else { + if enchantments.is_empty() { self.enchantment_id[i] = -1; self.enchantment_level[i] = -1; + } else { + let clue_index = + random.next_bounded_i32(enchantments.len() as i32) as usize; + let clue = enchantments[clue_index]; + self.enchantment_id[i] = clue.0.id as i32; + self.enchantment_level[i] = clue.1; } } else { self.enchantment_id[i] = -1; @@ -117,7 +153,7 @@ impl EnchantingTableScreenHandler { fn calculate_level_requirement( &self, - random: &mut pumpkin_util::random::xoroshiro128::Xoroshiro, + random: &mut LegacyRand, slot: usize, _enchantability: i32, ) -> i32 { @@ -132,8 +168,12 @@ impl EnchantingTableScreenHandler { } } + const fn create_enchantment_random(&self, slot: usize) -> LegacyRand { + LegacyRand::from_seed(self.enchantment_seed.wrapping_add(slot as i32) as u64) + } + fn get_enchantment_list( - random: &mut pumpkin_util::random::xoroshiro128::Xoroshiro, + random: &mut LegacyRand, item: &ItemStack, _slot: usize, level: i32, @@ -314,7 +354,9 @@ impl ScreenHandler for EnchantingTableScreenHandler { let lapis_cost = (id + 1) as u8; if !player.is_creative() - && (lapis_stack.is_empty() || lapis_stack.item_count < lapis_cost) + && (lapis_stack.is_empty() + || !is_lapis(&lapis_stack) + || lapis_stack.item_count < lapis_cost) { return false; } @@ -327,9 +369,7 @@ impl ScreenHandler for EnchantingTableScreenHandler { return false; } - let mut random = pumpkin_util::random::xoroshiro128::Xoroshiro::from_seed( - self.enchantment_seed as u64, - ); + let mut random = self.create_enchantment_random(id as usize); let enchantments = Self::get_enchantment_list(&mut random, &item_stack, id as usize, level_req); @@ -444,3 +484,14 @@ impl ScreenHandler for EnchantingTableScreenHandler { }) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn lapis_slot_only_accepts_lapis_lazuli() { + assert!(is_lapis(&ItemStack::new(1, &Item::LAPIS_LAZULI))); + assert!(!is_lapis(&ItemStack::new(1, &Item::DIRT))); + } +}