1use super::SemanticSupport;
4
5bitflags::bitflags! {
6 #[derive(Copy, Clone, Debug, Default, Eq, Hash, PartialEq)]
8 pub struct SpellCastTargetFlags: u32 {
9 const UNUSED_0 = 1 << 0;
10 const UNIT = 1 << 1;
11 const UNIT_RAID = 1 << 2;
12 const UNIT_PARTY = 1 << 3;
13 const ITEM = 1 << 4;
14 const SOURCE_LOCATION = 1 << 5;
15 const DEST_LOCATION = 1 << 6;
16 const UNIT_ENEMY = 1 << 7;
17 const UNIT_ALLY = 1 << 8;
18 const CORPSE_ENEMY = 1 << 9;
19 const UNIT_DEAD = 1 << 10;
20 const GAMEOBJECT = 1 << 11;
21 const TRADE_ITEM = 1 << 12;
22 const STRING = 1 << 13;
23 const GAMEOBJECT_ITEM = 1 << 14;
24 const CORPSE_ALLY = 1 << 15;
25 const UNIT_MINIPET = 1 << 16;
26 const GLYPH_SLOT = 1 << 17;
27 const DEST_TARGET = 1 << 18;
28 const EXTRA_TARGETS = 1 << 19;
29 const UNIT_PASSENGER = 1 << 20;
30 const UNKNOWN_22 = 1 << 22;
31 const UNKNOWN_24 = 1 << 24;
32 const UNKNOWN_26 = 1 << 26;
33 const UNKNOWN_28 = 1 << 28;
34 const UNKNOWN_30 = 1 << 30;
35
36 const UNIT_MASK = Self::UNIT.bits()
37 | Self::UNIT_RAID.bits()
38 | Self::UNIT_PARTY.bits()
39 | Self::UNIT_ENEMY.bits()
40 | Self::UNIT_ALLY.bits()
41 | Self::UNIT_DEAD.bits()
42 | Self::UNIT_MINIPET.bits()
43 | Self::UNIT_PASSENGER.bits();
44 const GAMEOBJECT_MASK = Self::GAMEOBJECT.bits() | Self::GAMEOBJECT_ITEM.bits();
45 const CORPSE_MASK = Self::CORPSE_ALLY.bits() | Self::CORPSE_ENEMY.bits();
46 const ITEM_MASK = Self::TRADE_ITEM.bits() | Self::ITEM.bits() | Self::GAMEOBJECT_ITEM.bits();
47 }
48}
49
50impl SpellCastTargetFlags {
51 #[must_use]
53 pub const fn from_dbc(raw: i32) -> Self {
54 Self::from_bits_retain(u32::from_ne_bytes(raw.to_ne_bytes()))
55 }
56
57 #[must_use]
59 pub const fn requires_hostile_unit(self) -> bool {
60 self.intersects(Self::from_bits_retain(
61 Self::UNIT.bits() | Self::UNIT_ENEMY.bits(),
62 ))
63 }
64}
65
66#[derive(Clone, Copy, Debug, Eq, PartialEq)]
68pub struct SpellCastTargetFlagSemantic {
69 pub flag: SpellCastTargetFlags,
70 pub name: &'static str,
71 pub support: SemanticSupport,
72 pub handler: &'static str,
73}
74
75const fn target_flag(
76 flag: SpellCastTargetFlags,
77 name: &'static str,
78 support: SemanticSupport,
79 handler: &'static str,
80) -> SpellCastTargetFlagSemantic {
81 SpellCastTargetFlagSemantic {
82 flag,
83 name,
84 support,
85 handler,
86 }
87}
88
89use SemanticSupport::{Generic, Ignored, Partial, Unsupported};
90
91pub const SPELL_CAST_TARGET_FLAG_SEMANTICS: &[SpellCastTargetFlagSemantic] = &[
93 target_flag(
94 SpellCastTargetFlags::UNUSED_0,
95 "Unused 0",
96 Ignored,
97 "unused protocol bit",
98 ),
99 target_flag(
100 SpellCastTargetFlags::UNIT,
101 "Unit",
102 Generic,
103 "shared explicit-unit cast and range resolution",
104 ),
105 target_flag(
106 SpellCastTargetFlags::UNIT_RAID,
107 "Raid Unit",
108 Unsupported,
109 "friendly raid actor selection",
110 ),
111 target_flag(
112 SpellCastTargetFlags::UNIT_PARTY,
113 "Party Unit",
114 Unsupported,
115 "friendly party actor selection",
116 ),
117 target_flag(
118 SpellCastTargetFlags::ITEM,
119 "Item",
120 Unsupported,
121 "inventory item cast targets",
122 ),
123 target_flag(
124 SpellCastTargetFlags::SOURCE_LOCATION,
125 "Source Location",
126 Generic,
127 "cast requests snapshot and carry the explicit source position into target execution",
128 ),
129 target_flag(
130 SpellCastTargetFlags::DEST_LOCATION,
131 "Destination Location",
132 Generic,
133 "cast requests snapshot and carry authored destination positions into target execution",
134 ),
135 target_flag(
136 SpellCastTargetFlags::UNIT_ENEMY,
137 "Enemy Unit",
138 Generic,
139 "shared hostile-unit cast, range, and line-of-sight resolution",
140 ),
141 target_flag(
142 SpellCastTargetFlags::UNIT_ALLY,
143 "Ally Unit",
144 Unsupported,
145 "explicit friendly actor selection",
146 ),
147 target_flag(
148 SpellCastTargetFlags::CORPSE_ENEMY,
149 "Enemy Corpse",
150 Unsupported,
151 "corpse actor selection",
152 ),
153 target_flag(
154 SpellCastTargetFlags::UNIT_DEAD,
155 "Dead Unit",
156 Unsupported,
157 "dead-unit selection",
158 ),
159 target_flag(
160 SpellCastTargetFlags::GAMEOBJECT,
161 "Game Object",
162 Unsupported,
163 "game-object selection",
164 ),
165 target_flag(
166 SpellCastTargetFlags::TRADE_ITEM,
167 "Trade Item",
168 Unsupported,
169 "trade item targets",
170 ),
171 target_flag(
172 SpellCastTargetFlags::STRING,
173 "String",
174 Unsupported,
175 "string cast-target payloads",
176 ),
177 target_flag(
178 SpellCastTargetFlags::GAMEOBJECT_ITEM,
179 "Game Object or Item",
180 Unsupported,
181 "game-object and item selection",
182 ),
183 target_flag(
184 SpellCastTargetFlags::CORPSE_ALLY,
185 "Ally Corpse",
186 Unsupported,
187 "friendly corpse selection",
188 ),
189 target_flag(
190 SpellCastTargetFlags::UNIT_MINIPET,
191 "Minipet",
192 Unsupported,
193 "minipet actor selection",
194 ),
195 target_flag(
196 SpellCastTargetFlags::GLYPH_SLOT,
197 "Glyph Slot",
198 Unsupported,
199 "glyph-slot payloads",
200 ),
201 target_flag(
202 SpellCastTargetFlags::DEST_TARGET,
203 "Destination Target",
204 Partial,
205 "implicit target-derived destinations",
206 ),
207 target_flag(
208 SpellCastTargetFlags::EXTRA_TARGETS,
209 "Extra Targets",
210 Unsupported,
211 "explicit extra-target payloads",
212 ),
213 target_flag(
214 SpellCastTargetFlags::UNIT_PASSENGER,
215 "Passenger Unit",
216 Unsupported,
217 "vehicle passenger selection",
218 ),
219 target_flag(
220 SpellCastTargetFlags::UNKNOWN_22,
221 "Unknown 22",
222 Unsupported,
223 "unknown TrinityCore target flag",
224 ),
225 target_flag(
226 SpellCastTargetFlags::UNKNOWN_24,
227 "Unknown 24",
228 Unsupported,
229 "unknown TrinityCore target flag",
230 ),
231 target_flag(
232 SpellCastTargetFlags::UNKNOWN_26,
233 "Unknown 26",
234 Unsupported,
235 "unknown TrinityCore target flag",
236 ),
237 target_flag(
238 SpellCastTargetFlags::UNKNOWN_28,
239 "Unknown 28",
240 Unsupported,
241 "unknown TrinityCore target flag",
242 ),
243 target_flag(
244 SpellCastTargetFlags::UNKNOWN_30,
245 "Unknown 30",
246 Unsupported,
247 "unknown TrinityCore target flag",
248 ),
249];
250
251#[must_use]
253pub fn spell_cast_target_flag_semantic(
254 flag: SpellCastTargetFlags,
255) -> Option<&'static SpellCastTargetFlagSemantic> {
256 SPELL_CAST_TARGET_FLAG_SEMANTICS
257 .iter()
258 .find(|semantic| semantic.flag == flag)
259}
260
261#[cfg(test)]
262mod tests {
263 use googletest::prelude::*;
264
265 use super::*;
266
267 #[gtest]
268 fn retains_unknown_bits_and_exposes_trinity_composites() {
269 let raw = i32::from_ne_bytes(((1_u32 << 30) | (1 << 22) | (1 << 7)).to_ne_bytes());
270 let mask = SpellCastTargetFlags::from_dbc(raw);
271
272 expect_that!(mask.contains(SpellCastTargetFlags::UNIT_ENEMY), is_true());
273 expect_that!(mask.contains(SpellCastTargetFlags::UNKNOWN_22), is_true());
274 expect_that!(mask.contains(SpellCastTargetFlags::UNKNOWN_30), is_true());
275 expect_that!(
276 SpellCastTargetFlags::UNIT_MASK.contains(SpellCastTargetFlags::UNIT_ENEMY),
277 is_true()
278 );
279 }
280
281 #[gtest]
282 fn every_declared_single_bit_flag_has_coverage() {
283 for bit in 0..u32::BITS {
284 let flag = SpellCastTargetFlags::from_bits_retain(1 << bit);
285
286 if SpellCastTargetFlags::all().contains(flag) {
287 expect_that!(spell_cast_target_flag_semantic(flag).is_some(), is_true());
288 }
289 }
290 }
291}