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            $semantic_fn(raw).is_some_and(|semantic| semantic.kind == expected)
156        }
157
158        #[must_use]
159        pub fn $is_any_fn(raw: i32, expected: &[$kind_ty]) -> bool {
160            $kind_fn(raw).is_some_and(|kind| expected.contains(&kind))
161        }
162
163        #[must_use]
164        pub fn $raw_fn(kind: $kind_ty) -> i32 {
165            $registry
166                .iter()
167                .find(|entry| entry.kind == kind)
168                .map_or(0, |entry| entry.kind as i32)
169        }
170    };
171}
172
173semantic_registry! {
174    kind = spell_effect_kind,
175    is = spell_effect_is,
176    is_any = spell_effect_is_any,
177    raw = spell_effect_raw,
178    semantic = spell_effect_semantic,
179    registry = SPELL_EFFECT_SEMANTICS,
180    type = SpellEffectKind
181}
182
183/// Registry lookup for a raw apply-aura subtype.
184#[must_use]
185pub fn aura_subtype_semantic(raw: i32) -> Option<&'static AuraSubtypeSemantic> {
186    AURA_SUBTYPE_SEMANTICS
187        .iter()
188        .find(|entry| entry.kind as i32 == raw)
189}
190
191/// Returns the modifier operation/filter carried by a modifier aura subtype.
192#[must_use]
193pub fn aura_subtype_modifier(raw: i32) -> Option<(ModifierOperation, ModifierFilter)> {
194    aura_subtype_semantic(raw).and_then(|semantic| semantic.modifier)
195}
196
197semantic_registry! {
198    kind = aura_subtype_kind,
199    is = aura_subtype_is,
200    is_any = aura_subtype_is_any,
201    raw = aura_subtype_raw,
202    semantic = aura_subtype_semantic,
203    registry = AURA_SUBTYPE_SEMANTICS,
204    type = AuraSubtypeKind
205}
206
207/// Typed aura-382 pet-stat identity, or `None` for an unknown raw value.
208#[must_use]
209pub const fn pet_stat_kind(raw: i32) -> Option<PetStatKind> {
210    match raw {
211        2 => Some(PetStatKind::AttackPowerInheritance),
212        3 => Some(PetStatKind::SpellPowerInheritance),
213        _ => None,
214    }
215}
216
217/// Registry lookup for a raw spell-modifier property.
218#[must_use]
219pub fn modifier_property_semantic(raw: i32) -> Option<&'static ModifierPropertySemantic> {
220    MODIFIER_PROPERTY_SEMANTICS
221        .iter()
222        .find(|entry| entry.kind as i32 == raw)
223}
224
225semantic_registry! {
226    kind = modifier_property_kind,
227    is = modifier_property_is,
228    is_any = modifier_property_is_any,
229    raw = modifier_property_raw,
230    semantic = modifier_property_semantic,
231    registry = MODIFIER_PROPERTY_SEMANTICS,
232    type = ModifierPropertyKind
233}
234
235/// Typed semantic predicates for a resolved DBC spell effect.
236pub trait SpellEffectSemanticExt {
237    fn effect_is(&self, expected: SpellEffectKind) -> bool;
238    fn effect_is_any(&self, expected: &[SpellEffectKind]) -> bool;
239    fn aura_is(&self, expected: AuraSubtypeKind) -> bool;
240    fn aura_is_any(&self, expected: &[AuraSubtypeKind]) -> bool;
241    fn modifier_property_is(&self, expected: ModifierPropertyKind) -> bool;
242    fn modifier_property_is_any(&self, expected: &[ModifierPropertyKind]) -> bool;
243}
244
245impl SpellEffectSemanticExt for SpellEffect {
246    fn effect_is(&self, expected: SpellEffectKind) -> bool {
247        spell_effect_is(self.effect, expected)
248    }
249
250    fn effect_is_any(&self, expected: &[SpellEffectKind]) -> bool {
251        spell_effect_is_any(self.effect, expected)
252    }
253
254    fn aura_is(&self, expected: AuraSubtypeKind) -> bool {
255        aura_subtype_is(self.aura, expected)
256    }
257
258    fn aura_is_any(&self, expected: &[AuraSubtypeKind]) -> bool {
259        aura_subtype_is_any(self.aura, expected)
260    }
261
262    fn modifier_property_is(&self, expected: ModifierPropertyKind) -> bool {
263        raw_modifier_property_is(self.misc_value_0, expected)
264    }
265
266    fn modifier_property_is_any(&self, expected: &[ModifierPropertyKind]) -> bool {
267        raw_modifier_property_is_any(self.misc_value_0, expected)
268    }
269}
270
271/// Whether a modifier aura/property combination is implemented generically.
272#[must_use]
273pub fn modifier_combination_supported(
274    aura: &AuraSubtypeSemantic,
275    property: &ModifierPropertySemantic,
276) -> bool {
277    aura.modifier.is_some_and(|(operation, _)| {
278        let required = match operation {
279            ModifierOperation::Flat => ModifierOperations::FLAT,
280            ModifierOperation::Percent => ModifierOperations::PERCENT,
281        };
282
283        property.operations.contains(required)
284    })
285}
286
287#[cfg(test)]
288mod tests;