1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use crate::comp::{body::Body, quadruped_medium};
use crossbeam_utils::atomic::AtomicCell;
use specs::Component;
use std::{num::NonZeroU64, sync::Arc};

use super::Mass;

pub type PetId = AtomicCell<Option<NonZeroU64>>;

// TODO: move to server crate
#[derive(Clone, Debug)]
pub struct Pet {
    database_id: Arc<PetId>,
}

impl Pet {
    /// Not to be used outside of persistence - provides mutable access to the
    /// pet component's database ID which is required to save the pet's data
    /// from the persistence thread.
    #[doc(hidden)]
    pub fn get_database_id(&self) -> Arc<PetId> { Arc::clone(&self.database_id) }

    pub fn new_from_database(database_id: NonZeroU64) -> Self {
        Self {
            database_id: Arc::new(AtomicCell::new(Some(database_id))),
        }
    }
}

impl Default for Pet {
    fn default() -> Self {
        Self {
            database_id: Arc::new(AtomicCell::new(None)),
        }
    }
}

/// Determines whether an entity of a particular body variant is tameable.
pub fn is_tameable(body: &Body) -> bool {
    // Currently only Quadruped animals can be tamed pending further work
    // on the pets feature (allowing larger animals to be tamed will
    // require balance issues to be addressed).
    match body {
        Body::QuadrupedMedium(quad_med) =>
        // NOTE: the reason we ban mammoth from being tameable even though they're
        // agressive anyway, is that UncomfySilence is going to make them
        // peaceful after this MR gets merged. Please, remove this note in your MR,
        // UncomfySilence!
        {
            !matches!(
                quad_med.species,
                quadruped_medium::Species::Catoblepas
                    | quadruped_medium::Species::Mammoth
                    | quadruped_medium::Species::Hirdrasil
            )
        },
        Body::QuadrupedLow(_)
        | Body::QuadrupedSmall(_)
        | Body::BirdMedium(_)
        | Body::Crustacean(_) => true,
        _ => false,
    }
}

pub fn is_mountable(
    mount: &Body,
    mount_mass: &Mass,
    rider: Option<&Body>,
    rider_mass: Option<&Mass>,
) -> bool {
    let is_light_enough = rider_mass.map_or(false, |r| r.0 / mount_mass.0 < 0.7);

    match mount {
        Body::Humanoid(_) => matches!(rider, Some(Body::BirdMedium(_))) && is_light_enough,
        Body::Ship(_) => true,
        Body::Object(_) => false,
        Body::ItemDrop(_) => false,
        _ => is_light_enough,
    }
}

impl Component for Pet {
    // Using `DenseVecStorage` has a u64 space overhead per entity and `Pet` just
    // has an `Arc` pointer which is the same size on 64-bit platforms. So it
    // isn't worth using `DenseVecStorage` here.
    type Storage = specs::VecStorage<Self>;
}