veloren_common/comp/body/
plugin.rs

1use common_assets::{AssetCombined, AssetHandle, Ron};
2use lazy_static::lazy_static;
3use serde::{Deserialize, Serialize};
4use vek::Vec3;
5
6use crate::{comp::Mass, npc::NpcBody};
7
8#[derive(Debug, Deserialize)]
9struct PluginSpecies {
10    id: String,
11    mass: f32,
12    dimensions: [f32; 3],
13    base_health: u16,
14    flee_health: f32,
15    parasite_drag: f32,
16    base_accel: f32,
17    base_ori_rate: f32,
18    swim_thrust: Option<f32>,
19}
20
21lazy_static! {
22    static ref PLUGIN_SPECIES: AssetHandle<Ron<Vec<PluginSpecies>>> =
23        Ron::load_expect_combined_static("common.plugin_bodies");
24}
25
26mod spec_parser {
27    use serde::{Deserialize, Deserializer, Serializer};
28
29    use super::{Body, Species};
30
31    pub fn serialize<S>(species: &Species, serializer: S) -> Result<S::Ok, S::Error>
32    where
33        S: Serializer,
34    {
35        serializer.serialize_str(&super::PLUGIN_SPECIES.read().0[*species].id)
36    }
37
38    pub fn deserialize<'de, D>(deserializer: D) -> Result<Species, D::Error>
39    where
40        D: Deserializer<'de>,
41    {
42        String::deserialize(deserializer)
43            .and_then(|species| {
44                Body::from_name(&species).map_err(|_err| {
45                    serde::de::Error::invalid_value(
46                        serde::de::Unexpected::Str(&species),
47                        &"known species",
48                    )
49                })
50            })
51            .map(|body| body.species)
52    }
53}
54
55#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
56pub struct Body {
57    #[serde(with = "spec_parser")]
58    pub species: Species,
59}
60
61struct NotFound;
62
63impl Body {
64    // TODO: use SpeciesIter here?
65    // At the moment, I'm mimicking what Self::random() does
66    pub fn iter() -> impl Iterator<Item = Self> { std::iter::once(Body { species: 0 }) }
67
68    pub fn mass(&self) -> Mass { Mass(PLUGIN_SPECIES.read().0[self.species].mass) }
69
70    pub fn dimensions(&self) -> Vec3<f32> {
71        Vec3::from_slice(&PLUGIN_SPECIES.read().0[self.species].dimensions)
72    }
73
74    pub fn base_health(&self) -> u16 { PLUGIN_SPECIES.read().0[self.species].base_health }
75
76    pub fn flee_health(&self) -> f32 { PLUGIN_SPECIES.read().0[self.species].flee_health }
77
78    pub fn parasite_drag(&self) -> f32 { PLUGIN_SPECIES.read().0[self.species].parasite_drag }
79
80    pub fn base_accel(&self) -> f32 { PLUGIN_SPECIES.read().0[self.species].base_accel }
81
82    pub fn base_ori_rate(&self) -> f32 { PLUGIN_SPECIES.read().0[self.species].base_ori_rate }
83
84    pub fn swim_thrust(&self) -> Option<f32> { PLUGIN_SPECIES.read().0[self.species].swim_thrust }
85
86    pub fn random() -> Self { Body { species: 0 } }
87
88    pub fn id(&self) -> String { PLUGIN_SPECIES.read().0[self.species].id.clone() }
89
90    #[inline]
91    pub fn random_with(_rng: &mut impl rand::Rng, &species: &Species) -> Self { Self { species } }
92
93    fn from_name(species: &str) -> Result<Body, NotFound> {
94        let guard = PLUGIN_SPECIES.read();
95        let elem = guard
96            .0
97            .iter()
98            .enumerate()
99            .find(|(_n, spec)| spec.id == species);
100        if let Some((n, _)) = elem {
101            Ok(Body { species: n })
102        } else {
103            Err(NotFound)
104        }
105    }
106}
107
108impl From<Body> for super::Body {
109    fn from(body: Body) -> Self { super::Body::Plugin(body) }
110}
111
112pub type Species = usize;
113
114/// Data representing per-species generic data.
115#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
116pub struct AllSpecies<SpeciesMeta> {
117    pub plugin: SpeciesMeta,
118}
119
120impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
121    type Output = SpeciesMeta;
122
123    #[inline]
124    fn index(&self, _index: &'a Species) -> &Self::Output { &self.plugin }
125}
126
127pub struct SpeciesIter {
128    current: usize,
129    max: usize,
130}
131
132impl Iterator for SpeciesIter {
133    type Item = Species;
134
135    fn next(&mut self) -> Option<Self::Item> {
136        if self.current >= self.max {
137            None
138        } else {
139            let result = self.current;
140            self.current += 1;
141            Some(result)
142        }
143    }
144}
145
146impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
147    type IntoIter = SpeciesIter;
148    type Item = Species;
149
150    fn into_iter(self) -> Self::IntoIter {
151        SpeciesIter {
152            current: 0,
153            max: PLUGIN_SPECIES.read().0.len(),
154        }
155    }
156}
157
158pub fn parse_name(s: &str) -> Option<NpcBody> {
159    tracing::info!("parse_name {s}");
160    let guard = PLUGIN_SPECIES.read();
161    let elem = guard
162        .0
163        .iter()
164        .enumerate()
165        .find(|(_n, species)| species.id == s);
166    elem.map(|(n, _species)| {
167        NpcBody(
168            crate::npc::NpcKind::Plugin,
169            Box::new(move || crate::comp::body::Body::Plugin(Body { species: n })),
170        )
171    })
172}
173
174pub fn test() {
175    println!("{:?}", &*PLUGIN_SPECIES);
176}