wowlab_engine_domain/dbc/semantics/
resolved_game_data.rs1use wowlab_engine_gamedata::ResolvedGameData;
4use wowlab_types::sim::SpellIdx;
5
6use super::{
7 AuraState, AuraSubtypeKind, DispelType, Mechanic, MechanicMask, SpellSchoolMask,
8 aura_subtype_kind,
9};
10
11fn infer_granted_aura_state(data: &ResolvedGameData, spell: SpellIdx) -> Option<AuraState> {
12 let mechanics = data.all_effect_mechanic_mask(spell);
13
14 if DispelType::try_from(data.dispel_type(spell)).ok() == Some(DispelType::Enrage) {
15 return Some(AuraState::Enraged);
16 }
17
18 if mechanics.contains(MechanicMask::BLEED) {
19 return Some(AuraState::Bleeding);
20 }
21
22 if mechanics.contains(MechanicMask::BANISH) {
23 return Some(AuraState::Banished);
24 }
25
26 let frost = data
27 .school_mask(spell)
28 .is_some_and(|raw| SpellSchoolMask::from_dbc(raw).contains(SpellSchoolMask::FROST));
29 let immobilizes = (1..=data.max_effect_index(spell)).any(|effect_index| {
30 matches!(
31 aura_subtype_kind(data.effect_aura(spell, effect_index)),
32 Some(
33 AuraSubtypeKind::Stun
34 | AuraSubtypeKind::Root
35 | AuraSubtypeKind::RootRespectsThreatTable
36 )
37 )
38 });
39
40 (frost && immobilizes).then_some(AuraState::Frozen)
41}
42
43pub trait ResolvedGameDataSemanticExt {
44 fn override_action_spell(&self, aura_spell_id: SpellIdx, effect_index: u8) -> Option<SpellIdx>;
46
47 fn effect_affects_spell(&self, source: SpellIdx, effect_index: u8, target: SpellIdx) -> bool;
49
50 fn spell_mechanic_mask(&self, spell: SpellIdx) -> MechanicMask;
52
53 fn effect_mechanic_mask(&self, spell: SpellIdx, effect_index: u8) -> MechanicMask;
55
56 fn all_effect_mechanic_mask(&self, spell: SpellIdx) -> MechanicMask;
58
59 fn granted_aura_state(&self, spell: SpellIdx) -> Option<AuraState>;
61}
62
63impl ResolvedGameDataSemanticExt for ResolvedGameData {
64 fn override_action_spell(&self, aura_spell_id: SpellIdx, effect_index: u8) -> Option<SpellIdx> {
65 (aura_subtype_kind(self.effect_aura(aura_spell_id, effect_index))
66 == Some(AuraSubtypeKind::OverrideActionSpell))
67 .then(|| self.effect_base_points_spell(aura_spell_id, effect_index))
68 .flatten()
69 }
70
71 fn effect_affects_spell(&self, source: SpellIdx, effect_index: u8, target: SpellIdx) -> bool {
72 let aura = aura_subtype_kind(self.effect_aura(source, effect_index));
73 let label = match aura {
74 Some(
75 AuraSubtypeKind::AddPercentLabelModifier | AuraSubtypeKind::AddFlatLabelModifier,
76 ) => self.effect_misc_value_1(source, effect_index),
77 Some(
78 AuraSubtypeKind::ModDamageTakenFromCasterSpellsLabel
79 | AuraSubtypeKind::ModTimeRateLabel,
80 ) => self.effect_misc_value_0(source, effect_index),
81 Some(AuraSubtypeKind::ModTimeRate) => return true,
82 _ => 0,
83 };
84
85 (label != 0 && self.has_label(target, label))
86 || self.effect_class_mask_affects_spell(source, effect_index, target)
87 }
88
89 fn spell_mechanic_mask(&self, spell: SpellIdx) -> MechanicMask {
90 Mechanic::from_dbc(self.spell_mechanic(spell)).map_or(MechanicMask::empty(), Mechanic::mask)
91 }
92
93 fn effect_mechanic_mask(&self, spell: SpellIdx, effect_index: u8) -> MechanicMask {
94 self.spell_mechanic_mask(spell)
95 | Mechanic::from_dbc(self.effect_mechanic(spell, effect_index))
96 .map_or(MechanicMask::empty(), Mechanic::mask)
97 }
98
99 fn all_effect_mechanic_mask(&self, spell: SpellIdx) -> MechanicMask {
100 (1..=self.max_effect_index(spell)).fold(
101 self.spell_mechanic_mask(spell),
102 |mechanics, effect_index| mechanics | self.effect_mechanic_mask(spell, effect_index),
103 )
104 }
105
106 fn granted_aura_state(&self, spell: SpellIdx) -> Option<AuraState> {
107 infer_granted_aura_state(self, spell)
108 }
109}