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 CHAOS_STRIKE: u32 = 162_794;
16 pub(crate) const ANNIHILATION: u32 = 201_427;
18 pub(crate) const BLADE_DANCE: u32 = 188_499;
20 pub(crate) const DEATH_SWEEP: u32 = 210_152;
22 pub(crate) const METAMORPHOSIS: u32 = 191_427;
24 pub(crate) const THROW_GLAIVE: u32 = 185_123;
26 pub(crate) const FEL_RUSH: u32 = 195_072;
28 pub(crate) const VENGEFUL_RETREAT: u32 = 198_793;
30 pub(crate) const ESSENCE_BREAK: u32 = 258_860;
32 pub(crate) const FELBLADE: u32 = 232_893;
34 pub(crate) const EYE_BEAM: u32 = 198_013;
36 pub(crate) const ABYSSAL_GAZE: u32 = 452_497;
38 pub(crate) const IMMOLATION_AURA: u32 = 258_920;
40 pub(crate) const THE_HUNT: u32 = 370_965;
42}
43pub(crate) mod AURA {
44 pub(crate) const METAMORPHOSIS: u32 = 162_264;
46 pub(crate) const ESSENCE_BREAK: u32 = 320_338;
48 pub(crate) const INERTIA: u32 = 427_640;
50 pub(crate) const CYCLE_OF_HATRED: u32 = 1_214_887;
52 pub(crate) const FURIOUS_GAZE: u32 = 343_312;
54 pub(crate) const STUDENT_OF_SUFFERING: u32 = 453_239;
56 pub(crate) const DEMONIC_INTENSITY: u32 = 452_489;
58 pub(crate) const EMPOWERED_EYE_BEAM: u32 = 1_271_144;
60 pub(crate) const REAVERS_GLAIVE: u32 = 442_294;
62 pub(crate) const EXERGY: u32 = 442_266;
64 pub(crate) const IMMOLATION_AURA: u32 = 258_920;
66 pub(crate) const THE_HUNT_DOT: u32 = 370_969;
68 pub(crate) const BURNING_BLADES: u32 = 453_177;
70 pub(crate) const SERRATED_GLAIVE: u32 = 390_155;
72 pub(crate) const SOULSCAR: u32 = 390_181;
74}
75pub(crate) mod TALENT {
76 pub(crate) const BLIND_FURY: u32 = 203_550;
78 pub(crate) const CHAOTIC_TRANSFORMATION: u32 = 388_112;
80 pub(crate) const CYCLE_OF_HATRED: u32 = 258_887;
82 pub(crate) const DEMONIC: u32 = 213_410;
84 pub(crate) const ESSENCE_BREAK: u32 = 258_860;
86 pub(crate) const ETERNAL_HUNT: u32 = 1_270_898;
88 pub(crate) const ETERNAL_HUNT_RANK_2: u32 = 1_270_900;
90 pub(crate) const EYE_BEAM: u32 = 198_013;
92 pub(crate) const FELBLADE: u32 = 232_893;
94 pub(crate) const FIRST_BLOOD: u32 = 206_416;
96 pub(crate) const FURIOUS_GAZE: u32 = 343_311;
98 pub(crate) const FURIOUS_THROWS: u32 = 393_029;
100 pub(crate) const SCREAMING_BRUTALITY: u32 = 1_220_506;
102 pub(crate) const SERRATED_GLAIVE: u32 = 390_154;
104 pub(crate) const SOULSCAR: u32 = 388_106;
106 pub(crate) const THE_HUNT: u32 = 370_965;
108}
109pub(crate) mod REPORTED_SPELL {
110 pub(crate) const ANNIHILATION_DAMAGE_1: u32 = 227_518;
112 pub(crate) const ANNIHILATION_DAMAGE_2: u32 = 201_428;
114 pub(crate) const AUTO_ATTACK: u32 = 6603;
116 pub(crate) const BLADE_DANCE_DAMAGE: u32 = 199_552;
118 pub(crate) const BLADE_DANCE_DAMAGE_4: u32 = 200_685;
120 pub(crate) const BLADE_DANCE_FIRST_BLOOD: u32 = 391_374;
122 pub(crate) const BLADE_DANCE_FIRST_BLOOD_4: u32 = 391_378;
124 pub(crate) const BURNING_BLADES: u32 = 453_177;
126 pub(crate) const CHAOS_STRIKE_DAMAGE_1: u32 = 222_031;
128 pub(crate) const CHAOS_STRIKE_DAMAGE_2: u32 = 199_547;
130 pub(crate) const DEATH_SWEEP_FIRST_BLOOD: u32 = 393_055;
132 pub(crate) const DEATH_SWEEP_FIRST_BLOOD_4: u32 = 393_054;
134 pub(crate) const DEMON_BLADES: u32 = 203_796;
136 pub(crate) const EYE_BEAM_TICK: u32 = 198_030;
138 pub(crate) const FEL_RUSH_DAMAGE: u32 = 192_611;
140 pub(crate) const FELBLADE_DAMAGE: u32 = 213_243;
142 pub(crate) const IMMOLATION_AURA_DAMAGE: u32 = 258_921;
144 pub(crate) const SOULSCAR: u32 = 390_181;
146 pub(crate) const THE_HUNT_DAMAGE: u32 = 370_966;
148 pub(crate) const THROW_GLAIVE_DAMAGE: u32 = 337_819;
150 pub(crate) const THROW_GLAIVE_FURIOUS_THROWS: u32 = 393_035;
152 pub(crate) const VENGEFUL_RETREAT_DAMAGE: u32 = 198_813;
154}
155pub(crate) mod HERO {
156 pub(crate) mod ALDRACHI_REAVER {
157 pub(crate) mod SPELL {
158 pub(crate) const ART_OF_THE_GLAIVE_DAMAGE: u32 = 444_810;
160 pub(crate) const FURY_OF_THE_ALDRACHI: u32 = 444_806;
162 pub(crate) const REAVERS_GLAIVE: u32 = 442_294;
164 pub(crate) const REAVERS_GLAIVE_HAVOC: u32 = 444_686;
166 pub(crate) const WOUNDED_QUARRY_DAMAGE: u32 = 442_808;
168 }
169 pub(crate) mod AURA {
170 pub(crate) const EVASIVE_ACTION: u32 = 444_929;
172 pub(crate) const GLAIVE_FLURRY: u32 = 442_435;
174 pub(crate) const REAVERS_GLAIVE_BUFF: u32 = 442_294;
176 pub(crate) const REAVERS_MARK: u32 = 442_624;
178 pub(crate) const RENDING_STRIKE: u32 = 442_442;
180 pub(crate) const THRILL_OF_THE_FIGHT: u32 = 442_695;
182 }
183 }
184 pub(crate) mod FEL_SCARRED {
185 pub(crate) mod SPELL {
186 pub(crate) const BURNING_BLADES: u32 = 452_408;
188 pub(crate) const DEMONIC_INTENSITY: u32 = 452_415;
190 pub(crate) const DEMONSURGE: u32 = 452_402;
192 pub(crate) const SPIRIT_BURST: u32 = 452_437;
194 pub(crate) const STUDENT_OF_SUFFERING: u32 = 452_412;
196 pub(crate) const STUDENT_OF_SUFFERING_FURY: u32 = 453_236;
198 pub(crate) const VOLATILE_INSTINCT: u32 = 1_272_453;
200 }
201 pub(crate) mod AURA {
202 pub(crate) const DEMONSURGE: u32 = 452_416;
204 pub(crate) const STUDENT_OF_SUFFERING: u32 = 453_239;
206 }
207 }
208}
209static HERO_TREE_ALDRACHI_REAVER_SPELLS: &[(&str, u32)] = &[
210 ("ART_OF_THE_GLAIVE_DAMAGE", 444_810),
211 ("FURY_OF_THE_ALDRACHI", 444_806),
212 ("REAVERS_GLAIVE", 442_294),
213 ("REAVERS_GLAIVE_HAVOC", 444_686),
214 ("WOUNDED_QUARRY_DAMAGE", 442_808),
215];
216static HERO_TREE_ALDRACHI_REAVER_AURAS: &[(&str, u32)] = &[
217 ("EVASIVE_ACTION", 444_929),
218 ("GLAIVE_FLURRY", 442_435),
219 ("REAVERS_GLAIVE_BUFF", 442_294),
220 ("REAVERS_MARK", 442_624),
221 ("RENDING_STRIKE", 442_442),
222 ("THRILL_OF_THE_FIGHT", 442_695),
223];
224static HERO_TREE_FEL_SCARRED_SPELLS: &[(&str, u32)] = &[
225 ("BURNING_BLADES", 452_408),
226 ("DEMONIC_INTENSITY", 452_415),
227 ("DEMONSURGE", 452_402),
228 ("SPIRIT_BURST", 452_437),
229 ("STUDENT_OF_SUFFERING", 452_412),
230 ("STUDENT_OF_SUFFERING_FURY", 453_236),
231 ("VOLATILE_INSTINCT", 1_272_453),
232];
233static HERO_TREE_FEL_SCARRED_AURAS: &[(&str, u32)] = &[
234 ("DEMONSURGE", 452_416),
235 ("STUDENT_OF_SUFFERING", 453_239),
236];
237pub(crate) const AURA_METAMORPHOSIS: LocalAuraIdx = LocalAuraIdx::new(0);
238pub(crate) const AURA_ESSENCE_BREAK: LocalAuraIdx = LocalAuraIdx::new(1);
239pub(crate) const AURA_INERTIA: LocalAuraIdx = LocalAuraIdx::new(2);
240pub(crate) const AURA_CYCLE_OF_HATRED: LocalAuraIdx = LocalAuraIdx::new(3);
241pub(crate) const AURA_FURIOUS_GAZE: LocalAuraIdx = LocalAuraIdx::new(4);
242pub(crate) const AURA_STUDENT_OF_SUFFERING: LocalAuraIdx = LocalAuraIdx::new(5);
243pub(crate) const AURA_DEMONIC_INTENSITY: LocalAuraIdx = LocalAuraIdx::new(6);
244pub(crate) const AURA_EMPOWERED_EYE_BEAM: LocalAuraIdx = LocalAuraIdx::new(7);
245pub(crate) const AURA_REAVERS_GLAIVE: LocalAuraIdx = LocalAuraIdx::new(8);
246pub(crate) const AURA_EXERGY: LocalAuraIdx = LocalAuraIdx::new(9);
247pub(crate) const AURA_IMMOLATION_AURA: LocalAuraIdx = LocalAuraIdx::new(10);
248pub(crate) const AURA_THE_HUNT_DOT: LocalAuraIdx = LocalAuraIdx::new(11);
249pub(crate) const AURA_BURNING_BLADES: LocalAuraIdx = LocalAuraIdx::new(12);
250pub(crate) const AURA_SERRATED_GLAIVE: LocalAuraIdx = LocalAuraIdx::new(13);
251pub(crate) const AURA_SOULSCAR: LocalAuraIdx = LocalAuraIdx::new(14);
252pub(crate) const AURA_COUNT: usize = 15;
253const _: () = assert!(
254 AURA_COUNT == 15, "AURA index ABI: count changed -- update positional hook consumers"
255);
256pub(crate) const SPELL_CHAOS_STRIKE: LocalSpellIdx = LocalSpellIdx::new(0);
257pub(crate) const SPELL_ANNIHILATION: LocalSpellIdx = LocalSpellIdx::new(1);
258pub(crate) const SPELL_BLADE_DANCE: LocalSpellIdx = LocalSpellIdx::new(2);
259pub(crate) const SPELL_DEATH_SWEEP: LocalSpellIdx = LocalSpellIdx::new(3);
260pub(crate) const SPELL_METAMORPHOSIS: LocalSpellIdx = LocalSpellIdx::new(4);
261pub(crate) const SPELL_THROW_GLAIVE: LocalSpellIdx = LocalSpellIdx::new(5);
262pub(crate) const SPELL_FEL_RUSH: LocalSpellIdx = LocalSpellIdx::new(6);
263pub(crate) const SPELL_VENGEFUL_RETREAT: LocalSpellIdx = LocalSpellIdx::new(7);
264pub(crate) const SPELL_ESSENCE_BREAK: LocalSpellIdx = LocalSpellIdx::new(8);
265pub(crate) const SPELL_FELBLADE: LocalSpellIdx = LocalSpellIdx::new(9);
266pub(crate) const SPELL_EYE_BEAM: LocalSpellIdx = LocalSpellIdx::new(10);
267pub(crate) const SPELL_ABYSSAL_GAZE: LocalSpellIdx = LocalSpellIdx::new(11);
268pub(crate) const SPELL_IMMOLATION_AURA: LocalSpellIdx = LocalSpellIdx::new(12);
269pub(crate) const SPELL_THE_HUNT: LocalSpellIdx = LocalSpellIdx::new(13);
270pub(crate) const SPELL_COUNT: usize = 14;
271const _: () = assert!(
272 SPELL_COUNT == 14,
273 "SPELL index ABI: count changed -- update positional hook consumers"
274);
275pub(crate) mod EFFECT {
276 pub(crate) const BLADE_DANCE_FINAL: (u32, u8) = (200_685, 2);
278 pub(crate) const BLADE_DANCE_HIT: (u32, u8) = (199_552, 2);
280 pub(crate) const BLIND_FURY_FURY: (u32, u8) = (203_550, 3);
282 pub(crate) const BURNING_BLADES_FRACTION: (u32, u8) = (452_408, 1);
284 pub(crate) const CHAOS_STRIKE_FURY_REFUND: (u32, u8) = (193_840, 1);
286 pub(crate) const CHAOS_STRIKE_REFUND_PROC: (u32, u8) = (197_125, 1);
288 pub(crate) const CYCLE_OF_HATRED_CDR: (u32, u8) = (258_887, 1);
290 pub(crate) const DEATH_SWEEP_FIRST_BLOOD_FINAL: (u32, u8) = (393_054, 2);
292 pub(crate) const DEATH_SWEEP_FIRST_BLOOD_HIT: (u32, u8) = (393_055, 2);
294 pub(crate) const DEMONIC_EXTENSION: (u32, u8) = (213_410, 1);
296 pub(crate) const DEMONSURGE_DAMAGE: (u32, u8) = (452_416, 1);
298 pub(crate) const DEMONSURGE_STACK_PCT: (u32, u8) = (452_416, 2);
300 pub(crate) const DEMON_BLADES_DAMAGE: (u32, u8) = (203_796, 1);
302 pub(crate) const DEMON_BLADES_FURY: (u32, u8) = (203_796, 2);
304 pub(crate) const EMPOWERED_EYE_BEAM_DAMAGE: (u32, u8) = (1_287_949, 1);
306 pub(crate) const ETERNAL_HUNT_COOLDOWN_MS: (u32, u8) = (1_270_900, 2);
308 pub(crate) const ETERNAL_HUNT_DAMAGE_PCT: (u32, u8) = (1_270_900, 1);
310 pub(crate) const ETERNAL_HUNT_PERIODIC_PCT: (u32, u8) = (1_270_900, 4);
312 pub(crate) const EYE_BEAM_DAMAGE: (u32, u8) = (198_030, 1);
314 pub(crate) const FIRST_BLOOD_FINAL: (u32, u8) = (391_378, 2);
316 pub(crate) const FIRST_BLOOD_HIT: (u32, u8) = (391_374, 2);
318 pub(crate) const FIRST_BLOOD_MULT: (u32, u8) = (206_416, 1);
320 pub(crate) const FURIOUS_THROWS_DAMAGE: (u32, u8) = (393_035, 1);
322 pub(crate) const HAVOC_AURA_DIRECT: (u32, u8) = (212_612, 1);
324 pub(crate) const MASTERY_DIRECT_BONUS: (u32, u8) = (185_164, 1);
326 pub(crate) const MASTERY_PERIODIC_BONUS: (u32, u8) = (185_164, 2);
328 pub(crate) const SB_FULL_PCT: (u32, u8) = (1_220_506, 3);
330 pub(crate) const SB_SLASH_CHANCE: (u32, u8) = (1_220_506, 2);
332 pub(crate) const SB_SLASH_PCT: (u32, u8) = (1_220_506, 1);
334 pub(crate) const SERRATED_GLAIVE_PCT: (u32, u8) = (390_154, 1);
336 pub(crate) const SOULSCAR_FRACTION: (u32, u8) = (388_106, 1);
338 pub(crate) const THROW_GLAIVE_DAMAGE: (u32, u8) = (337_819, 1);
340}
341static REPORTED_SPELLS: &[(&str, u32)] = &[
342 ("annihilation_damage_1", 227_518),
343 ("annihilation_damage_2", 201_428),
344 ("auto_attack", 6603),
345 ("blade_dance_damage", 199_552),
346 ("blade_dance_damage_4", 200_685),
347 ("blade_dance_first_blood", 391_374),
348 ("blade_dance_first_blood_4", 391_378),
349 ("burning_blades", 453_177),
350 ("chaos_strike_damage_1", 222_031),
351 ("chaos_strike_damage_2", 199_547),
352 ("death_sweep_first_blood", 393_055),
353 ("death_sweep_first_blood_4", 393_054),
354 ("demon_blades", 203_796),
355 ("eye_beam_tick", 198_030),
356 ("fel_rush_damage", 192_611),
357 ("felblade_damage", 213_243),
358 ("immolation_aura_damage", 258_921),
359 ("soulscar", 390_181),
360 ("the_hunt_damage", 370_966),
361 ("throw_glaive_damage", 337_819),
362 ("throw_glaive_furious_throws", 393_035),
363 ("vengeful_retreat_damage", 198_813),
364];
365static DECLARED_SPELL_IDS: &[u32] = &[
366 162_794, 201_427, 188_499, 210_152, 191_427, 185_123, 195_072, 198_793, 258_860,
367 232_893, 198_013, 452_497, 258_920, 370_965, 162_264, 320_338, 427_640, 1_214_887,
368 343_312, 453_239, 452_489, 1_271_144, 442_294, 442_266, 258_920, 370_969, 453_177,
369 390_155, 390_181, 6603, 6603, 6603, 185_164, 192_611, 193_840, 197_125, 198_030,
370 198_813, 199_547, 199_552, 200_685, 201_428, 203_550, 203_796, 206_416, 212_612,
371 213_243, 213_410, 222_031, 227_518, 258_887, 258_920, 258_921, 258_922, 337_819,
372 370_966, 388_106, 390_154, 390_181, 391_374, 391_378, 393_035, 393_054, 393_055,
373 452_408, 452_416, 453_177, 453_236, 453_239, 1_220_506, 1_270_900, 1_287_949,
374 227_518, 201_428, 6603, 199_552, 200_685, 391_374, 391_378, 453_177, 222_031,
375 199_547, 393_055, 393_054, 203_796, 198_030, 192_611, 213_243, 258_921, 390_181,
376 370_966, 337_819, 393_035, 198_813, 444_810, 444_806, 442_294, 444_686, 442_808,
377 444_929, 442_435, 442_294, 442_624, 442_442, 442_695, 452_408, 452_415, 452_402,
378 452_437, 452_412, 453_236, 1_272_453, 452_416, 453_239,
379];
380static DECLARED_AURA_IDS: &[u32] = &[
381 162_264, 320_338, 427_640, 1_214_887, 343_312, 453_239, 452_489, 1_271_144, 442_294,
382 442_266, 258_920, 370_969, 453_177, 390_155, 390_181, 444_929, 442_435, 442_294,
383 442_624, 442_442, 442_695, 452_416, 453_239,
384];
385static TALENTS: &[(&str, u32)] = &[
386 ("BLIND_FURY", TALENT::BLIND_FURY),
387 ("CHAOTIC_TRANSFORMATION", TALENT::CHAOTIC_TRANSFORMATION),
388 ("CYCLE_OF_HATRED", TALENT::CYCLE_OF_HATRED),
389 ("DEMONIC", TALENT::DEMONIC),
390 ("ESSENCE_BREAK", TALENT::ESSENCE_BREAK),
391 ("ETERNAL_HUNT", TALENT::ETERNAL_HUNT),
392 ("ETERNAL_HUNT_RANK_2", TALENT::ETERNAL_HUNT_RANK_2),
393 ("EYE_BEAM", TALENT::EYE_BEAM),
394 ("FELBLADE", TALENT::FELBLADE),
395 ("FIRST_BLOOD", TALENT::FIRST_BLOOD),
396 ("FURIOUS_GAZE", TALENT::FURIOUS_GAZE),
397 ("FURIOUS_THROWS", TALENT::FURIOUS_THROWS),
398 ("SCREAMING_BRUTALITY", TALENT::SCREAMING_BRUTALITY),
399 ("SERRATED_GLAIVE", TALENT::SERRATED_GLAIVE),
400 ("SOULSCAR", TALENT::SOULSCAR),
401 ("THE_HUNT", TALENT::THE_HUNT),
402];
403fn aura_metamorphosis(
404 a: AuraDefinitionDraft,
405 data: &ResolvedGameData,
406) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
407 Ok(
408 a
409 .on_player()
410 .apply_base_from_data(data, 162_264)?
411 .max_stacks(
412 data
413 .require_aura_max_stacks(
414 wowlab_types::sim::SpellIdx::from_raw(162_264),
415 )?,
416 ),
417 )
418}
419fn aura_essence_break(
420 a: AuraDefinitionDraft,
421 data: &ResolvedGameData,
422) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
423 Ok(
424 a
425 .on_target()
426 .apply_base_from_data(data, 320_338)?
427 .max_stacks(
428 data
429 .require_aura_max_stacks(
430 wowlab_types::sim::SpellIdx::from_raw(320_338),
431 )?,
432 ),
433 )
434}
435fn aura_inertia(
436 a: AuraDefinitionDraft,
437 data: &ResolvedGameData,
438) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
439 Ok(
440 a
441 .on_player()
442 .apply_base_from_data(data, 427_640)?
443 .max_stacks(
444 data
445 .require_aura_max_stacks(
446 wowlab_types::sim::SpellIdx::from_raw(427_640),
447 )?,
448 ),
449 )
450}
451fn aura_cycle_of_hatred(
452 a: AuraDefinitionDraft,
453 data: &ResolvedGameData,
454) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
455 Ok(
456 a
457 .on_player()
458 .apply_base_from_data(data, 1_214_887)?
459 .max_stacks(
460 data
461 .require_aura_max_stacks(
462 wowlab_types::sim::SpellIdx::from_raw(1_214_887),
463 )?,
464 ),
465 )
466}
467fn aura_furious_gaze(
468 a: AuraDefinitionDraft,
469 data: &ResolvedGameData,
470) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
471 Ok(
472 a
473 .on_player()
474 .apply_base_from_data(data, 343_312)?
475 .max_stacks(
476 data
477 .require_aura_max_stacks(
478 wowlab_types::sim::SpellIdx::from_raw(343_312),
479 )?,
480 ),
481 )
482}
483fn aura_student_of_suffering(
484 a: AuraDefinitionDraft,
485 data: &ResolvedGameData,
486) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
487 a.on_player()
488 .apply_base_from_data(data, 453_239)?
489 .max_stacks(
490 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(453_239))?,
491 )
492 .periodic_resource(
493 wowlab_engine_combat::validated_milliseconds(
494 data.period(wowlab_types::sim::SpellIdx::from_raw(453_239), 2),
495 453_239,
496 "periodic_resource.tick_ms",
497 )?,
498 data.base_points(wowlab_types::sim::SpellIdx::from_raw(453_236), 1),
499 )
500 .apply_periodic_behavior_from_data(data, 453_239)
501}
502fn aura_demonic_intensity(
503 a: AuraDefinitionDraft,
504 data: &ResolvedGameData,
505) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
506 Ok(
507 a
508 .on_player()
509 .apply_base_from_data(data, 452_489)?
510 .max_stacks(
511 data
512 .require_aura_max_stacks(
513 wowlab_types::sim::SpellIdx::from_raw(452_489),
514 )?,
515 ),
516 )
517}
518fn aura_empowered_eye_beam(
519 a: AuraDefinitionDraft,
520 data: &ResolvedGameData,
521) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
522 Ok(
523 a
524 .on_player()
525 .apply_base_from_data(data, 1_271_144)?
526 .max_stacks(
527 data
528 .require_aura_max_stacks(
529 wowlab_types::sim::SpellIdx::from_raw(1_271_144),
530 )?,
531 ),
532 )
533}
534fn aura_reavers_glaive(
535 a: AuraDefinitionDraft,
536 data: &ResolvedGameData,
537) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
538 Ok(
539 a
540 .on_player()
541 .apply_base_from_data(data, 442_294)?
542 .max_stacks(
543 data
544 .require_aura_max_stacks(
545 wowlab_types::sim::SpellIdx::from_raw(442_294),
546 )?,
547 ),
548 )
549}
550fn aura_exergy(
551 a: AuraDefinitionDraft,
552 data: &ResolvedGameData,
553) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
554 Ok(
555 a
556 .on_player()
557 .apply_base_from_data(data, 442_266)?
558 .max_stacks(
559 data
560 .require_aura_max_stacks(
561 wowlab_types::sim::SpellIdx::from_raw(442_266),
562 )?,
563 ),
564 )
565}
566fn aura_immolation_aura(
567 a: AuraDefinitionDraft,
568 data: &ResolvedGameData,
569) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
570 a.on_player()
571 .apply_base_from_data(data, 258_920)?
572 .max_stacks(
573 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(258_920))?,
574 )
575 .periodic_damage_ap_from_data(
576 data,
577 wowlab_engine_combat::PeriodicDamageDataInput {
578 tick_ms: wowlab_engine_combat::validated_milliseconds(
579 data.period(wowlab_types::sim::SpellIdx::from_raw(258_920), 1),
580 258_920,
581 "periodic_damage.tick_ms",
582 )?,
583 spell_id: 258_922,
584 effect_index: 1,
585 multiplier: 1.0,
586 offset: 0.0,
587 round_decimals: Some(4),
588 },
589 wowlab_engine_combat::PeriodicDamageMode::Standard,
590 )?
591 .periodic_resource(
592 wowlab_engine_combat::validated_milliseconds(
593 data.period(wowlab_types::sim::SpellIdx::from_raw(258_920), 1),
594 258_920,
595 "periodic_resource.tick_ms",
596 )?,
597 data.base_points(wowlab_types::sim::SpellIdx::from_raw(258_922), 2),
598 )
599 .apply_periodic_behavior_from_data(data, 258_920)
600}
601fn aura_the_hunt_dot(
602 a: AuraDefinitionDraft,
603 data: &ResolvedGameData,
604) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
605 Ok(
606 a
607 .on_target()
608 .apply_base_from_data(data, 370_969)?
609 .max_stacks(
610 data
611 .require_aura_max_stacks(
612 wowlab_types::sim::SpellIdx::from_raw(370_969),
613 )?,
614 ),
615 )
616}
617fn aura_burning_blades(
618 a: AuraDefinitionDraft,
619 data: &ResolvedGameData,
620) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
621 a.on_target()
622 .apply_base_from_data(data, 453_177)?
623 .max_stacks(
624 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(453_177))?,
625 )
626 .periodic_residual_damage(
627 wowlab_engine_combat::validated_milliseconds(
628 data.period(wowlab_types::sim::SpellIdx::from_raw(453_177), 1),
629 453_177,
630 "periodic_residual_damage.tick_ms",
631 )?,
632 )
633 .apply_periodic_behavior_from_data(data, 453_177)
634}
635fn aura_serrated_glaive(
636 a: AuraDefinitionDraft,
637 data: &ResolvedGameData,
638) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
639 a.on_target()
640 .apply_base_from_data(data, 390_155)?
641 .max_stacks(
642 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(390_155))?,
643 )
644 .damage_taken_from_caster_spells(
645 data,
646 data.base_points(wowlab_types::sim::SpellIdx::from_raw(390_154), 1),
647 )
648}
649fn aura_soulscar(
650 a: AuraDefinitionDraft,
651 data: &ResolvedGameData,
652) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
653 a.on_target()
654 .apply_base_from_data(data, 390_181)?
655 .max_stacks(
656 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(390_181))?,
657 )
658 .periodic_residual_damage(
659 wowlab_engine_combat::validated_milliseconds(
660 data.period(wowlab_types::sim::SpellIdx::from_raw(390_181), 1),
661 390_181,
662 "periodic_residual_damage.tick_ms",
663 )?,
664 )
665 .apply_periodic_behavior_from_data(data, 390_181)
666}
667fn spell_chaos_strike(
668 s: SpellDefinitionDraft,
669 data: &ResolvedGameData,
670) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
671 Ok(
672 s
673 .apply_base_from_data(data, 162_794)?
674 .apply_aoe_from_data(data)
675 .on_cast(crate::hooks::havoc_demon_hunter::chaos_strike_hook)
676 .overridden_by(SPELL::ANNIHILATION, AURA::METAMORPHOSIS)
677 .override_preserves_aura(),
678 )
679}
680fn spell_annihilation(
681 s: SpellDefinitionDraft,
682 data: &ResolvedGameData,
683) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
684 Ok(
685 s
686 .apply_base_from_data(data, 201_427)?
687 .apply_aoe_from_data(data)
688 .on_cast(crate::hooks::havoc_demon_hunter::annihilation_hook),
689 )
690}
691fn spell_blade_dance(
692 s: SpellDefinitionDraft,
693 data: &ResolvedGameData,
694) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
695 Ok(
696 s
697 .apply_base_from_data(data, 188_499)?
698 .without_data_damage()
699 .apply_aoe_from_data(data)
700 .on_cast(crate::hooks::havoc_demon_hunter::blade_dance_hook)
701 .overridden_by(SPELL::DEATH_SWEEP, AURA::METAMORPHOSIS)
702 .override_shares_base_cooldown()
703 .override_preserves_aura(),
704 )
705}
706fn spell_death_sweep(
707 s: SpellDefinitionDraft,
708 data: &ResolvedGameData,
709) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
710 Ok(
711 s
712 .apply_base_from_data(data, 210_152)?
713 .without_data_damage()
714 .apply_aoe_from_data(data)
715 .on_cast(crate::hooks::havoc_demon_hunter::death_sweep_hook),
716 )
717}
718fn spell_metamorphosis(
719 s: SpellDefinitionDraft,
720 data: &ResolvedGameData,
721) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
722 Ok(
723 s
724 .apply_base_from_data(data, 191_427)?
725 .applies_aura(AURA_METAMORPHOSIS)
726 .apply_aoe_from_data(data)
727 .on_cast(crate::hooks::havoc_demon_hunter::metamorphosis_hook),
728 )
729}
730fn spell_throw_glaive(
731 s: SpellDefinitionDraft,
732 data: &ResolvedGameData,
733) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
734 Ok(
735 s
736 .apply_base_from_data(data, 185_123)?
737 .apply_aoe_from_data(data)
738 .on_cast(crate::hooks::havoc_demon_hunter::throw_glaive_hook),
739 )
740}
741fn spell_fel_rush(
742 s: SpellDefinitionDraft,
743 data: &ResolvedGameData,
744) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
745 Ok(
746 s
747 .apply_base_from_data(data, 195_072)?
748 .damage_ap_from_data(
749 wowlab_engine_domain::dbc::EffectLookup::new(
750 data,
751 wowlab_types::sim::EffectRef::new(
752 wowlab_types::sim::SpellIdx::from_raw(192_611),
753 1,
754 ),
755 ),
756 )?
757 .apply_aoe_from_data(data),
758 )
759}
760fn spell_vengeful_retreat(
761 s: SpellDefinitionDraft,
762 data: &ResolvedGameData,
763) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
764 Ok(s.apply_base_from_data(data, 198_793)?.apply_aoe_from_data(data))
765}
766fn spell_essence_break(
767 s: SpellDefinitionDraft,
768 data: &ResolvedGameData,
769) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
770 Ok(
771 s
772 .apply_base_from_data(data, 258_860)?
773 .applies_aura(AURA_ESSENCE_BREAK)
774 .apply_aoe_from_data(data),
775 )
776}
777fn spell_felblade(
778 s: SpellDefinitionDraft,
779 data: &ResolvedGameData,
780) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
781 Ok(
782 s
783 .apply_base_from_data(data, 232_893)?
784 .gain(data.base_points(wowlab_types::sim::SpellIdx::from_raw(213_243), 3))
785 .damage_ap_from_data(
786 wowlab_engine_domain::dbc::EffectLookup::new(
787 data,
788 wowlab_types::sim::EffectRef::new(
789 wowlab_types::sim::SpellIdx::from_raw(213_243),
790 2,
791 ),
792 ),
793 )?
794 .apply_aoe_from_data(data),
795 )
796}
797fn spell_eye_beam(
798 s: SpellDefinitionDraft,
799 data: &ResolvedGameData,
800) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
801 Ok(
802 s
803 .apply_base_from_data(data, 198_013)?
804 .apply_aoe_from_data(data)
805 .channel(
806 wowlab_engine_combat::validated_channel_tick_count(
807 10.0,
808 198_013,
809 "channel.tick_count",
810 )?,
811 )
812 .on_channel_tick(crate::hooks::havoc_demon_hunter::eye_beam_tick_hook)
813 .on_cast(crate::hooks::havoc_demon_hunter::eye_beam_hook)
814 .overridden_by(SPELL::ABYSSAL_GAZE, AURA::DEMONIC_INTENSITY)
815 .override_shares_base_cooldown(),
816 )
817}
818fn spell_abyssal_gaze(
819 s: SpellDefinitionDraft,
820 data: &ResolvedGameData,
821) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
822 Ok(
823 s
824 .apply_base_from_data(data, 452_497)?
825 .apply_aoe_from_data(data)
826 .channel(
827 wowlab_engine_combat::validated_channel_tick_count(
828 10.0,
829 452_497,
830 "channel.tick_count",
831 )?,
832 )
833 .on_channel_tick(crate::hooks::havoc_demon_hunter::eye_beam_tick_hook)
834 .on_cast(crate::hooks::havoc_demon_hunter::abyssal_gaze_hook),
835 )
836}
837fn spell_immolation_aura(
838 s: SpellDefinitionDraft,
839 data: &ResolvedGameData,
840) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
841 Ok(
842 s
843 .apply_base_from_data(data, 258_920)?
844 .damage_ap_from_data(
845 wowlab_engine_domain::dbc::EffectLookup::new(
846 data,
847 wowlab_types::sim::EffectRef::new(
848 wowlab_types::sim::SpellIdx::from_raw(258_921),
849 1,
850 ),
851 ),
852 )?
853 .applies_inferred_aura(AURA_IMMOLATION_AURA, 258_920)
854 .apply_aoe_from_data(data)
855 .on_cast(crate::hooks::havoc_demon_hunter::consuming_fire_hook),
856 )
857}
858fn spell_the_hunt(
859 s: SpellDefinitionDraft,
860 data: &ResolvedGameData,
861) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
862 Ok(
863 s
864 .apply_base_from_data(data, 370_965)?
865 .damage_ap_from_data(
866 wowlab_engine_domain::dbc::EffectLookup::new(
867 data,
868 wowlab_types::sim::EffectRef::new(
869 wowlab_types::sim::SpellIdx::from_raw(370_966),
870 1,
871 ),
872 ),
873 )?
874 .applies_aura(AURA_THE_HUNT_DOT)
875 .apply_aoe_from_data(data)
876 .on_cast(crate::hooks::havoc_demon_hunter::the_hunt_hook),
877 )
878}
879pub(crate) fn build_combat_system(
880 params: &wowlab_engine_ports::HandlerParams<'_>,
881) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
882 let builder = BuiltCombatSystem::builder(params.stats.clone())
883 .spec_id(SpecId::Havoc)
884 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
885 .mastery_spell(185_164)
886 .mastery_hook(crate::hooks::havoc_demon_hunter::mastery)
887 .resource(
888 "fury",
889 120.0,
890 params
891 .game_data
892 .power_regen_for_max(17, 120.0)
893 .ok_or_else(|| {
894 wowlab_engine_ports::EngineError::spec_construction(
895 "missing power-type power_regen_for_max for type_id 17",
896 )
897 })?,
898 )
899 .resource_type_id(17)
900 .resource_start(
901 params
902 .game_data
903 .power_default(17)
904 .ok_or_else(|| {
905 wowlab_engine_ports::EngineError::spec_construction(
906 "missing power-type power_default for type_id 17",
907 )
908 })?,
909 )
910 .aura(
911 "metamorphosis",
912 AURA::METAMORPHOSIS,
913 |a| aura_metamorphosis(a, ¶ms.game_data),
914 )
915 .aura(
916 "essence_break",
917 AURA::ESSENCE_BREAK,
918 |a| aura_essence_break(a, ¶ms.game_data),
919 )
920 .aura("inertia", AURA::INERTIA, |a| aura_inertia(a, ¶ms.game_data))
921 .aura(
922 "cycle_of_hatred",
923 AURA::CYCLE_OF_HATRED,
924 |a| aura_cycle_of_hatred(a, ¶ms.game_data),
925 )
926 .aura(
927 "furious_gaze",
928 AURA::FURIOUS_GAZE,
929 |a| aura_furious_gaze(a, ¶ms.game_data),
930 )
931 .aura(
932 "student_of_suffering",
933 AURA::STUDENT_OF_SUFFERING,
934 |a| aura_student_of_suffering(a, ¶ms.game_data),
935 )
936 .aura(
937 "demonic_intensity",
938 AURA::DEMONIC_INTENSITY,
939 |a| aura_demonic_intensity(a, ¶ms.game_data),
940 )
941 .aura(
942 "empowered_eye_beam",
943 AURA::EMPOWERED_EYE_BEAM,
944 |a| aura_empowered_eye_beam(a, ¶ms.game_data),
945 )
946 .aura(
947 "reavers_glaive",
948 AURA::REAVERS_GLAIVE,
949 |a| aura_reavers_glaive(a, ¶ms.game_data),
950 )
951 .aura("exergy", AURA::EXERGY, |a| aura_exergy(a, ¶ms.game_data))
952 .aura(
953 "immolation_aura",
954 AURA::IMMOLATION_AURA,
955 |a| aura_immolation_aura(a, ¶ms.game_data),
956 )
957 .aura(
958 "the_hunt_dot",
959 AURA::THE_HUNT_DOT,
960 |a| aura_the_hunt_dot(a, ¶ms.game_data),
961 )
962 .aura(
963 "burning_blades",
964 AURA::BURNING_BLADES,
965 |a| aura_burning_blades(a, ¶ms.game_data),
966 )
967 .aura(
968 "serrated_glaive",
969 AURA::SERRATED_GLAIVE,
970 |a| aura_serrated_glaive(a, ¶ms.game_data),
971 )
972 .aura("soulscar", AURA::SOULSCAR, |a| aura_soulscar(a, ¶ms.game_data))
973 .spell(
974 "chaos_strike",
975 SPELL::CHAOS_STRIKE,
976 |s| spell_chaos_strike(s, ¶ms.game_data),
977 )
978 .spell(
979 "annihilation",
980 SPELL::ANNIHILATION,
981 |s| spell_annihilation(s, ¶ms.game_data),
982 )
983 .spell(
984 "blade_dance",
985 SPELL::BLADE_DANCE,
986 |s| spell_blade_dance(s, ¶ms.game_data),
987 )
988 .spell(
989 "death_sweep",
990 SPELL::DEATH_SWEEP,
991 |s| spell_death_sweep(s, ¶ms.game_data),
992 )
993 .spell(
994 "metamorphosis",
995 SPELL::METAMORPHOSIS,
996 |s| spell_metamorphosis(s, ¶ms.game_data),
997 )
998 .spell(
999 "throw_glaive",
1000 SPELL::THROW_GLAIVE,
1001 |s| spell_throw_glaive(s, ¶ms.game_data),
1002 )
1003 .spell("fel_rush", SPELL::FEL_RUSH, |s| spell_fel_rush(s, ¶ms.game_data))
1004 .spell(
1005 "vengeful_retreat",
1006 SPELL::VENGEFUL_RETREAT,
1007 |s| spell_vengeful_retreat(s, ¶ms.game_data),
1008 )
1009 .spell(
1010 "essence_break",
1011 SPELL::ESSENCE_BREAK,
1012 |s| spell_essence_break(s, ¶ms.game_data),
1013 )
1014 .spell("felblade", SPELL::FELBLADE, |s| spell_felblade(s, ¶ms.game_data))
1015 .spell("eye_beam", SPELL::EYE_BEAM, |s| spell_eye_beam(s, ¶ms.game_data))
1016 .spell(
1017 "abyssal_gaze",
1018 SPELL::ABYSSAL_GAZE,
1019 |s| spell_abyssal_gaze(s, ¶ms.game_data),
1020 )
1021 .spell(
1022 "immolation_aura",
1023 SPELL::IMMOLATION_AURA,
1024 |s| spell_immolation_aura(s, ¶ms.game_data),
1025 )
1026 .spell("the_hunt", SPELL::THE_HUNT, |s| spell_the_hunt(s, ¶ms.game_data))
1027 .auto_attack(|a| {
1028 a
1029 .spell_id(6603)
1030 .swing_ms(2600)
1031 .apply_base_from_data(¶ms.game_data)
1032 .on_swing(crate::hooks::havoc_demon_hunter::swing_hook)
1033 })
1034 .auto_attack(|a| {
1035 a
1036 .spell_id(6603)
1037 .swing_ms(2600)
1038 .apply_base_from_data(¶ms.game_data)
1039 .on_swing(crate::hooks::havoc_demon_hunter::swing_hook)
1040 })
1041 .game_data(params.game_data.clone())
1042 .talent_spell_ids(TALENTS)
1043 .talent_selections(params.talent_selections)
1044 .set_bonus_auras(params.set_bonus_auras)
1045 .encounter(params.encounter.clone());
1046 let builder = crate::generated::items::register_equipped(
1047 builder,
1048 ¶ms.game_data,
1049 params.equipped_items,
1050 )?;
1051
1052 builder
1053 .build(params.rotation)
1054 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
1055}
1056fn new_handler(
1057 params: wowlab_engine_ports::HandlerParams<'_>,
1058) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
1059 let handler = crate::composition::compose_handler(
1060 build_combat_system,
1061 ¶ms,
1062 |parts, params| Ok(
1063 crate::hooks::havoc_demon_hunter::HavocHandler::new(parts, params),
1064 ),
1065 )?;
1066
1067 Ok(Box::new(handler))
1068}
1069static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
1070 primary_resource: ResourceType::Fury,
1071 secondary_resource: None,
1072 spell_ids: DECLARED_SPELL_IDS,
1073 aura_ids: DECLARED_AURA_IDS,
1074 server_proc_entries: &[],
1075 talents: TALENTS,
1076 rotation_field_schema: &[],
1077 metrics_plugin_id: None,
1078 hero_talent_trees: &[
1079 HeroTalentTreeDesc {
1080 name: "Aldrachi Reaver",
1081 spells: HERO_TREE_ALDRACHI_REAVER_SPELLS,
1082 auras: HERO_TREE_ALDRACHI_REAVER_AURAS,
1083 },
1084 HeroTalentTreeDesc {
1085 name: "Fel Scarred",
1086 spells: HERO_TREE_FEL_SCARRED_SPELLS,
1087 auras: HERO_TREE_FEL_SCARRED_AURAS,
1088 },
1089 ],
1090 reported_spells: REPORTED_SPELLS,
1091 masked_passive_effects: &[],
1092 passive_effect_overrides: &[
1093 wowlab_engine_ports::DeclaredPassiveEffectOverride {
1094 spell_id: 388_118,
1095 effect: 2,
1096 source: Some((388_118, 2)),
1097 value: 0.0,
1098 multiplier: 0.5,
1099 },
1100 ],
1101};
1102pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1103 spec_id: SpecId::Havoc,
1104 display_name: "Havoc Demon Hunter",
1105 metadata: DECLARED_METADATA,
1106 handler_factory: new_handler,
1107};
1108wowlab_engine_combat::smoke_test!(build_combat_system);