steel_core/inventory/slots/
furnace_slots.rs1use std::sync::atomic::{AtomicI32, Ordering};
4
5use steel_registry::item_stack::ItemStack;
6use steel_registry::stat::vanilla_stat_types;
7use steel_registry::vanilla_items;
8use steel_utils::{DowncastType, DowncastTypeKey};
9
10use crate::block_entity::entities::{FurnaceContainer, pop_furnace_experience};
11use crate::entity::Entity as _;
12use crate::inventory::fuel_values::VANILLA_FUEL_VALUES;
13use crate::inventory::lock::{ContainerLockGuard, ContainerRef};
14use crate::inventory::slots::{NormalSlot, Slot, SlotStorage};
15use crate::player::Player;
16
17pub struct FurnaceFuelSlot {
19 base: NormalSlot,
20}
21
22unsafe impl DowncastType for FurnaceFuelSlot {
24 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:slot/furnace_fuel");
25}
26
27impl FurnaceFuelSlot {
28 #[must_use]
30 pub fn new(container: impl Into<ContainerRef>, index: usize) -> Self {
31 Self {
32 base: NormalSlot::new(container, index),
33 }
34 }
35}
36
37impl Slot for FurnaceFuelSlot {
38 fn storage(&self) -> &SlotStorage {
39 self.base.storage()
40 }
41
42 fn get_item<'a>(&self, guard: &'a ContainerLockGuard) -> &'a ItemStack {
43 self.base.get_item(guard)
44 }
45
46 fn get_item_mut<'a>(&self, guard: &'a mut ContainerLockGuard) -> &'a mut ItemStack {
47 self.base.get_item_mut(guard)
48 }
49
50 fn set_item(&self, guard: &mut ContainerLockGuard, stack: ItemStack) {
51 self.base.set_item(guard, stack);
52 }
53
54 fn may_place(&self, stack: &ItemStack) -> bool {
55 VANILLA_FUEL_VALUES.is_fuel(stack.item()) || stack.is(&vanilla_items::BUCKET)
56 }
57
58 fn get_max_stack_size(&self, guard: &ContainerLockGuard) -> i32 {
59 self.base.get_max_stack_size(guard)
60 }
61
62 fn get_max_stack_size_for_item(&self, guard: &ContainerLockGuard, stack: &ItemStack) -> i32 {
63 if stack.is(&vanilla_items::BUCKET) {
64 1
65 } else {
66 self.base.get_max_stack_size_for_item(guard, stack)
67 }
68 }
69
70 fn set_changed(&self, guard: &mut ContainerLockGuard) {
71 self.base.set_changed(guard);
72 }
73
74 fn get_container_slot(&self) -> usize {
75 self.base.get_container_slot()
76 }
77}
78
79pub struct FurnaceResultSlot {
84 base: NormalSlot,
85 remove_count: AtomicI32,
86}
87
88unsafe impl DowncastType for FurnaceResultSlot {
90 const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:slot/furnace_result");
91}
92
93impl FurnaceResultSlot {
94 #[must_use]
96 pub fn new(container: impl Into<ContainerRef>, index: usize) -> Self {
97 Self {
98 base: NormalSlot::new(container, index),
99 remove_count: AtomicI32::new(0),
100 }
101 }
102
103 fn check_take_achievements(&self, stack: &ItemStack, player: &Player) {
104 let count = self.remove_count.swap(0, Ordering::Relaxed);
105 if count > 0 {
106 player.award_stat_with_count(&vanilla_stat_types::ITEM_CRAFTED, stack.item(), count);
107 }
108 }
109}
110
111impl Slot for FurnaceResultSlot {
112 fn storage(&self) -> &SlotStorage {
113 self.base.storage()
114 }
115
116 fn get_item<'a>(&self, guard: &'a ContainerLockGuard) -> &'a ItemStack {
117 self.base.get_item(guard)
118 }
119
120 fn get_item_mut<'a>(&self, guard: &'a mut ContainerLockGuard) -> &'a mut ItemStack {
121 self.base.get_item_mut(guard)
122 }
123
124 fn set_item(&self, guard: &mut ContainerLockGuard, stack: ItemStack) {
125 self.base.set_item(guard, stack);
126 }
127
128 fn may_place(&self, _stack: &ItemStack) -> bool {
129 false
130 }
131
132 fn get_max_stack_size(&self, guard: &ContainerLockGuard) -> i32 {
133 self.base.get_max_stack_size(guard)
134 }
135
136 fn set_changed(&self, guard: &mut ContainerLockGuard) {
137 self.base.set_changed(guard);
138 }
139
140 fn get_container_slot(&self) -> usize {
141 self.base.get_container_slot()
142 }
143
144 fn on_quick_craft(&self, stack: &ItemStack, player: &Player) {
145 self.remove_count
146 .fetch_add(stack.count(), Ordering::Relaxed);
147 self.check_take_achievements(stack, player);
148 }
149
150 fn remove(&self, guard: &mut ContainerLockGuard, amount: i32) -> ItemStack {
151 let item = self.get_item_mut(guard);
152 if item.is_empty() || amount <= 0 {
153 return ItemStack::empty();
154 }
155 let removed = item.split(amount);
156 self.remove_count
157 .fetch_add(removed.count(), Ordering::Relaxed);
158 removed
159 }
160
161 fn on_take(
162 &self,
163 guard: &mut ContainerLockGuard,
164 stack: &ItemStack,
165 player: &Player,
166 ) -> Option<ItemStack> {
167 self.check_take_achievements(stack, player);
168 let (container, _) = self.storage().physical_backing()?;
170 let container_id = container.container_id();
171 let recipes = guard
172 .get_typed_mut::<FurnaceContainer>(container_id)
173 .map(FurnaceContainer::take_recipes_used);
174 self.base.set_changed(guard);
175 if let Some(recipes) = recipes {
176 guard.run_unlocked(|| {
177 pop_furnace_experience(&player.get_world(), player.position(), recipes);
178 });
179 }
180 None
181 }
182}