Skip to main content

wowlab_engine_content/generated/specs/
devourer_demon_hunter.rs

1//! @generated by codegen-cli from manifests/specs/demon_hunter/devourer/manifest.toml -- do not edit by hand.
2// Generated code: naming follows game data conventions
3#![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    ///<https://wowlab.gg/inspect/spell/473662>
17    pub(crate) const CONSUME: u32 = 473_662;
18    ///<https://wowlab.gg/inspect/spell/1217610>
19    pub(crate) const DEVOUR: u32 = 1_217_610;
20    ///<https://wowlab.gg/inspect/spell/1245412>
21    pub(crate) const VOIDBLADE: u32 = 1_245_412;
22    ///<https://wowlab.gg/inspect/spell/473728>
23    pub(crate) const VOID_RAY: u32 = 473_728;
24    ///<https://wowlab.gg/inspect/spell/1213649>
25    pub(crate) const VOID_RAY_TICK: u32 = 1_213_649;
26    ///<https://wowlab.gg/inspect/spell/1214595>
27    pub(crate) const VOID_RAY_TICK_META: u32 = 1_214_595;
28    ///<https://wowlab.gg/inspect/spell/1226019>
29    pub(crate) const REAP: u32 = 1_226_019;
30    ///<https://wowlab.gg/inspect/spell/1217607>
31    pub(crate) const VOID_METAMORPHOSIS: u32 = 1_217_607;
32    ///<https://wowlab.gg/inspect/spell/258920>
33    pub(crate) const IMMOLATION_AURA: u32 = 258_920;
34    ///<https://wowlab.gg/inspect/spell/370965>
35    pub(crate) const THE_HUNT: u32 = 370_965;
36    ///<https://wowlab.gg/inspect/spell/1245453>
37    pub(crate) const CULL: u32 = 1_245_453;
38    ///<https://wowlab.gg/inspect/spell/1225826>
39    pub(crate) const ERADICATE: u32 = 1_225_826;
40    ///<https://wowlab.gg/inspect/spell/1221150>
41    pub(crate) const COLLAPSING_STAR_SPELL: u32 = 1_221_150;
42    ///<https://wowlab.gg/inspect/spell/1241937>
43    pub(crate) const SOUL_IMMOLATION_SPELL: u32 = 1_241_937;
44    ///<https://wowlab.gg/inspect/spell/1239123>
45    pub(crate) const HUNGERING_SLASH: u32 = 1_239_123;
46    ///<https://wowlab.gg/inspect/spell/1246160>
47    pub(crate) const PREDATORS_WAKE: u32 = 1_246_160;
48    ///<https://wowlab.gg/inspect/spell/1246158>
49    pub(crate) const PIERCE_THE_VEIL: u32 = 1_246_158;
50    ///<https://wowlab.gg/inspect/spell/179057>
51    pub(crate) const STUN: u32 = 179_057;
52}
53pub(crate) mod AURA {
54    ///<https://wowlab.gg/inspect/spell/1217607>
55    pub(crate) const VOID_METAMORPHOSIS: u32 = 1_217_607;
56    ///<https://wowlab.gg/inspect/spell/1225789>
57    pub(crate) const VOID_METAMORPHOSIS_STACK: u32 = 1_225_789;
58    ///<https://wowlab.gg/inspect/spell/1221171>
59    pub(crate) const COLLAPSING_STAR: u32 = 1_221_171;
60    ///<https://wowlab.gg/inspect/spell/1238495>
61    pub(crate) const MOMENT_OF_CRAVING: u32 = 1_238_495;
62    ///<https://wowlab.gg/inspect/spell/1241937>
63    pub(crate) const SOUL_IMMOLATION: u32 = 1_241_937;
64    ///<https://wowlab.gg/inspect/spell/1246556>
65    pub(crate) const SPONTANEOUS_IMMOLATION: u32 = 1_246_556;
66    ///<https://wowlab.gg/inspect/spell/1261684>
67    pub(crate) const ENTROPY_TICKER: u32 = 1_261_684;
68    ///<https://wowlab.gg/inspect/spell/1245577>
69    pub(crate) const SOUL_FRAGMENTS: u32 = 1_245_577;
70    ///<https://wowlab.gg/inspect/spell/1232310>
71    pub(crate) const FEAST_OF_SOULS: u32 = 1_232_310;
72    ///<https://wowlab.gg/inspect/spell/1240203>
73    pub(crate) const FOCUSED_RAY: u32 = 1_240_203;
74    ///<https://wowlab.gg/inspect/spell/473728>
75    pub(crate) const VOID_RAY: u32 = 473_728;
76    ///<https://wowlab.gg/inspect/spell/1256301>
77    pub(crate) const VOIDFALL_BUILDING: u32 = 1_256_301;
78    ///<https://wowlab.gg/inspect/spell/1256302>
79    pub(crate) const VOIDFALL_SPENDING: u32 = 1_256_302;
80    ///<https://wowlab.gg/inspect/spell/1256322>
81    pub(crate) const VOIDFALL_FINAL_HOUR: u32 = 1_256_322;
82    ///<https://wowlab.gg/inspect/spell/1256676>
83    pub(crate) const CATASTROPHE: u32 = 1_256_676;
84}
85pub(crate) mod TALENT {
86    ///<https://wowlab.gg/inspect/spell/1221150>
87    pub(crate) const COLLAPSING_STAR: u32 = 1_221_150;
88    ///<https://wowlab.gg/inspect/spell/1237270>
89    pub(crate) const FEAST_OF_SOULS: u32 = 1_237_270;
90    ///<https://wowlab.gg/inspect/spell/1238739>
91    pub(crate) const SOULSHAPER: u32 = 1_238_739;
92    ///<https://wowlab.gg/inspect/spell/370965>
93    pub(crate) const THE_HUNT: u32 = 370_965;
94    ///<https://wowlab.gg/inspect/spell/473728>
95    pub(crate) const VOID_RAY: u32 = 473_728;
96}
97pub(crate) mod REPORTED_SPELL {
98    ///<https://wowlab.gg/inspect/spell/1225823>
99    pub(crate) const REAP_DAMAGE: u32 = 1_225_823;
100    ///<https://wowlab.gg/inspect/spell/1245414>
101    pub(crate) const VOIDBLADE_DAMAGE: u32 = 1_245_414;
102}
103pub(crate) mod HERO {
104    pub(crate) mod ANNIHILATOR {
105        pub(crate) mod SPELL {
106            ///<https://wowlab.gg/inspect/spell/1253769>
107            pub(crate) const CATASTROPHE: u32 = 1_253_769;
108            ///<https://wowlab.gg/inspect/spell/1279247>
109            pub(crate) const HARNESS_THE_COSMOS: u32 = 1_279_247;
110            ///<https://wowlab.gg/inspect/spell/1256295>
111            pub(crate) const MASS_ACCELERATION: u32 = 1_256_295;
112            ///<https://wowlab.gg/inspect/spell/1253391>
113            pub(crate) const METEORIC_FALL: u32 = 1_253_391;
114            ///<https://wowlab.gg/inspect/spell/1253377>
115            pub(crate) const METEORIC_RISE: u32 = 1_253_377;
116            ///<https://wowlab.gg/inspect/spell/1253817>
117            pub(crate) const OTHERWORLDLY_FOCUS: u32 = 1_253_817;
118            ///<https://wowlab.gg/inspect/spell/1253304>
119            pub(crate) const VOIDFALL: u32 = 1_253_304;
120            ///<https://wowlab.gg/inspect/spell/1256305>
121            pub(crate) const VOIDFALL_METEOR_DAMAGE: u32 = 1_256_305;
122            ///<https://wowlab.gg/inspect/spell/1256353>
123            pub(crate) const WORLD_KILLER: u32 = 1_256_353;
124            ///<https://wowlab.gg/inspect/spell/1256619>
125            pub(crate) const WORLD_KILLER_DAMAGE: u32 = 1_256_619;
126        }
127        pub(crate) mod AURA {
128            ///<https://wowlab.gg/inspect/spell/1256301>
129            pub(crate) const VOIDFALL_BUILDING: u32 = 1_256_301;
130            ///<https://wowlab.gg/inspect/spell/1256322>
131            pub(crate) const VOIDFALL_FINAL_HOUR: u32 = 1_256_322;
132            ///<https://wowlab.gg/inspect/spell/1256302>
133            pub(crate) const VOIDFALL_SPENDING: u32 = 1_256_302;
134        }
135    }
136}
137static HERO_TREE_ANNIHILATOR_SPELLS: &[(&str, u32)] = &[
138    ("CATASTROPHE", 1_253_769),
139    ("HARNESS_THE_COSMOS", 1_279_247),
140    ("MASS_ACCELERATION", 1_256_295),
141    ("METEORIC_FALL", 1_253_391),
142    ("METEORIC_RISE", 1_253_377),
143    ("OTHERWORLDLY_FOCUS", 1_253_817),
144    ("VOIDFALL", 1_253_304),
145    ("VOIDFALL_METEOR_DAMAGE", 1_256_305),
146    ("WORLD_KILLER", 1_256_353),
147    ("WORLD_KILLER_DAMAGE", 1_256_619),
148];
149static HERO_TREE_ANNIHILATOR_AURAS: &[(&str, u32)] = &[
150    ("VOIDFALL_BUILDING", 1_256_301),
151    ("VOIDFALL_FINAL_HOUR", 1_256_322),
152    ("VOIDFALL_SPENDING", 1_256_302),
153];
154pub(crate) const AURA_VOID_METAMORPHOSIS: LocalAuraIdx = LocalAuraIdx::new(0);
155pub(crate) const AURA_VOID_METAMORPHOSIS_STACK: LocalAuraIdx = LocalAuraIdx::new(1);
156pub(crate) const AURA_COLLAPSING_STAR: LocalAuraIdx = LocalAuraIdx::new(2);
157pub(crate) const AURA_MOMENT_OF_CRAVING: LocalAuraIdx = LocalAuraIdx::new(3);
158pub(crate) const AURA_SOUL_IMMOLATION: LocalAuraIdx = LocalAuraIdx::new(4);
159pub(crate) const AURA_SPONTANEOUS_IMMOLATION: LocalAuraIdx = LocalAuraIdx::new(5);
160pub(crate) const AURA_ENTROPY_TICKER: LocalAuraIdx = LocalAuraIdx::new(6);
161pub(crate) const AURA_SOUL_FRAGMENTS: LocalAuraIdx = LocalAuraIdx::new(7);
162pub(crate) const AURA_FEAST_OF_SOULS: LocalAuraIdx = LocalAuraIdx::new(8);
163pub(crate) const AURA_FOCUSED_RAY: LocalAuraIdx = LocalAuraIdx::new(9);
164pub(crate) const AURA_VOID_RAY: LocalAuraIdx = LocalAuraIdx::new(10);
165pub(crate) const AURA_VOIDFALL_BUILDING: LocalAuraIdx = LocalAuraIdx::new(11);
166pub(crate) const AURA_VOIDFALL_SPENDING: LocalAuraIdx = LocalAuraIdx::new(12);
167pub(crate) const AURA_VOIDFALL_FINAL_HOUR: LocalAuraIdx = LocalAuraIdx::new(13);
168pub(crate) const AURA_CATASTROPHE: LocalAuraIdx = LocalAuraIdx::new(14);
169pub(crate) const AURA_COUNT: usize = 15;
170const _: () = assert!(
171    AURA_COUNT == 15, "AURA index ABI: count changed -- update positional hook consumers"
172);
173pub(crate) const SPELL_CONSUME: LocalSpellIdx = LocalSpellIdx::new(0);
174pub(crate) const SPELL_DEVOUR: LocalSpellIdx = LocalSpellIdx::new(1);
175pub(crate) const SPELL_VOIDBLADE: LocalSpellIdx = LocalSpellIdx::new(2);
176pub(crate) const SPELL_VOID_RAY: LocalSpellIdx = LocalSpellIdx::new(3);
177pub(crate) const SPELL_VOID_RAY_TICK: LocalSpellIdx = LocalSpellIdx::new(4);
178pub(crate) const SPELL_VOID_RAY_TICK_META: LocalSpellIdx = LocalSpellIdx::new(5);
179pub(crate) const SPELL_REAP: LocalSpellIdx = LocalSpellIdx::new(6);
180pub(crate) const SPELL_VOID_METAMORPHOSIS: LocalSpellIdx = LocalSpellIdx::new(7);
181pub(crate) const SPELL_IMMOLATION_AURA: LocalSpellIdx = LocalSpellIdx::new(8);
182pub(crate) const SPELL_THE_HUNT: LocalSpellIdx = LocalSpellIdx::new(9);
183pub(crate) const SPELL_CULL: LocalSpellIdx = LocalSpellIdx::new(10);
184pub(crate) const SPELL_ERADICATE: LocalSpellIdx = LocalSpellIdx::new(11);
185pub(crate) const SPELL_COLLAPSING_STAR_SPELL: LocalSpellIdx = LocalSpellIdx::new(12);
186pub(crate) const SPELL_SOUL_IMMOLATION_SPELL: LocalSpellIdx = LocalSpellIdx::new(13);
187pub(crate) const SPELL_HUNGERING_SLASH: LocalSpellIdx = LocalSpellIdx::new(14);
188pub(crate) const SPELL_PREDATORS_WAKE: LocalSpellIdx = LocalSpellIdx::new(15);
189pub(crate) const SPELL_PIERCE_THE_VEIL: LocalSpellIdx = LocalSpellIdx::new(16);
190pub(crate) const SPELL_STUN: LocalSpellIdx = LocalSpellIdx::new(17);
191pub(crate) const SPELL_COUNT: usize = 18;
192const _: () = assert!(
193    SPELL_COUNT == 18,
194    "SPELL index ABI: count changed -- update positional hook consumers"
195);
196pub(crate) mod EFFECT {
197    ///<https://wowlab.gg/inspect/spell/1261684>
198    pub(crate) const ENTROPY_STARTING_SOULS: (u32, u8) = (1_261_684, 2);
199    ///<https://wowlab.gg/inspect/spell/1238855>
200    pub(crate) const MONSTER_WITHIN_DIRECT: (u32, u8) = (1_238_855, 1);
201    ///<https://wowlab.gg/inspect/spell/1238855>
202    pub(crate) const MONSTER_WITHIN_PERIODIC: (u32, u8) = (1_238_855, 2);
203    ///<https://wowlab.gg/inspect/spell/1238495>
204    pub(crate) const MOMENT_OF_CRAVING_EXTRA_SOULS: (u32, u8) = (1_238_495, 1);
205    ///<https://wowlab.gg/inspect/spell/1225823>
206    pub(crate) const REAP_DAMAGE: (u32, u8) = (1_225_823, 1);
207    ///<https://wowlab.gg/inspect/spell/1227619>
208    pub(crate) const REAP_SOUL_LIMIT: (u32, u8) = (1_227_619, 2);
209    ///<https://wowlab.gg/inspect/spell/1227619>
210    pub(crate) const SHATTERED_SOULS_CHANCE: (u32, u8) = (1_227_619, 1);
211    ///<https://wowlab.gg/inspect/spell/1238739>
212    pub(crate) const SOULSHAPER_PER_FRAGMENT: (u32, u8) = (1_238_739, 1);
213    ///<https://wowlab.gg/inspect/spell/1266330>
214    pub(crate) const SOUL_SPLITTER_CHANCE: (u32, u8) = (1_266_330, 1);
215    ///<https://wowlab.gg/inspect/spell/1253769>
216    pub(crate) const CATASTROPHE_PCT: (u32, u8) = (1_253_769, 1);
217    ///<https://wowlab.gg/inspect/spell/1279247>
218    pub(crate) const HARNESS_THE_COSMOS_PCT: (u32, u8) = (1_279_247, 1);
219    ///<https://wowlab.gg/inspect/spell/1256295>
220    pub(crate) const MASS_ACCELERATION_STACKS: (u32, u8) = (1_256_295, 1);
221    ///<https://wowlab.gg/inspect/spell/1253817>
222    pub(crate) const OTHERWORLDLY_FOCUS_PCT: (u32, u8) = (1_253_817, 1);
223    ///<https://wowlab.gg/inspect/spell/1253304>
224    pub(crate) const VOIDFALL_CHANCE: (u32, u8) = (1_253_304, 3);
225    ///<https://wowlab.gg/inspect/spell/1256305>
226    pub(crate) const VOIDFALL_METEOR_COEF: (u32, u8) = (1_256_305, 1);
227    ///<https://wowlab.gg/inspect/spell/1253304>
228    pub(crate) const VOIDFALL_STACKS: (u32, u8) = (1_253_304, 1);
229    ///<https://wowlab.gg/inspect/spell/1256619>
230    pub(crate) const WORLD_KILLER_COEF: (u32, u8) = (1_256_619, 1);
231    ///<https://wowlab.gg/inspect/spell/1256353>
232    pub(crate) const WORLD_KILLER_SOULS: (u32, u8) = (1_256_353, 4);
233}
234static REPORTED_SPELLS: &[(&str, u32)] = &[
235    ("reap_damage", 1_225_823),
236    ("voidblade_damage", 1_245_414),
237];
238static DECLARED_SPELL_IDS: &[u32] = &[
239    473_662, 1_217_610, 1_245_412, 473_728, 1_213_649, 1_214_595, 1_226_019, 1_217_607,
240    258_920, 370_965, 1_245_453, 1_225_826, 1_221_150, 1_241_937, 1_239_123, 1_246_160,
241    1_246_158, 179_057, 1_217_607, 1_225_789, 1_221_171, 1_238_495, 1_241_937, 1_246_556,
242    1_261_684, 1_245_577, 1_232_310, 1_240_203, 473_728, 1_256_301, 1_256_302, 1_256_322,
243    1_256_676, 1_213_649, 1_225_823, 1_227_619, 1_238_495, 1_238_739, 1_238_855,
244    1_240_203, 1_245_414, 1_253_304, 1_253_769, 1_253_817, 1_256_295, 1_256_305,
245    1_256_353, 1_256_619, 1_256_676, 1_261_684, 1_266_330, 1_279_247, 1_225_823,
246    1_245_414, 1_253_769, 1_279_247, 1_256_295, 1_253_391, 1_253_377, 1_253_817,
247    1_253_304, 1_256_305, 1_256_353, 1_256_619, 1_256_301, 1_256_322, 1_256_302,
248];
249static DECLARED_AURA_IDS: &[u32] = &[
250    1_217_607, 1_225_789, 1_221_171, 1_238_495, 1_241_937, 1_246_556, 1_261_684,
251    1_245_577, 1_232_310, 1_240_203, 473_728, 1_256_301, 1_256_302, 1_256_322, 1_256_676,
252    1_256_301, 1_256_322, 1_256_302,
253];
254static TALENTS: &[(&str, u32)] = &[
255    ("COLLAPSING_STAR", TALENT::COLLAPSING_STAR),
256    ("FEAST_OF_SOULS", TALENT::FEAST_OF_SOULS),
257    ("SOULSHAPER", TALENT::SOULSHAPER),
258    ("THE_HUNT", TALENT::THE_HUNT),
259    ("VOID_RAY", TALENT::VOID_RAY),
260];
261fn aura_void_metamorphosis(
262    a: AuraDefinitionDraft,
263    data: &ResolvedGameData,
264) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
265    a.on_player()
266        .apply_base_from_data(data, 1_217_607)?
267        .max_stacks(
268            data
269                .require_aura_max_stacks(
270                    wowlab_types::sim::SpellIdx::from_raw(1_217_607),
271                )?,
272        )
273        .periodic_resource_drain(
274            wowlab_engine_combat::validated_milliseconds(
275                2000.0,
276                1_217_607,
277                "periodic_resource_drain.tick_ms",
278            )?,
279            30.0,
280        )
281        .apply_periodic_behavior_from_data(data, 1_217_607)
282}
283fn aura_void_metamorphosis_stack(
284    a: AuraDefinitionDraft,
285    data: &ResolvedGameData,
286) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
287    Ok(
288        a
289            .on_player()
290            .apply_base_from_data(data, 1_225_789)?
291            .max_stacks(
292                data
293                    .require_aura_max_stacks(
294                        wowlab_types::sim::SpellIdx::from_raw(1_225_789),
295                    )?,
296            ),
297    )
298}
299fn aura_collapsing_star(
300    a: AuraDefinitionDraft,
301    data: &ResolvedGameData,
302) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
303    Ok(
304        a
305            .on_player()
306            .apply_base_from_data(data, 1_221_171)?
307            .max_stacks(
308                data
309                    .require_aura_max_stacks(
310                        wowlab_types::sim::SpellIdx::from_raw(1_221_171),
311                    )?,
312            ),
313    )
314}
315fn aura_moment_of_craving(
316    a: AuraDefinitionDraft,
317    data: &ResolvedGameData,
318) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
319    Ok(
320        a
321            .on_player()
322            .apply_base_from_data(data, 1_238_495)?
323            .max_stacks(
324                data
325                    .require_aura_max_stacks(
326                        wowlab_types::sim::SpellIdx::from_raw(1_238_495),
327                    )?,
328            ),
329    )
330}
331fn aura_soul_immolation(
332    a: AuraDefinitionDraft,
333    data: &ResolvedGameData,
334) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
335    a.on_player()
336        .apply_base_from_data(data, 1_241_937)?
337        .max_stacks(
338            data
339                .require_aura_max_stacks(
340                    wowlab_types::sim::SpellIdx::from_raw(1_241_937),
341                )?,
342        )
343        .periodic_damage(
344            wowlab_engine_combat::validated_milliseconds(
345                1000.0,
346                1_241_937,
347                "periodic_damage.tick_ms",
348            )?,
349            0.0,
350            wowlab_engine_combat::DamageFlags::empty(),
351        )
352        .apply_periodic_behavior_from_data(data, 1_241_937)
353}
354fn aura_spontaneous_immolation(
355    a: AuraDefinitionDraft,
356    data: &ResolvedGameData,
357) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
358    Ok(
359        a
360            .on_player()
361            .apply_base_from_data(data, 1_246_556)?
362            .max_stacks(
363                data
364                    .require_aura_max_stacks(
365                        wowlab_types::sim::SpellIdx::from_raw(1_246_556),
366                    )?,
367            ),
368    )
369}
370fn aura_entropy_ticker(
371    a: AuraDefinitionDraft,
372    data: &ResolvedGameData,
373) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
374    a.on_player()
375        .apply_base_from_data(data, 1_261_684)?
376        .max_stacks(
377            data
378                .require_aura_max_stacks(
379                    wowlab_types::sim::SpellIdx::from_raw(1_261_684),
380                )?,
381        )
382        .periodic_apply_aura(
383            wowlab_engine_combat::validated_milliseconds(
384                data.period(wowlab_types::sim::SpellIdx::from_raw(1_261_684), 1),
385                1_261_684,
386                "periodic_apply_aura.tick_ms",
387            )?,
388            1,
389        )
390        .apply_periodic_behavior_from_data(data, 1_261_684)
391}
392fn aura_soul_fragments(
393    a: AuraDefinitionDraft,
394    data: &ResolvedGameData,
395) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
396    Ok(
397        a
398            .on_player()
399            .apply_base_from_data(data, 1_245_577)?
400            .max_stacks(
401                data
402                    .require_aura_max_stacks(
403                        wowlab_types::sim::SpellIdx::from_raw(1_245_577),
404                    )?,
405            ),
406    )
407}
408fn aura_feast_of_souls(
409    a: AuraDefinitionDraft,
410    data: &ResolvedGameData,
411) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
412    Ok(
413        a
414            .on_player()
415            .apply_base_from_data(data, 1_232_310)?
416            .max_stacks(
417                data
418                    .require_aura_max_stacks(
419                        wowlab_types::sim::SpellIdx::from_raw(1_232_310),
420                    )?,
421            )
422            .async_stacks(),
423    )
424}
425fn aura_focused_ray(
426    a: AuraDefinitionDraft,
427    data: &ResolvedGameData,
428) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
429    a.on_player()
430        .apply_base_from_data(data, 1_240_203)?
431        .max_stacks(
432            data
433                .require_aura_max_stacks(
434                    wowlab_types::sim::SpellIdx::from_raw(1_240_203),
435                )?,
436        )
437        .damage_percent_spells(
438            data.base_points(wowlab_types::sim::SpellIdx::from_raw(1_240_203), 1),
439            &[473_728],
440        )
441}
442fn aura_void_ray(
443    a: AuraDefinitionDraft,
444    data: &ResolvedGameData,
445) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
446    Ok(
447        a
448            .on_player()
449            .apply_base_from_data(data, 473_728)?
450            .max_stacks(
451                data
452                    .require_aura_max_stacks(
453                        wowlab_types::sim::SpellIdx::from_raw(473_728),
454                    )?,
455            ),
456    )
457}
458fn aura_voidfall_building(
459    a: AuraDefinitionDraft,
460    data: &ResolvedGameData,
461) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
462    Ok(
463        a
464            .on_player()
465            .apply_base_from_data(data, 1_256_301)?
466            .max_stacks(
467                data
468                    .require_aura_max_stacks(
469                        wowlab_types::sim::SpellIdx::from_raw(1_256_301),
470                    )?,
471            ),
472    )
473}
474fn aura_voidfall_spending(
475    a: AuraDefinitionDraft,
476    data: &ResolvedGameData,
477) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
478    Ok(
479        a
480            .on_player()
481            .apply_base_from_data(data, 1_256_302)?
482            .max_stacks(
483                data
484                    .require_aura_max_stacks(
485                        wowlab_types::sim::SpellIdx::from_raw(1_256_302),
486                    )?,
487            ),
488    )
489}
490fn aura_voidfall_final_hour(
491    a: AuraDefinitionDraft,
492    data: &ResolvedGameData,
493) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
494    Ok(
495        a
496            .on_player()
497            .apply_base_from_data(data, 1_256_322)?
498            .max_stacks(
499                data
500                    .require_aura_max_stacks(
501                        wowlab_types::sim::SpellIdx::from_raw(1_256_322),
502                    )?,
503            ),
504    )
505}
506fn aura_catastrophe(
507    a: AuraDefinitionDraft,
508    data: &ResolvedGameData,
509) -> Result<AuraDefinitionDraft, wowlab_engine_combat::BuilderError> {
510    a.on_target()
511        .apply_base_from_data(data, 1_256_676)?
512        .max_stacks(
513            data
514                .require_aura_max_stacks(
515                    wowlab_types::sim::SpellIdx::from_raw(1_256_676),
516                )?,
517        )
518        .periodic_residual_damage(
519            wowlab_engine_combat::validated_milliseconds(
520                data.period(wowlab_types::sim::SpellIdx::from_raw(1_256_676), 1),
521                1_256_676,
522                "periodic_residual_damage.tick_ms",
523            )?,
524        )
525        .apply_periodic_behavior_from_data(data, 1_256_676)
526}
527fn spell_consume(
528    s: SpellDefinitionDraft,
529    data: &ResolvedGameData,
530) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
531    Ok(
532        s
533            .apply_base_from_data(data, 473_662)?
534            .gain(8.0)
535            .apply_aoe_from_data(data)
536            .on_cast(crate::hooks::devourer_demon_hunter::consume_hook),
537    )
538}
539fn spell_devour(
540    s: SpellDefinitionDraft,
541    data: &ResolvedGameData,
542) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
543    Ok(
544        s
545            .apply_base_from_data(data, 1_217_610)?
546            .gain(40.0)
547            .apply_aoe_from_data(data)
548            .on_cast(crate::hooks::devourer_demon_hunter::consume_hook),
549    )
550}
551fn spell_voidblade(
552    s: SpellDefinitionDraft,
553    data: &ResolvedGameData,
554) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
555    Ok(
556        s
557            .apply_base_from_data(data, 1_245_412)?
558            .gain(20.0)
559            .damage_sp_from_data(
560                wowlab_engine_domain::dbc::EffectLookup::new(
561                    data,
562                    wowlab_types::sim::EffectRef::new(
563                        wowlab_types::sim::SpellIdx::from_raw(1_245_414),
564                        2,
565                    ),
566                ),
567            )?
568            .apply_aoe_from_data(data),
569    )
570}
571fn spell_void_ray(
572    s: SpellDefinitionDraft,
573    data: &ResolvedGameData,
574) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
575    Ok(
576        s
577            .apply_base_from_data(data, 473_728)?
578            .applies_inferred_aura(AURA_VOID_RAY, 473_728)
579            .apply_aoe_from_data(data)
580            .channel(
581                wowlab_engine_combat::validated_channel_tick_count(
582                    20.0,
583                    473_728,
584                    "channel.tick_count",
585                )?,
586            )
587            .channel_tick_direct_damage()
588            .channel_tick_damage_sp_from_data(
589                wowlab_engine_domain::dbc::EffectLookup::new(
590                    data,
591                    wowlab_types::sim::EffectRef::new(
592                        wowlab_types::sim::SpellIdx::from_raw(1_213_649),
593                        1,
594                    ),
595                ),
596            )?
597            .channel_tick_damage_alt_sp_from_data(
598                wowlab_engine_domain::dbc::EffectLookup::new(
599                    data,
600                    wowlab_types::sim::EffectRef::new(
601                        wowlab_types::sim::SpellIdx::from_raw(1_214_595),
602                        1,
603                    ),
604                ),
605                AURA::VOID_METAMORPHOSIS,
606            )?
607            .channel_tick_cost(
608                data.require_cost(wowlab_types::sim::SpellIdx::from_raw(1_213_649))?,
609            )
610            .channel_tick_cost_alt(
611                data.require_cost(wowlab_types::sim::SpellIdx::from_raw(1_214_595))?,
612                AURA::VOID_METAMORPHOSIS,
613            )
614            .on_cast(crate::hooks::devourer_demon_hunter::void_ray_hook)
615            .cost_free_when_aura(1_217_607),
616    )
617}
618fn spell_void_ray_tick(
619    s: SpellDefinitionDraft,
620    data: &ResolvedGameData,
621) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
622    Ok(s.apply_base_from_data(data, 1_213_649)?.apply_aoe_from_data(data))
623}
624fn spell_void_ray_tick_meta(
625    s: SpellDefinitionDraft,
626    data: &ResolvedGameData,
627) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
628    Ok(s.apply_base_from_data(data, 1_214_595)?.apply_aoe_from_data(data))
629}
630fn spell_reap(
631    s: SpellDefinitionDraft,
632    data: &ResolvedGameData,
633) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
634    Ok(
635        s
636            .apply_base_from_data(data, 1_226_019)?
637            .apply_aoe_from_data(data)
638            .on_cast(crate::hooks::devourer_demon_hunter::reap_hook),
639    )
640}
641fn spell_void_metamorphosis(
642    s: SpellDefinitionDraft,
643    data: &ResolvedGameData,
644) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
645    Ok(
646        s
647            .apply_base_from_data(data, 1_217_607)?
648            .gain(200.0)
649            .applies_inferred_aura(AURA_VOID_METAMORPHOSIS, 1_217_607)
650            .apply_aoe_from_data(data)
651            .on_cast(crate::hooks::devourer_demon_hunter::void_metamorphosis_hook)
652            .requires_aura_at_min_stacks(1_225_789, 50),
653    )
654}
655fn spell_immolation_aura(
656    s: SpellDefinitionDraft,
657    data: &ResolvedGameData,
658) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
659    Ok(s.apply_base_from_data(data, 258_920)?.apply_aoe_from_data(data))
660}
661fn spell_the_hunt(
662    s: SpellDefinitionDraft,
663    data: &ResolvedGameData,
664) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
665    Ok(s.apply_base_from_data(data, 370_965)?.apply_aoe_from_data(data))
666}
667fn spell_cull(
668    s: SpellDefinitionDraft,
669    data: &ResolvedGameData,
670) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
671    Ok(s.apply_base_from_data(data, 1_245_453)?.apply_aoe_from_data(data))
672}
673fn spell_eradicate(
674    s: SpellDefinitionDraft,
675    data: &ResolvedGameData,
676) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
677    Ok(
678        s
679            .apply_base_from_data(data, 1_225_826)?
680            .apply_aoe_from_data(data)
681            .on_cast(crate::hooks::devourer_demon_hunter::eradicate_hook),
682    )
683}
684fn spell_collapsing_star_spell(
685    s: SpellDefinitionDraft,
686    data: &ResolvedGameData,
687) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
688    Ok(s.apply_base_from_data(data, 1_221_150)?.apply_aoe_from_data(data))
689}
690fn spell_soul_immolation_spell(
691    s: SpellDefinitionDraft,
692    data: &ResolvedGameData,
693) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
694    Ok(
695        s
696            .apply_base_from_data(data, 1_241_937)?
697            .applies_inferred_aura(AURA_SOUL_IMMOLATION, 1_241_937)
698            .apply_aoe_from_data(data),
699    )
700}
701fn spell_hungering_slash(
702    s: SpellDefinitionDraft,
703    data: &ResolvedGameData,
704) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
705    Ok(s.apply_base_from_data(data, 1_239_123)?.apply_aoe_from_data(data))
706}
707fn spell_predators_wake(
708    s: SpellDefinitionDraft,
709    data: &ResolvedGameData,
710) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
711    Ok(s.apply_base_from_data(data, 1_246_160)?.apply_aoe_from_data(data))
712}
713fn spell_pierce_the_veil(
714    s: SpellDefinitionDraft,
715    data: &ResolvedGameData,
716) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
717    Ok(s.apply_base_from_data(data, 1_246_158)?.apply_aoe_from_data(data))
718}
719fn spell_stun(
720    s: SpellDefinitionDraft,
721    data: &ResolvedGameData,
722) -> Result<SpellDefinitionDraft, wowlab_engine_combat::BuilderError> {
723    Ok(s.apply_base_from_data(data, 179_057)?.apply_aoe_from_data(data))
724}
725pub(crate) fn build_combat_system(
726    params: &wowlab_engine_ports::HandlerParams<'_>,
727) -> Result<BuiltCombatSystem, wowlab_engine_ports::EngineError> {
728    let builder = BuiltCombatSystem::builder(params.stats.clone())
729        .spec_id(SpecId::Devourer)
730        .hero_talent_trees(DECLARED_METADATA.hero_talent_trees)
731        .mastery_spell(1_238_855)
732        .mastery_hook(crate::hooks::devourer_demon_hunter::mastery)
733        .resource(
734            "fury",
735            120.0,
736            params
737                .game_data
738                .power_regen_for_max(17, 120.0)
739                .ok_or_else(|| {
740                    wowlab_engine_ports::EngineError::spec_construction(
741                        "missing power-type power_regen_for_max for type_id 17",
742                    )
743                })?,
744        )
745        .resource_type_id(17)
746        .resource_start(
747            params
748                .game_data
749                .power_default(17)
750                .ok_or_else(|| {
751                    wowlab_engine_ports::EngineError::spec_construction(
752                        "missing power-type power_default for type_id 17",
753                    )
754                })?,
755        )
756        .aura(
757            "void_metamorphosis",
758            AURA::VOID_METAMORPHOSIS,
759            |a| aura_void_metamorphosis(a, &params.game_data),
760        )
761        .aura(
762            "void_metamorphosis_stack",
763            AURA::VOID_METAMORPHOSIS_STACK,
764            |a| aura_void_metamorphosis_stack(a, &params.game_data),
765        )
766        .aura(
767            "collapsing_star",
768            AURA::COLLAPSING_STAR,
769            |a| aura_collapsing_star(a, &params.game_data),
770        )
771        .aura(
772            "moment_of_craving",
773            AURA::MOMENT_OF_CRAVING,
774            |a| aura_moment_of_craving(a, &params.game_data),
775        )
776        .aura(
777            "soul_immolation",
778            AURA::SOUL_IMMOLATION,
779            |a| aura_soul_immolation(a, &params.game_data),
780        )
781        .aura(
782            "spontaneous_immolation",
783            AURA::SPONTANEOUS_IMMOLATION,
784            |a| aura_spontaneous_immolation(a, &params.game_data),
785        )
786        .aura(
787            "entropy_ticker",
788            AURA::ENTROPY_TICKER,
789            |a| aura_entropy_ticker(a, &params.game_data),
790        )
791        .aura(
792            "soul_fragments",
793            AURA::SOUL_FRAGMENTS,
794            |a| aura_soul_fragments(a, &params.game_data),
795        )
796        .aura(
797            "feast_of_souls",
798            AURA::FEAST_OF_SOULS,
799            |a| aura_feast_of_souls(a, &params.game_data),
800        )
801        .aura(
802            "focused_ray",
803            AURA::FOCUSED_RAY,
804            |a| aura_focused_ray(a, &params.game_data),
805        )
806        .aura("void_ray", AURA::VOID_RAY, |a| aura_void_ray(a, &params.game_data))
807        .aura(
808            "voidfall_building",
809            AURA::VOIDFALL_BUILDING,
810            |a| aura_voidfall_building(a, &params.game_data),
811        )
812        .aura(
813            "voidfall_spending",
814            AURA::VOIDFALL_SPENDING,
815            |a| aura_voidfall_spending(a, &params.game_data),
816        )
817        .aura(
818            "voidfall_final_hour",
819            AURA::VOIDFALL_FINAL_HOUR,
820            |a| aura_voidfall_final_hour(a, &params.game_data),
821        )
822        .aura(
823            "catastrophe",
824            AURA::CATASTROPHE,
825            |a| aura_catastrophe(a, &params.game_data),
826        )
827        .spell("consume", SPELL::CONSUME, |s| spell_consume(s, &params.game_data))
828        .spell("devour", SPELL::DEVOUR, |s| spell_devour(s, &params.game_data))
829        .spell("voidblade", SPELL::VOIDBLADE, |s| spell_voidblade(s, &params.game_data))
830        .spell("void_ray", SPELL::VOID_RAY, |s| spell_void_ray(s, &params.game_data))
831        .spell(
832            "void_ray_tick",
833            SPELL::VOID_RAY_TICK,
834            |s| spell_void_ray_tick(s, &params.game_data),
835        )
836        .spell(
837            "void_ray_tick_meta",
838            SPELL::VOID_RAY_TICK_META,
839            |s| spell_void_ray_tick_meta(s, &params.game_data),
840        )
841        .spell("reap", SPELL::REAP, |s| spell_reap(s, &params.game_data))
842        .spell(
843            "void_metamorphosis",
844            SPELL::VOID_METAMORPHOSIS,
845            |s| spell_void_metamorphosis(s, &params.game_data),
846        )
847        .spell(
848            "immolation_aura",
849            SPELL::IMMOLATION_AURA,
850            |s| spell_immolation_aura(s, &params.game_data),
851        )
852        .spell("the_hunt", SPELL::THE_HUNT, |s| spell_the_hunt(s, &params.game_data))
853        .spell("cull", SPELL::CULL, |s| spell_cull(s, &params.game_data))
854        .spell("eradicate", SPELL::ERADICATE, |s| spell_eradicate(s, &params.game_data))
855        .spell(
856            "collapsing_star_spell",
857            SPELL::COLLAPSING_STAR_SPELL,
858            |s| spell_collapsing_star_spell(s, &params.game_data),
859        )
860        .spell(
861            "soul_immolation_spell",
862            SPELL::SOUL_IMMOLATION_SPELL,
863            |s| spell_soul_immolation_spell(s, &params.game_data),
864        )
865        .spell(
866            "hungering_slash",
867            SPELL::HUNGERING_SLASH,
868            |s| spell_hungering_slash(s, &params.game_data),
869        )
870        .spell(
871            "predators_wake",
872            SPELL::PREDATORS_WAKE,
873            |s| spell_predators_wake(s, &params.game_data),
874        )
875        .spell(
876            "pierce_the_veil",
877            SPELL::PIERCE_THE_VEIL,
878            |s| spell_pierce_the_veil(s, &params.game_data),
879        )
880        .spell("stun", SPELL::STUN, |s| spell_stun(s, &params.game_data))
881        .game_data(params.game_data.clone())
882        .talent_spell_ids(TALENTS)
883        .talent_selections(params.talent_selections)
884        .set_bonus_auras(params.set_bonus_auras)
885        .encounter(params.encounter.clone());
886    let builder = crate::generated::items::register_equipped(
887        builder,
888        &params.game_data,
889        params.equipped_items,
890    )?;
891
892    builder
893        .build(params.rotation)
894        .map_err(wowlab_engine_ports::EngineError::rotation_compile)
895}
896fn new_handler(
897    params: wowlab_engine_ports::HandlerParams<'_>,
898) -> Result<Box<dyn SpecHandler>, wowlab_engine_ports::EngineError> {
899    let handler = crate::composition::compose_handler(
900        build_combat_system,
901        &params,
902        crate::hooks::devourer_demon_hunter::DevourerHandler::try_new,
903    )?;
904
905    Ok(Box::new(handler))
906}
907static DECLARED_METADATA: DeclaredSpecMetadata = DeclaredSpecMetadata {
908    primary_resource: ResourceType::Fury,
909    secondary_resource: None,
910    spell_ids: DECLARED_SPELL_IDS,
911    aura_ids: DECLARED_AURA_IDS,
912    server_proc_entries: &[
913        DeclaredServerProcEntry {
914            spell_id: 1_238_495,
915            attributes: ProcAttributes::TRIGGERED_CAN_PROC.bits()
916                | ProcAttributes::USE_STACKS_FOR_CHARGES.bits(),
917        },
918    ],
919    talents: TALENTS,
920    rotation_field_schema: &[],
921    metrics_plugin_id: None,
922    hero_talent_trees: &[
923        HeroTalentTreeDesc {
924            name: "Annihilator",
925            spells: HERO_TREE_ANNIHILATOR_SPELLS,
926            auras: HERO_TREE_ANNIHILATOR_AURAS,
927        },
928    ],
929    reported_spells: REPORTED_SPELLS,
930    masked_passive_effects: &[],
931    passive_effect_overrides: &[],
932};
933pub(super) const SPEC_DESCRIPTOR: SpecDescriptor = SpecDescriptor {
934    spec_id: SpecId::Devourer,
935    display_name: "Devourer Demon Hunter",
936    metadata: DECLARED_METADATA,
937    handler_factory: new_handler,
938};
939wowlab_engine_combat::smoke_test!(build_combat_system);