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 MIND_BLAST: u32 = 8092;
16 pub(crate) const SHADOW_WORD_PAIN: u32 = 589;
18 pub(crate) const VAMPIRIC_TOUCH: u32 = 34914;
20 pub(crate) const SHADOW_WORD_MADNESS: u32 = 335_467;
22 pub(crate) const VOIDFORM: u32 = 228_260;
24 pub(crate) const VOID_TORRENT: u32 = 263_165;
26 pub(crate) const MIND_FLAY: u32 = 15407;
28 pub(crate) const SHADOW_WORD_DEATH: u32 = 32379;
30 pub(crate) const HALO: u32 = 120_644;
32 pub(crate) const POWER_INFUSION: u32 = 10060;
34 pub(crate) const SHADOWFORM: u32 = 232_698;
36 pub(crate) const POWER_WORD_FORTITUDE: u32 = 21562;
38 pub(crate) const SHADOWY_APPARITION: u32 = 413_231;
40 pub(crate) const VOID_APPARITION: u32 = 1_264_176;
42 pub(crate) const VOID_BOLT: u32 = 1_264_177;
44 pub(crate) const TENTACLE_SLAM: u32 = 1_227_280;
46 pub(crate) const TENTACLE_SLAM_DAMAGE: u32 = 1_227_621;
48}
49pub(crate) mod AURA {
50 pub(crate) const SHADOWFORM: u32 = 232_698;
52 pub(crate) const VOIDFORM: u32 = 194_249;
54 pub(crate) const SHADOW_WORD_PAIN: u32 = 589;
56 pub(crate) const VAMPIRIC_TOUCH: u32 = 34914;
58 pub(crate) const SHADOW_WORD_MADNESS: u32 = 335_467;
60 pub(crate) const SHADOWY_INSIGHT: u32 = 375_981;
62 pub(crate) const MIND_DEVOURER: u32 = 373_204;
64 pub(crate) const MIND_FLAY_INSANITY: u32 = 391_401;
66 pub(crate) const SCREAMS_OF_THE_VOID: u32 = 393_919;
68 pub(crate) const DEATHSPEAKER: u32 = 392_507;
70 pub(crate) const MISERY: u32 = 238_558;
72 pub(crate) const POWER_WORD_FORTITUDE_HIGHLIGHT: u32 = 1_271_911;
74 pub(crate) const POWER_INFUSION: u32 = 10060;
76 pub(crate) const VOID_TORRENT: u32 = 289_577;
78 pub(crate) const INSIDIOUS_IRE: u32 = 373_212;
80 pub(crate) const SHATTERED_PSYCHE: u32 = 391_092;
82 pub(crate) const VOID_BLAST: u32 = 450_983;
84 pub(crate) const VOIDTOUCHED: u32 = 407_430;
86 pub(crate) const CRUSHING_VOID: u32 = 1_279_437;
88 pub(crate) const TENTACLE_SLAM: u32 = 1_227_280;
90 pub(crate) const ENTROPIC_RIFT: u32 = 447_448;
92 pub(crate) const VOIDHEART: u32 = 449_887;
94}
95pub(crate) mod TALENT {
96 pub(crate) const ANCIENT_MADNESS: u32 = 1_231_346;
98 pub(crate) const AUSPICIOUS_SPIRITS: u32 = 155_271;
100 pub(crate) const COLLAPSING_VOID: u32 = 448_403;
102 pub(crate) const CRUSHING_VOID: u32 = 1_279_437;
104 pub(crate) const DARKENING_HORIZON: u32 = 449_912;
106 pub(crate) const DEATHSPEAKER: u32 = 392_507;
108 pub(crate) const HALO: u32 = 120_644;
110 pub(crate) const IDOL_OF_CTHUN: u32 = 377_349;
112 pub(crate) const IDOL_OF_NZOTH: u32 = 373_280;
114 pub(crate) const IDOL_OF_YOGGSARON: u32 = 373_273;
116 pub(crate) const IMPROVED_VOIDFORM: u32 = 341_240;
118 pub(crate) const INSIDIOUS_IRE: u32 = 373_212;
120 pub(crate) const MADDENING_TENTACLES: u32 = 1_279_353;
122 pub(crate) const MADNESS_WEAVING: u32 = 1_240_394;
124 pub(crate) const MIND_DEVOURER: u32 = 373_202;
126 pub(crate) const MISERY: u32 = 238_558;
128 pub(crate) const POWER_INFUSION: u32 = 10060;
130 pub(crate) const PSYCHIC_LINK: u32 = 199_484;
132 pub(crate) const SCREAMS_OF_THE_VOID: u32 = 375_767;
134 pub(crate) const SHADOWY_APPARITIONS: u32 = 341_491;
136 pub(crate) const SHADOWY_INSIGHT: u32 = 375_888;
138 pub(crate) const SHADOW_WORD_MADNESS: u32 = 335_467;
140 pub(crate) const SHATTERED_PSYCHE: u32 = 391_090;
142 pub(crate) const SURGE_OF_INSANITY: u32 = 391_399;
144 pub(crate) const TENTACLE_SLAM: u32 = 1_227_280;
146 pub(crate) const TORMENTED_SPIRITS: u32 = 391_284;
148 pub(crate) const VOIDFORM: u32 = 228_260;
150 pub(crate) const VOIDHEART: u32 = 449_880;
152 pub(crate) const VOID_APPARITIONS_1: u32 = 1_264_096;
154 pub(crate) const VOID_APPARITIONS_2: u32 = 1_264_104;
156 pub(crate) const VOID_APPARITIONS_3: u32 = 1_264_107;
158 pub(crate) const VOID_BLAST: u32 = 450_405;
160 pub(crate) const VOID_TORRENT: u32 = 263_165;
162}
163pub(crate) mod HERO {
164 pub(crate) mod ARCHON {
165 pub(crate) mod SPELL {
166 pub(crate) const HALO_DAMAGE: u32 = 120_696;
168 pub(crate) const MIND_FLAY_INSANITY: u32 = 391_403;
170 pub(crate) const VOID_VOLLEY_DAMAGE: u32 = 1_242_189;
172 pub(crate) const VOID_VOLLEY_MISSILE: u32 = 1_242_173;
174 }
175 pub(crate) mod AURA {
176 pub(crate) const MIND_FLAY_INSANITY_BUFF: u32 = 391_401;
178 pub(crate) const POWER_SURGE: u32 = 453_112;
180 pub(crate) const RESONANT_ENERGY: u32 = 453_850;
182 pub(crate) const SUSTAINED_POTENCY: u32 = 454_002;
184 }
185 }
186 pub(crate) mod VOIDWEAVER {
187 pub(crate) mod SPELL {
188 pub(crate) const COLLAPSING_VOID: u32 = 448_405;
190 pub(crate) const ENTROPIC_RIFT_DAMAGE: u32 = 447_448;
192 pub(crate) const VOIDWRAITH: u32 = 451_235;
194 }
195 pub(crate) mod AURA {
196 pub(crate) const ENTROPIC_RIFT_DRIVER: u32 = 459_314;
198 pub(crate) const VOIDHEART_BUFF: u32 = 449_887;
200 pub(crate) const VOID_EMPOWERMENT: u32 = 450_138;
202 }
203 }
204}
205static HERO_TREE_ARCHON_SPELLS: &[(&str, u32)] = &[
206 ("HALO_DAMAGE", 120_696),
207 ("MIND_FLAY_INSANITY", 391_403),
208 ("VOID_VOLLEY_DAMAGE", 1_242_189),
209 ("VOID_VOLLEY_MISSILE", 1_242_173),
210];
211static HERO_TREE_ARCHON_AURAS: &[(&str, u32)] = &[
212 ("MIND_FLAY_INSANITY_BUFF", 391_401),
213 ("POWER_SURGE", 453_112),
214 ("RESONANT_ENERGY", 453_850),
215 ("SUSTAINED_POTENCY", 454_002),
216];
217static HERO_TREE_VOIDWEAVER_SPELLS: &[(&str, u32)] = &[
218 ("COLLAPSING_VOID", 448_405),
219 ("ENTROPIC_RIFT_DAMAGE", 447_448),
220 ("VOIDWRAITH", 451_235),
221];
222static HERO_TREE_VOIDWEAVER_AURAS: &[(&str, u32)] = &[
223 ("ENTROPIC_RIFT_DRIVER", 459_314),
224 ("VOIDHEART_BUFF", 449_887),
225 ("VOID_EMPOWERMENT", 450_138),
226];
227pub(crate) const AURA_SHADOWFORM: LocalAuraIdx = LocalAuraIdx::new(0);
228pub(crate) const AURA_VOIDFORM: LocalAuraIdx = LocalAuraIdx::new(1);
229pub(crate) const AURA_SHADOW_WORD_PAIN: LocalAuraIdx = LocalAuraIdx::new(2);
230pub(crate) const AURA_VAMPIRIC_TOUCH: LocalAuraIdx = LocalAuraIdx::new(3);
231pub(crate) const AURA_SHADOW_WORD_MADNESS: LocalAuraIdx = LocalAuraIdx::new(4);
232pub(crate) const AURA_SHADOWY_INSIGHT: LocalAuraIdx = LocalAuraIdx::new(5);
233pub(crate) const AURA_MIND_DEVOURER: LocalAuraIdx = LocalAuraIdx::new(6);
234pub(crate) const AURA_MIND_FLAY_INSANITY: LocalAuraIdx = LocalAuraIdx::new(7);
235pub(crate) const AURA_SCREAMS_OF_THE_VOID: LocalAuraIdx = LocalAuraIdx::new(8);
236pub(crate) const AURA_DEATHSPEAKER: LocalAuraIdx = LocalAuraIdx::new(9);
237pub(crate) const AURA_MISERY: LocalAuraIdx = LocalAuraIdx::new(10);
238pub(crate) const AURA_POWER_WORD_FORTITUDE_HIGHLIGHT: LocalAuraIdx = LocalAuraIdx::new(
239 11,
240);
241pub(crate) const AURA_POWER_INFUSION: LocalAuraIdx = LocalAuraIdx::new(12);
242pub(crate) const AURA_VOID_TORRENT: LocalAuraIdx = LocalAuraIdx::new(13);
243pub(crate) const AURA_INSIDIOUS_IRE: LocalAuraIdx = LocalAuraIdx::new(14);
244pub(crate) const AURA_SHATTERED_PSYCHE: LocalAuraIdx = LocalAuraIdx::new(15);
245pub(crate) const AURA_VOID_BLAST: LocalAuraIdx = LocalAuraIdx::new(16);
246pub(crate) const AURA_VOIDTOUCHED: LocalAuraIdx = LocalAuraIdx::new(17);
247pub(crate) const AURA_CRUSHING_VOID: LocalAuraIdx = LocalAuraIdx::new(18);
248pub(crate) const AURA_TENTACLE_SLAM: LocalAuraIdx = LocalAuraIdx::new(19);
249pub(crate) const AURA_ENTROPIC_RIFT: LocalAuraIdx = LocalAuraIdx::new(20);
250pub(crate) const AURA_VOIDHEART: LocalAuraIdx = LocalAuraIdx::new(21);
251pub(crate) const AURA_COUNT: usize = 22;
252const _: () = assert!(
253 AURA_COUNT == 22, "AURA index ABI: count changed -- update positional hook consumers"
254);
255pub(crate) const SPELL_MIND_BLAST: LocalSpellIdx = LocalSpellIdx::new(0);
256pub(crate) const SPELL_SHADOW_WORD_PAIN: LocalSpellIdx = LocalSpellIdx::new(1);
257pub(crate) const SPELL_VAMPIRIC_TOUCH: LocalSpellIdx = LocalSpellIdx::new(2);
258pub(crate) const SPELL_SHADOW_WORD_MADNESS: LocalSpellIdx = LocalSpellIdx::new(3);
259pub(crate) const SPELL_VOIDFORM: LocalSpellIdx = LocalSpellIdx::new(4);
260pub(crate) const SPELL_VOID_TORRENT: LocalSpellIdx = LocalSpellIdx::new(5);
261pub(crate) const SPELL_MIND_FLAY: LocalSpellIdx = LocalSpellIdx::new(6);
262pub(crate) const SPELL_SHADOW_WORD_DEATH: LocalSpellIdx = LocalSpellIdx::new(7);
263pub(crate) const SPELL_HALO: LocalSpellIdx = LocalSpellIdx::new(8);
264pub(crate) const SPELL_POWER_INFUSION: LocalSpellIdx = LocalSpellIdx::new(9);
265pub(crate) const SPELL_SHADOWFORM: LocalSpellIdx = LocalSpellIdx::new(10);
266pub(crate) const SPELL_POWER_WORD_FORTITUDE: LocalSpellIdx = LocalSpellIdx::new(11);
267pub(crate) const SPELL_SHADOWY_APPARITION: LocalSpellIdx = LocalSpellIdx::new(12);
268pub(crate) const SPELL_VOID_APPARITION: LocalSpellIdx = LocalSpellIdx::new(13);
269pub(crate) const SPELL_VOID_BOLT: LocalSpellIdx = LocalSpellIdx::new(14);
270pub(crate) const SPELL_TENTACLE_SLAM: LocalSpellIdx = LocalSpellIdx::new(15);
271pub(crate) const SPELL_TENTACLE_SLAM_DAMAGE: LocalSpellIdx = LocalSpellIdx::new(16);
272pub(crate) const SPELL_COUNT: usize = 17;
273const _: () = assert!(
274 SPELL_COUNT == 17,
275 "SPELL index ABI: count changed -- update positional hook consumers"
276);
277pub(crate) mod EFFECT {
278 pub(crate) const MIND_BLAST_DAMAGE: (u32, u8) = (8092, 1);
280 pub(crate) const VOID_BLAST_DAMAGE: (u32, u8) = (450_983, 1);
282 pub(crate) const COLLAPSING_VOID_DAMAGE: (u32, u8) = (448_405, 1);
284 pub(crate) const COLLAPSING_VOID_PCT: (u32, u8) = (448_403, 3);
286 pub(crate) const DARKENING_HORIZON_MAX: (u32, u8) = (449_912, 2);
288 pub(crate) const DARKENING_HORIZON_EXT: (u32, u8) = (449_912, 3);
290 pub(crate) const TENTACLE_SLAM_DAMAGE: (u32, u8) = (1_227_621, 1);
292 pub(crate) const SHADOWY_APPARITION_DAMAGE: (u32, u8) = (413_231, 1);
294 pub(crate) const VOID_APPARITION_DAMAGE: (u32, u8) = (1_264_176, 1);
296 pub(crate) const VOID_BOLT_DAMAGE: (u32, u8) = (1_264_177, 1);
298 pub(crate) const VOID_APPARITIONS_CHANCE: (u32, u8) = (1_264_104, 2);
300 pub(crate) const AUSPICIOUS_SPIRITS_INSANITY: (u32, u8) = (155_271, 2);
302 pub(crate) const INSIDIOUS_IRE_BONUS: (u32, u8) = (373_212, 1);
304 pub(crate) const MIND_FLAY_TICK_INSANITY: (u32, u8) = (15407, 3);
306 pub(crate) const SHADOW_WORD_DEATH_DAMAGE: (u32, u8) = (32379, 2);
308 pub(crate) const SHADOW_WORD_DEATH_EXECUTE: (u32, u8) = (32379, 4);
310 pub(crate) const SHADOW_WORD_DEATH_THRESHOLD: (u32, u8) = (32379, 3);
312 pub(crate) const SURGE_OF_INSANITY_GEN: (u32, u8) = (391_399, 2);
314}
315static REPORTED_SPELLS: &[(&str, u32)] = &[];
316static DECLARED_SPELL_IDS: &[u32] = &[
317 8092, 589, 34914, 335_467, 228_260, 263_165, 15407, 32379, 120_644, 10060, 232_698,
318 21562, 413_231, 1_264_176, 1_264_177, 1_227_280, 1_227_621, 232_698, 194_249, 589,
319 34914, 335_467, 375_981, 373_204, 391_401, 393_919, 392_507, 238_558, 1_271_911,
320 10060, 289_577, 373_212, 391_092, 450_983, 407_430, 1_279_437, 1_227_280, 447_448,
321 449_887, 589, 8092, 15407, 32379, 34914, 155_271, 263_165, 335_467, 341_240, 343_690,
322 373_212, 390_964, 391_399, 413_231, 447_448, 448_403, 448_405, 449_912, 450_983,
323 459_314, 1_227_621, 1_264_104, 1_264_176, 1_264_177, 120_696, 391_403, 1_242_189,
324 1_242_173, 391_401, 453_112, 453_850, 454_002, 448_405, 447_448, 451_235, 459_314,
325 449_887, 450_138,
326];
327static DECLARED_AURA_IDS: &[u32] = &[
328 232_698, 194_249, 589, 34914, 335_467, 375_981, 373_204, 391_401, 393_919, 392_507,
329 238_558, 1_271_911, 10060, 289_577, 373_212, 391_092, 450_983, 407_430, 1_279_437,
330 1_227_280, 447_448, 449_887, 391_401, 453_112, 453_850, 454_002, 459_314, 449_887,
331 450_138,
332];
333static TALENTS: &[(&str, u32)] = &[
334 ("ANCIENT_MADNESS", TALENT::ANCIENT_MADNESS),
335 ("AUSPICIOUS_SPIRITS", TALENT::AUSPICIOUS_SPIRITS),
336 ("COLLAPSING_VOID", TALENT::COLLAPSING_VOID),
337 ("CRUSHING_VOID", TALENT::CRUSHING_VOID),
338 ("DARKENING_HORIZON", TALENT::DARKENING_HORIZON),
339 ("DEATHSPEAKER", TALENT::DEATHSPEAKER),
340 ("HALO", TALENT::HALO),
341 ("IDOL_OF_CTHUN", TALENT::IDOL_OF_CTHUN),
342 ("IDOL_OF_NZOTH", TALENT::IDOL_OF_NZOTH),
343 ("IDOL_OF_YOGGSARON", TALENT::IDOL_OF_YOGGSARON),
344 ("IMPROVED_VOIDFORM", TALENT::IMPROVED_VOIDFORM),
345 ("INSIDIOUS_IRE", TALENT::INSIDIOUS_IRE),
346 ("MADDENING_TENTACLES", TALENT::MADDENING_TENTACLES),
347 ("MADNESS_WEAVING", TALENT::MADNESS_WEAVING),
348 ("MIND_DEVOURER", TALENT::MIND_DEVOURER),
349 ("MISERY", TALENT::MISERY),
350 ("POWER_INFUSION", TALENT::POWER_INFUSION),
351 ("PSYCHIC_LINK", TALENT::PSYCHIC_LINK),
352 ("SCREAMS_OF_THE_VOID", TALENT::SCREAMS_OF_THE_VOID),
353 ("SHADOWY_APPARITIONS", TALENT::SHADOWY_APPARITIONS),
354 ("SHADOWY_INSIGHT", TALENT::SHADOWY_INSIGHT),
355 ("SHADOW_WORD_MADNESS", TALENT::SHADOW_WORD_MADNESS),
356 ("SHATTERED_PSYCHE", TALENT::SHATTERED_PSYCHE),
357 ("SURGE_OF_INSANITY", TALENT::SURGE_OF_INSANITY),
358 ("TENTACLE_SLAM", TALENT::TENTACLE_SLAM),
359 ("TORMENTED_SPIRITS", TALENT::TORMENTED_SPIRITS),
360 ("VOIDFORM", TALENT::VOIDFORM),
361 ("VOIDHEART", TALENT::VOIDHEART),
362 ("VOID_APPARITIONS_1", TALENT::VOID_APPARITIONS_1),
363 ("VOID_APPARITIONS_2", TALENT::VOID_APPARITIONS_2),
364 ("VOID_APPARITIONS_3", TALENT::VOID_APPARITIONS_3),
365 ("VOID_BLAST", TALENT::VOID_BLAST),
366 ("VOID_TORRENT", TALENT::VOID_TORRENT),
367];
368pub(crate) mod SHADOW_PRIEST {
369 pub(crate) const MIND_BLAST_CASTS: u32 = 0;
370 pub(crate) const SHADOW_WORD_MADNESS_CASTS: u32 = 1;
371 pub(crate) const VOIDFORM_CASTS: u32 = 2;
372 pub(crate) const VOID_TORRENT_CASTS: u32 = 3;
373 pub(crate) const SHADOW_WORD_DEATH_CASTS: u32 = 4;
374 pub(crate) const HALO_CASTS: u32 = 5;
375 pub(crate) const TENTACLE_SLAM_CASTS: u32 = 6;
376 pub(crate) const MIND_FLAY_CASTS: u32 = 7;
377 pub(crate) const VOIDFORM_UPTIME_MS: u32 = 8;
378 pub(crate) const SHADOW_WORD_PAIN_UPTIME_MS: u32 = 9;
379 pub(crate) const VAMPIRIC_TOUCH_UPTIME_MS: u32 = 10;
380}
381fn aura_shadowform(
382 a: AuraDefinitionDraft,
383 data: &ResolvedGameData,
384) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
385 Ok(
386 a
387 .on_player()
388 .apply_base_from_data(data, 232_698)?
389 .max_stacks(
390 data
391 .require_aura_max_stacks(
392 wowlab_types::sim::SpellIdx::from_raw(232_698),
393 )?,
394 ),
395 )
396}
397fn aura_voidform(
398 a: AuraDefinitionDraft,
399 data: &ResolvedGameData,
400) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
401 Ok(
402 a
403 .on_player()
404 .apply_base_from_data(data, 194_249)?
405 .max_stacks(
406 data
407 .require_aura_max_stacks(
408 wowlab_types::sim::SpellIdx::from_raw(194_249),
409 )?,
410 ),
411 )
412}
413fn aura_shadow_word_pain(
414 a: AuraDefinitionDraft,
415 data: &ResolvedGameData,
416) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
417 a.on_target()
418 .apply_base_from_data(data, 589)?
419 .max_stacks(
420 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(589))?,
421 )
422 .periodic_damage_sp(
423 wowlab_engine_combat::validated_milliseconds(
424 data.period(wowlab_types::sim::SpellIdx::from_raw(589), 2),
425 589,
426 "periodic_damage.tick_ms",
427 )?,
428 ((data.sp_coef(wowlab_types::sim::SpellIdx::from_raw(589), 2)) * 10000.0)
429 .round() / 10000.0,
430 wowlab_engine_combat::DamageFlags::empty(),
431 )
432 .apply_periodic_behavior_from_data(data, 589)
433}
434fn aura_vampiric_touch(
435 a: AuraDefinitionDraft,
436 data: &ResolvedGameData,
437) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
438 a.on_target()
439 .apply_base_from_data(data, 34914)?
440 .max_stacks(
441 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(34914))?,
442 )
443 .periodic_damage_sp(
444 wowlab_engine_combat::validated_milliseconds(
445 data.period(wowlab_types::sim::SpellIdx::from_raw(34914), 2),
446 34914,
447 "periodic_damage.tick_ms",
448 )?,
449 ((data.sp_coef(wowlab_types::sim::SpellIdx::from_raw(34914), 2)) * 10000.0)
450 .round() / 10000.0,
451 wowlab_engine_combat::DamageFlags::empty(),
452 )
453 .apply_periodic_behavior_from_data(data, 34914)
454}
455fn aura_shadow_word_madness(
456 a: AuraDefinitionDraft,
457 data: &ResolvedGameData,
458) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
459 a.on_target()
460 .apply_base_from_data(data, 335_467)?
461 .max_stacks(
462 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(335_467))?,
463 )
464 .periodic_damage_sp(
465 wowlab_engine_combat::validated_milliseconds(
466 data.period(wowlab_types::sim::SpellIdx::from_raw(335_467), 3),
467 335_467,
468 "periodic_damage.tick_ms",
469 )?,
470 ((data.sp_coef(wowlab_types::sim::SpellIdx::from_raw(335_467), 3)) * 1000.0)
471 .round() / 1000.0,
472 wowlab_engine_combat::DamageFlags::empty(),
473 )
474 .apply_periodic_behavior_from_data(data, 335_467)
475}
476fn aura_shadowy_insight(
477 a: AuraDefinitionDraft,
478 data: &ResolvedGameData,
479) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
480 Ok(
481 a
482 .on_player()
483 .apply_base_from_data(data, 375_981)?
484 .max_stacks(
485 data
486 .require_aura_max_stacks(
487 wowlab_types::sim::SpellIdx::from_raw(375_981),
488 )?,
489 ),
490 )
491}
492fn aura_mind_devourer(
493 a: AuraDefinitionDraft,
494 data: &ResolvedGameData,
495) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
496 Ok(
497 a
498 .on_player()
499 .apply_base_from_data(data, 373_204)?
500 .max_stacks(
501 data
502 .require_aura_max_stacks(
503 wowlab_types::sim::SpellIdx::from_raw(373_204),
504 )?,
505 ),
506 )
507}
508fn aura_mind_flay_insanity(
509 a: AuraDefinitionDraft,
510 data: &ResolvedGameData,
511) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
512 Ok(
513 a
514 .on_player()
515 .apply_base_from_data(data, 391_401)?
516 .max_stacks(
517 data
518 .require_aura_max_stacks(
519 wowlab_types::sim::SpellIdx::from_raw(391_401),
520 )?,
521 ),
522 )
523}
524fn aura_screams_of_the_void(
525 a: AuraDefinitionDraft,
526 data: &ResolvedGameData,
527) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
528 Ok(
529 a
530 .on_player()
531 .apply_base_from_data(data, 393_919)?
532 .max_stacks(
533 data
534 .require_aura_max_stacks(
535 wowlab_types::sim::SpellIdx::from_raw(393_919),
536 )?,
537 ),
538 )
539}
540fn aura_deathspeaker(
541 a: AuraDefinitionDraft,
542 data: &ResolvedGameData,
543) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
544 Ok(
545 a
546 .on_player()
547 .apply_base_from_data(data, 392_507)?
548 .max_stacks(
549 data
550 .require_aura_max_stacks(
551 wowlab_types::sim::SpellIdx::from_raw(392_507),
552 )?,
553 ),
554 )
555}
556fn aura_misery(
557 a: AuraDefinitionDraft,
558 data: &ResolvedGameData,
559) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
560 Ok(
561 a
562 .on_player()
563 .apply_base_from_data(data, 238_558)?
564 .max_stacks(
565 data
566 .require_aura_max_stacks(
567 wowlab_types::sim::SpellIdx::from_raw(238_558),
568 )?,
569 ),
570 )
571}
572fn aura_power_word_fortitude_highlight(
573 a: AuraDefinitionDraft,
574 data: &ResolvedGameData,
575) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
576 Ok(
577 a
578 .on_player()
579 .apply_base_from_data(data, 1_271_911)?
580 .max_stacks(
581 data
582 .require_aura_max_stacks(
583 wowlab_types::sim::SpellIdx::from_raw(1_271_911),
584 )?,
585 ),
586 )
587}
588fn aura_power_infusion(
589 a: AuraDefinitionDraft,
590 data: &ResolvedGameData,
591) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
592 Ok(
593 a
594 .on_player()
595 .apply_base_from_data(data, 10060)?
596 .max_stacks(
597 data
598 .require_aura_max_stacks(
599 wowlab_types::sim::SpellIdx::from_raw(10060),
600 )?,
601 ),
602 )
603}
604fn aura_void_torrent(
605 a: AuraDefinitionDraft,
606 data: &ResolvedGameData,
607) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
608 Ok(
609 a
610 .on_player()
611 .apply_base_from_data(data, 289_577)?
612 .max_stacks(
613 data
614 .require_aura_max_stacks(
615 wowlab_types::sim::SpellIdx::from_raw(289_577),
616 )?,
617 ),
618 )
619}
620fn aura_insidious_ire(
621 a: AuraDefinitionDraft,
622 data: &ResolvedGameData,
623) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
624 Ok(
625 a
626 .on_player()
627 .apply_base_from_data(data, 373_212)?
628 .max_stacks(
629 data
630 .require_aura_max_stacks(
631 wowlab_types::sim::SpellIdx::from_raw(373_212),
632 )?,
633 ),
634 )
635}
636fn aura_shattered_psyche(
637 a: AuraDefinitionDraft,
638 data: &ResolvedGameData,
639) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
640 Ok(
641 a
642 .on_player()
643 .apply_base_from_data(data, 391_092)?
644 .max_stacks(
645 data
646 .require_aura_max_stacks(
647 wowlab_types::sim::SpellIdx::from_raw(391_092),
648 )?,
649 ),
650 )
651}
652fn aura_void_blast(
653 a: AuraDefinitionDraft,
654 data: &ResolvedGameData,
655) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
656 a.on_player()
657 .duration_ms(
658 wowlab_engine_combat::validated_milliseconds(
659 f64::from(
660 data
661 .require_aura_duration_ms(
662 wowlab_types::sim::SpellIdx::from_raw(459_314),
663 )?,
664 ),
665 450_983,
666 "aura.duration_ms",
667 )?,
668 )
669 .apply_periodic_effect_from_data(data, 450_983)?
670 .apply_duration_behavior_from_data(data, 459_314)?
671 .apply_timing_modifiers_from_data(data, 450_983)?
672 .max_stacks(
673 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(450_983))?,
674 )
675 .apply_periodic_behavior_from_data(data, 459_314)
676}
677fn aura_voidtouched(
678 a: AuraDefinitionDraft,
679 data: &ResolvedGameData,
680) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
681 Ok(
682 a
683 .on_player()
684 .apply_base_from_data(data, 407_430)?
685 .max_stacks(
686 data
687 .require_aura_max_stacks(
688 wowlab_types::sim::SpellIdx::from_raw(407_430),
689 )?,
690 ),
691 )
692}
693fn aura_crushing_void(
694 a: AuraDefinitionDraft,
695 data: &ResolvedGameData,
696) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
697 Ok(
698 a
699 .on_player()
700 .apply_base_from_data(data, 1_279_437)?
701 .max_stacks(
702 data
703 .require_aura_max_stacks(
704 wowlab_types::sim::SpellIdx::from_raw(1_279_437),
705 )?,
706 ),
707 )
708}
709fn aura_tentacle_slam(
710 a: AuraDefinitionDraft,
711 data: &ResolvedGameData,
712) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
713 Ok(
714 a
715 .on_player()
716 .apply_base_from_data(data, 1_227_280)?
717 .max_stacks(
718 data
719 .require_aura_max_stacks(
720 wowlab_types::sim::SpellIdx::from_raw(1_227_280),
721 )?,
722 ),
723 )
724}
725fn aura_entropic_rift(
726 a: AuraDefinitionDraft,
727 data: &ResolvedGameData,
728) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
729 Ok(
730 a
731 .on_player()
732 .duration_ms(
733 wowlab_engine_combat::validated_milliseconds(
734 f64::from(
735 data
736 .require_aura_duration_ms(
737 wowlab_types::sim::SpellIdx::from_raw(459_314),
738 )?,
739 ),
740 447_448,
741 "aura.duration_ms",
742 )?,
743 )
744 .apply_periodic_effect_from_data(data, 447_448)?
745 .apply_duration_behavior_from_data(data, 459_314)?
746 .apply_timing_modifiers_from_data(data, 447_448)?
747 .max_stacks(
748 data
749 .require_aura_max_stacks(
750 wowlab_types::sim::SpellIdx::from_raw(447_448),
751 )?,
752 )
753 .no_pandemic()
754 .periodic_damage_sp_from_data(
755 data,
756 wowlab_engine_combat::PeriodicDamageDataInput {
757 tick_ms: wowlab_engine_combat::validated_milliseconds(
758 data.period(wowlab_types::sim::SpellIdx::from_raw(459_314), 1),
759 447_448,
760 "periodic_damage.tick_ms",
761 )?,
762 spell_id: 447_448,
763 effect_index: 1,
764 multiplier: 1.0,
765 offset: 0.0,
766 round_decimals: Some(1),
767 },
768 )?
769 .apply_periodic_behavior_from_data(data, 459_314)?
770 .on_expire(crate::hooks::shadow_priest::entropic_rift_expire_hook),
771 )
772}
773fn aura_voidheart(
774 a: AuraDefinitionDraft,
775 data: &ResolvedGameData,
776) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
777 Ok(
778 a
779 .on_player()
780 .apply_base_from_data(data, 449_887)?
781 .max_stacks(
782 data
783 .require_aura_max_stacks(
784 wowlab_types::sim::SpellIdx::from_raw(449_887),
785 )?,
786 )
787 .no_pandemic(),
788 )
789}
790fn spell_mind_blast(
791 s: SpellDefinitionDraft,
792 data: &ResolvedGameData,
793) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
794 Ok(
795 s
796 .apply_base_from_data(data, 8092)?
797 .hasted_cooldown()
798 .damage_sp(0.0, wowlab_engine_combat::DamageFlags::empty())
799 .apply_aoe_from_data(data)
800 .on_cast(crate::hooks::shadow_priest::mind_blast_hook),
801 )
802}
803fn spell_shadow_word_pain(
804 s: SpellDefinitionDraft,
805 data: &ResolvedGameData,
806) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
807 Ok(
808 s
809 .apply_base_from_data(data, 589)?
810 .applies_inferred_aura(AURA_SHADOW_WORD_PAIN, 589)
811 .apply_aoe_from_data(data)
812 .on_cast(crate::hooks::shadow_priest::shadow_word_pain_hook),
813 )
814}
815fn spell_vampiric_touch(
816 s: SpellDefinitionDraft,
817 data: &ResolvedGameData,
818) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
819 Ok(
820 s
821 .apply_base_from_data(data, 34914)?
822 .damage_sp(0.0, wowlab_engine_combat::DamageFlags::empty())
823 .applies_inferred_aura(AURA_VAMPIRIC_TOUCH, 34914)
824 .apply_aoe_from_data(data)
825 .on_cast(crate::hooks::shadow_priest::vampiric_touch_hook),
826 )
827}
828fn spell_shadow_word_madness(
829 s: SpellDefinitionDraft,
830 data: &ResolvedGameData,
831) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
832 Ok(
833 s
834 .apply_base_from_data(data, 335_467)?
835 .cost(
836 wowlab_engine_combat::validated_nonnegative(40.0, 335_467, "spell.cost")?,
837 )
838 .applies_inferred_aura(AURA_SHADOW_WORD_MADNESS, 335_467)
839 .apply_aoe_from_data(data)
840 .on_cast(crate::hooks::shadow_priest::shadow_word_madness_hook),
841 )
842}
843fn spell_voidform(
844 s: SpellDefinitionDraft,
845 data: &ResolvedGameData,
846) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
847 Ok(
848 s
849 .apply_base_from_data(data, 228_260)?
850 .gain(data.base_points(wowlab_types::sim::SpellIdx::from_raw(341_240), 1))
851 .applies_aura(AURA_VOIDFORM)
852 .apply_aoe_from_data(data),
853 )
854}
855fn spell_void_torrent(
856 s: SpellDefinitionDraft,
857 data: &ResolvedGameData,
858) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
859 Ok(
860 s
861 .apply_base_from_data(data, 263_165)?
862 .damage_sp(0.0, wowlab_engine_combat::DamageFlags::empty())
863 .applies_inferred_aura(AURA_VOID_TORRENT, 289_577)
864 .apply_aoe_from_data(data)
865 .channel(
866 wowlab_engine_combat::validated_channel_tick_count(
867 4.0,
868 263_165,
869 "channel.tick_count",
870 )?,
871 )
872 .channel_tick_damage_sp_from_data(
873 wowlab_engine_domain::dbc::EffectLookup::new(
874 data,
875 wowlab_types::sim::EffectRef::new(
876 wowlab_types::sim::SpellIdx::from_raw(263_165),
877 1,
878 ),
879 ),
880 )?
881 .channel_complete_extend_aura(
882 AURA_ENTROPIC_RIFT,
883 wowlab_engine_combat::validated_milliseconds(
884 f64::from(
885 data
886 .require_aura_duration_ms(
887 wowlab_types::sim::SpellIdx::from_raw(263_165),
888 )?,
889 ),
890 263_165,
891 "channel.complete_effects.extend_aura.amount_ms",
892 )?,
893 )
894 .channel_complete_extend_aura(
895 AURA_VOIDHEART,
896 wowlab_engine_combat::validated_milliseconds(
897 f64::from(
898 data
899 .require_aura_duration_ms(
900 wowlab_types::sim::SpellIdx::from_raw(263_165),
901 )?,
902 ),
903 263_165,
904 "channel.complete_effects.extend_aura.amount_ms",
905 )?,
906 )
907 .on_cast(crate::hooks::shadow_priest::void_torrent_hook),
908 )
909}
910fn spell_mind_flay(
911 s: SpellDefinitionDraft,
912 data: &ResolvedGameData,
913) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
914 Ok(
915 s
916 .apply_base_from_data(data, 15407)?
917 .damage_sp(0.0, wowlab_engine_combat::DamageFlags::empty())
918 .apply_aoe_from_data(data)
919 .channel(
920 wowlab_engine_combat::validated_channel_tick_count(
921 6.0,
922 15407,
923 "channel.tick_count",
924 )?,
925 )
926 .channel_tick_damage_sp_from_data(
927 wowlab_engine_domain::dbc::EffectLookup::new(
928 data,
929 wowlab_types::sim::EffectRef::new(
930 wowlab_types::sim::SpellIdx::from_raw(15407),
931 1,
932 ),
933 ),
934 )?
935 .on_channel_tick(crate::hooks::shadow_priest::mind_flay_tick_hook),
936 )
937}
938fn spell_shadow_word_death(
939 s: SpellDefinitionDraft,
940 data: &ResolvedGameData,
941) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
942 Ok(
943 s
944 .apply_base_from_data(data, 32379)?
945 .gain(4.0)
946 .damage_sp(0.0, wowlab_engine_combat::DamageFlags::empty())
947 .apply_aoe_from_data(data)
948 .on_cast(crate::hooks::shadow_priest::shadow_word_death_hook),
949 )
950}
951fn spell_halo(
952 s: SpellDefinitionDraft,
953 data: &ResolvedGameData,
954) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
955 Ok(
956 s
957 .apply_base_from_data(data, 120_644)?
958 .damage_sp_from_data(
959 wowlab_engine_domain::dbc::EffectLookup::new(
960 data,
961 wowlab_types::sim::EffectRef::new(
962 wowlab_types::sim::SpellIdx::from_raw(390_964),
963 1,
964 ),
965 ),
966 )?
967 .apply_aoe_from_data(data),
968 )
969}
970fn spell_power_infusion(
971 s: SpellDefinitionDraft,
972 data: &ResolvedGameData,
973) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
974 Ok(
975 s
976 .apply_base_from_data(data, 10060)?
977 .applies_inferred_aura(AURA_POWER_INFUSION, 10060)
978 .apply_aoe_from_data(data),
979 )
980}
981fn spell_shadowform(
982 s: SpellDefinitionDraft,
983 data: &ResolvedGameData,
984) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
985 Ok(
986 s
987 .apply_base_from_data(data, 232_698)?
988 .applies_inferred_aura(AURA_SHADOWFORM, 232_698)
989 .apply_aoe_from_data(data),
990 )
991}
992fn spell_power_word_fortitude(
993 s: SpellDefinitionDraft,
994 data: &ResolvedGameData,
995) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
996 Ok(s.apply_base_from_data(data, 21562)?.apply_aoe_from_data(data))
997}
998fn spell_shadowy_apparition(
999 s: SpellDefinitionDraft,
1000 data: &ResolvedGameData,
1001) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1002 Ok(s.apply_base_from_data(data, 413_231)?.apply_aoe_from_data(data))
1003}
1004fn spell_void_apparition(
1005 s: SpellDefinitionDraft,
1006 data: &ResolvedGameData,
1007) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1008 Ok(s.apply_base_from_data(data, 1_264_176)?.apply_aoe_from_data(data))
1009}
1010fn spell_void_bolt(
1011 s: SpellDefinitionDraft,
1012 data: &ResolvedGameData,
1013) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1014 Ok(s.apply_base_from_data(data, 1_264_177)?.apply_aoe_from_data(data))
1015}
1016fn spell_tentacle_slam(
1017 s: SpellDefinitionDraft,
1018 data: &ResolvedGameData,
1019) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1020 Ok(
1021 s
1022 .apply_base_from_data(data, 1_227_280)?
1023 .applies_aura(AURA_VAMPIRIC_TOUCH)
1024 .apply_aoe_from_data(data)
1025 .on_cast(crate::hooks::shadow_priest::tentacle_slam_hook),
1026 )
1027}
1028fn spell_tentacle_slam_damage(
1029 s: SpellDefinitionDraft,
1030 data: &ResolvedGameData,
1031) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1032 Ok(s.apply_base_from_data(data, 1_227_621)?.apply_aoe_from_data(data))
1033}
1034pub(crate) fn build_combat_system(
1035 params: &wowlab_engine_ports::HandlerParams<'_>,
1036) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
1037 let builder = BuiltCombatSystem::builder(params.stats.clone())
1038 .spec_id(SpecId::Shadow)
1039 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
1040 .mastery_spell(343_690)
1041 .mastery_hook(crate::hooks::shadow_priest::mastery)
1042 .resource(
1043 "insanity",
1044 100.0,
1045 params
1046 .game_data
1047 .power_regen_for_max(13, 100.0)
1048 .ok_or_else(|| {
1049 wowlab_engine_ports::EngineError::spec_construction(
1050 "missing power-type power_regen_for_max for type_id 13",
1051 )
1052 })?,
1053 )
1054 .resource_type_id(13)
1055 .resource_start(0.0)
1056 .aura("shadowform", AURA::SHADOWFORM, |a| aura_shadowform(a, ¶ms.game_data))
1057 .aura("voidform", AURA::VOIDFORM, |a| aura_voidform(a, ¶ms.game_data))
1058 .aura(
1059 "shadow_word_pain",
1060 AURA::SHADOW_WORD_PAIN,
1061 |a| aura_shadow_word_pain(a, ¶ms.game_data),
1062 )
1063 .aura(
1064 "vampiric_touch",
1065 AURA::VAMPIRIC_TOUCH,
1066 |a| aura_vampiric_touch(a, ¶ms.game_data),
1067 )
1068 .aura(
1069 "shadow_word_madness",
1070 AURA::SHADOW_WORD_MADNESS,
1071 |a| aura_shadow_word_madness(a, ¶ms.game_data),
1072 )
1073 .aura(
1074 "shadowy_insight",
1075 AURA::SHADOWY_INSIGHT,
1076 |a| aura_shadowy_insight(a, ¶ms.game_data),
1077 )
1078 .aura(
1079 "mind_devourer",
1080 AURA::MIND_DEVOURER,
1081 |a| aura_mind_devourer(a, ¶ms.game_data),
1082 )
1083 .aura(
1084 "mind_flay_insanity",
1085 AURA::MIND_FLAY_INSANITY,
1086 |a| aura_mind_flay_insanity(a, ¶ms.game_data),
1087 )
1088 .aura(
1089 "screams_of_the_void",
1090 AURA::SCREAMS_OF_THE_VOID,
1091 |a| aura_screams_of_the_void(a, ¶ms.game_data),
1092 )
1093 .aura(
1094 "deathspeaker",
1095 AURA::DEATHSPEAKER,
1096 |a| aura_deathspeaker(a, ¶ms.game_data),
1097 )
1098 .aura("misery", AURA::MISERY, |a| aura_misery(a, ¶ms.game_data))
1099 .aura(
1100 "power_word_fortitude_highlight",
1101 AURA::POWER_WORD_FORTITUDE_HIGHLIGHT,
1102 |a| aura_power_word_fortitude_highlight(a, ¶ms.game_data),
1103 )
1104 .aura(
1105 "power_infusion",
1106 AURA::POWER_INFUSION,
1107 |a| aura_power_infusion(a, ¶ms.game_data),
1108 )
1109 .aura(
1110 "void_torrent",
1111 AURA::VOID_TORRENT,
1112 |a| aura_void_torrent(a, ¶ms.game_data),
1113 )
1114 .aura(
1115 "insidious_ire",
1116 AURA::INSIDIOUS_IRE,
1117 |a| aura_insidious_ire(a, ¶ms.game_data),
1118 )
1119 .aura(
1120 "shattered_psyche",
1121 AURA::SHATTERED_PSYCHE,
1122 |a| aura_shattered_psyche(a, ¶ms.game_data),
1123 )
1124 .aura("void_blast", AURA::VOID_BLAST, |a| aura_void_blast(a, ¶ms.game_data))
1125 .aura(
1126 "voidtouched",
1127 AURA::VOIDTOUCHED,
1128 |a| aura_voidtouched(a, ¶ms.game_data),
1129 )
1130 .aura(
1131 "crushing_void",
1132 AURA::CRUSHING_VOID,
1133 |a| aura_crushing_void(a, ¶ms.game_data),
1134 )
1135 .aura(
1136 "tentacle_slam",
1137 AURA::TENTACLE_SLAM,
1138 |a| aura_tentacle_slam(a, ¶ms.game_data),
1139 )
1140 .aura(
1141 "entropic_rift",
1142 AURA::ENTROPIC_RIFT,
1143 |a| aura_entropic_rift(a, ¶ms.game_data),
1144 )
1145 .aura("voidheart", AURA::VOIDHEART, |a| aura_voidheart(a, ¶ms.game_data))
1146 .spell(
1147 "mind_blast",
1148 SPELL::MIND_BLAST,
1149 |s| spell_mind_blast(s, ¶ms.game_data),
1150 )
1151 .spell(
1152 "shadow_word_pain",
1153 SPELL::SHADOW_WORD_PAIN,
1154 |s| spell_shadow_word_pain(s, ¶ms.game_data),
1155 )
1156 .spell(
1157 "vampiric_touch",
1158 SPELL::VAMPIRIC_TOUCH,
1159 |s| spell_vampiric_touch(s, ¶ms.game_data),
1160 )
1161 .spell(
1162 "shadow_word_madness",
1163 SPELL::SHADOW_WORD_MADNESS,
1164 |s| spell_shadow_word_madness(s, ¶ms.game_data),
1165 )
1166 .spell("voidform", SPELL::VOIDFORM, |s| spell_voidform(s, ¶ms.game_data))
1167 .spell(
1168 "void_torrent",
1169 SPELL::VOID_TORRENT,
1170 |s| spell_void_torrent(s, ¶ms.game_data),
1171 )
1172 .spell("mind_flay", SPELL::MIND_FLAY, |s| spell_mind_flay(s, ¶ms.game_data))
1173 .spell(
1174 "shadow_word_death",
1175 SPELL::SHADOW_WORD_DEATH,
1176 |s| spell_shadow_word_death(s, ¶ms.game_data),
1177 )
1178 .spell("halo", SPELL::HALO, |s| spell_halo(s, ¶ms.game_data))
1179 .spell(
1180 "power_infusion",
1181 SPELL::POWER_INFUSION,
1182 |s| spell_power_infusion(s, ¶ms.game_data),
1183 )
1184 .spell(
1185 "shadowform",
1186 SPELL::SHADOWFORM,
1187 |s| spell_shadowform(s, ¶ms.game_data),
1188 )
1189 .spell(
1190 "power_word_fortitude",
1191 SPELL::POWER_WORD_FORTITUDE,
1192 |s| spell_power_word_fortitude(s, ¶ms.game_data),
1193 )
1194 .spell(
1195 "shadowy_apparition",
1196 SPELL::SHADOWY_APPARITION,
1197 |s| spell_shadowy_apparition(s, ¶ms.game_data),
1198 )
1199 .spell(
1200 "void_apparition",
1201 SPELL::VOID_APPARITION,
1202 |s| spell_void_apparition(s, ¶ms.game_data),
1203 )
1204 .spell("void_bolt", SPELL::VOID_BOLT, |s| spell_void_bolt(s, ¶ms.game_data))
1205 .spell(
1206 "tentacle_slam",
1207 SPELL::TENTACLE_SLAM,
1208 |s| spell_tentacle_slam(s, ¶ms.game_data),
1209 )
1210 .spell(
1211 "tentacle_slam_damage",
1212 SPELL::TENTACLE_SLAM_DAMAGE,
1213 |s| spell_tentacle_slam_damage(s, ¶ms.game_data),
1214 )
1215 .game_data(params.game_data.clone())
1216 .talent_spell_ids(TALENTS)
1217 .talent_selections(params.talent_selections)
1218 .set_bonus_auras(params.set_bonus_auras)
1219 .encounter(params.encounter.clone());
1220 let builder = crate::generated::items::register_equipped(
1221 builder,
1222 ¶ms.game_data,
1223 params.equipped_items,
1224 )?;
1225
1226 builder
1227 .build(params.rotation)
1228 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
1229}
1230fn new_handler(
1231 params: wowlab_engine_ports::HandlerParams<'_>,
1232) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
1233 let handler = crate::composition::compose_handler(
1234 build_combat_system,
1235 ¶ms,
1236 |parts, params| Ok(
1237 crate::hooks::shadow_priest::ShadowPriestHandler::new(parts, params),
1238 ),
1239 )?;
1240
1241 Ok(Box::new(handler))
1242}
1243static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
1244 primary_resource: ResourceType::Insanity,
1245 secondary_resource: None,
1246 spell_ids: DECLARED_SPELL_IDS,
1247 aura_ids: DECLARED_AURA_IDS,
1248 server_proc_entries: &[],
1249 talents: TALENTS,
1250 rotation_field_schema: &[],
1251 metrics_plugin_id: None,
1252 hero_talent_trees: &[
1253 HeroTalentTreeDesc {
1254 name: "Archon",
1255 spells: HERO_TREE_ARCHON_SPELLS,
1256 auras: HERO_TREE_ARCHON_AURAS,
1257 },
1258 HeroTalentTreeDesc {
1259 name: "Voidweaver",
1260 spells: HERO_TREE_VOIDWEAVER_SPELLS,
1261 auras: HERO_TREE_VOIDWEAVER_AURAS,
1262 },
1263 ],
1264 reported_spells: REPORTED_SPELLS,
1265 masked_passive_effects: &[],
1266 passive_effect_overrides: &[],
1267};
1268pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1269 spec_id: SpecId::Shadow,
1270 display_name: "Shadow Priest",
1271 metadata: DECLARED_METADATA,
1272 handler_factory: new_handler,
1273};
1274wowlab_engine_combat::smoke_test!(build_combat_system);