wowlab_engine_content/hooks/devourer_demon_hunter/
config.rs1use wowlab_engine_combat::AuraOps as _;
2use wowlab_engine_combat::{HookCtx, ImpactEvent, ImpactProc};
3use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
4use wowlab_engine_rng::prd_constant;
5use wowlab_types::constants::HUNDRED;
6
7use crate::{
8 generated::specs::devourer_demon_hunter::{AURA_CATASTROPHE, EFFECT, HERO, TALENT},
9 hooks::{shared::demon_hunter::annihilator_meteor_impact, try_gated},
10};
11
12use super::hooks::{shattered_souls_fire, shattered_souls_spell_filter};
13
14fn catastrophe_impact(ctx: &mut HookCtx<'_>, impact: ImpactEvent) {
15 let fraction = devourer_config(ctx).catastrophe_pct;
16
17 ctx.add_residual_damage(AURA_CATASTROPHE.raw(), impact.amount * fraction);
18}
19
20crate::hooks::define_spec_config! {
21 pub(super) struct DevourerConfig {
23 soul_splitter_chance: f64 {
24 provenance: "Soul Splitter 1266330 e1, converted from percent points.",
25 gate: true,
26 source: params.game_data.effect_base_points(EFFECT::SOUL_SPLITTER_CHANCE),
27 transform: |value: f64| (value / HUNDRED).max(0.0),
28 },
29 shattered_souls_chance: f64 {
30 provenance: "Shattered Souls proc chance converted from percent points.",
31 gate: true,
32 source: params.game_data.effect_base_points(EFFECT::SHATTERED_SOULS_CHANCE),
33 transform: |value: f64| (value / HUNDRED).max(0.0),
34 },
35 reap_soul_limit: i32 {
36 provenance: "Shattered Souls 1227619 e2 base Reap collection limit.",
37 gate: true,
38 source: params.game_data.effect_base_points(EFFECT::REAP_SOUL_LIMIT),
39 transform: |value: f64| wowlab_types::numeric::f64_to_i32_saturating_trunc(value.max(0.0)),
40 },
41 moment_of_craving_extra_souls: i32 {
42 provenance: "Moment of Craving 1238495 e1 additional Reap collection limit.",
43 gate: true,
44 source: params.game_data.effect_base_points(EFFECT::MOMENT_OF_CRAVING_EXTRA_SOULS),
45 transform: |value: f64| wowlab_types::numeric::f64_to_i32_saturating_trunc(value.max(0.0)),
46 },
47 soulshaper_per_fragment: f64 {
48 provenance: "Soulshaper 1238739 e1 Reap damage per collected fragment.",
49 gate: params.talent_picked(TALENT::SOULSHAPER),
50 source: params.game_data.effect_base_points(EFFECT::SOULSHAPER_PER_FRAGMENT),
51 transform: |value: f64| (value / HUNDRED).max(0.0),
52 },
53 feast_of_souls_enabled: bool {
54 provenance: "Feast of Souls 1237270 talent selection gates its consumed-fragment buff.",
55 gate: params.talent_picked(TALENT::FEAST_OF_SOULS),
56 source: 1.0,
57 transform: |value: f64| value > 0.0,
58 },
59 voidfall: bool {
60 provenance: "Voidfall hero talent enables the shared building/spending state machine.",
61 gate: voidfall,
62 source: true,
63 transform: |value| value,
64 },
65 voidfall_chance: f64 {
66 provenance: "Voidfall 1253304 e3 accumulated proc chance.",
67 gate: voidfall,
68 source: params.game_data.require_effect_base_points(EFFECT::VOIDFALL_CHANCE)?,
69 transform: |value: f64| prd_constant(value / HUNDRED),
70 },
71 voidfall_stacks: i32 {
72 provenance: "Voidfall 1253304 e1 building stacks per proc.",
73 gate: voidfall,
74 source: params.game_data.require_effect_base_points(EFFECT::VOIDFALL_STACKS)?,
75 transform: wowlab_types::numeric::f64_to_i32_saturating_trunc,
76 },
77 voidfall_max_stacks: i32 {
78 provenance: "Voidfall building aura 1256301 stack cap.",
79 gate: voidfall,
80 source: params.game_data.require_aura_max_stacks(wowlab_types::sim::SpellIdx::from_raw(HERO::ANNIHILATOR::AURA::VOIDFALL_BUILDING))?,
81 transform: i32::from,
82 },
83 meteoric_fall: bool {
84 provenance: "Meteoric Fall releases the complete spending state together.",
85 gate: meteoric_fall,
86 source: true,
87 transform: |value| value,
88 },
89 world_killer: bool {
90 provenance: "World Killer replaces the final Voidfall meteor.",
91 gate: world_killer,
92 source: true,
93 transform: |value| value,
94 },
95 meteor_coef: f64 {
96 provenance: "Devourer Voidfall Meteor 1256305 e1 SP coefficient with selected damage talents.",
97 gate: voidfall,
98 source: params.game_data.effect_sp_coefficient(EFFECT::VOIDFALL_METEOR_COEF),
99 transform: |value: f64| value * voidfall_mult,
100 },
101 world_killer_coef: f64 {
102 provenance: "Devourer World Killer 1256619 e1 SP coefficient with selected damage talents.",
103 gate: world_killer,
104 source: params.game_data.effect_sp_coefficient(EFFECT::WORLD_KILLER_COEF),
105 transform: |value: f64| value * voidfall_mult,
106 },
107 world_killer_souls: i32 {
108 provenance: "World Killer 1256353 e4 Lesser Soul Fragments for Devourer.",
109 gate: world_killer,
110 source: params.game_data.require_effect_base_points(EFFECT::WORLD_KILLER_SOULS)?,
111 transform: wowlab_types::numeric::f64_to_i32_saturating_trunc,
112 },
113 catastrophe_pct: f64 {
114 provenance: "Catastrophe 1253769 e1 residual fraction of post-mitigation meteor damage.",
115 gate: catastrophe,
116 source: params.game_data.require_effect_base_points(EFFECT::CATASTROPHE_PCT)?,
117 transform: |value: f64| value / HUNDRED,
118 },
119 mass_acceleration_stacks: i32 {
120 provenance: "Mass Acceleration 1256295 e1 Voidfall stacks on Metamorphosis.",
121 gate: mass_acceleration,
122 source: params.game_data.require_effect_base_points(EFFECT::MASS_ACCELERATION_STACKS)?,
123 transform: wowlab_types::numeric::f64_to_i32_saturating_trunc,
124 },
125 meteoric_rise_stacks: i32 {
126 provenance: "Meteoric Rise grants one Voidfall building stack when Void Ray completes.",
127 gate: meteoric_rise,
128 source: 1,
129 transform: |value| value,
130 },
131 }
132 accessor pub(super) fn devourer_config(ctx);
133 register pub(super) fn register_devourer(built, params) -> Result<(), wowlab_engine_ports::EngineError>;
134 prepare {
135 let voidfall = params.talent_picked(HERO::ANNIHILATOR::SPELL::VOIDFALL);
136 let meteoric_fall = voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::METEORIC_FALL);
137 let world_killer = voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::WORLD_KILLER);
138 let catastrophe = voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::CATASTROPHE);
139 let mass_acceleration = voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::MASS_ACCELERATION);
140 let meteoric_rise = voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::METEORIC_RISE);
141 let harness = try_gated(
142 voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::HARNESS_THE_COSMOS),
143 || Ok::<_, wowlab_engine_ports::EngineError>(params.game_data.require_effect_base_points(EFFECT::HARNESS_THE_COSMOS_PCT)? / HUNDRED),
144 )?;
145 let otherworldly = try_gated(
146 voidfall && params.talent_picked(HERO::ANNIHILATOR::SPELL::OTHERWORLDLY_FOCUS),
147 || Ok::<_, wowlab_engine_ports::EngineError>(params.game_data.require_effect_base_points(EFFECT::OTHERWORLDLY_FOCUS_PCT)? / HUNDRED),
148 )?;
149 let voidfall_mult = (1.0 + harness) * (1.0 + otherworldly);
150 }
151 auras {}
152 wiring {
153 { provenance: "Shattered Souls impact proc registration.", gate: cfg.shattered_souls_chance > 0.0, apply: {
154 built.register_impact_proc(
155 ImpactProc::new(shattered_souls_fire)
156 .with_chance(cfg.shattered_souls_chance)
157 .with_spell_filter(shattered_souls_spell_filter),
158 );
159 } },
160 { provenance: "Catastrophe receives post-mitigation Annihilator meteor impacts.", gate: catastrophe, apply: {
161 built.register_impact_proc(
162 ImpactProc::new(catastrophe_impact)
163 .with_spell_filter(annihilator_meteor_impact)
164 .skip_periodic(),
165 );
166 } },
167 }
168 finish |cfg| {
169 built.state.set_spec_config(cfg);
170 Ok(())
171 }
172}