Skip to main content

wowlab_engine_combat/builder/aura_builder/effects/
modifiers.rs

1use super::{
2    AuraDefinitionDraft, AuraSubtypeKind, BuffEffect, BuilderError, EffectLookup,
3    LiveDamageModifierKind, ModifierPropertyKind, aura_subtype_is, aura_subtype_is_any,
4    aura_subtype_modifier, effect_parts, live_damage_modifier, modifier_percent,
5    modifier_property_is, modifier_property_kind,
6};
7
8pub(in crate::builder::aura_builder) fn apply_effect_points_modifier(
9    mut aura: AuraDefinitionDraft,
10    lookup: EffectLookup<'_>,
11) -> Result<AuraDefinitionDraft, BuilderError> {
12    let (data, idx, spell_id, effect_index) = effect_parts(lookup);
13    let property = data.effect_misc_value_0(idx, effect_index);
14    let operation = aura_subtype_modifier(data.effect_aura(idx, effect_index))
15        .filter(|_| modifier_property_is(property, ModifierPropertyKind::Points));
16
17    if let Some((operation, _)) = operation {
18        aura = aura.push_effect(BuffEffect::SpellModifierFromEffect {
19            value: data.base_points(idx, effect_index),
20            source_spell_id: spell_id,
21            effect_index,
22            property: ModifierPropertyKind::Points,
23            operation,
24        })?;
25    }
26
27    let runtime_property = modifier_property_kind(property).filter(|property| {
28        matches!(
29            property,
30            ModifierPropertyKind::Effect1
31                | ModifierPropertyKind::Effect2
32                | ModifierPropertyKind::Effect3
33                | ModifierPropertyKind::Effect4
34                | ModifierPropertyKind::Effect5
35                | ModifierPropertyKind::ProcCharges
36                | ModifierPropertyKind::TargetResistance
37                | ModifierPropertyKind::HitChance
38                | ModifierPropertyKind::ProcFrequency
39                | ModifierPropertyKind::Amplitude
40                | ModifierPropertyKind::DispelResistance
41                | ModifierPropertyKind::ProcCooldown
42        )
43    });
44
45    if let (Some((operation, _)), Some(property)) = (
46        aura_subtype_modifier(data.effect_aura(idx, effect_index)),
47        runtime_property,
48    ) {
49        aura = aura.push_effect(BuffEffect::SpellModifierFromEffect {
50            value: data.base_points(idx, effect_index),
51            source_spell_id: spell_id,
52            effect_index,
53            property,
54            operation,
55        })?;
56    }
57
58    Ok(aura)
59}
60
61fn apply_spell_cost_modifier(
62    mut aura: AuraDefinitionDraft,
63    lookup: EffectLookup<'_>,
64) -> Result<AuraDefinitionDraft, BuilderError> {
65    let (data, idx, spell_id, effect_index) = effect_parts(lookup);
66    let subtype = data.effect_aura(idx, effect_index);
67    let property = data.effect_misc_value_0(idx, effect_index);
68    let cost_entry = [
69        ModifierPropertyKind::ResourceCost1,
70        ModifierPropertyKind::ResourceCost2,
71        ModifierPropertyKind::ResourceCost3,
72    ]
73    .into_iter()
74    .position(|kind| modifier_property_is(property, kind))
75    .and_then(|index| u8::try_from(index + 1).ok());
76
77    if let (Some((operation, _)), Some(cost_entry)) = (aura_subtype_modifier(subtype), cost_entry) {
78        aura = aura.push_effect(BuffEffect::SpellCostFromEffect {
79            value: data.base_points(idx, effect_index),
80            source_spell_id: spell_id,
81            effect_index,
82            cost_entry,
83            operation,
84        })?;
85    }
86
87    if aura_subtype_is(subtype, AuraSubtypeKind::ModAdditionalPowerCost) {
88        aura = aura.push_effect(BuffEffect::AdditionalPowerCostFromEffect {
89            value: data.base_points(idx, effect_index),
90            source_spell_id: spell_id,
91            effect_index,
92            resource_type: property,
93        })?;
94    }
95
96    if aura_subtype_is(subtype, AuraSubtypeKind::ModAutoAttackDamage) {
97        aura = aura.push_effect(BuffEffect::AutoAttackDamage(
98            data.base_points(idx, effect_index),
99        ))?;
100    }
101
102    Ok(aura)
103}
104
105fn apply_recharge_modifier(
106    mut aura: AuraDefinitionDraft,
107    lookup: EffectLookup<'_>,
108) -> Result<AuraDefinitionDraft, BuilderError> {
109    let (data, idx, spell_id, effect_index) = effect_parts(lookup);
110    let subtype = data.effect_aura(idx, effect_index);
111    let property = data.effect_misc_value_0(idx, effect_index);
112    let percent = data.base_points(idx, effect_index);
113
114    if aura_subtype_is(subtype, AuraSubtypeKind::ModCooldownChargeCategory) {
115        aura = aura.push_effect(BuffEffect::MaxChargesCategory(percent, property))?;
116    }
117
118    if aura_subtype_is(subtype, AuraSubtypeKind::IgnoreSpellChargeCooldown) {
119        aura = aura.push_effect(BuffEffect::IgnoreChargeCooldownCategory(property))?;
120    }
121
122    if aura_subtype_is(
123        subtype,
124        AuraSubtypeKind::ModChargeCooldownRechargeRateCategory,
125    ) {
126        aura = aura.push_effect(BuffEffect::ChargeRechargeRateCategory {
127            percent,
128            category: property,
129            source_spell_id: spell_id,
130            effect_index,
131        })?;
132    }
133
134    if aura_subtype_is(subtype, AuraSubtypeKind::ModCooldownRechargeRatePercent) {
135        aura = aura.push_effect(BuffEffect::CooldownRechargeRateFromEffect {
136            percent,
137            source_spell_id: spell_id,
138            effect_index,
139        })?;
140    }
141
142    Ok(aura)
143}
144
145fn apply_damage_modifier(
146    aura: AuraDefinitionDraft,
147    lookup: EffectLookup<'_>,
148) -> Result<AuraDefinitionDraft, BuilderError> {
149    let (data, idx, spell_id, effect_index) = effect_parts(lookup);
150    let periodic = match live_damage_modifier(
151        data.effect_aura(idx, effect_index),
152        data.effect_misc_value_0(idx, effect_index),
153    ) {
154        Some(LiveDamageModifierKind::SpellDirectAmount) => false,
155        Some(LiveDamageModifierKind::SpellPeriodicAmount) => true,
156        _ => return Ok(aura),
157    };
158
159    aura.push_effect(BuffEffect::SpellDamageFromEffect {
160        percent: data.base_points(idx, effect_index),
161        source_spell_id: spell_id,
162        effect_index,
163        periodic,
164    })
165}
166
167pub(in crate::builder::aura_builder) fn apply_spell_modifier_effect(
168    aura: AuraDefinitionDraft,
169    lookup: EffectLookup<'_>,
170) -> Result<AuraDefinitionDraft, BuilderError> {
171    let aura = apply_spell_cost_modifier(aura, lookup)?;
172    let aura = apply_targeting_modifier_effect(aura, lookup)?;
173    let aura = apply_recharge_modifier(aura, lookup)?;
174
175    apply_damage_modifier(aura, lookup)
176}
177
178fn apply_targeting_modifier_effect(
179    aura: AuraDefinitionDraft,
180    lookup: EffectLookup<'_>,
181) -> Result<AuraDefinitionDraft, BuilderError> {
182    let (data, idx, spell_id, effect_index) = effect_parts(lookup);
183    let raw_property = data.effect_misc_value_0(idx, effect_index);
184    let property = [
185        ModifierPropertyKind::Range,
186        ModifierPropertyKind::Radius,
187        ModifierPropertyKind::ChainTargetRange,
188        ModifierPropertyKind::MaxTargets,
189    ]
190    .into_iter()
191    .find(|kind| modifier_property_is(raw_property, *kind));
192    let (Some((operation, _)), Some(property)) = (
193        aura_subtype_modifier(data.effect_aura(idx, effect_index)),
194        property,
195    ) else {
196        return Ok(aura);
197    };
198
199    aura.push_effect(BuffEffect::SpellTargetingFromEffect {
200        value: data.base_points(idx, effect_index),
201        source_spell_id: spell_id,
202        effect_index,
203        property,
204        operation,
205    })
206}
207
208pub(in crate::builder::aura_builder) fn apply_critical_modifier_effect(
209    mut aura: AuraDefinitionDraft,
210    lookup: EffectLookup<'_>,
211) -> Result<AuraDefinitionDraft, BuilderError> {
212    let (data, idx, spell_id, effect_index) = effect_parts(lookup);
213    let kind = live_damage_modifier(
214        data.effect_aura(idx, effect_index),
215        data.effect_misc_value_0(idx, effect_index),
216    );
217
218    if kind == Some(LiveDamageModifierKind::SpellCritChance) {
219        aura = aura.push_effect(BuffEffect::SpellCritChanceFromEffect {
220            percent: modifier_percent(data.base_points(idx, effect_index)),
221            source_spell_id: spell_id,
222            effect_index,
223        })?;
224    }
225
226    if kind == Some(LiveDamageModifierKind::SpellCritDamage) {
227        aura = aura.push_effect(BuffEffect::SpellCritDamageFromEffect {
228            percent: data.base_points(idx, effect_index),
229            source_spell_id: spell_id,
230            effect_index,
231        })?;
232    }
233
234    Ok(aura)
235}
236
237pub(in crate::builder::aura_builder) fn apply_forwarded_value(
238    effect: &mut BuffEffect,
239    aura_id: u32,
240    carrier_effect_index: u8,
241    subtype: i32,
242    value: f64,
243) -> bool {
244    let replacement = match *effect {
245        BuffEffect::HasteMultFromEffect {
246            percent: current,
247            source_spell_id,
248            effect_index,
249        } if current.abs() <= f64::EPSILON
250            && source_spell_id == aura_id
251            && effect_index == carrier_effect_index
252            && aura_subtype_is(subtype, AuraSubtypeKind::HasteAll) =>
253        {
254            Some(BuffEffect::HasteMultFromEffect {
255                percent: value,
256                source_spell_id,
257                effect_index,
258            })
259        }
260        BuffEffect::AttackSpeed(current)
261            if current.abs() <= f64::EPSILON
262                && aura_subtype_is_any(
263                    subtype,
264                    &[
265                        AuraSubtypeKind::ModMeleeAutoAttackSpeedPercent,
266                        AuraSubtypeKind::ModRangedAndMeleeAutoAttackSpeedPercent,
267                    ],
268                ) =>
269        {
270            Some(BuffEffect::AttackSpeed(value))
271        }
272        BuffEffect::SpellDamageFromEffect {
273            percent: current,
274            source_spell_id,
275            effect_index,
276            periodic,
277        } if current.abs() <= f64::EPSILON
278            && source_spell_id == aura_id
279            && effect_index == carrier_effect_index =>
280        {
281            Some(BuffEffect::SpellDamageFromEffect {
282                percent: value,
283                source_spell_id,
284                effect_index,
285                periodic,
286            })
287        }
288        BuffEffect::AutoAttackDamage(current)
289            if current.abs() <= f64::EPSILON
290                && aura_subtype_is(subtype, AuraSubtypeKind::ModAutoAttackDamage) =>
291        {
292            Some(BuffEffect::AutoAttackDamage(value))
293        }
294        _ => None,
295    };
296
297    if let Some(replacement) = replacement {
298        *effect = replacement;
299
300        true
301    } else {
302        false
303    }
304}