1#![allow(non_snake_case, dead_code)]
4use wowlab_types::{combat::ResourceType, game::SpecId};
5use wowlab_engine_combat::{
6 BuiltCombatSystem, AuraDefinitionDraft, LocalAuraIdx, LocalSpellIdx,
7 SpellDefinitionDraft,
8};
9use wowlab_engine_ports::{
10 DeclaredSpecMetadata, SpecDescriptor, SpecHandler, HeroTalentTreeDesc,
11};
12use wowlab_engine_gamedata::ResolvedGameData;
13pub(crate) mod SPELL {
14 pub(crate) const SUMMON_FELHUNTER: u32 = 691;
16 pub(crate) const SUMMON_IMP: u32 = 688;
18 pub(crate) const GRIMOIRE_OF_SACRIFICE: u32 = 108_503;
20 pub(crate) const AGONY: u32 = 980;
22 pub(crate) const CORRUPTION: u32 = 172;
24 pub(crate) const UNSTABLE_AFFLICTION: u32 = 1_259_790;
26 pub(crate) const SEED_OF_CORRUPTION: u32 = 27243;
28 pub(crate) const HAUNT: u32 = 48181;
30 pub(crate) const SUMMON_DARKGLARE: u32 = 205_180;
32 pub(crate) const DARK_HARVEST: u32 = 1_257_052;
34 pub(crate) const SHADOW_BOLT: u32 = 686;
36 pub(crate) const DRAIN_SOUL: u32 = 198_590;
38 pub(crate) const MALEFIC_GRASP: u32 = 1_261_153;
40 pub(crate) const DRAIN_LIFE: u32 = 234_153;
42 pub(crate) const MALEVOLENCE: u32 = 430_014;
44}
45pub(crate) mod AURA {
46 pub(crate) const AGONY: u32 = 980;
48 pub(crate) const CORRUPTION: u32 = 146_739;
50 pub(crate) const UNSTABLE_AFFLICTION: u32 = 1_259_790;
52 pub(crate) const SEED_OF_CORRUPTION: u32 = 27243;
54 pub(crate) const SEED_OF_CORRUPTION_IS_OUT_DNT: u32 = 1_279_998;
56 pub(crate) const RECENTLY_DEMON_HAS_BEEN_SUMMONED_DNT: u32 = 1_288_970;
58 pub(crate) const NIGHTFALL: u32 = 264_571;
60 pub(crate) const HAUNT: u32 = 48181;
62 pub(crate) const DARKGLARE_PRESENCE: u32 = 1_280_663;
64 pub(crate) const SHARD_INSTABILITY: u32 = 1_260_269;
66 pub(crate) const CASCADING_CALAMITY: u32 = 1_261_125;
68 pub(crate) const GRIMOIRE_OF_SACRIFICE: u32 = 196_099;
70 pub(crate) const WITHER: u32 = 445_474;
72 pub(crate) const MALEVOLENCE: u32 = 442_726;
74 pub(crate) const SUCCULENT_SOUL: u32 = 449_793;
76 pub(crate) const MANIFESTED_DEMONIC_SOUL: u32 = 1_269_042;
78 pub(crate) const SOUL_ANATHEMA: u32 = 450_538;
80 pub(crate) const TRACK_PETS: u32 = 462_599;
82}
83pub(crate) mod TALENT {
84 pub(crate) const ABSOLUTE_CORRUPTION: u32 = 196_103;
86 pub(crate) const CASCADING_CALAMITY: u32 = 1_261_124;
88 pub(crate) const CREEPING_DEATH: u32 = 264_000;
90 pub(crate) const CUNNING_CRUELTY: u32 = 453_172;
92 pub(crate) const DARK_HARVEST: u32 = 1_257_052;
94 pub(crate) const DEATHS_EMBRACE: u32 = 234_876;
96 pub(crate) const DEMONIACS_FERVOR: u32 = 449_629;
98 pub(crate) const DEMONIC_SOUL: u32 = 449_614;
100 pub(crate) const DRAIN_SOUL: u32 = 388_667;
102 pub(crate) const FATAL_ECHOES: u32 = 1_260_229;
104 pub(crate) const GRIMOIRE_OF_SACRIFICE: u32 = 108_503;
106 pub(crate) const HAUNT: u32 = 48181;
108 pub(crate) const IMPROVED_SHADOW_BOLT: u32 = 453_080;
110 pub(crate) const MALEFIC_GRASP: u32 = 1_261_149;
112 pub(crate) const MALEVOLENCE: u32 = 430_014;
114 pub(crate) const MANIFESTED_AVARICE: u32 = 1_268_884;
116 pub(crate) const NIGHTFALL: u32 = 108_558;
118 pub(crate) const NOCTURNAL_YIELD: u32 = 1_260_271;
120 pub(crate) const RAVENOUS_AFFLICTIONS: u32 = 459_440;
122 pub(crate) const SHADOW_OF_DEATH: u32 = 449_638;
124 pub(crate) const SHADOW_OF_NATHREZA: u32 = 1_261_984;
126 pub(crate) const SHADOW_OF_NATHREZA_2: u32 = 1_261_990;
128 pub(crate) const SHADOW_OF_NATHREZA_3: u32 = 1_261_992;
130 pub(crate) const SHARD_INSTABILITY: u32 = 1_260_264;
132 pub(crate) const SOUL_ANATHEMA: u32 = 449_624;
134 pub(crate) const SUMMON_DARKGLARE: u32 = 205_180;
136 pub(crate) const WITHERING_BOLT: u32 = 386_976;
138}
139pub(crate) mod REPORTED_SPELL {
140 pub(crate) const DARK_HARVEST_TICK: u32 = 1_257_065;
142 pub(crate) const DEMONIC_SOUL: u32 = 449_801;
144 pub(crate) const EYE_BEAM: u32 = 205_231;
146 pub(crate) const SHADOW_OF_NATHREZA: u32 = 1_262_710;
148 pub(crate) const SOUL_SWIPE: u32 = 1_269_049;
150 pub(crate) const WRATH_OF_NATHREZA: u32 = 1_278_047;
152}
153pub(crate) mod HERO {
154 pub(crate) mod HELLCALLER {
155 pub(crate) mod SPELL {
156 pub(crate) const BLACKENED_SOUL_DAMAGE: u32 = 445_736;
158 pub(crate) const BLACKENED_SOUL_TRIGGER: u32 = 445_731;
160 pub(crate) const MALEVOLENCE: u32 = 430_014;
162 pub(crate) const MALEVOLENCE_DAMAGE: u32 = 446_285;
164 pub(crate) const WITHER: u32 = 445_465;
166 pub(crate) const WITHER_DIRECT: u32 = 445_468;
168 }
169 pub(crate) mod AURA {
170 pub(crate) const BLACKENED_SOUL: u32 = 445_731;
172 pub(crate) const MALEVOLENCE_BUFF: u32 = 442_726;
174 pub(crate) const WITHER_DOT: u32 = 445_474;
176 }
177 }
178 pub(crate) mod SOUL_HARVESTER {
179 pub(crate) mod SPELL {
180 pub(crate) const DARK_HARVEST_ENERGIZE: u32 = 449_858;
182 pub(crate) const DEMONIC_SOUL_DAMAGE: u32 = 449_801;
184 pub(crate) const SHARED_FATE: u32 = 450_591;
186 pub(crate) const SOUL_ANATHEMA: u32 = 450_538;
188 pub(crate) const WICKED_REAPING: u32 = 449_826;
190 }
191 pub(crate) mod AURA {
192 pub(crate) const MANIFESTED_DEMONIC_SOUL: u32 = 1_269_042;
194 pub(crate) const SUCCULENT_SOUL: u32 = 449_793;
196 }
197 }
198}
199static HERO_TREE_HELLCALLER_SPELLS: &[(&str, u32)] = &[
200 ("BLACKENED_SOUL_DAMAGE", 445_736),
201 ("BLACKENED_SOUL_TRIGGER", 445_731),
202 ("MALEVOLENCE", 430_014),
203 ("MALEVOLENCE_DAMAGE", 446_285),
204 ("WITHER", 445_465),
205 ("WITHER_DIRECT", 445_468),
206];
207static HERO_TREE_HELLCALLER_AURAS: &[(&str, u32)] = &[
208 ("BLACKENED_SOUL", 445_731),
209 ("MALEVOLENCE_BUFF", 442_726),
210 ("WITHER_DOT", 445_474),
211];
212static HERO_TREE_SOUL_HARVESTER_SPELLS: &[(&str, u32)] = &[
213 ("DARK_HARVEST_ENERGIZE", 449_858),
214 ("DEMONIC_SOUL_DAMAGE", 449_801),
215 ("SHARED_FATE", 450_591),
216 ("SOUL_ANATHEMA", 450_538),
217 ("WICKED_REAPING", 449_826),
218];
219static HERO_TREE_SOUL_HARVESTER_AURAS: &[(&str, u32)] = &[
220 ("MANIFESTED_DEMONIC_SOUL", 1_269_042),
221 ("SUCCULENT_SOUL", 449_793),
222];
223pub(crate) const AURA_AGONY: LocalAuraIdx = LocalAuraIdx::new(0);
224pub(crate) const AURA_CORRUPTION: LocalAuraIdx = LocalAuraIdx::new(1);
225pub(crate) const AURA_UNSTABLE_AFFLICTION: LocalAuraIdx = LocalAuraIdx::new(2);
226pub(crate) const AURA_SEED_OF_CORRUPTION: LocalAuraIdx = LocalAuraIdx::new(3);
227pub(crate) const AURA_SEED_OF_CORRUPTION_IS_OUT_DNT: LocalAuraIdx = LocalAuraIdx::new(4);
228pub(crate) const AURA_RECENTLY_DEMON_HAS_BEEN_SUMMONED_DNT: LocalAuraIdx = LocalAuraIdx::new(
229 5,
230);
231pub(crate) const AURA_NIGHTFALL: LocalAuraIdx = LocalAuraIdx::new(6);
232pub(crate) const AURA_HAUNT: LocalAuraIdx = LocalAuraIdx::new(7);
233pub(crate) const AURA_DARKGLARE_PRESENCE: LocalAuraIdx = LocalAuraIdx::new(8);
234pub(crate) const AURA_SHARD_INSTABILITY: LocalAuraIdx = LocalAuraIdx::new(9);
235pub(crate) const AURA_CASCADING_CALAMITY: LocalAuraIdx = LocalAuraIdx::new(10);
236pub(crate) const AURA_GRIMOIRE_OF_SACRIFICE: LocalAuraIdx = LocalAuraIdx::new(11);
237pub(crate) const AURA_WITHER: LocalAuraIdx = LocalAuraIdx::new(12);
238pub(crate) const AURA_MALEVOLENCE: LocalAuraIdx = LocalAuraIdx::new(13);
239pub(crate) const AURA_SUCCULENT_SOUL: LocalAuraIdx = LocalAuraIdx::new(14);
240pub(crate) const AURA_MANIFESTED_DEMONIC_SOUL: LocalAuraIdx = LocalAuraIdx::new(15);
241pub(crate) const AURA_SOUL_ANATHEMA: LocalAuraIdx = LocalAuraIdx::new(16);
242pub(crate) const AURA_TRACK_PETS: LocalAuraIdx = LocalAuraIdx::new(17);
243pub(crate) const AURA_COUNT: usize = 18;
244const _: () = assert!(
245 AURA_COUNT == 18, "AURA index ABI: count changed -- update positional hook consumers"
246);
247pub(crate) const SPELL_SUMMON_FELHUNTER: LocalSpellIdx = LocalSpellIdx::new(0);
248pub(crate) const SPELL_SUMMON_IMP: LocalSpellIdx = LocalSpellIdx::new(1);
249pub(crate) const SPELL_GRIMOIRE_OF_SACRIFICE: LocalSpellIdx = LocalSpellIdx::new(2);
250pub(crate) const SPELL_AGONY: LocalSpellIdx = LocalSpellIdx::new(3);
251pub(crate) const SPELL_CORRUPTION: LocalSpellIdx = LocalSpellIdx::new(4);
252pub(crate) const SPELL_UNSTABLE_AFFLICTION: LocalSpellIdx = LocalSpellIdx::new(5);
253pub(crate) const SPELL_SEED_OF_CORRUPTION: LocalSpellIdx = LocalSpellIdx::new(6);
254pub(crate) const SPELL_HAUNT: LocalSpellIdx = LocalSpellIdx::new(7);
255pub(crate) const SPELL_SUMMON_DARKGLARE: LocalSpellIdx = LocalSpellIdx::new(8);
256pub(crate) const SPELL_DARK_HARVEST: LocalSpellIdx = LocalSpellIdx::new(9);
257pub(crate) const SPELL_SHADOW_BOLT: LocalSpellIdx = LocalSpellIdx::new(10);
258pub(crate) const SPELL_DRAIN_SOUL: LocalSpellIdx = LocalSpellIdx::new(11);
259pub(crate) const SPELL_MALEFIC_GRASP: LocalSpellIdx = LocalSpellIdx::new(12);
260pub(crate) const SPELL_DRAIN_LIFE: LocalSpellIdx = LocalSpellIdx::new(13);
261pub(crate) const SPELL_MALEVOLENCE: LocalSpellIdx = LocalSpellIdx::new(14);
262pub(crate) const SPELL_COUNT: usize = 15;
263const _: () = assert!(
264 SPELL_COUNT == 15,
265 "SPELL index ABI: count changed -- update positional hook consumers"
266);
267pub(crate) mod EFFECT {
268 pub(crate) const POTENT_AFFLICTIONS_DAMAGE: (u32, u8) = (77215, 1);
270 pub(crate) const POTENT_AFFLICTIONS_DIRECT: (u32, u8) = (77215, 2);
272 pub(crate) const POTENT_AFFLICTIONS_LABEL_DAMAGE: (u32, u8) = (77215, 3);
274 pub(crate) const EYE_BEAM_DAMAGE: (u32, u8) = (205_231, 1);
276 pub(crate) const EYE_BEAM_PER_DOT: (u32, u8) = (205_180, 3);
278 pub(crate) const DEMONIC_SOUL_DAMAGE: (u32, u8) = (449_801, 1);
280 pub(crate) const DEMONIACS_FERVOR_DAMAGE: (u32, u8) = (449_629, 1);
282 pub(crate) const SOUL_SWIPE_DAMAGE: (u32, u8) = (1_269_049, 1);
284 pub(crate) const SOUL_SWIPE_AOE_DAMAGE: (u32, u8) = (1_269_049, 2);
286 pub(crate) const SHADOW_OF_NATHREZA_DAMAGE: (u32, u8) = (1_262_710, 1);
288 pub(crate) const WRATH_OF_NATHREZA_DAMAGE: (u32, u8) = (1_278_047, 1);
290 pub(crate) const SHADOW_OF_DEATH_SHARDS: (u32, u8) = (449_638, 2);
292}
293static REPORTED_SPELLS: &[(&str, u32)] = &[
294 ("dark_harvest_tick", 1_257_065),
295 ("demonic_soul", 449_801),
296 ("eye_beam", 205_231),
297 ("shadow_of_nathreza", 1_262_710),
298 ("soul_swipe", 1_269_049),
299 ("wrath_of_nathreza", 1_278_047),
300];
301static DECLARED_SPELL_IDS: &[u32] = &[
302 691, 688, 108_503, 980, 172, 1_259_790, 27243, 48181, 205_180, 1_257_052, 686,
303 198_590, 1_261_153, 234_153, 430_014, 980, 146_739, 1_259_790, 27243, 1_279_998,
304 1_288_970, 264_571, 48181, 1_280_663, 1_260_269, 1_261_125, 196_099, 445_474,
305 442_726, 449_793, 1_269_042, 450_538, 462_599, 1, 980, 48181, 77215, 146_739,
306 196_103, 205_180, 205_231, 449_629, 449_638, 449_801, 1_257_065, 1_262_710,
307 1_269_049, 1_278_047, 1_257_065, 449_801, 205_231, 1_262_710, 1_269_049, 1_278_047,
308 445_736, 445_731, 430_014, 446_285, 445_465, 445_468, 445_731, 442_726, 445_474,
309 449_858, 449_801, 450_591, 450_538, 449_826, 1_269_042, 449_793,
310];
311static DECLARED_AURA_IDS: &[u32] = &[
312 980, 146_739, 1_259_790, 27243, 1_279_998, 1_288_970, 264_571, 48181, 1_280_663,
313 1_260_269, 1_261_125, 196_099, 445_474, 442_726, 449_793, 1_269_042, 450_538,
314 462_599, 445_731, 442_726, 445_474, 1_269_042, 449_793,
315];
316static TALENTS: &[(&str, u32)] = &[
317 ("ABSOLUTE_CORRUPTION", TALENT::ABSOLUTE_CORRUPTION),
318 ("CASCADING_CALAMITY", TALENT::CASCADING_CALAMITY),
319 ("CREEPING_DEATH", TALENT::CREEPING_DEATH),
320 ("CUNNING_CRUELTY", TALENT::CUNNING_CRUELTY),
321 ("DARK_HARVEST", TALENT::DARK_HARVEST),
322 ("DEATHS_EMBRACE", TALENT::DEATHS_EMBRACE),
323 ("DEMONIACS_FERVOR", TALENT::DEMONIACS_FERVOR),
324 ("DEMONIC_SOUL", TALENT::DEMONIC_SOUL),
325 ("DRAIN_SOUL", TALENT::DRAIN_SOUL),
326 ("FATAL_ECHOES", TALENT::FATAL_ECHOES),
327 ("GRIMOIRE_OF_SACRIFICE", TALENT::GRIMOIRE_OF_SACRIFICE),
328 ("HAUNT", TALENT::HAUNT),
329 ("IMPROVED_SHADOW_BOLT", TALENT::IMPROVED_SHADOW_BOLT),
330 ("MALEFIC_GRASP", TALENT::MALEFIC_GRASP),
331 ("MALEVOLENCE", TALENT::MALEVOLENCE),
332 ("MANIFESTED_AVARICE", TALENT::MANIFESTED_AVARICE),
333 ("NIGHTFALL", TALENT::NIGHTFALL),
334 ("NOCTURNAL_YIELD", TALENT::NOCTURNAL_YIELD),
335 ("RAVENOUS_AFFLICTIONS", TALENT::RAVENOUS_AFFLICTIONS),
336 ("SHADOW_OF_DEATH", TALENT::SHADOW_OF_DEATH),
337 ("SHADOW_OF_NATHREZA", TALENT::SHADOW_OF_NATHREZA),
338 ("SHADOW_OF_NATHREZA_2", TALENT::SHADOW_OF_NATHREZA_2),
339 ("SHADOW_OF_NATHREZA_3", TALENT::SHADOW_OF_NATHREZA_3),
340 ("SHARD_INSTABILITY", TALENT::SHARD_INSTABILITY),
341 ("SOUL_ANATHEMA", TALENT::SOUL_ANATHEMA),
342 ("SUMMON_DARKGLARE", TALENT::SUMMON_DARKGLARE),
343 ("WITHERING_BOLT", TALENT::WITHERING_BOLT),
344];
345pub(crate) mod AFFLICTION_WARLOCK {
346 pub(crate) const AGONY_CASTS: u32 = 0;
347 pub(crate) const CORRUPTION_CASTS: u32 = 1;
348 pub(crate) const UNSTABLE_AFFLICTION_CASTS: u32 = 2;
349 pub(crate) const SHADOW_BOLT_CASTS: u32 = 3;
350 pub(crate) const HAUNT_CASTS: u32 = 4;
351 pub(crate) const SUMMON_DARKGLARE_CASTS: u32 = 5;
352 pub(crate) const DARK_HARVEST_CASTS: u32 = 6;
353 pub(crate) const AGONY_UPTIME_MS: u32 = 7;
354 pub(crate) const CORRUPTION_UPTIME_MS: u32 = 8;
355 pub(crate) const UNSTABLE_AFFLICTION_UPTIME_MS: u32 = 9;
356}
357fn aura_agony(
358 a: AuraDefinitionDraft,
359 data: &ResolvedGameData,
360) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
361 a.on_target()
362 .apply_base_from_data(data, 980)?
363 .max_stacks(
364 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(980))?,
365 )
366 .snapshot()
367 .periodic_damage_sp_from_data(
368 data,
369 wowlab_engine_combat::PeriodicDamageDataInput {
370 tick_ms: wowlab_engine_combat::validated_milliseconds(
371 data.period(wowlab_types::sim::SpellIdx::from_raw(980), 1),
372 980,
373 "periodic_damage.tick_ms",
374 )?,
375 spell_id: 980,
376 effect_index: 1,
377 multiplier: 8.0,
378 offset: -0.000_000_808_56,
379 round_decimals: Some(6),
380 },
381 )?
382 .apply_periodic_behavior_from_data(data, 980)
383}
384fn aura_corruption(
385 a: AuraDefinitionDraft,
386 data: &ResolvedGameData,
387) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
388 a.on_target()
389 .duration_ms(
390 wowlab_engine_combat::validated_milliseconds(
391 f64::from(
392 data
393 .require_aura_duration_ms(
394 wowlab_types::sim::SpellIdx::from_raw(196_103),
395 )?,
396 ),
397 146_739,
398 "aura.duration_ms",
399 )?,
400 )
401 .apply_periodic_effect_from_data(data, 146_739)?
402 .apply_duration_behavior_from_data(data, 196_103)?
403 .apply_timing_modifiers_from_data(data, 146_739)?
404 .max_stacks(
405 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(146_739))?,
406 )
407 .snapshot()
408 .periodic_damage_sp_from_data(
409 data,
410 wowlab_engine_combat::PeriodicDamageDataInput {
411 tick_ms: wowlab_engine_combat::validated_milliseconds(
412 data.period(wowlab_types::sim::SpellIdx::from_raw(146_739), 1),
413 146_739,
414 "periodic_damage.tick_ms",
415 )?,
416 spell_id: 146_739,
417 effect_index: 1,
418 multiplier: 1.15,
419 offset: 0.0,
420 round_decimals: Some(6),
421 },
422 )?
423 .apply_periodic_behavior_from_data(data, 146_739)
424}
425fn aura_unstable_affliction(
426 a: AuraDefinitionDraft,
427 data: &ResolvedGameData,
428) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
429 Ok(
430 a
431 .on_target()
432 .apply_base_from_data(data, 1_259_790)?
433 .max_stacks(
434 data
435 .require_aura_max_stacks(
436 wowlab_types::sim::SpellIdx::from_raw(1_259_790),
437 )?,
438 )
439 .snapshot(),
440 )
441}
442fn aura_seed_of_corruption(
443 a: AuraDefinitionDraft,
444 data: &ResolvedGameData,
445) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
446 Ok(
447 a
448 .on_target()
449 .apply_base_from_data(data, 27243)?
450 .max_stacks(
451 data
452 .require_aura_max_stacks(
453 wowlab_types::sim::SpellIdx::from_raw(27243),
454 )?,
455 ),
456 )
457}
458fn aura_seed_of_corruption_is_out_dnt(
459 a: AuraDefinitionDraft,
460 data: &ResolvedGameData,
461) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
462 Ok(
463 a
464 .on_player()
465 .apply_base_from_data(data, 1_279_998)?
466 .max_stacks(
467 data
468 .require_aura_max_stacks(
469 wowlab_types::sim::SpellIdx::from_raw(1_279_998),
470 )?,
471 ),
472 )
473}
474fn aura_recently_demon_has_been_summoned_dnt(
475 a: AuraDefinitionDraft,
476 data: &ResolvedGameData,
477) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
478 Ok(
479 a
480 .on_player()
481 .apply_base_from_data(data, 1_288_970)?
482 .max_stacks(
483 data
484 .require_aura_max_stacks(
485 wowlab_types::sim::SpellIdx::from_raw(1_288_970),
486 )?,
487 ),
488 )
489}
490fn aura_nightfall(
491 a: AuraDefinitionDraft,
492 data: &ResolvedGameData,
493) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
494 Ok(
495 a
496 .on_player()
497 .apply_base_from_data(data, 264_571)?
498 .max_stacks(
499 data
500 .require_aura_max_stacks(
501 wowlab_types::sim::SpellIdx::from_raw(264_571),
502 )?,
503 ),
504 )
505}
506fn aura_haunt(
507 a: AuraDefinitionDraft,
508 data: &ResolvedGameData,
509) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
510 Ok(
511 a
512 .on_target()
513 .apply_base_from_data(data, 48181)?
514 .max_stacks(
515 data
516 .require_aura_max_stacks(
517 wowlab_types::sim::SpellIdx::from_raw(48181),
518 )?,
519 )
520 .periodic_hook(
521 wowlab_engine_combat::validated_milliseconds(
522 data.period(wowlab_types::sim::SpellIdx::from_raw(48181), 5),
523 48181,
524 "periodic_hook.tick_ms",
525 )?,
526 )
527 .apply_periodic_behavior_from_data(data, 48181)?
528 .tick_hook(crate::hooks::affliction_warlock::haunt_tick_hook),
529 )
530}
531fn aura_darkglare_presence(
532 a: AuraDefinitionDraft,
533 data: &ResolvedGameData,
534) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
535 Ok(
536 a
537 .on_player()
538 .apply_base_from_data(data, 1_280_663)?
539 .max_stacks(
540 data
541 .require_aura_max_stacks(
542 wowlab_types::sim::SpellIdx::from_raw(1_280_663),
543 )?,
544 ),
545 )
546}
547fn aura_shard_instability(
548 a: AuraDefinitionDraft,
549 data: &ResolvedGameData,
550) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
551 Ok(
552 a
553 .on_player()
554 .apply_base_from_data(data, 1_260_269)?
555 .max_stacks(
556 data
557 .require_aura_max_stacks(
558 wowlab_types::sim::SpellIdx::from_raw(1_260_269),
559 )?,
560 ),
561 )
562}
563fn aura_cascading_calamity(
564 a: AuraDefinitionDraft,
565 data: &ResolvedGameData,
566) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
567 Ok(
568 a
569 .on_player()
570 .apply_base_from_data(data, 1_261_125)?
571 .max_stacks(
572 data
573 .require_aura_max_stacks(
574 wowlab_types::sim::SpellIdx::from_raw(1_261_125),
575 )?,
576 ),
577 )
578}
579fn aura_grimoire_of_sacrifice(
580 a: AuraDefinitionDraft,
581 data: &ResolvedGameData,
582) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
583 Ok(
584 a
585 .on_player()
586 .apply_base_from_data(data, 196_099)?
587 .max_stacks(
588 data
589 .require_aura_max_stacks(
590 wowlab_types::sim::SpellIdx::from_raw(196_099),
591 )?,
592 ),
593 )
594}
595fn aura_wither(
596 a: AuraDefinitionDraft,
597 data: &ResolvedGameData,
598) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
599 a.on_target()
600 .apply_base_from_data(data, 445_474)?
601 .max_stacks(
602 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(445_474))?,
603 )
604 .snapshot()
605 .periodic_damage_sp(
606 wowlab_engine_combat::validated_milliseconds(
607 2000.0,
608 445_474,
609 "periodic_damage.tick_ms",
610 )?,
611 0.112,
612 wowlab_engine_combat::DamageFlags::empty(),
613 )
614 .apply_periodic_behavior_from_data(data, 445_474)
615}
616fn aura_malevolence(
617 a: AuraDefinitionDraft,
618 data: &ResolvedGameData,
619) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
620 Ok(
621 a
622 .on_player()
623 .apply_base_from_data(data, 442_726)?
624 .max_stacks(
625 data
626 .require_aura_max_stacks(
627 wowlab_types::sim::SpellIdx::from_raw(442_726),
628 )?,
629 ),
630 )
631}
632fn aura_succulent_soul(
633 a: AuraDefinitionDraft,
634 data: &ResolvedGameData,
635) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
636 Ok(
637 a
638 .on_player()
639 .apply_base_from_data(data, 449_793)?
640 .max_stacks(
641 data
642 .require_aura_max_stacks(
643 wowlab_types::sim::SpellIdx::from_raw(449_793),
644 )?,
645 ),
646 )
647}
648fn aura_manifested_demonic_soul(
649 a: AuraDefinitionDraft,
650 data: &ResolvedGameData,
651) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
652 Ok(
653 a
654 .on_player()
655 .apply_base_from_data(data, 1_269_042)?
656 .max_stacks(
657 data
658 .require_aura_max_stacks(
659 wowlab_types::sim::SpellIdx::from_raw(1_269_042),
660 )?,
661 ),
662 )
663}
664fn aura_soul_anathema(
665 a: AuraDefinitionDraft,
666 data: &ResolvedGameData,
667) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
668 Ok(
669 a
670 .on_target()
671 .apply_base_from_data(data, 450_538)?
672 .max_stacks(
673 data
674 .require_aura_max_stacks(
675 wowlab_types::sim::SpellIdx::from_raw(450_538),
676 )?,
677 ),
678 )
679}
680fn aura_track_pets(
681 a: AuraDefinitionDraft,
682 data: &ResolvedGameData,
683) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
684 Ok(
685 a
686 .on_player()
687 .apply_base_from_data(data, 462_599)?
688 .max_stacks(
689 data
690 .require_aura_max_stacks(
691 wowlab_types::sim::SpellIdx::from_raw(462_599),
692 )?,
693 ),
694 )
695}
696fn spell_summon_felhunter(
697 s: SpellDefinitionDraft,
698 data: &ResolvedGameData,
699) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
700 Ok(s.apply_base_from_data(data, 691)?.apply_aoe_from_data(data))
701}
702fn spell_summon_imp(
703 s: SpellDefinitionDraft,
704 data: &ResolvedGameData,
705) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
706 Ok(s.apply_base_from_data(data, 688)?.apply_aoe_from_data(data))
707}
708fn spell_grimoire_of_sacrifice(
709 s: SpellDefinitionDraft,
710 data: &ResolvedGameData,
711) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
712 Ok(
713 s
714 .apply_base_from_data(data, 108_503)?
715 .applies_aura(AURA_GRIMOIRE_OF_SACRIFICE)
716 .apply_aoe_from_data(data),
717 )
718}
719fn spell_agony(
720 s: SpellDefinitionDraft,
721 data: &ResolvedGameData,
722) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
723 Ok(
724 s
725 .apply_base_from_data(data, 980)?
726 .applies_inferred_aura(AURA_AGONY, 980)
727 .apply_aoe_from_data(data),
728 )
729}
730fn spell_corruption(
731 s: SpellDefinitionDraft,
732 data: &ResolvedGameData,
733) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
734 Ok(
735 s
736 .apply_base_from_data(data, 172)?
737 .applies_aura(AURA_CORRUPTION)
738 .apply_aoe_from_data(data),
739 )
740}
741fn spell_unstable_affliction(
742 s: SpellDefinitionDraft,
743 data: &ResolvedGameData,
744) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
745 Ok(
746 s
747 .apply_base_from_data(data, 1_259_790)?
748 .applies_inferred_aura(AURA_UNSTABLE_AFFLICTION, 1_259_790)
749 .apply_aoe_from_data(data)
750 .on_cast(crate::hooks::affliction_warlock::unstable_affliction_hook),
751 )
752}
753fn spell_seed_of_corruption(
754 s: SpellDefinitionDraft,
755 data: &ResolvedGameData,
756) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
757 Ok(
758 s
759 .apply_base_from_data(data, 27243)?
760 .applies_inferred_aura(AURA_SEED_OF_CORRUPTION, 27243)
761 .apply_aoe_from_data(data),
762 )
763}
764fn spell_haunt(
765 s: SpellDefinitionDraft,
766 data: &ResolvedGameData,
767) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
768 Ok(
769 s
770 .apply_base_from_data(data, 48181)?
771 .applies_inferred_aura(AURA_HAUNT, 48181)
772 .apply_aoe_from_data(data),
773 )
774}
775fn spell_summon_darkglare(
776 s: SpellDefinitionDraft,
777 data: &ResolvedGameData,
778) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
779 Ok(
780 s
781 .apply_base_from_data(data, 205_180)?
782 .apply_aoe_from_data(data)
783 .on_cast(crate::hooks::affliction_warlock::summon_darkglare_hook),
784 )
785}
786fn spell_dark_harvest(
787 s: SpellDefinitionDraft,
788 data: &ResolvedGameData,
789) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
790 Ok(
791 s
792 .apply_base_from_data(data, 1_257_052)?
793 .apply_aoe_from_data(data)
794 .channel(
795 wowlab_engine_combat::validated_channel_tick_count(
796 4.0,
797 1_257_052,
798 "channel.tick_count",
799 )?,
800 )
801 .channel_tick_damage_sp_from_data(
802 wowlab_engine_domain::dbc::EffectLookup::new(
803 data,
804 wowlab_types::sim::EffectRef::new(
805 wowlab_types::sim::SpellIdx::from_raw(1_257_065),
806 1,
807 ),
808 ),
809 )?
810 .on_channel_tick(crate::hooks::affliction_warlock::dark_harvest_tick_hook)
811 .on_cast(crate::hooks::affliction_warlock::dark_harvest_hook),
812 )
813}
814fn spell_shadow_bolt(
815 s: SpellDefinitionDraft,
816 data: &ResolvedGameData,
817) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
818 Ok(s.apply_base_from_data(data, 686)?.apply_aoe_from_data(data))
819}
820fn spell_drain_soul(
821 s: SpellDefinitionDraft,
822 data: &ResolvedGameData,
823) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
824 Ok(s.apply_base_from_data(data, 198_590)?.apply_aoe_from_data(data))
825}
826fn spell_malefic_grasp(
827 s: SpellDefinitionDraft,
828 data: &ResolvedGameData,
829) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
830 Ok(s.apply_base_from_data(data, 1_261_153)?.apply_aoe_from_data(data))
831}
832fn spell_drain_life(
833 s: SpellDefinitionDraft,
834 data: &ResolvedGameData,
835) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
836 Ok(s.apply_base_from_data(data, 234_153)?.apply_aoe_from_data(data))
837}
838fn spell_malevolence(
839 s: SpellDefinitionDraft,
840 data: &ResolvedGameData,
841) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
842 Ok(
843 s
844 .apply_base_from_data(data, 430_014)?
845 .applies_aura(AURA_MALEVOLENCE)
846 .apply_aoe_from_data(data),
847 )
848}
849pub(crate) fn build_combat_system(
850 params: &wowlab_engine_ports::HandlerParams<'_>,
851) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
852 let builder = BuiltCombatSystem::builder(params.stats.clone())
853 .spec_id(SpecId::Affliction)
854 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
855 .mastery_spell(77215)
856 .mastery_hook(crate::hooks::affliction_warlock::mastery)
857 .resource(
858 "soul_shards",
859 5.0,
860 params
861 .game_data
862 .power_regen_for_max(7, 5.0)
863 .ok_or_else(|| {
864 wowlab_engine_ports::EngineError::spec_construction(
865 "missing power-type power_regen_for_max for type_id 7",
866 )
867 })?,
868 )
869 .resource_type_id(7)
870 .resource_start(3.0)
871 .has_pet(true)
872 .aura("agony", AURA::AGONY, |a| aura_agony(a, ¶ms.game_data))
873 .aura("corruption", AURA::CORRUPTION, |a| aura_corruption(a, ¶ms.game_data))
874 .aura(
875 "unstable_affliction",
876 AURA::UNSTABLE_AFFLICTION,
877 |a| aura_unstable_affliction(a, ¶ms.game_data),
878 )
879 .aura(
880 "seed_of_corruption",
881 AURA::SEED_OF_CORRUPTION,
882 |a| aura_seed_of_corruption(a, ¶ms.game_data),
883 )
884 .aura(
885 "seed_of_corruption_is_out_dnt",
886 AURA::SEED_OF_CORRUPTION_IS_OUT_DNT,
887 |a| aura_seed_of_corruption_is_out_dnt(a, ¶ms.game_data),
888 )
889 .aura(
890 "recently_demon_has_been_summoned_dnt",
891 AURA::RECENTLY_DEMON_HAS_BEEN_SUMMONED_DNT,
892 |a| aura_recently_demon_has_been_summoned_dnt(a, ¶ms.game_data),
893 )
894 .aura("nightfall", AURA::NIGHTFALL, |a| aura_nightfall(a, ¶ms.game_data))
895 .aura("haunt", AURA::HAUNT, |a| aura_haunt(a, ¶ms.game_data))
896 .aura(
897 "darkglare_presence",
898 AURA::DARKGLARE_PRESENCE,
899 |a| aura_darkglare_presence(a, ¶ms.game_data),
900 )
901 .aura(
902 "shard_instability",
903 AURA::SHARD_INSTABILITY,
904 |a| aura_shard_instability(a, ¶ms.game_data),
905 )
906 .aura(
907 "cascading_calamity",
908 AURA::CASCADING_CALAMITY,
909 |a| aura_cascading_calamity(a, ¶ms.game_data),
910 )
911 .aura(
912 "grimoire_of_sacrifice",
913 AURA::GRIMOIRE_OF_SACRIFICE,
914 |a| aura_grimoire_of_sacrifice(a, ¶ms.game_data),
915 )
916 .aura("wither", AURA::WITHER, |a| aura_wither(a, ¶ms.game_data))
917 .aura(
918 "malevolence",
919 AURA::MALEVOLENCE,
920 |a| aura_malevolence(a, ¶ms.game_data),
921 )
922 .aura(
923 "succulent_soul",
924 AURA::SUCCULENT_SOUL,
925 |a| aura_succulent_soul(a, ¶ms.game_data),
926 )
927 .aura(
928 "manifested_demonic_soul",
929 AURA::MANIFESTED_DEMONIC_SOUL,
930 |a| aura_manifested_demonic_soul(a, ¶ms.game_data),
931 )
932 .aura(
933 "soul_anathema",
934 AURA::SOUL_ANATHEMA,
935 |a| aura_soul_anathema(a, ¶ms.game_data),
936 )
937 .aura("track_pets", AURA::TRACK_PETS, |a| aura_track_pets(a, ¶ms.game_data))
938 .spell(
939 "summon_felhunter",
940 SPELL::SUMMON_FELHUNTER,
941 |s| spell_summon_felhunter(s, ¶ms.game_data),
942 )
943 .spell(
944 "summon_imp",
945 SPELL::SUMMON_IMP,
946 |s| spell_summon_imp(s, ¶ms.game_data),
947 )
948 .spell(
949 "grimoire_of_sacrifice",
950 SPELL::GRIMOIRE_OF_SACRIFICE,
951 |s| spell_grimoire_of_sacrifice(s, ¶ms.game_data),
952 )
953 .spell("agony", SPELL::AGONY, |s| spell_agony(s, ¶ms.game_data))
954 .spell(
955 "corruption",
956 SPELL::CORRUPTION,
957 |s| spell_corruption(s, ¶ms.game_data),
958 )
959 .spell(
960 "unstable_affliction",
961 SPELL::UNSTABLE_AFFLICTION,
962 |s| spell_unstable_affliction(s, ¶ms.game_data),
963 )
964 .spell(
965 "seed_of_corruption",
966 SPELL::SEED_OF_CORRUPTION,
967 |s| spell_seed_of_corruption(s, ¶ms.game_data),
968 )
969 .spell("haunt", SPELL::HAUNT, |s| spell_haunt(s, ¶ms.game_data))
970 .spell(
971 "summon_darkglare",
972 SPELL::SUMMON_DARKGLARE,
973 |s| spell_summon_darkglare(s, ¶ms.game_data),
974 )
975 .spell(
976 "dark_harvest",
977 SPELL::DARK_HARVEST,
978 |s| spell_dark_harvest(s, ¶ms.game_data),
979 )
980 .spell(
981 "shadow_bolt",
982 SPELL::SHADOW_BOLT,
983 |s| spell_shadow_bolt(s, ¶ms.game_data),
984 )
985 .spell(
986 "drain_soul",
987 SPELL::DRAIN_SOUL,
988 |s| spell_drain_soul(s, ¶ms.game_data),
989 )
990 .spell(
991 "malefic_grasp",
992 SPELL::MALEFIC_GRASP,
993 |s| spell_malefic_grasp(s, ¶ms.game_data),
994 )
995 .spell(
996 "drain_life",
997 SPELL::DRAIN_LIFE,
998 |s| spell_drain_life(s, ¶ms.game_data),
999 )
1000 .spell(
1001 "malevolence",
1002 SPELL::MALEVOLENCE,
1003 |s| spell_malevolence(s, ¶ms.game_data),
1004 )
1005 .auto_attack(|a| {
1006 a
1007 .spell_id(1)
1008 .swing_ms(2000)
1009 .apply_base_from_data(¶ms.game_data)
1010 .ap_coef(
1011 wowlab_engine_domain::damage::pet_swing_power_coefficient(
1012 0.575,
1013 2000,
1014 1.0,
1015 ),
1016 )
1017 .is_pet()
1018 .uses_spell_power()
1019 })
1020 .game_data(params.game_data.clone())
1021 .talent_spell_ids(TALENTS)
1022 .talent_selections(params.talent_selections)
1023 .register_impact_effect_proc(
1024 wowlab_engine_combat::ImpactEffectProcDefinition::new(
1025 wowlab_engine_combat::ImpactSource::Spell(SPELL::AGONY),
1026 )
1027 .chance(0.185)
1028 .periodic_only()
1029 .gain(1.0),
1030 )
1031 .set_bonus_auras(params.set_bonus_auras)
1032 .encounter(params.encounter.clone());
1033 let builder = crate::generated::items::register_equipped(
1034 builder,
1035 ¶ms.game_data,
1036 params.equipped_items,
1037 )?;
1038
1039 builder
1040 .build(params.rotation)
1041 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
1042}
1043fn new_handler(
1044 params: wowlab_engine_ports::HandlerParams<'_>,
1045) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
1046 let handler = crate::composition::compose_handler(
1047 build_combat_system,
1048 ¶ms,
1049 crate::hooks::affliction_warlock::AfflictionHandler::try_new,
1050 )?;
1051
1052 Ok(Box::new(handler))
1053}
1054static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
1055 primary_resource: ResourceType::SoulShards,
1056 secondary_resource: None,
1057 spell_ids: DECLARED_SPELL_IDS,
1058 aura_ids: DECLARED_AURA_IDS,
1059 server_proc_entries: &[],
1060 talents: TALENTS,
1061 rotation_field_schema: &[],
1062 metrics_plugin_id: None,
1063 hero_talent_trees: &[
1064 HeroTalentTreeDesc {
1065 name: "Hellcaller",
1066 spells: HERO_TREE_HELLCALLER_SPELLS,
1067 auras: HERO_TREE_HELLCALLER_AURAS,
1068 },
1069 HeroTalentTreeDesc {
1070 name: "Soul Harvester",
1071 spells: HERO_TREE_SOUL_HARVESTER_SPELLS,
1072 auras: HERO_TREE_SOUL_HARVESTER_AURAS,
1073 },
1074 ],
1075 reported_spells: REPORTED_SPELLS,
1076 masked_passive_effects: &[],
1077 passive_effect_overrides: &[],
1078};
1079pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1080 spec_id: SpecId::Affliction,
1081 display_name: "Affliction Warlock",
1082 metadata: DECLARED_METADATA,
1083 handler_factory: new_handler,
1084};
1085wowlab_engine_combat::smoke_test!(build_combat_system);