1#![allow(non_snake_case, dead_code)]
4use wowlab_types::{combat::ResourceType, game::SpecId};
5use wowlab_engine_domain::dbc::ProcAttributes;
6use wowlab_engine_combat::{
7 BuiltCombatSystem, AuraDefinitionDraft, LocalAuraIdx, LocalSpellIdx,
8 SpellDefinitionDraft,
9};
10use wowlab_engine_ports::{
11 DeclaredSpecMetadata, SpecDescriptor, SpecHandler, HeroTalentTreeDesc,
12 DeclaredServerProcEntry,
13};
14use wowlab_engine_gamedata::ResolvedGameData;
15pub(crate) mod SPELL {
16 pub(crate) const FROSTBOLT: u32 = 116;
18 pub(crate) const ICE_LANCE: u32 = 30455;
20 pub(crate) const FLURRY: u32 = 44614;
22 pub(crate) const GLACIAL_SPIKE: u32 = 199_786;
24 pub(crate) const FROZEN_ORB: u32 = 84714;
26 pub(crate) const FROZEN_ORB_BOLT: u32 = 84721;
28 pub(crate) const FLURRY_BOLT: u32 = 228_354;
30 pub(crate) const SHATTER_ICE_LANCE: u32 = 1_246_949;
32 pub(crate) const FROST_SPLINTER: u32 = 443_722;
34 pub(crate) const BLIZZARD: u32 = 190_356;
36 pub(crate) const COMET_STORM: u32 = 153_595;
38 pub(crate) const RAY_OF_FROST: u32 = 205_021;
40 pub(crate) const HAND_OF_FROST: u32 = 1_262_769;
42 pub(crate) const CONE_OF_COLD: u32 = 120;
44 pub(crate) const ARCANE_INTELLECT: u32 = 1459;
46}
47pub(crate) mod AURA {
48 pub(crate) const FINGERS_OF_FROST: u32 = 44544;
50 pub(crate) const BRAIN_FREEZE: u32 = 190_446;
52 pub(crate) const GLACIAL_SPIKE: u32 = 1_222_865;
54 pub(crate) const COMET_STORM: u32 = 1_247_778;
56 pub(crate) const FREEZING: u32 = 1_221_389;
58 pub(crate) const SPLINTERSTORM: u32 = 1_247_908;
60 pub(crate) const THERMAL_VOID: u32 = 1_247_730;
62 pub(crate) const FREEZING_RAIN: u32 = 270_232;
64 pub(crate) const PERMAFROST_LANCES: u32 = 455_122;
66 pub(crate) const HAND_OF_FROST: u32 = 1_263_263;
68 pub(crate) const FROSTFIRE_EMPOWERMENT: u32 = 431_177;
70 pub(crate) const ARCANE_INTELLECT_HIGHLIGHT: u32 = 1459;
72 pub(crate) const IMPROVED_SHATTER: u32 = 1_246_811;
74}
75pub(crate) mod TALENT {
76 pub(crate) const AUGURY_ABOUNDS: u32 = 1_280_165;
78 pub(crate) const BLIZZARD_1: u32 = 190_356;
80 pub(crate) const BLIZZARD_2: u32 = 1_248_829;
82 pub(crate) const BRAIN_FREEZE: u32 = 190_447;
84 pub(crate) const COLD_SNAP: u32 = 235_219;
86 pub(crate) const COMET_STORM: u32 = 153_595;
88 pub(crate) const CONE_OF_FROST: u32 = 378_835;
90 pub(crate) const CRYSTALLINE_REFRACTION: u32 = 1_247_544;
92 pub(crate) const DEEP_SHATTER: u32 = 1_247_025;
94 pub(crate) const EVERLASTING_FROST: u32 = 385_167;
96 pub(crate) const FINGERS_OF_FROST: u32 = 112_965;
98 pub(crate) const FLASH_FREEZE: u32 = 379_993;
100 pub(crate) const FLURRY: u32 = 44614;
102 pub(crate) const FORCE_OF_WILL: u32 = 444_719;
104 pub(crate) const FRACTURED_FROST: u32 = 378_448;
106 pub(crate) const FREEZING_RAIN: u32 = 270_233;
108 pub(crate) const FROSTBITE: u32 = 1_248_825;
110 pub(crate) const FROZEN_ORB: u32 = 84714;
112 pub(crate) const FROZEN_TOUCH: u32 = 205_030;
114 pub(crate) const GLACIAL_ASSAULT: u32 = 378_947;
116 pub(crate) const GLACIAL_SHATTER: u32 = 451_022;
118 pub(crate) const GLACIATE: u32 = 1_247_798;
120 pub(crate) const HAND_OF_FROST_1: u32 = 1_262_935;
122 pub(crate) const HAND_OF_FROST_2: u32 = 1_262_981;
124 pub(crate) const HAND_OF_FROST_3: u32 = 1_263_249;
126 pub(crate) const HEART_OF_ICE: u32 = 1_247_799;
128 pub(crate) const ICE_LANCE: u32 = 30455;
130 pub(crate) const ICICLES: u32 = 1_246_832;
132 pub(crate) const IMPROVED_FLURRY: u32 = 1_247_769;
134 pub(crate) const INFUSED_SPLINTERS: u32 = 1_261_080;
136 pub(crate) const LONELY_WINTER: u32 = 205_024;
138 pub(crate) const PERMAFROST_LANCES: u32 = 460_590;
140 pub(crate) const PIERCING_COLD: u32 = 378_919;
142 pub(crate) const POLISHED_FOCUS: u32 = 1_261_082;
144 pub(crate) const RAY_OF_FROST: u32 = 205_021;
146 pub(crate) const SIGNATURE_SPELL: u32 = 470_021;
148 pub(crate) const SPELLFROST_TEACHINGS: u32 = 444_986;
150 pub(crate) const SPLINTERING_ORBS: u32 = 444_256;
152 pub(crate) const SPLINTERING_RAY: u32 = 418_733;
154 pub(crate) const SPLINTERING_SORCERY: u32 = 443_739;
156 pub(crate) const SPLINTERSTORM: u32 = 443_783;
158 pub(crate) const SPLITTING_ICE: u32 = 56377;
160 pub(crate) const SUMMON_WATER_ELEMENTAL: u32 = 205_024;
162 pub(crate) const THERMAL_VOID: u32 = 451_030;
164 pub(crate) const THERMAL_VOID_TRAIT: u32 = 1_247_729;
166 pub(crate) const WHITE_OUT: u32 = 451_117;
168 pub(crate) const WINTERTIDE: u32 = 378_398;
170 pub(crate) const WINTERTIDE_TRAIT: u32 = 378_406;
172}
173pub(crate) mod SET_BONUS {
174 pub(crate) const MID1_FROST_2PC: u32 = 1_264_836;
176 pub(crate) const MID1_FROST_4PC: u32 = 1_264_837;
178}
179pub(crate) mod REPORTED_SPELL {
180 pub(crate) const BLIZZARD_DAMAGE: u32 = 190_357;
182 pub(crate) const CRYSTALLINE_REFRACTION_DAMAGE: u32 = 1_247_554;
184 pub(crate) const FLURRY: u32 = 228_596;
186 pub(crate) const FROSTBOLT: u32 = 228_597;
188 pub(crate) const FROSTFIRE_BOLT_DAMAGE: u32 = 468_655;
190 pub(crate) const GLACIAL_SPIKE: u32 = 228_600;
192 pub(crate) const ICE_LANCE: u32 = 228_598;
194 pub(crate) const SPLINTERSTORM_DAMAGE: u32 = 443_763;
196 pub(crate) const WATER_JET: u32 = 157_997;
198}
199pub(crate) mod HERO {
200 pub(crate) mod FROSTFIRE {
201 pub(crate) mod SPELL {
202 pub(crate) const DUALITY_PYROBLAST: u32 = 1_262_863;
204 pub(crate) const FLASH_FREEZEBURN_DAMAGE: u32 = 1_278_079;
206 pub(crate) const FROSTFIRE_BOLT: u32 = 431_044;
208 pub(crate) const FROSTFIRE_EMPOWERMENT_DAMAGE: u32 = 431_186;
210 pub(crate) const ISOTHERMIC_METEOR: u32 = 351_140;
212 pub(crate) const MOLTEN_CHILL_IGNITE: u32 = 1_262_887;
214 }
215 pub(crate) mod AURA {
216 pub(crate) const FROSTFIRE_EMPOWERMENT: u32 = 431_177;
218 }
219 }
220 pub(crate) mod SPELLSLINGER {
221 pub(crate) mod SPELL {
222 pub(crate) const CONTROLLED_INSTINCTS: u32 = 444_487;
224 pub(crate) const GLACIAL_ASSAULT: u32 = 379_029;
226 pub(crate) const HAND_OF_FROST: u32 = 1_262_769;
228 pub(crate) const SPLINTER: u32 = 443_722;
230 pub(crate) const WINTERS_END: u32 = 1_280_757;
232 }
233 pub(crate) mod AURA {
234 pub(crate) const SPLINTERSTORM: u32 = 1_247_908;
236 }
237 }
238}
239static HERO_TREE_FROSTFIRE_SPELLS: &[(&str, u32)] = &[
240 ("DUALITY_PYROBLAST", 1_262_863),
241 ("FLASH_FREEZEBURN_DAMAGE", 1_278_079),
242 ("FROSTFIRE_BOLT", 431_044),
243 ("FROSTFIRE_EMPOWERMENT_DAMAGE", 431_186),
244 ("ISOTHERMIC_METEOR", 351_140),
245 ("MOLTEN_CHILL_IGNITE", 1_262_887),
246];
247static HERO_TREE_FROSTFIRE_AURAS: &[(&str, u32)] = &[("FROSTFIRE_EMPOWERMENT", 431_177)];
248static HERO_TREE_SPELLSLINGER_SPELLS: &[(&str, u32)] = &[
249 ("CONTROLLED_INSTINCTS", 444_487),
250 ("GLACIAL_ASSAULT", 379_029),
251 ("HAND_OF_FROST", 1_262_769),
252 ("SPLINTER", 443_722),
253 ("WINTERS_END", 1_280_757),
254];
255static HERO_TREE_SPELLSLINGER_AURAS: &[(&str, u32)] = &[("SPLINTERSTORM", 1_247_908)];
256pub(crate) const AURA_FINGERS_OF_FROST: LocalAuraIdx = LocalAuraIdx::new(0);
257pub(crate) const AURA_BRAIN_FREEZE: LocalAuraIdx = LocalAuraIdx::new(1);
258pub(crate) const AURA_GLACIAL_SPIKE: LocalAuraIdx = LocalAuraIdx::new(2);
259pub(crate) const AURA_COMET_STORM: LocalAuraIdx = LocalAuraIdx::new(3);
260pub(crate) const AURA_FREEZING: LocalAuraIdx = LocalAuraIdx::new(4);
261pub(crate) const AURA_SPLINTERSTORM: LocalAuraIdx = LocalAuraIdx::new(5);
262pub(crate) const AURA_THERMAL_VOID: LocalAuraIdx = LocalAuraIdx::new(6);
263pub(crate) const AURA_FREEZING_RAIN: LocalAuraIdx = LocalAuraIdx::new(7);
264pub(crate) const AURA_PERMAFROST_LANCES: LocalAuraIdx = LocalAuraIdx::new(8);
265pub(crate) const AURA_HAND_OF_FROST: LocalAuraIdx = LocalAuraIdx::new(9);
266pub(crate) const AURA_FROSTFIRE_EMPOWERMENT: LocalAuraIdx = LocalAuraIdx::new(10);
267pub(crate) const AURA_ARCANE_INTELLECT_HIGHLIGHT: LocalAuraIdx = LocalAuraIdx::new(11);
268pub(crate) const AURA_IMPROVED_SHATTER: LocalAuraIdx = LocalAuraIdx::new(12);
269pub(crate) const AURA_COUNT: usize = 13;
270const _: () = assert!(
271 AURA_COUNT == 13, "AURA index ABI: count changed -- update positional hook consumers"
272);
273pub(crate) const SPELL_FROSTBOLT: LocalSpellIdx = LocalSpellIdx::new(0);
274pub(crate) const SPELL_ICE_LANCE: LocalSpellIdx = LocalSpellIdx::new(1);
275pub(crate) const SPELL_FLURRY: LocalSpellIdx = LocalSpellIdx::new(2);
276pub(crate) const SPELL_GLACIAL_SPIKE: LocalSpellIdx = LocalSpellIdx::new(3);
277pub(crate) const SPELL_FROZEN_ORB: LocalSpellIdx = LocalSpellIdx::new(4);
278pub(crate) const SPELL_FROZEN_ORB_BOLT: LocalSpellIdx = LocalSpellIdx::new(5);
279pub(crate) const SPELL_FLURRY_BOLT: LocalSpellIdx = LocalSpellIdx::new(6);
280pub(crate) const SPELL_SHATTER_ICE_LANCE: LocalSpellIdx = LocalSpellIdx::new(7);
281pub(crate) const SPELL_FROST_SPLINTER: LocalSpellIdx = LocalSpellIdx::new(8);
282pub(crate) const SPELL_BLIZZARD: LocalSpellIdx = LocalSpellIdx::new(9);
283pub(crate) const SPELL_COMET_STORM: LocalSpellIdx = LocalSpellIdx::new(10);
284pub(crate) const SPELL_RAY_OF_FROST: LocalSpellIdx = LocalSpellIdx::new(11);
285pub(crate) const SPELL_HAND_OF_FROST: LocalSpellIdx = LocalSpellIdx::new(12);
286pub(crate) const SPELL_CONE_OF_COLD: LocalSpellIdx = LocalSpellIdx::new(13);
287pub(crate) const SPELL_ARCANE_INTELLECT: LocalSpellIdx = LocalSpellIdx::new(14);
288pub(crate) const SPELL_COUNT: usize = 15;
289const _: () = assert!(
290 SPELL_COUNT == 15,
291 "SPELL index ABI: count changed -- update positional hook consumers"
292);
293pub(crate) mod EFFECT {
294 pub(crate) const BRAIN_FREEZE_CHANCE: (u32, u8) = (190_447, 1);
296 pub(crate) const DEEP_SHATTER_BONUS: (u32, u8) = (1_247_025, 1);
298 pub(crate) const FINGERS_OF_FROST_CHANCE: (u32, u8) = (112_965, 1);
300 pub(crate) const FLASH_FREEZE_FOF_CHANCE: (u32, u8) = (379_993, 1);
302 pub(crate) const FLURRY_BOLT_DAMAGE: (u32, u8) = (228_354, 1);
304 pub(crate) const FREEZE_AND_SHATTER_DAMAGE: (u32, u8) = (1_246_752, 1);
306 pub(crate) const FREEZE_AND_SHATTER_LABEL_DAMAGE: (u32, u8) = (1_246_752, 5);
308 pub(crate) const FREEZE_AND_SHATTER_PERIODIC: (u32, u8) = (1_246_752, 3);
310 pub(crate) const FREEZE_AND_SHATTER_SHATTER_DAMAGE: (u32, u8) = (1_246_752, 2);
312 pub(crate) const FREEZE_AND_SHATTER_SHATTER_PERIODIC: (u32, u8) = (1_246_752, 4);
314 pub(crate) const FROST_SPLINTER_DAMAGE: (u32, u8) = (443_722, 1);
316 pub(crate) const FROZEN_ORB_BOLT_DAMAGE: (u32, u8) = (84721, 1);
318 pub(crate) const HAND_OF_FROST_CHANCE: (u32, u8) = (1_262_981, 1);
320 pub(crate) const ICICLES_PERIOD: (u32, u8) = (1_246_832, 1);
322 pub(crate) const MID1_FROST_2PC_FOF_CHANCE: (u32, u8) = (1_264_836, 3);
324 pub(crate) const MID1_FROST_4PC_FOF_SHATTER: (u32, u8) = (1_264_837, 1);
326 pub(crate) const SHATTER_ICE_LANCE_DAMAGE: (u32, u8) = (1_246_949, 1);
328}
329static REPORTED_SPELLS: &[(&str, u32)] = &[
330 ("blizzard_damage", 190_357),
331 ("crystalline_refraction_damage", 1_247_554),
332 ("flurry", 228_596),
333 ("frostbolt", 228_597),
334 ("frostfire_bolt_damage", 468_655),
335 ("glacial_spike", 228_600),
336 ("ice_lance", 228_598),
337 ("splinterstorm_damage", 443_763),
338 ("water_jet", 157_997),
339];
340static DECLARED_SPELL_IDS: &[u32] = &[
341 116, 30455, 44614, 199_786, 84714, 84721, 228_354, 1_246_949, 443_722, 190_356,
342 153_595, 205_021, 1_262_769, 120, 1459, 44544, 190_446, 1_222_865, 1_247_778,
343 1_221_389, 1_247_908, 1_247_730, 270_232, 455_122, 1_263_263, 431_177, 1459,
344 1_246_811, 84721, 112_965, 157_997, 190_357, 190_447, 205_021, 228_354, 228_596,
345 228_597, 228_598, 228_600, 379_993, 443_722, 443_763, 468_655, 1_246_752, 1_246_832,
346 1_246_949, 1_247_025, 1_247_554, 1_262_981, 1_264_836, 1_264_837, 190_357, 1_247_554,
347 228_596, 228_597, 468_655, 228_600, 228_598, 443_763, 157_997, 1_262_863, 1_278_079,
348 431_044, 431_186, 351_140, 1_262_887, 431_177, 444_487, 379_029, 1_262_769, 443_722,
349 1_280_757, 1_247_908,
350];
351static DECLARED_AURA_IDS: &[u32] = &[
352 44544, 190_446, 1_222_865, 1_247_778, 1_221_389, 1_247_908, 1_247_730, 270_232,
353 455_122, 1_263_263, 431_177, 1459, 1_246_811, 431_177, 1_247_908,
354];
355static TALENTS: &[(&str, u32)] = &[
356 ("AUGURY_ABOUNDS", TALENT::AUGURY_ABOUNDS),
357 ("BLIZZARD_1", TALENT::BLIZZARD_1),
358 ("BLIZZARD_2", TALENT::BLIZZARD_2),
359 ("BRAIN_FREEZE", TALENT::BRAIN_FREEZE),
360 ("COLD_SNAP", TALENT::COLD_SNAP),
361 ("COMET_STORM", TALENT::COMET_STORM),
362 ("CONE_OF_FROST", TALENT::CONE_OF_FROST),
363 ("CRYSTALLINE_REFRACTION", TALENT::CRYSTALLINE_REFRACTION),
364 ("DEEP_SHATTER", TALENT::DEEP_SHATTER),
365 ("EVERLASTING_FROST", TALENT::EVERLASTING_FROST),
366 ("FINGERS_OF_FROST", TALENT::FINGERS_OF_FROST),
367 ("FLASH_FREEZE", TALENT::FLASH_FREEZE),
368 ("FLURRY", TALENT::FLURRY),
369 ("FORCE_OF_WILL", TALENT::FORCE_OF_WILL),
370 ("FRACTURED_FROST", TALENT::FRACTURED_FROST),
371 ("FREEZING_RAIN", TALENT::FREEZING_RAIN),
372 ("FROSTBITE", TALENT::FROSTBITE),
373 ("FROZEN_ORB", TALENT::FROZEN_ORB),
374 ("FROZEN_TOUCH", TALENT::FROZEN_TOUCH),
375 ("GLACIAL_ASSAULT", TALENT::GLACIAL_ASSAULT),
376 ("GLACIAL_SHATTER", TALENT::GLACIAL_SHATTER),
377 ("GLACIATE", TALENT::GLACIATE),
378 ("HAND_OF_FROST_1", TALENT::HAND_OF_FROST_1),
379 ("HAND_OF_FROST_2", TALENT::HAND_OF_FROST_2),
380 ("HAND_OF_FROST_3", TALENT::HAND_OF_FROST_3),
381 ("HEART_OF_ICE", TALENT::HEART_OF_ICE),
382 ("ICE_LANCE", TALENT::ICE_LANCE),
383 ("ICICLES", TALENT::ICICLES),
384 ("IMPROVED_FLURRY", TALENT::IMPROVED_FLURRY),
385 ("INFUSED_SPLINTERS", TALENT::INFUSED_SPLINTERS),
386 ("LONELY_WINTER", TALENT::LONELY_WINTER),
387 ("PERMAFROST_LANCES", TALENT::PERMAFROST_LANCES),
388 ("PIERCING_COLD", TALENT::PIERCING_COLD),
389 ("POLISHED_FOCUS", TALENT::POLISHED_FOCUS),
390 ("RAY_OF_FROST", TALENT::RAY_OF_FROST),
391 ("SIGNATURE_SPELL", TALENT::SIGNATURE_SPELL),
392 ("SPELLFROST_TEACHINGS", TALENT::SPELLFROST_TEACHINGS),
393 ("SPLINTERING_ORBS", TALENT::SPLINTERING_ORBS),
394 ("SPLINTERING_RAY", TALENT::SPLINTERING_RAY),
395 ("SPLINTERING_SORCERY", TALENT::SPLINTERING_SORCERY),
396 ("SPLINTERSTORM", TALENT::SPLINTERSTORM),
397 ("SPLITTING_ICE", TALENT::SPLITTING_ICE),
398 ("SUMMON_WATER_ELEMENTAL", TALENT::SUMMON_WATER_ELEMENTAL),
399 ("THERMAL_VOID", TALENT::THERMAL_VOID),
400 ("THERMAL_VOID_TRAIT", TALENT::THERMAL_VOID_TRAIT),
401 ("WHITE_OUT", TALENT::WHITE_OUT),
402 ("WINTERTIDE", TALENT::WINTERTIDE),
403 ("WINTERTIDE_TRAIT", TALENT::WINTERTIDE_TRAIT),
404];
405pub(crate) mod FROST_MAGE {
406 pub(crate) const FROSTBOLT_CASTS: u32 = 0;
407 pub(crate) const ICE_LANCE_CASTS: u32 = 1;
408 pub(crate) const FLURRY_CASTS: u32 = 2;
409 pub(crate) const GLACIAL_SPIKE_CASTS: u32 = 3;
410 pub(crate) const FROZEN_ORB_CASTS: u32 = 4;
411 pub(crate) const RAY_OF_FROST_CASTS: u32 = 5;
412 pub(crate) const COMET_STORM_CASTS: u32 = 6;
413 pub(crate) const FINGERS_OF_FROST_UPTIME_MS: u32 = 7;
414}
415fn aura_fingers_of_frost(
416 a: AuraDefinitionDraft,
417 data: &ResolvedGameData,
418) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
419 Ok(
420 a
421 .on_player()
422 .apply_base_from_data(data, 44544)?
423 .max_stacks(
424 data
425 .require_aura_max_stacks(
426 wowlab_types::sim::SpellIdx::from_raw(44544),
427 )?,
428 ),
429 )
430}
431fn aura_brain_freeze(
432 a: AuraDefinitionDraft,
433 data: &ResolvedGameData,
434) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
435 Ok(
436 a
437 .on_player()
438 .apply_base_from_data(data, 190_446)?
439 .max_stacks(
440 data
441 .require_aura_max_stacks(
442 wowlab_types::sim::SpellIdx::from_raw(190_446),
443 )?,
444 ),
445 )
446}
447fn aura_glacial_spike(
448 a: AuraDefinitionDraft,
449 data: &ResolvedGameData,
450) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
451 Ok(
452 a
453 .on_player()
454 .apply_base_from_data(data, 1_222_865)?
455 .max_stacks(
456 wowlab_engine_combat::validated_max_stacks(
457 5.0,
458 1_222_865,
459 "aura.max_stacks",
460 )?,
461 ),
462 )
463}
464fn aura_comet_storm(
465 a: AuraDefinitionDraft,
466 data: &ResolvedGameData,
467) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
468 Ok(
469 a
470 .on_player()
471 .apply_base_from_data(data, 1_247_778)?
472 .max_stacks(
473 data
474 .require_aura_max_stacks(
475 wowlab_types::sim::SpellIdx::from_raw(1_247_778),
476 )?,
477 ),
478 )
479}
480fn aura_freezing(
481 a: AuraDefinitionDraft,
482 data: &ResolvedGameData,
483) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
484 Ok(
485 a
486 .on_target()
487 .apply_base_from_data(data, 1_221_389)?
488 .max_stacks(
489 data
490 .require_aura_max_stacks(
491 wowlab_types::sim::SpellIdx::from_raw(1_221_389),
492 )?,
493 ),
494 )
495}
496fn aura_splinterstorm(
497 a: AuraDefinitionDraft,
498 data: &ResolvedGameData,
499) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
500 Ok(
501 a
502 .on_player()
503 .apply_base_from_data(data, 1_247_908)?
504 .max_stacks(
505 data
506 .require_aura_max_stacks(
507 wowlab_types::sim::SpellIdx::from_raw(1_247_908),
508 )?,
509 ),
510 )
511}
512fn aura_thermal_void(
513 a: AuraDefinitionDraft,
514 data: &ResolvedGameData,
515) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
516 Ok(
517 a
518 .on_player()
519 .apply_base_from_data(data, 1_247_730)?
520 .max_stacks(
521 data
522 .require_aura_max_stacks(
523 wowlab_types::sim::SpellIdx::from_raw(1_247_730),
524 )?,
525 ),
526 )
527}
528fn aura_freezing_rain(
529 a: AuraDefinitionDraft,
530 data: &ResolvedGameData,
531) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
532 Ok(
533 a
534 .on_player()
535 .apply_base_from_data(data, 270_232)?
536 .max_stacks(
537 data
538 .require_aura_max_stacks(
539 wowlab_types::sim::SpellIdx::from_raw(270_232),
540 )?,
541 ),
542 )
543}
544fn aura_permafrost_lances(
545 a: AuraDefinitionDraft,
546 data: &ResolvedGameData,
547) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
548 Ok(
549 a
550 .on_player()
551 .apply_base_from_data(data, 455_122)?
552 .max_stacks(
553 data
554 .require_aura_max_stacks(
555 wowlab_types::sim::SpellIdx::from_raw(455_122),
556 )?,
557 ),
558 )
559}
560fn aura_hand_of_frost(
561 a: AuraDefinitionDraft,
562 data: &ResolvedGameData,
563) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
564 Ok(
565 a
566 .on_player()
567 .apply_base_from_data(data, 1_263_263)?
568 .max_stacks(
569 data
570 .require_aura_max_stacks(
571 wowlab_types::sim::SpellIdx::from_raw(1_263_263),
572 )?,
573 )
574 .async_stacks(),
575 )
576}
577fn aura_frostfire_empowerment(
578 a: AuraDefinitionDraft,
579 data: &ResolvedGameData,
580) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
581 Ok(
582 a
583 .on_player()
584 .apply_base_from_data(data, 431_177)?
585 .max_stacks(
586 data
587 .require_aura_max_stacks(
588 wowlab_types::sim::SpellIdx::from_raw(431_177),
589 )?,
590 ),
591 )
592}
593fn aura_arcane_intellect_highlight(
594 a: AuraDefinitionDraft,
595 data: &ResolvedGameData,
596) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
597 Ok(
598 a
599 .on_player()
600 .apply_base_from_data(data, 1459)?
601 .max_stacks(
602 data
603 .require_aura_max_stacks(
604 wowlab_types::sim::SpellIdx::from_raw(1459),
605 )?,
606 ),
607 )
608}
609fn aura_improved_shatter(
610 a: AuraDefinitionDraft,
611 data: &ResolvedGameData,
612) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
613 Ok(
614 a
615 .on_player()
616 .apply_base_from_data(data, 1_246_811)?
617 .max_stacks(
618 data
619 .require_aura_max_stacks(
620 wowlab_types::sim::SpellIdx::from_raw(1_246_811),
621 )?,
622 ),
623 )
624}
625fn spell_frostbolt(
626 s: SpellDefinitionDraft,
627 data: &ResolvedGameData,
628) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
629 Ok(
630 s
631 .apply_base_from_data(data, 116)?
632 .apply_aoe_from_data(data)
633 .on_cast(crate::hooks::frost_mage::frostbolt_hook)
634 .overridden_by(SPELL::GLACIAL_SPIKE, AURA::GLACIAL_SPIKE)
635 .override_at_min_stacks(5),
636 )
637}
638fn spell_ice_lance(
639 s: SpellDefinitionDraft,
640 data: &ResolvedGameData,
641) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
642 Ok(
643 s
644 .apply_base_from_data(data, 30455)?
645 .damage_sp_from_data(
646 wowlab_engine_domain::dbc::EffectLookup::new(
647 data,
648 wowlab_types::sim::EffectRef::new(
649 wowlab_types::sim::SpellIdx::from_raw(228_598),
650 1,
651 ),
652 ),
653 )?
654 .apply_aoe_from_data(data)
655 .on_cast(crate::hooks::frost_mage::ice_lance_hook),
656 )
657}
658fn spell_flurry(
659 s: SpellDefinitionDraft,
660 data: &ResolvedGameData,
661) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
662 Ok(
663 s
664 .apply_base_from_data(data, 44614)?
665 .damage_flat(0.0)
666 .apply_aoe_from_data(data)
667 .on_cast(crate::hooks::frost_mage::flurry_hook),
668 )
669}
670fn spell_glacial_spike(
671 s: SpellDefinitionDraft,
672 data: &ResolvedGameData,
673) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
674 Ok(
675 s
676 .apply_base_from_data(data, 199_786)?
677 .damage_sp_from_data(
678 wowlab_engine_domain::dbc::EffectLookup::new(
679 data,
680 wowlab_types::sim::EffectRef::new(
681 wowlab_types::sim::SpellIdx::from_raw(228_600),
682 1,
683 ),
684 ),
685 )?
686 .applies_inferred_aura(AURA_GLACIAL_SPIKE, 1_222_865)
687 .apply_aoe_from_data(data)
688 .on_cast(crate::hooks::frost_mage::glacial_spike_hook),
689 )
690}
691fn spell_frozen_orb(
692 s: SpellDefinitionDraft,
693 data: &ResolvedGameData,
694) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
695 Ok(
696 s
697 .apply_base_from_data(data, 84714)?
698 .apply_aoe_from_data(data)
699 .on_cast(crate::hooks::frost_mage::frozen_orb_hook),
700 )
701}
702fn spell_frozen_orb_bolt(
703 s: SpellDefinitionDraft,
704 data: &ResolvedGameData,
705) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
706 Ok(s.apply_base_from_data(data, 84721)?.apply_aoe_from_data(data))
707}
708fn spell_flurry_bolt(
709 s: SpellDefinitionDraft,
710 data: &ResolvedGameData,
711) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
712 Ok(s.apply_base_from_data(data, 228_354)?.apply_aoe_from_data(data))
713}
714fn spell_shatter_ice_lance(
715 s: SpellDefinitionDraft,
716 data: &ResolvedGameData,
717) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
718 Ok(s.apply_base_from_data(data, 1_246_949)?.apply_aoe_from_data(data))
719}
720fn spell_frost_splinter(
721 s: SpellDefinitionDraft,
722 data: &ResolvedGameData,
723) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
724 Ok(s.apply_base_from_data(data, 443_722)?.apply_aoe_from_data(data))
725}
726fn spell_blizzard(
727 s: SpellDefinitionDraft,
728 data: &ResolvedGameData,
729) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
730 Ok(s.apply_base_from_data(data, 190_356)?.apply_aoe_from_data(data))
731}
732fn spell_comet_storm(
733 s: SpellDefinitionDraft,
734 data: &ResolvedGameData,
735) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
736 Ok(
737 s
738 .apply_base_from_data(data, 153_595)?
739 .applies_aura(AURA_COMET_STORM)
740 .apply_aoe_from_data(data),
741 )
742}
743fn spell_ray_of_frost(
744 s: SpellDefinitionDraft,
745 data: &ResolvedGameData,
746) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
747 Ok(
748 s
749 .apply_base_from_data(data, 205_021)?
750 .apply_aoe_from_data(data)
751 .channel(
752 wowlab_engine_combat::validated_channel_tick_count(
753 8.0,
754 205_021,
755 "channel.tick_count",
756 )?,
757 )
758 .channel_tick_damage_sp_from_data(
759 wowlab_engine_domain::dbc::EffectLookup::new(
760 data,
761 wowlab_types::sim::EffectRef::new(
762 wowlab_types::sim::SpellIdx::from_raw(205_021),
763 1,
764 ),
765 ),
766 )?
767 .on_cast(crate::hooks::frost_mage::ray_of_frost_hook),
768 )
769}
770fn spell_hand_of_frost(
771 s: SpellDefinitionDraft,
772 data: &ResolvedGameData,
773) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
774 Ok(
775 s
776 .apply_base_from_data(data, 1_262_769)?
777 .applies_inferred_aura(AURA_HAND_OF_FROST, 1_263_263)
778 .apply_aoe_from_data(data),
779 )
780}
781fn spell_cone_of_cold(
782 s: SpellDefinitionDraft,
783 data: &ResolvedGameData,
784) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
785 Ok(s.apply_base_from_data(data, 120)?.apply_aoe_from_data(data))
786}
787fn spell_arcane_intellect(
788 s: SpellDefinitionDraft,
789 data: &ResolvedGameData,
790) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
791 Ok(
792 s
793 .apply_base_from_data(data, 1459)?
794 .applies_inferred_aura(AURA_ARCANE_INTELLECT_HIGHLIGHT, 1459)
795 .apply_aoe_from_data(data),
796 )
797}
798pub(crate) fn build_combat_system(
799 params: &wowlab_engine_ports::HandlerParams<'_>,
800) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
801 let builder = BuiltCombatSystem::builder(params.stats.clone())
802 .spec_id(SpecId::FrostMage)
803 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
804 .mastery_spell(1_246_752)
805 .mastery_hook(crate::hooks::frost_mage::mastery)
806 .resource(
807 "mana",
808 100_000.0,
809 params
810 .game_data
811 .power_regen_for_max(0, 100_000.0)
812 .ok_or_else(|| {
813 wowlab_engine_ports::EngineError::spec_construction(
814 "missing power-type power_regen_for_max for type_id 0",
815 )
816 })?,
817 )
818 .resource_type_id(0)
819 .resource_start(100_000.0)
820 .mastery_crit_damage_hook(crate::hooks::frost_mage::crit_damage)
821 .aura(
822 "fingers_of_frost",
823 AURA::FINGERS_OF_FROST,
824 |a| aura_fingers_of_frost(a, ¶ms.game_data),
825 )
826 .aura(
827 "brain_freeze",
828 AURA::BRAIN_FREEZE,
829 |a| aura_brain_freeze(a, ¶ms.game_data),
830 )
831 .aura(
832 "glacial_spike",
833 AURA::GLACIAL_SPIKE,
834 |a| aura_glacial_spike(a, ¶ms.game_data),
835 )
836 .aura(
837 "comet_storm",
838 AURA::COMET_STORM,
839 |a| aura_comet_storm(a, ¶ms.game_data),
840 )
841 .aura("freezing", AURA::FREEZING, |a| aura_freezing(a, ¶ms.game_data))
842 .aura(
843 "splinterstorm",
844 AURA::SPLINTERSTORM,
845 |a| aura_splinterstorm(a, ¶ms.game_data),
846 )
847 .aura(
848 "thermal_void",
849 AURA::THERMAL_VOID,
850 |a| aura_thermal_void(a, ¶ms.game_data),
851 )
852 .aura(
853 "freezing_rain",
854 AURA::FREEZING_RAIN,
855 |a| aura_freezing_rain(a, ¶ms.game_data),
856 )
857 .aura(
858 "permafrost_lances",
859 AURA::PERMAFROST_LANCES,
860 |a| aura_permafrost_lances(a, ¶ms.game_data),
861 )
862 .aura(
863 "hand_of_frost",
864 AURA::HAND_OF_FROST,
865 |a| aura_hand_of_frost(a, ¶ms.game_data),
866 )
867 .aura(
868 "frostfire_empowerment",
869 AURA::FROSTFIRE_EMPOWERMENT,
870 |a| aura_frostfire_empowerment(a, ¶ms.game_data),
871 )
872 .aura(
873 "arcane_intellect_highlight",
874 AURA::ARCANE_INTELLECT_HIGHLIGHT,
875 |a| aura_arcane_intellect_highlight(a, ¶ms.game_data),
876 )
877 .aura(
878 "improved_shatter",
879 AURA::IMPROVED_SHATTER,
880 |a| aura_improved_shatter(a, ¶ms.game_data),
881 )
882 .spell("frostbolt", SPELL::FROSTBOLT, |s| spell_frostbolt(s, ¶ms.game_data))
883 .spell("ice_lance", SPELL::ICE_LANCE, |s| spell_ice_lance(s, ¶ms.game_data))
884 .spell("flurry", SPELL::FLURRY, |s| spell_flurry(s, ¶ms.game_data))
885 .spell(
886 "glacial_spike",
887 SPELL::GLACIAL_SPIKE,
888 |s| spell_glacial_spike(s, ¶ms.game_data),
889 )
890 .spell(
891 "frozen_orb",
892 SPELL::FROZEN_ORB,
893 |s| spell_frozen_orb(s, ¶ms.game_data),
894 )
895 .spell(
896 "frozen_orb_bolt",
897 SPELL::FROZEN_ORB_BOLT,
898 |s| spell_frozen_orb_bolt(s, ¶ms.game_data),
899 )
900 .spell(
901 "flurry_bolt",
902 SPELL::FLURRY_BOLT,
903 |s| spell_flurry_bolt(s, ¶ms.game_data),
904 )
905 .spell(
906 "shatter_ice_lance",
907 SPELL::SHATTER_ICE_LANCE,
908 |s| spell_shatter_ice_lance(s, ¶ms.game_data),
909 )
910 .spell(
911 "frost_splinter",
912 SPELL::FROST_SPLINTER,
913 |s| spell_frost_splinter(s, ¶ms.game_data),
914 )
915 .spell("blizzard", SPELL::BLIZZARD, |s| spell_blizzard(s, ¶ms.game_data))
916 .spell(
917 "comet_storm",
918 SPELL::COMET_STORM,
919 |s| spell_comet_storm(s, ¶ms.game_data),
920 )
921 .spell(
922 "ray_of_frost",
923 SPELL::RAY_OF_FROST,
924 |s| spell_ray_of_frost(s, ¶ms.game_data),
925 )
926 .spell(
927 "hand_of_frost",
928 SPELL::HAND_OF_FROST,
929 |s| spell_hand_of_frost(s, ¶ms.game_data),
930 )
931 .spell(
932 "cone_of_cold",
933 SPELL::CONE_OF_COLD,
934 |s| spell_cone_of_cold(s, ¶ms.game_data),
935 )
936 .spell(
937 "arcane_intellect",
938 SPELL::ARCANE_INTELLECT,
939 |s| spell_arcane_intellect(s, ¶ms.game_data),
940 )
941 .game_data(params.game_data.clone())
942 .talent_spell_ids(TALENTS)
943 .talent_selections(params.talent_selections)
944 .set_bonus_auras(params.set_bonus_auras)
945 .encounter(params.encounter.clone());
946 let builder = crate::generated::items::register_equipped(
947 builder,
948 ¶ms.game_data,
949 params.equipped_items,
950 )?;
951
952 builder
953 .build(params.rotation)
954 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
955}
956fn new_handler(
957 params: wowlab_engine_ports::HandlerParams<'_>,
958) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
959 let handler = crate::composition::compose_handler(
960 build_combat_system,
961 ¶ms,
962 crate::hooks::frost_mage::FrostHandler::try_new,
963 )?;
964
965 Ok(Box::new(handler))
966}
967static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
968 primary_resource: ResourceType::Mana,
969 secondary_resource: None,
970 spell_ids: DECLARED_SPELL_IDS,
971 aura_ids: DECLARED_AURA_IDS,
972 server_proc_entries: &[
973 DeclaredServerProcEntry {
974 spell_id: 44544,
975 attributes: ProcAttributes::USE_STACKS_FOR_CHARGES.bits(),
976 },
977 ],
978 talents: TALENTS,
979 rotation_field_schema: &[],
980 metrics_plugin_id: None,
981 hero_talent_trees: &[
982 HeroTalentTreeDesc {
983 name: "Frostfire",
984 spells: HERO_TREE_FROSTFIRE_SPELLS,
985 auras: HERO_TREE_FROSTFIRE_AURAS,
986 },
987 HeroTalentTreeDesc {
988 name: "Spellslinger",
989 spells: HERO_TREE_SPELLSLINGER_SPELLS,
990 auras: HERO_TREE_SPELLSLINGER_AURAS,
991 },
992 ],
993 reported_spells: REPORTED_SPELLS,
994 masked_passive_effects: &[],
995 passive_effect_overrides: &[],
996};
997pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
998 spec_id: SpecId::FrostMage,
999 display_name: "Frost Mage",
1000 metadata: DECLARED_METADATA,
1001 handler_factory: new_handler,
1002};
1003wowlab_engine_combat::smoke_test!(build_combat_system);