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