1use super::{
2 AbsorbEffect, AbsorbKind, ActiveSpellGate, AuraDefinitionDraft, AuraSubtypeKind, BuffEffect,
3 BuilderError, DamageTransfer, DamageTransferTiming, EffectLookup, HUNDRED, SpellSchoolMask,
4 aura_subtype_is, aura_subtype_is_any, effect_parts, health_threshold,
5};
6
7fn apply_non_passive_damage_effects(
9 mut aura: AuraDefinitionDraft,
10 lookup: EffectLookup<'_>,
11) -> Result<AuraDefinitionDraft, BuilderError> {
12 let (data, idx, spell_id, effect_index) = effect_parts(lookup);
13 let subtype = data.effect_aura(idx, effect_index);
14 let value = data.base_points(idx, effect_index);
15
16 if aura_subtype_is(subtype, AuraSubtypeKind::ModAutoAttackDamageTakenFromCaster) {
17 aura = aura.push_effect(BuffEffect::AutoAttackDamageTaken(value))?;
18 }
19
20 if aura_subtype_is(subtype, AuraSubtypeKind::IgnoreArmorPercent) {
21 aura = aura.push_effect(BuffEffect::ArmorBypassFromCaster(value))?;
22 }
23
24 if aura_subtype_is(subtype, AuraSubtypeKind::OverrideAutoAttackWithAbility) {
25 let replacement = data.trigger_spell(idx, effect_index).ok_or(
26 crate::builder::BuilderErrorKind::MissingSpellData {
27 spell_id,
28 field: "override_auto_attack.trigger_spell",
29 },
30 )?;
31
32 aura = aura.push_effect(BuffEffect::AutoAttackReplacement(replacement.as_u32()))?;
33 }
34
35 Ok(aura)
36}
37
38fn apply_non_passive_school_effects(
39 mut aura: AuraDefinitionDraft,
40 lookup: EffectLookup<'_>,
41) -> Result<AuraDefinitionDraft, BuilderError> {
42 let (data, idx, spell_id, effect_index) = effect_parts(lookup);
43 let subtype = data.effect_aura(idx, effect_index);
44 let property = data.effect_misc_value_0(idx, effect_index);
45
46 if aura_subtype_is(subtype, AuraSubtypeKind::ModSpellSchool) {
47 let school_mask = SpellSchoolMask::from_dbc(property);
48
49 if school_mask.is_empty() {
50 return Err(crate::builder::BuilderErrorKind::MissingSpellData {
51 spell_id,
52 field: "mod_spell_school.school_mask",
53 }
54 .into());
55 }
56
57 aura = aura.push_effect(BuffEffect::SpellSchoolFromEffect {
58 school: school_mask.primary_school(),
59 source_spell_id: spell_id,
60 effect_index,
61 })?;
62 }
63
64 if aura_subtype_is(subtype, AuraSubtypeKind::ModDamageDonePercent) {
65 aura = aura.push_effect(BuffEffect::DamageMultSchool(
66 1.0 + data.base_points(idx, effect_index) / HUNDRED,
67 SpellSchoolMask::from_dbc(property).primary_school(),
68 ))?;
69 }
70
71 aura = apply_incoming_damage_effects(aura, lookup)?;
72 aura = apply_healing_and_absorb_effects(aura, lookup)?;
73 aura = apply_immunity_effects(aura, lookup)?;
74 aura = apply_regen_effects(aura, lookup)?;
75 aura = apply_spell_gate_effects(aura, lookup)?;
76
77 Ok(aura)
78}
79
80fn apply_healing_and_absorb_effects(
83 mut aura: AuraDefinitionDraft,
84 lookup: EffectLookup<'_>,
85) -> Result<AuraDefinitionDraft, BuilderError> {
86 let (data, idx, _, effect_index) = effect_parts(lookup);
87 let subtype = data.effect_aura(idx, effect_index);
88 let value = data.base_points(idx, effect_index);
89 let schools = SpellSchoolMask::from_dbc(data.effect_misc_value_0(idx, effect_index));
90
91 let effect = if aura_subtype_is_any(
92 subtype,
93 &[
94 AuraSubtypeKind::ModHealingFlat,
95 AuraSubtypeKind::ModHealingDoneFlat,
96 ],
97 ) {
98 Some(BuffEffect::HealingDoneFlat {
99 amount: value,
100 schools,
101 })
102 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModHealingDonePercent) {
103 Some(BuffEffect::HealingDonePercent(value))
104 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModHealingReceivedPercent) {
105 Some(BuffEffect::HealingReceivedPercent(value))
106 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModHealingTakenFromCasterPercent) {
107 Some(BuffEffect::HealingReceivedFromCasterPercent(value))
108 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModPeriodicHealingPercent) {
109 Some(BuffEffect::PeriodicHealingPercent(value))
110 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModLeechPercent) {
111 Some(BuffEffect::LeechPercent(value))
112 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModAbsorbDonePercent) {
113 Some(BuffEffect::AbsorbDonePercent(value))
114 } else if aura_subtype_is(subtype, AuraSubtypeKind::ModAbsorbReceivedPercent) {
115 Some(BuffEffect::AbsorbTakenPercent(value))
116 } else if aura_subtype_is_any(
117 subtype,
118 &[
119 AuraSubtypeKind::AbsorbDamage,
120 AuraSubtypeKind::AbsorbHealing,
121 AuraSubtypeKind::AbsorbOverkillDamageFromSchool,
122 ],
123 ) {
124 let kind = if aura_subtype_is(subtype, AuraSubtypeKind::AbsorbHealing) {
125 AbsorbKind::Healing
126 } else if aura_subtype_is(subtype, AuraSubtypeKind::AbsorbOverkillDamageFromSchool) {
127 AbsorbKind::Overkill
128 } else {
129 AbsorbKind::Damage
130 };
131
132 Some(BuffEffect::Absorb(AbsorbEffect {
133 amount: value.max(0.0),
134 effect_index,
135 schools,
136 kind,
137 high_priority: false,
138 overkill_script: None,
139 priority: data.effect_misc_value_1(idx, effect_index),
140 }))
141 } else if aura_subtype_is_any(
142 subtype,
143 &[
144 AuraSubtypeKind::TransferDamagePercent,
145 AuraSubtypeKind::ShareDamagePercent,
146 ],
147 ) {
148 Some(BuffEffect::DamageTransfer(DamageTransfer {
149 fraction: value / HUNDRED,
150 schools,
151 timing: if aura_subtype_is(subtype, AuraSubtypeKind::TransferDamagePercent) {
152 DamageTransferTiming::PreMitigationSplit
153 } else {
154 DamageTransferTiming::PostMitigationShare
155 },
156 }))
157 } else {
158 None
159 };
160
161 if let Some(effect) = effect {
162 aura = aura.push_effect(effect)?;
163 }
164
165 Ok(aura)
166}
167
168fn apply_incoming_damage_effects(
169 mut aura: AuraDefinitionDraft,
170 lookup: EffectLookup<'_>,
171) -> Result<AuraDefinitionDraft, BuilderError> {
172 let (data, idx, _, effect_index) = effect_parts(lookup);
173 let subtype = data.effect_aura(idx, effect_index);
174 let property = data.effect_misc_value_0(idx, effect_index);
175 let school_mask = SpellSchoolMask::from_dbc(property);
176
177 if aura_subtype_is(subtype, AuraSubtypeKind::ModDamageTakenPercent) {
178 aura = aura.push_effect(BuffEffect::IncomingDamagePercent {
179 percent: data.base_points(idx, effect_index),
180 schools: school_mask,
181 })?;
182 }
183
184 if aura_subtype_is(subtype, AuraSubtypeKind::ModAoeDamageTakenPercent) {
185 aura = aura.push_effect(BuffEffect::IncomingAoeDamagePercent {
186 percent: data.base_points(idx, effect_index),
187 schools: school_mask,
188 })?;
189 }
190
191 if aura_subtype_is(
192 subtype,
193 AuraSubtypeKind::ModDamagePercentDoneByTargetAuraMechanic,
194 ) {
195 aura = aura.push_effect(BuffEffect::DamageToTargetAuraMechanicPercent {
196 percent: data.base_points(idx, effect_index),
197 mechanic_mask: wowlab_engine_domain::dbc::Mechanic::from_dbc(property).map_or(
198 wowlab_engine_domain::dbc::MechanicMask::empty(),
199 wowlab_engine_domain::dbc::Mechanic::mask,
200 ),
201 })?;
202 }
203
204 if aura_subtype_is(subtype, AuraSubtypeKind::ModMechanicDamageTakenPercent) {
205 aura = aura.push_effect(BuffEffect::MechanicDamageTakenPercent {
206 percent: data.base_points(idx, effect_index),
207 mechanic_mask: wowlab_engine_domain::dbc::Mechanic::from_dbc(property).map_or(
208 wowlab_engine_domain::dbc::MechanicMask::empty(),
209 wowlab_engine_domain::dbc::Mechanic::mask,
210 ),
211 })?;
212 }
213
214 if aura_subtype_is(subtype, AuraSubtypeKind::ModCreatureAoeDamageAvoidance) {
215 aura = aura.push_effect(BuffEffect::CreatureAoeDamageAvoidance {
216 percent: data.base_points(idx, effect_index),
217 schools: school_mask,
218 })?;
219 }
220
221 Ok(aura)
222}
223
224fn apply_regen_effects(
225 mut aura: AuraDefinitionDraft,
226 lookup: EffectLookup<'_>,
227) -> Result<AuraDefinitionDraft, BuilderError> {
228 let (data, idx, _, effect_index) = effect_parts(lookup);
229 let subtype = data.effect_aura(idx, effect_index);
230 let property = data.effect_misc_value_0(idx, effect_index);
231
232 if aura_subtype_is(subtype, AuraSubtypeKind::InterruptRegen) {
233 aura = aura.push_effect(BuffEffect::InterruptRegen)?;
234 }
235
236 if aura_subtype_is(subtype, AuraSubtypeKind::ModManaRegenInterrupted) {
237 aura = aura.push_effect(BuffEffect::InterruptedRegenPercent(
238 data.base_points(idx, effect_index),
239 ))?;
240 }
241
242 if aura_subtype_is(subtype, AuraSubtypeKind::PreventRegeneratePower) {
243 aura = aura.push_effect(BuffEffect::PreventResourceRegen(u64::from(
244 u32::from_ne_bytes(property.to_ne_bytes()),
245 )))?;
246 }
247
248 Ok(aura)
249}
250
251fn apply_immunity_effects(
252 mut aura: AuraDefinitionDraft,
253 lookup: EffectLookup<'_>,
254) -> Result<AuraDefinitionDraft, BuilderError> {
255 let (data, idx, _, effect_index) = effect_parts(lookup);
256 let subtype = data.effect_aura(idx, effect_index);
257 let property = data.effect_misc_value_0(idx, effect_index);
258
259 if aura_subtype_is(subtype, AuraSubtypeKind::SchoolImmunity) {
260 aura = aura.push_effect(BuffEffect::SchoolImmunity(SpellSchoolMask::from_dbc(
261 property,
262 )))?;
263 }
264
265 if aura_subtype_is(subtype, AuraSubtypeKind::MechanicImmunity) {
266 let mechanic = u32::try_from(property)
267 .ok()
268 .and_then(|mechanic| 1_u64.checked_shl(mechanic))
269 .unwrap_or(0);
270
271 aura = aura.push_effect(BuffEffect::MechanicImmunity(mechanic))?;
272 }
273
274 if aura_subtype_is(subtype, AuraSubtypeKind::CreatureImmunities) {
275 aura = aura.push_effect(BuffEffect::MechanicImmunity(u64::from(u32::from_ne_bytes(
276 property.to_ne_bytes(),
277 ))))?;
278 }
279
280 Ok(aura)
281}
282
283fn apply_spell_gate_effects(
284 mut aura: AuraDefinitionDraft,
285 lookup: EffectLookup<'_>,
286) -> Result<AuraDefinitionDraft, BuilderError> {
287 let (data, idx, spell_id, effect_index) = effect_parts(lookup);
288 let subtype = data.effect_aura(idx, effect_index);
289
290 if aura_subtype_is(subtype, AuraSubtypeKind::ModSpellHastePercent) {
291 aura = aura.push_effect(BuffEffect::SpellCastSpeed(
292 data.base_points(idx, effect_index),
293 ))?;
294 }
295
296 let gate = if aura_subtype_is(subtype, AuraSubtypeKind::IgnoreAuraState) {
297 Some(ActiveSpellGate::IgnoreAuraState)
298 } else if aura_subtype_is(subtype, AuraSubtypeKind::DisableAbilities) {
299 Some(ActiveSpellGate::DisableAbilities)
300 } else {
301 None
302 };
303
304 if let Some(gate) = gate {
305 aura = aura.push_effect(BuffEffect::SpellGateFromEffect {
306 source_spell_id: spell_id,
307 effect_index,
308 gate,
309 })?;
310 }
311
312 if aura_subtype_is(subtype, AuraSubtypeKind::ModStanceMask) {
313 aura = aura.push_effect(BuffEffect::SpellStanceMaskFromEffect {
314 source_spell_id: spell_id,
315 effect_index,
316 })?;
317 }
318
319 Ok(aura)
320}
321
322pub(in crate::builder::aura_builder) fn apply_non_passive_effect(
324 aura: AuraDefinitionDraft,
325 lookup: EffectLookup<'_>,
326) -> Result<AuraDefinitionDraft, BuilderError> {
327 let aura = apply_non_passive_damage_effects(aura, lookup)?;
328 let aura = apply_non_passive_stat_effects(aura, lookup)?;
329 let aura = health_threshold::apply_from_data(aura, lookup)?;
330
331 apply_non_passive_school_effects(aura, lookup)
332}
333
334fn apply_non_passive_stat_effects(
336 mut aura: AuraDefinitionDraft,
337 lookup: EffectLookup<'_>,
338) -> Result<AuraDefinitionDraft, BuilderError> {
339 let (data, idx, _, effect_index) = effect_parts(lookup);
340
341 if aura_subtype_is_any(
342 data.effect_aura(idx, effect_index),
343 &[
344 AuraSubtypeKind::ModMeleeAutoAttackSpeedPercent,
345 AuraSubtypeKind::ModRangedAndMeleeAutoAttackSpeedPercent,
346 ],
347 ) {
348 aura = aura.push_effect(BuffEffect::AttackSpeed(data.base_points(idx, effect_index)))?;
349 }
350
351 Ok(aura)
352}