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