Skip to main content

wowlab_engine_domain/dbc/semantics/
implicit_targets.rs

1//! Canonical implicit-target selector semantics; DBC `ImplicitTarget` values index this table, not bit flags.
2
3/// Raw `Targets` selector identity when no implicit selector is present.
4#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, num_enum::TryFromPrimitive)]
5#[repr(i32)]
6#[non_exhaustive]
7pub enum ImplicitTargetSelector {
8    #[default]
9    None = 0,
10}
11
12/// Object produced or selected by an implicit-target selector.
13#[derive(Clone, Copy, Debug, Eq, PartialEq)]
14#[repr(u8)]
15#[non_exhaustive]
16pub enum ImplicitTargetObject {
17    None,
18    Source,
19    Destination,
20    Unit,
21    UnitAndDestination,
22    GameObject,
23    GameObjectItem,
24    Item,
25    Corpse,
26    CorpseEnemy,
27    CorpseAlly,
28}
29
30/// Coordinate or actor from which an implicit selector resolves.
31#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32#[repr(u8)]
33#[non_exhaustive]
34pub enum ImplicitTargetReference {
35    None,
36    Caster,
37    Target,
38    Last,
39    Source,
40    Destination,
41}
42
43/// Spatial selection algorithm used by an implicit selector.
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45#[repr(u8)]
46#[non_exhaustive]
47pub enum ImplicitTargetSelection {
48    NotImplemented,
49    Default,
50    Channel,
51    Nearby,
52    Cone,
53    Area,
54    Trajectory,
55    Line,
56}
57
58/// Relationship/identity predicate applied to candidates.
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
60#[repr(u8)]
61#[non_exhaustive]
62pub enum ImplicitTargetCheck {
63    Default,
64    Entry,
65    Enemy,
66    Ally,
67    Party,
68    Raid,
69    RaidClass,
70    Passenger,
71    Summoned,
72}
73
74/// Direction modifier carried by an implicit selector.
75#[derive(Clone, Copy, Debug, Eq, PartialEq)]
76#[repr(u8)]
77#[non_exhaustive]
78pub enum ImplicitTargetDirection {
79    None,
80    Front,
81    Back,
82    Right,
83    Left,
84    FrontRight,
85    BackRight,
86    BackLeft,
87    FrontLeft,
88    Random,
89    Entry,
90}
91
92impl ImplicitTargetObject {
93    #[must_use]
94    pub const fn as_str(&self) -> &'static str {
95        match self {
96            Self::None => "none",
97            Self::Source => "source",
98            Self::Destination => "destination",
99            Self::Unit => "unit",
100            Self::UnitAndDestination => "unit and destination",
101            Self::GameObject => "game object",
102            Self::GameObjectItem => "game object item",
103            Self::Item => "item",
104            Self::Corpse => "corpse",
105            Self::CorpseEnemy => "enemy corpse",
106            Self::CorpseAlly => "ally corpse",
107        }
108    }
109}
110
111impl ImplicitTargetReference {
112    #[must_use]
113    pub const fn as_str(&self) -> &'static str {
114        match self {
115            Self::None => "none",
116            Self::Caster => "caster",
117            Self::Target => "target",
118            Self::Last => "last",
119            Self::Source => "source",
120            Self::Destination => "destination",
121        }
122    }
123}
124
125impl ImplicitTargetSelection {
126    #[must_use]
127    pub const fn as_str(&self) -> &'static str {
128        match self {
129            Self::NotImplemented => "not implemented",
130            Self::Default => "default",
131            Self::Channel => "channel",
132            Self::Nearby => "nearby",
133            Self::Cone => "cone",
134            Self::Area => "area",
135            Self::Trajectory => "trajectory",
136            Self::Line => "line",
137        }
138    }
139}
140
141impl ImplicitTargetCheck {
142    #[must_use]
143    pub const fn as_str(&self) -> &'static str {
144        match self {
145            Self::Default => "default",
146            Self::Entry => "entry",
147            Self::Enemy => "enemy",
148            Self::Ally => "ally",
149            Self::Party => "party",
150            Self::Raid => "raid",
151            Self::RaidClass => "raid class",
152            Self::Passenger => "passenger",
153            Self::Summoned => "summoned",
154        }
155    }
156}
157
158impl ImplicitTargetDirection {
159    #[must_use]
160    pub const fn as_str(&self) -> &'static str {
161        match self {
162            Self::None => "none",
163            Self::Front => "front",
164            Self::Back => "back",
165            Self::Right => "right",
166            Self::Left => "left",
167            Self::FrontRight => "front right",
168            Self::BackRight => "back right",
169            Self::BackLeft => "back left",
170            Self::FrontLeft => "front left",
171            Self::Random => "random",
172            Self::Entry => "entry",
173        }
174    }
175}
176
177/// `TrinityCore`'s five-axis decomposition of one DBC `Targets` selector.
178#[derive(Clone, Copy, Debug, Eq, PartialEq)]
179pub struct ImplicitTargetSemantic {
180    pub object: ImplicitTargetObject,
181    pub reference: ImplicitTargetReference,
182    pub selection: ImplicitTargetSelection,
183    pub check: ImplicitTargetCheck,
184    pub direction: ImplicitTargetDirection,
185}
186
187const fn target(
188    object: ImplicitTargetObject,
189    reference: ImplicitTargetReference,
190    selection: ImplicitTargetSelection,
191    check: ImplicitTargetCheck,
192    direction: ImplicitTargetDirection,
193) -> ImplicitTargetSemantic {
194    ImplicitTargetSemantic {
195        object,
196        reference,
197        selection,
198        check,
199        direction,
200    }
201}
202
203use ImplicitTargetCheck as C;
204use ImplicitTargetDirection as D;
205use ImplicitTargetObject as O;
206use ImplicitTargetReference as R;
207use ImplicitTargetSelection as S;
208
209/// Complete selector table for `TrinityCore` `Targets` values 0 through 152.
210#[rustfmt::skip]
211pub const IMPLICIT_TARGET_SEMANTICS: &[ImplicitTargetSemantic] = &[
212    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
213    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
214    target(O::Unit, R::Caster, S::Nearby, C::Enemy, D::None),
215    target(O::Unit, R::Caster, S::Nearby, C::Ally, D::None),
216    target(O::Unit, R::Caster, S::Nearby, C::Party, D::None),
217    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
218    target(O::Unit, R::Target, S::Default, C::Enemy, D::None),
219    target(O::Unit, R::Source, S::Area, C::Entry, D::None),
220    target(O::Unit, R::Destination, S::Area, C::Entry, D::None),
221    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
222    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
223    target(O::Unit, R::Source, S::NotImplemented, C::Default, D::None),
224    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
225    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
226    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
227    target(O::Unit, R::Source, S::Area, C::Enemy, D::None),
228    target(O::Unit, R::Destination, S::Area, C::Enemy, D::None),
229    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
230    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
231    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
232    target(O::Unit, R::Caster, S::Area, C::Party, D::None),
233    target(O::Unit, R::Target, S::Default, C::Ally, D::None),
234    target(O::Source, R::Caster, S::Default, C::Default, D::None),
235    target(O::GameObject, R::Target, S::Default, C::Default, D::None),
236    target(O::Unit, R::Caster, S::Cone, C::Enemy, D::Front),
237    target(O::Unit, R::Target, S::Default, C::Default, D::None),
238    target(O::GameObjectItem, R::Target, S::Default, C::Default, D::None),
239    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
240    target(O::Destination, R::Destination, S::Default, C::Enemy, D::None),
241    target(O::Destination, R::Destination, S::Default, C::Ally, D::None),
242    target(O::Unit, R::Source, S::Area, C::Ally, D::None),
243    target(O::Unit, R::Destination, S::Area, C::Ally, D::None),
244    target(O::Destination, R::Caster, S::Default, C::Default, D::FrontLeft),
245    target(O::Unit, R::Source, S::Area, C::Party, D::None),
246    target(O::Unit, R::Destination, S::Area, C::Party, D::None),
247    target(O::Unit, R::Target, S::Default, C::Party, D::None),
248    target(O::Destination, R::Caster, S::NotImplemented, C::Default, D::None),
249    target(O::Unit, R::Last, S::Area, C::Party, D::None),
250    target(O::Unit, R::Caster, S::Nearby, C::Entry, D::None),
251    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
252    target(O::GameObject, R::Caster, S::Nearby, C::Entry, D::None),
253    target(O::Destination, R::Caster, S::Default, C::Default, D::FrontRight),
254    target(O::Destination, R::Caster, S::Default, C::Default, D::BackRight),
255    target(O::Destination, R::Caster, S::Default, C::Default, D::BackLeft),
256    target(O::Destination, R::Caster, S::Default, C::Default, D::FrontLeft),
257    target(O::Unit, R::Target, S::Default, C::Ally, D::None),
258    target(O::Destination, R::Caster, S::Nearby, C::Entry, D::None),
259    target(O::Destination, R::Caster, S::Default, C::Default, D::Front),
260    target(O::Destination, R::Caster, S::Default, C::Default, D::Back),
261    target(O::Destination, R::Caster, S::Default, C::Default, D::Right),
262    target(O::Destination, R::Caster, S::Default, C::Default, D::Left),
263    target(O::GameObject, R::Source, S::Area, C::Default, D::None),
264    target(O::GameObject, R::Destination, S::Area, C::Default, D::None),
265    target(O::Destination, R::Target, S::Default, C::Enemy, D::None),
266    target(O::Unit, R::Caster, S::Cone, C::Enemy, D::Front),
267    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
268    target(O::Unit, R::Caster, S::Area, C::Raid, D::None),
269    target(O::Unit, R::Target, S::Default, C::Raid, D::None),
270    target(O::Unit, R::Caster, S::Nearby, C::Raid, D::None),
271    target(O::Unit, R::Caster, S::Cone, C::Ally, D::Front),
272    target(O::Unit, R::Caster, S::Cone, C::Entry, D::Front),
273    target(O::Unit, R::Target, S::Area, C::RaidClass, D::None),
274    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
275    target(O::Destination, R::Target, S::Default, C::Default, D::None),
276    target(O::Destination, R::Target, S::Default, C::Default, D::Front),
277    target(O::Destination, R::Target, S::Default, C::Default, D::Back),
278    target(O::Destination, R::Target, S::Default, C::Default, D::Right),
279    target(O::Destination, R::Target, S::Default, C::Default, D::Left),
280    target(O::Destination, R::Target, S::Default, C::Default, D::FrontRight),
281    target(O::Destination, R::Target, S::Default, C::Default, D::BackRight),
282    target(O::Destination, R::Target, S::Default, C::Default, D::BackLeft),
283    target(O::Destination, R::Target, S::Default, C::Default, D::FrontLeft),
284    target(O::Destination, R::Caster, S::Default, C::Default, D::Random),
285    target(O::Destination, R::Caster, S::Default, C::Default, D::Random),
286    target(O::Destination, R::Target, S::Default, C::Default, D::Random),
287    target(O::Destination, R::Target, S::Default, C::Default, D::Random),
288    target(O::Destination, R::Caster, S::Channel, C::Default, D::None),
289    target(O::Unit, R::Caster, S::Channel, C::Default, D::None),
290    target(O::Destination, R::Destination, S::Default, C::Default, D::Front),
291    target(O::Destination, R::Destination, S::Default, C::Default, D::Back),
292    target(O::Destination, R::Destination, S::Default, C::Default, D::Right),
293    target(O::Destination, R::Destination, S::Default, C::Default, D::Left),
294    target(O::Destination, R::Destination, S::Default, C::Default, D::FrontRight),
295    target(O::Destination, R::Destination, S::Default, C::Default, D::BackRight),
296    target(O::Destination, R::Destination, S::Default, C::Default, D::BackLeft),
297    target(O::Destination, R::Destination, S::Default, C::Default, D::FrontLeft),
298    target(O::Destination, R::Destination, S::Default, C::Default, D::Random),
299    target(O::Destination, R::Destination, S::Default, C::Default, D::None),
300    target(O::Destination, R::Destination, S::Default, C::Default, D::None),
301    target(O::Destination, R::Destination, S::Trajectory, C::Default, D::None),
302    target(O::Unit, R::Target, S::Default, C::Default, D::None),
303    target(O::Destination, R::Destination, S::Default, C::Default, D::Random),
304    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
305    target(O::Corpse, R::Source, S::Area, C::Enemy, D::None),
306    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
307    target(O::Unit, R::Target, S::Default, C::Passenger, D::None),
308    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
309    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
310    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
311    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
312    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
313    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
314    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
315    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
316    target(O::Unit, R::Caster, S::Cone, C::Enemy, D::Front),
317    target(O::Unit, R::Caster, S::Area, C::Default, D::None),
318    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
319    target(O::Destination, R::Caster, S::Nearby, C::Entry, D::None),
320    target(O::GameObject, R::Caster, S::Cone, C::Enemy, D::Front),
321    target(O::GameObject, R::Caster, S::Cone, C::Ally, D::Front),
322    target(O::Unit, R::Caster, S::Cone, C::Entry, D::Front),
323    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
324    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
325    target(O::Destination, R::Target, S::Default, C::Default, D::None),
326    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
327    target(O::Unit, R::Source, S::Area, C::Enemy, D::None),
328    target(O::UnitAndDestination, R::Last, S::Area, C::Enemy, D::None),
329    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
330    target(O::Unit, R::Target, S::Area, C::Raid, D::None),
331    target(O::Corpse, R::Caster, S::Area, C::Raid, D::None),
332    target(O::Unit, R::Caster, S::Area, C::Summoned, D::None),
333    target(O::Corpse, R::Target, S::Default, C::Ally, D::None),
334    target(O::Unit, R::Caster, S::Area, C::Default, D::None),
335    target(O::Unit, R::Caster, S::Area, C::Default, D::None),
336    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
337    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
338    target(O::Unit, R::None, S::NotImplemented, C::Default, D::None),
339    target(O::Destination, R::None, S::NotImplemented, C::Default, D::None),
340    target(O::Unit, R::Caster, S::Cone, C::Ally, D::Front),
341    target(O::Unit, R::Caster, S::Cone, C::Enemy, D::Front),
342    target(O::Unit, R::Caster, S::Cone, C::Default, D::Front),
343    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
344    target(O::Destination, R::Target, S::Default, C::Ally, D::None),
345    target(O::Unit, R::Destination, S::Line, C::Ally, D::None),
346    target(O::Unit, R::Destination, S::Line, C::Enemy, D::None),
347    target(O::Unit, R::Destination, S::Line, C::Default, D::None),
348    target(O::Unit, R::Caster, S::Cone, C::Ally, D::Front),
349    target(O::Destination, R::Caster, S::Default, C::Default, D::None),
350    target(O::Destination, R::Destination, S::Default, C::Default, D::None),
351    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
352    target(O::Destination, R::None, S::NotImplemented, C::Default, D::None),
353    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
354    target(O::Destination, R::Caster, S::Nearby, C::Entry, D::FrontRight),
355    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
356    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
357    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
358    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
359    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
360    target(O::Destination, R::Destination, S::Default, C::Default, D::None),
361    target(O::Destination, R::Caster, S::Default, C::Default, D::Random),
362    target(O::Unit, R::Caster, S::Default, C::Default, D::None),
363    target(O::Unit, R::Caster, S::Area, C::Enemy, D::None),
364    target(O::None, R::None, S::NotImplemented, C::Default, D::None),
365];
366
367/// Typed selector lookup; unknown DBC values resolve to `None`.
368#[must_use]
369pub fn implicit_target_semantic(raw: i32) -> Option<&'static ImplicitTargetSemantic> {
370    usize::try_from(raw)
371        .ok()
372        .and_then(|index| IMPLICIT_TARGET_SEMANTICS.get(index))
373}
374
375#[cfg(test)]
376mod tests {
377    use googletest::prelude::*;
378
379    use super::*;
380
381    const TRINITY_SELECTOR_FINGERPRINT: u64 = 0x2c65_6d80_c2bb_9917;
382
383    fn selector_fingerprint() -> u64 {
384        IMPLICIT_TARGET_SEMANTICS
385            .iter()
386            .flat_map(|semantic| {
387                [
388                    semantic.object as u8,
389                    semantic.reference as u8,
390                    semantic.selection as u8,
391                    semantic.check as u8,
392                    semantic.direction as u8,
393                ]
394            })
395            .fold(0xcbf2_9ce4_8422_2325, |hash, byte| {
396                (hash ^ u64::from(byte)).wrapping_mul(0x0000_0100_0000_01b3)
397            })
398    }
399
400    #[gtest]
401    fn complete_ordered_registry_matches_the_trinity_source_table() {
402        expect_that!(IMPLICIT_TARGET_SEMANTICS.len(), eq(153));
403        expect_that!(selector_fingerprint(), eq(TRINITY_SELECTOR_FINGERPRINT));
404        expect_that!(implicit_target_semantic(-1), eq(None));
405        expect_that!(implicit_target_semantic(153), eq(None));
406    }
407
408    #[gtest]
409    fn selector_axes_preserve_source_target_and_destination_area_origins() {
410        let source = implicit_target_semantic(15).expect("source area enemy");
411        let destination = implicit_target_semantic(16).expect("destination area enemy");
412
413        expect_that!(source.reference, eq(ImplicitTargetReference::Source));
414        expect_that!(
415            destination.reference,
416            eq(ImplicitTargetReference::Destination)
417        );
418        expect_that!(source.selection, eq(ImplicitTargetSelection::Area));
419        expect_that!(destination.selection, eq(ImplicitTargetSelection::Area));
420    }
421}