Skip to main content

wowlab_engine_domain/rotation/buffer/
resource.rs

1wowlab_engine_macros::define_slot! {
2    #[slot(domain = "resource", kind = keyed, key_domain = "resource")]
3    pub struct ResourceSlot {
4        #[expr("current", Float, Direct)]
5        #[expr("deficit", Float, ResourceDeficit)]
6        #[expr("pct", Float, ResourcePct)]
7        #[expr("deficit_pct", Float, ResourceDeficitPct)]
8        #[expr("time_to_max", Float, ResourceTimeToMax)]
9        pub current: f64,
10
11        #[expr("max", Float, Direct)]
12        pub max: f64,
13
14        #[expr("regen", Float, Direct)]
15        pub regen_per_sec: f64,
16    }
17
18    impl {
19        #[inline]
20        pub fn spend(&mut self, amount: f64) -> bool {
21            if self.current < amount {
22                return false;
23            }
24            self.current -= amount;
25            true
26        }
27
28        #[inline]
29        // docref:start resources-slot-gain
30        pub fn gain(&mut self, amount: f64) -> f64 {
31            let new = (self.current + amount).min(self.max);
32            let actual = new - self.current;
33            self.current = new;
34            amount - actual
35        }
36        // docref:end resources-slot-gain
37
38        #[must_use]
39        pub fn deficit(&self) -> f64 {
40            crate::rotation::lower::ops::eval_resource_deficit(
41                &mut crate::rotation::lower::ops::ScalarEvalOps,
42                self.current,
43                self.max,
44            )
45        }
46
47        #[must_use]
48        pub fn pct(&self) -> f64 {
49            crate::rotation::lower::ops::eval_resource_pct(
50                &mut crate::rotation::lower::ops::ScalarEvalOps,
51                self.current,
52                self.max,
53            )
54        }
55
56        #[must_use]
57        pub fn deficit_pct(&self) -> f64 {
58            crate::rotation::lower::ops::eval_resource_deficit_pct(
59                &mut crate::rotation::lower::ops::ScalarEvalOps,
60                self.current,
61                self.max,
62            )
63        }
64
65        #[must_use]
66        pub fn time_to_max(&self) -> f64 {
67            crate::rotation::lower::ops::eval_resource_time_to_max(
68                &mut crate::rotation::lower::ops::ScalarEvalOps,
69                self.current,
70                self.max,
71                self.regen_per_sec,
72            )
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use googletest::prelude::*;
80
81    use super::*;
82
83    #[gtest]
84    fn gain_returns_overflow_above_cap() -> Result<()> {
85        let mut slot = ResourceSlot {
86            current: 600.0,
87            max: 1000.0,
88            ..Default::default()
89        };
90        let overflow = slot.gain(500.0);
91
92        verify_that!(overflow, near(100.0, 1e-9))?;
93
94        verify_that!(slot.current, near(1000.0, 1e-9))
95    }
96
97    #[gtest]
98    fn gain_returns_zero_under_cap() -> Result<()> {
99        let mut slot = ResourceSlot {
100            current: 600.0,
101            max: 1000.0,
102            ..Default::default()
103        };
104        let overflow = slot.gain(200.0);
105
106        verify_that!(overflow, near(0.0, 1e-9))?;
107
108        verify_that!(slot.current, near(800.0, 1e-9))
109    }
110
111    #[gtest]
112    fn spend_rejects_insufficient_and_leaves_current() -> Result<()> {
113        let mut slot = ResourceSlot {
114            current: 100.0,
115            ..Default::default()
116        };
117
118        verify_that!(slot.spend(150.0), eq(false))?;
119
120        verify_that!(slot.current, near(100.0, 1e-9))
121    }
122
123    #[gtest]
124    fn spend_deducts_when_affordable() -> Result<()> {
125        let mut slot = ResourceSlot {
126            current: 100.0,
127            ..Default::default()
128        };
129
130        verify_that!(slot.spend(40.0), eq(true))?;
131
132        verify_that!(slot.current, near(60.0, 1e-9))
133    }
134}