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}