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
75static EMPTY_ITEM: LazyLock<ItemStack> = LazyLock::new(ItemStack::empty);
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct InvalidHotbarSlot;
81
82impl Container for PlayerInventory {
83 fn items(&self) -> &[ItemStack] {
84 &self.items
85 }
86
87 fn items_mut(&mut self) -> &mut [ItemStack] {
88 &mut self.items
89 }
90
91 fn get_container_size(&self) -> usize {
92 Self::CONTAINER_SIZE
93 }
94
95 fn add(&mut self, stack: &mut ItemStack) -> bool {
101 if stack.is_empty() {
102 return true;
103 }
104
105 let max_size = self.get_max_stack_size_for_item(stack);
106 let mut changed = false;
107
108 if stack.is_stackable() {
111 let selected = self.selected as usize;
112 for slot in [selected, Self::SLOT_OFFHAND] {
113 if stack.is_empty() {
114 if changed {
115 self.set_changed();
116 }
117 return true;
118 }
119 let existing = &mut self.items[slot];
120 if !existing.is_empty() && ItemStack::is_same_item_same_components(existing, stack)
121 {
122 let space = max_size - existing.count();
123 if space > 0 {
124 let to_add = stack.count().min(space);
125 existing.grow(to_add);
126 stack.shrink(to_add);
127 changed = true;
128 }
129 }
130 }
131
132 for slot in 0..Self::INVENTORY_SIZE {
133 if stack.is_empty() {
134 if changed {
135 self.set_changed();
136 }
137 return true;
138 }
139 if slot == selected {
140 continue;
141 }
142 let existing = &mut self.items[slot];
143 if !existing.is_empty() && ItemStack::is_same_item_same_components(existing, stack)
144 {
145 let space = max_size - existing.count();
146 if space > 0 {
147 let to_add = stack.count().min(space);
148 existing.grow(to_add);
149 stack.shrink(to_add);
150 changed = true;
151 }
152 }
153 }
154 }
155
156 for slot in 0..Self::INVENTORY_SIZE {
158 if stack.is_empty() {
159 if changed {
160 self.set_changed();
161 }
162 return true;
163 }
164 if self.items[slot].is_empty() {
165 let to_place = stack.count().min(max_size);
166 self.items[slot] = stack.split(to_place);
167 changed = true;
168 }
169 }
170
171 if changed {
172 self.set_changed();
173 }
174 stack.is_empty()
175 }
176
177 fn get_item(&self, slot: usize) -> &ItemStack {
178 if slot < Self::CONTAINER_SIZE {
179 &self.items[slot]
180 } else {
181 &EMPTY_ITEM
182 }
183 }
184
185 fn get_item_mut(&mut self, slot: usize) -> &mut ItemStack {
186 assert!(slot < Self::CONTAINER_SIZE, "Invalid slot index: {slot}");
187 &mut self.items[slot]
188 }
189
190 fn set_item(&mut self, slot: usize, stack: ItemStack) {
191 if slot == self.selected as usize {
192 let _ = EntityEquipment::set(self, EquipmentSlot::MainHand, stack);
193 return;
194 }
195 if let Some(equipment_slot) = slot_to_equipment(slot) {
196 let _ = EntityEquipment::set(self, equipment_slot, stack);
197 return;
198 }
199 if slot < Self::INVENTORY_SIZE {
200 self.items[slot] = stack;
201 }
202 self.set_changed();
203 }
204
205 fn is_empty(&self) -> bool {
206 self.items.iter().all(ItemStack::is_empty)
207 }
208
209 fn set_changed(&mut self) {
210 self.times_changed = self.times_changed.wrapping_add(1);
211 }
212
213 fn clear_content(&mut self) -> i32 {
214 let mut count = 0;
215 for item in &mut self.items {
216 count += item.count();
217 *item = ItemStack::empty();
218 }
219 if count > 0 {
220 self.set_changed();
221 }
222 count
223 }
224
225 fn clear_content_matching(&mut self, predicate: &mut dyn FnMut(&mut ItemStack) -> bool) -> i32 {
226 let mut count = 0;
227 for item in &mut self.items {
228 if predicate(item) {
229 count += item.count();
230 *item = ItemStack::empty();
231 }
232 }
233 if count > 0 {
234 self.set_changed();
235 }
236 count
237 }
238}