Skip to main content

banished_game/nature/
spawn.rs

1//! SpawnComponent's independent per-band clocks.
2//!
3//! Original: `FUN_140056430`.
4use crate::scheduler::ticks;
5
6/// The spawn state of a resource that makes new resources around it (for example a tree that
7/// seeds saplings).
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Spawn {
10    // +0x28/+0x30
11    /// Component: one System::Time per SpawnDescription entry, in resource order.
12    pub timers: Vec<i64>,
13}
14
15impl Spawn {
16    /// Newly allocated entry clocks start at zero, without RNG.
17    ///
18    /// Original: `FUN_1400567f0`, `FUN_140001bb0`.
19    pub fn new(count: usize) -> Self {
20        Self {
21            timers: vec![0; count],
22        }
23    }
24
25    // +0x28
26    /// Entries contain inclusive growth bounds and the CHILD GrowthDescription duration.
27    /// Out-of-band clocks retain their previous value. Unlike GrowthComponent's periodic timer,
28    /// this consumes intervals at equality.
29    ///
30    /// Original: `FUN_140056430`.
31    pub fn update(
32        &mut self,
33        growth: f32,
34        delta: i64,
35        entries: &[(f32, f32, f32)],
36    ) -> Vec<(usize, usize)> {
37        assert_eq!(self.timers.len(), entries.len());
38        let mut attempts = Vec::new();
39        for (index, (&(lo, hi, duration), timer)) in
40            entries.iter().zip(&mut self.timers).enumerate()
41        {
42            if !(lo <= growth && growth <= hi) {
43                continue;
44            }
45            *timer += delta;
46            let interval = ticks(duration * 300.0); // DAT_140178934
47                                                    // Deviation: invalid zero/negative resource periods must not hang the browser.
48            if interval <= 0 {
49                continue;
50            }
51            let mut count = 0;
52            while interval <= *timer {
53                *timer -= interval;
54                count += 1;
55            }
56            if count != 0 {
57                attempts.push((index, count));
58            }
59        }
60        attempts
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn clocks_freeze_outside_band_and_fire_at_exact_interval() {
70        let entries = [(0.1, 1.1, 1.0), (1.1, 3.0, 0.5)];
71        let mut spawn = Spawn::new(2);
72        assert!(spawn.update(0.5, 200_000_000, &entries).is_empty());
73        assert_eq!(spawn.timers, [200_000_000, 0]);
74        assert!(spawn.update(4.0, 999_999_999, &entries).is_empty());
75        assert_eq!(spawn.timers, [200_000_000, 0]);
76        assert_eq!(spawn.update(1.1, 100_000_000, &entries), [(0, 1)]);
77        assert_eq!(spawn.timers, [0, 100_000_000]);
78        assert_eq!(spawn.update(1.1, 650_000_000, &entries), [(0, 2), (1, 5)]);
79        assert_eq!(spawn.timers, [50_000_000, 0]);
80    }
81}