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 SOUL_CLEAVE: u32 = 228_477;
16 pub(crate) const IMMOLATION_AURA: u32 = 258_920;
18 pub(crate) const FEL_DEVASTATION: u32 = 212_084;
20 pub(crate) const SIGIL_OF_FLAME: u32 = 204_596;
22 pub(crate) const SHEAR: u32 = 203_782;
24 pub(crate) const METAMORPHOSIS: u32 = 187_827;
26 pub(crate) const THROW_GLAIVE: u32 = 204_157;
28 pub(crate) const SOUL_CARVER: u32 = 207_407;
30 pub(crate) const SPIRIT_BOMB: u32 = 247_454;
32 pub(crate) const SIGIL_OF_SPITE: u32 = 390_163;
34 pub(crate) const FRACTURE: u32 = 263_642;
36}
37pub(crate) mod AURA {
38 pub(crate) const SIGIL_OF_FLAME: u32 = 204_598;
40 pub(crate) const SOUL_FRAGMENTS: u32 = 203_981;
42 pub(crate) const REAVERS_GLAIVE: u32 = 442_294;
44 pub(crate) const RENDING_STRIKE: u32 = 442_442;
46 pub(crate) const GLAIVE_FLURRY: u32 = 442_435;
48 pub(crate) const VOIDFALL_BUILDING: u32 = 1_256_301;
50 pub(crate) const VOIDFALL_SPENDING: u32 = 1_256_302;
52 pub(crate) const VOIDFALL_FINAL_HOUR: u32 = 1_256_322;
54 pub(crate) const CATASTROPHE: u32 = 1_256_667;
56 pub(crate) const DARK_MATTER: u32 = 1_256_308;
58 pub(crate) const UNTETHERED_RAGE: u32 = 1_270_476;
60 pub(crate) const SEETHING_ANGER: u32 = 1_270_547;
62}
63pub(crate) mod TALENT {
64 pub(crate) const SIGIL_OF_SPITE: u32 = 390_163;
66 pub(crate) const SOUL_CARVER: u32 = 207_407;
68 pub(crate) const SPIRIT_BOMB: u32 = 247_454;
70 pub(crate) const UNTETHERED_RAGE_1: u32 = 1_270_444;
72 pub(crate) const UNTETHERED_RAGE_2: u32 = 1_270_448;
74 pub(crate) const UNTETHERED_RAGE_3: u32 = 1_270_449;
76}
77pub(crate) mod REPORTED_SPELL {
78 pub(crate) const FRACTURE_MH: u32 = 225_919;
80 pub(crate) const FRACTURE_OH: u32 = 225_921;
82 pub(crate) const SOUL_CLEAVE_DAMAGE: u32 = 228_478;
84}
85pub(crate) mod HERO {
86 pub(crate) mod ALDRACHI_REAVER {
87 pub(crate) mod SPELL {
88 pub(crate) const ART_OF_THE_GLAIVE_DAMAGE: u32 = 444_810;
90 pub(crate) const FURY_OF_THE_ALDRACHI: u32 = 444_806;
92 pub(crate) const REAVERS_GLAIVE: u32 = 442_294;
94 pub(crate) const REAVERS_GLAIVE_VENGEANCE: u32 = 444_764;
96 pub(crate) const WOUNDED_QUARRY_DAMAGE: u32 = 442_808;
98 }
99 pub(crate) mod AURA {
100 pub(crate) const EVASIVE_ACTION: u32 = 444_929;
102 pub(crate) const GLAIVE_FLURRY: u32 = 442_435;
104 pub(crate) const REAVERS_GLAIVE_BUFF: u32 = 442_294;
106 pub(crate) const REAVERS_MARK: u32 = 442_624;
108 pub(crate) const RENDING_STRIKE: u32 = 442_442;
110 pub(crate) const THRILL_OF_THE_FIGHT: u32 = 442_695;
112 }
113 }
114 pub(crate) mod ANNIHILATOR {
115 pub(crate) mod SPELL {
116 pub(crate) const CATASTROPHE: u32 = 1_253_769;
118 pub(crate) const CATASTROPHE_DOT: u32 = 1_256_667;
120 pub(crate) const DARK_MATTER: u32 = 1_256_307;
122 pub(crate) const FINAL_HOUR: u32 = 1_253_805;
124 pub(crate) const HARNESS_THE_COSMOS: u32 = 1_279_247;
126 pub(crate) const MASS_ACCELERATION: u32 = 1_256_295;
128 pub(crate) const METEORIC_FALL: u32 = 1_253_391;
130 pub(crate) const METEORIC_RISE: u32 = 1_253_377;
132 pub(crate) const METEOR_SHOWER_DAMAGE: u32 = 1_264_130;
134 pub(crate) const OTHERWORLDLY_FOCUS: u32 = 1_253_817;
136 pub(crate) const SWIFT_ERASURE: u32 = 1_253_668;
138 pub(crate) const VOIDFALL: u32 = 1_253_304;
140 pub(crate) const VOIDFALL_METEOR_DAMAGE: u32 = 1_256_306;
142 pub(crate) const WORLD_KILLER: u32 = 1_256_353;
144 pub(crate) const WORLD_KILLER_DAMAGE: u32 = 1_256_617;
146 }
147 pub(crate) mod AURA {
148 pub(crate) const DARK_MATTER: u32 = 1_256_308;
150 pub(crate) const VOIDFALL_BUILDING: u32 = 1_256_301;
152 pub(crate) const VOIDFALL_FINAL_HOUR: u32 = 1_256_322;
154 pub(crate) const VOIDFALL_SPENDING: u32 = 1_256_302;
156 }
157 }
158 pub(crate) mod FEL_SCARRED {
159 pub(crate) mod SPELL {
160 pub(crate) const DEMONSURGE_DAMAGE: u32 = 451_263;
162 pub(crate) const SPIRIT_BURST: u32 = 452_437;
164 pub(crate) const STUDENT_OF_SUFFERING_FURY: u32 = 453_236;
166 }
167 pub(crate) mod AURA {
168 pub(crate) const BURNING_BLADES: u32 = 452_408;
170 pub(crate) const DEMONIC_INTENSITY: u32 = 452_415;
172 pub(crate) const DEMONSURGE: u32 = 452_416;
174 pub(crate) const STUDENT_OF_SUFFERING: u32 = 453_239;
176 }
177 }
178}
179static HERO_TREE_ALDRACHI_REAVER_SPELLS: &[(&str, u32)] = &[
180 ("ART_OF_THE_GLAIVE_DAMAGE", 444_810),
181 ("FURY_OF_THE_ALDRACHI", 444_806),
182 ("REAVERS_GLAIVE", 442_294),
183 ("REAVERS_GLAIVE_VENGEANCE", 444_764),
184 ("WOUNDED_QUARRY_DAMAGE", 442_808),
185];
186static HERO_TREE_ALDRACHI_REAVER_AURAS: &[(&str, u32)] = &[
187 ("EVASIVE_ACTION", 444_929),
188 ("GLAIVE_FLURRY", 442_435),
189 ("REAVERS_GLAIVE_BUFF", 442_294),
190 ("REAVERS_MARK", 442_624),
191 ("RENDING_STRIKE", 442_442),
192 ("THRILL_OF_THE_FIGHT", 442_695),
193];
194static HERO_TREE_ANNIHILATOR_SPELLS: &[(&str, u32)] = &[
195 ("CATASTROPHE", 1_253_769),
196 ("CATASTROPHE_DOT", 1_256_667),
197 ("DARK_MATTER", 1_256_307),
198 ("FINAL_HOUR", 1_253_805),
199 ("HARNESS_THE_COSMOS", 1_279_247),
200 ("MASS_ACCELERATION", 1_256_295),
201 ("METEORIC_FALL", 1_253_391),
202 ("METEORIC_RISE", 1_253_377),
203 ("METEOR_SHOWER_DAMAGE", 1_264_130),
204 ("OTHERWORLDLY_FOCUS", 1_253_817),
205 ("SWIFT_ERASURE", 1_253_668),
206 ("VOIDFALL", 1_253_304),
207 ("VOIDFALL_METEOR_DAMAGE", 1_256_306),
208 ("WORLD_KILLER", 1_256_353),
209 ("WORLD_KILLER_DAMAGE", 1_256_617),
210];
211static HERO_TREE_ANNIHILATOR_AURAS: &[(&str, u32)] = &[
212 ("DARK_MATTER", 1_256_308),
213 ("VOIDFALL_BUILDING", 1_256_301),
214 ("VOIDFALL_FINAL_HOUR", 1_256_322),
215 ("VOIDFALL_SPENDING", 1_256_302),
216];
217static HERO_TREE_FEL_SCARRED_SPELLS: &[(&str, u32)] = &[
218 ("DEMONSURGE_DAMAGE", 451_263),
219 ("SPIRIT_BURST", 452_437),
220 ("STUDENT_OF_SUFFERING_FURY", 453_236),
221];
222static HERO_TREE_FEL_SCARRED_AURAS: &[(&str, u32)] = &[
223 ("BURNING_BLADES", 452_408),
224 ("DEMONIC_INTENSITY", 452_415),
225 ("DEMONSURGE", 452_416),
226 ("STUDENT_OF_SUFFERING", 453_239),
227];
228pub(crate) const AURA_SIGIL_OF_FLAME: LocalAuraIdx = LocalAuraIdx::new(0);
229pub(crate) const AURA_SOUL_FRAGMENTS: LocalAuraIdx = LocalAuraIdx::new(1);
230pub(crate) const AURA_REAVERS_GLAIVE: LocalAuraIdx = LocalAuraIdx::new(2);
231pub(crate) const AURA_RENDING_STRIKE: LocalAuraIdx = LocalAuraIdx::new(3);
232pub(crate) const AURA_GLAIVE_FLURRY: LocalAuraIdx = LocalAuraIdx::new(4);
233pub(crate) const AURA_VOIDFALL_BUILDING: LocalAuraIdx = LocalAuraIdx::new(5);
234pub(crate) const AURA_VOIDFALL_SPENDING: LocalAuraIdx = LocalAuraIdx::new(6);
235pub(crate) const AURA_VOIDFALL_FINAL_HOUR: LocalAuraIdx = LocalAuraIdx::new(7);
236pub(crate) const AURA_CATASTROPHE: LocalAuraIdx = LocalAuraIdx::new(8);
237pub(crate) const AURA_DARK_MATTER: LocalAuraIdx = LocalAuraIdx::new(9);
238pub(crate) const AURA_UNTETHERED_RAGE: LocalAuraIdx = LocalAuraIdx::new(10);
239pub(crate) const AURA_SEETHING_ANGER: LocalAuraIdx = LocalAuraIdx::new(11);
240pub(crate) const AURA_COUNT: usize = 12;
241const _: () = assert!(
242 AURA_COUNT == 12, "AURA index ABI: count changed -- update positional hook consumers"
243);
244pub(crate) const SPELL_SOUL_CLEAVE: LocalSpellIdx = LocalSpellIdx::new(0);
245pub(crate) const SPELL_IMMOLATION_AURA: LocalSpellIdx = LocalSpellIdx::new(1);
246pub(crate) const SPELL_FEL_DEVASTATION: LocalSpellIdx = LocalSpellIdx::new(2);
247pub(crate) const SPELL_SIGIL_OF_FLAME: LocalSpellIdx = LocalSpellIdx::new(3);
248pub(crate) const SPELL_SHEAR: LocalSpellIdx = LocalSpellIdx::new(4);
249pub(crate) const SPELL_METAMORPHOSIS: LocalSpellIdx = LocalSpellIdx::new(5);
250pub(crate) const SPELL_THROW_GLAIVE: LocalSpellIdx = LocalSpellIdx::new(6);
251pub(crate) const SPELL_SOUL_CARVER: LocalSpellIdx = LocalSpellIdx::new(7);
252pub(crate) const SPELL_SPIRIT_BOMB: LocalSpellIdx = LocalSpellIdx::new(8);
253pub(crate) const SPELL_SIGIL_OF_SPITE: LocalSpellIdx = LocalSpellIdx::new(9);
254pub(crate) const SPELL_FRACTURE: LocalSpellIdx = LocalSpellIdx::new(10);
255pub(crate) const SPELL_COUNT: usize = 11;
256const _: () = assert!(
257 SPELL_COUNT == 11,
258 "SPELL index ABI: count changed -- update positional hook consumers"
259);
260pub(crate) mod EFFECT {
261 pub(crate) const FEL_BLOOD: (u32, u8) = (321_299, 1);
263 pub(crate) const FRACTURE_FRAGMENTS: (u32, u8) = (263_642, 1);
265 pub(crate) const SPIRIT_BOMB_DAMAGE: (u32, u8) = (247_455, 1);
267 pub(crate) const VOIDFALL_CHANCE: (u32, u8) = (1_253_304, 3);
269 pub(crate) const VOIDFALL_STACKS: (u32, u8) = (1_253_304, 1);
271 pub(crate) const VOIDFALL_METEOR_COEF: (u32, u8) = (1_256_306, 1);
273 pub(crate) const WORLD_KILLER_COEF: (u32, u8) = (1_256_617, 1);
275 pub(crate) const WORLD_KILLER_META_CDR: (u32, u8) = (1_256_353, 3);
277 pub(crate) const CATASTROPHE_PCT: (u32, u8) = (1_253_769, 1);
279 pub(crate) const METEOR_SHOWER_TICKS: (u32, u8) = (1_256_307, 1);
281 pub(crate) const METEOR_SHOWER_COEF: (u32, u8) = (1_264_130, 1);
283 pub(crate) const MASS_ACCELERATION_STACKS: (u32, u8) = (1_256_295, 1);
285 pub(crate) const OTHERWORLDLY_FOCUS_PCT: (u32, u8) = (1_253_817, 1);
287 pub(crate) const HARNESS_THE_COSMOS_PCT: (u32, u8) = (1_279_247, 1);
289}
290static REPORTED_SPELLS: &[(&str, u32)] = &[
291 ("fracture_mh", 225_919),
292 ("fracture_oh", 225_921),
293 ("soul_cleave_damage", 228_478),
294];
295static DECLARED_SPELL_IDS: &[u32] = &[
296 228_477, 258_920, 212_084, 204_596, 203_782, 187_827, 204_157, 207_407, 247_454,
297 390_163, 263_642, 204_598, 203_981, 442_294, 442_442, 442_435, 1_256_301, 1_256_302,
298 1_256_322, 1_256_667, 1_256_308, 1_270_476, 1_270_547, 6603, 6603, 203_747, 204_255,
299 204_598, 225_919, 225_921, 228_478, 247_455, 263_642, 321_299, 1_253_304, 1_253_769,
300 1_253_817, 1_256_295, 1_256_306, 1_256_307, 1_256_353, 1_256_617, 1_256_667,
301 1_264_130, 1_279_247, 225_919, 225_921, 228_478, 444_810, 444_806, 442_294, 444_764,
302 442_808, 444_929, 442_435, 442_294, 442_624, 442_442, 442_695, 1_253_769, 1_256_667,
303 1_256_307, 1_253_805, 1_279_247, 1_256_295, 1_253_391, 1_253_377, 1_264_130,
304 1_253_817, 1_253_668, 1_253_304, 1_256_306, 1_256_353, 1_256_617, 1_256_308,
305 1_256_301, 1_256_322, 1_256_302, 451_263, 452_437, 453_236, 452_408, 452_415,
306 452_416, 453_239,
307];
308static DECLARED_AURA_IDS: &[u32] = &[
309 204_598, 203_981, 442_294, 442_442, 442_435, 1_256_301, 1_256_302, 1_256_322,
310 1_256_667, 1_256_308, 1_270_476, 1_270_547, 444_929, 442_435, 442_294, 442_624,
311 442_442, 442_695, 1_256_308, 1_256_301, 1_256_322, 1_256_302, 452_408, 452_415,
312 452_416, 453_239,
313];
314static TALENTS: &[(&str, u32)] = &[
315 ("SIGIL_OF_SPITE", TALENT::SIGIL_OF_SPITE),
316 ("SOUL_CARVER", TALENT::SOUL_CARVER),
317 ("SPIRIT_BOMB", TALENT::SPIRIT_BOMB),
318 ("UNTETHERED_RAGE_1", TALENT::UNTETHERED_RAGE_1),
319 ("UNTETHERED_RAGE_2", TALENT::UNTETHERED_RAGE_2),
320 ("UNTETHERED_RAGE_3", TALENT::UNTETHERED_RAGE_3),
321];
322fn aura_sigil_of_flame(
323 a: AuraDefinitionDraft,
324 data: &ResolvedGameData,
325) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
326 Ok(
327 a
328 .on_target()
329 .apply_base_from_data(data, 204_598)?
330 .max_stacks(
331 data
332 .require_aura_max_stacks(
333 wowlab_types::sim::SpellIdx::from_raw(204_598),
334 )?,
335 ),
336 )
337}
338fn aura_soul_fragments(
339 a: AuraDefinitionDraft,
340 data: &ResolvedGameData,
341) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
342 a.on_player()
343 .duration_ms(
344 wowlab_engine_combat::validated_milliseconds(
345 f64::from(
346 data
347 .require_aura_duration_ms(
348 wowlab_types::sim::SpellIdx::from_raw(204_255),
349 )?,
350 ),
351 203_981,
352 "aura.duration_ms",
353 )?,
354 )
355 .apply_periodic_effect_from_data(data, 203_981)?
356 .apply_duration_behavior_from_data(data, 204_255)?
357 .apply_timing_modifiers_from_data(data, 203_981)?
358 .max_stacks(
359 wowlab_engine_combat::validated_max_stacks(5.0, 203_981, "aura.max_stacks")?,
360 )
361 .apply_periodic_behavior_from_data(data, 204_255)
362}
363fn aura_reavers_glaive(
364 a: AuraDefinitionDraft,
365 data: &ResolvedGameData,
366) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
367 Ok(
368 a
369 .on_player()
370 .apply_base_from_data(data, 442_294)?
371 .max_stacks(
372 data
373 .require_aura_max_stacks(
374 wowlab_types::sim::SpellIdx::from_raw(442_294),
375 )?,
376 ),
377 )
378}
379fn aura_rending_strike(
380 a: AuraDefinitionDraft,
381 data: &ResolvedGameData,
382) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
383 Ok(
384 a
385 .on_player()
386 .apply_base_from_data(data, 442_442)?
387 .max_stacks(
388 data
389 .require_aura_max_stacks(
390 wowlab_types::sim::SpellIdx::from_raw(442_442),
391 )?,
392 ),
393 )
394}
395fn aura_glaive_flurry(
396 a: AuraDefinitionDraft,
397 data: &ResolvedGameData,
398) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
399 Ok(
400 a
401 .on_player()
402 .apply_base_from_data(data, 442_435)?
403 .max_stacks(
404 data
405 .require_aura_max_stacks(
406 wowlab_types::sim::SpellIdx::from_raw(442_435),
407 )?,
408 ),
409 )
410}
411fn aura_voidfall_building(
412 a: AuraDefinitionDraft,
413 data: &ResolvedGameData,
414) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
415 Ok(
416 a
417 .on_player()
418 .apply_base_from_data(data, 1_256_301)?
419 .max_stacks(
420 data
421 .require_aura_max_stacks(
422 wowlab_types::sim::SpellIdx::from_raw(1_256_301),
423 )?,
424 ),
425 )
426}
427fn aura_voidfall_spending(
428 a: AuraDefinitionDraft,
429 data: &ResolvedGameData,
430) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
431 Ok(
432 a
433 .on_player()
434 .apply_base_from_data(data, 1_256_302)?
435 .max_stacks(
436 data
437 .require_aura_max_stacks(
438 wowlab_types::sim::SpellIdx::from_raw(1_256_302),
439 )?,
440 ),
441 )
442}
443fn aura_voidfall_final_hour(
444 a: AuraDefinitionDraft,
445 data: &ResolvedGameData,
446) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
447 Ok(
448 a
449 .on_player()
450 .apply_base_from_data(data, 1_256_322)?
451 .max_stacks(
452 data
453 .require_aura_max_stacks(
454 wowlab_types::sim::SpellIdx::from_raw(1_256_322),
455 )?,
456 ),
457 )
458}
459fn aura_catastrophe(
460 a: AuraDefinitionDraft,
461 data: &ResolvedGameData,
462) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
463 a.on_target()
464 .apply_base_from_data(data, 1_256_667)?
465 .max_stacks(
466 data
467 .require_aura_max_stacks(
468 wowlab_types::sim::SpellIdx::from_raw(1_256_667),
469 )?,
470 )
471 .periodic_residual_damage(
472 wowlab_engine_combat::validated_milliseconds(
473 data.period(wowlab_types::sim::SpellIdx::from_raw(1_256_667), 1),
474 1_256_667,
475 "periodic_residual_damage.tick_ms",
476 )?,
477 )
478 .apply_periodic_behavior_from_data(data, 1_256_667)
479}
480fn aura_dark_matter(
481 a: AuraDefinitionDraft,
482 data: &ResolvedGameData,
483) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
484 Ok(
485 a
486 .on_player()
487 .apply_base_from_data(data, 1_256_308)?
488 .max_stacks(
489 data
490 .require_aura_max_stacks(
491 wowlab_types::sim::SpellIdx::from_raw(1_256_308),
492 )?,
493 ),
494 )
495}
496fn aura_untethered_rage(
497 a: AuraDefinitionDraft,
498 data: &ResolvedGameData,
499) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
500 Ok(
501 a
502 .on_player()
503 .apply_base_from_data(data, 1_270_476)?
504 .max_stacks(
505 data
506 .require_aura_max_stacks(
507 wowlab_types::sim::SpellIdx::from_raw(1_270_476),
508 )?,
509 ),
510 )
511}
512fn aura_seething_anger(
513 a: AuraDefinitionDraft,
514 data: &ResolvedGameData,
515) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
516 Ok(
517 a
518 .on_player()
519 .apply_base_from_data(data, 1_270_547)?
520 .max_stacks(
521 data
522 .require_aura_max_stacks(
523 wowlab_types::sim::SpellIdx::from_raw(1_270_547),
524 )?,
525 ),
526 )
527}
528fn spell_soul_cleave(
529 s: SpellDefinitionDraft,
530 data: &ResolvedGameData,
531) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
532 Ok(
533 s
534 .apply_base_from_data(data, 228_477)?
535 .without_primary_data_damage()
536 .apply_aoe_from_data(data)
537 .on_cast(crate::hooks::vengeance_demon_hunter::soul_cleave_hook),
538 )
539}
540fn spell_immolation_aura(
541 s: SpellDefinitionDraft,
542 data: &ResolvedGameData,
543) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
544 Ok(s.apply_base_from_data(data, 258_920)?.apply_aoe_from_data(data))
545}
546fn spell_fel_devastation(
547 s: SpellDefinitionDraft,
548 data: &ResolvedGameData,
549) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
550 Ok(s.apply_base_from_data(data, 212_084)?.apply_aoe_from_data(data))
551}
552fn spell_sigil_of_flame(
553 s: SpellDefinitionDraft,
554 data: &ResolvedGameData,
555) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
556 Ok(
557 s
558 .apply_base_from_data(data, 204_596)?
559 .damage_ap_from_data(
560 wowlab_engine_domain::dbc::EffectLookup::new(
561 data,
562 wowlab_types::sim::EffectRef::new(
563 wowlab_types::sim::SpellIdx::from_raw(204_598),
564 1,
565 ),
566 ),
567 )?
568 .applies_aura(AURA_SIGIL_OF_FLAME)
569 .apply_aoe_from_data(data),
570 )
571}
572fn spell_shear(
573 s: SpellDefinitionDraft,
574 data: &ResolvedGameData,
575) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
576 Ok(s.apply_base_from_data(data, 203_782)?.apply_aoe_from_data(data))
577}
578fn spell_metamorphosis(
579 s: SpellDefinitionDraft,
580 data: &ResolvedGameData,
581) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
582 Ok(
583 s
584 .apply_base_from_data(data, 187_827)?
585 .apply_aoe_from_data(data)
586 .on_cast(crate::hooks::vengeance_demon_hunter::metamorphosis_hook),
587 )
588}
589fn spell_throw_glaive(
590 s: SpellDefinitionDraft,
591 data: &ResolvedGameData,
592) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
593 Ok(s.apply_base_from_data(data, 204_157)?.apply_aoe_from_data(data))
594}
595fn spell_soul_carver(
596 s: SpellDefinitionDraft,
597 data: &ResolvedGameData,
598) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
599 Ok(s.apply_base_from_data(data, 207_407)?.apply_aoe_from_data(data))
600}
601fn spell_spirit_bomb(
602 s: SpellDefinitionDraft,
603 data: &ResolvedGameData,
604) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
605 Ok(
606 s
607 .apply_base_from_data(data, 247_454)?
608 .apply_aoe_from_data(data)
609 .on_cast(crate::hooks::vengeance_demon_hunter::spirit_bomb_hook),
610 )
611}
612fn spell_sigil_of_spite(
613 s: SpellDefinitionDraft,
614 data: &ResolvedGameData,
615) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
616 Ok(s.apply_base_from_data(data, 390_163)?.apply_aoe_from_data(data))
617}
618fn spell_fracture(
619 s: SpellDefinitionDraft,
620 data: &ResolvedGameData,
621) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
622 Ok(
623 s
624 .apply_base_from_data(data, 263_642)?
625 .without_primary_data_damage()
626 .apply_aoe_from_data(data)
627 .on_cast(crate::hooks::vengeance_demon_hunter::fracture_hook),
628 )
629}
630pub(crate) fn build_combat_system(
631 params: &wowlab_engine_ports::HandlerParams<'_>,
632) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
633 let builder = BuiltCombatSystem::builder(params.stats.clone())
634 .spec_id(SpecId::Vengeance)
635 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
636 .mastery_spell(203_747)
637 .mastery_hook(crate::hooks::vengeance_demon_hunter::mastery)
638 .resource(
639 "fury",
640 params
641 .game_data
642 .power_max(17)
643 .ok_or_else(|| {
644 wowlab_engine_ports::EngineError::spec_construction(
645 "missing power-type power_max for type_id 17",
646 )
647 })?,
648 params
649 .game_data
650 .power_regen_for_max(
651 17,
652 params
653 .game_data
654 .power_max(17)
655 .ok_or_else(|| {
656 wowlab_engine_ports::EngineError::spec_construction(
657 "missing power-type power_max for type_id 17",
658 )
659 })?,
660 )
661 .ok_or_else(|| {
662 wowlab_engine_ports::EngineError::spec_construction(
663 "missing power-type power_regen_for_max for type_id 17",
664 )
665 })?,
666 )
667 .resource_type_id(17)
668 .resource_start(
669 params
670 .game_data
671 .power_default(17)
672 .ok_or_else(|| {
673 wowlab_engine_ports::EngineError::spec_construction(
674 "missing power-type power_default for type_id 17",
675 )
676 })?,
677 )
678 .aura(
679 "sigil_of_flame",
680 AURA::SIGIL_OF_FLAME,
681 |a| aura_sigil_of_flame(a, ¶ms.game_data),
682 )
683 .aura(
684 "soul_fragments",
685 AURA::SOUL_FRAGMENTS,
686 |a| aura_soul_fragments(a, ¶ms.game_data),
687 )
688 .aura(
689 "reavers_glaive",
690 AURA::REAVERS_GLAIVE,
691 |a| aura_reavers_glaive(a, ¶ms.game_data),
692 )
693 .aura(
694 "rending_strike",
695 AURA::RENDING_STRIKE,
696 |a| aura_rending_strike(a, ¶ms.game_data),
697 )
698 .aura(
699 "glaive_flurry",
700 AURA::GLAIVE_FLURRY,
701 |a| aura_glaive_flurry(a, ¶ms.game_data),
702 )
703 .aura(
704 "voidfall_building",
705 AURA::VOIDFALL_BUILDING,
706 |a| aura_voidfall_building(a, ¶ms.game_data),
707 )
708 .aura(
709 "voidfall_spending",
710 AURA::VOIDFALL_SPENDING,
711 |a| aura_voidfall_spending(a, ¶ms.game_data),
712 )
713 .aura(
714 "voidfall_final_hour",
715 AURA::VOIDFALL_FINAL_HOUR,
716 |a| aura_voidfall_final_hour(a, ¶ms.game_data),
717 )
718 .aura(
719 "catastrophe",
720 AURA::CATASTROPHE,
721 |a| aura_catastrophe(a, ¶ms.game_data),
722 )
723 .aura(
724 "dark_matter",
725 AURA::DARK_MATTER,
726 |a| aura_dark_matter(a, ¶ms.game_data),
727 )
728 .aura(
729 "untethered_rage",
730 AURA::UNTETHERED_RAGE,
731 |a| aura_untethered_rage(a, ¶ms.game_data),
732 )
733 .aura(
734 "seething_anger",
735 AURA::SEETHING_ANGER,
736 |a| aura_seething_anger(a, ¶ms.game_data),
737 )
738 .spell(
739 "soul_cleave",
740 SPELL::SOUL_CLEAVE,
741 |s| spell_soul_cleave(s, ¶ms.game_data),
742 )
743 .spell(
744 "immolation_aura",
745 SPELL::IMMOLATION_AURA,
746 |s| spell_immolation_aura(s, ¶ms.game_data),
747 )
748 .spell(
749 "fel_devastation",
750 SPELL::FEL_DEVASTATION,
751 |s| spell_fel_devastation(s, ¶ms.game_data),
752 )
753 .spell(
754 "sigil_of_flame",
755 SPELL::SIGIL_OF_FLAME,
756 |s| spell_sigil_of_flame(s, ¶ms.game_data),
757 )
758 .spell("shear", SPELL::SHEAR, |s| spell_shear(s, ¶ms.game_data))
759 .spell(
760 "metamorphosis",
761 SPELL::METAMORPHOSIS,
762 |s| spell_metamorphosis(s, ¶ms.game_data),
763 )
764 .spell(
765 "throw_glaive",
766 SPELL::THROW_GLAIVE,
767 |s| spell_throw_glaive(s, ¶ms.game_data),
768 )
769 .spell(
770 "soul_carver",
771 SPELL::SOUL_CARVER,
772 |s| spell_soul_carver(s, ¶ms.game_data),
773 )
774 .spell(
775 "spirit_bomb",
776 SPELL::SPIRIT_BOMB,
777 |s| spell_spirit_bomb(s, ¶ms.game_data),
778 )
779 .spell(
780 "sigil_of_spite",
781 SPELL::SIGIL_OF_SPITE,
782 |s| spell_sigil_of_spite(s, ¶ms.game_data),
783 )
784 .spell("fracture", SPELL::FRACTURE, |s| spell_fracture(s, ¶ms.game_data))
785 .auto_attack(|a| {
786 a.spell_id(6603).swing_ms(2600).apply_base_from_data(¶ms.game_data)
787 })
788 .auto_attack(|a| {
789 a.spell_id(6603).swing_ms(2600).apply_base_from_data(¶ms.game_data)
790 })
791 .game_data(params.game_data.clone())
792 .talent_spell_ids(TALENTS)
793 .talent_selections(params.talent_selections)
794 .set_bonus_auras(params.set_bonus_auras)
795 .encounter(params.encounter.clone());
796 let builder = crate::generated::items::register_equipped(
797 builder,
798 ¶ms.game_data,
799 params.equipped_items,
800 )?;
801
802 builder
803 .build(params.rotation)
804 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
805}
806fn new_handler(
807 params: wowlab_engine_ports::HandlerParams<'_>,
808) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
809 let handler = crate::composition::compose_handler(
810 build_combat_system,
811 ¶ms,
812 crate::hooks::vengeance_demon_hunter::VengeanceHandler::try_new,
813 )?;
814
815 Ok(Box::new(handler))
816}
817static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
818 primary_resource: ResourceType::Fury,
819 secondary_resource: None,
820 spell_ids: DECLARED_SPELL_IDS,
821 aura_ids: DECLARED_AURA_IDS,
822 server_proc_entries: &[],
823 talents: TALENTS,
824 rotation_field_schema: &[],
825 metrics_plugin_id: None,
826 hero_talent_trees: &[
827 HeroTalentTreeDesc {
828 name: "Aldrachi Reaver",
829 spells: HERO_TREE_ALDRACHI_REAVER_SPELLS,
830 auras: HERO_TREE_ALDRACHI_REAVER_AURAS,
831 },
832 HeroTalentTreeDesc {
833 name: "Annihilator",
834 spells: HERO_TREE_ANNIHILATOR_SPELLS,
835 auras: HERO_TREE_ANNIHILATOR_AURAS,
836 },
837 HeroTalentTreeDesc {
838 name: "Fel Scarred",
839 spells: HERO_TREE_FEL_SCARRED_SPELLS,
840 auras: HERO_TREE_FEL_SCARRED_AURAS,
841 },
842 ],
843 reported_spells: REPORTED_SPELLS,
844 masked_passive_effects: &[],
845 passive_effect_overrides: &[],
846};
847pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
848 spec_id: SpecId::Vengeance,
849 display_name: "Vengeance Demon Hunter",
850 metadata: DECLARED_METADATA,
851 handler_factory: new_handler,
852};
853wowlab_engine_combat::smoke_test!(build_combat_system);