veloren_common/comp/body/
humanoid.rs

1use crate::{make_case_elim, make_proj_elim};
2use rand::{Rng, seq::SliceRandom, thread_rng};
3use serde::{Deserialize, Serialize};
4use serde_repr::{Deserialize_repr, Serialize_repr};
5use strum::{EnumIter, IntoEnumIterator};
6
7make_proj_elim!(
8    body,
9    #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10    pub struct Body {
11        pub species: Species,
12        pub body_type: BodyType,
13        #[typed(pure)]
14        pub hair_style: u8,
15        #[typed(pure)]
16        pub beard: u8,
17        #[typed(pure)]
18        pub eyes: u8,
19        #[typed(pure)]
20        pub accessory: u8,
21        #[typed(pure)]
22        pub hair_color: u8,
23        #[typed(pure)]
24        pub skin: u8,
25        #[typed(pure)]
26        pub eye_color: u8,
27    }
28);
29
30impl Body {
31    pub fn iter() -> impl Iterator<Item = Self> {
32        // I'm too lazy to figure out decorations and I don't think we need that
33        Species::iter().flat_map(move |species| {
34            BodyType::iter().map(move |body_type| Self {
35                species,
36                body_type,
37                hair_style: 0,
38                beard: 0,
39                accessory: 0,
40                hair_color: 0,
41                skin: 0,
42                eye_color: 0,
43                eyes: 0,
44            })
45        })
46    }
47
48    pub fn random() -> Self {
49        let mut rng = thread_rng();
50        let species = *ALL_SPECIES.choose(&mut rng).unwrap();
51        Self::random_with(&mut rng, &species)
52    }
53
54    #[inline]
55    pub fn random_with(rng: &mut impl Rng, &species: &Species) -> Self {
56        let body_type = *ALL_BODY_TYPES.choose(rng).unwrap();
57        Self {
58            species,
59            body_type,
60            hair_style: rng.gen_range(0..species.num_hair_styles(body_type)),
61            beard: rng.gen_range(0..species.num_beards(body_type)),
62            accessory: rng.gen_range(0..species.num_accessories(body_type)),
63            hair_color: rng.gen_range(0..species.num_hair_colors()),
64            skin: rng.gen_range(0..species.num_skin_colors()),
65            eye_color: rng.gen_range(0..species.num_eye_colors()),
66            eyes: rng.gen_range(0..1), /* TODO Add a way to set specific head-segments for NPCs
67                                        * with the default being a random one */
68        }
69    }
70
71    pub fn validate(&mut self) {
72        self.hair_style = self
73            .hair_style
74            .min(self.species.num_hair_styles(self.body_type) - 1);
75        self.beard = self.beard.min(self.species.num_beards(self.body_type) - 1);
76        self.hair_color = self.hair_color.min(self.species.num_hair_colors() - 1);
77        self.skin = self.skin.min(self.species.num_skin_colors() - 1);
78        self.eyes = self.eyes.min(self.species.num_eyes(self.body_type) - 1);
79        self.eye_color = self.eye_color.min(self.species.num_eye_colors() - 1);
80        self.accessory = self
81            .accessory
82            .min(self.species.num_accessories(self.body_type) - 1);
83    }
84
85    pub fn height(&self) -> f32 { (20.0 / 9.0) * self.scaler() }
86
87    pub fn scaler(&self) -> f32 {
88        match (self.species, self.body_type) {
89            (Species::Orc, BodyType::Male) => 0.91,
90            (Species::Orc, BodyType::Female) => 0.81,
91            (Species::Human, BodyType::Male) => 0.81,
92            (Species::Human, BodyType::Female) => 0.76,
93            (Species::Elf, BodyType::Male) => 0.82,
94            (Species::Elf, BodyType::Female) => 0.76,
95            (Species::Dwarf, BodyType::Male) => 0.67,
96            (Species::Dwarf, BodyType::Female) => 0.62,
97            (Species::Draugr, BodyType::Male) => 0.78,
98            (Species::Draugr, BodyType::Female) => 0.72,
99            (Species::Danari, BodyType::Male) => 0.56,
100            (Species::Danari, BodyType::Female) => 0.56,
101        }
102    }
103}
104
105impl From<Body> for super::Body {
106    fn from(body: Body) -> Self { super::Body::Humanoid(body) }
107}
108
109make_case_elim!(
110    species,
111    #[derive(
112        Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumIter,
113    )]
114    #[repr(u32)]
115    pub enum Species {
116        Danari = 0,
117        Dwarf = 1,
118        Elf = 2,
119        Human = 3,
120        Orc = 4,
121        Draugr = 5,
122    }
123);
124
125/// Data representing per-species generic data.
126#[derive(Clone, Debug, Serialize, Deserialize)]
127pub struct AllSpecies<SpeciesMeta> {
128    pub danari: SpeciesMeta,
129    pub dwarf: SpeciesMeta,
130    pub elf: SpeciesMeta,
131    pub human: SpeciesMeta,
132    pub orc: SpeciesMeta,
133    pub draugr: SpeciesMeta,
134}
135
136impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
137    type Output = SpeciesMeta;
138
139    #[inline]
140    fn index(&self, &index: &'a Species) -> &Self::Output {
141        match index {
142            Species::Danari => &self.danari,
143            Species::Dwarf => &self.dwarf,
144            Species::Elf => &self.elf,
145            Species::Human => &self.human,
146            Species::Orc => &self.orc,
147            Species::Draugr => &self.draugr,
148        }
149    }
150}
151
152pub const ALL_SPECIES: [Species; 6] = [
153    Species::Danari,
154    Species::Dwarf,
155    Species::Elf,
156    Species::Human,
157    Species::Orc,
158    Species::Draugr,
159];
160
161impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
162    type IntoIter = std::iter::Copied<std::slice::Iter<'static, Self::Item>>;
163    type Item = Species;
164
165    fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
166}
167
168// Skin colors
169pub const DANARI_SKIN_COLORS: [Skin; 7] = [
170    Skin::DanariOne,
171    Skin::DanariTwo,
172    Skin::DanariThree,
173    Skin::DanariFour,
174    Skin::DanariFive,
175    Skin::DanariSix,
176    Skin::DanariSeven,
177];
178pub const DWARF_SKIN_COLORS: [Skin; 14] = [
179    Skin::DwarfOne,
180    Skin::DwarfTwo,
181    Skin::DwarfThree,
182    Skin::DwarfFour,
183    Skin::DwarfFive,
184    Skin::DwarfSix,
185    Skin::DwarfSeven,
186    Skin::DwarfEight,
187    Skin::DwarfNine,
188    Skin::DwarfTen,
189    Skin::DwarfEleven,
190    Skin::DwarfTwelve,
191    Skin::DwarfThirteen,
192    Skin::DwarfFourteen,
193];
194pub const ELF_SKIN_COLORS: [Skin; 18] = [
195    Skin::ElfOne,
196    Skin::ElfTwo,
197    Skin::ElfThree,
198    Skin::ElfFour,
199    Skin::ElfFive,
200    Skin::ElfSix,
201    Skin::ElfSeven,
202    Skin::ElfEight,
203    Skin::ElfNine,
204    Skin::ElfTen,
205    Skin::ElfEleven,
206    Skin::ElfTwelve,
207    Skin::ElfThirteen,
208    Skin::ElfFourteen,
209    Skin::ElfFifteen,
210    Skin::ElfSixteen,
211    Skin::ElfSeventeen,
212    Skin::ElfEighteen,
213];
214pub const HUMAN_SKIN_COLORS: [Skin; 18] = [
215    Skin::HumanOne,
216    Skin::HumanTwo,
217    Skin::HumanThree,
218    Skin::HumanFour,
219    Skin::HumanFive,
220    Skin::HumanSix,
221    Skin::HumanSeven,
222    Skin::HumanEight,
223    Skin::HumanNine,
224    Skin::HumanTen,
225    Skin::HumanEleven,
226    Skin::HumanTwelve,
227    Skin::HumanThirteen,
228    Skin::HumanFourteen,
229    Skin::HumanFifteen,
230    Skin::HumanSixteen,
231    Skin::HumanSeventeen,
232    Skin::HumanEighteen,
233];
234pub const ORC_SKIN_COLORS: [Skin; 8] = [
235    Skin::OrcOne,
236    Skin::OrcTwo,
237    Skin::OrcThree,
238    Skin::OrcFour,
239    Skin::OrcFive,
240    Skin::OrcSix,
241    Skin::OrcSeven,
242    Skin::OrcEight,
243];
244pub const DRAUGR_SKIN_COLORS: [Skin; 9] = [
245    Skin::DraugrOne,
246    Skin::DraugrTwo,
247    Skin::DraugrThree,
248    Skin::DraugrFour,
249    Skin::DraugrFive,
250    Skin::DraugrSix,
251    Skin::DraugrSeven,
252    Skin::DraugrEight,
253    Skin::DraugrNine,
254];
255
256// Eye colors
257pub const DANARI_EYE_COLORS: [EyeColor; 4] = [
258    EyeColor::EmeraldGreen,
259    EyeColor::LoyalBrown,
260    EyeColor::RegalPurple,
261    EyeColor::ViciousRed,
262];
263pub const DWARF_EYE_COLORS: [EyeColor; 6] = [
264    EyeColor::AmberYellow,
265    EyeColor::CornflowerBlue,
266    EyeColor::LoyalBrown,
267    EyeColor::NobleBlue,
268    EyeColor::PineGreen,
269    EyeColor::RustBrown,
270];
271pub const ELF_EYE_COLORS: [EyeColor; 7] = [
272    EyeColor::AmberYellow,
273    EyeColor::BrightBrown,
274    EyeColor::EmeraldGreen,
275    EyeColor::NobleBlue,
276    EyeColor::SapphireBlue,
277    EyeColor::RegalPurple,
278    EyeColor::RubyRed,
279];
280pub const HUMAN_EYE_COLORS: [EyeColor; 5] = [
281    EyeColor::NobleBlue,
282    EyeColor::CornflowerBlue,
283    EyeColor::CuriousGreen,
284    EyeColor::LoyalBrown,
285    EyeColor::VigorousBlack,
286];
287pub const ORC_EYE_COLORS: [EyeColor; 6] = [
288    EyeColor::AmberYellow,
289    EyeColor::CornflowerBlue,
290    EyeColor::ExoticPurple,
291    EyeColor::LoyalBrown,
292    EyeColor::PineGreen,
293    EyeColor::RustBrown,
294];
295pub const DRAUGR_EYE_COLORS: [EyeColor; 6] = [
296    EyeColor::FrozenBlue,
297    EyeColor::GhastlyYellow,
298    EyeColor::MagicPurple,
299    EyeColor::PumpkinOrange,
300    EyeColor::ToxicGreen,
301    EyeColor::ViciousRed,
302];
303
304impl Species {
305    fn skin_colors(self) -> &'static [Skin] {
306        match self {
307            Species::Danari => &DANARI_SKIN_COLORS,
308            Species::Dwarf => &DWARF_SKIN_COLORS,
309            Species::Elf => &ELF_SKIN_COLORS,
310            Species::Human => &HUMAN_SKIN_COLORS,
311            Species::Orc => &ORC_SKIN_COLORS,
312            Species::Draugr => &DRAUGR_SKIN_COLORS,
313        }
314    }
315
316    fn eye_colors(self) -> &'static [EyeColor] {
317        match self {
318            Species::Danari => &DANARI_EYE_COLORS,
319            Species::Dwarf => &DWARF_EYE_COLORS,
320            Species::Elf => &ELF_EYE_COLORS,
321            Species::Human => &HUMAN_EYE_COLORS,
322            Species::Orc => &ORC_EYE_COLORS,
323            Species::Draugr => &DRAUGR_EYE_COLORS,
324        }
325    }
326
327    /// FIXME: This is a hack!  The only reason we need to do this is because
328    /// hair colors are currently just indices into an array, not enum
329    /// variants.  Once we have proper variants for hair colors, we won't
330    /// need to do this anymore, since we will use locally defined arrays to
331    /// represent per-species stuff (or have some other solution for validity).
332    pub fn num_hair_colors(self) -> u8 {
333        match self {
334            Species::Danari => 17,
335            Species::Dwarf => 21,
336            Species::Elf => 24,
337            Species::Human => 22,
338            Species::Orc => 13,
339            Species::Draugr => 25,
340        }
341    }
342
343    pub fn skin_color(self, val: u8) -> Skin {
344        self.skin_colors()
345            .get(val as usize)
346            .copied()
347            .unwrap_or(Skin::HumanThree)
348    }
349
350    pub fn num_skin_colors(self) -> u8 { self.skin_colors().len() as u8 }
351
352    pub fn eye_color(self, val: u8) -> EyeColor {
353        self.eye_colors()
354            .get(val as usize)
355            .copied()
356            .unwrap_or(EyeColor::NobleBlue)
357    }
358
359    pub fn num_eye_colors(self) -> u8 { self.eye_colors().len() as u8 }
360
361    pub fn num_hair_styles(self, body_type: BodyType) -> u8 {
362        match (self, body_type) {
363            (Species::Danari, BodyType::Female) => 15,
364            (Species::Danari, BodyType::Male) => 15,
365            (Species::Dwarf, BodyType::Female) => 15,
366            (Species::Dwarf, BodyType::Male) => 15,
367            (Species::Elf, BodyType::Female) => 22,
368            (Species::Elf, BodyType::Male) => 15,
369            (Species::Human, BodyType::Female) => 20,
370            (Species::Human, BodyType::Male) => 21,
371            (Species::Orc, BodyType::Female) => 15,
372            (Species::Orc, BodyType::Male) => 15,
373            (Species::Draugr, BodyType::Female) => 15,
374            (Species::Draugr, BodyType::Male) => 15,
375        }
376    }
377
378    pub fn num_accessories(self, body_type: BodyType) -> u8 {
379        match (self, body_type) {
380            (Species::Danari, BodyType::Female) => 7,
381            (Species::Danari, BodyType::Male) => 7,
382            (Species::Dwarf, BodyType::Female) => 7,
383            (Species::Dwarf, BodyType::Male) => 7,
384            (Species::Elf, BodyType::Female) => 6,
385            (Species::Elf, BodyType::Male) => 5,
386            (Species::Human, BodyType::Female) => 1,
387            (Species::Human, BodyType::Male) => 1,
388            (Species::Orc, BodyType::Female) => 9,
389            (Species::Orc, BodyType::Male) => 12,
390            (Species::Draugr, BodyType::Female) => 2,
391            (Species::Draugr, BodyType::Male) => 2,
392        }
393    }
394
395    pub fn num_eyebrows(self, _body_type: BodyType) -> u8 { 1 }
396
397    pub fn num_eyes(self, body_type: BodyType) -> u8 {
398        match (self, body_type) {
399            (Species::Danari, BodyType::Female) => 6,
400            (Species::Danari, BodyType::Male) => 8,
401            (Species::Dwarf, BodyType::Female) => 6,
402            (Species::Dwarf, BodyType::Male) => 9,
403            (Species::Elf, BodyType::Female) => 6,
404            (Species::Elf, BodyType::Male) => 8,
405            (Species::Human, BodyType::Female) => 6,
406            (Species::Human, BodyType::Male) => 7,
407            (Species::Orc, BodyType::Female) => 6,
408            (Species::Orc, BodyType::Male) => 2,
409            (Species::Draugr, BodyType::Female) => 3,
410            (Species::Draugr, BodyType::Male) => 8,
411        }
412    }
413
414    pub fn num_beards(self, body_type: BodyType) -> u8 {
415        match (self, body_type) {
416            (Species::Danari, BodyType::Female) => 1,
417            (Species::Danari, BodyType::Male) => 16,
418            (Species::Dwarf, BodyType::Female) => 1,
419            (Species::Dwarf, BodyType::Male) => 23,
420            (Species::Elf, BodyType::Female) => 1,
421            (Species::Elf, BodyType::Male) => 8,
422            (Species::Human, BodyType::Female) => 1,
423            (Species::Human, BodyType::Male) => 10,
424            (Species::Orc, BodyType::Female) => 1,
425            (Species::Orc, BodyType::Male) => 7,
426            (Species::Draugr, BodyType::Female) => 1,
427            (Species::Draugr, BodyType::Male) => 6,
428        }
429    }
430}
431
432make_case_elim!(
433    body_type,
434    #[derive(
435        Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, EnumIter,
436    )]
437    #[repr(u32)]
438    pub enum BodyType {
439        Female = 0,
440        Male = 1,
441    }
442);
443
444pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];
445
446make_case_elim!(
447    eye_color,
448    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
449    #[repr(u32)]
450    pub enum EyeColor {
451        AmberOrange = 0,
452        AmberYellow = 1,
453        BrightBrown = 2,
454        CornflowerBlue = 3,
455        CuriousGreen = 4,
456        EmeraldGreen = 5,
457        ExoticPurple = 6,
458        FrozenBlue = 7,
459        GhastlyYellow = 8,
460        LoyalBrown = 9,
461        MagicPurple = 10,
462        NobleBlue = 11,
463        PineGreen = 12,
464        PumpkinOrange = 13,
465        RubyRed = 14,
466        RegalPurple = 15,
467        RustBrown = 16,
468        SapphireBlue = 17,
469        SulfurYellow = 18,
470        ToxicGreen = 19,
471        ViciousRed = 20,
472        VigorousBlack = 21,
473    }
474);
475
476make_case_elim!(
477    skin,
478    #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
479    #[repr(u32)]
480    pub enum Skin {
481        // Humans
482        HumanOne = 0,
483        HumanTwo = 1,
484        HumanThree = 2,
485        HumanFour = 3,
486        HumanFive = 4,
487        HumanSix = 5,
488        HumanSeven = 6,
489        HumanEight = 7,
490        HumanNine = 8,
491        HumanTen = 9,
492        HumanEleven = 10,
493        HumanTwelve = 11,
494        HumanThirteen = 12,
495        HumanFourteen = 13,
496        HumanFifteen = 14,
497        HumanSixteen = 15,
498        HumanSeventeen = 16,
499        HumanEighteen = 17,
500        // Dwarves
501        DwarfOne = 18,
502        DwarfTwo = 19,
503        DwarfThree = 20,
504        DwarfFour = 21,
505        DwarfFive = 22,
506        DwarfSix = 23,
507        DwarfSeven = 24,
508        DwarfEight = 25,
509        DwarfNine = 26,
510        DwarfTen = 27,
511        DwarfEleven = 28,
512        DwarfTwelve = 29,
513        DwarfThirteen = 30,
514        DwarfFourteen = 31,
515        // Elves
516        ElfOne = 32,
517        ElfTwo = 33,
518        ElfThree = 34,
519        ElfFour = 35,
520        ElfFive = 36,
521        ElfSix = 37,
522        ElfSeven = 38,
523        ElfEight = 39,
524        ElfNine = 40,
525        ElfTen = 41,
526        ElfEleven = 42,
527        ElfTwelve = 43,
528        ElfThirteen = 44,
529        ElfFourteen = 45,
530        ElfFifteen = 46,
531        ElfSixteen = 47,
532        ElfSeventeen = 48,
533        ElfEighteen = 49,
534        // Orcs
535        OrcOne = 50,
536        OrcTwo = 51,
537        OrcThree = 52,
538        OrcFour = 53,
539        OrcFive = 54,
540        OrcSix = 55,
541        OrcSeven = 56,
542        OrcEight = 57,
543        // Danaris
544        DanariOne = 58,
545        DanariTwo = 59,
546        DanariThree = 60,
547        DanariFour = 61,
548        DanariFive = 62,
549        DanariSix = 63,
550        DanariSeven = 64,
551        // Draugrs
552        DraugrOne = 65,
553        DraugrTwo = 66,
554        DraugrThree = 67,
555        DraugrFour = 68,
556        DraugrFive = 69,
557        DraugrSix = 70,
558        DraugrSeven = 71,
559        DraugrEight = 72,
560        DraugrNine = 73,
561    }
562);