veloren_common/comp/body/
plugin.rs1use 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 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::RngExt, &species: &Species) -> Self {
92 Self { species }
93 }
94
95 fn from_name(species: &str) -> Result<Body, NotFound> {
96 let guard = PLUGIN_SPECIES.read();
97 let elem = guard
98 .0
99 .iter()
100 .enumerate()
101 .find(|(_n, spec)| spec.id == species);
102 if let Some((n, _)) = elem {
103 Ok(Body { species: n })
104 } else {
105 Err(NotFound)
106 }
107 }
108}
109
110impl From<Body> for super::Body {
111 fn from(body: Body) -> Self { super::Body::Plugin(body) }
112}
113
114pub type Species = usize;
115
116#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
118pub struct AllSpecies<SpeciesMeta> {
119 pub plugin: SpeciesMeta,
120}
121
122impl<'a, SpeciesMeta> core::ops::Index<&'a Species> for AllSpecies<SpeciesMeta> {
123 type Output = SpeciesMeta;
124
125 #[inline]
126 fn index(&self, _index: &'a Species) -> &Self::Output { &self.plugin }
127}
128
129pub struct SpeciesIter {
130 current: usize,
131 max: usize,
132}
133
134impl Iterator for SpeciesIter {
135 type Item = Species;
136
137 fn next(&mut self) -> Option<Self::Item> {
138 if self.current >= self.max {
139 None
140 } else {
141 let result = self.current;
142 self.current += 1;
143 Some(result)
144 }
145 }
146}
147
148impl<'a, SpeciesMeta: 'a> IntoIterator for &'a AllSpecies<SpeciesMeta> {
149 type IntoIter = SpeciesIter;
150 type Item = Species;
151
152 fn into_iter(self) -> Self::IntoIter {
153 SpeciesIter {
154 current: 0,
155 max: PLUGIN_SPECIES.read().0.len(),
156 }
157 }
158}
159
160pub fn parse_name(s: &str) -> Option<NpcBody> {
161 tracing::info!("parse_name {s}");
162 let guard = PLUGIN_SPECIES.read();
163 let elem = guard
164 .0
165 .iter()
166 .enumerate()
167 .find(|(_n, species)| species.id == s);
168 elem.map(|(n, _species)| {
169 NpcBody(
170 crate::npc::NpcKind::Plugin,
171 Box::new(move || crate::comp::body::Body::Plugin(Body { species: n })),
172 )
173 })
174}
175
176pub fn test() {
177 println!("{:?}", &*PLUGIN_SPECIES);
178}