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 FIREBALL: u32 = 133;
16 pub(crate) const PYROBLAST: u32 = 11366;
18 pub(crate) const FIRE_BLAST: u32 = 108_853;
20 pub(crate) const FLAMESTRIKE: u32 = 2120;
22 pub(crate) const COMBUSTION: u32 = 190_319;
24 pub(crate) const METEOR: u32 = 153_561;
26 pub(crate) const SCORCH: u32 = 2948;
28 pub(crate) const FROSTFIRE_BOLT: u32 = 431_044;
30 pub(crate) const ARCANE_INTELLECT: u32 = 1459;
32 pub(crate) const IGNITE: u32 = 12654;
34 pub(crate) const INTENSIFYING_FLAME: u32 = 419_800;
36 pub(crate) const BURNOUT: u32 = 1_271_335;
38}
39pub(crate) mod AURA {
40 pub(crate) const HEATING_UP: u32 = 48107;
42 pub(crate) const HOT_STREAK: u32 = 48108;
44 pub(crate) const COMBUSTION: u32 = 190_319;
46 pub(crate) const IGNITE: u32 = 12654;
48 pub(crate) const CONTROLLED_DESTRUCTION: u32 = 453_268;
50 pub(crate) const HYPERTHERMIA: u32 = 383_874;
52 pub(crate) const HYPERTHERMIA_DAMAGE: u32 = 1_242_220;
54 pub(crate) const PYROTECHNICS: u32 = 157_644;
56 pub(crate) const PYROCLASM: u32 = 269_651;
58 pub(crate) const FEEL_THE_BURN: u32 = 383_395;
60 pub(crate) const FEVERED_INCANTATION: u32 = 383_811;
62 pub(crate) const FIRED_UP: u32 = 1_257_350;
64 pub(crate) const HEAT_SHIMMER: u32 = 458_964;
66 pub(crate) const FROSTFIRE_EMPOWERMENT: u32 = 431_177;
68 pub(crate) const ARCANE_INTELLECT_HIGHLIGHT: u32 = 1459;
70 pub(crate) const SPELLFIRE_SPHERE: u32 = 448_604;
72 pub(crate) const FIERY_RUSH_EFFECT: u32 = 383_637;
74}
75pub(crate) mod TALENT {
76 pub(crate) const BURNOUT: u32 = 1_271_177;
78 pub(crate) const BURN_IT_ALL: u32 = 1_254_512;
80 pub(crate) const CINDERSTORM: u32 = 1_254_865;
82 pub(crate) const CODEX_OF_THE_SUNSTRIDERS: u32 = 449_382;
84 pub(crate) const COMBUSTION: u32 = 190_319;
86 pub(crate) const CONFLAGRATION: u32 = 1_271_171;
88 pub(crate) const CONTROLLED_DESTRUCTION: u32 = 383_669;
90 pub(crate) const CRITICAL_MASS: u32 = 117_216;
92 pub(crate) const DEEP_IMPACT: u32 = 416_719;
94 pub(crate) const FEEL_THE_BURN: u32 = 383_391;
96 pub(crate) const FEVERED_INCANTATION: u32 = 383_810;
98 pub(crate) const FIERY_RUSH: u32 = 383_634;
100 pub(crate) const FIRED_UP_1: u32 = 1_257_343;
102 pub(crate) const FIRED_UP_2: u32 = 1_257_349;
104 pub(crate) const FIRED_UP_3: u32 = 1_257_348;
106 pub(crate) const FIRESTARTER: u32 = 205_026;
108 pub(crate) const FIRES_IRE: u32 = 450_831;
110 pub(crate) const FIRE_BLAST: u32 = 108_853;
112 pub(crate) const FLAMESTRIKE: u32 = 2120;
114 pub(crate) const FLAME_ON: u32 = 205_029;
116 pub(crate) const FROM_THE_ASHES: u32 = 342_344;
118 pub(crate) const FUEL_THE_FIRE: u32 = 383_550;
120 pub(crate) const HEAT_SHIMMER: u32 = 457_736;
122 pub(crate) const IGNITION: u32 = 383_549;
124 pub(crate) const INFLAME: u32 = 417_467;
126 pub(crate) const INTENSIFYING_FLAME: u32 = 416_714;
128 pub(crate) const INVOCATION_ARCANE_PHOENIX: u32 = 448_658;
130 pub(crate) const KINDLING: u32 = 155_148;
132 pub(crate) const MASTER_OF_FLAME: u32 = 384_174;
134 pub(crate) const MEMORY_OF_ALAR: u32 = 449_619;
136 pub(crate) const METEOR: u32 = 153_561;
138 pub(crate) const MOLTEN_FURY: u32 = 457_803;
140 pub(crate) const PYROBLAST: u32 = 11366;
142 pub(crate) const PYROCLASM: u32 = 269_650;
144 pub(crate) const PYROMANIAC: u32 = 451_466;
146 pub(crate) const SAVOR_THE_MOMENT: u32 = 449_412;
148 pub(crate) const SCALD: u32 = 450_746;
150 pub(crate) const SCORCH: u32 = 2948;
152 pub(crate) const SLOW_BURN: u32 = 383_967;
154 pub(crate) const SPONTANEOUS_COMBUSTION: u32 = 451_875;
156 pub(crate) const WILDFIRE: u32 = 383_489;
158}
159pub(crate) mod SET_BONUS {
160 pub(crate) const MID1_FIRE_4PC: u32 = 1_264_835;
162}
163pub(crate) mod REPORTED_SPELL {
164 pub(crate) const ARCANE_BARRAGE: u32 = 450_499;
166 pub(crate) const ARCANE_PHOENIX_CONTROLLER: u32 = 448_659;
168 pub(crate) const ARCANE_SURGE: u32 = 453_326;
170 pub(crate) const GREATER_PYROBLAST: u32 = 450_421;
172 pub(crate) const IGNITE: u32 = 12654;
174 pub(crate) const METEORITE_BUG_IMPACT: u32 = 449_560;
176 pub(crate) const METEORITE_IMPACT: u32 = 456_139;
178 pub(crate) const PHOENIX_PYROBLAST: u32 = 450_461;
180 pub(crate) const PYROBLAST_PYROMANIAC: u32 = 460_475;
182 pub(crate) const PYROMANIAC_CONTROLLER: u32 = 451_466;
184}
185pub(crate) mod HERO {
186 pub(crate) mod FROSTFIRE {
187 pub(crate) mod SPELL {
188 pub(crate) const DUALITY_GLACIAL_SPIKE: u32 = 1_236_209;
190 pub(crate) const FLASH_FREEZEBURN_DAMAGE: u32 = 1_278_079;
192 pub(crate) const FROSTFIRE_BOLT: u32 = 431_044;
194 pub(crate) const FROSTFIRE_EMPOWERMENT_DAMAGE: u32 = 431_186;
196 pub(crate) const MOLTEN_CHILL_IGNITE: u32 = 1_262_887;
198 }
199 pub(crate) mod AURA {
200 pub(crate) const FROSTFIRE_EMPOWERMENT: u32 = 431_177;
202 }
203 }
204 pub(crate) mod SUNFURY {
205 pub(crate) mod SPELL {
206 pub(crate) const METEORITE: u32 = 449_559;
208 pub(crate) const METEORITE_IMPACT: u32 = 449_569;
210 }
211 pub(crate) mod AURA {
212 pub(crate) const GLORIOUS_INCANDESCENCE: u32 = 451_073;
214 pub(crate) const HYPERTHERMIA: u32 = 383_874;
216 pub(crate) const HYPERTHERMIA_DAMAGE: u32 = 1_242_220;
218 pub(crate) const LESSER_TIME_WARP: u32 = 1_260_277;
220 pub(crate) const MANA_CASCADE: u32 = 449_314;
222 pub(crate) const SPELLFIRE_SPHERE: u32 = 448_604;
224 }
225 }
226}
227static HERO_TREE_FROSTFIRE_SPELLS: &[(&str, u32)] = &[
228 ("DUALITY_GLACIAL_SPIKE", 1_236_209),
229 ("FLASH_FREEZEBURN_DAMAGE", 1_278_079),
230 ("FROSTFIRE_BOLT", 431_044),
231 ("FROSTFIRE_EMPOWERMENT_DAMAGE", 431_186),
232 ("MOLTEN_CHILL_IGNITE", 1_262_887),
233];
234static HERO_TREE_FROSTFIRE_AURAS: &[(&str, u32)] = &[("FROSTFIRE_EMPOWERMENT", 431_177)];
235static HERO_TREE_SUNFURY_SPELLS: &[(&str, u32)] = &[
236 ("METEORITE", 449_559),
237 ("METEORITE_IMPACT", 449_569),
238];
239static HERO_TREE_SUNFURY_AURAS: &[(&str, u32)] = &[
240 ("GLORIOUS_INCANDESCENCE", 451_073),
241 ("HYPERTHERMIA", 383_874),
242 ("HYPERTHERMIA_DAMAGE", 1_242_220),
243 ("LESSER_TIME_WARP", 1_260_277),
244 ("MANA_CASCADE", 449_314),
245 ("SPELLFIRE_SPHERE", 448_604),
246];
247pub(crate) const AURA_HEATING_UP: LocalAuraIdx = LocalAuraIdx::new(0);
248pub(crate) const AURA_HOT_STREAK: LocalAuraIdx = LocalAuraIdx::new(1);
249pub(crate) const AURA_COMBUSTION: LocalAuraIdx = LocalAuraIdx::new(2);
250pub(crate) const AURA_IGNITE: LocalAuraIdx = LocalAuraIdx::new(3);
251pub(crate) const AURA_CONTROLLED_DESTRUCTION: LocalAuraIdx = LocalAuraIdx::new(4);
252pub(crate) const AURA_HYPERTHERMIA: LocalAuraIdx = LocalAuraIdx::new(5);
253pub(crate) const AURA_HYPERTHERMIA_DAMAGE: LocalAuraIdx = LocalAuraIdx::new(6);
254pub(crate) const AURA_PYROTECHNICS: LocalAuraIdx = LocalAuraIdx::new(7);
255pub(crate) const AURA_PYROCLASM: LocalAuraIdx = LocalAuraIdx::new(8);
256pub(crate) const AURA_FEEL_THE_BURN: LocalAuraIdx = LocalAuraIdx::new(9);
257pub(crate) const AURA_FEVERED_INCANTATION: LocalAuraIdx = LocalAuraIdx::new(10);
258pub(crate) const AURA_FIRED_UP: LocalAuraIdx = LocalAuraIdx::new(11);
259pub(crate) const AURA_HEAT_SHIMMER: LocalAuraIdx = LocalAuraIdx::new(12);
260pub(crate) const AURA_FROSTFIRE_EMPOWERMENT: LocalAuraIdx = LocalAuraIdx::new(13);
261pub(crate) const AURA_ARCANE_INTELLECT_HIGHLIGHT: LocalAuraIdx = LocalAuraIdx::new(14);
262pub(crate) const AURA_SPELLFIRE_SPHERE: LocalAuraIdx = LocalAuraIdx::new(15);
263pub(crate) const AURA_FIERY_RUSH_EFFECT: LocalAuraIdx = LocalAuraIdx::new(16);
264pub(crate) const AURA_COUNT: usize = 17;
265const _: () = assert!(
266 AURA_COUNT == 17, "AURA index ABI: count changed -- update positional hook consumers"
267);
268pub(crate) const SPELL_FIREBALL: LocalSpellIdx = LocalSpellIdx::new(0);
269pub(crate) const SPELL_PYROBLAST: LocalSpellIdx = LocalSpellIdx::new(1);
270pub(crate) const SPELL_FIRE_BLAST: LocalSpellIdx = LocalSpellIdx::new(2);
271pub(crate) const SPELL_FLAMESTRIKE: LocalSpellIdx = LocalSpellIdx::new(3);
272pub(crate) const SPELL_COMBUSTION: LocalSpellIdx = LocalSpellIdx::new(4);
273pub(crate) const SPELL_METEOR: LocalSpellIdx = LocalSpellIdx::new(5);
274pub(crate) const SPELL_SCORCH: LocalSpellIdx = LocalSpellIdx::new(6);
275pub(crate) const SPELL_FROSTFIRE_BOLT: LocalSpellIdx = LocalSpellIdx::new(7);
276pub(crate) const SPELL_ARCANE_INTELLECT: LocalSpellIdx = LocalSpellIdx::new(8);
277pub(crate) const SPELL_IGNITE: LocalSpellIdx = LocalSpellIdx::new(9);
278pub(crate) const SPELL_INTENSIFYING_FLAME: LocalSpellIdx = LocalSpellIdx::new(10);
279pub(crate) const SPELL_BURNOUT: LocalSpellIdx = LocalSpellIdx::new(11);
280pub(crate) const SPELL_COUNT: usize = 12;
281const _: () = assert!(
282 SPELL_COUNT == 12,
283 "SPELL index ABI: count changed -- update positional hook consumers"
284);
285pub(crate) mod EFFECT {
286 pub(crate) const FIRESTARTER_THRESHOLD: (u32, u8) = (205_026, 1);
288 pub(crate) const IGNITE_MASTERY: (u32, u8) = (12846, 1);
290 pub(crate) const METEOR_BURN_DAMAGE: (u32, u8) = (155_158, 1);
292 pub(crate) const METEOR_BURN_DURATION: (u32, u8) = (175_396, 1);
294 pub(crate) const METEOR_DELAY: (u32, u8) = (177_345, 1);
296 pub(crate) const METEOR_IMPACT_DAMAGE: (u32, u8) = (351_140, 1);
298 pub(crate) const METEORITE_IMPACT_DAMAGE: (u32, u8) = (449_569, 1);
300 pub(crate) const PHOENIX_ARCANE_BARRAGE_DAMAGE: (u32, u8) = (450_499, 1);
302 pub(crate) const PHOENIX_ARCANE_SURGE_DAMAGE: (u32, u8) = (453_326, 1);
304 pub(crate) const PHOENIX_GREATER_PYROBLAST_DAMAGE: (u32, u8) = (450_421, 1);
306 pub(crate) const PHOENIX_METEORITE_DAMAGE: (u32, u8) = (456_139, 1);
308 pub(crate) const PHOENIX_PYROBLAST_DAMAGE: (u32, u8) = (450_461, 1);
310 pub(crate) const PYROBLAST_PYROMANIAC_DAMAGE: (u32, u8) = (460_475, 1);
312 pub(crate) const BURNOUT_PCT: (u32, u8) = (1_271_177, 1);
314 pub(crate) const INTENSIFYING_FLAME_PCT: (u32, u8) = (416_714, 2);
316 pub(crate) const FIRED_UP_CHANCE: (u32, u8) = (1_257_343, 1);
318 pub(crate) const FIRED_UP_COMBUSTION_EXTEND_MS: (u32, u8) = (1_257_348, 2);
320 pub(crate) const FIRED_UP_FIRE_BLAST_CDR_MS: (u32, u8) = (1_257_349, 1);
322 pub(crate) const SET4PC_FIRE_BLAST_CDR_MS: (u32, u8) = (1_264_835, 1);
324 pub(crate) const BURN_IT_ALL_BASE_PCT: (u32, u8) = (1_254_512, 2);
326 pub(crate) const BURN_IT_ALL_CRIT_PCT: (u32, u8) = (1_254_512, 1);
328 pub(crate) const CODEX_COMBUSTION_PCT: (u32, u8) = (449_382, 2);
330 pub(crate) const CONTROLLED_DESTRUCTION_PCT_X100: (u32, u8) = (383_669, 2);
332 pub(crate) const SAVOR_CAP_MS: (u32, u8) = (449_412, 2);
334 pub(crate) const SAVOR_PER_SPHERE_MS: (u32, u8) = (449_412, 1);
336}
337static REPORTED_SPELLS: &[(&str, u32)] = &[
338 ("arcane_barrage", 450_499),
339 ("arcane_phoenix_controller", 448_659),
340 ("arcane_surge", 453_326),
341 ("greater_pyroblast", 450_421),
342 ("ignite", 12654),
343 ("meteorite_bug_impact", 449_560),
344 ("meteorite_impact", 456_139),
345 ("phoenix_pyroblast", 450_461),
346 ("pyroblast_pyromaniac", 460_475),
347 ("pyromaniac_controller", 451_466),
348];
349static DECLARED_SPELL_IDS: &[u32] = &[
350 133, 11366, 108_853, 2120, 190_319, 153_561, 2948, 431_044, 1459, 12654, 419_800,
351 1_271_335, 48107, 48108, 190_319, 12654, 453_268, 383_874, 1_242_220, 157_644,
352 269_651, 383_395, 383_811, 1_257_350, 458_964, 431_177, 1459, 448_604, 383_637,
353 12654, 12846, 155_158, 175_396, 177_345, 205_026, 351_140, 383_637, 383_669, 383_874,
354 416_714, 448_659, 449_382, 449_412, 449_560, 449_569, 450_421, 450_461, 450_499,
355 451_466, 453_326, 456_139, 460_475, 1_254_512, 1_257_343, 1_257_348, 1_257_349,
356 1_257_350, 1_264_835, 1_271_177, 450_499, 448_659, 453_326, 450_421, 12654, 449_560,
357 456_139, 450_461, 460_475, 451_466, 1_236_209, 1_278_079, 431_044, 431_186,
358 1_262_887, 431_177, 449_559, 449_569, 451_073, 383_874, 1_242_220, 1_260_277,
359 449_314, 448_604,
360];
361static DECLARED_AURA_IDS: &[u32] = &[
362 48107, 48108, 190_319, 12654, 453_268, 383_874, 1_242_220, 157_644, 269_651, 383_395,
363 383_811, 1_257_350, 458_964, 431_177, 1459, 448_604, 383_637, 431_177, 451_073,
364 383_874, 1_242_220, 1_260_277, 449_314, 448_604,
365];
366static TALENTS: &[(&str, u32)] = &[
367 ("BURNOUT", TALENT::BURNOUT),
368 ("BURN_IT_ALL", TALENT::BURN_IT_ALL),
369 ("CINDERSTORM", TALENT::CINDERSTORM),
370 ("CODEX_OF_THE_SUNSTRIDERS", TALENT::CODEX_OF_THE_SUNSTRIDERS),
371 ("COMBUSTION", TALENT::COMBUSTION),
372 ("CONFLAGRATION", TALENT::CONFLAGRATION),
373 ("CONTROLLED_DESTRUCTION", TALENT::CONTROLLED_DESTRUCTION),
374 ("CRITICAL_MASS", TALENT::CRITICAL_MASS),
375 ("DEEP_IMPACT", TALENT::DEEP_IMPACT),
376 ("FEEL_THE_BURN", TALENT::FEEL_THE_BURN),
377 ("FEVERED_INCANTATION", TALENT::FEVERED_INCANTATION),
378 ("FIERY_RUSH", TALENT::FIERY_RUSH),
379 ("FIRED_UP_1", TALENT::FIRED_UP_1),
380 ("FIRED_UP_2", TALENT::FIRED_UP_2),
381 ("FIRED_UP_3", TALENT::FIRED_UP_3),
382 ("FIRESTARTER", TALENT::FIRESTARTER),
383 ("FIRES_IRE", TALENT::FIRES_IRE),
384 ("FIRE_BLAST", TALENT::FIRE_BLAST),
385 ("FLAMESTRIKE", TALENT::FLAMESTRIKE),
386 ("FLAME_ON", TALENT::FLAME_ON),
387 ("FROM_THE_ASHES", TALENT::FROM_THE_ASHES),
388 ("FUEL_THE_FIRE", TALENT::FUEL_THE_FIRE),
389 ("HEAT_SHIMMER", TALENT::HEAT_SHIMMER),
390 ("IGNITION", TALENT::IGNITION),
391 ("INFLAME", TALENT::INFLAME),
392 ("INTENSIFYING_FLAME", TALENT::INTENSIFYING_FLAME),
393 ("INVOCATION_ARCANE_PHOENIX", TALENT::INVOCATION_ARCANE_PHOENIX),
394 ("KINDLING", TALENT::KINDLING),
395 ("MASTER_OF_FLAME", TALENT::MASTER_OF_FLAME),
396 ("MEMORY_OF_ALAR", TALENT::MEMORY_OF_ALAR),
397 ("METEOR", TALENT::METEOR),
398 ("MOLTEN_FURY", TALENT::MOLTEN_FURY),
399 ("PYROBLAST", TALENT::PYROBLAST),
400 ("PYROCLASM", TALENT::PYROCLASM),
401 ("PYROMANIAC", TALENT::PYROMANIAC),
402 ("SAVOR_THE_MOMENT", TALENT::SAVOR_THE_MOMENT),
403 ("SCALD", TALENT::SCALD),
404 ("SCORCH", TALENT::SCORCH),
405 ("SLOW_BURN", TALENT::SLOW_BURN),
406 ("SPONTANEOUS_COMBUSTION", TALENT::SPONTANEOUS_COMBUSTION),
407 ("WILDFIRE", TALENT::WILDFIRE),
408];
409pub(crate) mod FIRE_MAGE {
410 pub(crate) const FIREBALL_CASTS: u32 = 0;
411 pub(crate) const PYROBLAST_CASTS: u32 = 1;
412 pub(crate) const FIRE_BLAST_CASTS: u32 = 2;
413 pub(crate) const COMBUSTION_CASTS: u32 = 3;
414 pub(crate) const METEOR_CASTS: u32 = 4;
415 pub(crate) const FLAMESTRIKE_CASTS: u32 = 5;
416 pub(crate) const COMBUSTION_UPTIME_MS: u32 = 6;
417 pub(crate) const IGNITE_UPTIME_MS: u32 = 7;
418}
419fn aura_heating_up(
420 a: AuraDefinitionDraft,
421 data: &ResolvedGameData,
422) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
423 Ok(
424 a
425 .on_player()
426 .apply_base_from_data(data, 48107)?
427 .max_stacks(
428 data
429 .require_aura_max_stacks(
430 wowlab_types::sim::SpellIdx::from_raw(48107),
431 )?,
432 ),
433 )
434}
435fn aura_hot_streak(
436 a: AuraDefinitionDraft,
437 data: &ResolvedGameData,
438) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
439 Ok(
440 a
441 .on_player()
442 .apply_base_from_data(data, 48108)?
443 .max_stacks(
444 data
445 .require_aura_max_stacks(
446 wowlab_types::sim::SpellIdx::from_raw(48108),
447 )?,
448 ),
449 )
450}
451fn aura_combustion(
452 a: AuraDefinitionDraft,
453 data: &ResolvedGameData,
454) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
455 Ok(
456 a
457 .on_player()
458 .apply_base_from_data(data, 190_319)?
459 .max_stacks(
460 data
461 .require_aura_max_stacks(
462 wowlab_types::sim::SpellIdx::from_raw(190_319),
463 )?,
464 )
465 .on_expire(crate::hooks::fire_mage::combustion_expire_hook),
466 )
467}
468fn aura_ignite(
469 a: AuraDefinitionDraft,
470 data: &ResolvedGameData,
471) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
472 a.on_target()
473 .apply_base_from_data(data, 12654)?
474 .max_stacks(
475 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(12654))?,
476 )
477 .periodic_residual_damage(
478 wowlab_engine_combat::validated_milliseconds(
479 data.period(wowlab_types::sim::SpellIdx::from_raw(12654), 1),
480 12654,
481 "periodic_residual_damage.tick_ms",
482 )?,
483 )
484 .apply_periodic_behavior_from_data(data, 12654)
485}
486fn aura_controlled_destruction(
487 a: AuraDefinitionDraft,
488 data: &ResolvedGameData,
489) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
490 Ok(
491 a
492 .on_target()
493 .apply_base_from_data(data, 453_268)?
494 .max_stacks(
495 data
496 .require_aura_max_stacks(
497 wowlab_types::sim::SpellIdx::from_raw(453_268),
498 )?,
499 ),
500 )
501}
502fn aura_hyperthermia(
503 a: AuraDefinitionDraft,
504 data: &ResolvedGameData,
505) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
506 Ok(
507 a
508 .on_player()
509 .duration_ms(
510 wowlab_engine_combat::validated_milliseconds(
511 (f64::from(
512 data
513 .require_aura_duration_ms(
514 wowlab_types::sim::SpellIdx::from_raw(383_874),
515 )?,
516 )) * 0.8,
517 383_874,
518 "aura.duration_ms",
519 )?,
520 )
521 .apply_periodic_effect_from_data(data, 383_874)?
522 .apply_duration_behavior_from_data(data, 383_874)?
523 .apply_timing_modifiers_from_data(data, 383_874)?
524 .max_stacks(
525 data
526 .require_aura_max_stacks(
527 wowlab_types::sim::SpellIdx::from_raw(383_874),
528 )?,
529 )
530 .crit_chance_spells(100.0, &[11366, 2120])?
531 .apply_periodic_behavior_from_data(data, 383_874)?
532 .on_expire(crate::hooks::fire_mage::hyperthermia_expire_hook),
533 )
534}
535fn aura_hyperthermia_damage(
536 a: AuraDefinitionDraft,
537 data: &ResolvedGameData,
538) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
539 Ok(
540 a
541 .on_player()
542 .apply_base_from_data(data, 1_242_220)?
543 .max_stacks(
544 data
545 .require_aura_max_stacks(
546 wowlab_types::sim::SpellIdx::from_raw(1_242_220),
547 )?,
548 ),
549 )
550}
551fn aura_pyrotechnics(
552 a: AuraDefinitionDraft,
553 data: &ResolvedGameData,
554) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
555 a.on_player()
556 .apply_base_from_data(data, 157_644)?
557 .max_stacks(
558 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(157_644))?,
559 )
560 .crit_chance_spells(20.0, &[133])
561}
562fn aura_pyroclasm(
563 a: AuraDefinitionDraft,
564 data: &ResolvedGameData,
565) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
566 Ok(
567 a
568 .on_player()
569 .apply_base_from_data(data, 269_651)?
570 .max_stacks(
571 data
572 .require_aura_max_stacks(
573 wowlab_types::sim::SpellIdx::from_raw(269_651),
574 )?,
575 ),
576 )
577}
578fn aura_feel_the_burn(
579 a: AuraDefinitionDraft,
580 data: &ResolvedGameData,
581) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
582 Ok(
583 a
584 .on_player()
585 .apply_base_from_data(data, 383_395)?
586 .max_stacks(
587 data
588 .require_aura_max_stacks(
589 wowlab_types::sim::SpellIdx::from_raw(383_395),
590 )?,
591 ),
592 )
593}
594fn aura_fevered_incantation(
595 a: AuraDefinitionDraft,
596 data: &ResolvedGameData,
597) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
598 a.on_player()
599 .apply_base_from_data(data, 383_811)?
600 .max_stacks(
601 data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(383_811))?,
602 )
603 .crit_damage_spells(2.0, &[133, 11366, 108_853, 2120, 153_561, 2948, 431_044])
604}
605fn aura_fired_up(
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, 1_257_350)?
613 .max_stacks(
614 data
615 .require_aura_max_stacks(
616 wowlab_types::sim::SpellIdx::from_raw(1_257_350),
617 )?,
618 )
619 .damage_multiplier_stacking(
620 (data.base_points(wowlab_types::sim::SpellIdx::from_raw(1_257_350), 1))
621 * 0.01,
622 (data.base_points(wowlab_types::sim::SpellIdx::from_raw(1_257_350), 1))
623 * 0.01,
624 )?
625 .async_stacks(),
626 )
627}
628fn aura_heat_shimmer(
629 a: AuraDefinitionDraft,
630 data: &ResolvedGameData,
631) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
632 Ok(
633 a
634 .on_player()
635 .apply_base_from_data(data, 458_964)?
636 .max_stacks(
637 data
638 .require_aura_max_stacks(
639 wowlab_types::sim::SpellIdx::from_raw(458_964),
640 )?,
641 ),
642 )
643}
644fn aura_frostfire_empowerment(
645 a: AuraDefinitionDraft,
646 data: &ResolvedGameData,
647) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
648 Ok(
649 a
650 .on_player()
651 .apply_base_from_data(data, 431_177)?
652 .max_stacks(
653 data
654 .require_aura_max_stacks(
655 wowlab_types::sim::SpellIdx::from_raw(431_177),
656 )?,
657 ),
658 )
659}
660fn aura_arcane_intellect_highlight(
661 a: AuraDefinitionDraft,
662 data: &ResolvedGameData,
663) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
664 Ok(
665 a
666 .on_player()
667 .apply_base_from_data(data, 1459)?
668 .max_stacks(
669 data
670 .require_aura_max_stacks(
671 wowlab_types::sim::SpellIdx::from_raw(1459),
672 )?,
673 ),
674 )
675}
676fn aura_spellfire_sphere(
677 a: AuraDefinitionDraft,
678 data: &ResolvedGameData,
679) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
680 Ok(
681 a
682 .on_player()
683 .duration_ms(
684 wowlab_engine_combat::validated_milliseconds(
685 0.0,
686 448_604,
687 "aura.duration_ms",
688 )?,
689 )
690 .apply_periodic_effect_from_data(data, 448_604)?
691 .max_stacks(
692 data
693 .require_aura_max_stacks(
694 wowlab_types::sim::SpellIdx::from_raw(448_604),
695 )?,
696 )
697 .apply_at_max_stacks(),
698 )
699}
700fn aura_fiery_rush_effect(
701 a: AuraDefinitionDraft,
702 data: &ResolvedGameData,
703) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
704 Ok(
705 a
706 .on_player()
707 .apply_base_from_data(data, 383_637)?
708 .max_stacks(
709 data
710 .require_aura_max_stacks(
711 wowlab_types::sim::SpellIdx::from_raw(383_637),
712 )?,
713 ),
714 )
715}
716fn spell_fireball(
717 s: SpellDefinitionDraft,
718 data: &ResolvedGameData,
719) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
720 Ok(
721 s
722 .apply_base_from_data(data, 133)?
723 .travel_time(
724 wowlab_engine_combat::validated_milliseconds(
725 200.0,
726 133,
727 "spell.travel_time_ms",
728 )?,
729 )
730 .apply_aoe_from_data(data)
731 .on_cast(crate::hooks::fire_mage::fireball_hook),
732 )
733}
734fn spell_pyroblast(
735 s: SpellDefinitionDraft,
736 data: &ResolvedGameData,
737) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
738 Ok(
739 s
740 .apply_base_from_data(data, 11366)?
741 .cast_time(
742 wowlab_engine_combat::validated_milliseconds(
743 0.0,
744 11366,
745 "spell.cast_time_ms",
746 )?,
747 )
748 .apply_aoe_from_data(data)
749 .on_cast(crate::hooks::fire_mage::pyroblast_hook),
750 )
751}
752fn spell_fire_blast(
753 s: SpellDefinitionDraft,
754 data: &ResolvedGameData,
755) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
756 Ok(
757 s
758 .apply_base_from_data(data, 108_853)?
759 .usable_while_casting()
760 .guaranteed_crit()
761 .apply_aoe_from_data(data)
762 .on_cast(crate::hooks::fire_mage::fire_blast_hook),
763 )
764}
765fn spell_flamestrike(
766 s: SpellDefinitionDraft,
767 data: &ResolvedGameData,
768) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
769 Ok(s.apply_base_from_data(data, 2120)?.apply_aoe_from_data(data))
770}
771fn spell_combustion(
772 s: SpellDefinitionDraft,
773 data: &ResolvedGameData,
774) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
775 Ok(
776 s
777 .apply_base_from_data(data, 190_319)?
778 .applies_inferred_aura(AURA_COMBUSTION, 190_319)
779 .apply_aoe_from_data(data)
780 .on_cast(crate::hooks::fire_mage::combustion_hook),
781 )
782}
783fn spell_meteor(
784 s: SpellDefinitionDraft,
785 data: &ResolvedGameData,
786) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
787 Ok(
788 s
789 .apply_base_from_data(data, 153_561)?
790 .apply_aoe_from_data(data)
791 .on_cast(crate::hooks::fire_mage::meteor_hook),
792 )
793}
794fn spell_scorch(
795 s: SpellDefinitionDraft,
796 data: &ResolvedGameData,
797) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
798 Ok(s.apply_base_from_data(data, 2948)?.apply_aoe_from_data(data))
799}
800fn spell_frostfire_bolt(
801 s: SpellDefinitionDraft,
802 data: &ResolvedGameData,
803) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
804 Ok(s.apply_base_from_data(data, 431_044)?.apply_aoe_from_data(data))
805}
806fn spell_arcane_intellect(
807 s: SpellDefinitionDraft,
808 data: &ResolvedGameData,
809) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
810 Ok(
811 s
812 .apply_base_from_data(data, 1459)?
813 .applies_inferred_aura(AURA_ARCANE_INTELLECT_HIGHLIGHT, 1459)
814 .apply_aoe_from_data(data),
815 )
816}
817fn spell_ignite(
818 s: SpellDefinitionDraft,
819 data: &ResolvedGameData,
820) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
821 Ok(
822 s
823 .apply_base_from_data(data, 12654)?
824 .applies_inferred_aura(AURA_IGNITE, 12654)
825 .apply_aoe_from_data(data),
826 )
827}
828fn spell_intensifying_flame(
829 s: SpellDefinitionDraft,
830 data: &ResolvedGameData,
831) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
832 Ok(s.apply_base_from_data(data, 419_800)?.apply_aoe_from_data(data))
833}
834fn spell_burnout(
835 s: SpellDefinitionDraft,
836 data: &ResolvedGameData,
837) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
838 Ok(s.apply_base_from_data(data, 1_271_335)?.apply_aoe_from_data(data))
839}
840pub(crate) fn build_combat_system(
841 params: &wowlab_engine_ports::HandlerParams<'_>,
842) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
843 let builder = BuiltCombatSystem::builder(params.stats.clone())
844 .spec_id(SpecId::Fire)
845 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
846 .mastery_spell(12846)
847 .mastery_hook(crate::hooks::fire_mage::mastery)
848 .resource(
849 "mana",
850 100_000.0,
851 params
852 .game_data
853 .power_regen_for_max(0, 100_000.0)
854 .ok_or_else(|| {
855 wowlab_engine_ports::EngineError::spec_construction(
856 "missing power-type power_regen_for_max for type_id 0",
857 )
858 })?,
859 )
860 .resource_type_id(0)
861 .resource_start(100_000.0)
862 .precombat_aura(AURA_SPELLFIRE_SPHERE)
863 .aura("heating_up", AURA::HEATING_UP, |a| aura_heating_up(a, ¶ms.game_data))
864 .aura("hot_streak", AURA::HOT_STREAK, |a| aura_hot_streak(a, ¶ms.game_data))
865 .aura("combustion", AURA::COMBUSTION, |a| aura_combustion(a, ¶ms.game_data))
866 .aura("ignite", AURA::IGNITE, |a| aura_ignite(a, ¶ms.game_data))
867 .aura(
868 "controlled_destruction",
869 AURA::CONTROLLED_DESTRUCTION,
870 |a| aura_controlled_destruction(a, ¶ms.game_data),
871 )
872 .aura(
873 "hyperthermia",
874 AURA::HYPERTHERMIA,
875 |a| aura_hyperthermia(a, ¶ms.game_data),
876 )
877 .aura(
878 "hyperthermia_damage",
879 AURA::HYPERTHERMIA_DAMAGE,
880 |a| aura_hyperthermia_damage(a, ¶ms.game_data),
881 )
882 .aura(
883 "pyrotechnics",
884 AURA::PYROTECHNICS,
885 |a| aura_pyrotechnics(a, ¶ms.game_data),
886 )
887 .aura("pyroclasm", AURA::PYROCLASM, |a| aura_pyroclasm(a, ¶ms.game_data))
888 .aura(
889 "feel_the_burn",
890 AURA::FEEL_THE_BURN,
891 |a| aura_feel_the_burn(a, ¶ms.game_data),
892 )
893 .aura(
894 "fevered_incantation",
895 AURA::FEVERED_INCANTATION,
896 |a| aura_fevered_incantation(a, ¶ms.game_data),
897 )
898 .aura("fired_up", AURA::FIRED_UP, |a| aura_fired_up(a, ¶ms.game_data))
899 .aura(
900 "heat_shimmer",
901 AURA::HEAT_SHIMMER,
902 |a| aura_heat_shimmer(a, ¶ms.game_data),
903 )
904 .aura(
905 "frostfire_empowerment",
906 AURA::FROSTFIRE_EMPOWERMENT,
907 |a| aura_frostfire_empowerment(a, ¶ms.game_data),
908 )
909 .aura(
910 "arcane_intellect_highlight",
911 AURA::ARCANE_INTELLECT_HIGHLIGHT,
912 |a| aura_arcane_intellect_highlight(a, ¶ms.game_data),
913 )
914 .aura(
915 "spellfire_sphere",
916 AURA::SPELLFIRE_SPHERE,
917 |a| aura_spellfire_sphere(a, ¶ms.game_data),
918 )
919 .aura(
920 "fiery_rush_effect",
921 AURA::FIERY_RUSH_EFFECT,
922 |a| aura_fiery_rush_effect(a, ¶ms.game_data),
923 )
924 .spell("fireball", SPELL::FIREBALL, |s| spell_fireball(s, ¶ms.game_data))
925 .spell("pyroblast", SPELL::PYROBLAST, |s| spell_pyroblast(s, ¶ms.game_data))
926 .spell(
927 "fire_blast",
928 SPELL::FIRE_BLAST,
929 |s| spell_fire_blast(s, ¶ms.game_data),
930 )
931 .spell(
932 "flamestrike",
933 SPELL::FLAMESTRIKE,
934 |s| spell_flamestrike(s, ¶ms.game_data),
935 )
936 .spell(
937 "combustion",
938 SPELL::COMBUSTION,
939 |s| spell_combustion(s, ¶ms.game_data),
940 )
941 .spell("meteor", SPELL::METEOR, |s| spell_meteor(s, ¶ms.game_data))
942 .spell("scorch", SPELL::SCORCH, |s| spell_scorch(s, ¶ms.game_data))
943 .spell(
944 "frostfire_bolt",
945 SPELL::FROSTFIRE_BOLT,
946 |s| spell_frostfire_bolt(s, ¶ms.game_data),
947 )
948 .spell(
949 "arcane_intellect",
950 SPELL::ARCANE_INTELLECT,
951 |s| spell_arcane_intellect(s, ¶ms.game_data),
952 )
953 .spell("ignite", SPELL::IGNITE, |s| spell_ignite(s, ¶ms.game_data))
954 .spell(
955 "intensifying_flame",
956 SPELL::INTENSIFYING_FLAME,
957 |s| spell_intensifying_flame(s, ¶ms.game_data),
958 )
959 .spell("burnout", SPELL::BURNOUT, |s| spell_burnout(s, ¶ms.game_data))
960 .game_data(params.game_data.clone())
961 .talent_spell_ids(TALENTS)
962 .talent_selections(params.talent_selections)
963 .talent_gated_aura_effect(TALENT::FIERY_RUSH, AURA_COMBUSTION, 383_637, 1)
964 .set_bonus_auras(params.set_bonus_auras)
965 .encounter(params.encounter.clone());
966 let builder = crate::generated::items::register_equipped(
967 builder,
968 ¶ms.game_data,
969 params.equipped_items,
970 )?;
971
972 builder
973 .build(params.rotation)
974 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
975}
976fn new_handler(
977 params: wowlab_engine_ports::HandlerParams<'_>,
978) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
979 let handler = crate::composition::compose_handler(
980 build_combat_system,
981 ¶ms,
982 |parts, params| Ok(crate::hooks::fire_mage::FireHandler::new(parts, params)),
983 )?;
984
985 Ok(Box::new(handler))
986}
987static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
988 primary_resource: ResourceType::Mana,
989 secondary_resource: None,
990 spell_ids: DECLARED_SPELL_IDS,
991 aura_ids: DECLARED_AURA_IDS,
992 server_proc_entries: &[],
993 talents: TALENTS,
994 rotation_field_schema: &[],
995 metrics_plugin_id: None,
996 hero_talent_trees: &[
997 HeroTalentTreeDesc {
998 name: "Frostfire",
999 spells: HERO_TREE_FROSTFIRE_SPELLS,
1000 auras: HERO_TREE_FROSTFIRE_AURAS,
1001 },
1002 HeroTalentTreeDesc {
1003 name: "Sunfury",
1004 spells: HERO_TREE_SUNFURY_SPELLS,
1005 auras: HERO_TREE_SUNFURY_AURAS,
1006 },
1007 ],
1008 reported_spells: REPORTED_SPELLS,
1009 masked_passive_effects: &[],
1010 passive_effect_overrides: &[],
1011};
1012pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1013 spec_id: SpecId::Fire,
1014 display_name: "Fire Mage",
1015 metadata: DECLARED_METADATA,
1016 handler_factory: new_handler,
1017};
1018wowlab_engine_combat::smoke_test!(build_combat_system);