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 FESTERING_STRIKE: u32 = 85948;
16 pub(crate) const SCOURGE_STRIKE: u32 = 55090;
18 pub(crate) const DEATH_COIL: u32 = 47541;
20 pub(crate) const EPIDEMIC: u32 = 212_739;
22 pub(crate) const SOUL_REAPER: u32 = 343_294;
24 pub(crate) const OUTBREAK: u32 = 77575;
26 pub(crate) const GARGOYLE_STRIKE: u32 = 51963;
28 pub(crate) const RAISE_DEAD: u32 = 46584;
30 pub(crate) const DARK_TRANSFORMATION: u32 = 1_233_448;
32 pub(crate) const ARMY_OF_THE_DEAD: u32 = 42650;
34 pub(crate) const PUTREFY: u32 = 1_247_378;
36}
37pub(crate) mod AURA {
38 pub(crate) const SUDDEN_DOOM: u32 = 81340;
40 pub(crate) const RUNIC_CORRUPTION: u32 = 51460;
42 pub(crate) const FESTERING_SCYTHE: u32 = 458_123;
44 pub(crate) const FESTERING_SCYTHE_TT: u32 = 1_241_077;
46 pub(crate) const COIL_OF_DEVASTATION: u32 = 390_271;
48 pub(crate) const APOCALYPSE_DEATH: u32 = 327_095;
50 pub(crate) const APOCALYPSE_FAMINE: u32 = 327_092;
52 pub(crate) const APOCALYPSE_PESTILENCE: u32 = 327_093;
54 pub(crate) const APOCALYPSE_WAR: u32 = 327_096;
56 pub(crate) const SOUL_REAPER: u32 = 1_241_521;
58 pub(crate) const MOGRAINES_MIGHT: u32 = 444_505;
60 pub(crate) const APOCALYPTIC_CONQUEST: u32 = 444_763;
62 pub(crate) const TROLLBANE_CHAINS: u32 = 444_828;
64 pub(crate) const UNDEATH: u32 = 444_633;
66 pub(crate) const REAPING: u32 = 1_242_654;
68 pub(crate) const FORBIDDEN_KNOWLEDGE: u32 = 1_242_223;
70 pub(crate) const BLIGHTFALL: u32 = 1_271_975;
72 pub(crate) const ICY_TALONS: u32 = 194_879;
74 pub(crate) const RUNE_MASTERY: u32 = 374_585;
76 pub(crate) const BRITTLE: u32 = 374_557;
78 pub(crate) const VIRULENT_PLAGUE: u32 = 191_587;
80 pub(crate) const DREAD_PLAGUE: u32 = 1_240_996;
82 pub(crate) const INFECTED_CLAWS: u32 = 1_241_786;
84 pub(crate) const LESSER_GHOUL: u32 = 1_254_252;
86 pub(crate) const LESSER_GHOUL_COUNTER: u32 = 1_242_998;
88 pub(crate) const UNHOLY_DEVOTION: u32 = 1_270_491;
90 pub(crate) const GARGOYLE_ACTIVE: u32 = 49206;
92 pub(crate) const GARGOYLE_EMPOWERMENT: u32 = 211_947;
94 pub(crate) const DARK_TRANSFORMATION: u32 = 1_235_391;
96 pub(crate) const COMMANDER_OF_THE_DEAD: u32 = 390_260;
98}
99pub(crate) mod TALENT {
100 pub(crate) const APOCALYPSE_NOW: u32 = 444_040;
102 pub(crate) const ARMY_OF_THE_DEAD: u32 = 42650;
104 pub(crate) const BLIGHTBURST: u32 = 1_254_552;
106 pub(crate) const BRITTLE: u32 = 374_504;
108 pub(crate) const DARK_TRANSFORMATION: u32 = 1_233_448;
110 pub(crate) const FORBIDDEN_KNOWLEDGE_2: u32 = 1_256_565;
112 pub(crate) const FORBIDDEN_KNOWLEDGE_3: u32 = 1_256_566;
114 pub(crate) const FURY_OF_THE_HORSEMEN: u32 = 444_069;
116 pub(crate) const GRAVE_MASTERY: u32 = 1_238_900;
118 pub(crate) const LET_TERROR_REIGN: u32 = 1_265_949;
120 pub(crate) const NAZGRIMS_CONQUEST: u32 = 444_052;
122 pub(crate) const OUTBREAK: u32 = 77575;
124 pub(crate) const PUTREFY: u32 = 1_247_378;
126 pub(crate) const RIDERS_CHAMPION: u32 = 444_005;
128 pub(crate) const SCOURGE_STRIKE: u32 = 55090;
130 pub(crate) const SOUL_REAPER: u32 = 343_294;
132 pub(crate) const SUMMON_GARGOYLE: u32 = 1_242_147;
134 pub(crate) const TROLLBANES_ICY_FURY: u32 = 444_097;
136 pub(crate) const UNHOLY_DEVOTION: u32 = 1_241_858;
138 pub(crate) const WHITEMANES_FAMINE: u32 = 444_033;
140}
141pub(crate) mod REPORTED_SPELL {
142 pub(crate) const AUTO_ATTACK: u32 = 6603;
144 pub(crate) const CLAW: u32 = 199_373;
146 pub(crate) const DEATH_AND_DECAY: u32 = 1_251_951;
148 pub(crate) const DEATH_COIL_LET_TERROR_REIGN: u32 = 1_265_949;
150 pub(crate) const DREAD_PLAGUE_ERUPT: u32 = 1_241_171;
152 pub(crate) const FESTERING_SCYTHE: u32 = 458_128;
154 pub(crate) const HEART_STRIKE: u32 = 445_504;
156 pub(crate) const NECROTIC_COIL_SHADOW: u32 = 1_242_178;
158 pub(crate) const OBLITERATE: u32 = 445_507;
160 pub(crate) const PESTILENCE_RUNEFORGE: u32 = 327_093;
162 pub(crate) const SCOURGE_STRIKE_SHADOW: u32 = 445_509;
164 pub(crate) const SWEEPING_CLAWS: u32 = 1_278_150;
166 pub(crate) const TROLLBANES_ICY_FURY: u32 = 444_834;
168 pub(crate) const UNDEATH: u32 = 444_633;
170 pub(crate) const VIRULENT_PLAGUE_ERUPT: u32 = 1_241_167;
172 pub(crate) const WHITEMANE_DEATH_COIL: u32 = 445_513;
174}
175pub(crate) mod HERO {
176 pub(crate) mod UNHOLY_PETS {
177 pub(crate) mod SPELL {
178 pub(crate) const CLAW: u32 = 91776;
180 pub(crate) const COIL_OF_DEVASTATION: u32 = 390_271;
182 pub(crate) const FESTERING_SCYTHE_STRIKE: u32 = 458_128;
184 pub(crate) const FROSTBOLT: u32 = 317_792;
186 pub(crate) const GARGOYLE_STRIKE: u32 = 51963;
188 pub(crate) const LESSER_CLAW: u32 = 199_373;
190 pub(crate) const LESSER_SWEEPING_CLAWS: u32 = 1_278_150;
192 pub(crate) const MELEE: u32 = 6603;
194 pub(crate) const NECROTIC_COIL: u32 = 1_242_178;
196 pub(crate) const NECROTIC_COIL_SHADOWSTRIKE: u32 = 1_242_172;
198 pub(crate) const PUTREFY_AOE: u32 = 390_220;
200 pub(crate) const PUTREFY_ST: u32 = 1_277_016;
202 pub(crate) const RUPTURED_VISCERA: u32 = 1_247_379;
204 pub(crate) const SHADOW_BOLT: u32 = 317_791;
206 pub(crate) const SUMMON_GARGOYLE: u32 = 49206;
208 pub(crate) const SWEEPING_CLAWS: u32 = 91778;
210 }
211 }
212 pub(crate) mod RIDERS {
213 pub(crate) mod SPELL {
214 pub(crate) const MOGRAINE: u32 = 444_248;
216 pub(crate) const NAZGRIM: u32 = 444_252;
218 pub(crate) const RANDOM_SUMMON: u32 = 454_389;
220 pub(crate) const TROLLBANE: u32 = 444_254;
222 pub(crate) const WHITEMANE: u32 = 444_251;
224 }
225 }
226}
227static HERO_TREE_UNHOLY_PETS_SPELLS: &[(&str, u32)] = &[
228 ("CLAW", 91776),
229 ("COIL_OF_DEVASTATION", 390_271),
230 ("FESTERING_SCYTHE_STRIKE", 458_128),
231 ("FROSTBOLT", 317_792),
232 ("GARGOYLE_STRIKE", 51963),
233 ("LESSER_CLAW", 199_373),
234 ("LESSER_SWEEPING_CLAWS", 1_278_150),
235 ("MELEE", 6603),
236 ("NECROTIC_COIL", 1_242_178),
237 ("NECROTIC_COIL_SHADOWSTRIKE", 1_242_172),
238 ("PUTREFY_AOE", 390_220),
239 ("PUTREFY_ST", 1_277_016),
240 ("RUPTURED_VISCERA", 1_247_379),
241 ("SHADOW_BOLT", 317_791),
242 ("SUMMON_GARGOYLE", 49206),
243 ("SWEEPING_CLAWS", 91778),
244];
245static HERO_TREE_UNHOLY_PETS_AURAS: &[(&str, u32)] = &[];
246static HERO_TREE_RIDERS_SPELLS: &[(&str, u32)] = &[
247 ("MOGRAINE", 444_248),
248 ("NAZGRIM", 444_252),
249 ("RANDOM_SUMMON", 454_389),
250 ("TROLLBANE", 444_254),
251 ("WHITEMANE", 444_251),
252];
253static HERO_TREE_RIDERS_AURAS: &[(&str, u32)] = &[];
254pub(crate) const AURA_SUDDEN_DOOM: LocalAuraIdx = LocalAuraIdx::new(0);
255pub(crate) const AURA_RUNIC_CORRUPTION: LocalAuraIdx = LocalAuraIdx::new(1);
256pub(crate) const AURA_FESTERING_SCYTHE: LocalAuraIdx = LocalAuraIdx::new(2);
257pub(crate) const AURA_FESTERING_SCYTHE_TT: LocalAuraIdx = LocalAuraIdx::new(3);
258pub(crate) const AURA_COIL_OF_DEVASTATION: LocalAuraIdx = LocalAuraIdx::new(4);
259pub(crate) const AURA_APOCALYPSE_DEATH: LocalAuraIdx = LocalAuraIdx::new(5);
260pub(crate) const AURA_APOCALYPSE_FAMINE: LocalAuraIdx = LocalAuraIdx::new(6);
261pub(crate) const AURA_APOCALYPSE_PESTILENCE: LocalAuraIdx = LocalAuraIdx::new(7);
262pub(crate) const AURA_APOCALYPSE_WAR: LocalAuraIdx = LocalAuraIdx::new(8);
263pub(crate) const AURA_SOUL_REAPER: LocalAuraIdx = LocalAuraIdx::new(9);
264pub(crate) const AURA_MOGRAINES_MIGHT: LocalAuraIdx = LocalAuraIdx::new(10);
265pub(crate) const AURA_APOCALYPTIC_CONQUEST: LocalAuraIdx = LocalAuraIdx::new(11);
266pub(crate) const AURA_TROLLBANE_CHAINS: LocalAuraIdx = LocalAuraIdx::new(12);
267pub(crate) const AURA_UNDEATH: LocalAuraIdx = LocalAuraIdx::new(13);
268pub(crate) const AURA_REAPING: LocalAuraIdx = LocalAuraIdx::new(14);
269pub(crate) const AURA_FORBIDDEN_KNOWLEDGE: LocalAuraIdx = LocalAuraIdx::new(15);
270pub(crate) const AURA_BLIGHTFALL: LocalAuraIdx = LocalAuraIdx::new(16);
271pub(crate) const AURA_ICY_TALONS: LocalAuraIdx = LocalAuraIdx::new(17);
272pub(crate) const AURA_RUNE_MASTERY: LocalAuraIdx = LocalAuraIdx::new(18);
273pub(crate) const AURA_BRITTLE: LocalAuraIdx = LocalAuraIdx::new(19);
274pub(crate) const AURA_VIRULENT_PLAGUE: LocalAuraIdx = LocalAuraIdx::new(20);
275pub(crate) const AURA_DREAD_PLAGUE: LocalAuraIdx = LocalAuraIdx::new(21);
276pub(crate) const AURA_INFECTED_CLAWS: LocalAuraIdx = LocalAuraIdx::new(22);
277pub(crate) const AURA_LESSER_GHOUL: LocalAuraIdx = LocalAuraIdx::new(23);
278pub(crate) const AURA_LESSER_GHOUL_COUNTER: LocalAuraIdx = LocalAuraIdx::new(24);
279pub(crate) const AURA_UNHOLY_DEVOTION: LocalAuraIdx = LocalAuraIdx::new(25);
280pub(crate) const AURA_GARGOYLE_ACTIVE: LocalAuraIdx = LocalAuraIdx::new(26);
281pub(crate) const AURA_GARGOYLE_EMPOWERMENT: LocalAuraIdx = LocalAuraIdx::new(27);
282pub(crate) const AURA_DARK_TRANSFORMATION: LocalAuraIdx = LocalAuraIdx::new(28);
283pub(crate) const AURA_COMMANDER_OF_THE_DEAD: LocalAuraIdx = LocalAuraIdx::new(29);
284pub(crate) const AURA_COUNT: usize = 30;
285const _: () = assert!(
286 AURA_COUNT == 30, "AURA index ABI: count changed -- update positional hook consumers"
287);
288pub(crate) const SPELL_FESTERING_STRIKE: LocalSpellIdx = LocalSpellIdx::new(0);
289pub(crate) const SPELL_SCOURGE_STRIKE: LocalSpellIdx = LocalSpellIdx::new(1);
290pub(crate) const SPELL_DEATH_COIL: LocalSpellIdx = LocalSpellIdx::new(2);
291pub(crate) const SPELL_EPIDEMIC: LocalSpellIdx = LocalSpellIdx::new(3);
292pub(crate) const SPELL_SOUL_REAPER: LocalSpellIdx = LocalSpellIdx::new(4);
293pub(crate) const SPELL_OUTBREAK: LocalSpellIdx = LocalSpellIdx::new(5);
294pub(crate) const SPELL_GARGOYLE_STRIKE: LocalSpellIdx = LocalSpellIdx::new(6);
295pub(crate) const SPELL_RAISE_DEAD: LocalSpellIdx = LocalSpellIdx::new(7);
296pub(crate) const SPELL_DARK_TRANSFORMATION: LocalSpellIdx = LocalSpellIdx::new(8);
297pub(crate) const SPELL_ARMY_OF_THE_DEAD: LocalSpellIdx = LocalSpellIdx::new(9);
298pub(crate) const SPELL_PUTREFY: LocalSpellIdx = LocalSpellIdx::new(10);
299pub(crate) const SPELL_COUNT: usize = 11;
300const _: () = assert!(
301 SPELL_COUNT == 11,
302 "SPELL index ABI: count changed -- update positional hook consumers"
303);
304pub(crate) mod EFFECT {
305 pub(crate) const APOCALYPSE_NOW_DURATION: (u32, u8) = (444_040, 2);
307 pub(crate) const ARMY_GHOUL_COUNT: (u32, u8) = (42650, 1);
309 pub(crate) const BLIGHTBURST_DAMAGE: (u32, u8) = (1_254_552, 2);
311 pub(crate) const BLIGHTBURST_DURATION: (u32, u8) = (1_254_552, 1);
313 pub(crate) const CLAW: (u32, u8) = (91776, 1);
315 pub(crate) const COIL_OF_DEVASTATION_PCT: (u32, u8) = (390_270, 1);
317 pub(crate) const COMMANDER_PCT: (u32, u8) = (390_260, 3);
319 pub(crate) const DC_DAMAGE: (u32, u8) = (47632, 1);
321 pub(crate) const DP_TICK: (u32, u8) = (1_240_996, 1);
323 pub(crate) const DREADBLADE_PET_CRIT: (u32, u8) = (77515, 6);
325 pub(crate) const DT_ENERGY_PCT: (u32, u8) = (1_233_448, 4);
327 pub(crate) const DT_PET_PCT: (u32, u8) = (1_233_448, 1);
329 pub(crate) const ETERNAL_AGONY_EXTENSION_MS: (u32, u8) = (390_268, 1);
331 pub(crate) const EBON_FEVER_PCT: (u32, u8) = (207_269, 1);
333 pub(crate) const FS_GHOULS_MAX: (u32, u8) = (85948, 4);
335 pub(crate) const FS_GHOULS_MIN: (u32, u8) = (85948, 3);
337 pub(crate) const FORBIDDEN_KNOWLEDGE_READY_STACKS: (u32, u8) = (1_256_565, 2);
339 pub(crate) const FORBIDDEN_KNOWLEDGE_PUTREFY_PCT: (u32, u8) = (1_256_566, 2);
341 pub(crate) const FORBIDDEN_KNOWLEDGE_PROC_PCT: (u32, u8) = (1_256_566, 3);
343 pub(crate) const FURY_EXTENSION_SECONDS: (u32, u8) = (444_069, 3);
345 pub(crate) const FURY_MAX_SECONDS: (u32, u8) = (444_069, 2);
347 pub(crate) const FURY_RP_THRESHOLD: (u32, u8) = (444_069, 1);
349 pub(crate) const GARGOYLE_STRIKE: (u32, u8) = (51963, 1);
351 pub(crate) const GRAVE_MASTERY_PCT: (u32, u8) = (1_238_902, 1);
353 pub(crate) const HARBINGER_SD_PCT: (u32, u8) = (276_023, 2);
355 pub(crate) const ICY_FURY_DAMAGE: (u32, u8) = (444_834, 2);
357 pub(crate) const LESSER_CLAW: (u32, u8) = (199_373, 1);
359 pub(crate) const LESSER_SWEEPING: (u32, u8) = (1_278_150, 2);
361 pub(crate) const MAGUS_FROSTBOLT: (u32, u8) = (317_792, 1);
363 pub(crate) const MAGUS_SHADOW_BOLT: (u32, u8) = (317_791, 1);
365 pub(crate) const LET_TERROR_REIGN_UNHOLY_PCT: (u32, u8) = (1_265_949, 2);
367 pub(crate) const MOGRAINE_DND: (u32, u8) = (1_251_951, 1);
369 pub(crate) const MOGRAINE_DND_PERIOD: (u32, u8) = (444_474, 3);
371 pub(crate) const MOGRAINE_HEART_STRIKE: (u32, u8) = (445_504, 1);
373 pub(crate) const NAZGRIM_CONQUEST_BASE_PCT: (u32, u8) = (444_763, 1);
375 pub(crate) const NAZGRIM_SCOURGE_STRIKE: (u32, u8) = (445_508, 1);
377 pub(crate) const NAZGRIM_SCOURGE_STRIKE_SHADOW: (u32, u8) = (445_509, 1);
379 pub(crate) const NECROTIC_SHADOW: (u32, u8) = (1_242_178, 1);
381 pub(crate) const NECROTIC_STRIKE: (u32, u8) = (1_242_172, 1);
383 pub(crate) const PUTREFY_AOE: (u32, u8) = (390_220, 1);
385 pub(crate) const PUTREFY_ST: (u32, u8) = (1_277_016, 1);
387 pub(crate) const RC_CHANCE_PCT: (u32, u8) = (51462, 1);
389 pub(crate) const RUPTURED_VISCERA: (u32, u8) = (1_247_379, 1);
391 pub(crate) const SCYTHE_DAMAGE: (u32, u8) = (458_128, 1);
393 pub(crate) const SD_DC_PCT: (u32, u8) = (81340, 4);
395 pub(crate) const SR_GHOULS: (u32, u8) = (343_294, 3);
397 pub(crate) const SS_ERUPT_PCT: (u32, u8) = (55090, 2);
399 pub(crate) const SUDDEN_DOOM_PCT: (u32, u8) = (49530, 2);
401 pub(crate) const SWEEPING_MAIN: (u32, u8) = (91778, 2);
403 pub(crate) const TROLLBANE_OBLITERATE: (u32, u8) = (445_507, 1);
405 pub(crate) const VP_TICK: (u32, u8) = (191_587, 1);
407 pub(crate) const WHITEMANE_DEATH_COIL: (u32, u8) = (445_513, 1);
409}
410static REPORTED_SPELLS: &[(&str, u32)] = &[
411 ("auto_attack", 6603),
412 ("claw", 199_373),
413 ("death_and_decay", 1_251_951),
414 ("death_coil_let_terror_reign", 1_265_949),
415 ("dread_plague_erupt", 1_241_171),
416 ("festering_scythe", 458_128),
417 ("heart_strike", 445_504),
418 ("necrotic_coil_shadow", 1_242_178),
419 ("obliterate", 445_507),
420 ("pestilence_runeforge", 327_093),
421 ("scourge_strike_shadow", 445_509),
422 ("sweeping_claws", 1_278_150),
423 ("trollbanes_icy_fury", 444_834),
424 ("undeath", 444_633),
425 ("virulent_plague_erupt", 1_241_167),
426 ("whitemane_death_coil", 445_513),
427];
428static DECLARED_SPELL_IDS: &[u32] = &[
429 85948, 55090, 47541, 212_739, 343_294, 77575, 51963, 46584, 1_233_448, 42650,
430 1_247_378, 81340, 51460, 458_123, 1_241_077, 390_271, 327_095, 327_092, 327_093,
431 327_096, 1_241_521, 444_505, 444_763, 444_828, 444_633, 1_242_654, 1_242_223,
432 1_271_975, 194_879, 374_585, 374_557, 191_587, 1_240_996, 1_241_786, 1_254_252,
433 1_242_998, 1_270_491, 49206, 211_947, 1_235_391, 390_260, 6603, 6603, 6603, 42650,
434 47632, 49530, 51462, 51963, 55090, 77515, 81340, 85948, 91776, 91778, 191_587,
435 199_373, 207_269, 211_947, 276_023, 317_791, 317_792, 327_093, 343_294, 374_585,
436 390_220, 390_260, 390_268, 390_270, 390_271, 444_040, 444_052, 444_069, 444_474,
437 444_633, 444_763, 444_834, 445_504, 445_507, 445_508, 445_509, 445_513, 458_128,
438 1_233_448, 1_238_902, 1_240_996, 1_241_167, 1_241_171, 1_242_172, 1_242_178,
439 1_247_379, 1_251_951, 1_254_552, 1_256_565, 1_256_566, 1_265_949, 1_277_016,
440 1_278_150, 6603, 199_373, 1_251_951, 1_265_949, 1_241_171, 458_128, 445_504,
441 1_242_178, 445_507, 327_093, 445_509, 1_278_150, 444_834, 444_633, 1_241_167,
442 445_513, 91776, 390_271, 458_128, 317_792, 51963, 199_373, 1_278_150, 6603,
443 1_242_178, 1_242_172, 390_220, 1_277_016, 1_247_379, 317_791, 49206, 91778, 444_248,
444 444_252, 454_389, 444_254, 444_251,
445];
446static DECLARED_AURA_IDS: &[u32] = &[
447 81340, 51460, 458_123, 1_241_077, 390_271, 327_095, 327_092, 327_093, 327_096,
448 1_241_521, 444_505, 444_763, 444_828, 444_633, 1_242_654, 1_242_223, 1_271_975,
449 194_879, 374_585, 374_557, 191_587, 1_240_996, 1_241_786, 1_254_252, 1_242_998,
450 1_270_491, 49206, 211_947, 1_235_391, 390_260,
451];
452static TALENTS: &[(&str, u32)] = &[
453 ("APOCALYPSE_NOW", TALENT::APOCALYPSE_NOW),
454 ("ARMY_OF_THE_DEAD", TALENT::ARMY_OF_THE_DEAD),
455 ("BLIGHTBURST", TALENT::BLIGHTBURST),
456 ("BRITTLE", TALENT::BRITTLE),
457 ("DARK_TRANSFORMATION", TALENT::DARK_TRANSFORMATION),
458 ("FORBIDDEN_KNOWLEDGE_2", TALENT::FORBIDDEN_KNOWLEDGE_2),
459 ("FORBIDDEN_KNOWLEDGE_3", TALENT::FORBIDDEN_KNOWLEDGE_3),
460 ("FURY_OF_THE_HORSEMEN", TALENT::FURY_OF_THE_HORSEMEN),
461 ("GRAVE_MASTERY", TALENT::GRAVE_MASTERY),
462 ("LET_TERROR_REIGN", TALENT::LET_TERROR_REIGN),
463 ("NAZGRIMS_CONQUEST", TALENT::NAZGRIMS_CONQUEST),
464 ("OUTBREAK", TALENT::OUTBREAK),
465 ("PUTREFY", TALENT::PUTREFY),
466 ("RIDERS_CHAMPION", TALENT::RIDERS_CHAMPION),
467 ("SCOURGE_STRIKE", TALENT::SCOURGE_STRIKE),
468 ("SOUL_REAPER", TALENT::SOUL_REAPER),
469 ("SUMMON_GARGOYLE", TALENT::SUMMON_GARGOYLE),
470 ("TROLLBANES_ICY_FURY", TALENT::TROLLBANES_ICY_FURY),
471 ("UNHOLY_DEVOTION", TALENT::UNHOLY_DEVOTION),
472 ("WHITEMANES_FAMINE", TALENT::WHITEMANES_FAMINE),
473];
474pub(crate) mod UNHOLY_DK {
475 pub(crate) const FESTERING_STRIKE_CASTS: u32 = 0;
476 pub(crate) const SCOURGE_STRIKE_CASTS: u32 = 1;
477 pub(crate) const DEATH_COIL_CASTS: u32 = 2;
478 pub(crate) const DARK_TRANSFORMATION_CASTS: u32 = 3;
479 pub(crate) const ARMY_OF_THE_DEAD_CASTS: u32 = 4;
480 pub(crate) const SOUL_REAPER_CASTS: u32 = 5;
481 pub(crate) const PUTREFY_CASTS: u32 = 6;
482 pub(crate) const DARK_TRANSFORMATION_UPTIME_MS: u32 = 7;
483 pub(crate) const VIRULENT_PLAGUE_UPTIME_MS: u32 = 8;
484}
485fn aura_sudden_doom(
486 a: AuraDefinitionDraft,
487 data: &ResolvedGameData,
488) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
489 Ok(
490 a
491 .on_player()
492 .apply_base_from_data(data, 81340)?
493 .max_stacks(
494 data
495 .require_aura_max_stacks(
496 wowlab_types::sim::SpellIdx::from_raw(81340),
497 )?,
498 ),
499 )
500}
501fn aura_runic_corruption(
502 a: AuraDefinitionDraft,
503 data: &ResolvedGameData,
504) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
505 Ok(
506 a
507 .on_player()
508 .apply_base_from_data(data, 51460)?
509 .max_stacks(
510 data
511 .require_aura_max_stacks(
512 wowlab_types::sim::SpellIdx::from_raw(51460),
513 )?,
514 ),
515 )
516}
517fn aura_festering_scythe(
518 a: AuraDefinitionDraft,
519 data: &ResolvedGameData,
520) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
521 Ok(
522 a
523 .on_player()
524 .apply_base_from_data(data, 458_123)?
525 .max_stacks(
526 data
527 .require_aura_max_stacks(
528 wowlab_types::sim::SpellIdx::from_raw(458_123),
529 )?,
530 ),
531 )
532}
533fn aura_festering_scythe_tt(
534 a: AuraDefinitionDraft,
535 data: &ResolvedGameData,
536) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
537 Ok(
538 a
539 .on_player()
540 .apply_base_from_data(data, 1_241_077)?
541 .max_stacks(
542 data
543 .require_aura_max_stacks(
544 wowlab_types::sim::SpellIdx::from_raw(1_241_077),
545 )?,
546 ),
547 )
548}
549fn aura_coil_of_devastation(
550 a: AuraDefinitionDraft,
551 data: &ResolvedGameData,
552) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
553 a.on_target()
554 .apply_base_from_data(data, 390_271)?
555 .max_stacks(
556 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(390_271))?,
557 )
558 .periodic_residual_damage(
559 wowlab_engine_combat::validated_milliseconds(
560 data.period(wowlab_types::sim::SpellIdx::from_raw(390_271), 1),
561 390_271,
562 "periodic_residual_damage.tick_ms",
563 )?,
564 )
565 .apply_periodic_behavior_from_data(data, 390_271)
566}
567fn aura_apocalypse_death(
568 a: AuraDefinitionDraft,
569 data: &ResolvedGameData,
570) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
571 Ok(
572 a
573 .on_target()
574 .apply_base_from_data(data, 327_095)?
575 .max_stacks(
576 data
577 .require_aura_max_stacks(
578 wowlab_types::sim::SpellIdx::from_raw(327_095),
579 )?,
580 ),
581 )
582}
583fn aura_apocalypse_famine(
584 a: AuraDefinitionDraft,
585 data: &ResolvedGameData,
586) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
587 Ok(
588 a
589 .on_target()
590 .apply_base_from_data(data, 327_092)?
591 .max_stacks(
592 data
593 .require_aura_max_stacks(
594 wowlab_types::sim::SpellIdx::from_raw(327_092),
595 )?,
596 ),
597 )
598}
599fn aura_apocalypse_pestilence(
600 a: AuraDefinitionDraft,
601 data: &ResolvedGameData,
602) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
603 Ok(
604 a
605 .on_target()
606 .apply_base_from_data(data, 327_093)?
607 .max_stacks(
608 data
609 .require_aura_max_stacks(
610 wowlab_types::sim::SpellIdx::from_raw(327_093),
611 )?,
612 )
613 .async_stacks(),
614 )
615}
616fn aura_apocalypse_war(
617 a: AuraDefinitionDraft,
618 data: &ResolvedGameData,
619) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
620 Ok(
621 a
622 .on_target()
623 .apply_base_from_data(data, 327_096)?
624 .max_stacks(
625 data
626 .require_aura_max_stacks(
627 wowlab_types::sim::SpellIdx::from_raw(327_096),
628 )?,
629 ),
630 )
631}
632fn aura_soul_reaper(
633 a: AuraDefinitionDraft,
634 data: &ResolvedGameData,
635) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
636 Ok(
637 a
638 .on_target()
639 .apply_base_from_data(data, 1_241_521)?
640 .max_stacks(
641 data
642 .require_aura_max_stacks(
643 wowlab_types::sim::SpellIdx::from_raw(1_241_521),
644 )?,
645 ),
646 )
647}
648fn aura_mograines_might(
649 a: AuraDefinitionDraft,
650 data: &ResolvedGameData,
651) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
652 Ok(
653 a
654 .on_player()
655 .apply_base_from_data(data, 444_505)?
656 .max_stacks(
657 data
658 .require_aura_max_stacks(
659 wowlab_types::sim::SpellIdx::from_raw(444_505),
660 )?,
661 ),
662 )
663}
664fn aura_apocalyptic_conquest(
665 a: AuraDefinitionDraft,
666 data: &ResolvedGameData,
667) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
668 a.on_player()
669 .apply_base_from_data(data, 444_763)?
670 .max_stacks(
671 wowlab_engine_combat::validated_max_stacks(
672 255.0,
673 444_763,
674 "aura.max_stacks",
675 )?,
676 )
677 .primary_pct_per_stack(
678 data.base_points(wowlab_types::sim::SpellIdx::from_raw(444_052), 2),
679 )
680}
681fn aura_trollbane_chains(
682 a: AuraDefinitionDraft,
683 data: &ResolvedGameData,
684) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
685 Ok(
686 a
687 .on_target()
688 .apply_base_from_data(data, 444_828)?
689 .max_stacks(
690 data
691 .require_aura_max_stacks(
692 wowlab_types::sim::SpellIdx::from_raw(444_828),
693 )?,
694 ),
695 )
696}
697fn aura_undeath(
698 a: AuraDefinitionDraft,
699 data: &ResolvedGameData,
700) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
701 Ok(
702 a
703 .on_target()
704 .apply_base_from_data(data, 444_633)?
705 .max_stacks(
706 wowlab_engine_combat::validated_max_stacks(
707 255.0,
708 444_633,
709 "aura.max_stacks",
710 )?,
711 )
712 .periodic_damage_scales_with_stacks()
713 .periodic_hook(
714 wowlab_engine_combat::validated_milliseconds(
715 data.period(wowlab_types::sim::SpellIdx::from_raw(444_633), 1),
716 444_633,
717 "periodic_hook.tick_ms",
718 )?,
719 )
720 .apply_periodic_behavior_from_data(data, 444_633)?
721 .tick_hook(crate::hooks::unholy_death_knight::undeath_tick_hook),
722 )
723}
724fn aura_reaping(
725 a: AuraDefinitionDraft,
726 data: &ResolvedGameData,
727) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
728 Ok(
729 a
730 .on_player()
731 .apply_base_from_data(data, 1_242_654)?
732 .max_stacks(
733 data
734 .require_aura_max_stacks(
735 wowlab_types::sim::SpellIdx::from_raw(1_242_654),
736 )?,
737 ),
738 )
739}
740fn aura_forbidden_knowledge(
741 a: AuraDefinitionDraft,
742 data: &ResolvedGameData,
743) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
744 Ok(
745 a
746 .on_player()
747 .apply_base_from_data(data, 1_242_223)?
748 .max_stacks(
749 data
750 .require_aura_max_stacks(
751 wowlab_types::sim::SpellIdx::from_raw(1_242_223),
752 )?,
753 ),
754 )
755}
756fn aura_blightfall(
757 a: AuraDefinitionDraft,
758 data: &ResolvedGameData,
759) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
760 Ok(
761 a
762 .on_player()
763 .apply_base_from_data(data, 1_271_975)?
764 .max_stacks(
765 data
766 .require_aura_max_stacks(
767 wowlab_types::sim::SpellIdx::from_raw(1_271_975),
768 )?,
769 ),
770 )
771}
772fn aura_icy_talons(
773 a: AuraDefinitionDraft,
774 data: &ResolvedGameData,
775) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
776 Ok(
777 a
778 .on_player()
779 .apply_base_from_data(data, 194_879)?
780 .max_stacks(
781 data
782 .require_aura_max_stacks(
783 wowlab_types::sim::SpellIdx::from_raw(194_879),
784 )?,
785 ),
786 )
787}
788fn aura_rune_mastery(
789 a: AuraDefinitionDraft,
790 data: &ResolvedGameData,
791) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
792 a.on_player()
793 .apply_base_from_data(data, 374_585)?
794 .max_stacks(
795 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(374_585))?,
796 )
797 .primary_pct_per_stack(
798 data.base_points(wowlab_types::sim::SpellIdx::from_raw(374_585), 1),
799 )
800}
801fn aura_brittle(
802 a: AuraDefinitionDraft,
803 data: &ResolvedGameData,
804) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
805 Ok(
806 a
807 .on_target()
808 .apply_base_from_data(data, 374_557)?
809 .max_stacks(
810 data
811 .require_aura_max_stacks(
812 wowlab_types::sim::SpellIdx::from_raw(374_557),
813 )?,
814 ),
815 )
816}
817fn aura_virulent_plague(
818 a: AuraDefinitionDraft,
819 data: &ResolvedGameData,
820) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
821 Ok(
822 a
823 .on_target()
824 .apply_base_from_data(data, 191_587)?
825 .max_stacks(
826 data
827 .require_aura_max_stacks(
828 wowlab_types::sim::SpellIdx::from_raw(191_587),
829 )?,
830 )
831 .periodic_hook(
832 wowlab_engine_combat::validated_milliseconds(
833 data.period(wowlab_types::sim::SpellIdx::from_raw(191_587), 1),
834 191_587,
835 "periodic_hook.tick_ms",
836 )?,
837 )
838 .apply_periodic_behavior_from_data(data, 191_587)?
839 .tick_hook(crate::hooks::unholy_death_knight::disease_tick_hook),
840 )
841}
842fn aura_dread_plague(
843 a: AuraDefinitionDraft,
844 data: &ResolvedGameData,
845) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
846 Ok(
847 a
848 .on_target()
849 .apply_base_from_data(data, 1_240_996)?
850 .max_stacks(
851 data
852 .require_aura_max_stacks(
853 wowlab_types::sim::SpellIdx::from_raw(1_240_996),
854 )?,
855 )
856 .periodic_hook(
857 wowlab_engine_combat::validated_milliseconds(
858 data.period(wowlab_types::sim::SpellIdx::from_raw(1_240_996), 1),
859 1_240_996,
860 "periodic_hook.tick_ms",
861 )?,
862 )
863 .apply_periodic_behavior_from_data(data, 1_240_996)?
864 .tick_hook(crate::hooks::unholy_death_knight::disease_tick_hook),
865 )
866}
867fn aura_infected_claws(
868 a: AuraDefinitionDraft,
869 data: &ResolvedGameData,
870) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
871 Ok(
872 a
873 .on_target()
874 .apply_base_from_data(data, 1_241_786)?
875 .max_stacks(
876 data
877 .require_aura_max_stacks(
878 wowlab_types::sim::SpellIdx::from_raw(1_241_786),
879 )?,
880 ),
881 )
882}
883fn aura_lesser_ghoul(
884 a: AuraDefinitionDraft,
885 data: &ResolvedGameData,
886) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
887 Ok(
888 a
889 .on_player()
890 .apply_base_from_data(data, 1_254_252)?
891 .max_stacks(
892 wowlab_engine_combat::validated_max_stacks(
893 8.0,
894 1_254_252,
895 "aura.max_stacks",
896 )?,
897 ),
898 )
899}
900fn aura_lesser_ghoul_counter(
901 a: AuraDefinitionDraft,
902 data: &ResolvedGameData,
903) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
904 Ok(
905 a
906 .on_player()
907 .apply_base_from_data(data, 1_242_998)?
908 .max_stacks(
909 wowlab_engine_combat::validated_max_stacks(
910 20.0,
911 1_242_998,
912 "aura.max_stacks",
913 )?,
914 ),
915 )
916}
917fn aura_unholy_devotion(
918 a: AuraDefinitionDraft,
919 data: &ResolvedGameData,
920) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
921 Ok(
922 a
923 .on_pet()
924 .apply_base_from_data(data, 1_270_491)?
925 .max_stacks(
926 data
927 .require_aura_max_stacks(
928 wowlab_types::sim::SpellIdx::from_raw(1_270_491),
929 )?,
930 )
931 .async_stacks(),
932 )
933}
934fn aura_gargoyle_active(
935 a: AuraDefinitionDraft,
936 data: &ResolvedGameData,
937) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
938 a.on_player()
939 .apply_base_from_data(data, 49206)?
940 .no_pandemic()
941 .max_stacks(
942 wowlab_engine_combat::validated_max_stacks(1.0, 49206, "aura.max_stacks")?,
943 )
944 .crit_chance_spells(100.0, &[51963])
945}
946fn aura_gargoyle_empowerment(
947 a: AuraDefinitionDraft,
948 data: &ResolvedGameData,
949) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
950 a.on_player()
951 .apply_base_from_data(data, 211_947)?
952 .max_stacks(
953 wowlab_engine_combat::validated_max_stacks(
954 255.0,
955 211_947,
956 "aura.max_stacks",
957 )?,
958 )
959 .damage_percent_spells_linear(
960 data.base_points(wowlab_types::sim::SpellIdx::from_raw(211_947), 1),
961 &[51963],
962 )
963}
964fn aura_dark_transformation(
965 a: AuraDefinitionDraft,
966 data: &ResolvedGameData,
967) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
968 Ok(
969 a
970 .on_player()
971 .apply_base_from_data(data, 1_235_391)?
972 .max_stacks(
973 data
974 .require_aura_max_stacks(
975 wowlab_types::sim::SpellIdx::from_raw(1_235_391),
976 )?,
977 ),
978 )
979}
980fn aura_commander_of_the_dead(
981 a: AuraDefinitionDraft,
982 data: &ResolvedGameData,
983) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
984 Ok(
985 a
986 .on_player()
987 .apply_base_from_data(data, 390_260)?
988 .max_stacks(
989 data
990 .require_aura_max_stacks(
991 wowlab_types::sim::SpellIdx::from_raw(390_260),
992 )?,
993 ),
994 )
995}
996fn spell_festering_strike(
997 s: SpellDefinitionDraft,
998 data: &ResolvedGameData,
999) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1000 Ok(
1001 s
1002 .apply_base_from_data(data, 85948)?
1003 .apply_aoe_from_data(data)
1004 .on_cast(crate::hooks::unholy_death_knight::festering_strike_hook),
1005 )
1006}
1007fn spell_scourge_strike(
1008 s: SpellDefinitionDraft,
1009 data: &ResolvedGameData,
1010) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1011 Ok(
1012 s
1013 .apply_base_from_data(data, 55090)?
1014 .apply_aoe_from_data(data)
1015 .on_cast(crate::hooks::unholy_death_knight::scourge_strike_hook),
1016 )
1017}
1018fn spell_death_coil(
1019 s: SpellDefinitionDraft,
1020 data: &ResolvedGameData,
1021) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1022 Ok(
1023 s
1024 .apply_base_from_data(data, 47541)?
1025 .extends_aura(AURA_VIRULENT_PLAGUE, 1500)
1026 .extends_aura(AURA_DREAD_PLAGUE, 1500)
1027 .apply_aoe_from_data(data)
1028 .on_cast(crate::hooks::unholy_death_knight::death_coil_hook)
1029 .cost_free_when_aura(81340),
1030 )
1031}
1032fn spell_epidemic(
1033 s: SpellDefinitionDraft,
1034 data: &ResolvedGameData,
1035) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1036 Ok(s.apply_base_from_data(data, 212_739)?.apply_aoe_from_data(data))
1037}
1038fn spell_soul_reaper(
1039 s: SpellDefinitionDraft,
1040 data: &ResolvedGameData,
1041) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1042 Ok(
1043 s
1044 .apply_base_from_data(data, 343_294)?
1045 .applies_inferred_aura(AURA_SOUL_REAPER, 1_241_521)
1046 .apply_aoe_from_data(data)
1047 .on_cast(crate::hooks::unholy_death_knight::soul_reaper_hook),
1048 )
1049}
1050fn spell_outbreak(
1051 s: SpellDefinitionDraft,
1052 data: &ResolvedGameData,
1053) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1054 Ok(
1055 s
1056 .apply_base_from_data(data, 77575)?
1057 .apply_aoe_from_data(data)
1058 .on_cast(crate::hooks::unholy_death_knight::outbreak_hook),
1059 )
1060}
1061fn spell_gargoyle_strike(
1062 s: SpellDefinitionDraft,
1063 data: &ResolvedGameData,
1064) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1065 Ok(s.apply_base_from_data(data, 51963)?.apply_aoe_from_data(data))
1066}
1067fn spell_raise_dead(
1068 s: SpellDefinitionDraft,
1069 data: &ResolvedGameData,
1070) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1071 Ok(s.apply_base_from_data(data, 46584)?.apply_aoe_from_data(data))
1072}
1073fn spell_dark_transformation(
1074 s: SpellDefinitionDraft,
1075 data: &ResolvedGameData,
1076) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1077 Ok(
1078 s
1079 .apply_base_from_data(data, 1_233_448)?
1080 .applies_aura(AURA_DARK_TRANSFORMATION)
1081 .apply_aoe_from_data(data)
1082 .on_cast(crate::hooks::unholy_death_knight::dark_transformation_hook),
1083 )
1084}
1085fn spell_army_of_the_dead(
1086 s: SpellDefinitionDraft,
1087 data: &ResolvedGameData,
1088) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1089 Ok(
1090 s
1091 .apply_base_from_data(data, 42650)?
1092 .apply_aoe_from_data(data)
1093 .on_cast(crate::hooks::unholy_death_knight::army_of_the_dead_hook),
1094 )
1095}
1096fn spell_putrefy(
1097 s: SpellDefinitionDraft,
1098 data: &ResolvedGameData,
1099) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1100 Ok(
1101 s
1102 .apply_base_from_data(data, 1_247_378)?
1103 .apply_aoe_from_data(data)
1104 .on_cast(crate::hooks::unholy_death_knight::putrefy_hook),
1105 )
1106}
1107pub(crate) fn build_combat_system(
1108 params: &wowlab_engine_ports::HandlerParams<'_>,
1109) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
1110 let builder = BuiltCombatSystem::builder(params.stats.clone())
1111 .spec_id(SpecId::Unholy)
1112 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
1113 .mastery_spell(77515)
1114 .mastery_hook(crate::hooks::unholy_death_knight::mastery)
1115 .resource("runes", 6.0, 0.0)
1116 .resource_type_id(5)
1117 .resource_start(6.0)
1118 .mastery_crit_damage_hook(crate::hooks::unholy_death_knight::crit_damage)
1119 .has_pet(true)
1120 .secondary_resource("runic_power", 100.0)
1121 .secondary_resource_type_id(6)
1122 .aura(
1123 "sudden_doom",
1124 AURA::SUDDEN_DOOM,
1125 |a| aura_sudden_doom(a, ¶ms.game_data),
1126 )
1127 .aura(
1128 "runic_corruption",
1129 AURA::RUNIC_CORRUPTION,
1130 |a| aura_runic_corruption(a, ¶ms.game_data),
1131 )
1132 .aura(
1133 "festering_scythe",
1134 AURA::FESTERING_SCYTHE,
1135 |a| aura_festering_scythe(a, ¶ms.game_data),
1136 )
1137 .aura(
1138 "festering_scythe_tt",
1139 AURA::FESTERING_SCYTHE_TT,
1140 |a| aura_festering_scythe_tt(a, ¶ms.game_data),
1141 )
1142 .aura(
1143 "coil_of_devastation",
1144 AURA::COIL_OF_DEVASTATION,
1145 |a| aura_coil_of_devastation(a, ¶ms.game_data),
1146 )
1147 .aura(
1148 "apocalypse_death",
1149 AURA::APOCALYPSE_DEATH,
1150 |a| aura_apocalypse_death(a, ¶ms.game_data),
1151 )
1152 .aura(
1153 "apocalypse_famine",
1154 AURA::APOCALYPSE_FAMINE,
1155 |a| aura_apocalypse_famine(a, ¶ms.game_data),
1156 )
1157 .aura(
1158 "apocalypse_pestilence",
1159 AURA::APOCALYPSE_PESTILENCE,
1160 |a| aura_apocalypse_pestilence(a, ¶ms.game_data),
1161 )
1162 .aura(
1163 "apocalypse_war",
1164 AURA::APOCALYPSE_WAR,
1165 |a| aura_apocalypse_war(a, ¶ms.game_data),
1166 )
1167 .aura(
1168 "soul_reaper",
1169 AURA::SOUL_REAPER,
1170 |a| aura_soul_reaper(a, ¶ms.game_data),
1171 )
1172 .aura(
1173 "mograines_might",
1174 AURA::MOGRAINES_MIGHT,
1175 |a| aura_mograines_might(a, ¶ms.game_data),
1176 )
1177 .aura(
1178 "apocalyptic_conquest",
1179 AURA::APOCALYPTIC_CONQUEST,
1180 |a| aura_apocalyptic_conquest(a, ¶ms.game_data),
1181 )
1182 .aura(
1183 "trollbane_chains",
1184 AURA::TROLLBANE_CHAINS,
1185 |a| aura_trollbane_chains(a, ¶ms.game_data),
1186 )
1187 .aura("undeath", AURA::UNDEATH, |a| aura_undeath(a, ¶ms.game_data))
1188 .aura("reaping", AURA::REAPING, |a| aura_reaping(a, ¶ms.game_data))
1189 .aura(
1190 "forbidden_knowledge",
1191 AURA::FORBIDDEN_KNOWLEDGE,
1192 |a| aura_forbidden_knowledge(a, ¶ms.game_data),
1193 )
1194 .aura("blightfall", AURA::BLIGHTFALL, |a| aura_blightfall(a, ¶ms.game_data))
1195 .aura("icy_talons", AURA::ICY_TALONS, |a| aura_icy_talons(a, ¶ms.game_data))
1196 .aura(
1197 "rune_mastery",
1198 AURA::RUNE_MASTERY,
1199 |a| aura_rune_mastery(a, ¶ms.game_data),
1200 )
1201 .aura("brittle", AURA::BRITTLE, |a| aura_brittle(a, ¶ms.game_data))
1202 .aura(
1203 "virulent_plague",
1204 AURA::VIRULENT_PLAGUE,
1205 |a| aura_virulent_plague(a, ¶ms.game_data),
1206 )
1207 .aura(
1208 "dread_plague",
1209 AURA::DREAD_PLAGUE,
1210 |a| aura_dread_plague(a, ¶ms.game_data),
1211 )
1212 .aura(
1213 "infected_claws",
1214 AURA::INFECTED_CLAWS,
1215 |a| aura_infected_claws(a, ¶ms.game_data),
1216 )
1217 .aura(
1218 "lesser_ghoul",
1219 AURA::LESSER_GHOUL,
1220 |a| aura_lesser_ghoul(a, ¶ms.game_data),
1221 )
1222 .aura(
1223 "lesser_ghoul_counter",
1224 AURA::LESSER_GHOUL_COUNTER,
1225 |a| aura_lesser_ghoul_counter(a, ¶ms.game_data),
1226 )
1227 .aura(
1228 "unholy_devotion",
1229 AURA::UNHOLY_DEVOTION,
1230 |a| aura_unholy_devotion(a, ¶ms.game_data),
1231 )
1232 .aura(
1233 "gargoyle_active",
1234 AURA::GARGOYLE_ACTIVE,
1235 |a| aura_gargoyle_active(a, ¶ms.game_data),
1236 )
1237 .aura(
1238 "gargoyle_empowerment",
1239 AURA::GARGOYLE_EMPOWERMENT,
1240 |a| aura_gargoyle_empowerment(a, ¶ms.game_data),
1241 )
1242 .aura(
1243 "dark_transformation",
1244 AURA::DARK_TRANSFORMATION,
1245 |a| aura_dark_transformation(a, ¶ms.game_data),
1246 )
1247 .aura(
1248 "commander_of_the_dead",
1249 AURA::COMMANDER_OF_THE_DEAD,
1250 |a| aura_commander_of_the_dead(a, ¶ms.game_data),
1251 )
1252 .spell(
1253 "festering_strike",
1254 SPELL::FESTERING_STRIKE,
1255 |s| spell_festering_strike(s, ¶ms.game_data),
1256 )
1257 .spell(
1258 "scourge_strike",
1259 SPELL::SCOURGE_STRIKE,
1260 |s| spell_scourge_strike(s, ¶ms.game_data),
1261 )
1262 .spell(
1263 "death_coil",
1264 SPELL::DEATH_COIL,
1265 |s| spell_death_coil(s, ¶ms.game_data),
1266 )
1267 .spell("epidemic", SPELL::EPIDEMIC, |s| spell_epidemic(s, ¶ms.game_data))
1268 .spell(
1269 "soul_reaper",
1270 SPELL::SOUL_REAPER,
1271 |s| spell_soul_reaper(s, ¶ms.game_data),
1272 )
1273 .spell("outbreak", SPELL::OUTBREAK, |s| spell_outbreak(s, ¶ms.game_data))
1274 .spell(
1275 "gargoyle_strike",
1276 SPELL::GARGOYLE_STRIKE,
1277 |s| spell_gargoyle_strike(s, ¶ms.game_data),
1278 )
1279 .spell(
1280 "raise_dead",
1281 SPELL::RAISE_DEAD,
1282 |s| spell_raise_dead(s, ¶ms.game_data),
1283 )
1284 .spell(
1285 "dark_transformation",
1286 SPELL::DARK_TRANSFORMATION,
1287 |s| spell_dark_transformation(s, ¶ms.game_data),
1288 )
1289 .spell(
1290 "army_of_the_dead",
1291 SPELL::ARMY_OF_THE_DEAD,
1292 |s| spell_army_of_the_dead(s, ¶ms.game_data),
1293 )
1294 .spell("putrefy", SPELL::PUTREFY, |s| spell_putrefy(s, ¶ms.game_data))
1295 .auto_attack(|a| {
1296 a.spell_id(6603).swing_ms(3600).apply_base_from_data(¶ms.game_data)
1297 })
1298 .auto_attack(|a| {
1299 a
1300 .spell_id(6603)
1301 .swing_ms(2000)
1302 .apply_base_from_data(¶ms.game_data)
1303 .ap_coef(
1304 wowlab_engine_domain::damage::pet_swing_power_coefficient(
1305 0.474,
1306 2000,
1307 1.0,
1308 ),
1309 )
1310 .is_pet()
1311 .npc_id(26125)
1312 .on_swing(crate::hooks::unholy_death_knight::pet_swing)
1313 })
1314 .game_data(params.game_data.clone())
1315 .talent_spell_ids(TALENTS)
1316 .talent_selections(params.talent_selections)
1317 .set_bonus_auras(params.set_bonus_auras)
1318 .encounter(params.encounter.clone());
1319 let builder = crate::generated::items::register_equipped(
1320 builder,
1321 ¶ms.game_data,
1322 params.equipped_items,
1323 )?;
1324
1325 builder
1326 .build(params.rotation)
1327 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
1328}
1329fn new_handler(
1330 params: wowlab_engine_ports::HandlerParams<'_>,
1331) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
1332 let handler = crate::composition::compose_handler(
1333 build_combat_system,
1334 ¶ms,
1335 crate::hooks::unholy_death_knight::UnholyDkHandler::try_new,
1336 )?;
1337
1338 Ok(Box::new(handler))
1339}
1340static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
1341 primary_resource: ResourceType::Runes,
1342 secondary_resource: Some(ResourceType::RunicPower),
1343 spell_ids: DECLARED_SPELL_IDS,
1344 aura_ids: DECLARED_AURA_IDS,
1345 server_proc_entries: &[],
1346 talents: TALENTS,
1347 rotation_field_schema: &[],
1348 metrics_plugin_id: None,
1349 hero_talent_trees: &[
1350 HeroTalentTreeDesc {
1351 name: "Unholy Pets",
1352 spells: HERO_TREE_UNHOLY_PETS_SPELLS,
1353 auras: HERO_TREE_UNHOLY_PETS_AURAS,
1354 },
1355 HeroTalentTreeDesc {
1356 name: "Riders",
1357 spells: HERO_TREE_RIDERS_SPELLS,
1358 auras: HERO_TREE_RIDERS_AURAS,
1359 },
1360 ],
1361 reported_spells: REPORTED_SPELLS,
1362 masked_passive_effects: &[],
1363 passive_effect_overrides: &[],
1364};
1365pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1366 spec_id: SpecId::Unholy,
1367 display_name: "Unholy Death Knight",
1368 metadata: DECLARED_METADATA,
1369 handler_factory: new_handler,
1370};
1371wowlab_engine_combat::smoke_test!(build_combat_system);