Skip to main content

steel_core/entity/
spawn.rs

1/// Vanilla `EntitySpawnReason`.
2#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3pub enum EntitySpawnReason {
4    Natural,
5    ChunkGeneration,
6    Spawner,
7    Structure,
8    Breeding,
9    MobSummoned,
10    Jockey,
11    Event,
12    Conversion,
13    Reinforcement,
14    Triggered,
15    Bucket,
16    SpawnItemUse,
17    Command,
18    Dispenser,
19    Patrol,
20    TrialSpawner,
21    Load,
22    DimensionTravel,
23}
24
25impl EntitySpawnReason {
26    #[must_use]
27    pub const fn is_spawner(self) -> bool {
28        matches!(self, Self::Spawner | Self::TrialSpawner)
29    }
30
31    #[must_use]
32    pub const fn ignores_light_requirements(self) -> bool {
33        matches!(self, Self::TrialSpawner)
34    }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq)]
38pub enum SpawnGroupData {
39    AgeableMob(AgeableMobGroupData),
40}
41
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub struct AgeableMobGroupData {
44    group_size: i32,
45    should_spawn_baby: bool,
46    baby_spawn_chance: f32,
47}
48
49impl AgeableMobGroupData {
50    pub const DEFAULT_BABY_SPAWN_CHANCE: f32 = 0.05;
51
52    #[must_use]
53    pub const fn new(should_spawn_baby: bool, baby_spawn_chance: f32) -> Self {
54        Self {
55            group_size: 0,
56            should_spawn_baby,
57            baby_spawn_chance,
58        }
59    }
60
61    #[must_use]
62    pub const fn with_should_spawn_baby(should_spawn_baby: bool) -> Self {
63        Self::new(should_spawn_baby, Self::DEFAULT_BABY_SPAWN_CHANCE)
64    }
65
66    #[must_use]
67    pub const fn with_baby_spawn_chance(baby_spawn_chance: f32) -> Self {
68        Self::new(true, baby_spawn_chance)
69    }
70
71    #[must_use]
72    pub const fn group_size(self) -> i32 {
73        self.group_size
74    }
75
76    #[must_use]
77    pub const fn should_spawn_baby(self) -> bool {
78        self.should_spawn_baby
79    }
80
81    #[must_use]
82    pub const fn baby_spawn_chance(self) -> f32 {
83        self.baby_spawn_chance
84    }
85
86    pub const fn increase_group_size_by_one(&mut self) {
87        self.group_size += 1;
88    }
89
90    #[must_use]
91    pub const fn needs_baby_spawn_roll(self) -> bool {
92        self.should_spawn_baby && self.group_size > 0
93    }
94
95    pub fn finalize_ageable_spawn(&mut self, baby_roll: impl FnOnce() -> f32) -> bool {
96        let spawn_baby = self.needs_baby_spawn_roll() && baby_roll() <= self.baby_spawn_chance;
97        self.increase_group_size_by_one();
98        spawn_baby
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::AgeableMobGroupData;
105
106    #[test]
107    fn ageable_group_data_increments_before_later_baby_rolls_can_apply() {
108        let mut group_data = AgeableMobGroupData::with_should_spawn_baby(true);
109
110        assert!(!group_data.finalize_ageable_spawn(|| {
111            panic!("first group member should not roll for baby spawn")
112        }));
113        assert_eq!(group_data.group_size(), 1);
114
115        assert!(group_data.finalize_ageable_spawn(|| 0.05));
116        assert_eq!(group_data.group_size(), 2);
117    }
118
119    #[test]
120    fn ageable_group_data_can_disable_baby_spawns() {
121        let mut group_data = AgeableMobGroupData::with_should_spawn_baby(false);
122
123        assert!(
124            !group_data
125                .finalize_ageable_spawn(|| { panic!("disabled baby spawning should not roll") })
126        );
127        assert!(
128            !group_data
129                .finalize_ageable_spawn(|| { panic!("disabled baby spawning should not roll") })
130        );
131        assert_eq!(group_data.group_size(), 2);
132    }
133}