Skip to main content

steel_core/inventory/slots/
furnace_slots.rs

1//! Furnace-specific input, fuel, and result slots.
2
3use steel_registry::item_stack::ItemStack;
4use steel_registry::vanilla_items;
5use steel_utils::{DowncastType, DowncastTypeKey};
6
7use crate::block_entity::entities::{FurnaceContainer, pop_furnace_experience};
8use crate::entity::Entity as _;
9use crate::inventory::fuel_values::VANILLA_FUEL_VALUES;
10use crate::inventory::lock::{ContainerLockGuard, ContainerRef};
11use crate::inventory::slots::{NormalSlot, Slot, SlotStorage};
12use crate::player::Player;
13
14/// Accepts furnace fuel and empty buckets, with Vanilla's bucket stack limit.
15pub struct FurnaceFuelSlot {
16    base: NormalSlot,
17}
18
19// SAFETY: This Steel-owned key uniquely identifies furnace fuel slots.
20unsafe impl DowncastType for FurnaceFuelSlot {
21    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:slot/furnace_fuel");
22}
23
24impl FurnaceFuelSlot {
25    /// Creates a furnace fuel slot.
26    #[must_use]
27    pub fn new(container: impl Into<ContainerRef>, index: usize) -> Self {
28        Self {
29            base: NormalSlot::new(container, index),
30        }
31    }
32}
33
34impl Slot for FurnaceFuelSlot {
35    fn storage(&self) -> &SlotStorage {
36        self.base.storage()
37    }
38
39    fn get_item<'a>(&self, guard: &'a ContainerLockGuard) -> &'a ItemStack {
40        self.base.get_item(guard)
41    }
42
43    fn get_item_mut<'a>(&self, guard: &'a mut ContainerLockGuard) -> &'a mut ItemStack {
44        self.base.get_item_mut(guard)
45    }
46
47    fn set_item(&self, guard: &mut ContainerLockGuard, stack: ItemStack) {
48        self.base.set_item(guard, stack);
49    }
50
51    fn may_place(&self, stack: &ItemStack) -> bool {
52        VANILLA_FUEL_VALUES.is_fuel(stack.item()) || stack.is(&vanilla_items::BUCKET)
53    }
54
55    fn get_max_stack_size(&self, guard: &ContainerLockGuard) -> i32 {
56        self.base.get_max_stack_size(guard)
57    }
58
59    fn get_max_stack_size_for_item(&self, guard: &ContainerLockGuard, stack: &ItemStack) -> i32 {
60        if stack.is(&vanilla_items::BUCKET) {
61            1
62        } else {
63            self.base.get_max_stack_size_for_item(guard, stack)
64        }
65    }
66
67    fn set_changed(&self, guard: &mut ContainerLockGuard) {
68        self.base.set_changed(guard);
69    }
70
71    fn get_container_slot(&self) -> usize {
72        self.base.get_container_slot()
73    }
74}
75
76/// Rejects insertion and awards accumulated cooking XP when output is taken.
77pub struct FurnaceResultSlot {
78    base: NormalSlot,
79}
80
81// SAFETY: This Steel-owned key uniquely identifies furnace result slots.
82unsafe impl DowncastType for FurnaceResultSlot {
83    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:slot/furnace_result");
84}
85
86impl FurnaceResultSlot {
87    /// Creates a furnace result slot.
88    #[must_use]
89    pub fn new(container: impl Into<ContainerRef>, index: usize) -> Self {
90        Self {
91            base: NormalSlot::new(container, index),
92        }
93    }
94}
95
96impl Slot for FurnaceResultSlot {
97    fn storage(&self) -> &SlotStorage {
98        self.base.storage()
99    }
100
101    fn get_item<'a>(&self, guard: &'a ContainerLockGuard) -> &'a ItemStack {
102        self.base.get_item(guard)
103    }
104
105    fn get_item_mut<'a>(&self, guard: &'a mut ContainerLockGuard) -> &'a mut ItemStack {
106        self.base.get_item_mut(guard)
107    }
108
109    fn set_item(&self, guard: &mut ContainerLockGuard, stack: ItemStack) {
110        self.base.set_item(guard, stack);
111    }
112
113    fn may_place(&self, _stack: &ItemStack) -> bool {
114        false
115    }
116
117    fn get_max_stack_size(&self, guard: &ContainerLockGuard) -> i32 {
118        self.base.get_max_stack_size(guard)
119    }
120
121    fn set_changed(&self, guard: &mut ContainerLockGuard) {
122        self.base.set_changed(guard);
123    }
124
125    fn get_container_slot(&self) -> usize {
126        self.base.get_container_slot()
127    }
128
129    fn on_take(
130        &self,
131        guard: &mut ContainerLockGuard,
132        _stack: &ItemStack,
133        player: &Player,
134    ) -> Option<ItemStack> {
135        // TODO: Award crafted stats/recipes and trigger RECIPE_CRAFTED once supported.
136        let (container, _) = self.storage().physical_backing()?;
137        let container_id = container.container_id();
138        let recipes = guard
139            .get_typed_mut::<FurnaceContainer>(container_id)
140            .map(FurnaceContainer::take_recipes_used);
141        self.base.set_changed(guard);
142        if let Some(recipes) = recipes {
143            guard.run_unlocked(|| {
144                pop_furnace_experience(&player.get_world(), player.position(), recipes);
145            });
146        }
147        None
148    }
149}