Skip to main content

wowlab_engine_domain/dbc/semantics/
spell_cast_target_flags.rs

1//! Explicit spell cast-target payload and validation flags.
2
3use super::SemanticSupport;
4
5bitflags::bitflags! {
6    /// TrinityCore `SpellCastTargetFlags`, sourced from `SpellDefines.h`.
7    #[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    /// Preserve every authored DBC bit, including currently unknown bits.
52    #[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    /// Whether the cast requires an explicitly selected hostile unit.
58    #[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/// Engine coverage for one authored or derived cast-target flag.
67#[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
91/// Canonical coverage registry for `TrinityCore` cast-target flags.
92pub 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/// Lookup one single-bit cast-target flag in the canonical coverage registry.
252#[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}