1use serde::Deserialize;
4use wowlab_types::data::RefreshBehavior;
5
6use crate::{
7 EventEffectDef, PeriodicApplyAura, PeriodicDamage, PeriodicHook, PeriodicRemoveStack,
8 PeriodicResidualDamage, PeriodicResource, PeriodicResourceDrain, ScalarRef,
9};
10
11#[derive(Debug, Deserialize)]
13#[serde(deny_unknown_fields)]
14#[expect(
15 clippy::struct_excessive_bools,
16 reason = "fields are independent switches in the stable flat aura manifest schema"
17)]
18pub struct ManifestAuraDef {
19 pub id: u32,
20 #[serde(default)]
22 pub proc_attributes: Vec<crate::ManifestProcAttribute>,
23 #[serde(default = "default_aura_on")]
24 pub on: String,
25 #[serde(default)]
26 pub duration_ms: Option<ScalarRef>,
27 #[serde(default)]
28 pub no_pandemic: bool,
29 #[serde(default)]
30 pub refresh_behavior: Option<RefreshBehavior>,
31 #[serde(default)]
33 pub forwarded_effect_value: Option<ScalarRef>,
34 #[serde(default)]
35 pub damage_mult: Option<ScalarRef>,
36 #[serde(default)]
37 pub damage_mult_stacking: Option<StackingDamageMult>,
38 #[serde(default)]
39 pub pet_damage_mult: Option<ScalarRef>,
40 #[serde(default)]
42 pub damage_taken_from_caster_spells: Option<ScalarRef>,
43 #[serde(default)]
44 pub max_stacks: Option<ScalarRef>,
45 #[serde(default)]
46 pub haste_per_stack: Option<ScalarRef>,
47 #[serde(default)]
49 pub attack_speed_per_stack: Option<ScalarRef>,
50 #[serde(default)]
51 pub auto_attack_damage_per_stack: Option<ScalarRef>,
52 #[serde(default)]
54 pub resource_gain_mult_per_stack: Option<ScalarRef>,
55 #[serde(default)]
56 pub regen_percent_per_stack: Option<ScalarRef>,
57 #[serde(default)]
58 pub max_resource_per_stack: Option<ScalarRef>,
59 #[serde(default)]
60 pub crit_per_stack: Option<ScalarRef>,
61 #[serde(default)]
62 pub mastery_per_stack: Option<ScalarRef>,
63 #[serde(default)]
64 pub primary_stat_percent_per_stack: Option<ScalarRef>,
65 #[serde(default)]
66 pub attack_power_percent_per_stack: Option<ScalarRef>,
67 #[serde(default)]
68 pub crit_rating_per_stack: Option<ScalarRef>,
69 #[serde(default)]
70 pub haste_rating_per_stack: Option<ScalarRef>,
71 #[serde(default)]
72 pub mastery_rating_per_stack: Option<ScalarRef>,
73 #[serde(default)]
74 pub versatility_rating_per_stack: Option<ScalarRef>,
75 #[serde(default)]
76 pub primary_stat_per_stack: Option<ScalarRef>,
77 #[serde(default)]
78 pub split_across_stacks: bool,
79 #[serde(default)]
80 pub apply_at_max_stacks: bool,
81 #[serde(default)]
83 pub reverse: bool,
84 #[serde(default)]
86 pub freeze_stacks: bool,
87 #[serde(default)]
89 pub tick_behavior: Option<ManifestAuraTickBehavior>,
90 #[serde(default)]
92 pub tick_stack_change: Option<u8>,
93 #[serde(default)]
95 pub stack_tick_ms: Option<ScalarRef>,
96 #[serde(default)]
98 pub async_stacks: bool,
99 #[serde(default)]
100 pub periodic_damage_scales_with_stacks: bool,
101 #[serde(default)]
102 pub reapply_on_expire: bool,
103 #[serde(default)]
105 pub expire_hook: Option<String>,
106 #[serde(default)]
107 pub on_consume: Vec<EventEffectDef>,
108 #[serde(default)]
109 pub periodic_remove_stack: Option<PeriodicRemoveStack>,
110 #[serde(default)]
111 pub periodic_damage: Option<PeriodicDamage>,
112 #[serde(default)]
113 pub periodic_residual_damage: Option<PeriodicResidualDamage>,
114 #[serde(default)]
115 pub periodic_hasted: Option<bool>,
116 #[serde(default)]
118 pub periodic_partial_tick: bool,
119 #[serde(default)]
120 pub periodic_resource: Option<PeriodicResource>,
121 #[serde(default)]
122 pub periodic_resource_drain: Option<PeriodicResourceDrain>,
123 #[serde(default)]
124 pub periodic_apply_aura: Option<PeriodicApplyAura>,
125 #[serde(default)]
127 pub periodic_hook: Option<PeriodicHook>,
128 #[serde(default)]
130 pub tick_hook: Option<String>,
131 #[serde(default)]
132 pub precombat: bool,
133 #[serde(default)]
134 pub snapshot: bool,
135 #[serde(default)]
137 pub crit_chance_spells: Option<SpellScopedPct>,
138 #[serde(default)]
140 pub crit_damage_spells: Option<SpellScopedPct>,
141 #[serde(default)]
142 pub damage_percent_spells: Option<SpellScopedPct>,
143 #[serde(default)]
145 pub damage_percent_spells_linear: Option<SpellScopedPct>,
146 #[serde(default)]
147 pub cast_time_percent: Option<SpellScopedPct>,
148 #[serde(default)]
149 pub cast_time_percent_per_stack: Option<SpellScopedPct>,
150 #[serde(default)]
151 pub gcd_percent: Option<SpellScopedPct>,
152 #[serde(default)]
153 pub spell_cost_flat: Option<SpellScopedValue>,
154 #[serde(default)]
155 pub spell_cost_percent: Option<SpellScopedValue>,
156 #[serde(default)]
157 pub secondary_spell_cost_percent: Option<SpellScopedValue>,
158 #[serde(default)]
159 pub secondary_spell_cost_flat: Option<SpellScopedValue>,
160 #[serde(default)]
161 pub recharge_rate_percent: Vec<SpellScopedValue>,
162}
163
164#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
165#[serde(rename_all = "snake_case")]
166#[non_exhaustive]
167pub enum ManifestAuraTickBehavior {
168 Clip,
169 Refresh,
170}
171
172impl ManifestAuraDef {
173 pub fn scalar_refs(&self) -> impl Iterator<Item = &ScalarRef> {
175 [
176 self.duration_ms.as_ref(),
177 self.max_stacks.as_ref(),
178 self.forwarded_effect_value.as_ref(),
179 self.damage_mult.as_ref(),
180 self.damage_mult_stacking
181 .as_ref()
182 .map(|effect| &effect.initial),
183 self.damage_mult_stacking
184 .as_ref()
185 .map(|effect| &effect.per_stack),
186 self.pet_damage_mult.as_ref(),
187 self.damage_taken_from_caster_spells.as_ref(),
188 self.haste_per_stack.as_ref(),
189 self.attack_speed_per_stack.as_ref(),
190 self.auto_attack_damage_per_stack.as_ref(),
191 self.resource_gain_mult_per_stack.as_ref(),
192 self.regen_percent_per_stack.as_ref(),
193 self.max_resource_per_stack.as_ref(),
194 self.crit_per_stack.as_ref(),
195 self.mastery_per_stack.as_ref(),
196 self.primary_stat_percent_per_stack.as_ref(),
197 self.attack_power_percent_per_stack.as_ref(),
198 self.crit_rating_per_stack.as_ref(),
199 self.haste_rating_per_stack.as_ref(),
200 self.mastery_rating_per_stack.as_ref(),
201 self.versatility_rating_per_stack.as_ref(),
202 self.primary_stat_per_stack.as_ref(),
203 self.crit_chance_spells.as_ref().map(|effect| &effect.pct),
204 self.crit_damage_spells.as_ref().map(|effect| &effect.pct),
205 self.damage_percent_spells
206 .as_ref()
207 .map(|effect| &effect.pct),
208 self.damage_percent_spells_linear
209 .as_ref()
210 .map(|effect| &effect.pct),
211 self.cast_time_percent.as_ref().map(|effect| &effect.pct),
212 self.cast_time_percent_per_stack
213 .as_ref()
214 .map(|effect| &effect.pct),
215 self.gcd_percent.as_ref().map(|effect| &effect.pct),
216 self.spell_cost_flat.as_ref().map(|effect| &effect.value),
217 self.spell_cost_percent.as_ref().map(|effect| &effect.value),
218 self.secondary_spell_cost_percent
219 .as_ref()
220 .map(|effect| &effect.value),
221 self.secondary_spell_cost_flat
222 .as_ref()
223 .map(|effect| &effect.value),
224 ]
225 .into_iter()
226 .flatten()
227 .chain(
228 self.recharge_rate_percent
229 .iter()
230 .map(|effect| &effect.value),
231 )
232 .chain(self.on_consume.iter().map(|effect| match effect {
233 EventEffectDef::Energize { amount, .. } => amount,
234 EventEffectDef::ExtendAura { amount_ms, .. } => amount_ms,
235 }))
236 .chain(self.periodic_damage.iter().flat_map(|periodic| {
237 [
238 Some(&periodic.tick_ms),
239 periodic.ap_coef.as_ref(),
240 periodic.sp_coef.as_ref(),
241 periodic.item_damage_total.as_ref(),
242 periodic.resource_gain.as_ref(),
243 periodic.crit_resource_gain.as_ref(),
244 periodic.crit_resource_chance.as_ref(),
245 ]
246 .into_iter()
247 .flatten()
248 }))
249 .chain(
250 self.periodic_residual_damage
251 .iter()
252 .map(|periodic| &periodic.tick_ms),
253 )
254 .chain(
255 self.periodic_resource
256 .iter()
257 .flat_map(|periodic| [&periodic.tick_ms, &periodic.amount]),
258 )
259 .chain(
260 self.periodic_resource_drain
261 .iter()
262 .flat_map(|periodic| [&periodic.tick_ms, &periodic.amount]),
263 )
264 .chain(
265 self.periodic_apply_aura
266 .iter()
267 .map(|periodic| &periodic.tick_ms),
268 )
269 .chain(
270 self.periodic_remove_stack
271 .iter()
272 .map(|periodic| &periodic.tick_ms),
273 )
274 .chain(self.stack_tick_ms.iter())
275 .chain(self.periodic_hook.iter().map(|periodic| &periodic.tick_ms))
276 }
277
278 pub(crate) fn scoped_spell_names(&self) -> impl Iterator<Item = &String> {
279 self.crit_chance_spells
280 .iter()
281 .flat_map(|effect| &effect.spells)
282 .chain(
283 self.crit_damage_spells
284 .iter()
285 .flat_map(|effect| &effect.spells),
286 )
287 .chain(
288 self.damage_percent_spells
289 .iter()
290 .flat_map(|effect| &effect.spells),
291 )
292 .chain(
293 self.damage_percent_spells_linear
294 .iter()
295 .flat_map(|effect| &effect.spells),
296 )
297 .chain(
298 self.cast_time_percent
299 .iter()
300 .flat_map(|effect| &effect.spells),
301 )
302 .chain(
303 self.cast_time_percent_per_stack
304 .iter()
305 .flat_map(|effect| &effect.spells),
306 )
307 .chain(self.gcd_percent.iter().flat_map(|effect| &effect.spells))
308 .chain(
309 self.spell_cost_flat
310 .iter()
311 .flat_map(|effect| &effect.spells),
312 )
313 .chain(
314 self.spell_cost_percent
315 .iter()
316 .flat_map(|effect| &effect.spells),
317 )
318 .chain(
319 self.secondary_spell_cost_percent
320 .iter()
321 .flat_map(|effect| &effect.spells),
322 )
323 .chain(
324 self.secondary_spell_cost_flat
325 .iter()
326 .flat_map(|effect| &effect.spells),
327 )
328 .chain(
329 self.recharge_rate_percent
330 .iter()
331 .flat_map(|effect| &effect.spells),
332 )
333 }
334}
335
336#[derive(Debug, Deserialize)]
338pub struct SpellScopedPct {
339 pub pct: ScalarRef,
340 pub spells: Vec<String>,
341}
342
343#[derive(Debug, Deserialize)]
345pub struct SpellScopedValue {
346 pub value: ScalarRef,
347 pub spells: Vec<String>,
348}
349
350#[derive(Debug, Deserialize)]
352pub struct StackingDamageMult {
353 pub initial: ScalarRef,
354 pub per_stack: ScalarRef,
355}
356
357fn default_aura_on() -> String {
358 "player".to_string()
359}