use crate::make_case_elim;
use rand::{seq::SliceRandom, thread_rng, Rng};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Body {
pub species: Species,
pub body_type: BodyType,
pub hair_style: u8,
pub beard: u8,
pub eyes: u8,
pub accessory: u8,
pub hair_color: u8,
pub skin: u8,
pub eye_color: u8,
}
impl Body {
pub fn random() -> Self {
let mut rng = thread_rng();
let species = *(&ALL_SPECIES).choose(&mut rng).unwrap();
Self::random_with(&mut rng, &species)
}
#[inline]
pub fn random_with(rng: &mut impl Rng, &species: &Species) -> Self {
let body_type = *(&ALL_BODY_TYPES).choose(rng).unwrap();
Self {
species,
body_type,
hair_style: rng.gen_range(0, species.num_hair_styles(body_type)),
beard: rng.gen_range(0, species.num_beards(body_type)),
accessory: rng.gen_range(0, species.num_accessories(body_type)),
hair_color: rng.gen_range(0, species.num_hair_colors()) as u8,
skin: rng.gen_range(0, species.num_skin_colors()) as u8,
eye_color: rng.gen_range(0, species.num_eye_colors()) as u8,
eyes: rng.gen_range(0, 1), /* TODO Add a way to set specific head-segments for NPCs
* with the default being a random one */
}
}
pub fn validate(&mut self) {
self.hair_style = self
.hair_style
.min(self.species.num_hair_styles(self.body_type) - 1);
self.beard = self.beard.min(self.species.num_beards(self.body_type) - 1);
self.hair_color = self.hair_color.min(self.species.num_hair_colors() - 1);
self.skin = self.skin.min(self.species.num_skin_colors() - 1);
self.eyes = self.eyes.min(self.species.num_eyes(self.body_type) - 1);
self.eye_color = self.hair_style.min(self.species.num_eye_colors() - 1);
self.accessory = self
.accessory
.min(self.species.num_accessories(self.body_type) - 1);
}
}
impl From
for super::Body {
fn from(body: Body) -> Self { super::Body::Humanoid(body) }
}
make_case_elim!(
species,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum Species {
Danari = 0,
Dwarf = 1,
Elf = 2,
Human = 3,
Orc = 4,
Undead = 5,
}
);
/// Data representing per-species generic data.
///
/// NOTE: Deliberately don't (yet?) implement serialize.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct AllSpecies {
pub danari: SpeciesMeta,
pub dwarf: SpeciesMeta,
pub elf: SpeciesMeta,
pub human: SpeciesMeta,
pub orc: SpeciesMeta,
pub undead: SpeciesMeta,
}
impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies {
type Output = SpeciesMeta;
#[inline]
fn index(&self, &index: &'a Species) -> &Self::Output {
match index {
Species::Danari => &self.danari,
Species::Dwarf => &self.dwarf,
Species::Elf => &self.elf,
Species::Human => &self.human,
Species::Orc => &self.orc,
Species::Undead => &self.undead,
}
}
}
pub const ALL_SPECIES: [Species; 6] = [
Species::Danari,
Species::Dwarf,
Species::Elf,
Species::Human,
Species::Orc,
Species::Undead,
];
impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies {
type IntoIter = std::iter::Copied>;
type Item = Species;
fn into_iter(self) -> Self::IntoIter { ALL_SPECIES.iter().copied() }
}
// Skin colors
pub const DANARI_SKIN_COLORS: [Skin; 4] = [
Skin::DanariOne,
Skin::DanariTwo,
Skin::DanariThree,
Skin::DanariFour,
];
pub const DWARF_SKIN_COLORS: [Skin; 14] = [
Skin::Skin1,
Skin::Skin2,
Skin::Skin3,
Skin::Skin4,
Skin::Skin5,
Skin::Skin6,
Skin::Skin7,
Skin::Skin8,
Skin::Skin9,
Skin::Skin10,
Skin::Skin11,
Skin::Skin12,
Skin::Iron,
Skin::Steel,
];
pub const ELF_SKIN_COLORS: [Skin; 13] = [
Skin::Skin1,
Skin::Skin2,
Skin::Skin3,
Skin::Skin5,
Skin::Skin6,
Skin::Skin7,
Skin::Skin8,
Skin::Skin9,
Skin::Skin10,
Skin::Skin11,
Skin::Skin12,
Skin::ElfOne,
Skin::ElfTwo,
//Skin::ElfThree,
];
pub const HUMAN_SKIN_COLORS: [Skin; 18] = [
Skin::Skin1,
Skin::Skin2,
Skin::Skin3,
Skin::Skin4,
Skin::Skin5,
Skin::Skin6,
Skin::Skin7,
Skin::Skin8,
Skin::Skin9,
Skin::Skin10,
Skin::Skin11,
Skin::Skin12,
Skin::Skin13,
Skin::Skin14,
Skin::Skin15,
Skin::Skin16,
Skin::Skin17,
Skin::Skin18,
];
pub const ORC_SKIN_COLORS: [Skin; 4] = [Skin::OrcOne, Skin::OrcTwo, Skin::OrcThree, Skin::OrcFour];
pub const UNDEAD_SKIN_COLORS: [Skin; 3] = [Skin::UndeadOne, Skin::UndeadTwo, Skin::UndeadThree];
// Eye colors
pub const DANARI_EYE_COLORS: [EyeColor; 3] = [
EyeColor::CuriousGreen,
EyeColor::LoyalBrown,
EyeColor::ViciousRed,
];
pub const DWARF_EYE_COLORS: [EyeColor; 4] = [
EyeColor::CuriousGreen,
EyeColor::LoyalBrown,
EyeColor::NobleBlue,
EyeColor::CornflowerBlue,
];
pub const ELF_EYE_COLORS: [EyeColor; 4] = [
EyeColor::NobleBlue,
EyeColor::CornflowerBlue,
EyeColor::CuriousGreen,
EyeColor::LoyalBrown,
];
pub const HUMAN_EYE_COLORS: [EyeColor; 4] = [
EyeColor::NobleBlue,
EyeColor::CornflowerBlue,
EyeColor::CuriousGreen,
EyeColor::LoyalBrown,
];
pub const ORC_EYE_COLORS: [EyeColor; 5] = [
EyeColor::LoyalBrown,
EyeColor::ExoticPurple,
EyeColor::AmberOrange,
EyeColor::PineGreen,
EyeColor::CornflowerBlue,
];
pub const UNDEAD_EYE_COLORS: [EyeColor; 5] = [
EyeColor::ViciousRed,
EyeColor::PumpkinOrange,
EyeColor::GhastlyYellow,
EyeColor::MagicPurple,
EyeColor::ToxicGreen,
];
impl Species {
fn skin_colors(self) -> &'static [Skin] {
match self {
Species::Danari => &DANARI_SKIN_COLORS,
Species::Dwarf => &DWARF_SKIN_COLORS,
Species::Elf => &ELF_SKIN_COLORS,
Species::Human => &HUMAN_SKIN_COLORS,
Species::Orc => &ORC_SKIN_COLORS,
Species::Undead => &UNDEAD_SKIN_COLORS,
}
}
fn eye_colors(self) -> &'static [EyeColor] {
match self {
Species::Danari => &DANARI_EYE_COLORS,
Species::Dwarf => &DWARF_EYE_COLORS,
Species::Elf => &ELF_EYE_COLORS,
Species::Human => &HUMAN_EYE_COLORS,
Species::Orc => &ORC_EYE_COLORS,
Species::Undead => &UNDEAD_EYE_COLORS,
}
}
/// FIXME: This is a hack! The only reason we need to do this is because
/// hair colors are currently just indices into an array, not enum
/// variants. Once we have proper variants for hair colors, we won't
/// need to do this anymore, since we will use locally defined arrays to
/// represent per-species stuff (or have some other solution for validity).
pub fn num_hair_colors(self) -> u8 {
match self {
Species::Danari => 12,
Species::Dwarf => 21,
Species::Elf => 24,
Species::Human => 22,
Species::Orc => 11,
Species::Undead => 22,
}
}
pub fn skin_color(self, val: u8) -> Skin {
self.skin_colors()
.get(val as usize)
.copied()
.unwrap_or(Skin::Skin3)
}
pub fn num_skin_colors(self) -> u8 { self.skin_colors().len() as u8 }
pub fn eye_color(self, val: u8) -> EyeColor {
self.eye_colors()
.get(val as usize)
.copied()
.unwrap_or(EyeColor::NobleBlue)
}
pub fn num_eye_colors(self) -> u8 { self.eye_colors().len() as u8 }
pub fn num_hair_styles(self, body_type: BodyType) -> u8 {
match (self, body_type) {
(Species::Danari, BodyType::Female) => 4,
(Species::Danari, BodyType::Male) => 4,
(Species::Dwarf, BodyType::Female) => 7,
(Species::Dwarf, BodyType::Male) => 4,
(Species::Elf, BodyType::Female) => 21,
(Species::Elf, BodyType::Male) => 4,
(Species::Human, BodyType::Female) => 19,
(Species::Human, BodyType::Male) => 17,
(Species::Orc, BodyType::Female) => 7,
(Species::Orc, BodyType::Male) => 8,
(Species::Undead, BodyType::Female) => 6,
(Species::Undead, BodyType::Male) => 5,
}
}
pub fn num_accessories(self, body_type: BodyType) -> u8 {
match (self, body_type) {
(Species::Danari, BodyType::Female) => 1,
(Species::Danari, BodyType::Male) => 1,
(Species::Dwarf, BodyType::Female) => 7,
(Species::Dwarf, BodyType::Male) => 7,
(Species::Elf, BodyType::Female) => 2,
(Species::Elf, BodyType::Male) => 1,
(Species::Human, BodyType::Female) => 1,
(Species::Human, BodyType::Male) => 1,
(Species::Orc, BodyType::Female) => 4,
(Species::Orc, BodyType::Male) => 6,
(Species::Undead, BodyType::Female) => 2,
(Species::Undead, BodyType::Male) => 2,
}
}
pub fn num_eyebrows(self, _body_type: BodyType) -> u8 { 1 }
pub fn num_eyes(self, body_type: BodyType) -> u8 {
match (self, body_type) {
(Species::Danari, BodyType::Female) => 6,
(Species::Danari, BodyType::Male) => 7,
(Species::Dwarf, BodyType::Female) => 6,
(Species::Dwarf, BodyType::Male) => 7,
(Species::Elf, BodyType::Female) => 6,
(Species::Elf, BodyType::Male) => 7,
(Species::Human, BodyType::Female) => 6,
(Species::Human, BodyType::Male) => 5,
(Species::Orc, BodyType::Female) => 6,
(Species::Orc, BodyType::Male) => 2,
(Species::Undead, BodyType::Female) => 3,
(Species::Undead, BodyType::Male) => 4,
}
}
pub fn num_beards(self, body_type: BodyType) -> u8 {
match (self, body_type) {
(Species::Danari, BodyType::Female) => 1,
(Species::Danari, BodyType::Male) => 2,
(Species::Dwarf, BodyType::Female) => 1,
(Species::Dwarf, BodyType::Male) => 20,
(Species::Elf, BodyType::Female) => 1,
(Species::Elf, BodyType::Male) => 1,
(Species::Human, BodyType::Female) => 1,
(Species::Human, BodyType::Male) => 4,
(Species::Orc, BodyType::Female) => 1,
(Species::Orc, BodyType::Male) => 3,
(Species::Undead, BodyType::Female) => 1,
(Species::Undead, BodyType::Male) => 1,
}
}
}
make_case_elim!(
body_type,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[repr(u32)]
pub enum BodyType {
Female = 0,
Male = 1,
}
);
pub const ALL_BODY_TYPES: [BodyType; 2] = [BodyType::Female, BodyType::Male];
make_case_elim!(
eye_color,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u32)]
pub enum EyeColor {
VigorousBlack = 0,
NobleBlue = 1,
CuriousGreen = 2,
LoyalBrown = 3,
ViciousRed = 4,
PumpkinOrange = 5,
GhastlyYellow = 6,
MagicPurple = 7,
ToxicGreen = 8,
ExoticPurple = 9,
SulfurYellow = 10,
AmberOrange = 11,
PineGreen = 12,
CornflowerBlue = 13,
}
);
make_case_elim!(
skin,
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize_repr, Deserialize_repr)]
#[repr(u32)]
pub enum Skin {
Skin1 = 0,
Skin2 = 1,
Skin3 = 2,
Skin4 = 3,
Skin5 = 4,
Skin6 = 5,
Iron = 6,
Steel = 7,
DanariOne = 8,
DanariTwo = 9,
DanariThree = 10,
DanariFour = 11,
ElfOne = 12,
ElfTwo = 13,
//ElfThree = 14,
OrcOne = 14,
OrcTwo = 15,
OrcThree = 16,
UndeadOne = 17,
UndeadTwo = 18,
UndeadThree = 19,
Skin7 = 20,
Skin8 = 21,
Skin9 = 22,
Skin10 = 23,
Skin11 = 24,
Skin12 = 25,
Skin13 = 26,
Skin14 = 27,
Skin15 = 28,
Skin16 = 29,
Skin17 = 30,
Skin18 = 31,
OrcFour = 32,
}
);