steel_core/inventory/slots/
grindstone_slots.rs1use std::sync::Arc;
3
4use steel_registry::vanilla_enchantment_tags::EnchantmentTag;
5use steel_registry::{REGISTRY, RegistryExt, TaggedRegistryExt};
6use steel_registry::{item_stack::ItemStack, level_events};
7use steel_utils::{BlockPos, locks::Shared};
8
9use crate::{
10 entity::entities::ExperienceOrbEntity,
11 inventory::{
12 container::{ResultContainer, SimpleContainer},
13 prelude::*,
14 },
15 world::World,
16};
17
18#[derive(Clone)]
20pub struct GrindstoneResultHandler {
21 input_container: Shared<SimpleContainer>,
22 result_container: Shared<ResultContainer>,
23 block_pos: BlockPos,
24 world: Arc<World>,
25}
26
27impl GrindstoneResultHandler {
28 pub const fn new(
30 input_container: Shared<SimpleContainer>,
31 result_container: Shared<ResultContainer>,
32 block_pos: BlockPos,
33 world: Arc<World>,
34 ) -> Self {
35 Self {
36 input_container,
37 result_container,
38 block_pos,
39 world,
40 }
41 }
42
43 fn get_experience_amount(&self, guard: &ContainerLockGuard) -> i32 {
47 let Some(input) = guard.get(ContainerId::from_arc(&self.input_container)) else {
48 log::warn!("input container not locked while awarding grindstone experience");
49 return 0;
50 };
51
52 let amount = Self::get_experience_from_item(input.get_item(0))
53 + Self::get_experience_from_item(input.get_item(1));
54
55 if amount > 0 {
56 let half_amount = (amount + 1) / 2;
58 return half_amount + rand::random_range(0..half_amount);
59 }
60 0
61 }
62
63 fn get_experience_from_item(item: &ItemStack) -> i32 {
64 let mut amount = 0;
65 let Some(enchantments) = item.get_enchantments_for_crafting() else {
66 return 0;
67 };
68
69 enchantments.iter().for_each(|(id, level)| {
70 let Some(enchantment) = REGISTRY.enchantments.by_key(id) else {
71 return;
72 };
73
74 if REGISTRY
75 .enchantments
76 .is_in_tag(enchantment, &EnchantmentTag::CURSE)
77 {
78 return;
79 }
80
81 amount += enchantment.min_cost.base
82 + enchantment.min_cost.per_level_above_first * (*level as i32 - 1);
83 });
84
85 amount
86 }
87}
88
89impl ResultHandler for GrindstoneResultHandler {
90 fn result_container(&self) -> ContainerRef {
91 ContainerRef::from(self.result_container.clone())
92 }
93
94 fn dependencies(&self) -> Vec<ContainerRef> {
95 vec![ContainerRef::from(self.input_container.clone())]
96 }
97
98 fn update_result(&self, _guard: &mut ContainerLockGuard) {}
99
100 fn on_result_taken(
101 &self,
102 guard: &mut ContainerLockGuard,
103 _player: &Player,
104 ) -> Option<ItemStack> {
105 let experience = self.get_experience_amount(guard);
107 ExperienceOrbEntity::award(&self.world, self.block_pos.get_center().into(), experience);
108
109 self.world
110 .level_event(level_events::SOUND_GRINDSTONE_USED, self.block_pos, 0, None);
111
112 let id = ContainerId::from_arc(&self.input_container);
113 let Some(input) = guard.get_mut(id) else {
114 log::warn!("Couldn't get lock for grindstone.");
115 return None;
116 };
117
118 input.set_item(0, ItemStack::empty());
119 input.set_item(1, ItemStack::empty());
120
121 None
122 }
123
124 fn is_result_valid(&self, _guard: &ContainerLockGuard, _player: &Player) -> bool {
125 true
126 }
127}