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 SHRED: u32 = 5221;
16 pub(crate) const SWIPE: u32 = 106_785;
18 pub(crate) const MOONFIRE: u32 = 155_625;
20 pub(crate) const CAT_FORM: u32 = 768;
22 pub(crate) const MARK_OF_THE_WILD: u32 = 1126;
24 pub(crate) const WAITING_FOR_ENERGY: u32 = 1_249_752;
26 pub(crate) const RAKE: u32 = 1822;
28 pub(crate) const RIP: u32 = 1079;
30 pub(crate) const FEROCIOUS_BITE: u32 = 22568;
32 pub(crate) const PRIMAL_WRATH: u32 = 285_381;
34 pub(crate) const FERAL_FRENZY: u32 = 274_837;
36 pub(crate) const FRANTIC_FRENZY: u32 = 1_243_807;
38 pub(crate) const TIGERS_FURY: u32 = 5217;
40 pub(crate) const BERSERK: u32 = 106_951;
42 pub(crate) const INCARNATION_AVATAR_OF_ASHAMANE: u32 = 102_543;
44 pub(crate) const CONVOKE_THE_SPIRITS: u32 = 391_528;
46 pub(crate) const WRATH: u32 = 5176;
48 pub(crate) const CHOMP: u32 = 1_244_292;
50}
51pub(crate) mod AURA {
52 pub(crate) const MOONFIRE: u32 = 155_625;
54 pub(crate) const CLEARCASTING: u32 = 135_700;
56 pub(crate) const PROWL: u32 = 5215;
58 pub(crate) const CAT_FORM: u32 = 768;
60 pub(crate) const MARK_OF_THE_WILD_HIGHLIGHT: u32 = 1126;
62 pub(crate) const LUNAR_INSPIRATION: u32 = 155_580;
64 pub(crate) const RIP: u32 = 1079;
66 pub(crate) const RAKE: u32 = 155_722;
68 pub(crate) const SABERTOOTH: u32 = 391_722;
70 pub(crate) const UNSEEN_SLASH: u32 = 1_271_863;
72 pub(crate) const FERAL_FRENZY: u32 = 274_838;
74 pub(crate) const TIGERS_FURY: u32 = 5217;
76 pub(crate) const BERSERK: u32 = 106_951;
78 pub(crate) const INCARNATION_AVATAR_OF_ASHAMANE: u32 = 102_543;
80 pub(crate) const APEX_PREDATORS_CRAVING: u32 = 391_882;
82 pub(crate) const SAVAGE_FURY: u32 = 449_646;
84 pub(crate) const CHOMP_ENABLER: u32 = 1_244_292;
86 pub(crate) const OVERFLOWING_POWER: u32 = 405_189;
88 pub(crate) const TIGERS_TENACITY: u32 = 391_873;
90 pub(crate) const SUDDEN_AMBUSH: u32 = 1_244_483;
92 pub(crate) const STRATEGIC_INFUSION: u32 = 439_891;
94 pub(crate) const LYCARAS_TEACHINGS_CRIT: u32 = 378_990;
96 pub(crate) const FLASH_OF_CLARITY: u32 = 1_272_262;
98 pub(crate) const UNSEEN_PREDATORS_CRAVING: u32 = 1_263_939;
100 pub(crate) const STALKING_PREDATOR: u32 = 1_263_962;
102 pub(crate) const BLOODSEEKER_VINES: u32 = 439_531;
104}
105pub(crate) mod TALENT {
106 pub(crate) const APEX_PREDATORS_CRAVING: u32 = 391_881;
108 pub(crate) const ASHAMANES_GUIDANCE: u32 = 391_548;
110 pub(crate) const BERSERK: u32 = 343_223;
112 pub(crate) const CARNIVOROUS_INSTINCT: u32 = 390_902;
114 pub(crate) const CHOMP: u32 = 1_244_292;
116 pub(crate) const CONVOKE_THE_SPIRITS: u32 = 391_528;
118 pub(crate) const FERAL_FRENZY: u32 = 274_837;
120 pub(crate) const FRANTIC_FRENZY: u32 = 1_243_807;
122 pub(crate) const INCARNATION_AVATAR_OF_ASHAMANE: u32 = 102_543;
124 pub(crate) const LUNAR_INSPIRATION: u32 = 155_580;
126 pub(crate) const LYCARAS_TEACHINGS: u32 = 378_988;
128 pub(crate) const MOMENT_OF_CLARITY: u32 = 236_068;
130 pub(crate) const OMEN_OF_CLARITY: u32 = 16864;
132 pub(crate) const PANTHERS_GUILE: u32 = 1_280_316;
134 pub(crate) const PRIMAL_FURY: u32 = 159_286;
136 pub(crate) const PRIMAL_WRATH: u32 = 285_381;
138 pub(crate) const RAMPANT_FEROCITY: u32 = 391_709;
140 pub(crate) const SABERTOOTH: u32 = 202_031;
142 pub(crate) const SOUL_OF_THE_FOREST: u32 = 158_476;
144 pub(crate) const STRATEGIC_INFUSION: u32 = 439_890;
146 pub(crate) const SUDDEN_AMBUSH: u32 = 384_667;
148 pub(crate) const TASTE_FOR_BLOOD: u32 = 384_665;
150 pub(crate) const THRIVING_GROWTH: u32 = 439_528;
152 pub(crate) const TIGERS_TENACITY: u32 = 391_872;
154 pub(crate) const UNSEEN_PREDATOR: u32 = 1_263_658;
156}
157pub(crate) mod SET_BONUS {
158 pub(crate) const MID1_FERAL_2PC: u32 = 1_264_812;
160}
161pub(crate) mod REPORTED_SPELL {
162 pub(crate) const UNSEEN_SLASH: u32 = 1_263_890;
164}
165pub(crate) mod HERO {
166 pub(crate) mod DRUID_OF_THE_CLAW {
167 pub(crate) mod SPELL {
168 pub(crate) const RAVAGE: u32 = 441_591;
170 pub(crate) const TWIN_CLAW: u32 = 1_271_635;
172 }
173 pub(crate) mod AURA {
174 pub(crate) const DREADFUL_WOUND: u32 = 441_812;
176 pub(crate) const KILLING_STRIKES: u32 = 441_825;
178 pub(crate) const KILLING_STRIKES_COMBAT: u32 = 441_827;
180 pub(crate) const RAVAGE_FB: u32 = 441_585;
182 pub(crate) const RUTHLESS_AGGRESSION: u32 = 441_814;
184 }
185 }
186 pub(crate) mod WILDSTALKER {
187 pub(crate) mod SPELL {
188 pub(crate) const BURSTING_GROWTH: u32 = 440_122;
190 }
191 pub(crate) mod AURA {
192 pub(crate) const BLOODSEEKER_VINES: u32 = 439_531;
194 pub(crate) const IMPLANT: u32 = 455_496;
196 pub(crate) const ROOT_NETWORK: u32 = 439_887;
198 pub(crate) const STRATEGIC_INFUSION: u32 = 439_891;
200 }
201 }
202}
203static HERO_TREE_DRUID_OF_THE_CLAW_SPELLS: &[(&str, u32)] = &[
204 ("RAVAGE", 441_591),
205 ("TWIN_CLAW", 1_271_635),
206];
207static HERO_TREE_DRUID_OF_THE_CLAW_AURAS: &[(&str, u32)] = &[
208 ("DREADFUL_WOUND", 441_812),
209 ("KILLING_STRIKES", 441_825),
210 ("KILLING_STRIKES_COMBAT", 441_827),
211 ("RAVAGE_FB", 441_585),
212 ("RUTHLESS_AGGRESSION", 441_814),
213];
214static HERO_TREE_WILDSTALKER_SPELLS: &[(&str, u32)] = &[("BURSTING_GROWTH", 440_122)];
215static HERO_TREE_WILDSTALKER_AURAS: &[(&str, u32)] = &[
216 ("BLOODSEEKER_VINES", 439_531),
217 ("IMPLANT", 455_496),
218 ("ROOT_NETWORK", 439_887),
219 ("STRATEGIC_INFUSION", 439_891),
220];
221pub(crate) const AURA_MOONFIRE: LocalAuraIdx = LocalAuraIdx::new(0);
222pub(crate) const AURA_CLEARCASTING: LocalAuraIdx = LocalAuraIdx::new(1);
223pub(crate) const AURA_PROWL: LocalAuraIdx = LocalAuraIdx::new(2);
224pub(crate) const AURA_CAT_FORM: LocalAuraIdx = LocalAuraIdx::new(3);
225pub(crate) const AURA_MARK_OF_THE_WILD_HIGHLIGHT: LocalAuraIdx = LocalAuraIdx::new(4);
226pub(crate) const AURA_LUNAR_INSPIRATION: LocalAuraIdx = LocalAuraIdx::new(5);
227pub(crate) const AURA_RIP: LocalAuraIdx = LocalAuraIdx::new(6);
228pub(crate) const AURA_RAKE: LocalAuraIdx = LocalAuraIdx::new(7);
229pub(crate) const AURA_SABERTOOTH: LocalAuraIdx = LocalAuraIdx::new(8);
230pub(crate) const AURA_UNSEEN_SLASH: LocalAuraIdx = LocalAuraIdx::new(9);
231pub(crate) const AURA_FERAL_FRENZY: LocalAuraIdx = LocalAuraIdx::new(10);
232pub(crate) const AURA_TIGERS_FURY: LocalAuraIdx = LocalAuraIdx::new(11);
233pub(crate) const AURA_BERSERK: LocalAuraIdx = LocalAuraIdx::new(12);
234pub(crate) const AURA_INCARNATION_AVATAR_OF_ASHAMANE: LocalAuraIdx = LocalAuraIdx::new(
235 13,
236);
237pub(crate) const AURA_APEX_PREDATORS_CRAVING: LocalAuraIdx = LocalAuraIdx::new(14);
238pub(crate) const AURA_SAVAGE_FURY: LocalAuraIdx = LocalAuraIdx::new(15);
239pub(crate) const AURA_CHOMP_ENABLER: LocalAuraIdx = LocalAuraIdx::new(16);
240pub(crate) const AURA_OVERFLOWING_POWER: LocalAuraIdx = LocalAuraIdx::new(17);
241pub(crate) const AURA_TIGERS_TENACITY: LocalAuraIdx = LocalAuraIdx::new(18);
242pub(crate) const AURA_SUDDEN_AMBUSH: LocalAuraIdx = LocalAuraIdx::new(19);
243pub(crate) const AURA_STRATEGIC_INFUSION: LocalAuraIdx = LocalAuraIdx::new(20);
244pub(crate) const AURA_LYCARAS_TEACHINGS_CRIT: LocalAuraIdx = LocalAuraIdx::new(21);
245pub(crate) const AURA_FLASH_OF_CLARITY: LocalAuraIdx = LocalAuraIdx::new(22);
246pub(crate) const AURA_UNSEEN_PREDATORS_CRAVING: LocalAuraIdx = LocalAuraIdx::new(23);
247pub(crate) const AURA_STALKING_PREDATOR: LocalAuraIdx = LocalAuraIdx::new(24);
248pub(crate) const AURA_BLOODSEEKER_VINES: LocalAuraIdx = LocalAuraIdx::new(25);
249pub(crate) const AURA_COUNT: usize = 26;
250const _: () = assert!(
251 AURA_COUNT == 26, "AURA index ABI: count changed -- update positional hook consumers"
252);
253pub(crate) const SPELL_SHRED: LocalSpellIdx = LocalSpellIdx::new(0);
254pub(crate) const SPELL_SWIPE: LocalSpellIdx = LocalSpellIdx::new(1);
255pub(crate) const SPELL_MOONFIRE: LocalSpellIdx = LocalSpellIdx::new(2);
256pub(crate) const SPELL_CAT_FORM: LocalSpellIdx = LocalSpellIdx::new(3);
257pub(crate) const SPELL_MARK_OF_THE_WILD: LocalSpellIdx = LocalSpellIdx::new(4);
258pub(crate) const SPELL_WAITING_FOR_ENERGY: LocalSpellIdx = LocalSpellIdx::new(5);
259pub(crate) const SPELL_RAKE: LocalSpellIdx = LocalSpellIdx::new(6);
260pub(crate) const SPELL_RIP: LocalSpellIdx = LocalSpellIdx::new(7);
261pub(crate) const SPELL_FEROCIOUS_BITE: LocalSpellIdx = LocalSpellIdx::new(8);
262pub(crate) const SPELL_PRIMAL_WRATH: LocalSpellIdx = LocalSpellIdx::new(9);
263pub(crate) const SPELL_FERAL_FRENZY: LocalSpellIdx = LocalSpellIdx::new(10);
264pub(crate) const SPELL_FRANTIC_FRENZY: LocalSpellIdx = LocalSpellIdx::new(11);
265pub(crate) const SPELL_TIGERS_FURY: LocalSpellIdx = LocalSpellIdx::new(12);
266pub(crate) const SPELL_BERSERK: LocalSpellIdx = LocalSpellIdx::new(13);
267pub(crate) const SPELL_INCARNATION_AVATAR_OF_ASHAMANE: LocalSpellIdx = LocalSpellIdx::new(
268 14,
269);
270pub(crate) const SPELL_CONVOKE_THE_SPIRITS: LocalSpellIdx = LocalSpellIdx::new(15);
271pub(crate) const SPELL_WRATH: LocalSpellIdx = LocalSpellIdx::new(16);
272pub(crate) const SPELL_CHOMP: LocalSpellIdx = LocalSpellIdx::new(17);
273pub(crate) const SPELL_COUNT: usize = 18;
274const _: () = assert!(
275 SPELL_COUNT == 18,
276 "SPELL index ABI: count changed -- update positional hook consumers"
277);
278pub(crate) mod EFFECT {
279 pub(crate) const APEX_PREDATOR_CHANCE: (u32, u8) = (391_881, 1);
281 pub(crate) const FERAL_FRENZY_HIT: (u32, u8) = (274_838, 1);
283 pub(crate) const FEROCIOUS_BITE_DMG: (u32, u8) = (22568, 1);
285 pub(crate) const MOMENT_OF_CLARITY_PROC_BONUS: (u32, u8) = (236_068, 2);
287 pub(crate) const RAZOR_CLAWS_BLOODSEEKER_PERIODIC: (u32, u8) = (77493, 6);
289 pub(crate) const RAZOR_CLAWS_DAMAGE: (u32, u8) = (77493, 1);
291 pub(crate) const RAZOR_CLAWS_DREADFUL_WOUND_PERIODIC: (u32, u8) = (77493, 7);
293 pub(crate) const RAZOR_CLAWS_PERIODIC: (u32, u8) = (77493, 2);
295 pub(crate) const RAZOR_CLAWS_RAMPANT_DIRECT: (u32, u8) = (77493, 5);
297 pub(crate) const RAZOR_CLAWS_RAMPANT_PERIODIC: (u32, u8) = (77493, 4);
299 pub(crate) const RAZOR_CLAWS_UNSEEN_SLASH_PERIODIC: (u32, u8) = (77493, 8);
301 pub(crate) const SUDDEN_AMBUSH_CHANCE: (u32, u8) = (384_667, 1);
303 pub(crate) const THRIVING_GROWTH_RAKE: (u32, u8) = (439_528, 2);
305 pub(crate) const THRIVING_GROWTH_RIP: (u32, u8) = (439_528, 1);
307 pub(crate) const UNSEEN_SLASH_DMG: (u32, u8) = (1_263_890, 1);
309}
310static REPORTED_SPELLS: &[(&str, u32)] = &[("unseen_slash", 1_263_890)];
311static DECLARED_SPELL_IDS: &[u32] = &[
312 5221, 106_785, 155_625, 768, 1126, 1_249_752, 1822, 1079, 22568, 285_381, 274_837,
313 1_243_807, 5217, 106_951, 102_543, 391_528, 5176, 1_244_292, 155_625, 135_700, 5215,
314 768, 1126, 155_580, 1079, 155_722, 391_722, 1_271_863, 274_838, 5217, 106_951,
315 102_543, 391_882, 449_646, 1_244_292, 405_189, 391_873, 1_244_483, 439_891, 378_990,
316 1_272_262, 1_263_939, 1_263_962, 439_531, 6603, 1079, 22568, 77493, 155_625, 155_722,
317 202_031, 236_068, 274_838, 378_988, 384_667, 391_881, 439_528, 1_263_658, 1_263_890,
318 1_271_863, 1_263_890, 441_591, 1_271_635, 441_812, 441_825, 441_827, 441_585,
319 441_814, 440_122, 439_531, 455_496, 439_887, 439_891,
320];
321static DECLARED_AURA_IDS: &[u32] = &[
322 155_625, 135_700, 5215, 768, 1126, 155_580, 1079, 155_722, 391_722, 1_271_863,
323 274_838, 5217, 106_951, 102_543, 391_882, 449_646, 1_244_292, 405_189, 391_873,
324 1_244_483, 439_891, 378_990, 1_272_262, 1_263_939, 1_263_962, 439_531, 441_812,
325 441_825, 441_827, 441_585, 441_814, 439_531, 455_496, 439_887, 439_891,
326];
327static TALENTS: &[(&str, u32)] = &[
328 ("APEX_PREDATORS_CRAVING", TALENT::APEX_PREDATORS_CRAVING),
329 ("ASHAMANES_GUIDANCE", TALENT::ASHAMANES_GUIDANCE),
330 ("BERSERK", TALENT::BERSERK),
331 ("CARNIVOROUS_INSTINCT", TALENT::CARNIVOROUS_INSTINCT),
332 ("CHOMP", TALENT::CHOMP),
333 ("CONVOKE_THE_SPIRITS", TALENT::CONVOKE_THE_SPIRITS),
334 ("FERAL_FRENZY", TALENT::FERAL_FRENZY),
335 ("FRANTIC_FRENZY", TALENT::FRANTIC_FRENZY),
336 ("INCARNATION_AVATAR_OF_ASHAMANE", TALENT::INCARNATION_AVATAR_OF_ASHAMANE),
337 ("LUNAR_INSPIRATION", TALENT::LUNAR_INSPIRATION),
338 ("LYCARAS_TEACHINGS", TALENT::LYCARAS_TEACHINGS),
339 ("MOMENT_OF_CLARITY", TALENT::MOMENT_OF_CLARITY),
340 ("OMEN_OF_CLARITY", TALENT::OMEN_OF_CLARITY),
341 ("PANTHERS_GUILE", TALENT::PANTHERS_GUILE),
342 ("PRIMAL_FURY", TALENT::PRIMAL_FURY),
343 ("PRIMAL_WRATH", TALENT::PRIMAL_WRATH),
344 ("RAMPANT_FEROCITY", TALENT::RAMPANT_FEROCITY),
345 ("SABERTOOTH", TALENT::SABERTOOTH),
346 ("SOUL_OF_THE_FOREST", TALENT::SOUL_OF_THE_FOREST),
347 ("STRATEGIC_INFUSION", TALENT::STRATEGIC_INFUSION),
348 ("SUDDEN_AMBUSH", TALENT::SUDDEN_AMBUSH),
349 ("TASTE_FOR_BLOOD", TALENT::TASTE_FOR_BLOOD),
350 ("THRIVING_GROWTH", TALENT::THRIVING_GROWTH),
351 ("TIGERS_TENACITY", TALENT::TIGERS_TENACITY),
352 ("UNSEEN_PREDATOR", TALENT::UNSEEN_PREDATOR),
353];
354pub(crate) mod FERAL_DRUID {
355 pub(crate) const SHRED_CASTS: u32 = 0;
356 pub(crate) const RAKE_CASTS: u32 = 1;
357 pub(crate) const RIP_CASTS: u32 = 2;
358 pub(crate) const FEROCIOUS_BITE_CASTS: u32 = 3;
359 pub(crate) const TIGERS_FURY_CASTS: u32 = 4;
360 pub(crate) const BERSERK_CASTS: u32 = 5;
361 pub(crate) const FERAL_FRENZY_CASTS: u32 = 6;
362 pub(crate) const TIGERS_FURY_UPTIME_MS: u32 = 7;
363 pub(crate) const BERSERK_UPTIME_MS: u32 = 8;
364 pub(crate) const RIP_UPTIME_MS: u32 = 9;
365 pub(crate) const RAKE_UPTIME_MS: u32 = 10;
366}
367fn aura_moonfire(
368 a: AuraDefinitionDraft,
369 data: &ResolvedGameData,
370) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
371 a.on_target()
372 .apply_base_from_data(data, 155_625)?
373 .max_stacks(
374 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(155_625))?,
375 )
376 .periodic_damage(
377 wowlab_engine_combat::validated_milliseconds(
378 (data.period(wowlab_types::sim::SpellIdx::from_raw(155_625), 1)) * 0.8,
379 155_625,
380 "periodic_damage.tick_ms",
381 )?,
382 ((data.ap_coef(wowlab_types::sim::SpellIdx::from_raw(155_625), 1)) * 100.0)
383 .round() / 100.0,
384 wowlab_engine_combat::DamageFlags::empty(),
385 )
386 .apply_periodic_behavior_from_data(data, 155_625)
387}
388fn aura_clearcasting(
389 a: AuraDefinitionDraft,
390 data: &ResolvedGameData,
391) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
392 Ok(
393 a
394 .on_player()
395 .apply_base_from_data(data, 135_700)?
396 .max_stacks(
397 data
398 .require_aura_max_stacks(
399 wowlab_types::sim::SpellIdx::from_raw(135_700),
400 )?,
401 ),
402 )
403}
404fn aura_prowl(
405 a: AuraDefinitionDraft,
406 data: &ResolvedGameData,
407) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
408 Ok(
409 a
410 .on_player()
411 .apply_base_from_data(data, 5215)?
412 .max_stacks(
413 data
414 .require_aura_max_stacks(
415 wowlab_types::sim::SpellIdx::from_raw(5215),
416 )?,
417 ),
418 )
419}
420fn aura_cat_form(
421 a: AuraDefinitionDraft,
422 data: &ResolvedGameData,
423) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
424 Ok(
425 a
426 .on_player()
427 .apply_base_from_data(data, 768)?
428 .max_stacks(
429 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(768))?,
430 ),
431 )
432}
433fn aura_mark_of_the_wild_highlight(
434 a: AuraDefinitionDraft,
435 data: &ResolvedGameData,
436) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
437 Ok(
438 a
439 .on_player()
440 .apply_base_from_data(data, 1126)?
441 .max_stacks(
442 data
443 .require_aura_max_stacks(
444 wowlab_types::sim::SpellIdx::from_raw(1126),
445 )?,
446 ),
447 )
448}
449fn aura_lunar_inspiration(
450 a: AuraDefinitionDraft,
451 data: &ResolvedGameData,
452) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
453 Ok(
454 a
455 .on_player()
456 .apply_base_from_data(data, 155_580)?
457 .max_stacks(
458 data
459 .require_aura_max_stacks(
460 wowlab_types::sim::SpellIdx::from_raw(155_580),
461 )?,
462 ),
463 )
464}
465fn aura_rip(
466 a: AuraDefinitionDraft,
467 data: &ResolvedGameData,
468) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
469 a.on_target()
470 .apply_base_from_data(data, 1079)?
471 .max_stacks(
472 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(1079))?,
473 )
474 .snapshot()
475 .periodic_damage(
476 wowlab_engine_combat::validated_milliseconds(
477 (data.period(wowlab_types::sim::SpellIdx::from_raw(1079), 1)) * 0.8,
478 1079,
479 "periodic_damage.tick_ms",
480 )?,
481 (((data.ap_coef(wowlab_types::sim::SpellIdx::from_raw(1079), 1)) * 1.3)
482 * 100_000.0)
483 .round() / 100_000.0,
484 wowlab_engine_combat::DamageFlags::PHYSICAL,
485 )
486 .apply_periodic_behavior_from_data(data, 1079)
487}
488fn aura_rake(
489 a: AuraDefinitionDraft,
490 data: &ResolvedGameData,
491) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
492 a.on_target()
493 .apply_base_from_data(data, 155_722)?
494 .max_stacks(
495 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(155_722))?,
496 )
497 .snapshot()
498 .periodic_damage(
499 wowlab_engine_combat::validated_milliseconds(
500 (data.period(wowlab_types::sim::SpellIdx::from_raw(155_722), 1)) * 0.8,
501 155_722,
502 "periodic_damage.tick_ms",
503 )?,
504 0.18,
505 wowlab_engine_combat::DamageFlags::PHYSICAL,
506 )
507 .apply_periodic_behavior_from_data(data, 155_722)
508}
509fn aura_sabertooth(
510 a: AuraDefinitionDraft,
511 data: &ResolvedGameData,
512) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
513 a.on_target()
514 .apply_base_from_data(data, 391_722)?
515 .no_pandemic()
516 .max_stacks(
517 wowlab_engine_combat::validated_max_stacks(5.0, 391_722, "aura.max_stacks")?,
518 )
519 .damage_taken_from_caster_spells(
520 data,
521 data.base_points(wowlab_types::sim::SpellIdx::from_raw(202_031), 2),
522 )
523}
524fn aura_unseen_slash(
525 a: AuraDefinitionDraft,
526 data: &ResolvedGameData,
527) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
528 a.on_target()
529 .apply_base_from_data(data, 1_271_863)?
530 .max_stacks(
531 data
532 .require_aura_max_stacks(
533 wowlab_types::sim::SpellIdx::from_raw(1_271_863),
534 )?,
535 )
536 .snapshot()
537 .periodic_damage(
538 wowlab_engine_combat::validated_milliseconds(
539 (data.period(wowlab_types::sim::SpellIdx::from_raw(1_271_863), 1)) * 0.8,
540 1_271_863,
541 "periodic_damage.tick_ms",
542 )?,
543 (((data.ap_coef(wowlab_types::sim::SpellIdx::from_raw(1_271_863), 1)) * 1.3)
544 * 1000.0)
545 .round() / 1000.0,
546 wowlab_engine_combat::DamageFlags::PHYSICAL,
547 )
548 .apply_periodic_behavior_from_data(data, 1_271_863)
549}
550fn aura_feral_frenzy(
551 a: AuraDefinitionDraft,
552 data: &ResolvedGameData,
553) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
554 a.on_target()
555 .apply_base_from_data(data, 274_838)?
556 .max_stacks(
557 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(274_838))?,
558 )
559 .snapshot()
560 .periodic_damage(
561 wowlab_engine_combat::validated_milliseconds(
562 (data.period(wowlab_types::sim::SpellIdx::from_raw(274_838), 2)) * 0.8,
563 274_838,
564 "periodic_damage.tick_ms",
565 )?,
566 (((data.ap_coef(wowlab_types::sim::SpellIdx::from_raw(274_838), 2)) * 5.0)
567 * 10000.0)
568 .round() / 10000.0,
569 wowlab_engine_combat::DamageFlags::PHYSICAL,
570 )
571 .apply_periodic_behavior_from_data(data, 274_838)
572}
573fn aura_tigers_fury(
574 a: AuraDefinitionDraft,
575 data: &ResolvedGameData,
576) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
577 Ok(
578 a
579 .on_player()
580 .apply_base_from_data(data, 5217)?
581 .max_stacks(
582 data
583 .require_aura_max_stacks(
584 wowlab_types::sim::SpellIdx::from_raw(5217),
585 )?,
586 ),
587 )
588}
589fn aura_berserk(
590 a: AuraDefinitionDraft,
591 data: &ResolvedGameData,
592) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
593 Ok(
594 a
595 .on_player()
596 .apply_base_from_data(data, 106_951)?
597 .max_stacks(
598 data
599 .require_aura_max_stacks(
600 wowlab_types::sim::SpellIdx::from_raw(106_951),
601 )?,
602 ),
603 )
604}
605fn aura_incarnation_avatar_of_ashamane(
606 a: AuraDefinitionDraft,
607 data: &ResolvedGameData,
608) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
609 Ok(
610 a
611 .on_player()
612 .apply_base_from_data(data, 102_543)?
613 .max_stacks(
614 data
615 .require_aura_max_stacks(
616 wowlab_types::sim::SpellIdx::from_raw(102_543),
617 )?,
618 ),
619 )
620}
621fn aura_apex_predators_craving(
622 a: AuraDefinitionDraft,
623 data: &ResolvedGameData,
624) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
625 Ok(
626 a
627 .on_player()
628 .apply_base_from_data(data, 391_882)?
629 .max_stacks(
630 data
631 .require_aura_max_stacks(
632 wowlab_types::sim::SpellIdx::from_raw(391_882),
633 )?,
634 ),
635 )
636}
637fn aura_savage_fury(
638 a: AuraDefinitionDraft,
639 data: &ResolvedGameData,
640) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
641 Ok(
642 a
643 .on_player()
644 .apply_base_from_data(data, 449_646)?
645 .max_stacks(
646 data
647 .require_aura_max_stacks(
648 wowlab_types::sim::SpellIdx::from_raw(449_646),
649 )?,
650 ),
651 )
652}
653fn aura_chomp_enabler(
654 a: AuraDefinitionDraft,
655 data: &ResolvedGameData,
656) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
657 Ok(
658 a
659 .on_player()
660 .apply_base_from_data(data, 1_244_292)?
661 .max_stacks(
662 data
663 .require_aura_max_stacks(
664 wowlab_types::sim::SpellIdx::from_raw(1_244_292),
665 )?,
666 ),
667 )
668}
669fn aura_overflowing_power(
670 a: AuraDefinitionDraft,
671 data: &ResolvedGameData,
672) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
673 Ok(
674 a
675 .on_player()
676 .apply_base_from_data(data, 405_189)?
677 .max_stacks(
678 data
679 .require_aura_max_stacks(
680 wowlab_types::sim::SpellIdx::from_raw(405_189),
681 )?,
682 ),
683 )
684}
685fn aura_tigers_tenacity(
686 a: AuraDefinitionDraft,
687 data: &ResolvedGameData,
688) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
689 Ok(
690 a
691 .on_player()
692 .apply_base_from_data(data, 391_873)?
693 .max_stacks(
694 wowlab_engine_combat::validated_max_stacks(
695 3.0,
696 391_873,
697 "aura.max_stacks",
698 )?,
699 ),
700 )
701}
702fn aura_sudden_ambush(
703 a: AuraDefinitionDraft,
704 data: &ResolvedGameData,
705) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
706 Ok(
707 a
708 .on_player()
709 .apply_base_from_data(data, 1_244_483)?
710 .max_stacks(
711 data
712 .require_aura_max_stacks(
713 wowlab_types::sim::SpellIdx::from_raw(1_244_483),
714 )?,
715 ),
716 )
717}
718fn aura_strategic_infusion(
719 a: AuraDefinitionDraft,
720 data: &ResolvedGameData,
721) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
722 Ok(
723 a
724 .on_player()
725 .apply_base_from_data(data, 439_891)?
726 .max_stacks(
727 data
728 .require_aura_max_stacks(
729 wowlab_types::sim::SpellIdx::from_raw(439_891),
730 )?,
731 ),
732 )
733}
734fn aura_lycaras_teachings_crit(
735 a: AuraDefinitionDraft,
736 data: &ResolvedGameData,
737) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
738 a.on_player()
739 .apply_base_from_data(data, 378_990)?
740 .max_stacks(
741 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(378_990))?,
742 )
743 .crit_per_stack(
744 data.base_points(wowlab_types::sim::SpellIdx::from_raw(378_988), 1),
745 )
746}
747fn aura_flash_of_clarity(
748 a: AuraDefinitionDraft,
749 data: &ResolvedGameData,
750) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
751 Ok(
752 a
753 .on_player()
754 .apply_base_from_data(data, 1_272_262)?
755 .max_stacks(
756 data
757 .require_aura_max_stacks(
758 wowlab_types::sim::SpellIdx::from_raw(1_272_262),
759 )?,
760 ),
761 )
762}
763fn aura_unseen_predators_craving(
764 a: AuraDefinitionDraft,
765 data: &ResolvedGameData,
766) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
767 a.on_player()
768 .apply_base_from_data(data, 1_263_939)?
769 .max_stacks(
770 data
771 .require_aura_max_stacks(
772 wowlab_types::sim::SpellIdx::from_raw(1_263_939),
773 )?,
774 )
775 .no_pandemic()
776 .forwarded_effect_value(
777 data,
778 data.base_points(wowlab_types::sim::SpellIdx::from_raw(1_263_658), 1),
779 )
780}
781fn aura_stalking_predator(
782 a: AuraDefinitionDraft,
783 data: &ResolvedGameData,
784) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
785 Ok(
786 a
787 .on_player()
788 .apply_base_from_data(data, 1_263_962)?
789 .max_stacks(
790 data
791 .require_aura_max_stacks(
792 wowlab_types::sim::SpellIdx::from_raw(1_263_962),
793 )?,
794 ),
795 )
796}
797fn aura_bloodseeker_vines(
798 a: AuraDefinitionDraft,
799 data: &ResolvedGameData,
800) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
801 Ok(
802 a
803 .on_target()
804 .apply_base_from_data(data, 439_531)?
805 .max_stacks(
806 data
807 .require_aura_max_stacks(
808 wowlab_types::sim::SpellIdx::from_raw(439_531),
809 )?,
810 )
811 .snapshot()
812 .async_stacks()
813 .periodic_damage_scales_with_stacks(),
814 )
815}
816fn spell_shred(
817 s: SpellDefinitionDraft,
818 data: &ResolvedGameData,
819) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
820 Ok(
821 s
822 .apply_base_from_data(data, 5221)?
823 .apply_aoe_from_data(data)
824 .on_cast(crate::hooks::feral_druid::shred_hook)
825 .cost_free_when_aura(135_700)
826 .consume_aura_on_cast(1_244_483),
827 )
828}
829fn spell_swipe(
830 s: SpellDefinitionDraft,
831 data: &ResolvedGameData,
832) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
833 Ok(
834 s
835 .apply_base_from_data(data, 106_785)?
836 .apply_aoe_from_data(data)
837 .on_cast(crate::hooks::feral_druid::builder_hook)
838 .consume_aura_on_cast(1_244_483),
839 )
840}
841fn spell_moonfire(
842 s: SpellDefinitionDraft,
843 data: &ResolvedGameData,
844) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
845 Ok(
846 s
847 .apply_base_from_data(data, 155_625)?
848 .applies_inferred_aura(AURA_MOONFIRE, 155_625)
849 .apply_aoe_from_data(data)
850 .on_cast(crate::hooks::feral_druid::moonfire_hook),
851 )
852}
853fn spell_cat_form(
854 s: SpellDefinitionDraft,
855 data: &ResolvedGameData,
856) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
857 Ok(
858 s
859 .apply_base_from_data(data, 768)?
860 .applies_inferred_aura(AURA_CAT_FORM, 768)
861 .apply_aoe_from_data(data)
862 .on_cast(crate::hooks::feral_druid::cat_form_hook),
863 )
864}
865fn spell_mark_of_the_wild(
866 s: SpellDefinitionDraft,
867 data: &ResolvedGameData,
868) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
869 Ok(
870 s
871 .apply_base_from_data(data, 1126)?
872 .applies_inferred_aura(AURA_MARK_OF_THE_WILD_HIGHLIGHT, 1126)
873 .apply_aoe_from_data(data),
874 )
875}
876fn spell_waiting_for_energy(
877 s: SpellDefinitionDraft,
878 data: &ResolvedGameData,
879) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
880 Ok(s.apply_base_from_data(data, 1_249_752)?.apply_aoe_from_data(data))
881}
882fn spell_rake(
883 s: SpellDefinitionDraft,
884 data: &ResolvedGameData,
885) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
886 Ok(
887 s
888 .apply_base_from_data(data, 1822)?
889 .applies_aura(AURA_RAKE)
890 .apply_aoe_from_data(data)
891 .on_cast(crate::hooks::feral_druid::rake_hook)
892 .consume_aura_on_cast(1_244_483),
893 )
894}
895fn spell_rip(
896 s: SpellDefinitionDraft,
897 data: &ResolvedGameData,
898) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
899 Ok(
900 s
901 .apply_base_from_data(data, 1079)?
902 .applies_inferred_aura(AURA_RIP, 1079)
903 .apply_aoe_from_data(data)
904 .on_cast(crate::hooks::feral_druid::rip_hook),
905 )
906}
907fn spell_ferocious_bite(
908 s: SpellDefinitionDraft,
909 data: &ResolvedGameData,
910) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
911 Ok(
912 s
913 .apply_base_from_data(data, 22568)?
914 .damage_flat(0.0)
915 .apply_aoe_from_data(data)
916 .on_cast(crate::hooks::feral_druid::ferocious_bite_hook),
917 )
918}
919fn spell_primal_wrath(
920 s: SpellDefinitionDraft,
921 data: &ResolvedGameData,
922) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
923 Ok(s.apply_base_from_data(data, 285_381)?.apply_aoe_from_data(data))
924}
925fn spell_feral_frenzy(
926 s: SpellDefinitionDraft,
927 data: &ResolvedGameData,
928) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
929 Ok(
930 s
931 .apply_base_from_data(data, 274_837)?
932 .applies_aura(AURA_FERAL_FRENZY)
933 .apply_aoe_from_data(data)
934 .on_cast(crate::hooks::feral_druid::feral_frenzy_hook),
935 )
936}
937fn spell_frantic_frenzy(
938 s: SpellDefinitionDraft,
939 data: &ResolvedGameData,
940) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
941 Ok(s.apply_base_from_data(data, 1_243_807)?.apply_aoe_from_data(data))
942}
943fn spell_tigers_fury(
944 s: SpellDefinitionDraft,
945 data: &ResolvedGameData,
946) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
947 Ok(
948 s
949 .apply_base_from_data(data, 5217)?
950 .applies_inferred_aura(AURA_TIGERS_FURY, 5217)
951 .apply_aoe_from_data(data)
952 .on_cast(crate::hooks::feral_druid::tigers_fury_hook),
953 )
954}
955fn spell_berserk(
956 s: SpellDefinitionDraft,
957 data: &ResolvedGameData,
958) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
959 Ok(
960 s
961 .apply_base_from_data(data, 106_951)?
962 .applies_inferred_aura(AURA_BERSERK, 106_951)
963 .apply_aoe_from_data(data),
964 )
965}
966fn spell_incarnation_avatar_of_ashamane(
967 s: SpellDefinitionDraft,
968 data: &ResolvedGameData,
969) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
970 Ok(
971 s
972 .apply_base_from_data(data, 102_543)?
973 .applies_inferred_aura(AURA_INCARNATION_AVATAR_OF_ASHAMANE, 102_543)
974 .apply_aoe_from_data(data),
975 )
976}
977fn spell_convoke_the_spirits(
978 s: SpellDefinitionDraft,
979 data: &ResolvedGameData,
980) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
981 Ok(
982 s
983 .apply_base_from_data(data, 391_528)?
984 .apply_aoe_from_data(data)
985 .channel(
986 data
987 .require_channel_tick_count(
988 wowlab_types::sim::SpellIdx::from_raw(391_528),
989 )?,
990 )
991 .on_channel_tick(crate::hooks::feral_druid::convoke_tick_hook)
992 .on_cast(crate::hooks::feral_druid::convoke_cast_hook),
993 )
994}
995fn spell_wrath(
996 s: SpellDefinitionDraft,
997 data: &ResolvedGameData,
998) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
999 Ok(s.apply_base_from_data(data, 5176)?.apply_aoe_from_data(data))
1000}
1001fn spell_chomp(
1002 s: SpellDefinitionDraft,
1003 data: &ResolvedGameData,
1004) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
1005 Ok(
1006 s
1007 .apply_base_from_data(data, 1_244_292)?
1008 .applies_inferred_aura(AURA_CHOMP_ENABLER, 1_244_292)
1009 .apply_aoe_from_data(data),
1010 )
1011}
1012pub(crate) fn build_combat_system(
1013 params: &wowlab_engine_ports::HandlerParams<'_>,
1014) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
1015 let builder = BuiltCombatSystem::builder(params.stats.clone())
1016 .spec_id(SpecId::Feral)
1017 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
1018 .mastery_spell(77493)
1019 .mastery_hook(crate::hooks::feral_druid::mastery)
1020 .resource(
1021 "energy",
1022 params
1023 .game_data
1024 .power_max(3)
1025 .ok_or_else(|| {
1026 wowlab_engine_ports::EngineError::spec_construction(
1027 "missing power-type power_max for type_id 3",
1028 )
1029 })?,
1030 12.65,
1031 )
1032 .resource_type_id(3)
1033 .resource_start(
1034 params
1035 .game_data
1036 .power_default(3)
1037 .ok_or_else(|| {
1038 wowlab_engine_ports::EngineError::spec_construction(
1039 "missing power-type power_default for type_id 3",
1040 )
1041 })?,
1042 )
1043 .secondary_resource(
1044 "combo_points",
1045 params
1046 .game_data
1047 .power_max(4)
1048 .ok_or_else(|| {
1049 wowlab_engine_ports::EngineError::spec_construction(
1050 "missing power-type power_max for type_id 4",
1051 )
1052 })?,
1053 )
1054 .secondary_resource_type_id(4)
1055 .aura("moonfire", AURA::MOONFIRE, |a| aura_moonfire(a, ¶ms.game_data))
1056 .aura(
1057 "clearcasting",
1058 AURA::CLEARCASTING,
1059 |a| aura_clearcasting(a, ¶ms.game_data),
1060 )
1061 .aura("prowl", AURA::PROWL, |a| aura_prowl(a, ¶ms.game_data))
1062 .aura("cat_form", AURA::CAT_FORM, |a| aura_cat_form(a, ¶ms.game_data))
1063 .aura(
1064 "mark_of_the_wild_highlight",
1065 AURA::MARK_OF_THE_WILD_HIGHLIGHT,
1066 |a| aura_mark_of_the_wild_highlight(a, ¶ms.game_data),
1067 )
1068 .aura(
1069 "lunar_inspiration",
1070 AURA::LUNAR_INSPIRATION,
1071 |a| aura_lunar_inspiration(a, ¶ms.game_data),
1072 )
1073 .aura("rip", AURA::RIP, |a| aura_rip(a, ¶ms.game_data))
1074 .aura("rake", AURA::RAKE, |a| aura_rake(a, ¶ms.game_data))
1075 .aura("sabertooth", AURA::SABERTOOTH, |a| aura_sabertooth(a, ¶ms.game_data))
1076 .aura(
1077 "unseen_slash",
1078 AURA::UNSEEN_SLASH,
1079 |a| aura_unseen_slash(a, ¶ms.game_data),
1080 )
1081 .aura(
1082 "feral_frenzy",
1083 AURA::FERAL_FRENZY,
1084 |a| aura_feral_frenzy(a, ¶ms.game_data),
1085 )
1086 .aura(
1087 "tigers_fury",
1088 AURA::TIGERS_FURY,
1089 |a| aura_tigers_fury(a, ¶ms.game_data),
1090 )
1091 .aura("berserk", AURA::BERSERK, |a| aura_berserk(a, ¶ms.game_data))
1092 .aura(
1093 "incarnation_avatar_of_ashamane",
1094 AURA::INCARNATION_AVATAR_OF_ASHAMANE,
1095 |a| aura_incarnation_avatar_of_ashamane(a, ¶ms.game_data),
1096 )
1097 .aura(
1098 "apex_predators_craving",
1099 AURA::APEX_PREDATORS_CRAVING,
1100 |a| aura_apex_predators_craving(a, ¶ms.game_data),
1101 )
1102 .aura(
1103 "savage_fury",
1104 AURA::SAVAGE_FURY,
1105 |a| aura_savage_fury(a, ¶ms.game_data),
1106 )
1107 .aura(
1108 "chomp_enabler",
1109 AURA::CHOMP_ENABLER,
1110 |a| aura_chomp_enabler(a, ¶ms.game_data),
1111 )
1112 .aura(
1113 "overflowing_power",
1114 AURA::OVERFLOWING_POWER,
1115 |a| aura_overflowing_power(a, ¶ms.game_data),
1116 )
1117 .aura(
1118 "tigers_tenacity",
1119 AURA::TIGERS_TENACITY,
1120 |a| aura_tigers_tenacity(a, ¶ms.game_data),
1121 )
1122 .aura(
1123 "sudden_ambush",
1124 AURA::SUDDEN_AMBUSH,
1125 |a| aura_sudden_ambush(a, ¶ms.game_data),
1126 )
1127 .aura(
1128 "strategic_infusion",
1129 AURA::STRATEGIC_INFUSION,
1130 |a| aura_strategic_infusion(a, ¶ms.game_data),
1131 )
1132 .aura(
1133 "lycaras_teachings_crit",
1134 AURA::LYCARAS_TEACHINGS_CRIT,
1135 |a| aura_lycaras_teachings_crit(a, ¶ms.game_data),
1136 )
1137 .aura(
1138 "flash_of_clarity",
1139 AURA::FLASH_OF_CLARITY,
1140 |a| aura_flash_of_clarity(a, ¶ms.game_data),
1141 )
1142 .aura(
1143 "unseen_predators_craving",
1144 AURA::UNSEEN_PREDATORS_CRAVING,
1145 |a| aura_unseen_predators_craving(a, ¶ms.game_data),
1146 )
1147 .aura(
1148 "stalking_predator",
1149 AURA::STALKING_PREDATOR,
1150 |a| aura_stalking_predator(a, ¶ms.game_data),
1151 )
1152 .aura(
1153 "bloodseeker_vines",
1154 AURA::BLOODSEEKER_VINES,
1155 |a| aura_bloodseeker_vines(a, ¶ms.game_data),
1156 )
1157 .spell("shred", SPELL::SHRED, |s| spell_shred(s, ¶ms.game_data))
1158 .spell("swipe", SPELL::SWIPE, |s| spell_swipe(s, ¶ms.game_data))
1159 .spell("moonfire", SPELL::MOONFIRE, |s| spell_moonfire(s, ¶ms.game_data))
1160 .spell("cat_form", SPELL::CAT_FORM, |s| spell_cat_form(s, ¶ms.game_data))
1161 .spell(
1162 "mark_of_the_wild",
1163 SPELL::MARK_OF_THE_WILD,
1164 |s| spell_mark_of_the_wild(s, ¶ms.game_data),
1165 )
1166 .spell(
1167 "waiting_for_energy",
1168 SPELL::WAITING_FOR_ENERGY,
1169 |s| spell_waiting_for_energy(s, ¶ms.game_data),
1170 )
1171 .spell("rake", SPELL::RAKE, |s| spell_rake(s, ¶ms.game_data))
1172 .spell("rip", SPELL::RIP, |s| spell_rip(s, ¶ms.game_data))
1173 .spell(
1174 "ferocious_bite",
1175 SPELL::FEROCIOUS_BITE,
1176 |s| spell_ferocious_bite(s, ¶ms.game_data),
1177 )
1178 .spell(
1179 "primal_wrath",
1180 SPELL::PRIMAL_WRATH,
1181 |s| spell_primal_wrath(s, ¶ms.game_data),
1182 )
1183 .spell(
1184 "feral_frenzy",
1185 SPELL::FERAL_FRENZY,
1186 |s| spell_feral_frenzy(s, ¶ms.game_data),
1187 )
1188 .spell(
1189 "frantic_frenzy",
1190 SPELL::FRANTIC_FRENZY,
1191 |s| spell_frantic_frenzy(s, ¶ms.game_data),
1192 )
1193 .spell(
1194 "tigers_fury",
1195 SPELL::TIGERS_FURY,
1196 |s| spell_tigers_fury(s, ¶ms.game_data),
1197 )
1198 .spell("berserk", SPELL::BERSERK, |s| spell_berserk(s, ¶ms.game_data))
1199 .spell(
1200 "incarnation_avatar_of_ashamane",
1201 SPELL::INCARNATION_AVATAR_OF_ASHAMANE,
1202 |s| spell_incarnation_avatar_of_ashamane(s, ¶ms.game_data),
1203 )
1204 .spell(
1205 "convoke_the_spirits",
1206 SPELL::CONVOKE_THE_SPIRITS,
1207 |s| spell_convoke_the_spirits(s, ¶ms.game_data),
1208 )
1209 .spell("wrath", SPELL::WRATH, |s| spell_wrath(s, ¶ms.game_data))
1210 .spell("chomp", SPELL::CHOMP, |s| spell_chomp(s, ¶ms.game_data))
1211 .auto_attack(|a| {
1212 a
1213 .spell_id(6603)
1214 .swing_ms(1000)
1215 .apply_base_from_data(¶ms.game_data)
1216 .on_swing(crate::hooks::feral_druid::swing_hook)
1217 })
1218 .game_data(params.game_data.clone())
1219 .talent_spell_ids(TALENTS)
1220 .talent_selections(params.talent_selections)
1221 .set_bonus_auras(params.set_bonus_auras)
1222 .encounter(params.encounter.clone());
1223 let builder = crate::generated::items::register_equipped(
1224 builder,
1225 ¶ms.game_data,
1226 params.equipped_items,
1227 )?;
1228
1229 builder
1230 .build(params.rotation)
1231 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
1232}
1233fn new_handler(
1234 params: wowlab_engine_ports::HandlerParams<'_>,
1235) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
1236 let handler = crate::composition::compose_handler(
1237 build_combat_system,
1238 ¶ms,
1239 |parts, params| Ok(crate::hooks::feral_druid::FeralHandler::new(parts, params)),
1240 )?;
1241
1242 Ok(Box::new(handler))
1243}
1244static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
1245 primary_resource: ResourceType::Energy,
1246 secondary_resource: Some(ResourceType::ComboPoints),
1247 spell_ids: DECLARED_SPELL_IDS,
1248 aura_ids: DECLARED_AURA_IDS,
1249 server_proc_entries: &[],
1250 talents: TALENTS,
1251 rotation_field_schema: &[],
1252 metrics_plugin_id: None,
1253 hero_talent_trees: &[
1254 HeroTalentTreeDesc {
1255 name: "Druid Of The Claw",
1256 spells: HERO_TREE_DRUID_OF_THE_CLAW_SPELLS,
1257 auras: HERO_TREE_DRUID_OF_THE_CLAW_AURAS,
1258 },
1259 HeroTalentTreeDesc {
1260 name: "Wildstalker",
1261 spells: HERO_TREE_WILDSTALKER_SPELLS,
1262 auras: HERO_TREE_WILDSTALKER_AURAS,
1263 },
1264 ],
1265 reported_spells: REPORTED_SPELLS,
1266 masked_passive_effects: &[(474_530, 3), (474_530, 4)],
1267 passive_effect_overrides: &[],
1268};
1269pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1270 spec_id: SpecId::Feral,
1271 display_name: "Feral Druid",
1272 metadata: DECLARED_METADATA,
1273 handler_factory: new_handler,
1274};
1275wowlab_engine_combat::smoke_test!(build_combat_system);