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 KILL_COMMAND: u32 = 34026;
16 pub(crate) const BARBED_SHOT: u32 = 217_200;
18 pub(crate) const BESTIAL_WRATH: u32 = 19574;
20 pub(crate) const ARCANE_SHOT: u32 = 193_455;
22 pub(crate) const STEADY_SHOT: u32 = 56641;
24 pub(crate) const BLACK_ARROW: u32 = 466_930;
26 pub(crate) const WILD_THRASH: u32 = 1_264_359;
28 pub(crate) const REVIVE_PET: u32 = 982;
30 pub(crate) const CALL_PET_1: u32 = 883;
32}
33pub(crate) mod AURA {
34 pub(crate) const BESTIAL_WRATH: u32 = 19574;
36 pub(crate) const UNNATURAL_CAUSES: u32 = 459_529;
38 pub(crate) const BARBED_SHOT_DOT: u32 = 217_200;
40 pub(crate) const BARBED_SHOT_REGEN: u32 = 246_152;
42 pub(crate) const BEAST_CLEAVE: u32 = 268_877;
44 pub(crate) const BLOODSHED_DOT: u32 = 321_538;
46 pub(crate) const DIRE_BEAST_ACTIVE: u32 = 219_199;
48 pub(crate) const NATURES_ALLY: u32 = 1_282_474;
50 pub(crate) const NATURES_ALLY_KILL_COMMAND: u32 = 1_276_720;
52 pub(crate) const BLOODY_FRENZY: u32 = 1_265_063;
54 pub(crate) const HOWL_COOLDOWN: u32 = 471_877;
56 pub(crate) const WYVERN_READY: u32 = 471_878;
58 pub(crate) const BOAR_READY: u32 = 472_324;
60 pub(crate) const BEAR_READY: u32 = 472_325;
62 pub(crate) const WYVERNS_CRY: u32 = 471_881;
64 pub(crate) const HUNTMASTERS_CALL: u32 = 459_731;
66 pub(crate) const BEAR_ACTIVE: u32 = 471_993;
68 pub(crate) const REND_FLESH: u32 = 471_999;
70}
71pub(crate) mod TALENT {
72 pub(crate) const BARBED_SHOT: u32 = 217_200;
74 pub(crate) const BESTIAL_WRATH: u32 = 19574;
76 pub(crate) const BLACK_ARROW: u32 = 466_930;
78 pub(crate) const BLOODY_FRENZY: u32 = 407_412;
80 pub(crate) const DIRE_BEAST: u32 = 120_679;
82 pub(crate) const KILL_COMMAND: u32 = 34026;
84 pub(crate) const PACK_TACTICS: u32 = 321_014;
86 pub(crate) const UNNATURAL_CAUSES: u32 = 459_527;
88 pub(crate) const WILD_THRASH: u32 = 1_264_359;
90}
91pub(crate) mod HERO {
92 pub(crate) mod PACK_LEADER {
93 pub(crate) mod SPELL {
94 pub(crate) const BOAR_CHARGE: u32 = 471_936;
96 pub(crate) const BOAR_CHARGE_CLEAVE: u32 = 471_938;
98 pub(crate) const STAMPEDE_TICK: u32 = 201_594;
100 }
101 }
102 pub(crate) mod BM_PETS {
103 pub(crate) mod SPELL {
104 pub(crate) const BEAST_CLEAVE: u32 = 118_459;
106 pub(crate) const BLOODSHED: u32 = 321_538;
108 pub(crate) const CLAW: u32 = 16827;
110 pub(crate) const KILL_COMMAND_PET: u32 = 83381;
112 pub(crate) const MELEE: u32 = 6603;
114 pub(crate) const PET_BESTIAL_WRATH: u32 = 344_572;
116 pub(crate) const RAVENOUS_LEAP: u32 = 459_753;
118 pub(crate) const STOMP: u32 = 201_754;
120 pub(crate) const WILDSPEAKER_KILL_COMMAND: u32 = 1_232_922;
122 pub(crate) const WILD_THRASH: u32 = 1_264_355;
124 }
125 }
126}
127static HERO_TREE_PACK_LEADER_SPELLS: &[(&str, u32)] = &[
128 ("BOAR_CHARGE", 471_936),
129 ("BOAR_CHARGE_CLEAVE", 471_938),
130 ("STAMPEDE_TICK", 201_594),
131];
132static HERO_TREE_PACK_LEADER_AURAS: &[(&str, u32)] = &[];
133static HERO_TREE_BM_PETS_SPELLS: &[(&str, u32)] = &[
134 ("BEAST_CLEAVE", 118_459),
135 ("BLOODSHED", 321_538),
136 ("CLAW", 16827),
137 ("KILL_COMMAND_PET", 83381),
138 ("MELEE", 6603),
139 ("PET_BESTIAL_WRATH", 344_572),
140 ("RAVENOUS_LEAP", 459_753),
141 ("STOMP", 201_754),
142 ("WILDSPEAKER_KILL_COMMAND", 1_232_922),
143 ("WILD_THRASH", 1_264_355),
144];
145static HERO_TREE_BM_PETS_AURAS: &[(&str, u32)] = &[];
146pub(crate) const AURA_BESTIAL_WRATH: LocalAuraIdx = LocalAuraIdx::new(0);
147pub(crate) const AURA_UNNATURAL_CAUSES: LocalAuraIdx = LocalAuraIdx::new(1);
148pub(crate) const AURA_BARBED_SHOT_DOT: LocalAuraIdx = LocalAuraIdx::new(2);
149pub(crate) const AURA_BARBED_SHOT_REGEN: LocalAuraIdx = LocalAuraIdx::new(3);
150pub(crate) const AURA_BEAST_CLEAVE: LocalAuraIdx = LocalAuraIdx::new(4);
151pub(crate) const AURA_BLOODSHED_DOT: LocalAuraIdx = LocalAuraIdx::new(5);
152pub(crate) const AURA_DIRE_BEAST_ACTIVE: LocalAuraIdx = LocalAuraIdx::new(6);
153pub(crate) const AURA_NATURES_ALLY: LocalAuraIdx = LocalAuraIdx::new(7);
154pub(crate) const AURA_NATURES_ALLY_KILL_COMMAND: LocalAuraIdx = LocalAuraIdx::new(8);
155pub(crate) const AURA_BLOODY_FRENZY: LocalAuraIdx = LocalAuraIdx::new(9);
156pub(crate) const AURA_HOWL_COOLDOWN: LocalAuraIdx = LocalAuraIdx::new(10);
157pub(crate) const AURA_WYVERN_READY: LocalAuraIdx = LocalAuraIdx::new(11);
158pub(crate) const AURA_BOAR_READY: LocalAuraIdx = LocalAuraIdx::new(12);
159pub(crate) const AURA_BEAR_READY: LocalAuraIdx = LocalAuraIdx::new(13);
160pub(crate) const AURA_WYVERNS_CRY: LocalAuraIdx = LocalAuraIdx::new(14);
161pub(crate) const AURA_HUNTMASTERS_CALL: LocalAuraIdx = LocalAuraIdx::new(15);
162pub(crate) const AURA_BEAR_ACTIVE: LocalAuraIdx = LocalAuraIdx::new(16);
163pub(crate) const AURA_REND_FLESH: LocalAuraIdx = LocalAuraIdx::new(17);
164pub(crate) const AURA_COUNT: usize = 18;
165const _: () = assert!(
166 AURA_COUNT == 18, "AURA index ABI: count changed -- update positional hook consumers"
167);
168pub(crate) const SPELL_KILL_COMMAND: LocalSpellIdx = LocalSpellIdx::new(0);
169pub(crate) const SPELL_BARBED_SHOT: LocalSpellIdx = LocalSpellIdx::new(1);
170pub(crate) const SPELL_BESTIAL_WRATH: LocalSpellIdx = LocalSpellIdx::new(2);
171pub(crate) const SPELL_ARCANE_SHOT: LocalSpellIdx = LocalSpellIdx::new(3);
172pub(crate) const SPELL_STEADY_SHOT: LocalSpellIdx = LocalSpellIdx::new(4);
173pub(crate) const SPELL_BLACK_ARROW: LocalSpellIdx = LocalSpellIdx::new(5);
174pub(crate) const SPELL_WILD_THRASH: LocalSpellIdx = LocalSpellIdx::new(6);
175pub(crate) const SPELL_REVIVE_PET: LocalSpellIdx = LocalSpellIdx::new(7);
176pub(crate) const SPELL_CALL_PET_1: LocalSpellIdx = LocalSpellIdx::new(8);
177pub(crate) const SPELL_COUNT: usize = 9;
178const _: () = assert!(
179 SPELL_COUNT == 9,
180 "SPELL index ABI: count changed -- update positional hook consumers"
181);
182pub(crate) mod EFFECT {
183 pub(crate) const ALPHA_PREDATOR_KC_PCT: (u32, u8) = (269_737, 2);
185 pub(crate) const BOAR_CHARGE: (u32, u8) = (471_936, 1);
187 pub(crate) const BOAR_CHARGE_CLEAVE: (u32, u8) = (471_938, 1);
189 pub(crate) const DIRE_COMMAND_PCT: (u32, u8) = (378_743, 1);
191 pub(crate) const HOWL_WYVERN_STACKS: (u32, u8) = (471_876, 3);
193 pub(crate) const KC_PET_DAMAGE: (u32, u8) = (83381, 1);
195 pub(crate) const PACK_MENTALITY_BARBED_CDR: (u32, u8) = (472_358, 2);
197 pub(crate) const PACK_MENTALITY_KC_PCT: (u32, u8) = (472_358, 1);
199 pub(crate) const PACK_TACTICS_FOCUS: (u32, u8) = (1_282_660, 1);
201 pub(crate) const PET_BW_DAMAGE: (u32, u8) = (344_572, 1);
203 pub(crate) const RAVENOUS_LEAP: (u32, u8) = (459_753, 1);
205 pub(crate) const STAMPEDE_DAMAGE: (u32, u8) = (201_594, 1);
207 pub(crate) const STAMPEDE_GEOMETRY: (u32, u8) = (1_258_344, 1);
209 pub(crate) const STOMP_DAMAGE: (u32, u8) = (201_754, 1);
211 pub(crate) const BEAST_CLEAVE_DAMAGE: (u32, u8) = (118_459, 1);
213 pub(crate) const BEAST_CLEAVE_FRACTION: (u32, u8) = (115_939, 1);
215 pub(crate) const BEAST_CLEAVE_REDUCED_TARGETS: (u32, u8) = (118_459, 2);
217 pub(crate) const BEAST_CLEAVE_DURATION: (u32, u8) = (115_939, 2);
219 pub(crate) const THUNDERING_HOOVES_PCT: (u32, u8) = (459_693, 1);
221 pub(crate) const WILD_THRASH_DAMAGE: (u32, u8) = (1_264_355, 1);
223 pub(crate) const WILD_THRASH_BONUS: (u32, u8) = (1_264_359, 1);
225 pub(crate) const WILD_THRASH_THRESHOLD: (u32, u8) = (1_264_359, 2);
227 pub(crate) const WILDSPEAKER_KC: (u32, u8) = (1_232_922, 1);
229 pub(crate) const WYVERNS_CRY_PCT: (u32, u8) = (471_881, 1);
231}
232static REPORTED_SPELLS: &[(&str, u32)] = &[];
233static DECLARED_SPELL_IDS: &[u32] = &[
234 34026, 217_200, 19574, 193_455, 56641, 466_930, 1_264_359, 982, 883, 19574, 459_529,
235 217_200, 246_152, 268_877, 321_538, 219_199, 1_282_474, 1_276_720, 1_265_063,
236 471_877, 471_878, 472_324, 472_325, 471_881, 459_731, 471_993, 471_999, 75, 6603,
237 76657, 83381, 115_939, 118_459, 120_679, 186_254, 193_455, 201_594, 201_754, 269_737,
238 321_538, 344_572, 378_743, 459_693, 459_753, 471_876, 471_881, 471_936, 471_938,
239 471_999, 472_358, 1_232_922, 1_258_344, 1_264_355, 1_264_359, 1_282_660, 471_936,
240 471_938, 201_594, 118_459, 321_538, 16827, 83381, 6603, 344_572, 459_753, 201_754,
241 1_232_922, 1_264_355,
242];
243static DECLARED_AURA_IDS: &[u32] = &[
244 19574, 459_529, 217_200, 246_152, 268_877, 321_538, 219_199, 1_282_474, 1_276_720,
245 1_265_063, 471_877, 471_878, 472_324, 472_325, 471_881, 459_731, 471_993, 471_999,
246];
247static TALENTS: &[(&str, u32)] = &[
248 ("BARBED_SHOT", TALENT::BARBED_SHOT),
249 ("BESTIAL_WRATH", TALENT::BESTIAL_WRATH),
250 ("BLACK_ARROW", TALENT::BLACK_ARROW),
251 ("BLOODY_FRENZY", TALENT::BLOODY_FRENZY),
252 ("DIRE_BEAST", TALENT::DIRE_BEAST),
253 ("KILL_COMMAND", TALENT::KILL_COMMAND),
254 ("PACK_TACTICS", TALENT::PACK_TACTICS),
255 ("UNNATURAL_CAUSES", TALENT::UNNATURAL_CAUSES),
256 ("WILD_THRASH", TALENT::WILD_THRASH),
257];
258pub(crate) mod BM_HUNTER {
259 pub(crate) const KILL_COMMAND_CASTS: u32 = 0;
260 pub(crate) const BARBED_SHOT_CASTS: u32 = 1;
261 pub(crate) const BESTIAL_WRATH_CASTS: u32 = 2;
262 pub(crate) const COBRA_SHOT_CASTS: u32 = 3;
263 pub(crate) const BESTIAL_WRATH_UPTIME_MS: u32 = 4;
264}
265fn aura_bestial_wrath(
266 a: AuraDefinitionDraft,
267 data: &ResolvedGameData,
268) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
269 a.on_player()
270 .apply_base_from_data(data, 19574)?
271 .max_stacks(
272 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(19574))?,
273 )
274 .pet_damage_multiplier(
275 ((data.base_points(wowlab_types::sim::SpellIdx::from_raw(186_254), 1))
276 * 0.01) + 1.0,
277 )
278}
279fn aura_unnatural_causes(
280 a: AuraDefinitionDraft,
281 data: &ResolvedGameData,
282) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
283 Ok(
284 a
285 .on_target()
286 .apply_base_from_data(data, 459_529)?
287 .max_stacks(
288 data
289 .require_aura_max_stacks(
290 wowlab_types::sim::SpellIdx::from_raw(459_529),
291 )?,
292 ),
293 )
294}
295fn aura_barbed_shot_dot(
296 a: AuraDefinitionDraft,
297 data: &ResolvedGameData,
298) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
299 Ok(
300 a
301 .on_target()
302 .apply_base_from_data(data, 217_200)?
303 .max_stacks(
304 data
305 .require_aura_max_stacks(
306 wowlab_types::sim::SpellIdx::from_raw(217_200),
307 )?,
308 ),
309 )
310}
311fn aura_barbed_shot_regen(
312 a: AuraDefinitionDraft,
313 data: &ResolvedGameData,
314) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
315 Ok(
316 a
317 .on_player()
318 .apply_base_from_data(data, 246_152)?
319 .max_stacks(
320 data
321 .require_aura_max_stacks(
322 wowlab_types::sim::SpellIdx::from_raw(246_152),
323 )?,
324 ),
325 )
326}
327fn aura_beast_cleave(
328 a: AuraDefinitionDraft,
329 data: &ResolvedGameData,
330) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
331 Ok(
332 a
333 .on_player()
334 .apply_base_from_data(data, 268_877)?
335 .max_stacks(
336 data
337 .require_aura_max_stacks(
338 wowlab_types::sim::SpellIdx::from_raw(268_877),
339 )?,
340 ),
341 )
342}
343fn aura_bloodshed_dot(
344 a: AuraDefinitionDraft,
345 data: &ResolvedGameData,
346) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
347 a.on_target()
348 .apply_base_from_data(data, 321_538)?
349 .max_stacks(
350 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(321_538))?,
351 )
352 .periodic_damage(
353 wowlab_engine_combat::validated_milliseconds(
354 data.period(wowlab_types::sim::SpellIdx::from_raw(321_538), 1),
355 321_538,
356 "periodic_damage.tick_ms",
357 )?,
358 (((data.ap_coef(wowlab_types::sim::SpellIdx::from_raw(321_538), 1)) * 1.8)
359 * 100.0)
360 .round() / 100.0,
361 wowlab_engine_combat::DamageFlags::PHYSICAL,
362 )
363 .apply_periodic_behavior_from_data(data, 321_538)
364}
365fn aura_dire_beast_active(
366 a: AuraDefinitionDraft,
367 data: &ResolvedGameData,
368) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
369 Ok(
370 a
371 .on_player()
372 .apply_base_from_data(data, 219_199)?
373 .max_stacks(
374 wowlab_engine_combat::validated_max_stacks(
375 6.0,
376 219_199,
377 "aura.max_stacks",
378 )?,
379 ),
380 )
381}
382fn aura_natures_ally(
383 a: AuraDefinitionDraft,
384 data: &ResolvedGameData,
385) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
386 Ok(
387 a
388 .on_player()
389 .apply_base_from_data(data, 1_282_474)?
390 .max_stacks(
391 data
392 .require_aura_max_stacks(
393 wowlab_types::sim::SpellIdx::from_raw(1_282_474),
394 )?,
395 ),
396 )
397}
398fn aura_natures_ally_kill_command(
399 a: AuraDefinitionDraft,
400 data: &ResolvedGameData,
401) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
402 Ok(
403 a
404 .on_player()
405 .apply_base_from_data(data, 1_276_720)?
406 .max_stacks(
407 data
408 .require_aura_max_stacks(
409 wowlab_types::sim::SpellIdx::from_raw(1_276_720),
410 )?,
411 ),
412 )
413}
414fn aura_bloody_frenzy(
415 a: AuraDefinitionDraft,
416 data: &ResolvedGameData,
417) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
418 Ok(
419 a
420 .on_player()
421 .apply_base_from_data(data, 1_265_063)?
422 .max_stacks(
423 data
424 .require_aura_max_stacks(
425 wowlab_types::sim::SpellIdx::from_raw(1_265_063),
426 )?,
427 ),
428 )
429}
430fn aura_howl_cooldown(
431 a: AuraDefinitionDraft,
432 data: &ResolvedGameData,
433) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
434 Ok(
435 a
436 .on_player()
437 .apply_base_from_data(data, 471_877)?
438 .max_stacks(
439 data
440 .require_aura_max_stacks(
441 wowlab_types::sim::SpellIdx::from_raw(471_877),
442 )?,
443 ),
444 )
445}
446fn aura_wyvern_ready(
447 a: AuraDefinitionDraft,
448 data: &ResolvedGameData,
449) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
450 Ok(
451 a
452 .on_player()
453 .apply_base_from_data(data, 471_878)?
454 .max_stacks(
455 data
456 .require_aura_max_stacks(
457 wowlab_types::sim::SpellIdx::from_raw(471_878),
458 )?,
459 ),
460 )
461}
462fn aura_boar_ready(
463 a: AuraDefinitionDraft,
464 data: &ResolvedGameData,
465) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
466 Ok(
467 a
468 .on_player()
469 .apply_base_from_data(data, 472_324)?
470 .max_stacks(
471 data
472 .require_aura_max_stacks(
473 wowlab_types::sim::SpellIdx::from_raw(472_324),
474 )?,
475 ),
476 )
477}
478fn aura_bear_ready(
479 a: AuraDefinitionDraft,
480 data: &ResolvedGameData,
481) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
482 Ok(
483 a
484 .on_player()
485 .apply_base_from_data(data, 472_325)?
486 .max_stacks(
487 data
488 .require_aura_max_stacks(
489 wowlab_types::sim::SpellIdx::from_raw(472_325),
490 )?,
491 ),
492 )
493}
494fn aura_wyverns_cry(
495 a: AuraDefinitionDraft,
496 data: &ResolvedGameData,
497) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
498 Ok(
499 a
500 .on_player()
501 .apply_base_from_data(data, 471_881)?
502 .max_stacks(
503 data
504 .require_aura_max_stacks(
505 wowlab_types::sim::SpellIdx::from_raw(471_881),
506 )?,
507 ),
508 )
509}
510fn aura_huntmasters_call(
511 a: AuraDefinitionDraft,
512 data: &ResolvedGameData,
513) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
514 Ok(
515 a
516 .on_player()
517 .apply_base_from_data(data, 459_731)?
518 .max_stacks(
519 data
520 .require_aura_max_stacks(
521 wowlab_types::sim::SpellIdx::from_raw(459_731),
522 )?,
523 ),
524 )
525}
526fn aura_bear_active(
527 a: AuraDefinitionDraft,
528 data: &ResolvedGameData,
529) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
530 Ok(
531 a
532 .on_player()
533 .apply_base_from_data(data, 471_993)?
534 .max_stacks(
535 data
536 .require_aura_max_stacks(
537 wowlab_types::sim::SpellIdx::from_raw(471_993),
538 )?,
539 ),
540 )
541}
542fn aura_rend_flesh(
543 a: AuraDefinitionDraft,
544 data: &ResolvedGameData,
545) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
546 a.on_target()
547 .apply_base_from_data(data, 471_999)?
548 .max_stacks(
549 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(471_999))?,
550 )
551 .periodic_damage(
552 wowlab_engine_combat::validated_milliseconds(
553 data.period(wowlab_types::sim::SpellIdx::from_raw(471_999), 1),
554 471_999,
555 "periodic_damage.tick_ms",
556 )?,
557 (((data.ap_coef(wowlab_types::sim::SpellIdx::from_raw(471_999), 1)) * 0.6)
558 * 100.0)
559 .round() / 100.0,
560 wowlab_engine_combat::DamageFlags::PHYSICAL,
561 )
562 .apply_periodic_behavior_from_data(data, 471_999)
563}
564fn spell_kill_command(
565 s: SpellDefinitionDraft,
566 data: &ResolvedGameData,
567) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
568 Ok(
569 s
570 .apply_base_from_data(data, 34026)?
571 .apply_aoe_from_data(data)
572 .on_cast(crate::hooks::beast_mastery_hunter::kill_command_hook),
573 )
574}
575fn spell_barbed_shot(
576 s: SpellDefinitionDraft,
577 data: &ResolvedGameData,
578) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
579 Ok(
580 s
581 .apply_base_from_data(data, 217_200)?
582 .applies_inferred_aura(AURA_BARBED_SHOT_DOT, 217_200)
583 .apply_aoe_from_data(data)
584 .on_cast(crate::hooks::beast_mastery_hunter::barbed_shot_hook),
585 )
586}
587fn spell_bestial_wrath(
588 s: SpellDefinitionDraft,
589 data: &ResolvedGameData,
590) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
591 Ok(
592 s
593 .apply_base_from_data(data, 19574)?
594 .applies_inferred_aura(AURA_BESTIAL_WRATH, 19574)
595 .apply_aoe_from_data(data)
596 .on_cast(crate::hooks::beast_mastery_hunter::bestial_wrath_hook),
597 )
598}
599fn spell_arcane_shot(
600 s: SpellDefinitionDraft,
601 data: &ResolvedGameData,
602) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
603 Ok(
604 s
605 .apply_base_from_data(data, 193_455)?
606 .reduces_cd(
607 SPELL_KILL_COMMAND,
608 wowlab_engine_combat::validated_milliseconds(
609 (data.base_points(wowlab_types::sim::SpellIdx::from_raw(193_455), 3))
610 * 1000.0,
611 193_455,
612 "spell.reduces_cd.amount_ms",
613 )?,
614 )
615 .apply_aoe_from_data(data)
616 .on_cast(crate::hooks::beast_mastery_hunter::cobra_shot_hook),
617 )
618}
619fn spell_steady_shot(
620 s: SpellDefinitionDraft,
621 data: &ResolvedGameData,
622) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
623 Ok(s.apply_base_from_data(data, 56641)?.apply_aoe_from_data(data))
624}
625fn spell_black_arrow(
626 s: SpellDefinitionDraft,
627 data: &ResolvedGameData,
628) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
629 Ok(s.apply_base_from_data(data, 466_930)?.apply_aoe_from_data(data))
630}
631fn spell_wild_thrash(
632 s: SpellDefinitionDraft,
633 data: &ResolvedGameData,
634) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
635 Ok(
636 s
637 .apply_base_from_data(data, 1_264_359)?
638 .apply_aoe_from_data(data)
639 .on_cast(crate::hooks::beast_mastery_hunter::wild_thrash_hook),
640 )
641}
642fn spell_revive_pet(
643 s: SpellDefinitionDraft,
644 data: &ResolvedGameData,
645) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
646 Ok(s.apply_base_from_data(data, 982)?.apply_aoe_from_data(data))
647}
648fn spell_call_pet_1(
649 s: SpellDefinitionDraft,
650 data: &ResolvedGameData,
651) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
652 Ok(s.apply_base_from_data(data, 883)?.apply_aoe_from_data(data))
653}
654pub(crate) fn build_combat_system(
655 params: &wowlab_engine_ports::HandlerParams<'_>,
656) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
657 let builder = BuiltCombatSystem::builder(params.stats.clone())
658 .spec_id(SpecId::BeastMastery)
659 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
660 .mastery_spell(76657)
661 .mastery_hook(crate::hooks::beast_mastery_hunter::mastery)
662 .resource(
663 "focus",
664 params
665 .game_data
666 .power_max(2)
667 .ok_or_else(|| {
668 wowlab_engine_ports::EngineError::spec_construction(
669 "missing power-type power_max for type_id 2",
670 )
671 })?,
672 params
673 .game_data
674 .power_regen_for_max(
675 2,
676 params
677 .game_data
678 .power_max(2)
679 .ok_or_else(|| {
680 wowlab_engine_ports::EngineError::spec_construction(
681 "missing power-type power_max for type_id 2",
682 )
683 })?,
684 )
685 .ok_or_else(|| {
686 wowlab_engine_ports::EngineError::spec_construction(
687 "missing power-type power_regen_for_max for type_id 2",
688 )
689 })?,
690 )
691 .resource_type_id(2)
692 .resource_start(
693 params
694 .game_data
695 .power_default(2)
696 .ok_or_else(|| {
697 wowlab_engine_ports::EngineError::spec_construction(
698 "missing power-type power_default for type_id 2",
699 )
700 })?,
701 )
702 .has_pet(true)
703 .aura(
704 "bestial_wrath",
705 AURA::BESTIAL_WRATH,
706 |a| aura_bestial_wrath(a, ¶ms.game_data),
707 )
708 .aura(
709 "unnatural_causes",
710 AURA::UNNATURAL_CAUSES,
711 |a| aura_unnatural_causes(a, ¶ms.game_data),
712 )
713 .aura(
714 "barbed_shot_dot",
715 AURA::BARBED_SHOT_DOT,
716 |a| aura_barbed_shot_dot(a, ¶ms.game_data),
717 )
718 .aura(
719 "barbed_shot_regen",
720 AURA::BARBED_SHOT_REGEN,
721 |a| aura_barbed_shot_regen(a, ¶ms.game_data),
722 )
723 .aura(
724 "beast_cleave",
725 AURA::BEAST_CLEAVE,
726 |a| aura_beast_cleave(a, ¶ms.game_data),
727 )
728 .aura(
729 "bloodshed_dot",
730 AURA::BLOODSHED_DOT,
731 |a| aura_bloodshed_dot(a, ¶ms.game_data),
732 )
733 .aura(
734 "dire_beast_active",
735 AURA::DIRE_BEAST_ACTIVE,
736 |a| aura_dire_beast_active(a, ¶ms.game_data),
737 )
738 .aura(
739 "natures_ally",
740 AURA::NATURES_ALLY,
741 |a| aura_natures_ally(a, ¶ms.game_data),
742 )
743 .aura(
744 "natures_ally_kill_command",
745 AURA::NATURES_ALLY_KILL_COMMAND,
746 |a| aura_natures_ally_kill_command(a, ¶ms.game_data),
747 )
748 .aura(
749 "bloody_frenzy",
750 AURA::BLOODY_FRENZY,
751 |a| aura_bloody_frenzy(a, ¶ms.game_data),
752 )
753 .aura(
754 "howl_cooldown",
755 AURA::HOWL_COOLDOWN,
756 |a| aura_howl_cooldown(a, ¶ms.game_data),
757 )
758 .aura(
759 "wyvern_ready",
760 AURA::WYVERN_READY,
761 |a| aura_wyvern_ready(a, ¶ms.game_data),
762 )
763 .aura("boar_ready", AURA::BOAR_READY, |a| aura_boar_ready(a, ¶ms.game_data))
764 .aura("bear_ready", AURA::BEAR_READY, |a| aura_bear_ready(a, ¶ms.game_data))
765 .aura(
766 "wyverns_cry",
767 AURA::WYVERNS_CRY,
768 |a| aura_wyverns_cry(a, ¶ms.game_data),
769 )
770 .aura(
771 "huntmasters_call",
772 AURA::HUNTMASTERS_CALL,
773 |a| aura_huntmasters_call(a, ¶ms.game_data),
774 )
775 .aura(
776 "bear_active",
777 AURA::BEAR_ACTIVE,
778 |a| aura_bear_active(a, ¶ms.game_data),
779 )
780 .aura("rend_flesh", AURA::REND_FLESH, |a| aura_rend_flesh(a, ¶ms.game_data))
781 .spell(
782 "kill_command",
783 SPELL::KILL_COMMAND,
784 |s| spell_kill_command(s, ¶ms.game_data),
785 )
786 .spell(
787 "barbed_shot",
788 SPELL::BARBED_SHOT,
789 |s| spell_barbed_shot(s, ¶ms.game_data),
790 )
791 .spell(
792 "bestial_wrath",
793 SPELL::BESTIAL_WRATH,
794 |s| spell_bestial_wrath(s, ¶ms.game_data),
795 )
796 .spell(
797 "arcane_shot",
798 SPELL::ARCANE_SHOT,
799 |s| spell_arcane_shot(s, ¶ms.game_data),
800 )
801 .spell(
802 "steady_shot",
803 SPELL::STEADY_SHOT,
804 |s| spell_steady_shot(s, ¶ms.game_data),
805 )
806 .spell(
807 "black_arrow",
808 SPELL::BLACK_ARROW,
809 |s| spell_black_arrow(s, ¶ms.game_data),
810 )
811 .spell(
812 "wild_thrash",
813 SPELL::WILD_THRASH,
814 |s| spell_wild_thrash(s, ¶ms.game_data),
815 )
816 .spell(
817 "revive_pet",
818 SPELL::REVIVE_PET,
819 |s| spell_revive_pet(s, ¶ms.game_data),
820 )
821 .spell(
822 "call_pet_1",
823 SPELL::CALL_PET_1,
824 |s| spell_call_pet_1(s, ¶ms.game_data),
825 )
826 .auto_attack(|a| {
827 a.spell_id(75).swing_ms(3000).apply_base_from_data(¶ms.game_data)
828 })
829 .auto_attack(|a| {
830 a
831 .spell_id(6603)
832 .swing_ms(1667)
833 .apply_base_from_data(¶ms.game_data)
834 .ap_coef(
835 wowlab_engine_domain::damage::pet_swing_power_coefficient(
836 0.6,
837 2000,
838 1.0,
839 ),
840 )
841 .is_pet()
842 .on_swing(crate::hooks::beast_mastery_hunter::pet_swing)
843 })
844 .game_data(params.game_data.clone())
845 .talent_spell_ids(TALENTS)
846 .talent_selections(params.talent_selections)
847 .talent_companion_aura(TALENT::UNNATURAL_CAUSES, AURA_UNNATURAL_CAUSES)
848 .register_impact_effect_proc_if_talent(
849 TALENT::DIRE_BEAST,
850 wowlab_engine_combat::ImpactEffectProcDefinition::new(
851 wowlab_engine_combat::ImpactSource::ProcTypeMask(
852 params
853 .game_data
854 .require_proc_type_mask(
855 wowlab_types::sim::SpellIdx::from_raw(120_679),
856 )?,
857 ),
858 )
859 .dbc_driver_from_data(
860 wowlab_engine_domain::dbc::EffectLookup::new(
861 ¶ms.game_data,
862 wowlab_types::sim::EffectRef::new(
863 wowlab_types::sim::SpellIdx::from_raw(120_679),
864 1,
865 ),
866 ),
867 )
868 .chance(
869 (params
870 .game_data
871 .base_points(wowlab_types::sim::SpellIdx::from_raw(120_679), 1))
872 / 100.0,
873 )
874 .icd_ms(
875 wowlab_engine_combat::validated_milliseconds(
876 f64::from(
877 params
878 .game_data
879 .require_proc_category_recovery_ms(
880 wowlab_types::sim::SpellIdx::from_raw(120_679),
881 )?,
882 ),
883 120_679,
884 "impact_proc.icd_ms",
885 )
886 .map_err(|error| {
887 wowlab_engine_ports::EngineError::spec_construction(
888 error.to_string(),
889 )
890 })?,
891 )
892 .physical_only()
893 .fire(crate::hooks::beast_mastery_hunter::dire_beast_proc),
894 )
895 .set_bonus_auras(params.set_bonus_auras)
896 .encounter(params.encounter.clone());
897 let builder = crate::generated::items::register_equipped(
898 builder,
899 ¶ms.game_data,
900 params.equipped_items,
901 )?;
902
903 builder
904 .build(params.rotation)
905 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
906}
907fn new_handler(
908 params: wowlab_engine_ports::HandlerParams<'_>,
909) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
910 let handler = crate::composition::compose_handler(
911 build_combat_system,
912 ¶ms,
913 crate::hooks::beast_mastery_hunter::BeastMasteryHandler::try_new,
914 )?;
915
916 Ok(Box::new(handler))
917}
918static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
919 primary_resource: ResourceType::Focus,
920 secondary_resource: None,
921 spell_ids: DECLARED_SPELL_IDS,
922 aura_ids: DECLARED_AURA_IDS,
923 server_proc_entries: &[],
924 talents: TALENTS,
925 rotation_field_schema: &[],
926 metrics_plugin_id: None,
927 hero_talent_trees: &[
928 HeroTalentTreeDesc {
929 name: "Pack Leader",
930 spells: HERO_TREE_PACK_LEADER_SPELLS,
931 auras: HERO_TREE_PACK_LEADER_AURAS,
932 },
933 HeroTalentTreeDesc {
934 name: "Bm Pets",
935 spells: HERO_TREE_BM_PETS_SPELLS,
936 auras: HERO_TREE_BM_PETS_AURAS,
937 },
938 ],
939 reported_spells: REPORTED_SPELLS,
940 masked_passive_effects: &[],
941 passive_effect_overrides: &[],
942};
943pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
944 spec_id: SpecId::BeastMastery,
945 display_name: "Beast Mastery Hunter",
946 metadata: DECLARED_METADATA,
947 handler_factory: new_handler,
948};
949wowlab_engine_combat::smoke_test!(build_combat_system);