steel_core/player/player_inventory/
container.rs1use std::sync::LazyLock;
2
3use steel_registry::item_stack::ItemStack;
4use steel_utils::types::InteractionHand;
5
6use crate::inventory::{
7 container::Container,
8 equipment::{EntityEquipment, EquipmentSlot},
9};
10
11use super::core::PlayerInventory;
12
13const fn slot_to_equipment(slot: usize) -> Option<EquipmentSlot> {
15 match slot {
16 36 => Some(EquipmentSlot::Feet),
17 37 => Some(EquipmentSlot::Legs),
18 38 => Some(EquipmentSlot::Chest),
19 39 => Some(EquipmentSlot::Head),
20 40 => Some(EquipmentSlot::OffHand),
21 41 => Some(EquipmentSlot::Body),
22 42 => Some(EquipmentSlot::Saddle),
23 _ => None,
24 }
25}
26
27pub(crate) const fn armor_equipment(index: usize) -> EquipmentSlot {
34 slot_to_equipment(index).expect("armor sections only cover armor indices")
35}
36
37impl PlayerInventory {
38 pub fn apply_filled_result(
44 &mut self,
45 hand: InteractionHand,
46 mut result_stack: ItemStack,
47 has_infinite_materials: bool,
48 limit_creative_stack_size: bool,
49 ) -> ItemStack {
50 if limit_creative_stack_size && has_infinite_materials {
51 if !self.contains_stack(&result_stack) {
52 let _ = self.add(&mut result_stack);
53 }
54 return ItemStack::empty();
55 }
56
57 if !has_infinite_materials {
58 self.shrink_item_in_hand(hand, 1);
59 }
60
61 if self.get_item_in_hand(hand).is_empty() {
62 self.set_item_in_hand(hand, result_stack);
63 return ItemStack::empty();
64 }
65
66 let added = self.add(&mut result_stack);
67 if added || has_infinite_materials {
68 ItemStack::empty()
69 } else {
70 result_stack
71 }
72 }
73
74 pub fn add_or_return(&mut self, mut stack: ItemStack) -> ItemStack {
77 if self.add(&mut stack) {
78 ItemStack::empty()
79 } else {
80 stack
81 }
82 }
83}
84
85static EMPTY_ITEM: LazyLock<ItemStack> = LazyLock::new(ItemStack::empty);
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
90pub struct InvalidHotbarSlot;
91
92impl Container for PlayerInventory {
93 fn items(&self) -> &[ItemStack] {
94 &self.items
95 }
96
97 fn items_mut(&mut self) -> &mut [ItemStack] {
98 &mut self.items
99 }
100
101 fn get_container_size(&self) -> usize {
102 Self::CONTAINER_SIZE
103 }
104
105 fn add(&mut self, stack: &mut ItemStack) -> bool {
111 if stack.is_empty() {
112 return true;
113 }
114
115 let max_size = self.get_max_stack_size_for_item(stack);
116 let mut changed = false;
117
118 if stack.is_stackable() {
121 let selected = self.selected as usize;
122 for slot in [selected, Self::SLOT_OFFHAND] {
123 if stack.is_empty() {
124 if changed {
125 self.set_changed();
126 }
127 return true;
128 }
129 let existing = &mut self.items[slot];
130 if !existing.is_empty() && ItemStack::is_same_item_same_components(existing, stack)
131 {
132 let space = max_size - existing.count();
133 if space > 0 {
134 let to_add = stack.count().min(space);
135 existing.grow(to_add);
136 stack.shrink(to_add);
137 changed = true;
138 }
139 }
140 }
141
142 for slot in 0..Self::INVENTORY_SIZE {
143 if stack.is_empty() {
144 if changed {
145 self.set_changed();
146 }
147 return true;
148 }
149 if slot == selected {
150 continue;
151 }
152 let existing = &mut self.items[slot];
153 if !existing.is_empty() && ItemStack::is_same_item_same_components(existing, stack)
154 {
155 let space = max_size - existing.count();
156 if space > 0 {
157 let to_add = stack.count().min(space);
158 existing.grow(to_add);
159 stack.shrink(to_add);
160 changed = true;
161 }
162 }
163 }
164 }
165
166 for slot in 0..Self::INVENTORY_SIZE {
168 if stack.is_empty() {
169 if changed {
170 self.set_changed();
171 }
172 return true;
173 }
174 if self.items[slot].is_empty() {
175 let to_place = stack.count().min(max_size);
176 self.items[slot] = stack.split(to_place);
177 changed = true;
178 }
179 }
180
181 if changed {
182 self.set_changed();
183 }
184 stack.is_empty()
185 }
186
187 fn get_item(&self, slot: usize) -> &ItemStack {
188 if slot < Self::CONTAINER_SIZE {
189 &self.items[slot]
190 } else {
191 &EMPTY_ITEM
192 }
193 }
194
195 fn get_item_mut(&mut self, slot: usize) -> &mut ItemStack {
196 assert!(slot < Self::CONTAINER_SIZE, "Invalid slot index: {slot}");
197 &mut self.items[slot]
198 }
199
200 fn set_item(&mut self, slot: usize, stack: ItemStack) {
201 if slot == self.selected as usize {
202 let _ = EntityEquipment::set(self, EquipmentSlot::MainHand, stack);
203 return;
204 }
205 if let Some(equipment_slot) = slot_to_equipment(slot) {
206 let _ = EntityEquipment::set(self, equipment_slot, stack);
207 return;
208 }
209 if slot < Self::INVENTORY_SIZE {
210 self.items[slot] = stack;
211 }
212 self.set_changed();
213 }
214
215 fn is_empty(&self) -> bool {
216 self.items.iter().all(ItemStack::is_empty)
217 }
218
219 fn set_changed(&mut self) {
220 self.times_changed = self.times_changed.wrapping_add(1);
221 }
222
223 fn clear_content(&mut self) -> i32 {
224 let mut count = 0;
225 for item in &mut self.items {
226 count += item.count();
227 *item = ItemStack::empty();
228 }
229 if count > 0 {
230 self.set_changed();
231 }
232 count
233 }
234
235 fn clear_content_matching(&mut self, predicate: &mut dyn FnMut(&mut ItemStack) -> bool) -> i32 {
236 let mut count = 0;
237 for item in &mut self.items {
238 if predicate(item) {
239 count += item.count();
240 *item = ItemStack::empty();
241 }
242 }
243 if count > 0 {
244 self.set_changed();
245 }
246 count
247 }
248}