Skip to main content

veloren_common/comp/body/
humanoid.rs

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