1use crate::item_stack::ItemStack;
4use steel_utils::entity_events::EntityStatus;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8pub enum EquipmentSlotType {
9 Hand,
11 HumanoidArmor,
13 AnimalArmor,
15 Saddle,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum EquipmentSlot {
24 MainHand,
26 OffHand,
28 Feet,
30 Legs,
32 Chest,
34 Head,
36 Body,
38 Saddle,
40}
41
42impl EquipmentSlot {
43 pub const ALL: [EquipmentSlot; 8] = [
45 EquipmentSlot::MainHand,
46 EquipmentSlot::OffHand,
47 EquipmentSlot::Feet,
48 EquipmentSlot::Legs,
49 EquipmentSlot::Chest,
50 EquipmentSlot::Head,
51 EquipmentSlot::Body,
52 EquipmentSlot::Saddle,
53 ];
54
55 pub const ARMOR_SLOTS: [EquipmentSlot; 4] = [
57 EquipmentSlot::Head,
58 EquipmentSlot::Chest,
59 EquipmentSlot::Legs,
60 EquipmentSlot::Feet,
61 ];
62
63 #[must_use]
65 pub const fn slot_type(self) -> EquipmentSlotType {
66 match self {
67 EquipmentSlot::MainHand | EquipmentSlot::OffHand => EquipmentSlotType::Hand,
68 EquipmentSlot::Feet
69 | EquipmentSlot::Legs
70 | EquipmentSlot::Chest
71 | EquipmentSlot::Head => EquipmentSlotType::HumanoidArmor,
72 EquipmentSlot::Body => EquipmentSlotType::AnimalArmor,
73 EquipmentSlot::Saddle => EquipmentSlotType::Saddle,
74 }
75 }
76
77 #[must_use]
79 pub const fn can_increase_experience(self) -> bool {
80 !matches!(self.slot_type(), EquipmentSlotType::Saddle)
81 }
82
83 #[must_use]
85 pub const fn index(self) -> usize {
86 match self {
87 EquipmentSlot::MainHand => 0,
88 EquipmentSlot::OffHand => 1,
89 EquipmentSlot::Feet => 2,
90 EquipmentSlot::Legs => 3,
91 EquipmentSlot::Chest => 4,
92 EquipmentSlot::Head => 5,
93 EquipmentSlot::Body => 6,
94 EquipmentSlot::Saddle => 7,
95 }
96 }
97
98 #[must_use]
100 pub const fn id(self) -> i32 {
101 match self {
102 EquipmentSlot::MainHand => 0,
103 EquipmentSlot::OffHand => 5,
104 EquipmentSlot::Feet => 1,
105 EquipmentSlot::Legs => 2,
106 EquipmentSlot::Chest => 3,
107 EquipmentSlot::Head => 4,
108 EquipmentSlot::Body => 6,
109 EquipmentSlot::Saddle => 7,
110 }
111 }
112
113 #[must_use]
115 pub const fn by_id(id: i32) -> Self {
116 match id {
117 1 => EquipmentSlot::Feet,
118 2 => EquipmentSlot::Legs,
119 3 => EquipmentSlot::Chest,
120 4 => EquipmentSlot::Head,
121 5 => EquipmentSlot::OffHand,
122 6 => EquipmentSlot::Body,
123 7 => EquipmentSlot::Saddle,
124 _ => EquipmentSlot::MainHand,
125 }
126 }
127
128 #[must_use]
132 pub const fn count_limit(self) -> i32 {
133 match self.slot_type() {
134 EquipmentSlotType::Hand => 0,
135 _ => 1,
136 }
137 }
138
139 pub fn limit(self, to_equip: &mut ItemStack) -> ItemStack {
143 let count_limit = self.count_limit();
144 if count_limit > 0 {
145 to_equip.split(count_limit)
146 } else {
147 to_equip.split(to_equip.count())
148 }
149 }
150
151 #[must_use]
153 pub const fn is_armor(self) -> bool {
154 matches!(
155 self.slot_type(),
156 EquipmentSlotType::HumanoidArmor | EquipmentSlotType::AnimalArmor
157 )
158 }
159
160 #[must_use]
162 pub const fn by_name(name: &str) -> Option<Self> {
163 match name {
164 "mainhand" => Some(EquipmentSlot::MainHand),
165 "offhand" => Some(EquipmentSlot::OffHand),
166 "feet" => Some(EquipmentSlot::Feet),
167 "legs" => Some(EquipmentSlot::Legs),
168 "chest" => Some(EquipmentSlot::Chest),
169 "head" => Some(EquipmentSlot::Head),
170 "body" => Some(EquipmentSlot::Body),
171 "saddle" => Some(EquipmentSlot::Saddle),
172 _ => None,
173 }
174 }
175
176 #[must_use]
178 pub const fn name(self) -> &'static str {
179 match self {
180 EquipmentSlot::MainHand => "mainhand",
181 EquipmentSlot::OffHand => "offhand",
182 EquipmentSlot::Feet => "feet",
183 EquipmentSlot::Legs => "legs",
184 EquipmentSlot::Chest => "chest",
185 EquipmentSlot::Head => "head",
186 EquipmentSlot::Body => "body",
187 EquipmentSlot::Saddle => "saddle",
188 }
189 }
190}
191
192impl From<EquipmentSlot> for EntityStatus {
193 fn from(value: EquipmentSlot) -> Self {
194 match value {
195 EquipmentSlot::MainHand => EntityStatus::MainhandBreak,
196 EquipmentSlot::OffHand => EntityStatus::OffhandBreak,
197 EquipmentSlot::Head => EntityStatus::HeadBreak,
198 EquipmentSlot::Chest => EntityStatus::ChestBreak,
199 EquipmentSlot::Legs => EntityStatus::LegsBreak,
200 EquipmentSlot::Feet => EntityStatus::FeetBreak,
201 EquipmentSlot::Body => EntityStatus::BodyBreak,
202 EquipmentSlot::Saddle => EntityStatus::SaddleBreak,
203 }
204 }
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
213pub enum EquipmentSlotGroup {
214 Any,
215 MainHand,
216 OffHand,
217 Hand,
218 Feet,
219 Legs,
220 Chest,
221 Head,
222 Armor,
223 Body,
224 Saddle,
225}
226
227impl EquipmentSlotGroup {
228 #[must_use]
229 pub const fn id(self) -> i32 {
230 match self {
231 Self::Any => 0,
232 Self::MainHand => 1,
233 Self::OffHand => 2,
234 Self::Hand => 3,
235 Self::Feet => 4,
236 Self::Legs => 5,
237 Self::Chest => 6,
238 Self::Head => 7,
239 Self::Armor => 8,
240 Self::Body => 9,
241 Self::Saddle => 10,
242 }
243 }
244
245 #[must_use]
246 pub const fn by_id(id: i32) -> Self {
247 match id {
248 1 => Self::MainHand,
249 2 => Self::OffHand,
250 3 => Self::Hand,
251 4 => Self::Feet,
252 5 => Self::Legs,
253 6 => Self::Chest,
254 7 => Self::Head,
255 8 => Self::Armor,
256 9 => Self::Body,
257 10 => Self::Saddle,
258 _ => Self::Any,
259 }
260 }
261
262 #[must_use]
263 pub const fn by_name(name: &str) -> Option<Self> {
264 match name {
265 "any" => Some(Self::Any),
266 "mainhand" | "main_hand" => Some(Self::MainHand),
267 "offhand" | "off_hand" => Some(Self::OffHand),
268 "hand" => Some(Self::Hand),
269 "feet" => Some(Self::Feet),
270 "legs" => Some(Self::Legs),
271 "chest" => Some(Self::Chest),
272 "head" => Some(Self::Head),
273 "armor" => Some(Self::Armor),
274 "body" => Some(Self::Body),
275 "saddle" => Some(Self::Saddle),
276 _ => None,
277 }
278 }
279
280 #[must_use]
281 pub const fn name(self) -> &'static str {
282 match self {
283 Self::Any => "any",
284 Self::MainHand => "mainhand",
285 Self::OffHand => "offhand",
286 Self::Hand => "hand",
287 Self::Feet => "feet",
288 Self::Legs => "legs",
289 Self::Chest => "chest",
290 Self::Head => "head",
291 Self::Armor => "armor",
292 Self::Body => "body",
293 Self::Saddle => "saddle",
294 }
295 }
296
297 #[must_use]
298 pub const fn as_str(self) -> &'static str {
299 self.name()
300 }
301
302 #[must_use]
303 pub const fn by_slot(slot: EquipmentSlot) -> Self {
304 match slot {
305 EquipmentSlot::MainHand => Self::MainHand,
306 EquipmentSlot::OffHand => Self::OffHand,
307 EquipmentSlot::Feet => Self::Feet,
308 EquipmentSlot::Legs => Self::Legs,
309 EquipmentSlot::Chest => Self::Chest,
310 EquipmentSlot::Head => Self::Head,
311 EquipmentSlot::Body => Self::Body,
312 EquipmentSlot::Saddle => Self::Saddle,
313 }
314 }
315
316 #[must_use]
317 pub const fn test(self, slot: EquipmentSlot) -> bool {
318 match self {
319 Self::Any => true,
320 Self::MainHand => matches!(slot, EquipmentSlot::MainHand),
321 Self::OffHand => matches!(slot, EquipmentSlot::OffHand),
322 Self::Hand => matches!(slot.slot_type(), EquipmentSlotType::Hand),
323 Self::Feet => matches!(slot, EquipmentSlot::Feet),
324 Self::Legs => matches!(slot, EquipmentSlot::Legs),
325 Self::Chest => matches!(slot, EquipmentSlot::Chest),
326 Self::Head => matches!(slot, EquipmentSlot::Head),
327 Self::Armor => slot.is_armor(),
328 Self::Body => matches!(slot, EquipmentSlot::Body),
329 Self::Saddle => matches!(slot, EquipmentSlot::Saddle),
330 }
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::EquipmentSlot;
337
338 #[test]
339 fn equipment_slot_protocol_ids_match_vanilla() {
340 let slots = [
341 (EquipmentSlot::MainHand, 0),
342 (EquipmentSlot::Feet, 1),
343 (EquipmentSlot::Legs, 2),
344 (EquipmentSlot::Chest, 3),
345 (EquipmentSlot::Head, 4),
346 (EquipmentSlot::OffHand, 5),
347 (EquipmentSlot::Body, 6),
348 (EquipmentSlot::Saddle, 7),
349 ];
350
351 for (slot, id) in slots {
352 assert_eq!(slot.id(), id);
353 assert_eq!(EquipmentSlot::by_id(id), slot);
354 }
355 assert_eq!(EquipmentSlot::by_id(-1), EquipmentSlot::MainHand);
356 assert_eq!(EquipmentSlot::by_id(8), EquipmentSlot::MainHand);
357 }
358
359 #[test]
360 fn only_saddle_slot_does_not_increase_experience() {
361 for slot in EquipmentSlot::ALL {
362 assert_eq!(
363 slot.can_increase_experience(),
364 slot != EquipmentSlot::Saddle
365 );
366 }
367 }
368}