Skip to main content

steel_core/inventory/slots/
result_slot.rs

1use std::{mem, sync::Arc};
2
3use steel_registry::item_stack::ItemStack;
4use steel_utils::{DowncastType, DowncastTypeKey};
5
6use crate::{
7    inventory::{
8        lock::{ContainerLockGuard, ContainerRef},
9        slots::{ResultHandler, Slot, SlotStorage},
10    },
11    player::Player,
12};
13
14/// A fake Slot that contains the resulting item of f.e. a craft
15pub struct ResultSlot {
16    handler: Arc<dyn ResultHandler + Send + Sync>,
17    storage: SlotStorage,
18}
19
20// SAFETY: This key uniquely identifies Steel's `ResultSlot`.
21unsafe impl DowncastType for ResultSlot {
22    const TYPE_KEY: DowncastTypeKey = DowncastTypeKey::new("steel:slot/result");
23}
24
25impl ResultSlot {
26    /// Creates a new `ResultSlot` backed by the handler's result container.
27    pub fn new(handler: impl ResultHandler + 'static) -> Self {
28        let result_container = handler.result_container();
29        let storage =
30            SlotStorage::physical(result_container, 0).with_dependencies(handler.dependencies());
31        Self {
32            handler: Arc::new(handler),
33            storage,
34        }
35    }
36
37    /// Returns the result container this slot derived from its handler.
38    pub(crate) fn result_container(&self) -> &ContainerRef {
39        let Some((container, _)) = self.storage.physical_backing() else {
40            unreachable!("ResultSlot always has physical storage");
41        };
42        container
43    }
44}
45
46impl Slot for ResultSlot {
47    fn storage(&self) -> &SlotStorage {
48        &self.storage
49    }
50
51    fn get_item<'a>(&self, guard: &'a ContainerLockGuard) -> &'a ItemStack {
52        guard
53            .get(self.result_container().container_id())
54            .expect("failed to get item from result container")
55            .get_item(0)
56    }
57
58    fn get_item_mut<'a>(&self, guard: &'a mut ContainerLockGuard) -> &'a mut ItemStack {
59        guard
60            .get_mut(self.result_container().container_id())
61            .expect("failed to get item mutabily from result container")
62            .get_item_mut(0)
63    }
64
65    fn set_item(&self, guard: &mut ContainerLockGuard, stack: ItemStack) {
66        guard
67            .get_mut(self.result_container().container_id())
68            .expect("failed to get item mutabily from result container")
69            .set_item(0, stack);
70    }
71
72    fn get_max_stack_size(&self, guard: &ContainerLockGuard) -> i32 {
73        guard
74            .get(self.result_container().container_id())
75            .expect("result container not locked")
76            .get_max_stack_size()
77    }
78
79    fn set_changed(&self, guard: &mut ContainerLockGuard) {
80        guard
81            .get_mut(self.result_container().container_id())
82            .expect("result container not locked")
83            .set_changed();
84    }
85
86    fn get_container_slot(&self) -> usize {
87        0
88    }
89
90    fn on_take(
91        &self,
92        guard: &mut ContainerLockGuard,
93        _stack: &ItemStack,
94        player: &Player,
95    ) -> Option<ItemStack> {
96        self.handler.on_result_taken(guard, player)
97    }
98
99    fn remove(&self, guard: &mut ContainerLockGuard, _amount: i32) -> ItemStack {
100        mem::take(self.get_item_mut(guard))
101    }
102
103    fn is_fake(&self) -> bool {
104        true
105    }
106
107    fn allow_modification(&self, _guard: &ContainerLockGuard, _player: &Player) -> bool {
108        false
109    }
110
111    fn may_place(&self, _stack: &ItemStack) -> bool {
112        false
113    }
114
115    fn may_pickup(&self, guard: &ContainerLockGuard, player: &Player) -> bool {
116        self.handler.is_result_valid(guard, player)
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use steel_utils::locks::IntoShared;
123
124    use super::*;
125    use crate::inventory::container::SimpleContainer;
126
127    struct NoopResultHandler(ContainerRef);
128
129    impl ResultHandler for NoopResultHandler {
130        fn result_container(&self) -> ContainerRef {
131            self.0.clone()
132        }
133
134        fn dependencies(&self) -> Vec<ContainerRef> {
135            Vec::new()
136        }
137
138        fn update_result(&self, _guard: &mut ContainerLockGuard) {}
139
140        fn on_result_taken(
141            &self,
142            _guard: &mut ContainerLockGuard,
143            _player: &Player,
144        ) -> Option<ItemStack> {
145            None
146        }
147
148        fn is_result_valid(&self, _guard: &ContainerLockGuard, _player: &Player) -> bool {
149            true
150        }
151    }
152
153    #[test]
154    fn constructor_uses_the_handlers_result_container() {
155        let container = ContainerRef::from(SimpleContainer::new(1).into_shared());
156        let expected = container.container_id();
157
158        let slot = ResultSlot::new(NoopResultHandler(container));
159
160        assert_eq!(slot.storage().physical_key(), Some((expected, 0)));
161    }
162}