veloren_common/comp/body/
humanoid.rs

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