Skip to main content

wowlab_engine_domain/dbc/semantics/
mod.rs

1//! Canonical DBC spell, effect, aura, modifier, and school semantics.
2
3use wowlab_types::data::SpellEffect;
4
5use self::{
6    modifier_property_is as raw_modifier_property_is,
7    modifier_property_is_any as raw_modifier_property_is_any,
8};
9
10mod aura_interrupt_flags;
11mod aura_states;
12mod aura_subtypes;
13mod control;
14mod coverage;
15mod defense_types;
16mod difficulty;
17mod enchantments;
18mod implicit_targets;
19mod items;
20mod loot;
21mod mechanics;
22mod modifier_properties;
23mod power_types;
24mod proc_attributes;
25mod proc_types;
26mod ratings;
27mod resolved_game_data;
28mod schools;
29mod spell_attributes;
30mod spell_cast_target_flags;
31mod spell_effect_attributes;
32mod spell_effect_targets;
33mod spell_effects;
34mod spell_interrupt_flags;
35mod spell_targets;
36mod stances;
37mod stat_masks;
38mod traits;
39
40pub use aura_interrupt_flags::SpellAuraInterruptFlags;
41pub use aura_states::AuraState;
42pub use control::{
43    CrowdControlKind, CrowdControlSemantic, DiminishingGroup, crowd_control_semantic,
44};
45pub use coverage::{EffectSemanticCoverage, effect_semantic_coverage};
46pub use defense_types::DefenseType;
47pub use difficulty::DifficultyId;
48pub use implicit_targets::{
49    IMPLICIT_TARGET_SEMANTICS, ImplicitTargetCheck, ImplicitTargetDirection, ImplicitTargetObject,
50    ImplicitTargetReference, ImplicitTargetSelection, ImplicitTargetSelector,
51    ImplicitTargetSemantic, implicit_target_semantic,
52};
53pub use items::{
54    ArmorSubclass, ConsumableSubclass, GemSubclass, InventoryType, ItemClass, ItemEffectTrigger,
55    WeaponSubclass, item_matches_consumable_query, resolve_item_budget,
56};
57pub use loot::{
58    DungeonDifficultyFlags, ItemBonusListGroupEntryFlags, ItemBonusType, ItemContext,
59    RaidDifficultyFlags,
60};
61pub use mechanics::{DispelType, Mechanic, MechanicMask};
62pub use power_types::PowerType;
63pub use proc_attributes::{
64    CONSUMED_PROC_ATTRIBUTES, PROC_ATTRIBUTE_SEMANTICS, ProcAttributeKind, ProcAttributeSemantic,
65    ProcAttributes, proc_attribute_semantic,
66};
67pub use proc_types::ProcTypeMask;
68pub use schools::SpellSchoolMask;
69pub use spell_cast_target_flags::{
70    SPELL_CAST_TARGET_FLAG_SEMANTICS, SpellCastTargetFlagSemantic, SpellCastTargetFlags,
71    spell_cast_target_flag_semantic,
72};
73pub use spell_effect_attributes::SpellEffectAttributes;
74pub use spell_effect_targets::{
75    SpellEffectImplicitTarget, SpellEffectTargetSemantic, spell_effect_target_semantic,
76};
77pub use spell_interrupt_flags::SpellInterruptFlags;
78pub use spell_targets::{SpellTargetMasks, spell_target_masks};
79pub use stat_masks::{AttributeMask, RatingMultiplierMask};
80
81/// Degree to which the shared engine interprets a DBC semantic automatically.
82#[derive(Clone, Copy, Debug, Eq, PartialEq)]
83#[non_exhaustive]
84pub enum SemanticSupport {
85    /// Shared resolution or combat code models the semantic directly.
86    Generic,
87    /// Shared code models part of the semantic, but content must wire its activation.
88    Partial,
89    /// The semantic is intentionally delegated to spec or item content.
90    ContentRequired,
91    /// The semantic has no outgoing-damage, resource, or action-timing impact in Patchwerk repair.
92    Ignored,
93    /// The semantic is known, but the shared engine has no implementation.
94    Unsupported,
95}
96
97pub use spell_attributes::{
98    SPELL_ATTRIBUTE_SEMANTICS, SpellAttributeKind, SpellAttributeSemantic, spell_attribute_is,
99    spell_attribute_is_any, spell_attribute_semantic,
100};
101pub use spell_effects::{
102    SPELL_EFFECT_SEMANTICS, SpellEffectKind, SpellEffectSemantic, spell_effect_semantic,
103};
104/// Operation performed by a spell-modifier aura.
105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106#[non_exhaustive]
107pub enum ModifierOperation {
108    Flat,
109    Percent,
110}
111
112/// Filter used by a spell-modifier aura to select affected spells.
113#[derive(Clone, Copy, Debug, Eq, PartialEq)]
114#[non_exhaustive]
115pub enum ModifierFilter {
116    ClassFamilyMask,
117    Label,
118}
119
120pub use aura_subtypes::{
121    AURA_SUBTYPE_SEMANTICS, AuraSubtypeKind, AuraSubtypeSemantic, PetStatKind, StatModifierKind,
122    is_primary_stat_misc,
123};
124pub use enchantments::{
125    EnchantEffectKind, EnchantTriggerSemantic, enchant_effect_is, enchant_effect_kind,
126    enchant_trigger_semantic, rppm_crit_scaled, rppm_haste_scaled,
127};
128pub use modifier_properties::{
129    MODIFIER_PROPERTY_SEMANTICS, ModifierOperations, ModifierPropertyKind,
130    ModifierPropertySemantic, modifier_property_for_effect,
131};
132pub use ratings::{CombatRating, CombatRatingMask};
133pub use resolved_game_data::ResolvedGameDataSemanticExt;
134pub use stances::{ShapeshiftForm, SpellShapeshiftFormFlags, stance_mask_allows};
135pub use traits::{
136    TraitConditionType, TraitCurrencyFlags, TraitEffectOperation, TraitNodeType, TraitTreeIndex,
137};
138macro_rules! semantic_registry {
139    (
140        kind = $kind_fn:ident,
141        is = $is_fn:ident,
142        is_any = $is_any_fn:ident,
143        raw = $raw_fn:ident,
144        semantic = $semantic_fn:ident,
145        registry = $registry:ident,
146        type = $kind_ty:ty
147    ) => {
148        #[must_use]
149        pub fn $kind_fn(raw: i32) -> Option<$kind_ty> {
150            $semantic_fn(raw).map(|semantic| semantic.kind)
151        }
152
153        #[must_use]
154        pub fn $is_fn(raw: i32, expected: $kind_ty) -> bool {
155            raw == expected as i32
156        }
157
158        #[must_use]
159        pub fn $is_any_fn(raw: i32, expected: &[$kind_ty]) -> bool {
160            expected.iter().any(|kind| raw == *kind as i32)
161        }
162
163        #[must_use]
164        pub fn $raw_fn(kind: $kind_ty) -> i32 {
165            kind as i32
166        }
167    };
168}
169
170semantic_registry! {
171    kind = spell_effect_kind,
172    is = spell_effect_is,
173    is_any = spell_effect_is_any,
174    raw = spell_effect_raw,
175    semantic = spell_effect_semantic,
176    registry = SPELL_EFFECT_SEMANTICS,
177    type = SpellEffectKind
178}
179
180/// Registry lookup for a raw apply-aura subtype.
181#[must_use]
182pub fn aura_subtype_semantic(raw: i32) -> Option<&'static AuraSubtypeSemantic> {
183    AURA_SUBTYPE_SEMANTICS
184        .binary_search_by_key(&raw, |entry| entry.kind as i32)
185        .ok()
186        .and_then(|index| AURA_SUBTYPE_SEMANTICS.get(index))
187}
188
189/// Returns the modifier operation/filter carried by a modifier aura subtype.
190#[must_use]
191pub fn aura_subtype_modifier(raw: i32) -> Option<(ModifierOperation, ModifierFilter)> {
192    aura_subtype_semantic(raw).and_then(|semantic| semantic.modifier)
193}
194
195semantic_registry! {
196    kind = aura_subtype_kind,
197    is = aura_subtype_is,
198    is_any = aura_subtype_is_any,
199    raw = aura_subtype_raw,
200    semantic = aura_subtype_semantic,
201    registry = AURA_SUBTYPE_SEMANTICS,
202    type = AuraSubtypeKind
203}
204
205/// Typed aura-382 pet-stat identity, or `None` for an unknown raw value.
206#[must_use]
207pub const fn pet_stat_kind(raw: i32) -> Option<PetStatKind> {
208    match raw {
209        2 => Some(PetStatKind::AttackPowerInheritance),
210        3 => Some(PetStatKind::SpellPowerInheritance),
211        _ => None,
212    }
213}
214
215/// Registry lookup for a raw spell-modifier property.
216#[must_use]
217pub fn modifier_property_semantic(raw: i32) -> Option<&'static ModifierPropertySemantic> {
218    MODIFIER_PROPERTY_SEMANTICS
219        .iter()
220        .find(|entry| entry.kind as i32 == raw)
221}
222
223semantic_registry! {
224    kind = modifier_property_kind,
225    is = modifier_property_is,
226    is_any = modifier_property_is_any,
227    raw = modifier_property_raw,
228    semantic = modifier_property_semantic,
229    registry = MODIFIER_PROPERTY_SEMANTICS,
230    type = ModifierPropertyKind
231}
232
233/// Typed semantic predicates for a resolved DBC spell effect.
234pub trait SpellEffectSemanticExt {
235    fn effect_is(&self, expected: SpellEffectKind) -> bool;
236    fn effect_is_any(&self, expected: &[SpellEffectKind]) -> bool;
237    fn aura_is(&self, expected: AuraSubtypeKind) -> bool;
238    fn aura_is_any(&self, expected: &[AuraSubtypeKind]) -> bool;
239    fn modifier_property_is(&self, expected: ModifierPropertyKind) -> bool;
240    fn modifier_property_is_any(&self, expected: &[ModifierPropertyKind]) -> bool;
241}
242
243impl SpellEffectSemanticExt for SpellEffect {
244    fn effect_is(&self, expected: SpellEffectKind) -> bool {
245        spell_effect_is(self.effect, expected)
246    }
247
248    fn effect_is_any(&self, expected: &[SpellEffectKind]) -> bool {
249        spell_effect_is_any(self.effect, expected)
250    }
251
252    fn aura_is(&self, expected: AuraSubtypeKind) -> bool {
253        aura_subtype_is(self.aura, expected)
254    }
255
256    fn aura_is_any(&self, expected: &[AuraSubtypeKind]) -> bool {
257        aura_subtype_is_any(self.aura, expected)
258    }
259
260    fn modifier_property_is(&self, expected: ModifierPropertyKind) -> bool {
261        raw_modifier_property_is(self.misc_value_0, expected)
262    }
263
264    fn modifier_property_is_any(&self, expected: &[ModifierPropertyKind]) -> bool {
265        raw_modifier_property_is_any(self.misc_value_0, expected)
266    }
267}
268
269/// Whether a modifier aura/property combination is implemented generically.
270#[must_use]
271pub fn modifier_combination_supported(
272    aura: &AuraSubtypeSemantic,
273    property: &ModifierPropertySemantic,
274) -> bool {
275    aura.modifier.is_some_and(|(operation, _)| {
276        let required = match operation {
277            ModifierOperation::Flat => ModifierOperations::FLAT,
278            ModifierOperation::Percent => ModifierOperations::PERCENT,
279        };
280
281        property.operations.contains(required)
282    })
283}
284
285#[cfg(test)]
286mod tests;