wowlab_engine_domain/rotation/
result.rs1use static_assertions::const_assert_eq;
4use wowlab_types::{game::GearSlot, sim::SpellIdx};
5
6#[derive(Clone, Copy, Debug, PartialEq)]
9#[repr(C)]
10pub struct EvalResult {
11 pub kind: u8,
12 pub empower_rank: u8,
14 pub spell_id: u32,
16 pub wait_time: f32,
18}
19pub(super) const KIND_NONE: u8 = 0;
22pub(super) const KIND_CAST: u8 = 1;
23pub(super) const KIND_WAIT: u8 = 2;
24pub(super) const KIND_POOL: u8 = 3;
25pub(super) const KIND_USE_ITEM: u8 = 4;
26
27const_assert_eq!(size_of::<EvalResult>(), 12);
28
29impl EvalResult {
30 pub const NONE: Self = Self {
31 kind: KIND_NONE,
32 empower_rank: 0,
33 spell_id: 0,
34 wait_time: 0.0,
35 };
36
37 #[must_use]
38 pub fn cast(spell: SpellIdx, empower_rank: u8) -> Self {
39 Self {
40 kind: KIND_CAST,
41 empower_rank,
42 spell_id: spell.0,
43 wait_time: 0.0,
44 }
45 }
46
47 #[must_use]
48 pub fn wait(seconds: f32) -> Self {
49 Self {
50 kind: KIND_WAIT,
51 empower_rank: 0,
52 spell_id: 0,
53 wait_time: seconds,
54 }
55 }
56
57 #[must_use]
58 pub fn pool(target: f32) -> Self {
59 Self {
60 kind: KIND_POOL,
61 empower_rank: 0,
62 spell_id: 0,
63 wait_time: target,
64 }
65 }
66
67 #[must_use]
68 pub fn use_item(slot: GearSlot, empower_rank: u8) -> Self {
69 Self::use_item_raw(slot as u8, empower_rank)
71 }
72
73 #[must_use]
75 pub fn use_item_raw(slot_repr: u8, empower_rank: u8) -> Self {
76 Self {
77 kind: KIND_USE_ITEM,
78 empower_rank,
79 spell_id: u32::from(slot_repr),
80 wait_time: 0.0,
81 }
82 }
83
84 #[must_use]
85 pub fn is_none(&self) -> bool {
86 self.kind == KIND_NONE
87 }
88
89 #[must_use]
90 pub fn is_cast(&self) -> bool {
91 self.kind == KIND_CAST
92 }
93
94 #[must_use]
95 pub fn is_wait(&self) -> bool {
96 self.kind == KIND_WAIT
97 }
98
99 #[must_use]
100 pub fn is_pool(&self) -> bool {
101 self.kind == KIND_POOL
102 }
103
104 #[must_use]
105 pub fn is_use_item(&self) -> bool {
106 self.kind == KIND_USE_ITEM
107 }
108
109 #[must_use]
110 pub fn item_slot(&self) -> Option<GearSlot> {
111 u8::try_from(self.spell_id)
112 .ok()
113 .and_then(|slot| GearSlot::try_from(slot).ok())
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use googletest::prelude::*;
120
121 use super::*;
122
123 #[gtest]
124 fn use_item_creates_correct_result() {
125 let result = EvalResult::use_item(GearSlot::Trinket1, 3);
126
127 expect_that!(
128 result,
129 matches_pattern!(EvalResult {
130 kind: eq(KIND_USE_ITEM),
131 empower_rank: eq(3),
132 spell_id: eq(GearSlot::Trinket1 as u32),
133 wait_time: near(0.0, f32::EPSILON),
134 })
135 );
136 }
137
138 #[gtest]
139 fn is_use_item_true_for_item_results() {
140 let item = EvalResult::use_item(GearSlot::Trinket2, 0);
141
142 expect_that!(item.is_use_item(), is_true());
143 }
144
145 #[gtest]
146 fn is_use_item_false_for_other_kinds() {
147 let cast = EvalResult::cast(SpellIdx(100), 0);
148
149 expect_that!(!cast.is_use_item(), is_true());
150
151 let wait = EvalResult::wait(1.0);
152
153 expect_that!(!wait.is_use_item(), is_true());
154
155 let pool = EvalResult::pool(50.0);
156
157 expect_that!(!pool.is_use_item(), is_true());
158
159 expect_that!(!EvalResult::NONE.is_use_item(), is_true());
160 }
161
162 #[gtest]
163 fn item_slot_returns_correct_gear_slot() {
164 let result = EvalResult::use_item(GearSlot::Trinket1, 0);
165
166 expect_that!(result.item_slot(), eq(Some(GearSlot::Trinket1)));
167
168 let result2 = EvalResult::use_item(GearSlot::Trinket2, 0);
169
170 expect_that!(result2.item_slot(), eq(Some(GearSlot::Trinket2)));
171 }
172
173 #[gtest]
174 fn item_slot_returns_none_for_invalid_value() {
175 let result = EvalResult {
176 kind: KIND_USE_ITEM,
177 empower_rank: 0,
178 spell_id: 255,
179 wait_time: 0.0,
180 };
181
182 expect_that!(result.item_slot(), eq(None));
183 }
184}