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 ARCANE_BLAST: u32 = 30451;
16 pub(crate) const ARCANE_BARRAGE: u32 = 44425;
18 pub(crate) const ARCANE_MISSILES: u32 = 5143;
20 pub(crate) const ARCANE_MISSILES_DAMAGE: u32 = 7268;
22 pub(crate) const ARCANE_SURGE: u32 = 365_350;
24 pub(crate) const ARCANE_ORB: u32 = 153_626;
26 pub(crate) const ARCANE_ORB_BOLT: u32 = 153_640;
28 pub(crate) const ARCANE_SPLINTER: u32 = 443_763;
30 pub(crate) const TOUCH_OF_THE_MAGI: u32 = 321_507;
32 pub(crate) const TOUCH_OF_THE_MAGI_EXPLOSION: u32 = 210_833;
34 pub(crate) const TOUCH_OF_THE_ARCHMAGE: u32 = 1_258_134;
36 pub(crate) const EVOCATION: u32 = 12051;
38 pub(crate) const PRESENCE_OF_MIND: u32 = 205_025;
40 pub(crate) const ARCANE_EXPLOSION: u32 = 1449;
42 pub(crate) const ARCANE_INTELLECT: u32 = 1459;
44 pub(crate) const ARCANE_ASSAULT: u32 = 225_119;
46}
47pub(crate) mod AURA {
48 pub(crate) const ARCANE_CHARGE: u32 = 36032;
50 pub(crate) const ARCANE_SURGE: u32 = 365_362;
52 pub(crate) const CLEARCASTING: u32 = 263_725;
54 pub(crate) const ENLIGHTENED: u32 = 1_217_242;
56 pub(crate) const ARCANE_SALVO: u32 = 1_242_974;
58 pub(crate) const INTUITION: u32 = 1_223_797;
60 pub(crate) const PRESENCE_OF_MIND: u32 = 205_025;
62 pub(crate) const EVOCATION: u32 = 12051;
64 pub(crate) const TOUCH_OF_THE_MAGI: u32 = 210_824;
66 pub(crate) const NETHER_PRECISION: u32 = 383_783;
68 pub(crate) const OVERPOWERED_MISSILES: u32 = 1_277_009;
70 pub(crate) const ARCANE_INTELLECT_HIGHLIGHT: u32 = 1459;
72 pub(crate) const HIGH_VOLTAGE: u32 = 461_524;
74 pub(crate) const AMPLIFICATION: u32 = 236_628;
76 pub(crate) const SPELLFIRE_SALVO: u32 = 449_322;
78}
79pub(crate) mod TALENT {
80 pub(crate) const AEGWYNNS_TECHNIQUE: u32 = 1_243_507;
82 pub(crate) const AMPLIFICATION: u32 = 236_628;
84 pub(crate) const ARCANE_ECHO: u32 = 342_231;
86 pub(crate) const ARCANE_FAMILIAR: u32 = 205_022;
88 pub(crate) const ARCANE_MISSILES: u32 = 5143;
90 pub(crate) const ARCANE_ORB: u32 = 153_626;
92 pub(crate) const ARCANE_PULSE: u32 = 381_006;
94 pub(crate) const ARCANE_SALVO: u32 = 1_242_974;
96 pub(crate) const ARCANE_SALVO_DRIVER: u32 = 384_452;
98 pub(crate) const ARCANE_SURGE: u32 = 365_350;
100 pub(crate) const ARCING_CLEAVE: u32 = 231_564;
102 pub(crate) const ATTUNED_FAMILIAR: u32 = 1_261_106;
104 pub(crate) const AUGURY_ABOUNDS: u32 = 1_280_165;
106 pub(crate) const CHARGED_MISSILES: u32 = 461_251;
108 pub(crate) const CHARGED_ORB: u32 = 384_651;
110 pub(crate) const ENERGIZED_FAMILIAR: u32 = 452_997;
112 pub(crate) const ENLIGHTENED: u32 = 321_387;
114 pub(crate) const EUREKA: u32 = 452_198;
116 pub(crate) const EVOCATION: u32 = 12051;
118 pub(crate) const EXPANDED_MIND: u32 = 451_548;
120 pub(crate) const EXPANDED_MIND_DRIVER: u32 = 1_243_557;
122 pub(crate) const FOCUSING_CRYSTAL: u32 = 452_143;
124 pub(crate) const FORCE_OF_WILL_DRIVER: u32 = 444_719;
126 pub(crate) const HIGH_VOLTAGE: u32 = 461_248;
128 pub(crate) const ILLUMINATED_THOUGHTS: u32 = 384_060;
130 pub(crate) const IMPROVED_CLEARCASTING: u32 = 321_420;
132 pub(crate) const INFUSED_SPLINTERS: u32 = 1_261_080;
134 pub(crate) const INTUITION: u32 = 1_223_798;
136 pub(crate) const MANA_ADEPT: u32 = 321_526;
138 pub(crate) const MANA_BOMB: u32 = 449_906;
140 pub(crate) const MANA_BOMB_DRIVER: u32 = 457_521;
142 pub(crate) const ORB_BARRAGE: u32 = 384_858;
144 pub(crate) const ORB_MASTERY: u32 = 452_197;
146 pub(crate) const ORB_MASTERY_DRIVER: u32 = 1_243_435;
148 pub(crate) const OVERPOWERED_MISSILES: u32 = 451_728;
150 pub(crate) const OVERPOWERED_MISSILES_DRIVER: u32 = 1_244_329;
152 pub(crate) const PRESENCE_OF_MIND: u32 = 205_025;
154 pub(crate) const PRODIGIOUS_SAVANT: u32 = 384_612;
156 pub(crate) const RESONANCE: u32 = 205_028;
158 pub(crate) const REVERBERATE: u32 = 281_482;
160 pub(crate) const SLIPSTREAM: u32 = 236_457;
162 pub(crate) const SPELLFROST_TEACHINGS: u32 = 444_986;
164 pub(crate) const SPLINTERING_ORBS: u32 = 444_256;
166 pub(crate) const SPLINTERING_SORCERY: u32 = 443_739;
168 pub(crate) const SPLINTERSTORM: u32 = 443_783;
170 pub(crate) const TOUCH_OF_THE_ARCHMAGE: u32 = 1_257_950;
172 pub(crate) const TOUCH_OF_THE_MAGI: u32 = 321_507;
174}
175pub(crate) mod REPORTED_SPELL {
176 pub(crate) const ARCANE_FAMILIAR: u32 = 210_126;
178 pub(crate) const ARCANE_MISSILES: u32 = 7268;
180}
181pub(crate) mod HERO {
182 pub(crate) mod SPELLSLINGER {
183 pub(crate) mod SPELL {
184 pub(crate) const CONTROLLED_INSTINCTS: u32 = 444_720;
186 pub(crate) const SPLINTER: u32 = 443_763;
188 }
189 pub(crate) mod AURA {
190 pub(crate) const SPLINTERSTORM: u32 = 443_783;
192 }
193 }
194 pub(crate) mod SUNFURY {
195 pub(crate) mod SPELL {
196 pub(crate) const METEORITE: u32 = 449_559;
198 pub(crate) const METEORITE_IMPACT: u32 = 449_569;
200 }
201 pub(crate) mod AURA {
202 pub(crate) const ARCANE_SOUL: u32 = 451_038;
204 pub(crate) const LESSER_TIME_WARP: u32 = 1_260_277;
206 pub(crate) const MANA_CASCADE: u32 = 449_322;
208 pub(crate) const SPELLFIRE_SPHERE: u32 = 448_604;
210 }
211 }
212}
213static HERO_TREE_SPELLSLINGER_SPELLS: &[(&str, u32)] = &[
214 ("CONTROLLED_INSTINCTS", 444_720),
215 ("SPLINTER", 443_763),
216];
217static HERO_TREE_SPELLSLINGER_AURAS: &[(&str, u32)] = &[("SPLINTERSTORM", 443_783)];
218static HERO_TREE_SUNFURY_SPELLS: &[(&str, u32)] = &[
219 ("METEORITE", 449_559),
220 ("METEORITE_IMPACT", 449_569),
221];
222static HERO_TREE_SUNFURY_AURAS: &[(&str, u32)] = &[
223 ("ARCANE_SOUL", 451_038),
224 ("LESSER_TIME_WARP", 1_260_277),
225 ("MANA_CASCADE", 449_322),
226 ("SPELLFIRE_SPHERE", 448_604),
227];
228pub(crate) const AURA_ARCANE_CHARGE: LocalAuraIdx = LocalAuraIdx::new(0);
229pub(crate) const AURA_ARCANE_SURGE: LocalAuraIdx = LocalAuraIdx::new(1);
230pub(crate) const AURA_CLEARCASTING: LocalAuraIdx = LocalAuraIdx::new(2);
231pub(crate) const AURA_ENLIGHTENED: LocalAuraIdx = LocalAuraIdx::new(3);
232pub(crate) const AURA_ARCANE_SALVO: LocalAuraIdx = LocalAuraIdx::new(4);
233pub(crate) const AURA_INTUITION: LocalAuraIdx = LocalAuraIdx::new(5);
234pub(crate) const AURA_PRESENCE_OF_MIND: LocalAuraIdx = LocalAuraIdx::new(6);
235pub(crate) const AURA_EVOCATION: LocalAuraIdx = LocalAuraIdx::new(7);
236pub(crate) const AURA_TOUCH_OF_THE_MAGI: LocalAuraIdx = LocalAuraIdx::new(8);
237pub(crate) const AURA_NETHER_PRECISION: LocalAuraIdx = LocalAuraIdx::new(9);
238pub(crate) const AURA_OVERPOWERED_MISSILES: LocalAuraIdx = LocalAuraIdx::new(10);
239pub(crate) const AURA_ARCANE_INTELLECT_HIGHLIGHT: LocalAuraIdx = LocalAuraIdx::new(11);
240pub(crate) const AURA_HIGH_VOLTAGE: LocalAuraIdx = LocalAuraIdx::new(12);
241pub(crate) const AURA_AMPLIFICATION: LocalAuraIdx = LocalAuraIdx::new(13);
242pub(crate) const AURA_SPELLFIRE_SALVO: LocalAuraIdx = LocalAuraIdx::new(14);
243pub(crate) const AURA_COUNT: usize = 15;
244const _: () = assert!(
245 AURA_COUNT == 15, "AURA index ABI: count changed -- update positional hook consumers"
246);
247pub(crate) const SPELL_ARCANE_BLAST: LocalSpellIdx = LocalSpellIdx::new(0);
248pub(crate) const SPELL_ARCANE_BARRAGE: LocalSpellIdx = LocalSpellIdx::new(1);
249pub(crate) const SPELL_ARCANE_MISSILES: LocalSpellIdx = LocalSpellIdx::new(2);
250pub(crate) const SPELL_ARCANE_MISSILES_DAMAGE: LocalSpellIdx = LocalSpellIdx::new(3);
251pub(crate) const SPELL_ARCANE_SURGE: LocalSpellIdx = LocalSpellIdx::new(4);
252pub(crate) const SPELL_ARCANE_ORB: LocalSpellIdx = LocalSpellIdx::new(5);
253pub(crate) const SPELL_ARCANE_ORB_BOLT: LocalSpellIdx = LocalSpellIdx::new(6);
254pub(crate) const SPELL_ARCANE_SPLINTER: LocalSpellIdx = LocalSpellIdx::new(7);
255pub(crate) const SPELL_TOUCH_OF_THE_MAGI: LocalSpellIdx = LocalSpellIdx::new(8);
256pub(crate) const SPELL_TOUCH_OF_THE_MAGI_EXPLOSION: LocalSpellIdx = LocalSpellIdx::new(
257 9,
258);
259pub(crate) const SPELL_TOUCH_OF_THE_ARCHMAGE: LocalSpellIdx = LocalSpellIdx::new(10);
260pub(crate) const SPELL_EVOCATION: LocalSpellIdx = LocalSpellIdx::new(11);
261pub(crate) const SPELL_PRESENCE_OF_MIND: LocalSpellIdx = LocalSpellIdx::new(12);
262pub(crate) const SPELL_ARCANE_EXPLOSION: LocalSpellIdx = LocalSpellIdx::new(13);
263pub(crate) const SPELL_ARCANE_INTELLECT: LocalSpellIdx = LocalSpellIdx::new(14);
264pub(crate) const SPELL_ARCANE_ASSAULT: LocalSpellIdx = LocalSpellIdx::new(15);
265pub(crate) const SPELL_COUNT: usize = 16;
266const _: () = assert!(
267 SPELL_COUNT == 16,
268 "SPELL index ABI: count changed -- update positional hook consumers"
269);
270pub(crate) mod EFFECT {
271 pub(crate) const AMPLIFICATION_WAVES: (u32, u8) = (236_628, 2);
273 pub(crate) const ARCANE_ASSAULT_DAMAGE: (u32, u8) = (225_119, 1);
275 pub(crate) const ARCANE_BARRAGE_DAMAGE: (u32, u8) = (44425, 1);
277 pub(crate) const ARCANE_BLAST_DAMAGE: (u32, u8) = (30451, 1);
279 pub(crate) const ARCANE_CHARGE_BARRAGE: (u32, u8) = (36032, 2);
281 pub(crate) const ARCANE_CHARGE_BLAST: (u32, u8) = (36032, 1);
283 pub(crate) const ARCANE_FAMILIAR_BONUS: (u32, u8) = (210_126, 1);
285 pub(crate) const ARCANE_MISSILES_DAMAGE: (u32, u8) = (7268, 1);
287 pub(crate) const ARCANE_ORB_BOLT_DAMAGE: (u32, u8) = (153_640, 1);
289 pub(crate) const ARCANE_ORB_TARGETS: (u32, u8) = (153_626, 1);
291 pub(crate) const ARCANE_SALVO_DAMAGE: (u32, u8) = (1_242_974, 1);
293 pub(crate) const ARCANE_SPLINTER_DAMAGE: (u32, u8) = (443_763, 1);
295 pub(crate) const ARCANE_SURGE_DAMAGE: (u32, u8) = (365_350, 1);
297 pub(crate) const ATTUNED_FAMILIAR_CHANCE: (u32, u8) = (1_261_106, 1);
299 pub(crate) const CHARGED_MISSILES_DAMAGE: (u32, u8) = (461_251, 1);
301 pub(crate) const CLEARCASTING_PROC: (u32, u8) = (79684, 2);
303 pub(crate) const ENERGIZED_FAMILIAR_ASSAULTS: (u32, u8) = (452_997, 1);
305 pub(crate) const ENERGIZED_FAMILIAR_CHANCE: (u32, u8) = (452_997, 2);
307 pub(crate) const ENERGIZED_FAMILIAR_GAIN: (u32, u8) = (454_020, 1);
309 pub(crate) const ENLIGHTENED_DAMAGE: (u32, u8) = (321_387, 2);
311 pub(crate) const ENLIGHTENED_REGEN: (u32, u8) = (321_387, 3);
313 pub(crate) const FORCE_OF_WILL_BARRAGE_DIVISOR: (u32, u8) = (444_719, 1);
315 pub(crate) const ILLUMINATED_THOUGHTS_CHANCE: (u32, u8) = (384_060, 1);
317 pub(crate) const INTUITION_DAMAGE: (u32, u8) = (1_223_797, 1);
319 pub(crate) const MANA_ADEPT_BONUS: (u32, u8) = (321_526, 1);
321 pub(crate) const ORB_MASTERY_COUNT: (u32, u8) = (1_243_435, 1);
323 pub(crate) const ORB_MASTERY_DAMAGE: (u32, u8) = (1_243_435, 2);
325 pub(crate) const SAVANT_BARRAGE: (u32, u8) = (190_740, 3);
327 pub(crate) const SAVANT_BASE: (u32, u8) = (190_740, 1);
329 pub(crate) const SAVANT_BLAST: (u32, u8) = (190_740, 2);
331 pub(crate) const SAVANT_MASTERY_DAMAGE: (u32, u8) = (190_740, 5);
333 pub(crate) const SAVANT_SPLINTER_DAMAGE: (u32, u8) = (190_740, 6);
335}
336static REPORTED_SPELLS: &[(&str, u32)] = &[
337 ("arcane_familiar", 210_126),
338 ("arcane_missiles", 7268),
339];
340static DECLARED_SPELL_IDS: &[u32] = &[
341 30451, 44425, 5143, 7268, 365_350, 153_626, 153_640, 443_763, 321_507, 210_833,
342 1_258_134, 12051, 205_025, 1449, 1459, 225_119, 36032, 365_362, 263_725, 1_217_242,
343 1_242_974, 1_223_797, 205_025, 12051, 210_824, 383_783, 1_277_009, 1459, 461_524,
344 236_628, 449_322, 7268, 30451, 36032, 44425, 79684, 153_626, 153_640, 190_740,
345 210_126, 225_119, 236_628, 321_387, 321_526, 365_350, 384_060, 443_763, 444_719,
346 452_997, 454_020, 461_251, 1_223_797, 1_242_974, 1_243_435, 1_261_106, 210_126, 7268,
347 444_720, 443_763, 443_783, 449_559, 449_569, 451_038, 1_260_277, 449_322, 448_604,
348];
349static DECLARED_AURA_IDS: &[u32] = &[
350 36032, 365_362, 263_725, 1_217_242, 1_242_974, 1_223_797, 205_025, 12051, 210_824,
351 383_783, 1_277_009, 1459, 461_524, 236_628, 449_322, 443_783, 451_038, 1_260_277,
352 449_322, 448_604,
353];
354static TALENTS: &[(&str, u32)] = &[
355 ("AEGWYNNS_TECHNIQUE", TALENT::AEGWYNNS_TECHNIQUE),
356 ("AMPLIFICATION", TALENT::AMPLIFICATION),
357 ("ARCANE_ECHO", TALENT::ARCANE_ECHO),
358 ("ARCANE_FAMILIAR", TALENT::ARCANE_FAMILIAR),
359 ("ARCANE_MISSILES", TALENT::ARCANE_MISSILES),
360 ("ARCANE_ORB", TALENT::ARCANE_ORB),
361 ("ARCANE_PULSE", TALENT::ARCANE_PULSE),
362 ("ARCANE_SALVO", TALENT::ARCANE_SALVO),
363 ("ARCANE_SALVO_DRIVER", TALENT::ARCANE_SALVO_DRIVER),
364 ("ARCANE_SURGE", TALENT::ARCANE_SURGE),
365 ("ARCING_CLEAVE", TALENT::ARCING_CLEAVE),
366 ("ATTUNED_FAMILIAR", TALENT::ATTUNED_FAMILIAR),
367 ("AUGURY_ABOUNDS", TALENT::AUGURY_ABOUNDS),
368 ("CHARGED_MISSILES", TALENT::CHARGED_MISSILES),
369 ("CHARGED_ORB", TALENT::CHARGED_ORB),
370 ("ENERGIZED_FAMILIAR", TALENT::ENERGIZED_FAMILIAR),
371 ("ENLIGHTENED", TALENT::ENLIGHTENED),
372 ("EUREKA", TALENT::EUREKA),
373 ("EVOCATION", TALENT::EVOCATION),
374 ("EXPANDED_MIND", TALENT::EXPANDED_MIND),
375 ("EXPANDED_MIND_DRIVER", TALENT::EXPANDED_MIND_DRIVER),
376 ("FOCUSING_CRYSTAL", TALENT::FOCUSING_CRYSTAL),
377 ("FORCE_OF_WILL_DRIVER", TALENT::FORCE_OF_WILL_DRIVER),
378 ("HIGH_VOLTAGE", TALENT::HIGH_VOLTAGE),
379 ("ILLUMINATED_THOUGHTS", TALENT::ILLUMINATED_THOUGHTS),
380 ("IMPROVED_CLEARCASTING", TALENT::IMPROVED_CLEARCASTING),
381 ("INFUSED_SPLINTERS", TALENT::INFUSED_SPLINTERS),
382 ("INTUITION", TALENT::INTUITION),
383 ("MANA_ADEPT", TALENT::MANA_ADEPT),
384 ("MANA_BOMB", TALENT::MANA_BOMB),
385 ("MANA_BOMB_DRIVER", TALENT::MANA_BOMB_DRIVER),
386 ("ORB_BARRAGE", TALENT::ORB_BARRAGE),
387 ("ORB_MASTERY", TALENT::ORB_MASTERY),
388 ("ORB_MASTERY_DRIVER", TALENT::ORB_MASTERY_DRIVER),
389 ("OVERPOWERED_MISSILES", TALENT::OVERPOWERED_MISSILES),
390 ("OVERPOWERED_MISSILES_DRIVER", TALENT::OVERPOWERED_MISSILES_DRIVER),
391 ("PRESENCE_OF_MIND", TALENT::PRESENCE_OF_MIND),
392 ("PRODIGIOUS_SAVANT", TALENT::PRODIGIOUS_SAVANT),
393 ("RESONANCE", TALENT::RESONANCE),
394 ("REVERBERATE", TALENT::REVERBERATE),
395 ("SLIPSTREAM", TALENT::SLIPSTREAM),
396 ("SPELLFROST_TEACHINGS", TALENT::SPELLFROST_TEACHINGS),
397 ("SPLINTERING_ORBS", TALENT::SPLINTERING_ORBS),
398 ("SPLINTERING_SORCERY", TALENT::SPLINTERING_SORCERY),
399 ("SPLINTERSTORM", TALENT::SPLINTERSTORM),
400 ("TOUCH_OF_THE_ARCHMAGE", TALENT::TOUCH_OF_THE_ARCHMAGE),
401 ("TOUCH_OF_THE_MAGI", TALENT::TOUCH_OF_THE_MAGI),
402];
403pub(crate) mod ARCANE_MAGE {
404 pub(crate) const ARCANE_BLAST_CASTS: u32 = 0;
405 pub(crate) const ARCANE_BARRAGE_CASTS: u32 = 1;
406 pub(crate) const ARCANE_MISSILES_CASTS: u32 = 2;
407 pub(crate) const ARCANE_SURGE_CASTS: u32 = 3;
408 pub(crate) const ARCANE_ORB_CASTS: u32 = 4;
409 pub(crate) const TOUCH_OF_THE_MAGI_CASTS: u32 = 5;
410 pub(crate) const ARCANE_SURGE_UPTIME_MS: u32 = 6;
411 pub(crate) const TOUCH_OF_THE_MAGI_UPTIME_MS: u32 = 7;
412}
413fn aura_arcane_charge(
414 a: AuraDefinitionDraft,
415 data: &ResolvedGameData,
416) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
417 Ok(
418 a
419 .on_player()
420 .apply_base_from_data(data, 36032)?
421 .max_stacks(
422 data
423 .require_aura_max_stacks(
424 wowlab_types::sim::SpellIdx::from_raw(36032),
425 )?,
426 ),
427 )
428}
429fn aura_arcane_surge(
430 a: AuraDefinitionDraft,
431 data: &ResolvedGameData,
432) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
433 Ok(
434 a
435 .on_player()
436 .apply_base_from_data(data, 365_362)?
437 .max_stacks(
438 data
439 .require_aura_max_stacks(
440 wowlab_types::sim::SpellIdx::from_raw(365_362),
441 )?,
442 ),
443 )
444}
445fn aura_clearcasting(
446 a: AuraDefinitionDraft,
447 data: &ResolvedGameData,
448) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
449 Ok(
450 a
451 .on_player()
452 .apply_base_from_data(data, 263_725)?
453 .max_stacks(
454 data
455 .require_aura_max_stacks(
456 wowlab_types::sim::SpellIdx::from_raw(263_725),
457 )?,
458 ),
459 )
460}
461fn aura_enlightened(
462 a: AuraDefinitionDraft,
463 data: &ResolvedGameData,
464) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
465 Ok(
466 a
467 .on_player()
468 .apply_base_from_data(data, 1_217_242)?
469 .max_stacks(
470 data
471 .require_aura_max_stacks(
472 wowlab_types::sim::SpellIdx::from_raw(1_217_242),
473 )?,
474 ),
475 )
476}
477fn aura_arcane_salvo(
478 a: AuraDefinitionDraft,
479 data: &ResolvedGameData,
480) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
481 Ok(
482 a
483 .on_player()
484 .apply_base_from_data(data, 1_242_974)?
485 .max_stacks(
486 data
487 .require_aura_max_stacks(
488 wowlab_types::sim::SpellIdx::from_raw(1_242_974),
489 )?,
490 ),
491 )
492}
493fn aura_intuition(
494 a: AuraDefinitionDraft,
495 data: &ResolvedGameData,
496) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
497 Ok(
498 a
499 .on_player()
500 .apply_base_from_data(data, 1_223_797)?
501 .max_stacks(
502 data
503 .require_aura_max_stacks(
504 wowlab_types::sim::SpellIdx::from_raw(1_223_797),
505 )?,
506 ),
507 )
508}
509fn aura_presence_of_mind(
510 a: AuraDefinitionDraft,
511 data: &ResolvedGameData,
512) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
513 Ok(
514 a
515 .on_player()
516 .apply_base_from_data(data, 205_025)?
517 .max_stacks(
518 data
519 .require_aura_max_stacks(
520 wowlab_types::sim::SpellIdx::from_raw(205_025),
521 )?,
522 ),
523 )
524}
525fn aura_evocation(
526 a: AuraDefinitionDraft,
527 data: &ResolvedGameData,
528) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
529 Ok(
530 a
531 .on_player()
532 .apply_base_from_data(data, 12051)?
533 .max_stacks(
534 data
535 .require_aura_max_stacks(
536 wowlab_types::sim::SpellIdx::from_raw(12051),
537 )?,
538 ),
539 )
540}
541fn aura_touch_of_the_magi(
542 a: AuraDefinitionDraft,
543 data: &ResolvedGameData,
544) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
545 Ok(
546 a
547 .on_target()
548 .apply_base_from_data(data, 210_824)?
549 .max_stacks(
550 data
551 .require_aura_max_stacks(
552 wowlab_types::sim::SpellIdx::from_raw(210_824),
553 )?,
554 )
555 .on_expire(crate::hooks::arcane_mage::touch_of_the_magi_expire_hook),
556 )
557}
558fn aura_nether_precision(
559 a: AuraDefinitionDraft,
560 data: &ResolvedGameData,
561) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
562 Ok(
563 a
564 .on_player()
565 .apply_base_from_data(data, 383_783)?
566 .max_stacks(
567 data
568 .require_aura_max_stacks(
569 wowlab_types::sim::SpellIdx::from_raw(383_783),
570 )?,
571 ),
572 )
573}
574fn aura_overpowered_missiles(
575 a: AuraDefinitionDraft,
576 data: &ResolvedGameData,
577) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
578 Ok(
579 a
580 .on_player()
581 .apply_base_from_data(data, 1_277_009)?
582 .max_stacks(
583 data
584 .require_aura_max_stacks(
585 wowlab_types::sim::SpellIdx::from_raw(1_277_009),
586 )?,
587 ),
588 )
589}
590fn aura_arcane_intellect_highlight(
591 a: AuraDefinitionDraft,
592 data: &ResolvedGameData,
593) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
594 Ok(
595 a
596 .on_player()
597 .apply_base_from_data(data, 1459)?
598 .max_stacks(
599 data
600 .require_aura_max_stacks(
601 wowlab_types::sim::SpellIdx::from_raw(1459),
602 )?,
603 ),
604 )
605}
606fn aura_high_voltage(
607 a: AuraDefinitionDraft,
608 data: &ResolvedGameData,
609) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
610 Ok(
611 a
612 .on_player()
613 .apply_base_from_data(data, 461_524)?
614 .max_stacks(
615 data
616 .require_aura_max_stacks(
617 wowlab_types::sim::SpellIdx::from_raw(461_524),
618 )?,
619 ),
620 )
621}
622fn aura_amplification(
623 a: AuraDefinitionDraft,
624 data: &ResolvedGameData,
625) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
626 Ok(
627 a
628 .on_player()
629 .apply_base_from_data(data, 236_628)?
630 .max_stacks(
631 data
632 .require_aura_max_stacks(
633 wowlab_types::sim::SpellIdx::from_raw(236_628),
634 )?,
635 ),
636 )
637}
638fn aura_spellfire_salvo(
639 a: AuraDefinitionDraft,
640 data: &ResolvedGameData,
641) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
642 Ok(
643 a
644 .on_player()
645 .apply_base_from_data(data, 449_322)?
646 .max_stacks(
647 data
648 .require_aura_max_stacks(
649 wowlab_types::sim::SpellIdx::from_raw(449_322),
650 )?,
651 ),
652 )
653}
654fn spell_arcane_blast(
655 s: SpellDefinitionDraft,
656 data: &ResolvedGameData,
657) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
658 Ok(
659 s
660 .apply_base_from_data(data, 30451)?
661 .apply_aoe_from_data(data)
662 .on_cast(crate::hooks::arcane_mage::arcane_blast_hook),
663 )
664}
665fn spell_arcane_barrage(
666 s: SpellDefinitionDraft,
667 data: &ResolvedGameData,
668) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
669 Ok(
670 s
671 .apply_base_from_data(data, 44425)?
672 .apply_aoe_from_data(data)
673 .on_cast(crate::hooks::arcane_mage::arcane_barrage_hook),
674 )
675}
676fn spell_arcane_missiles(
677 s: SpellDefinitionDraft,
678 data: &ResolvedGameData,
679) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
680 Ok(
681 s
682 .apply_base_from_data(data, 5143)?
683 .apply_aoe_from_data(data)
684 .on_cast(crate::hooks::arcane_mage::arcane_missiles_hook),
685 )
686}
687fn spell_arcane_missiles_damage(
688 s: SpellDefinitionDraft,
689 data: &ResolvedGameData,
690) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
691 Ok(s.apply_base_from_data(data, 7268)?.apply_aoe_from_data(data))
692}
693fn spell_arcane_surge(
694 s: SpellDefinitionDraft,
695 data: &ResolvedGameData,
696) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
697 Ok(
698 s
699 .apply_base_from_data(data, 365_350)?
700 .applies_aura(AURA_ARCANE_SURGE)
701 .apply_aoe_from_data(data)
702 .on_cast(crate::hooks::arcane_mage::arcane_surge_hook),
703 )
704}
705fn spell_arcane_orb(
706 s: SpellDefinitionDraft,
707 data: &ResolvedGameData,
708) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
709 Ok(
710 s
711 .apply_base_from_data(data, 153_626)?
712 .apply_aoe_from_data(data)
713 .on_cast(crate::hooks::arcane_mage::arcane_orb_hook),
714 )
715}
716fn spell_arcane_orb_bolt(
717 s: SpellDefinitionDraft,
718 data: &ResolvedGameData,
719) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
720 Ok(s.apply_base_from_data(data, 153_640)?.apply_aoe_from_data(data))
721}
722fn spell_arcane_splinter(
723 s: SpellDefinitionDraft,
724 data: &ResolvedGameData,
725) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
726 Ok(s.apply_base_from_data(data, 443_763)?.apply_aoe_from_data(data))
727}
728fn spell_touch_of_the_magi(
729 s: SpellDefinitionDraft,
730 data: &ResolvedGameData,
731) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
732 Ok(
733 s
734 .apply_base_from_data(data, 321_507)?
735 .applies_aura(AURA_TOUCH_OF_THE_MAGI)
736 .apply_aoe_from_data(data)
737 .on_cast(crate::hooks::arcane_mage::touch_of_the_magi_hook),
738 )
739}
740fn spell_touch_of_the_magi_explosion(
741 s: SpellDefinitionDraft,
742 data: &ResolvedGameData,
743) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
744 Ok(s.apply_base_from_data(data, 210_833)?.apply_aoe_from_data(data))
745}
746fn spell_touch_of_the_archmage(
747 s: SpellDefinitionDraft,
748 data: &ResolvedGameData,
749) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
750 Ok(s.apply_base_from_data(data, 1_258_134)?.apply_aoe_from_data(data))
751}
752fn spell_evocation(
753 s: SpellDefinitionDraft,
754 data: &ResolvedGameData,
755) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
756 Ok(
757 s
758 .apply_base_from_data(data, 12051)?
759 .applies_inferred_aura(AURA_EVOCATION, 12051)
760 .apply_aoe_from_data(data),
761 )
762}
763fn spell_presence_of_mind(
764 s: SpellDefinitionDraft,
765 data: &ResolvedGameData,
766) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
767 Ok(
768 s
769 .apply_base_from_data(data, 205_025)?
770 .applies_inferred_aura(AURA_PRESENCE_OF_MIND, 205_025)
771 .apply_aoe_from_data(data),
772 )
773}
774fn spell_arcane_explosion(
775 s: SpellDefinitionDraft,
776 data: &ResolvedGameData,
777) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
778 Ok(s.apply_base_from_data(data, 1449)?.apply_aoe_from_data(data))
779}
780fn spell_arcane_intellect(
781 s: SpellDefinitionDraft,
782 data: &ResolvedGameData,
783) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
784 Ok(
785 s
786 .apply_base_from_data(data, 1459)?
787 .applies_inferred_aura(AURA_ARCANE_INTELLECT_HIGHLIGHT, 1459)
788 .apply_aoe_from_data(data),
789 )
790}
791fn spell_arcane_assault(
792 s: SpellDefinitionDraft,
793 data: &ResolvedGameData,
794) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
795 Ok(s.apply_base_from_data(data, 225_119)?.apply_aoe_from_data(data))
796}
797pub(crate) fn build_combat_system(
798 params: &wowlab_engine_ports::HandlerParams<'_>,
799) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
800 let builder = BuiltCombatSystem::builder(params.stats.clone())
801 .spec_id(SpecId::Arcane)
802 .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
803 .mastery_spell(190_740)
804 .mastery_hook(crate::hooks::arcane_mage::mastery)
805 .resource(
806 "mana",
807 100_000.0,
808 params
809 .game_data
810 .power_regen_for_max(0, 100_000.0)
811 .ok_or_else(|| {
812 wowlab_engine_ports::EngineError::spec_construction(
813 "missing power-type power_regen_for_max for type_id 0",
814 )
815 })?,
816 )
817 .resource_type_id(0)
818 .resource_start(100_000.0)
819 .secondary_resource(
820 "arcane_charges",
821 params
822 .game_data
823 .power_max(16)
824 .ok_or_else(|| {
825 wowlab_engine_ports::EngineError::spec_construction(
826 "missing power-type power_max for type_id 16",
827 )
828 })?,
829 )
830 .secondary_resource_type_id(16)
831 .aura(
832 "arcane_charge",
833 AURA::ARCANE_CHARGE,
834 |a| aura_arcane_charge(a, ¶ms.game_data),
835 )
836 .aura(
837 "arcane_surge",
838 AURA::ARCANE_SURGE,
839 |a| aura_arcane_surge(a, ¶ms.game_data),
840 )
841 .aura(
842 "clearcasting",
843 AURA::CLEARCASTING,
844 |a| aura_clearcasting(a, ¶ms.game_data),
845 )
846 .aura(
847 "enlightened",
848 AURA::ENLIGHTENED,
849 |a| aura_enlightened(a, ¶ms.game_data),
850 )
851 .aura(
852 "arcane_salvo",
853 AURA::ARCANE_SALVO,
854 |a| aura_arcane_salvo(a, ¶ms.game_data),
855 )
856 .aura("intuition", AURA::INTUITION, |a| aura_intuition(a, ¶ms.game_data))
857 .aura(
858 "presence_of_mind",
859 AURA::PRESENCE_OF_MIND,
860 |a| aura_presence_of_mind(a, ¶ms.game_data),
861 )
862 .aura("evocation", AURA::EVOCATION, |a| aura_evocation(a, ¶ms.game_data))
863 .aura(
864 "touch_of_the_magi",
865 AURA::TOUCH_OF_THE_MAGI,
866 |a| aura_touch_of_the_magi(a, ¶ms.game_data),
867 )
868 .aura(
869 "nether_precision",
870 AURA::NETHER_PRECISION,
871 |a| aura_nether_precision(a, ¶ms.game_data),
872 )
873 .aura(
874 "overpowered_missiles",
875 AURA::OVERPOWERED_MISSILES,
876 |a| aura_overpowered_missiles(a, ¶ms.game_data),
877 )
878 .aura(
879 "arcane_intellect_highlight",
880 AURA::ARCANE_INTELLECT_HIGHLIGHT,
881 |a| aura_arcane_intellect_highlight(a, ¶ms.game_data),
882 )
883 .aura(
884 "high_voltage",
885 AURA::HIGH_VOLTAGE,
886 |a| aura_high_voltage(a, ¶ms.game_data),
887 )
888 .aura(
889 "amplification",
890 AURA::AMPLIFICATION,
891 |a| aura_amplification(a, ¶ms.game_data),
892 )
893 .aura(
894 "spellfire_salvo",
895 AURA::SPELLFIRE_SALVO,
896 |a| aura_spellfire_salvo(a, ¶ms.game_data),
897 )
898 .spell(
899 "arcane_blast",
900 SPELL::ARCANE_BLAST,
901 |s| spell_arcane_blast(s, ¶ms.game_data),
902 )
903 .spell(
904 "arcane_barrage",
905 SPELL::ARCANE_BARRAGE,
906 |s| spell_arcane_barrage(s, ¶ms.game_data),
907 )
908 .spell(
909 "arcane_missiles",
910 SPELL::ARCANE_MISSILES,
911 |s| spell_arcane_missiles(s, ¶ms.game_data),
912 )
913 .spell(
914 "arcane_missiles_damage",
915 SPELL::ARCANE_MISSILES_DAMAGE,
916 |s| spell_arcane_missiles_damage(s, ¶ms.game_data),
917 )
918 .spell(
919 "arcane_surge",
920 SPELL::ARCANE_SURGE,
921 |s| spell_arcane_surge(s, ¶ms.game_data),
922 )
923 .spell(
924 "arcane_orb",
925 SPELL::ARCANE_ORB,
926 |s| spell_arcane_orb(s, ¶ms.game_data),
927 )
928 .spell(
929 "arcane_orb_bolt",
930 SPELL::ARCANE_ORB_BOLT,
931 |s| spell_arcane_orb_bolt(s, ¶ms.game_data),
932 )
933 .spell(
934 "arcane_splinter",
935 SPELL::ARCANE_SPLINTER,
936 |s| spell_arcane_splinter(s, ¶ms.game_data),
937 )
938 .spell(
939 "touch_of_the_magi",
940 SPELL::TOUCH_OF_THE_MAGI,
941 |s| spell_touch_of_the_magi(s, ¶ms.game_data),
942 )
943 .spell(
944 "touch_of_the_magi_explosion",
945 SPELL::TOUCH_OF_THE_MAGI_EXPLOSION,
946 |s| spell_touch_of_the_magi_explosion(s, ¶ms.game_data),
947 )
948 .spell(
949 "touch_of_the_archmage",
950 SPELL::TOUCH_OF_THE_ARCHMAGE,
951 |s| spell_touch_of_the_archmage(s, ¶ms.game_data),
952 )
953 .spell("evocation", SPELL::EVOCATION, |s| spell_evocation(s, ¶ms.game_data))
954 .spell(
955 "presence_of_mind",
956 SPELL::PRESENCE_OF_MIND,
957 |s| spell_presence_of_mind(s, ¶ms.game_data),
958 )
959 .spell(
960 "arcane_explosion",
961 SPELL::ARCANE_EXPLOSION,
962 |s| spell_arcane_explosion(s, ¶ms.game_data),
963 )
964 .spell(
965 "arcane_intellect",
966 SPELL::ARCANE_INTELLECT,
967 |s| spell_arcane_intellect(s, ¶ms.game_data),
968 )
969 .spell(
970 "arcane_assault",
971 SPELL::ARCANE_ASSAULT,
972 |s| spell_arcane_assault(s, ¶ms.game_data),
973 )
974 .game_data(params.game_data.clone())
975 .talent_spell_ids(TALENTS)
976 .talent_selections(params.talent_selections)
977 .talent_companion_aura(TALENT::ENLIGHTENED, AURA_ENLIGHTENED)
978 .set_bonus_auras(params.set_bonus_auras)
979 .encounter(params.encounter.clone());
980 let builder = crate::generated::items::register_equipped(
981 builder,
982 ¶ms.game_data,
983 params.equipped_items,
984 )?;
985
986 builder
987 .build(params.rotation)
988 .map_err(wowlab_engine_ports::EngineError::rotation_compile)
989}
990fn new_handler(
991 params: wowlab_engine_ports::HandlerParams<'_>,
992) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
993 let handler = crate::composition::compose_handler(
994 build_combat_system,
995 ¶ms,
996 |parts, params| Ok(crate::hooks::arcane_mage::ArcaneHandler::new(parts, params)),
997 )?;
998
999 Ok(Box::new(handler))
1000}
1001static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
1002 primary_resource: ResourceType::Mana,
1003 secondary_resource: Some(ResourceType::ArcaneCharges),
1004 spell_ids: DECLARED_SPELL_IDS,
1005 aura_ids: DECLARED_AURA_IDS,
1006 server_proc_entries: &[],
1007 talents: TALENTS,
1008 rotation_field_schema: &[],
1009 metrics_plugin_id: None,
1010 hero_talent_trees: &[
1011 HeroTalentTreeDesc {
1012 name: "Spellslinger",
1013 spells: HERO_TREE_SPELLSLINGER_SPELLS,
1014 auras: HERO_TREE_SPELLSLINGER_AURAS,
1015 },
1016 HeroTalentTreeDesc {
1017 name: "Sunfury",
1018 spells: HERO_TREE_SUNFURY_SPELLS,
1019 auras: HERO_TREE_SUNFURY_AURAS,
1020 },
1021 ],
1022 reported_spells: REPORTED_SPELLS,
1023 masked_passive_effects: &[],
1024 passive_effect_overrides: &[],
1025};
1026pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
1027 spec_id: SpecId::Arcane,
1028 display_name: "Arcane Mage",
1029 metadata: DECLARED_METADATA,
1030 handler_factory: new_handler,
1031};
1032wowlab_engine_combat::smoke_test!(build_combat_system);