veloren_common/comp/body/
mod.rs

1pub mod arthropod;
2pub mod biped_large;
3pub mod biped_small;
4pub mod bird_large;
5pub mod bird_medium;
6pub mod crustacean;
7pub mod dragon;
8pub mod fish_medium;
9pub mod fish_small;
10pub mod golem;
11pub mod humanoid;
12pub mod item;
13pub mod object;
14pub mod parts;
15pub mod plugin;
16pub mod quadruped_low;
17pub mod quadruped_medium;
18pub mod quadruped_small;
19pub mod ship;
20pub mod theropod;
21
22use crate::{
23    assets::{BoxedError, FileAsset, load_ron},
24    consts::{HUMAN_DENSITY, WATER_DENSITY},
25    npc::NpcKind,
26};
27use common_base::enum_iter;
28use common_i18n::Content;
29use serde::{Deserialize, Serialize};
30use specs::{Component, DerefFlaggedStorage};
31use strum::{Display, IntoEnumIterator};
32use vek::*;
33
34use super::{BuffKind, CapsulePrism, Collider, Density, Mass, Scale};
35
36enum_iter! {
37    #[derive(
38        Copy, Clone, Debug, Display, PartialEq, PartialOrd, Ord, Eq, Hash, Serialize, Deserialize,
39    )]
40    #[repr(u32)]
41    pub enum Body {
42        Humanoid(humanoid::Body) = 0,
43        QuadrupedSmall(quadruped_small::Body) = 1,
44        QuadrupedMedium(quadruped_medium::Body) = 2,
45        BirdMedium(bird_medium::Body) = 3,
46        FishMedium(fish_medium::Body) = 4,
47        Dragon(dragon::Body) = 5,
48        BirdLarge(bird_large::Body) = 6,
49        FishSmall(fish_small::Body) = 7,
50        BipedLarge(biped_large::Body) = 8,
51        BipedSmall(biped_small::Body) = 9,
52        Object(object::Body) = 10,
53        Golem(golem::Body) = 11,
54        Theropod(theropod::Body) = 12,
55        QuadrupedLow(quadruped_low::Body) = 13,
56        Ship(ship::Body) = 14,
57        Arthropod(arthropod::Body) = 15,
58        Item(item::Body) = 16,
59        Crustacean(crustacean::Body) = 17,
60        Plugin(plugin::Body) = 18,
61    }
62}
63
64// Implemented for Buff, to be able to implement EnumIter.
65impl Default for Body {
66    fn default() -> Self {
67        Body::QuadrupedSmall(quadruped_small::Body {
68            species: quadruped_small::Species::Frog,
69            body_type: quadruped_small::BodyType::Female,
70        })
71    }
72}
73
74/// Data representing data generic to the body together with per-species data.
75#[derive(Clone, Debug, Deserialize, Serialize)]
76pub struct BodyData<BodyMeta, SpeciesData> {
77    /// Shared metadata for this whole body type.
78    pub body: BodyMeta,
79    /// All the metadata for species with this body type.
80    pub species: SpeciesData,
81}
82
83/// Metadata intended to be stored per-body, together with data intended to be
84/// stored for each species for each body.
85///
86/// NOTE: If you are adding new body kind and it should be spawned via /spawn
87/// please add it to `[ENTITIES](crate::cmd::ENTITIES)`
88#[derive(Clone, Debug, Deserialize, Serialize)]
89pub struct AllBodies<BodyMeta, SpeciesMeta> {
90    pub humanoid: BodyData<BodyMeta, humanoid::AllSpecies<SpeciesMeta>>,
91    pub quadruped_small: BodyData<BodyMeta, quadruped_small::AllSpecies<SpeciesMeta>>,
92    pub quadruped_medium: BodyData<BodyMeta, quadruped_medium::AllSpecies<SpeciesMeta>>,
93    pub bird_medium: BodyData<BodyMeta, bird_medium::AllSpecies<SpeciesMeta>>,
94    pub fish_medium: BodyData<BodyMeta, fish_medium::AllSpecies<SpeciesMeta>>,
95    pub dragon: BodyData<BodyMeta, dragon::AllSpecies<SpeciesMeta>>,
96    pub bird_large: BodyData<BodyMeta, bird_large::AllSpecies<SpeciesMeta>>,
97    pub fish_small: BodyData<BodyMeta, fish_small::AllSpecies<SpeciesMeta>>,
98    pub biped_large: BodyData<BodyMeta, biped_large::AllSpecies<SpeciesMeta>>,
99    pub biped_small: BodyData<BodyMeta, biped_small::AllSpecies<SpeciesMeta>>,
100    pub object: BodyData<BodyMeta, ()>,
101    pub item: BodyData<BodyMeta, ()>,
102    pub golem: BodyData<BodyMeta, golem::AllSpecies<SpeciesMeta>>,
103    pub theropod: BodyData<BodyMeta, theropod::AllSpecies<SpeciesMeta>>,
104    pub quadruped_low: BodyData<BodyMeta, quadruped_low::AllSpecies<SpeciesMeta>>,
105    pub ship: BodyData<BodyMeta, ()>,
106    pub arthropod: BodyData<BodyMeta, arthropod::AllSpecies<SpeciesMeta>>,
107    pub crustacean: BodyData<BodyMeta, crustacean::AllSpecies<SpeciesMeta>>,
108    pub plugin: BodyData<BodyMeta, plugin::AllSpecies<SpeciesMeta>>,
109}
110
111impl<BodyMeta, SpeciesMeta> AllBodies<BodyMeta, SpeciesMeta> {
112    /// Get species meta associated with the body.
113    ///
114    /// Returns `None` if the body doesn't have any associated meta, i.e ships,
115    /// objects, items.
116    pub fn get_species_meta<'a>(&'a self, body: &Body) -> Option<&'a SpeciesMeta> {
117        Some(match body {
118            Body::Humanoid(b) => &self.humanoid.species[&b.species],
119            Body::QuadrupedSmall(b) => &self.quadruped_small.species[&b.species],
120            Body::QuadrupedMedium(b) => &self.quadruped_medium.species[&b.species],
121            Body::BirdMedium(b) => &self.bird_medium.species[&b.species],
122            Body::BirdLarge(b) => &self.bird_large.species[&b.species],
123            Body::FishMedium(b) => &self.fish_medium.species[&b.species],
124            Body::Dragon(b) => &self.dragon.species[&b.species],
125            Body::FishSmall(b) => &self.fish_small.species[&b.species],
126            Body::BipedLarge(b) => &self.biped_large.species[&b.species],
127            Body::BipedSmall(b) => &self.biped_small.species[&b.species],
128            Body::Golem(b) => &self.golem.species[&b.species],
129            Body::Theropod(b) => &self.theropod.species[&b.species],
130            Body::QuadrupedLow(b) => &self.quadruped_low.species[&b.species],
131            Body::Arthropod(b) => &self.arthropod.species[&b.species],
132            Body::Crustacean(b) => &self.crustacean.species[&b.species],
133            Body::Plugin(b) => &self.plugin.species[&b.species],
134            Body::Item(_) | Body::Ship(_) | Body::Object(_) => return None,
135        })
136    }
137}
138
139/// Can only retrieve body metadata by direct index.
140impl<BodyMeta, SpeciesMeta> core::ops::Index<NpcKind> for AllBodies<BodyMeta, SpeciesMeta> {
141    type Output = BodyMeta;
142
143    #[inline]
144    fn index(&self, index: NpcKind) -> &Self::Output {
145        match index {
146            NpcKind::Humanoid => &self.humanoid.body,
147            NpcKind::Pig => &self.quadruped_small.body,
148            NpcKind::Wolf => &self.quadruped_medium.body,
149            NpcKind::Duck => &self.bird_medium.body,
150            NpcKind::Phoenix => &self.bird_large.body,
151            NpcKind::Marlin => &self.fish_medium.body,
152            NpcKind::Clownfish => &self.fish_small.body,
153            NpcKind::Ogre => &self.biped_large.body,
154            NpcKind::Gnome => &self.biped_small.body,
155            NpcKind::StoneGolem => &self.golem.body,
156            NpcKind::Archaeos => &self.theropod.body,
157            NpcKind::Reddragon => &self.dragon.body,
158            NpcKind::Crocodile => &self.quadruped_low.body,
159            NpcKind::Tarantula => &self.arthropod.body,
160            NpcKind::Crab => &self.crustacean.body,
161            NpcKind::Plugin => &self.plugin.body,
162        }
163    }
164}
165
166/// Can only retrieve body metadata by direct index.
167impl<BodyMeta, SpeciesMeta> core::ops::Index<&Body> for AllBodies<BodyMeta, SpeciesMeta> {
168    type Output = BodyMeta;
169
170    #[inline]
171    fn index(&self, index: &Body) -> &Self::Output {
172        match index {
173            Body::Humanoid(_) => &self.humanoid.body,
174            Body::QuadrupedSmall(_) => &self.quadruped_small.body,
175            Body::QuadrupedMedium(_) => &self.quadruped_medium.body,
176            Body::BirdMedium(_) => &self.bird_medium.body,
177            Body::BirdLarge(_) => &self.bird_large.body,
178            Body::FishMedium(_) => &self.fish_medium.body,
179            Body::Dragon(_) => &self.dragon.body,
180            Body::FishSmall(_) => &self.fish_small.body,
181            Body::BipedLarge(_) => &self.biped_large.body,
182            Body::BipedSmall(_) => &self.biped_small.body,
183            Body::Object(_) => &self.object.body,
184            Body::Item(_) => &self.item.body,
185            Body::Golem(_) => &self.golem.body,
186            Body::Theropod(_) => &self.theropod.body,
187            Body::QuadrupedLow(_) => &self.quadruped_low.body,
188            Body::Arthropod(_) => &self.arthropod.body,
189            Body::Ship(_) => &self.ship.body,
190            Body::Crustacean(_) => &self.crustacean.body,
191            Body::Plugin(_) => &self.plugin.body,
192        }
193    }
194}
195
196impl<
197    BodyMeta: Send + Sync + for<'de> serde::Deserialize<'de> + 'static,
198    SpeciesMeta: Send + Sync + for<'de> serde::Deserialize<'de> + 'static,
199> FileAsset for AllBodies<BodyMeta, SpeciesMeta>
200{
201    const EXTENSION: &'static str = "ron";
202
203    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Result<Self, BoxedError> { load_ron(&bytes) }
204}
205
206/// Semantic gender aka body_type
207///
208/// Should be used for localization with extreme care.
209/// For basically everything except *maybe* humanoids, it's simply wrong to
210/// assume that this may be used as grammatical gender.
211/// Read more on `Body::default_gender`.
212///
213/// TODO: move this to common::i18n (or create new alternative), extend with
214/// more options and add GUI for players to choose preferred gender.
215/// Read a comment for `gender_str` in voxygen/i18n-helpers/src/lib.rs.
216#[derive(Copy, Clone, Debug, Deserialize, Serialize)]
217pub enum Gender {
218    Masculine,
219    Feminine,
220    Neuter,
221}
222
223impl Body {
224    pub fn is_same_species_as(&self, other: &Body) -> bool {
225        match self {
226            Body::Humanoid(b1) => match other {
227                Body::Humanoid(b2) => b1.species == b2.species,
228                _ => false,
229            },
230            Body::QuadrupedSmall(b1) => match other {
231                Body::QuadrupedSmall(b2) => b1.species == b2.species,
232                _ => false,
233            },
234            Body::QuadrupedMedium(b1) => match other {
235                Body::QuadrupedMedium(b2) => b1.species == b2.species,
236                _ => false,
237            },
238            Body::BirdMedium(b1) => match other {
239                Body::BirdMedium(b2) => b1.species == b2.species,
240                _ => false,
241            },
242            Body::BirdLarge(b1) => match other {
243                Body::BirdLarge(b2) => b1.species == b2.species,
244                _ => false,
245            },
246            Body::FishMedium(b1) => match other {
247                Body::FishMedium(b2) => b1.species == b2.species,
248                _ => false,
249            },
250            Body::Dragon(b1) => match other {
251                Body::Dragon(b2) => b1.species == b2.species,
252                _ => false,
253            },
254            Body::FishSmall(b1) => match other {
255                Body::FishSmall(b2) => b1.species == b2.species,
256                _ => false,
257            },
258            Body::BipedLarge(b1) => match other {
259                Body::BipedLarge(b2) => b1.species == b2.species,
260                _ => false,
261            },
262            Body::BipedSmall(b1) => match other {
263                Body::BipedSmall(b2) => b1.species == b2.species,
264                _ => false,
265            },
266            Body::Object(_) => false,
267            Body::Item(_) => false,
268            Body::Golem(b1) => match other {
269                Body::Golem(b2) => b1.species == b2.species,
270                _ => false,
271            },
272            Body::Theropod(b1) => match other {
273                Body::Theropod(b2) => b1.species == b2.species,
274                _ => false,
275            },
276            Body::QuadrupedLow(b1) => match other {
277                Body::QuadrupedLow(b2) => b1.species == b2.species,
278                _ => false,
279            },
280            Body::Arthropod(b1) => match other {
281                Body::Arthropod(b2) => b1.species == b2.species,
282                _ => false,
283            },
284            Body::Ship(_) => false,
285            Body::Crustacean(b1) => match other {
286                Body::Crustacean(b2) => b1.species == b2.species,
287                _ => false,
288            },
289            Body::Plugin(b1) => match other {
290                Body::Plugin(b2) => b1.species == b2.species,
291                _ => false,
292            },
293        }
294    }
295
296    /// How many heads this body has in the `Heads` component if any.
297    pub fn heads(&self) -> Option<usize> {
298        match self {
299            Body::QuadrupedLow(body) => match body.species {
300                quadruped_low::Species::Hydra => Some(3),
301                _ => None,
302            },
303            _ => None,
304        }
305    }
306
307    /// This is used to see if a body can be controlled by its rider.
308    pub fn has_free_will(&self) -> bool {
309        match self {
310            Body::Humanoid(_) | Body::BipedLarge(_) | Body::BipedSmall(_) => true,
311            Body::Crustacean(body) => match body.species {
312                crustacean::Species::Crab => false,
313                crustacean::Species::SoldierCrab => false,
314                crustacean::Species::Karkatha => true,
315            },
316            Body::QuadrupedSmall(_)
317            | Body::QuadrupedMedium(_)
318            | Body::BirdMedium(_)
319            | Body::FishMedium(_)
320            | Body::Dragon(_)
321            | Body::BirdLarge(_)
322            | Body::FishSmall(_)
323            | Body::Object(_)
324            | Body::Golem(_)
325            | Body::Theropod(_)
326            | Body::QuadrupedLow(_)
327            | Body::Ship(_)
328            | Body::Arthropod(_)
329            | Body::Item(_)
330            | Body::Plugin(_) => false,
331        }
332    }
333
334    pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
335
336    pub fn is_campfire(&self) -> bool { matches!(self, Body::Object(object::Body::CampfireLit)) }
337
338    pub fn is_portal(&self) -> bool {
339        matches!(
340            self,
341            Body::Object(object::Body::Portal | object::Body::PortalActive)
342        )
343    }
344
345    pub fn bleeds(&self) -> bool {
346        !matches!(
347            self,
348            Body::Object(_) | Body::Ship(_) | Body::Item(_) | Body::Golem(_)
349        )
350    }
351
352    /// The length of the stride of the body, in metres (not accounting for
353    /// different legs)
354    pub fn stride_length(&self) -> f32 {
355        if let Body::Humanoid(body) = self {
356            body.scaler() * 3.75
357        } else {
358            // Rough heuristic
359            let dims = self.dimensions();
360            0.65 + (dims.y + dims.z) * 0.6
361        }
362    }
363
364    pub fn scale(&self) -> Scale {
365        let s = match self {
366            Body::BirdMedium(bird_medium) => match bird_medium.species {
367                bird_medium::Species::Bat | bird_medium::Species::VampireBat => 0.5,
368                _ => 1.0,
369            },
370            _ => 1.0,
371        };
372        Scale(s)
373    }
374
375    /// Average density of the body
376    // Units are based on kg/m³
377    pub fn density(&self) -> Density {
378        let d = match self {
379            // based on a house sparrow (Passer domesticus)
380            Body::BirdMedium(_) => 700.0,
381            Body::BirdLarge(_) => 2_200.0,
382
383            Body::Dragon(_) => 5_000.0,
384
385            Body::Golem(_) => WATER_DENSITY * 2.5,
386            Body::Humanoid(_) => HUMAN_DENSITY,
387            Body::Ship(ship) => ship.density().0,
388            Body::Object(object) => object.density().0,
389            Body::Item(item) => item.density().0,
390            _ => HUMAN_DENSITY,
391        };
392        Density(d)
393    }
394
395    // Values marked with ~✅ are checked based on their RL equivalent.
396    // Discrepancy in size compared to their RL equivalents has not necessarily been
397    // taken into account.
398    pub fn mass(&self) -> Mass {
399        let m = match self {
400            Body::BipedLarge(body) => match body.species {
401                biped_large::Species::Slysaurok => 400.0,
402                biped_large::Species::Occultsaurok => 400.0,
403                biped_large::Species::Mightysaurok => 400.0,
404                biped_large::Species::Mindflayer => 420.0,
405                biped_large::Species::Minotaur => 500.0,
406                biped_large::Species::Cavetroll => 600.0,
407                biped_large::Species::Mountaintroll => 600.0,
408                biped_large::Species::Swamptroll => 600.0,
409                biped_large::Species::Gigasfrost | biped_large::Species::Gigasfire => 400.0,
410                biped_large::Species::AdletElder => 350.0,
411                biped_large::Species::HaniwaGeneral => 360.0,
412                biped_large::Species::TerracottaBesieger
413                | biped_large::Species::TerracottaDemolisher
414                | biped_large::Species::TerracottaPunisher
415                | biped_large::Species::TerracottaPursuer
416                | biped_large::Species::Cursekeeper => 380.0,
417                biped_large::Species::Forgemaster => 1600.0,
418                _ => 400.0,
419            },
420            Body::BipedSmall(body) => match body.species {
421                biped_small::Species::IronDwarf => 1000.0,
422                biped_small::Species::Flamekeeper => 1000.0,
423                biped_small::Species::Boreal => 270.0,
424                _ => 50.0,
425            },
426            // ravens are 0.69-2 kg, crows are 0.51 kg on average.
427            Body::BirdMedium(body) => match body.species {
428                bird_medium::Species::SnowyOwl => 3.0,
429                bird_medium::Species::HornedOwl => 3.0,
430                bird_medium::Species::Duck => 3.5,
431                bird_medium::Species::Cockatiel => 2.0,
432                bird_medium::Species::Chicken => 2.5, // ~✅ Red junglefowl are 1-1.5 kg
433                bird_medium::Species::Bat => 1.5,
434                bird_medium::Species::Penguin => 10.0,
435                bird_medium::Species::Eagle => 7.0, // ~✅ Steller's sea eagle are 5-9 kg
436                bird_medium::Species::Goose => 3.5, // ~✅ Swan geese are 2.8-3.5 kg
437                bird_medium::Species::Parrot => 1.0,
438                bird_medium::Species::Peacock => 6.0,
439                bird_medium::Species::Crow => 3.0,
440                bird_medium::Species::Dodo => 4.0,
441                bird_medium::Species::Parakeet => 1.0,
442                bird_medium::Species::Puffin => 2.0,
443                bird_medium::Species::Toucan => 4.5,
444                bird_medium::Species::BloodmoonBat => 1.5,
445                bird_medium::Species::VampireBat => 1.5,
446            },
447            Body::BirdLarge(_) => 250.0,
448            Body::Dragon(_) => 20_000.0,
449            Body::FishMedium(_) => 5.0,
450            Body::FishSmall(_) => 1.0,
451            Body::Golem(_) => 10_000.0,
452            Body::Humanoid(humanoid) => {
453                // Understand that changing the mass values can have effects
454                // on multiple systems.
455                //
456                // If you want to change that value, consult with
457                // Physics and Combat teams
458                //
459                // Weight is proportional height, where
460                // a 1.75m character would weigh 65kg
461                65.0 * humanoid.height() / 1.75f32
462            },
463            Body::Object(obj) => obj.mass().0,
464            Body::Item(item) => item.mass().0,
465            Body::QuadrupedLow(body) => match body.species {
466                quadruped_low::Species::Alligator => 360.0, // ~✅
467                quadruped_low::Species::Snaretongue => 280.0,
468                quadruped_low::Species::Asp => 300.0,
469                // saltwater crocodiles can weigh around 1 ton, but our version is the size of an
470                // alligator or smaller, so whatever
471                quadruped_low::Species::Crocodile => 360.0,
472                quadruped_low::Species::SeaCrocodile => 410.0,
473                quadruped_low::Species::Deadwood => 200.0,
474                quadruped_low::Species::Monitor => 200.0,
475                quadruped_low::Species::Pangolin => 300.0,
476                quadruped_low::Species::Salamander => 350.0,
477                quadruped_low::Species::Elbst => 350.0,
478                quadruped_low::Species::Tortoise => 300.0,
479                quadruped_low::Species::Lavadrake => 700.0,
480                quadruped_low::Species::Icedrake => 700.0,
481                quadruped_low::Species::Mossdrake => 700.0,
482                quadruped_low::Species::Rocksnapper => 450.0,
483                quadruped_low::Species::Rootsnapper => 450.0,
484                quadruped_low::Species::Reefsnapper => 450.0,
485                quadruped_low::Species::Maneater => 80.0,
486                quadruped_low::Species::Sandshark => 450.0,
487                quadruped_low::Species::Hakulaq => 400.0,
488                quadruped_low::Species::Dagon => 600.0,
489                quadruped_low::Species::Basilisk => 800.0,
490                quadruped_low::Species::Driggle => 55.0,
491                quadruped_low::Species::Hydra => 800.0,
492            },
493            Body::QuadrupedMedium(body) => match body.species {
494                quadruped_medium::Species::Bear => 500.0, // ~✅ (350-700 kg)
495                quadruped_medium::Species::Cattle => 575.0, // ~✅ (500-650 kg)
496                quadruped_medium::Species::Deer => 80.0,
497                quadruped_medium::Species::Donkey => 200.0,
498                quadruped_medium::Species::Highland => 200.0,
499                quadruped_medium::Species::Horse => 300.0, // ~✅
500                quadruped_medium::Species::Kelpie => 250.0,
501                quadruped_medium::Species::Lion => 170.0, // ~✅ (110-225 kg)
502                quadruped_medium::Species::Panda => 200.0,
503                quadruped_medium::Species::Saber => 130.0,
504                quadruped_medium::Species::Yak => 200.0,
505                quadruped_medium::Species::Dreadhorn => 500.0,
506                quadruped_medium::Species::Mammoth => 1500.0,
507                quadruped_medium::Species::Elephant => 1500.0,
508                quadruped_medium::Species::Catoblepas => 300.0,
509                _ => 200.0,
510            },
511            Body::QuadrupedSmall(body) => match body.species {
512                quadruped_small::Species::Axolotl => 1.0,
513                quadruped_small::Species::Batfox => 10.0,
514                quadruped_small::Species::Beaver => 10.0,
515                quadruped_small::Species::Boar => 80.0, // ~✅ (60-100 kg)
516                quadruped_small::Species::Cat => 4.0,   // ~✅ (4-5 kg)
517                quadruped_small::Species::Dog => 30.0,  // ~✅ (German Shepherd: 30-40 kg)
518                quadruped_small::Species::Fox => 10.0,
519                quadruped_small::Species::Frog => 1.0,
520                quadruped_small::Species::Fungome => 10.0,
521                quadruped_small::Species::Gecko => 1.0,
522                quadruped_small::Species::Goat => 50.0,
523                quadruped_small::Species::Hare => 10.0,
524                quadruped_small::Species::Holladon => 70.0,
525                quadruped_small::Species::Hyena => 70.0, // ~✅ (vaguely)
526                quadruped_small::Species::Jackalope => 10.0,
527                quadruped_small::Species::Pig => 20.0,
528                quadruped_small::Species::Porcupine => 5.0,
529                quadruped_small::Species::Quokka => 10.0,
530                quadruped_small::Species::Rabbit => 2.0,
531                quadruped_small::Species::Raccoon => 30.0,
532                quadruped_small::Species::Rat => 1.0,
533                quadruped_small::Species::Sheep => 50.0,
534                quadruped_small::Species::Skunk => 5.0,
535                quadruped_small::Species::Squirrel => 1.0,
536                quadruped_small::Species::Truffler => 70.0,
537                quadruped_small::Species::Turtle => 40.0,
538                quadruped_small::Species::Seal => 15.0,
539                quadruped_small::Species::TreantSapling => 80.0,
540                quadruped_small::Species::MossySnail => 5.0,
541            },
542            Body::Theropod(body) => match body.species {
543                // for reference, elephants are in the range of 2.6-6.9 tons
544                // and Tyrannosaurus rex were ~8.4-14 tons
545                theropod::Species::Archaeos => 8_000.0,
546                theropod::Species::Ntouka => 8_000.0,
547                theropod::Species::Odonto => 8_000.0,
548                theropod::Species::Dodarock => 700.0,
549                theropod::Species::Sandraptor => 500.0,
550                theropod::Species::Snowraptor => 500.0,
551                theropod::Species::Sunlizard => 500.0,
552                theropod::Species::Woodraptor => 500.0,
553                theropod::Species::Yale => 1_000.0,
554                theropod::Species::Axebeak => 300.0,
555            },
556            Body::Ship(ship) => ship.mass().0,
557            Body::Arthropod(_) => 200.0,
558            // TODO: mass
559            Body::Crustacean(body) => match body.species {
560                crustacean::Species::Crab | crustacean::Species::SoldierCrab => 50.0,
561                crustacean::Species::Karkatha => 1200.0,
562            },
563            Body::Plugin(body) => body.mass().0,
564        };
565        Mass(m)
566    }
567
568    /// The width (shoulder to shoulder), length (nose to tail) and height
569    /// respectively (in metres)
570    // Code reviewers: should we replace metres with 'block height'?
571    // #[inline_tweak::tweak_fn]
572    pub fn dimensions(&self) -> Vec3<f32> {
573        match self {
574            Body::BipedLarge(body) => match body.species {
575                biped_large::Species::Cyclops => Vec3::new(5.6, 3.0, 8.0),
576                biped_large::Species::Dullahan => Vec3::new(4.6, 3.0, 5.5),
577                biped_large::Species::Mightysaurok => Vec3::new(4.0, 3.0, 3.4),
578                biped_large::Species::Mindflayer => Vec3::new(4.4, 3.0, 8.5),
579                biped_large::Species::Minotaur => Vec3::new(6.0, 3.0, 8.0),
580                biped_large::Species::Occultsaurok => Vec3::new(4.0, 3.0, 3.4),
581                biped_large::Species::Slysaurok => Vec3::new(4.0, 3.0, 3.4),
582                biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5),
583                biped_large::Species::Harvester => Vec3::new(4.6, 3.0, 5.4),
584                biped_large::Species::Cultistwarlord => Vec3::new(3.0, 3.0, 4.5),
585                biped_large::Species::Cultistwarlock => Vec3::new(3.0, 3.0, 3.5),
586                biped_large::Species::Huskbrute => Vec3::new(4.6, 3.0, 5.0),
587                biped_large::Species::Tursus => Vec3::new(4.0, 3.0, 4.0),
588                biped_large::Species::Gigasfrost => Vec3::new(6.0, 3.0, 11.0),
589                biped_large::Species::Gigasfire => Vec3::new(6.0, 3.0, 11.0),
590                biped_large::Species::AdletElder => Vec3::new(3.5, 3.0, 4.0),
591                biped_large::Species::SeaBishop => Vec3::new(3.7, 2.5, 3.4),
592                biped_large::Species::HaniwaGeneral => Vec3::new(3.3, 2.3, 3.6),
593                biped_large::Species::TerracottaBesieger => Vec3::new(3.8, 3.0, 5.0),
594                biped_large::Species::TerracottaDemolisher => Vec3::new(3.3, 2.5, 3.6),
595                biped_large::Species::TerracottaPunisher => Vec3::new(3.3, 2.5, 3.8),
596                biped_large::Species::TerracottaPursuer => Vec3::new(3.3, 2.5, 3.8),
597                biped_large::Species::Cursekeeper => Vec3::new(3.8, 3.0, 5.0),
598                biped_large::Species::Forgemaster => Vec3::new(6.5, 5.0, 8.0),
599                biped_large::Species::Strigoi => Vec3::new(3.8, 3.0, 5.0),
600                biped_large::Species::Executioner => Vec3::new(2.8, 2.8, 4.7),
601                biped_large::Species::Tidalwarrior => Vec3::new(7.0, 8.2, 6.8),
602                biped_large::Species::Cavetroll => Vec3::new(4.6, 3.0, 5.0),
603                biped_large::Species::Mountaintroll => Vec3::new(4.6, 3.0, 5.0),
604                biped_large::Species::Swamptroll => Vec3::new(4.6, 3.0, 4.5),
605                biped_large::Species::Yeti => Vec3::new(4.6, 3.0, 4.8),
606                biped_large::Species::Blueoni => Vec3::new(4.6, 3.0, 4.8),
607                biped_large::Species::Redoni => Vec3::new(4.6, 3.0, 4.5),
608                biped_large::Species::Ogre => match body.body_type {
609                    biped_large::BodyType::Male => Vec3::new(4.6, 3.0, 5.6),
610                    biped_large::BodyType::Female => Vec3::new(4.6, 3.0, 5.9),
611                },
612                biped_large::Species::Wendigo => Vec3::new(4.6, 3.0, 6.0),
613            },
614            Body::BipedSmall(body) => match body.species {
615                biped_small::Species::Gnarling => Vec3::new(1.0, 0.75, 1.4),
616                biped_small::Species::Haniwa => Vec3::new(1.3, 1.0, 2.2),
617                biped_small::Species::Adlet => Vec3::new(1.3, 1.0, 2.0),
618                biped_small::Species::Sahagin => Vec3::new(1.3, 2.0, 1.7),
619                biped_small::Species::Myrmidon => Vec3::new(1.3, 1.0, 2.2),
620                biped_small::Species::Husk => Vec3::new(1.7, 0.7, 2.7),
621                biped_small::Species::Boreal => Vec3::new(2.6, 2.0, 4.6),
622                biped_small::Species::Ashen => Vec3::new(1.3, 2.0, 2.5),
623                biped_small::Species::Bushly => Vec3::new(1.2, 1.3, 1.6),
624                biped_small::Species::Cactid => Vec3::new(1.0, 0.75, 1.4),
625                biped_small::Species::Irrwurz => Vec3::new(1.5, 1.5, 2.0),
626                biped_small::Species::IronDwarf => Vec3::new(1.3, 2.0, 2.5),
627                biped_small::Species::ShamanicSpirit => Vec3::new(1.3, 2.0, 2.3),
628                biped_small::Species::Jiangshi => Vec3::new(1.3, 1.8, 2.5),
629                biped_small::Species::Flamekeeper => Vec3::new(1.5, 1.5, 2.5),
630                biped_small::Species::TreasureEgg => Vec3::new(1.1, 1.1, 1.4),
631                biped_small::Species::GnarlingChieftain => Vec3::new(1.0, 0.75, 1.4),
632                biped_small::Species::BloodmoonHeiress => Vec3::new(3.5, 3.5, 5.5),
633                biped_small::Species::Bloodservant => Vec3::new(1.3, 1.8, 2.5),
634                biped_small::Species::Harlequin => Vec3::new(1.3, 2.0, 2.5),
635                biped_small::Species::GoblinThug => Vec3::new(1.3, 1.0, 1.6),
636                biped_small::Species::GoblinChucker => Vec3::new(1.3, 1.0, 1.6),
637                biped_small::Species::GoblinRuffian => Vec3::new(1.3, 1.0, 1.6),
638                biped_small::Species::GreenLegoom => Vec3::new(0.9, 0.8, 1.15),
639                biped_small::Species::OchreLegoom => Vec3::new(0.9, 0.8, 1.15),
640                biped_small::Species::PurpleLegoom => Vec3::new(0.9, 0.8, 1.15),
641                biped_small::Species::RedLegoom => Vec3::new(0.9, 0.8, 1.15),
642                biped_small::Species::UmberLegoom => Vec3::new(0.9, 0.8, 1.15),
643                biped_small::Species::Gnome => Vec3::new(1.0, 0.75, 1.4),
644                // TODO: doublecheck, derived from default
645                biped_small::Species::Mandragora => Vec3::new(1.0, 0.75, 1.4),
646                biped_small::Species::Kappa => Vec3::new(1.0, 0.75, 1.4),
647                biped_small::Species::Gnoll => Vec3::new(1.0, 0.75, 1.4),
648            },
649            Body::BirdLarge(body) => match body.species {
650                bird_large::Species::Cockatrice => Vec3::new(2.5, 5.5, 3.5),
651                bird_large::Species::Roc => Vec3::new(2.2, 7.5, 4.0),
652                bird_large::Species::FlameWyvern
653                | bird_large::Species::FrostWyvern
654                | bird_large::Species::CloudWyvern
655                | bird_large::Species::SeaWyvern
656                | bird_large::Species::WealdWyvern => Vec3::new(2.5, 9.0, 4.5),
657                bird_large::Species::Phoenix => Vec3::new(2.0, 6.0, 4.4),
658            },
659            Body::Dragon(body) => match body.species {
660                dragon::Species::Reddragon => Vec3::new(16.0, 10.0, 16.0),
661            },
662            Body::FishMedium(body) => match body.species {
663                fish_medium::Species::Marlin => Vec3::new(0.5, 2.0, 0.8),
664                fish_medium::Species::Icepike => Vec3::new(0.5, 2.0, 0.8),
665            },
666            Body::FishSmall(body) => match body.species {
667                fish_small::Species::Clownfish => Vec3::new(0.3, 1.2, 0.6),
668                fish_small::Species::Piranha => Vec3::new(0.3, 1.2, 0.6),
669            },
670            Body::Golem(body) => match body.species {
671                golem::Species::CoralGolem => Vec3::new(3.0, 5.0, 4.0),
672                golem::Species::ClayGolem => Vec3::new(6.8, 3.5, 7.5),
673                golem::Species::AncientEffigy => Vec3::new(2.5, 2.5, 3.8),
674                golem::Species::Mogwai => Vec3::new(2.5, 2.5, 3.8),
675                golem::Species::StoneGolem => Vec3::new(10.0, 4.5, 9.0),
676                golem::Species::Treant => Vec3::new(7.8, 4.5, 5.8),
677                golem::Species::WoodGolem => Vec3::new(5.8, 4.5, 7.2),
678                golem::Species::Gravewarden => Vec3::new(7.3, 4.5, 7.7),
679                golem::Species::IronGolem => Vec3::new(9.8, 4.5, 9.4),
680            },
681            Body::Humanoid(humanoid) => {
682                let height = humanoid.height();
683                Vec3::new(height / 1.3, 1.75 / 2.0, height)
684            },
685            Body::Object(object) => object.dimensions(),
686            Body::Item(item) => item.dimensions(),
687            Body::QuadrupedMedium(body) => match body.species {
688                quadruped_medium::Species::Akhlut => Vec3::new(2.5, 7.0, 3.0),
689                quadruped_medium::Species::Barghest => Vec3::new(2.0, 4.4, 2.7),
690                quadruped_medium::Species::Bear => Vec3::new(2.0, 3.8, 3.0),
691                quadruped_medium::Species::Catoblepas => Vec3::new(2.0, 4.0, 2.3),
692                quadruped_medium::Species::Cattle => Vec3::new(2.0, 3.6, 2.4),
693                quadruped_medium::Species::Deer => Vec3::new(2.0, 3.0, 2.2),
694                quadruped_medium::Species::Dreadhorn => Vec3::new(3.5, 7.0, 4.0),
695                quadruped_medium::Species::Frostfang => Vec3::new(1.5, 3.0, 1.5),
696                quadruped_medium::Species::Grolgar => Vec3::new(2.0, 4.0, 2.0),
697                quadruped_medium::Species::Highland => Vec3::new(2.0, 3.6, 2.4),
698                quadruped_medium::Species::Horse => Vec3::new(1.6, 3.0, 2.4),
699                quadruped_medium::Species::Lion => Vec3::new(2.0, 3.3, 2.0),
700                quadruped_medium::Species::Moose => Vec3::new(2.0, 4.0, 2.5),
701                quadruped_medium::Species::Bristleback => Vec3::new(2.0, 3.0, 2.0),
702                quadruped_medium::Species::Roshwalr => Vec3::new(3.4, 5.2, 3.7),
703                quadruped_medium::Species::Saber => Vec3::new(2.0, 3.0, 2.0),
704                quadruped_medium::Species::Tarasque => Vec3::new(2.0, 4.0, 2.6),
705                quadruped_medium::Species::Yak => Vec3::new(2.0, 3.6, 3.0),
706                quadruped_medium::Species::Mammoth => Vec3::new(7.5, 11.5, 8.0),
707                quadruped_medium::Species::Elephant => Vec3::new(7.5, 11.5, 8.0),
708                quadruped_medium::Species::Ngoubou => Vec3::new(2.0, 3.2, 2.4),
709                quadruped_medium::Species::Llama => Vec3::new(2.0, 2.5, 2.6),
710                quadruped_medium::Species::ClaySteed => Vec3::new(2.2, 4.8, 4.0),
711                quadruped_medium::Species::Alpaca => Vec3::new(2.0, 2.0, 2.0),
712                quadruped_medium::Species::Camel => Vec3::new(2.0, 4.0, 3.5),
713                quadruped_medium::Species::Wolf => Vec3::new(1.25, 3.0, 1.8),
714                // TODO: doublecheck, derived from default
715                quadruped_medium::Species::Tiger => Vec3::new(2.0, 3.0, 2.0),
716                quadruped_medium::Species::Tuskram => Vec3::new(2.0, 3.0, 2.0),
717                quadruped_medium::Species::Mouflon => Vec3::new(2.0, 3.0, 2.0),
718                quadruped_medium::Species::Bonerattler => Vec3::new(2.0, 3.0, 2.0),
719                quadruped_medium::Species::Hirdrasil => Vec3::new(2.0, 3.0, 2.0),
720                quadruped_medium::Species::Donkey => Vec3::new(2.0, 3.0, 2.0),
721                quadruped_medium::Species::Zebra => Vec3::new(2.0, 3.0, 2.0),
722                quadruped_medium::Species::Antelope => Vec3::new(2.0, 3.0, 2.0),
723                quadruped_medium::Species::Kelpie => Vec3::new(2.0, 3.0, 2.0),
724                quadruped_medium::Species::Darkhound => Vec3::new(2.0, 3.0, 2.0),
725                quadruped_medium::Species::Panda => Vec3::new(2.0, 3.0, 2.0),
726                quadruped_medium::Species::Snowleopard => Vec3::new(2.0, 3.0, 2.0),
727            },
728            Body::QuadrupedSmall(body) => match body.species {
729                quadruped_small::Species::Batfox => Vec3::new(1.4, 1.7, 1.3),
730                quadruped_small::Species::Holladon => Vec3::new(1.3, 1.9, 1.5),
731                quadruped_small::Species::Hyena => Vec3::new(1.2, 1.4, 1.3),
732                quadruped_small::Species::Truffler => Vec3::new(1.2, 1.8, 2.2),
733                quadruped_small::Species::MossySnail => Vec3::new(1.4, 1.4, 1.2),
734                quadruped_small::Species::Gecko => Vec3::new(1.2, 1.2, 0.5),
735                // TODO: doublecheck, derived from default
736                quadruped_small::Species::Pig => Vec3::new(1.2, 1.2, 1.0),
737                quadruped_small::Species::Fox => Vec3::new(1.2, 1.2, 1.0),
738                quadruped_small::Species::Sheep => Vec3::new(1.2, 1.2, 1.0),
739                quadruped_small::Species::Boar => Vec3::new(1.2, 1.2, 1.0),
740                quadruped_small::Species::Jackalope => Vec3::new(1.2, 1.2, 1.0),
741                quadruped_small::Species::Skunk => Vec3::new(1.2, 1.2, 1.0),
742                quadruped_small::Species::Cat => Vec3::new(1.2, 1.2, 1.0),
743                quadruped_small::Species::Raccoon => Vec3::new(1.2, 1.2, 1.0),
744                quadruped_small::Species::Quokka => Vec3::new(1.2, 1.2, 1.0),
745                quadruped_small::Species::Goat => Vec3::new(1.2, 1.2, 1.0),
746                quadruped_small::Species::Rabbit => Vec3::new(1.2, 1.2, 1.0),
747                quadruped_small::Species::Frog => Vec3::new(1.2, 1.2, 1.0),
748                quadruped_small::Species::Rat => Vec3::new(1.2, 1.2, 1.0),
749                quadruped_small::Species::Axolotl => Vec3::new(1.2, 1.2, 1.0),
750                quadruped_small::Species::Turtle => Vec3::new(1.2, 1.2, 1.0),
751                quadruped_small::Species::Squirrel => Vec3::new(1.2, 1.2, 1.0),
752                quadruped_small::Species::Fungome => Vec3::new(1.2, 1.2, 1.0),
753                quadruped_small::Species::Porcupine => Vec3::new(1.2, 1.2, 1.0),
754                quadruped_small::Species::Beaver => Vec3::new(1.2, 1.2, 1.0),
755                quadruped_small::Species::Hare => Vec3::new(1.2, 1.2, 1.0),
756                quadruped_small::Species::Dog => Vec3::new(1.2, 1.2, 1.0),
757                quadruped_small::Species::Seal => Vec3::new(1.2, 1.2, 1.0),
758                quadruped_small::Species::TreantSapling => Vec3::new(1.2, 1.2, 1.0),
759            },
760            Body::QuadrupedLow(body) => match body.species {
761                quadruped_low::Species::Asp => Vec3::new(2.0, 3.0, 1.7),
762                quadruped_low::Species::Crocodile => Vec3::new(1.0, 2.8, 1.3),
763                quadruped_low::Species::SeaCrocodile => Vec3::new(1.2, 4.5, 1.3),
764                quadruped_low::Species::Deadwood => Vec3::new(1.3, 1.3, 1.4),
765                quadruped_low::Species::Hakulaq => Vec3::new(1.8, 3.0, 2.0),
766                quadruped_low::Species::Dagon => Vec3::new(3.0, 6.0, 2.0),
767                quadruped_low::Species::Icedrake => Vec3::new(2.0, 5.5, 2.5),
768                quadruped_low::Species::Lavadrake => Vec3::new(2.0, 5.5, 2.5),
769                quadruped_low::Species::Mossdrake => Vec3::new(2.0, 5.5, 2.5),
770                quadruped_low::Species::Maneater => Vec3::new(2.0, 3.7, 4.0),
771                quadruped_low::Species::Monitor => Vec3::new(1.4, 3.2, 1.3),
772                quadruped_low::Species::Pangolin => Vec3::new(1.0, 2.6, 1.1),
773                quadruped_low::Species::Rocksnapper => Vec3::new(2.5, 3.5, 2.9),
774                quadruped_low::Species::Rootsnapper => Vec3::new(2.5, 3.5, 2.9),
775                quadruped_low::Species::Reefsnapper => Vec3::new(2.5, 3.5, 2.9),
776                quadruped_low::Species::Sandshark => Vec3::new(2.1, 4.3, 1.7),
777                quadruped_low::Species::Basilisk => Vec3::new(2.7, 6.0, 2.9),
778                quadruped_low::Species::Salamander => Vec3::new(1.7, 4.0, 1.3),
779                quadruped_low::Species::Elbst => Vec3::new(1.7, 4.0, 1.3),
780                quadruped_low::Species::Tortoise => Vec3::new(1.7, 2.7, 1.5),
781                quadruped_low::Species::Driggle => Vec3::new(1.6, 2.7, 1.0),
782                quadruped_low::Species::Snaretongue => Vec3::new(2.0, 2.8, 1.6),
783                quadruped_low::Species::Hydra => Vec3::new(3.0, 5.0, 2.8),
784                // TODO: doublecheck, derived from default
785                quadruped_low::Species::Alligator => Vec3::new(1.0, 1.6, 1.3),
786            },
787            Body::Ship(ship) => ship.dimensions(),
788            Body::Theropod(body) => match body.species {
789                theropod::Species::Archaeos => Vec3::new(4.5, 8.5, 8.0),
790                theropod::Species::Ntouka => Vec3::new(4.5, 9.0, 6.6),
791                theropod::Species::Odonto => Vec3::new(4.5, 8.0, 6.6),
792                theropod::Species::Dodarock => Vec3::new(2.0, 3.0, 2.6),
793                theropod::Species::Sandraptor => Vec3::new(2.0, 3.0, 2.6),
794                theropod::Species::Snowraptor => Vec3::new(2.0, 3.0, 2.6),
795                theropod::Species::Sunlizard => Vec3::new(2.0, 3.6, 2.5),
796                theropod::Species::Woodraptor => Vec3::new(2.0, 3.0, 2.6),
797                theropod::Species::Yale => Vec3::new(2.0, 3.2, 4.0),
798                theropod::Species::Axebeak => Vec3::new(2.0, 3.6, 3.0),
799            },
800            Body::Arthropod(body) => match body.species {
801                arthropod::Species::Tarantula => Vec3::new(4.0, 4.0, 1.8),
802                arthropod::Species::Blackwidow => Vec3::new(4.0, 4.0, 2.0),
803                arthropod::Species::Antlion => Vec3::new(4.0, 4.0, 2.2),
804                arthropod::Species::Hornbeetle => Vec3::new(3.2, 3.2, 1.3),
805                arthropod::Species::Leafbeetle => Vec3::new(2.4, 2.8, 1.2),
806                arthropod::Species::Stagbeetle => Vec3::new(3.2, 3.2, 1.3),
807                arthropod::Species::Weevil => Vec3::new(2.2, 2.4, 1.1),
808                arthropod::Species::Cavespider => Vec3::new(4.0, 4.0, 1.4),
809                arthropod::Species::Moltencrawler => Vec3::new(3.2, 4.0, 1.5),
810                arthropod::Species::Mosscrawler => Vec3::new(3.2, 4.0, 1.4),
811                arthropod::Species::Sandcrawler => Vec3::new(3.2, 4.0, 1.4),
812                arthropod::Species::Dagonite => Vec3::new(3.2, 4.7, 1.4),
813                arthropod::Species::Emberfly => Vec3::new(1.3, 1.5, 0.9),
814            },
815            Body::BirdMedium(body) => match body.species {
816                bird_medium::Species::SnowyOwl => Vec3::new(1.2, 1.2, 0.9),
817                bird_medium::Species::HornedOwl => Vec3::new(1.2, 1.2, 0.9),
818                bird_medium::Species::Duck => Vec3::new(0.8, 1.3, 0.8),
819                bird_medium::Species::Cockatiel => Vec3::new(0.8, 1.0, 0.7),
820                bird_medium::Species::Chicken => Vec3::new(1.2, 1.5, 0.9),
821                bird_medium::Species::Bat => Vec3::new(2.0, 1.8, 1.3),
822                bird_medium::Species::Penguin => Vec3::new(1.0, 1.0, 1.2),
823                bird_medium::Species::Goose => Vec3::new(1.5, 1.5, 1.1),
824                bird_medium::Species::Peacock => Vec3::new(1.6, 1.8, 1.4),
825                bird_medium::Species::Eagle => Vec3::new(1.5, 2.2, 1.0),
826                bird_medium::Species::Parrot => Vec3::new(1.2, 1.5, 1.1),
827                bird_medium::Species::Crow => Vec3::new(1.0, 1.2, 0.8),
828                bird_medium::Species::Dodo => Vec3::new(1.2, 1.8, 1.1),
829                bird_medium::Species::Parakeet => Vec3::new(0.8, 0.9, 0.7),
830                bird_medium::Species::Puffin => Vec3::new(1.0, 1.0, 1.0),
831                bird_medium::Species::Toucan => Vec3::new(2.1, 1.1, 1.2),
832                bird_medium::Species::BloodmoonBat => Vec3::new(3.5, 3.5, 2.5),
833                bird_medium::Species::VampireBat => Vec3::new(2.0, 1.8, 1.3),
834            },
835            Body::Crustacean(body) => match body.species {
836                crustacean::Species::Crab => Vec3::new(1.2, 1.2, 0.7),
837                crustacean::Species::SoldierCrab => Vec3::new(1.2, 1.2, 1.0),
838                crustacean::Species::Karkatha => Vec3::new(10.0, 10.0, 7.5),
839            },
840            Body::Plugin(body) => body.dimensions(),
841        }
842    }
843
844    // Note: This is used for collisions, but it's not very accurate for shapes that
845    // are very much not cylindrical. Eventually this ought to be replaced by more
846    // accurate collision shapes.
847    pub fn max_radius(&self) -> f32 {
848        let dim = self.dimensions();
849        let (x, y) = (dim.x, dim.y);
850
851        x.max(y) / 2.0
852    }
853
854    pub fn front_radius(&self) -> f32 { self.dimensions().y / 2.0 }
855
856    pub fn min_radius(&self) -> f32 {
857        let (_p0, _p1, radius) = self.sausage();
858
859        radius
860    }
861
862    /// Base of our Capsule Prism used for collisions.
863    /// Returns line segment and radius. See [this wiki page][stadium_wiki].
864    ///
865    /// [stadium_wiki]: <https://en.wikipedia.org/wiki/Stadium_(geometry)>
866    pub fn sausage(&self) -> (Vec2<f32>, Vec2<f32>, f32) {
867        // Consider this ascii-art stadium with radius `r` and line segment `a`
868        //
869        //      xxxxxxxxxxxxxxxxx
870        //
871        //        _ ----------_
872        // y    -*      r      *-
873        // y   *        r        *
874        // y  * rrr aaaaaaaaa rrr *
875        // y  *         r         *
876        // y   *        r        *
877        //       *____________ ^
878        let dim = self.dimensions();
879        // The width (shoulder to shoulder) and length (nose to tail)
880        let (width, length) = (dim.x, dim.y);
881
882        if length > width {
883            // Dachshund-like
884            let radius = width / 2.0;
885
886            let a = length - 2.0 * radius;
887
888            let p0 = Vec2::new(0.0, -a / 2.0);
889            let p1 = Vec2::new(0.0, a / 2.0);
890
891            (p0, p1, radius)
892        } else {
893            // Cyclops-like
894            let radius = length / 2.0;
895
896            let a = width - 2.0 * radius;
897
898            let p0 = Vec2::new(-a / 2.0, 0.0);
899            let p1 = Vec2::new(a / 2.0, 0.0);
900
901            (p0, p1, radius)
902        }
903    }
904
905    /// Body collider
906    pub fn collider(&self) -> Collider {
907        if let Body::Ship(ship) = self {
908            ship.make_collider()
909        } else {
910            let (p0, p1, radius) = self.sausage();
911
912            Collider::CapsulePrism(CapsulePrism {
913                p0,
914                p1,
915                radius,
916                z_min: 0.0,
917                z_max: self.height(),
918            })
919        }
920    }
921
922    /// How far away other entities should try to be. Will be added upon the
923    /// other entity's spacing_radius. So an entity with 2.0 and an entity
924    /// with 3.0 will lead to that both entities will try to keep 5.0 units
925    /// away from each other.
926    pub fn spacing_radius(&self) -> f32 {
927        self.max_radius()
928            + match self {
929                Body::QuadrupedSmall(body) => match body.species {
930                    quadruped_small::Species::Rat => 0.0,
931                    _ => 2.0,
932                },
933                Body::QuadrupedLow(body) => match body.species {
934                    quadruped_low::Species::Hakulaq => 0.0,
935                    _ => 2.0,
936                },
937                Body::BipedSmall(body) => match body.species {
938                    biped_small::Species::Husk => 3.0,
939                    _ => 2.0,
940                },
941                _ => 2.0,
942            }
943    }
944
945    /// Height from the bottom to the top (in metres)
946    pub fn height(&self) -> f32 { self.dimensions().z }
947
948    pub fn base_energy(&self) -> u16 {
949        match self {
950            Body::BipedLarge(biped_large) => match biped_large.species {
951                biped_large::Species::Dullahan => 400,
952                biped_large::Species::Cultistwarlord | biped_large::Species::Cultistwarlock => 240,
953                _ => 300,
954            },
955            Body::BirdLarge(body) => match body.species {
956                bird_large::Species::Cockatrice => 400,
957                bird_large::Species::Phoenix => 600,
958                bird_large::Species::Roc => 500,
959                bird_large::Species::FlameWyvern => 600,
960                bird_large::Species::CloudWyvern => 600,
961                bird_large::Species::FrostWyvern => 600,
962                bird_large::Species::SeaWyvern => 600,
963                bird_large::Species::WealdWyvern => 600,
964            },
965            Body::Humanoid(_) => 100,
966            Body::Object(object::Body::Crux) => 0,
967            _ => 100,
968        }
969    }
970
971    /// If this body will retain 1 hp when it would die, and consume death
972    /// protection, and entering a downed state.
973    pub fn has_death_protection(&self) -> bool { matches!(self, Body::Humanoid(_)) }
974
975    pub fn base_health(&self) -> u16 {
976        match self {
977            Body::Humanoid(_) => 100,
978            Body::QuadrupedSmall(quadruped_small) => match quadruped_small.species {
979                // T1
980                quadruped_small::Species::Batfox => 40,
981                quadruped_small::Species::Boar => 55,
982                quadruped_small::Species::Fox => 25,
983                quadruped_small::Species::Goat => 30,
984                quadruped_small::Species::Hare => 20,
985                quadruped_small::Species::Holladon => 25,
986                quadruped_small::Species::Jackalope => 30,
987                quadruped_small::Species::MossySnail => 15,
988                quadruped_small::Species::Porcupine => 25,
989                quadruped_small::Species::Sheep => 30,
990                quadruped_small::Species::TreantSapling => 20,
991                quadruped_small::Species::Truffler => 70,
992                // T2
993                quadruped_small::Species::Hyena => 85,
994                // T0
995                quadruped_small::Species::Beaver => 20,
996                quadruped_small::Species::Cat => 25,
997                quadruped_small::Species::Dog => 30,
998                quadruped_small::Species::Fungome => 15,
999                quadruped_small::Species::Pig => 25,
1000                quadruped_small::Species::Quokka => 15,
1001                quadruped_small::Species::Rabbit => 15,
1002                quadruped_small::Species::Raccoon => 20,
1003                quadruped_small::Species::Rat => 10,
1004                quadruped_small::Species::Seal => 20,
1005                quadruped_small::Species::Skunk => 20,
1006                quadruped_small::Species::Turtle => 10,
1007                _ => 5,
1008            },
1009            Body::QuadrupedMedium(quadruped_medium) => match quadruped_medium.species {
1010                // T1
1011                quadruped_medium::Species::Alpaca => 55,
1012                quadruped_medium::Species::Antelope => 70,
1013                quadruped_medium::Species::Darkhound => 80,
1014                quadruped_medium::Species::Camel => 100,
1015                quadruped_medium::Species::Cattle => 90,
1016                quadruped_medium::Species::Deer => 55,
1017                quadruped_medium::Species::Donkey => 65,
1018                quadruped_medium::Species::Horse => 75,
1019                quadruped_medium::Species::Llama => 65,
1020                quadruped_medium::Species::Mouflon => 75,
1021                quadruped_medium::Species::Zebra => 90,
1022                // T2
1023                quadruped_medium::Species::Barghest => 120,
1024                quadruped_medium::Species::Bear => 240,
1025                quadruped_medium::Species::Bristleback => 175,
1026                quadruped_medium::Species::Bonerattler => 100,
1027                quadruped_medium::Species::Frostfang => 185,
1028                quadruped_medium::Species::Highland => 205,
1029                quadruped_medium::Species::Kelpie => 150,
1030                quadruped_medium::Species::Lion => 175,
1031                quadruped_medium::Species::Moose => 265,
1032                quadruped_medium::Species::Panda => 215,
1033                quadruped_medium::Species::Saber => 210,
1034                quadruped_medium::Species::Snowleopard => 175,
1035                quadruped_medium::Species::Tiger => 205,
1036                quadruped_medium::Species::Tuskram => 175,
1037                quadruped_medium::Species::Wolf => 110,
1038                quadruped_medium::Species::Yak => 215,
1039                // T3A
1040                quadruped_medium::Species::Akhlut => 720,
1041                quadruped_medium::Species::Catoblepas => 720,
1042                quadruped_medium::Species::ClaySteed => 400,
1043                quadruped_medium::Species::Dreadhorn => 690,
1044                quadruped_medium::Species::Grolgar => 450,
1045                quadruped_medium::Species::Hirdrasil => 480,
1046                quadruped_medium::Species::Mammoth => 880,
1047                quadruped_medium::Species::Elephant => 880,
1048                quadruped_medium::Species::Ngoubou => 590,
1049                quadruped_medium::Species::Roshwalr => 640,
1050                quadruped_medium::Species::Tarasque => 370,
1051            },
1052            Body::FishMedium(fish_medium) => match fish_medium.species {
1053                // T2
1054                fish_medium::Species::Marlin => 50,
1055                fish_medium::Species::Icepike => 90,
1056            },
1057            Body::Dragon(_) => 500,
1058            Body::BirdLarge(bird_large) => match bird_large.species {
1059                // T3A
1060                bird_large::Species::Cockatrice => 540,
1061                bird_large::Species::Roc => 450,
1062                // T3B
1063                bird_large::Species::FlameWyvern
1064                | bird_large::Species::CloudWyvern
1065                | bird_large::Species::FrostWyvern
1066                | bird_large::Species::SeaWyvern
1067                | bird_large::Species::WealdWyvern => 1000,
1068                bird_large::Species::Phoenix => 2000,
1069            },
1070            Body::BirdMedium(bird_medium) => match bird_medium.species {
1071                // T0
1072                bird_medium::Species::Bat => 10,
1073                bird_medium::Species::Chicken => 10,
1074                bird_medium::Species::Cockatiel => 10,
1075                bird_medium::Species::Dodo => 20,
1076                bird_medium::Species::Duck => 10,
1077                bird_medium::Species::Parakeet => 10,
1078                bird_medium::Species::Peacock => 20,
1079                bird_medium::Species::Penguin => 10,
1080                bird_medium::Species::Puffin => 20,
1081                // T1
1082                bird_medium::Species::Crow => 15,
1083                bird_medium::Species::Eagle => 35,
1084                bird_medium::Species::Goose => 25,
1085                bird_medium::Species::HornedOwl => 35,
1086                bird_medium::Species::Parrot => 15,
1087                bird_medium::Species::SnowyOwl => 35,
1088                bird_medium::Species::Toucan => 15,
1089                // T3B
1090                bird_medium::Species::VampireBat => 100,
1091                bird_medium::Species::BloodmoonBat => 1200,
1092            },
1093            Body::FishSmall(fish_small) => match fish_small.species {
1094                // T0
1095                fish_small::Species::Clownfish => 5,
1096                // T1
1097                fish_small::Species::Piranha => 10,
1098            },
1099            Body::BipedLarge(biped_large) => match biped_large.species {
1100                biped_large::Species::Ogre => 320,
1101                biped_large::Species::Cyclops => 1000,
1102                biped_large::Species::Wendigo => 280,
1103                biped_large::Species::Cavetroll => 240,
1104                biped_large::Species::Mountaintroll => 240,
1105                biped_large::Species::Swamptroll => 240,
1106                biped_large::Species::Dullahan => 600,
1107                biped_large::Species::Mindflayer => 2000,
1108                biped_large::Species::Tidalwarrior => 1600,
1109                biped_large::Species::Yeti => 1800,
1110                biped_large::Species::Minotaur => 3000,
1111                biped_large::Species::Harvester => 1300,
1112                biped_large::Species::Blueoni => 240,
1113                biped_large::Species::Redoni => 240,
1114                biped_large::Species::Huskbrute => 800,
1115                biped_large::Species::Cultistwarlord => 200,
1116                biped_large::Species::Cultistwarlock => 200,
1117                biped_large::Species::Gigasfrost => 30000,
1118                biped_large::Species::Gigasfire => 25000,
1119                biped_large::Species::AdletElder => 1500,
1120                biped_large::Species::Tursus => 300,
1121                biped_large::Species::SeaBishop => 550,
1122                biped_large::Species::HaniwaGeneral => 600,
1123                biped_large::Species::TerracottaBesieger
1124                | biped_large::Species::TerracottaDemolisher
1125                | biped_large::Species::TerracottaPunisher
1126                | biped_large::Species::TerracottaPursuer => 300,
1127                biped_large::Species::Cursekeeper => 3000,
1128                biped_large::Species::Forgemaster => 10000,
1129                biped_large::Species::Strigoi => 800,
1130                biped_large::Species::Executioner => 800,
1131                _ => 120,
1132            },
1133            Body::BipedSmall(biped_small) => match biped_small.species {
1134                biped_small::Species::GoblinThug
1135                | biped_small::Species::GoblinChucker
1136                | biped_small::Species::GoblinRuffian => 30,
1137                biped_small::Species::GreenLegoom
1138                | biped_small::Species::OchreLegoom
1139                | biped_small::Species::PurpleLegoom
1140                | biped_small::Species::RedLegoom
1141                | biped_small::Species::UmberLegoom => 25,
1142                biped_small::Species::Cactid => 50,
1143                biped_small::Species::Gnarling => 50,
1144                biped_small::Species::GnarlingChieftain => 150,
1145                biped_small::Species::Mandragora => 65,
1146                biped_small::Species::Adlet => 65,
1147                biped_small::Species::Sahagin => 85,
1148                biped_small::Species::Haniwa => 100,
1149                biped_small::Species::Myrmidon => 100,
1150                biped_small::Species::Husk => 50,
1151                biped_small::Species::Boreal => 800,
1152                biped_small::Species::Ashen => 300,
1153                biped_small::Species::IronDwarf => 250,
1154                biped_small::Species::Irrwurz => 100,
1155                biped_small::Species::ShamanicSpirit => 240,
1156                biped_small::Species::Jiangshi => 250,
1157                biped_small::Species::Flamekeeper => 2000,
1158                biped_small::Species::BloodmoonHeiress => 2000,
1159                biped_small::Species::Bloodservant => 300,
1160                biped_small::Species::Harlequin => 500,
1161                _ => 60,
1162            },
1163            Body::Object(object) => match object {
1164                object::Body::TrainingDummy => 60000,
1165                object::Body::Crossbow => 80,
1166                object::Body::Flamethrower => 80,
1167                object::Body::Lavathrower => 80,
1168                object::Body::BarrelOrgan => 500,
1169                object::Body::HaniwaSentry => 60,
1170                object::Body::SeaLantern => 100,
1171                object::Body::TerracottaStatue => 600,
1172                object::Body::GnarlingTotemGreen => 15,
1173                object::Body::GnarlingTotemRed | object::Body::GnarlingTotemWhite => 15,
1174                object::Body::Crux => 350,
1175                _ => 1000,
1176            },
1177            Body::Item(_) => 1000,
1178            Body::Golem(golem) => match golem.species {
1179                golem::Species::WoodGolem => 120,
1180                golem::Species::ClayGolem => 350,
1181                golem::Species::Gravewarden => 1000,
1182                golem::Species::CoralGolem => 550,
1183                golem::Species::AncientEffigy => 250,
1184                golem::Species::Mogwai => 500,
1185                golem::Species::IronGolem => 2500,
1186                _ => 1000,
1187            },
1188            Body::Theropod(theropod) => match theropod.species {
1189                // T1
1190                theropod::Species::Dodarock => 20,
1191                // T2
1192                theropod::Species::Axebeak => 275,
1193                theropod::Species::Sandraptor => 110,
1194                theropod::Species::Snowraptor => 110,
1195                theropod::Species::Sunlizard => 110,
1196                theropod::Species::Woodraptor => 110,
1197                // T3A
1198                theropod::Species::Yale => 610,
1199                // T3B
1200                theropod::Species::Archaeos => 880,
1201                theropod::Species::Ntouka => 880,
1202                theropod::Species::Odonto => 1320,
1203            },
1204            Body::QuadrupedLow(quadruped_low) => match quadruped_low.species {
1205                // T1
1206                quadruped_low::Species::Driggle => 50,
1207                quadruped_low::Species::Pangolin => 20,
1208                quadruped_low::Species::Tortoise => 45,
1209                // T2
1210                quadruped_low::Species::Alligator => 130,
1211                quadruped_low::Species::Asp => 175,
1212                quadruped_low::Species::Crocodile => 145,
1213                quadruped_low::Species::Deadwood => 85,
1214                quadruped_low::Species::Elbst => 145,
1215                quadruped_low::Species::Hakulaq => 155,
1216                quadruped_low::Species::Monitor => 95,
1217                quadruped_low::Species::Salamander => 210,
1218                quadruped_low::Species::SeaCrocodile => 180,
1219                // T3A
1220                quadruped_low::Species::Dagon => 1200,
1221                quadruped_low::Species::Icedrake => 340,
1222                quadruped_low::Species::Lavadrake => 340,
1223                quadruped_low::Species::Maneater => 510,
1224                quadruped_low::Species::Mossdrake => 340,
1225                quadruped_low::Species::Rocksnapper => 400,
1226                quadruped_low::Species::Reefsnapper => 400,
1227                quadruped_low::Species::Rootsnapper => 400,
1228                quadruped_low::Species::Sandshark => 540,
1229                quadruped_low::Species::Hydra => 1000,
1230                // T3B
1231                quadruped_low::Species::Basilisk => 660,
1232                quadruped_low::Species::Snaretongue => 1500,
1233            },
1234            Body::Arthropod(arthropod) => match arthropod.species {
1235                // T1
1236                arthropod::Species::Dagonite => 70,
1237                arthropod::Species::Emberfly => 20,
1238                arthropod::Species::Leafbeetle => 40,
1239                arthropod::Species::Weevil => 40,
1240                // T2
1241                arthropod::Species::Cavespider => 170,
1242                arthropod::Species::Hornbeetle => 170,
1243                arthropod::Species::Moltencrawler => 145,
1244                arthropod::Species::Mosscrawler => 145,
1245                arthropod::Species::Sandcrawler => 145,
1246                arthropod::Species::Stagbeetle => 170,
1247                arthropod::Species::Tarantula => 155,
1248                // T3A
1249                arthropod::Species::Antlion => 480,
1250                arthropod::Species::Blackwidow => 370,
1251            },
1252            Body::Ship(_) => 1000,
1253            Body::Crustacean(crustacean) => match crustacean.species {
1254                // T0
1255                crustacean::Species::Crab => 40,
1256                // T2
1257                crustacean::Species::SoldierCrab => 50,
1258                crustacean::Species::Karkatha => 2000,
1259            },
1260            Body::Plugin(body) => body.base_health(),
1261        }
1262    }
1263
1264    pub fn flying_height(&self) -> f32 {
1265        match self {
1266            Body::BirdLarge(_) => 50.0,
1267            Body::BirdMedium(_) => 40.0,
1268            Body::Dragon(_) => 60.0,
1269            Body::Ship(ship) => ship.flying_height(),
1270            _ => 0.0,
1271        }
1272    }
1273
1274    pub fn immune_to(&self, buff: BuffKind) -> bool {
1275        match buff {
1276            BuffKind::Bleeding => match self {
1277                Body::Golem(_) | Body::Ship(_) => true,
1278                Body::Object(object) => !matches!(object, object::Body::TrainingDummy),
1279                Body::BipedSmall(b) => matches!(
1280                    b.species,
1281                    biped_small::Species::Husk
1282                        | biped_small::Species::Boreal
1283                        | biped_small::Species::IronDwarf
1284                        | biped_small::Species::Haniwa
1285                        | biped_small::Species::ShamanicSpirit
1286                        | biped_small::Species::Jiangshi
1287                ),
1288                Body::BipedLarge(b) => matches!(
1289                    b.species,
1290                    biped_large::Species::Huskbrute
1291                        | biped_large::Species::Gigasfrost
1292                        | biped_large::Species::Gigasfire
1293                        | biped_large::Species::Dullahan
1294                        | biped_large::Species::HaniwaGeneral
1295                        | biped_large::Species::TerracottaBesieger
1296                        | biped_large::Species::TerracottaDemolisher
1297                        | biped_large::Species::TerracottaPunisher
1298                        | biped_large::Species::TerracottaPursuer
1299                        | biped_large::Species::Cursekeeper
1300                ),
1301                Body::QuadrupedMedium(b) => {
1302                    matches!(b.species, quadruped_medium::Species::ClaySteed)
1303                },
1304                _ => false,
1305            },
1306            BuffKind::Crippled => match self {
1307                Body::Golem(_) | Body::Ship(_) => true,
1308                Body::Object(object) => !matches!(object, object::Body::TrainingDummy),
1309                Body::BipedLarge(b) => matches!(
1310                    b.species,
1311                    biped_large::Species::Dullahan | biped_large::Species::HaniwaGeneral
1312                ),
1313                Body::BipedSmall(b) => matches!(b.species, biped_small::Species::Haniwa),
1314                Body::QuadrupedMedium(b) => {
1315                    matches!(b.species, quadruped_medium::Species::ClaySteed)
1316                },
1317                _ => false,
1318            },
1319            BuffKind::Burning => match self {
1320                Body::Golem(g) => matches!(
1321                    g.species,
1322                    golem::Species::Gravewarden
1323                        | golem::Species::AncientEffigy
1324                        | golem::Species::IronGolem
1325                ),
1326                Body::BipedSmall(b) => matches!(
1327                    b.species,
1328                    biped_small::Species::Haniwa
1329                        | biped_small::Species::Flamekeeper
1330                        | biped_small::Species::IronDwarf
1331                ),
1332                Body::Object(object) => matches!(
1333                    object,
1334                    object::Body::HaniwaSentry
1335                        | object::Body::Lavathrower
1336                        | object::Body::Flamethrower
1337                        | object::Body::TerracottaStatue
1338                        | object::Body::Crux
1339                ),
1340                Body::QuadrupedLow(q) => matches!(
1341                    q.species,
1342                    quadruped_low::Species::Lavadrake | quadruped_low::Species::Salamander
1343                ),
1344                Body::BirdLarge(b) => matches!(
1345                    b.species,
1346                    bird_large::Species::Phoenix
1347                        | bird_large::Species::Cockatrice
1348                        | bird_large::Species::FlameWyvern
1349                        | bird_large::Species::CloudWyvern
1350                        | bird_large::Species::FrostWyvern
1351                        | bird_large::Species::SeaWyvern
1352                        | bird_large::Species::WealdWyvern
1353                ),
1354                Body::Arthropod(b) => matches!(b.species, arthropod::Species::Moltencrawler),
1355                Body::BipedLarge(b) => matches!(
1356                    b.species,
1357                    biped_large::Species::Cyclops
1358                        | biped_large::Species::Minotaur
1359                        | biped_large::Species::Forgemaster
1360                        | biped_large::Species::Gigasfire
1361                ),
1362                _ => false,
1363            },
1364            BuffKind::Ensnared => match self {
1365                Body::BipedLarge(b) => matches!(b.species, biped_large::Species::Harvester),
1366                Body::Arthropod(_) => true,
1367                _ => false,
1368            },
1369            BuffKind::Regeneration => {
1370                matches!(
1371                    self,
1372                    Body::Object(
1373                        object::Body::GnarlingTotemRed
1374                            | object::Body::GnarlingTotemGreen
1375                            | object::Body::GnarlingTotemWhite
1376                            | object::Body::Crux
1377                    )
1378                )
1379            },
1380            BuffKind::Frozen => match self {
1381                Body::BipedLarge(b) => matches!(
1382                    b.species,
1383                    biped_large::Species::Yeti
1384                        | biped_large::Species::Gigasfrost
1385                        | biped_large::Species::Tursus
1386                ),
1387                Body::QuadrupedLow(q) => matches!(q.species, quadruped_low::Species::Icedrake),
1388                Body::BirdLarge(b) => matches!(b.species, bird_large::Species::FrostWyvern),
1389                Body::BipedSmall(b) => matches!(b.species, biped_small::Species::Boreal),
1390                Body::QuadrupedMedium(b) => matches!(
1391                    b.species,
1392                    quadruped_medium::Species::Roshwalr | quadruped_medium::Species::Frostfang
1393                ),
1394                _ => false,
1395            },
1396            BuffKind::ProtectingWard => matches!(self, Body::Object(object::Body::BarrelOrgan)),
1397            _ => false,
1398        }
1399    }
1400
1401    // Entity still recieves the buff to allow for particle rendering or other
1402    // secondary effects, but still removes any direct `BuffEffect`
1403    pub fn negates_buff(&self, buff: BuffKind) -> bool {
1404        self.immune_to(buff)
1405            || match buff {
1406                BuffKind::Burning => match self {
1407                    Body::BipedSmall(b) => matches!(b.species, biped_small::Species::Ashen),
1408                    _ => false,
1409                },
1410                _ => false,
1411            }
1412    }
1413
1414    /// Returns a multiplier representing increased difficulty not accounted for
1415    /// due to AI or not using an actual weapon
1416    // TODO: Match on species
1417    pub fn combat_multiplier(&self) -> f32 {
1418        match self {
1419            Body::Object(object) => match object {
1420                object::Body::BarrelOrgan | object::Body::ArrowTurret => 0.05,
1421                object::Body::TerracottaStatue => 1.5,
1422                _ => 0.0,
1423            },
1424            Body::Ship(_) => 0.0,
1425            Body::BipedLarge(b) => match b.species {
1426                biped_large::Species::Gigasfire => 1.2,
1427                biped_large::Species::Mindflayer => 4.35,
1428                biped_large::Species::Minotaur => 4.05,
1429                biped_large::Species::Tidalwarrior => 2.75,
1430                biped_large::Species::Yeti => 2.25,
1431                _ => 1.0,
1432            },
1433            Body::BipedSmall(b) => match b.species {
1434                biped_small::Species::Ashen => 1.33,
1435                biped_small::Species::IronDwarf => 2.0,
1436                biped_small::Species::Flamekeeper => 4.0,
1437                biped_small::Species::GreenLegoom
1438                | biped_small::Species::OchreLegoom
1439                | biped_small::Species::PurpleLegoom
1440                | biped_small::Species::RedLegoom
1441                | biped_small::Species::UmberLegoom => 0.8,
1442                _ => 1.0,
1443            },
1444            Body::Golem(g) => match g.species {
1445                golem::Species::Gravewarden => 2.45,
1446                _ => 1.0,
1447            },
1448            Body::QuadrupedLow(b) => match b.species {
1449                quadruped_low::Species::Snaretongue => 2.0,
1450                _ => 1.0,
1451            },
1452            Body::QuadrupedSmall(b) => match b.species {
1453                quadruped_small::Species::Axolotl | quadruped_small::Species::Gecko => 0.6,
1454                _ => 1.0,
1455            },
1456            #[expect(unreachable_patterns)] // TODO: Remove when more medium fish species are added
1457            Body::FishMedium(b) => match b.species {
1458                fish_medium::Species::Marlin | fish_medium::Species::Icepike => 0.6,
1459                _ => 1.0,
1460            },
1461            #[expect(unreachable_patterns)] // TODO: Remove when more small fish species are added
1462            Body::FishSmall(b) => match b.species {
1463                fish_small::Species::Clownfish | fish_small::Species::Piranha => 0.6,
1464                _ => 1.0,
1465            },
1466            Body::Crustacean(b) => match b.species {
1467                crustacean::Species::Crab | crustacean::Species::SoldierCrab => 0.6,
1468                _ => 1.0,
1469            },
1470            _ => 1.0,
1471        }
1472    }
1473
1474    pub fn base_poise(&self) -> u16 {
1475        match self {
1476            Body::Humanoid(_) => 100,
1477            Body::BipedLarge(biped_large) => match biped_large.species {
1478                biped_large::Species::Mindflayer => 777,
1479                biped_large::Species::Minotaur => 340,
1480                biped_large::Species::Forgemaster => 300,
1481                biped_large::Species::Gigasfrost => 990,
1482                biped_large::Species::Gigasfire => 990,
1483                _ => 300,
1484            },
1485            Body::BipedSmall(b) => match b.species {
1486                biped_small::Species::GnarlingChieftain => 130,
1487                biped_small::Species::IronDwarf | biped_small::Species::Flamekeeper => 300,
1488                biped_small::Species::Boreal => 470,
1489                _ => 100,
1490            },
1491            Body::BirdLarge(b) => match b.species {
1492                bird_large::Species::FlameWyvern
1493                | bird_large::Species::FrostWyvern
1494                | bird_large::Species::CloudWyvern
1495                | bird_large::Species::SeaWyvern
1496                | bird_large::Species::WealdWyvern => 220,
1497                _ => 165,
1498            },
1499            Body::Golem(_) => 365,
1500            Body::QuadrupedMedium(b) => match b.species {
1501                quadruped_medium::Species::Bear | quadruped_medium::Species::Grolgar => 195,
1502                quadruped_medium::Species::Cattle
1503                | quadruped_medium::Species::Llama
1504                | quadruped_medium::Species::Alpaca
1505                | quadruped_medium::Species::Camel
1506                | quadruped_medium::Species::ClaySteed
1507                | quadruped_medium::Species::Zebra
1508                | quadruped_medium::Species::Donkey
1509                | quadruped_medium::Species::Highland
1510                | quadruped_medium::Species::Horse
1511                | quadruped_medium::Species::Kelpie
1512                | quadruped_medium::Species::Hirdrasil
1513                | quadruped_medium::Species::Antelope => 165,
1514                quadruped_medium::Species::Deer => 140,
1515                quadruped_medium::Species::Wolf
1516                | quadruped_medium::Species::Tiger
1517                | quadruped_medium::Species::Barghest
1518                | quadruped_medium::Species::Bonerattler
1519                | quadruped_medium::Species::Darkhound
1520                | quadruped_medium::Species::Moose
1521                | quadruped_medium::Species::Snowleopard
1522                | quadruped_medium::Species::Akhlut
1523                | quadruped_medium::Species::Bristleback
1524                | quadruped_medium::Species::Catoblepas
1525                | quadruped_medium::Species::Lion => 190,
1526                quadruped_medium::Species::Panda => 150,
1527                quadruped_medium::Species::Saber
1528                | quadruped_medium::Species::Yak
1529                | quadruped_medium::Species::Frostfang
1530                | quadruped_medium::Species::Tarasque
1531                | quadruped_medium::Species::Tuskram
1532                | quadruped_medium::Species::Mouflon
1533                | quadruped_medium::Species::Roshwalr
1534                | quadruped_medium::Species::Dreadhorn => 205,
1535                quadruped_medium::Species::Mammoth
1536                | quadruped_medium::Species::Elephant
1537                | quadruped_medium::Species::Ngoubou => 230,
1538            },
1539            Body::QuadrupedLow(b) => match b.species {
1540                quadruped_low::Species::Dagon => 270,
1541                quadruped_low::Species::Crocodile
1542                | quadruped_low::Species::Deadwood
1543                | quadruped_low::Species::SeaCrocodile
1544                | quadruped_low::Species::Alligator
1545                | quadruped_low::Species::Sandshark
1546                | quadruped_low::Species::Snaretongue
1547                | quadruped_low::Species::Asp => 190,
1548                quadruped_low::Species::Tortoise
1549                | quadruped_low::Species::Rocksnapper
1550                | quadruped_low::Species::Rootsnapper
1551                | quadruped_low::Species::Reefsnapper
1552                | quadruped_low::Species::Maneater
1553                | quadruped_low::Species::Hakulaq
1554                | quadruped_low::Species::Lavadrake
1555                | quadruped_low::Species::Icedrake
1556                | quadruped_low::Species::Basilisk
1557                | quadruped_low::Species::Hydra
1558                | quadruped_low::Species::Mossdrake => 205,
1559                quadruped_low::Species::Elbst
1560                | quadruped_low::Species::Salamander
1561                | quadruped_low::Species::Monitor
1562                | quadruped_low::Species::Pangolin
1563                | quadruped_low::Species::Driggle => 130,
1564            },
1565            Body::Theropod(b) => match b.species {
1566                theropod::Species::Archaeos
1567                | theropod::Species::Ntouka
1568                | theropod::Species::Odonto => 240,
1569                theropod::Species::Yale => 220,
1570                _ => 195,
1571            },
1572            _ => 100,
1573        }
1574    }
1575
1576    /// Returns the eye height for this creature.
1577    pub fn eye_height(&self, scale: f32) -> f32 { self.height() * 0.8 * scale }
1578
1579    /// Returns the "head" size ratio for this creature, like 0.35 of body.
1580    ///
1581    /// (By design?) broken for non-humanoids, cause most animals
1582    /// heads placed in the middle of their bodies, instead their
1583    /// headshots are spine shots.
1584    ///
1585    /// Unless it's a camel which defies reality and our hitbox assumptions.
1586    pub fn top_ratio(&self) -> f32 {
1587        match self {
1588            // our cartoonish characters have bigger heads than irl
1589            Body::Humanoid(_) => 0.35,
1590            // have big heads, but insanely massive bodies
1591            Body::BipedLarge(_) => 0.25,
1592            // these are basically heads on legs
1593            Body::BipedSmall(_) => 0.45,
1594            // whatever
1595            _ => 0.2,
1596        }
1597    }
1598
1599    pub fn default_light_offset(&self) -> Vec3<f32> {
1600        // TODO: Make this a manifest
1601        match self {
1602            Body::Object(_) => Vec3::unit_z() * 0.5,
1603            _ => Vec3::unit_z(),
1604        }
1605    }
1606
1607    pub fn can_strafe(&self) -> bool {
1608        matches!(
1609            self,
1610            Body::Humanoid(_)
1611                | Body::BipedSmall(_)
1612                | Body::BipedLarge(_)
1613                | Body::Crustacean(crustacean::Body {
1614                    species: crustacean::Species::Crab,
1615                    ..
1616                })
1617                // Allows for reverse gear!
1618                | Body::Ship(ship::Body::Train)
1619        )
1620    }
1621
1622    /// Component of the mounting offset specific to the mount
1623    // #[inline_tweak::tweak_fn]
1624    pub fn mount_offset(&self) -> Vec3<f32> {
1625        match self {
1626            Body::Humanoid(_) => (self.dimensions() * Vec3::new(0.5, 0.0, 0.6)).into_tuple(),
1627            Body::QuadrupedSmall(b) => match (b.species, b.body_type) {
1628                (quadruped_small::Species::Pig, _) => (0.0, 0.1, 0.6),
1629                (quadruped_small::Species::Fox, _) => (0.0, 0.1, 0.6),
1630                (quadruped_small::Species::Sheep, _) => (0.0, 0.1, 0.7),
1631                (quadruped_small::Species::Boar, _) => (0.0, -0.2, 1.0),
1632                (quadruped_small::Species::Jackalope, _) => (0.0, -0.1, 0.5),
1633                (quadruped_small::Species::Skunk, _) => (0.0, 0.0, 0.55),
1634                (quadruped_small::Species::Cat, _) => (0.0, 0.0, 0.45),
1635                (quadruped_small::Species::Batfox, _) => (0.0, -0.1, 0.7),
1636                (quadruped_small::Species::Raccoon, _) => (0.0, 0.0, 0.65),
1637                (quadruped_small::Species::Quokka, _) => (0.0, 0.1, 0.7),
1638                (quadruped_small::Species::Goat, _) => (0.0, 0.2, 0.7),
1639                (quadruped_small::Species::Holladon, _) => (0.0, -0.3, 1.05),
1640                (quadruped_small::Species::Hyena, _) => (0.0, -0.4, 0.95),
1641                (quadruped_small::Species::Rabbit, _) => (0.0, -0.05, 0.4),
1642                (quadruped_small::Species::Truffler, _) => (0.0, -0.5, 1.84),
1643                (quadruped_small::Species::Frog, _) => (0.0, -0.07, 0.35),
1644                (quadruped_small::Species::Rat, _) => (0.0, 0.35, 0.35),
1645                (quadruped_small::Species::Axolotl, _) => (0.0, 0.095, 0.295),
1646                (quadruped_small::Species::Gecko, _) => (0.0, 0.35, 0.25),
1647                (quadruped_small::Species::Turtle, _) => (0.0, -0.16, 0.5),
1648                (quadruped_small::Species::Squirrel, _) => (0.0, 0.16, 0.24),
1649                (quadruped_small::Species::Fungome, _) => (0.0, 0.03, 0.43),
1650                (quadruped_small::Species::Porcupine, _) => (0.0, 0.56, 0.74),
1651                (quadruped_small::Species::Beaver, _) => (0.0, 0.18, 0.7),
1652                (quadruped_small::Species::Hare, quadruped_small::BodyType::Female) => {
1653                    (0.0, -0.21, 0.44)
1654                },
1655                (quadruped_small::Species::Hare, quadruped_small::BodyType::Male) => {
1656                    (0.0, -0.21, 0.54)
1657                },
1658                (quadruped_small::Species::Dog, _) => (0.0, -0.27, 0.76),
1659                (quadruped_small::Species::Seal, _) => (0.0, -0.14, 0.54),
1660                (quadruped_small::Species::TreantSapling, _) => (0.0, -0.2, 1.03),
1661                (quadruped_small::Species::MossySnail, _) => (0.0, -0.34, 0.96),
1662            },
1663            Body::QuadrupedMedium(b) => match (b.species, b.body_type) {
1664                (quadruped_medium::Species::Grolgar, _) => (0.0, 0.4, 1.98),
1665                (quadruped_medium::Species::Saber, _) => (0.0, -0.1, 1.3),
1666                (quadruped_medium::Species::Tiger, _) => (0.0, 0.2, 1.63),
1667                (quadruped_medium::Species::Tuskram, _) => (0.0, -0.5, 1.5),
1668                (quadruped_medium::Species::Lion, _) => (0.0, 0.3, 1.5),
1669                (quadruped_medium::Species::Tarasque, _) => (0.0, 0.6, 2.0),
1670                (quadruped_medium::Species::Wolf, _) => (0.0, 0.5, 1.3),
1671                (quadruped_medium::Species::Frostfang, _) => (0.0, 0.05, 1.2),
1672                (quadruped_medium::Species::Mouflon, _) => (0.0, 0.3, 1.2),
1673                (quadruped_medium::Species::Catoblepas, _) => (0.0, 0.0, 2.0),
1674                (quadruped_medium::Species::Bonerattler, _) => (0.0, 0.5, 1.2),
1675                (quadruped_medium::Species::Deer, _) => (0.0, 0.2, 1.3),
1676                (quadruped_medium::Species::Hirdrasil, _) => (0.0, 0.0, 1.4),
1677                (quadruped_medium::Species::Roshwalr, _) => (0.0, 0.9, 3.2),
1678                (quadruped_medium::Species::Donkey, _) => (0.0, 0.5, 1.6),
1679                (quadruped_medium::Species::Camel, _) => (0.0, -0.1, 2.7),
1680                (quadruped_medium::Species::Zebra, _) => (0.0, 0.5, 1.8),
1681                (quadruped_medium::Species::Antelope, _) => (0.0, 0.3, 1.4),
1682                (quadruped_medium::Species::Kelpie, _) => (0.0, 0.5, 1.9),
1683                (quadruped_medium::Species::Horse, _) => (0.0, 0.0, 2.0),
1684                (quadruped_medium::Species::Barghest, _) => (0.0, 0.5, 2.3),
1685                (quadruped_medium::Species::Cattle, quadruped_medium::BodyType::Male) => {
1686                    (0.0, 0.5, 2.5)
1687                },
1688                (quadruped_medium::Species::Cattle, quadruped_medium::BodyType::Female) => {
1689                    (0.0, 0.7, 2.3)
1690                },
1691                (quadruped_medium::Species::Darkhound, _) => (0.0, 0.5, 1.4),
1692                (quadruped_medium::Species::Highland, _) => (0.0, 0.5, 2.3),
1693                (quadruped_medium::Species::Yak, _) => (0.0, 0.0, 3.0),
1694                (quadruped_medium::Species::Panda, _) => (0.0, -0.2, 1.4),
1695                (quadruped_medium::Species::Bear, _) => (0.0, -0.4, 2.35),
1696                (quadruped_medium::Species::Dreadhorn, _) => (0.0, 0.2, 3.8),
1697                (quadruped_medium::Species::Moose, _) => (0.0, -0.6, 2.1),
1698                (quadruped_medium::Species::Snowleopard, _) => (0.0, -0.5, 1.4),
1699                (quadruped_medium::Species::Mammoth, _) => (0.0, 4.9, 7.2),
1700                (quadruped_medium::Species::Elephant, _) => (0.0, 4.9, 7.2),
1701                (quadruped_medium::Species::Ngoubou, _) => (0.0, 0.3, 2.4),
1702                (quadruped_medium::Species::Llama, _) => (0.0, 0.1, 1.5),
1703                (quadruped_medium::Species::Alpaca, _) => (0.0, -0.1, 1.0),
1704                (quadruped_medium::Species::Akhlut, _) => (0.0, 1.9, 2.6),
1705                (quadruped_medium::Species::Bristleback, _) => (0.0, -0.4, 1.3),
1706                (quadruped_medium::Species::ClaySteed, _) => (0.0, -0.3, 2.8),
1707            },
1708            Body::BirdMedium(b) => match (b.species, b.body_type) {
1709                (bird_medium::Species::SnowyOwl, _) => (0.0, -0.25, 0.52),
1710                (bird_medium::Species::HornedOwl, _) => (0.0, -0.25, 0.45),
1711                (bird_medium::Species::Duck, _) => (0.0, -0.18, 0.47),
1712                (bird_medium::Species::Cockatiel, _) => (0.0, -0.05, 0.44),
1713                (bird_medium::Species::Chicken, bird_medium::BodyType::Female) => {
1714                    (0.0, -0.13, 0.62)
1715                },
1716                (bird_medium::Species::Chicken, bird_medium::BodyType::Male) => (0.0, -0.1, 0.62),
1717                (bird_medium::Species::Bat, _) => (0.0, 0.02, 0.47),
1718                (bird_medium::Species::Penguin, _) => (0.0, -0.15, 0.88),
1719                (bird_medium::Species::Goose, _) => (0.0, -0.04, 0.74),
1720                (bird_medium::Species::Peacock, _) => (0.0, -0.18, 0.77),
1721                (bird_medium::Species::Eagle, _) => (0.0, -0.26, 0.64),
1722                (bird_medium::Species::Parrot, _) => (0.0, -0.18, 0.52),
1723                (bird_medium::Species::Crow, _) => (0.0, -0.08, 0.5),
1724                (bird_medium::Species::Dodo, _) => (0.0, -0.25, 0.7),
1725                (bird_medium::Species::Parakeet, _) => (0.0, -0.08, 0.42),
1726                (bird_medium::Species::Puffin, _) => (0.0, -0.21, 0.56),
1727                (bird_medium::Species::Toucan, _) => (0.0, -0.12, 0.52),
1728                (bird_medium::Species::BloodmoonBat, _) => (0.0, 0.1, 1.6),
1729                (bird_medium::Species::VampireBat, _) => (0.0, 0.02, 0.5),
1730            },
1731            Body::FishMedium(b) => match (b.species, b.body_type) {
1732                (fish_medium::Species::Marlin, _) => (0.0, 0.26, 0.6),
1733                (fish_medium::Species::Icepike, _) => (0.0, 0.34, 0.65),
1734            },
1735            Body::Dragon(b) => match (b.species, b.body_type) {
1736                (dragon::Species::Reddragon, _) => (0.0, 0.5, 20.5),
1737            },
1738            Body::BirdLarge(b) => match (b.species, b.body_type) {
1739                (bird_large::Species::Phoenix, _) => (0.0, 0.4, 3.1),
1740                (bird_large::Species::Cockatrice, _) => (0.0, 0.94, 2.98),
1741                (bird_large::Species::Roc, _) => (0.0, 2.18, 4.23),
1742                (bird_large::Species::FlameWyvern, _) => (0.0, 1.58, 3.28),
1743                (bird_large::Species::CloudWyvern, _) => (0.0, 1.7, 3.15),
1744                (bird_large::Species::FrostWyvern, _) => (0.0, 1.8, 3.09),
1745                (bird_large::Species::SeaWyvern, _) => (0.0, 1.41, 3.31),
1746                (bird_large::Species::WealdWyvern, _) => (0.0, 1.8, 3.46),
1747            },
1748            Body::FishSmall(b) => match (b.species, b.body_type) {
1749                (fish_small::Species::Clownfish, _) => (0.0, 0.05, 0.6),
1750                (fish_small::Species::Piranha, _) => (0.0, -0.065, 0.715),
1751            },
1752            Body::BipedLarge(b) => match (b.species, b.body_type) {
1753                (biped_large::Species::Ogre, biped_large::BodyType::Female) => (1.2, 0.2, 4.2),
1754                (biped_large::Species::Ogre, biped_large::BodyType::Male) => (1.5, -0.25, 4.5),
1755                (biped_large::Species::Cyclops, _) => (3.0, 1.1, 6.6),
1756                (biped_large::Species::Wendigo, _) => (1.25, -0.2, 4.3),
1757                (biped_large::Species::Cavetroll, _) => (1.8, 0.1, 4.1),
1758                (biped_large::Species::Mountaintroll, _) => (1.9, 0.5, 4.2),
1759                (biped_large::Species::Swamptroll, _) => (1.9, 0.0, 4.3),
1760                (biped_large::Species::Dullahan, _) => (0.0, -0.2, 4.5),
1761                (biped_large::Species::Werewolf, _) => (1.0, 1.0, 2.8),
1762                (biped_large::Species::Occultsaurok, _) => (0.0, 0.4, 3.4),
1763                (biped_large::Species::Mightysaurok, _) => (0.0, 0.4, 3.4),
1764                (biped_large::Species::Slysaurok, _) => (0.0, 0.4, 3.4),
1765                (biped_large::Species::Mindflayer, _) => (1.8, 0.6, 6.6),
1766                (biped_large::Species::Minotaur, _) => (2.1, 0.4, 6.6),
1767                (biped_large::Species::Tidalwarrior, _) => (0.0, -0.5, 7.4),
1768                (biped_large::Species::Yeti, _) => (1.6, 0.5, 3.7),
1769                (biped_large::Species::Harvester, _) => (1.3, 0.4, 2.6),
1770                (biped_large::Species::Blueoni, _) => (1.7, 0.4, 3.6),
1771                (biped_large::Species::Redoni, _) => (1.7, 0.4, 3.6),
1772                (biped_large::Species::Cultistwarlord, _) => (1.1, 0.3, 2.7),
1773                (biped_large::Species::Cultistwarlock, _) => (1.0, 0.3, 2.7),
1774                (biped_large::Species::Huskbrute, _) => (1.6, 0.2, 3.8),
1775                (biped_large::Species::Tursus, _) => (1.6, 0.5, 3.4),
1776                (biped_large::Species::Gigasfrost, _) => (2.5, 0.5, 7.3),
1777                (biped_large::Species::Gigasfire, _) => (2.5, 0.5, 7.3),
1778                (biped_large::Species::AdletElder, _) => (1.2, 0.6, 2.4),
1779                (biped_large::Species::SeaBishop, _) => (0.9, 0.2, 2.0),
1780                (biped_large::Species::HaniwaGeneral, _) => (1.2, 0.4, 2.4),
1781                (biped_large::Species::TerracottaBesieger, _) => (1.5, -0.2, 3.3),
1782                (biped_large::Species::TerracottaDemolisher, _) => (1.1, -0.1, 2.2),
1783                (biped_large::Species::TerracottaPunisher, _) => (1.1, -0.1, 2.1),
1784                (biped_large::Species::TerracottaPursuer, _) => (1.1, -0.1, 2.2),
1785                (biped_large::Species::Cursekeeper, _) => (1.3, -0.4, 3.0),
1786                (biped_large::Species::Forgemaster, _) => (2.6, 0.8, 6.1),
1787                (biped_large::Species::Strigoi, _) => (1.7, -0.1, 3.6),
1788                (biped_large::Species::Executioner, _) => (1.1, 0.1, 3.3),
1789            },
1790            Body::BipedSmall(b) => match (b.species, b.body_type) {
1791                (biped_small::Species::Gnome, _) => (0.0, -0.33, 1.22),
1792                (biped_small::Species::Sahagin, _) => (0.0, 0.0, 1.9),
1793                (biped_small::Species::Adlet, _) => (0.0, -0.35, 1.7),
1794                (biped_small::Species::Gnarling, _) => (0.0, -0.3, 1.07),
1795                (biped_small::Species::Mandragora, _) => (0.0, -0.25, 0.72),
1796                (biped_small::Species::Kappa, _) => (0.0, -0.34, 1.1),
1797                (biped_small::Species::Cactid, _) => (0.0, -0.25, 1.13),
1798                (biped_small::Species::Gnoll, _) => (0.0, -0.28, 1.25),
1799                (biped_small::Species::Haniwa, _) => (0.0, -0.6, 1.91),
1800                (biped_small::Species::Myrmidon, _) => (0.0, -0.89, 0.96),
1801                (biped_small::Species::Husk, _) => (0.0, -0.5, 2.32),
1802                (biped_small::Species::Boreal, _) => (0.0, -0.81, 4.4),
1803                (biped_small::Species::Ashen, _) => (0.0, -0.81, 4.4),
1804                (biped_small::Species::Bushly, _) => (0.0, -0.26, 1.81),
1805                (biped_small::Species::Irrwurz, _) => (0.0, -0.35, 1.42),
1806                (biped_small::Species::IronDwarf, _) => (0.0, -0.12, 2.98),
1807                (biped_small::Species::Flamekeeper, _) => (0.0, -0.31, 2.2),
1808                (biped_small::Species::ShamanicSpirit, _) => (0.0, -0.03, 2.29),
1809                (biped_small::Species::Jiangshi, _) => (0.0, -0.21, 2.53),
1810                (biped_small::Species::TreasureEgg, _) => (0.0, -0.31, 1.0),
1811                (biped_small::Species::GnarlingChieftain, _) => (0.0, -0.27, 1.28),
1812                (biped_small::Species::BloodmoonHeiress, _) => (0.0, -0.12, 5.22),
1813                (biped_small::Species::Bloodservant, _) => (0.0, -0.21, 2.26),
1814                (biped_small::Species::Harlequin, _) => (0.0, -0.26, 2.17),
1815                (biped_small::Species::GoblinThug, _) => (0.0, -0.37, 0.49),
1816                (biped_small::Species::GoblinChucker, _) => (0.0, -0.83, 0.49),
1817                (biped_small::Species::GoblinRuffian, _) => (0.0, -0.37, 0.49),
1818                (biped_small::Species::GreenLegoom, _) => (0.0, -0.4, 1.48),
1819                (biped_small::Species::OchreLegoom, _) => (0.0, -0.4, 1.2),
1820                (biped_small::Species::PurpleLegoom, _) => (0.0, -0.36, 1.66),
1821                (biped_small::Species::RedLegoom, _) => (0.0, -0.3, 1.4),
1822                (biped_small::Species::UmberLegoom, _) => (0.0, -0.3, 1.4),
1823            },
1824            Body::Golem(b) => match (b.species, b.body_type) {
1825                (golem::Species::StoneGolem, _) => (0.0, 0.2, 8.8),
1826                (golem::Species::Treant, _) => (0.0, 3.6, 6.4),
1827                (golem::Species::ClayGolem, _) => (0.0, 0.0, 7.8),
1828                (golem::Species::WoodGolem, _) => (0.0, 0.5, 7.2),
1829                (golem::Species::CoralGolem, _) => (0.0, 0.0, 4.0),
1830                (golem::Species::Gravewarden, _) => (0.0, -0.5, 7.5),
1831                (golem::Species::AncientEffigy, _) => (0.0, -0.2, 3.5),
1832                (golem::Species::Mogwai, _) => (0.0, 0.4, 3.7),
1833                (golem::Species::IronGolem, _) => (0.0, 0.2, 10.2),
1834            },
1835            Body::Theropod(b) => match (b.species, b.body_type) {
1836                (theropod::Species::Archaeos, _) => (0.0, 2.2, 6.1),
1837                (theropod::Species::Odonto, _) => (0.0, 4.1, 4.0),
1838                (theropod::Species::Sandraptor, _) => (0.0, -0.15, 1.63),
1839                (theropod::Species::Snowraptor, _) => (0.0, -0.15, 1.58),
1840                (theropod::Species::Woodraptor, _) => (0.0, -0.14, 1.66),
1841                (theropod::Species::Sunlizard, _) => (0.0, -0.2, 1.75),
1842                (theropod::Species::Yale, _) => (0.0, -0.615, 2.75),
1843                (theropod::Species::Ntouka, _) => (0.0, -1.1, 5.5),
1844                (theropod::Species::Dodarock, _) => (0.0, -0.09, 1.49),
1845                (theropod::Species::Axebeak, _) => (0.0, -0.34, 1.85),
1846            },
1847            Body::QuadrupedLow(b) => match (b.species, b.body_type) {
1848                (quadruped_low::Species::Crocodile, _) => (0.0, 0.35, 0.8),
1849                (quadruped_low::Species::Alligator, _) => (0.0, 0.27, 0.8),
1850                (quadruped_low::Species::Salamander, _) => (0.0, 0.21, 0.9),
1851                (quadruped_low::Species::Monitor, _) => (0.0, 0.01, 0.64),
1852                (quadruped_low::Species::Asp, _) => (0.0, 0.21, 1.2),
1853                (quadruped_low::Species::Tortoise, _) => (0.0, 0.01, 1.37),
1854                (quadruped_low::Species::Pangolin, _) => (0.0, -0.08, 1.08),
1855                (quadruped_low::Species::Maneater, _) => (0.0, 1.3, 2.0),
1856                (quadruped_low::Species::Sandshark, _) => (0.0, -0.48, 1.62),
1857                (quadruped_low::Species::Hakulaq, _) => (0.0, 0.49, 1.51),
1858                (quadruped_low::Species::Lavadrake, _) => (0.0, 0.22, 1.61),
1859                (quadruped_low::Species::Basilisk, _) => (0.0, -0.17, 2.71),
1860                (quadruped_low::Species::Deadwood, _) => (0.0, -0.15, 1.21),
1861                (quadruped_low::Species::Icedrake, _) => (0.0, -0.7, 2.28),
1862                (quadruped_low::Species::SeaCrocodile, _) => (0.1, 0.35, 1.03),
1863                (quadruped_low::Species::Dagon, _) => (0.1, 0.92, 1.32),
1864                (quadruped_low::Species::Rocksnapper, _) => (0.0, 0.72, 2.73),
1865                (quadruped_low::Species::Rootsnapper, _) => (0.0, -0.18, 3.13),
1866                (quadruped_low::Species::Reefsnapper, _) => (0.0, 0.22, 2.32),
1867                (quadruped_low::Species::Elbst, _) => (0.0, 0.22, 0.8),
1868                (quadruped_low::Species::Mossdrake, _) => (0.0, 0.22, 2.02),
1869                (quadruped_low::Species::Driggle, _) => (0.0, 0.5, 0.8),
1870                (quadruped_low::Species::Snaretongue, _) => (0.0, -0.54, 1.35),
1871                (quadruped_low::Species::Hydra, _) => (0.0, 0.28, 2.71),
1872            },
1873            Body::Arthropod(b) => match (b.species, b.body_type) {
1874                (arthropod::Species::Tarantula, _) => (0.0, -0.65, 1.66),
1875                (arthropod::Species::Blackwidow, _) => (0.0, -0.82, 2.48),
1876                (arthropod::Species::Antlion, _) => (0.0, -0.49, 1.98),
1877                (arthropod::Species::Hornbeetle, _) => (0.0, 0.27, 1.98),
1878                (arthropod::Species::Leafbeetle, _) => (0.0, -0.06, 1.1),
1879                (arthropod::Species::Stagbeetle, _) => (0.0, 0.6, 1.48),
1880                (arthropod::Species::Weevil, _) => (0.0, -0.1, 1.02),
1881                (arthropod::Species::Cavespider, _) => (0.0, -0.65, 1.82),
1882                (arthropod::Species::Moltencrawler, _) => (0.0, 0.43, 1.82),
1883                (arthropod::Species::Mosscrawler, _) => (0.0, 0.6, 1.82),
1884                (arthropod::Species::Sandcrawler, _) => (0.0, 0.6, 1.82),
1885                (arthropod::Species::Dagonite, _) => (0.0, 1.1, 2.15),
1886                (arthropod::Species::Emberfly, _) => (0.0, -0.28, 0.36),
1887            },
1888            Body::Crustacean(b) => match (b.species, b.body_type) {
1889                (crustacean::Species::Crab, _) => (0.0, -0.22, 0.36),
1890                (crustacean::Species::SoldierCrab, _) => (0.0, -0.14, 0.5),
1891                (crustacean::Species::Karkatha, _) => (0.0, -0.23, 7.66),
1892            },
1893            Body::Ship(ship) => match ship {
1894                ship::Body::DefaultAirship => (0.0, 0.0, 10.0),
1895                ship::Body::AirBalloon => (0.0, 0.0, 5.0),
1896                ship::Body::SailBoat => (-2.0, -5.0, 4.0),
1897                ship::Body::Galleon => (-2.0, -5.0, 4.0),
1898                ship::Body::Skiff => (1.0, -2.0, 2.0),
1899                ship::Body::Submarine => (1.0, -2.0, 2.0),
1900                ship::Body::Carriage => (1.0, -2.0, 2.0),
1901                ship::Body::Cart => (1.0, -2.0, 2.0),
1902                ship::Body::Volume => (0.0, 0.0, 0.0),
1903                ship::Body::Train => (1.0, -2.0, 2.0),
1904            },
1905            _ => (0.0, 0.0, 0.0),
1906        }
1907        .into()
1908    }
1909
1910    /// Component of the mounting offset specific to the rider
1911    pub fn rider_offset(&self) -> Vec3<f32> {
1912        match self {
1913            Body::Humanoid(_) => [0.0, 0.0, 0.0],
1914            _ => [0.0, 0.0, 0.0],
1915        }
1916        .into()
1917    }
1918
1919    pub fn tether_offset_leader(&self) -> Vec3<f32> {
1920        Vec3::new(0.0, self.dimensions().y * -0.4, self.dimensions().z * 0.7)
1921    }
1922
1923    pub fn tether_offset_follower(&self) -> Vec3<f32> {
1924        Vec3::new(0.0, self.dimensions().y * 0.6, self.dimensions().z * 0.7)
1925    }
1926
1927    /// Should be only used with npc-tell_monster.
1928    ///
1929    /// If you want to use for displaying names in HUD, add new strings.
1930    /// If you want to use for anything else, add new strings.
1931    pub fn localize_npc(&self) -> Content {
1932        fn try_localize(body: &Body) -> Option<Content> {
1933            match body {
1934                Body::BipedLarge(biped_large) => biped_large.localize_npc(),
1935                _ => None,
1936            }
1937        }
1938
1939        try_localize(self).unwrap_or_else(|| Content::localized("noun-creature"))
1940    }
1941
1942    /// Read comment on `Gender` for more
1943    pub fn humanoid_gender(&self) -> Option<Gender> {
1944        match self {
1945            Body::Humanoid(b) => match b.body_type {
1946                humanoid::BodyType::Male => Some(Gender::Masculine),
1947                humanoid::BodyType::Female => Some(Gender::Feminine),
1948            },
1949            _ => None,
1950        }
1951    }
1952
1953    /// Return gender information for this entity.
1954    ///
1955    /// It's an imprecise approximation, because body type != gender, and
1956    /// we need more advanced scheme here, but that's all we have atm.
1957    ///
1958    /// At the moment used for two things:
1959    /// - Grammatical gender indicator regarding players for proper grammatical
1960    ///   agreement in sentences for languages that require it. At the moment
1961    ///   can be used only for chat messages, but should be extended further.
1962    /// - Semantic indicator to pick proper name variant for NPC. For example,
1963    ///   Hunter vs Huntress or Lion vs Lioness.
1964    pub fn default_gender(&self) -> Gender {
1965        match self {
1966            Body::Humanoid(b) => match b.body_type {
1967                humanoid::BodyType::Male => Gender::Masculine,
1968                humanoid::BodyType::Female => Gender::Feminine,
1969            },
1970            Body::QuadrupedSmall(b) => match b.body_type {
1971                quadruped_small::BodyType::Male => Gender::Masculine,
1972                quadruped_small::BodyType::Female => Gender::Feminine,
1973            },
1974            Body::QuadrupedMedium(b) => match b.body_type {
1975                quadruped_medium::BodyType::Male => Gender::Masculine,
1976                quadruped_medium::BodyType::Female => Gender::Feminine,
1977            },
1978            Body::QuadrupedLow(b) => match b.body_type {
1979                quadruped_low::BodyType::Male => Gender::Masculine,
1980                quadruped_low::BodyType::Female => Gender::Feminine,
1981            },
1982            Body::BirdMedium(b) => match b.body_type {
1983                bird_medium::BodyType::Male => Gender::Masculine,
1984                bird_medium::BodyType::Female => Gender::Feminine,
1985            },
1986            Body::BirdLarge(b) => match b.body_type {
1987                bird_large::BodyType::Male => Gender::Masculine,
1988                bird_large::BodyType::Female => Gender::Feminine,
1989            },
1990            Body::FishMedium(b) => match b.body_type {
1991                fish_medium::BodyType::Male => Gender::Masculine,
1992                fish_medium::BodyType::Female => Gender::Feminine,
1993            },
1994            Body::FishSmall(b) => match b.body_type {
1995                fish_small::BodyType::Male => Gender::Masculine,
1996                fish_small::BodyType::Female => Gender::Feminine,
1997            },
1998            Body::Dragon(b) => match b.body_type {
1999                dragon::BodyType::Male => Gender::Masculine,
2000                dragon::BodyType::Female => Gender::Feminine,
2001            },
2002            Body::BipedLarge(b) => match b.body_type {
2003                biped_large::BodyType::Male => Gender::Masculine,
2004                biped_large::BodyType::Female => Gender::Feminine,
2005            },
2006            Body::BipedSmall(b) => match b.body_type {
2007                biped_small::BodyType::Male => Gender::Masculine,
2008                biped_small::BodyType::Female => Gender::Feminine,
2009            },
2010            Body::Golem(b) => match b.body_type {
2011                golem::BodyType::Male => Gender::Masculine,
2012                golem::BodyType::Female => Gender::Feminine,
2013            },
2014            Body::Theropod(b) => match b.body_type {
2015                theropod::BodyType::Male => Gender::Masculine,
2016                theropod::BodyType::Female => Gender::Feminine,
2017            },
2018            Body::Arthropod(b) => match b.body_type {
2019                arthropod::BodyType::Male => Gender::Masculine,
2020                arthropod::BodyType::Female => Gender::Feminine,
2021            },
2022            Body::Crustacean(b) => match b.body_type {
2023                crustacean::BodyType::Male => Gender::Masculine,
2024                crustacean::BodyType::Female => Gender::Feminine,
2025            },
2026            // TODO: do smth about it
2027            Body::Plugin(_) => Gender::Neuter,
2028            Body::Object(_) | Body::Ship(_) | Body::Item(_) => Gender::Neuter,
2029        }
2030    }
2031
2032    /// For use with NPC name localization.
2033    ///
2034    /// Not a grammatical gender, keep that in mind.
2035    pub fn gender_attr(&self) -> &'static str {
2036        match self.default_gender() {
2037            Gender::Feminine => "fem",
2038            Gender::Masculine => "masc",
2039            Gender::Neuter => "neut",
2040        }
2041    }
2042}
2043
2044impl Component for Body {
2045    type Storage = DerefFlaggedStorage<Self, specs::VecStorage<Self>>;
2046}