Skip to main content

wowlab_engine_content/hooks/enhancement_shaman/
config.rs

1use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
2use wowlab_types::{
3    constants::{HUNDRED, THOUSAND},
4    sim::SpellIdx,
5};
6
7use crate::generated::specs::enhancement_shaman::{EFFECT, HERO, SET_BONUS, SPELL, TALENT};
8
9const ELEMENTAL_WEAPONS_TENTHS_DIVISOR: f64 = 10.0;
10const MID2_CRASH_SNAPSHOT_CAPACITY: usize = 16;
11pub(super) const WINDFURY_BASE_HITS: u32 = 2;
12#[derive(Clone, Copy, Debug, Default)]
13pub(super) struct EnhancementRuntime {
14    pub(super) imbuement_mastery_failures: u32,
15    pub(super) lively_totems_failures: u32,
16    pub(super) primordial_storm_mw_stacks: i32,
17    pub(super) mid2_crash_snapshots: [f64; MID2_CRASH_SNAPSHOT_CAPACITY],
18    pub(super) mid2_crash_snapshot_head: usize,
19    pub(super) mid2_crash_snapshot_count: usize,
20    pub(super) mid2_storm_unleashed_snapshot: f64,
21}
22
23crate::hooks::define_spec_config! {
24    pub(super) struct EnhancementConfig {
25        splitstream_mult: f64 { provenance: "Splitstream talent e1 damage fraction.", gate: params.talent_picked(TALENT::SPLITSTREAM), source: params.game_data.effect_base_points(EFFECT::SPLITSTREAM_DAMAGE), transform: |value| value / HUNDRED },
26        feral_spirit: bool { provenance: "Feral Spirit enables guardian summons.", gate: params.talent_picked(TALENT::FERAL_SPIRIT), source: true, transform: |value| value },
27        imbuement_mastery: bool { provenance: "Imbuement Mastery enables its weapon-imbue riders.", gate: params.talent_picked(TALENT::IMBUEMENT_MASTERY), source: true, transform: |value| value },
28        lively_totems: bool { provenance: "Lively Totems enables its accumulated trigger.", gate: params.talent_picked(TALENT::LIVELY_TOTEMS), source: true, transform: |value| value },
29        elemental_weapons_per_imbue: f64 { provenance: "Elemental Weapons e1 bonus per imbue in tenths of a percent.", gate: params.talent_picked(TALENT::ELEMENTAL_WEAPONS), source: params.game_data.effect_base_points(EFFECT::ELEMENTAL_WEAPONS_BONUS), transform: |value| value / (HUNDRED * ELEMENTAL_WEAPONS_TENTHS_DIVISOR) },
30        windfury_hits: u32 { provenance: "Windfury base hits plus one with Unruly Winds.", gate: true, source: WINDFURY_BASE_HITS + u32::from(params.talent_picked(TALENT::UNRULY_WINDS)), transform: |value| value },
31        stormblast_mult: f64 { provenance: "Stormblast effect damage fraction.", gate: params.talent_picked(TALENT::STORMBLAST), source: params.game_data.effect_base_points(EFFECT::STORMBLAST_BONUS), transform: |value| value / HUNDRED },
32        elemental_tempo_cdr_per_stack_ms: u32 { provenance: "Elemental Tempo effect cooldown reduction per stack.", gate: params.talent_picked(TALENT::ELEMENTAL_TEMPO), source: params.game_data.effect_base_points(EFFECT::ELEMENTAL_TEMPO_CDR), transform: wowlab_types::numeric::f64_to_u32_saturating_trunc },
33        totemic_momentum_hot_hand_ms: u32 { provenance: "Totemic Momentum effect Hot Hand extension milliseconds.", gate: params.talent_picked(TALENT::TOTEMIC_MOMENTUM), source: params.game_data.effect_base_points(EFFECT::TOTEMIC_MOMENTUM_BONUS), transform: wowlab_types::numeric::f64_to_u32_saturating_trunc },
34        surging_totem_duration_ms: u32 { provenance: "Surging Totem's summon-spell duration owns the guardian lifetime.", gate: params.talent_selected(HERO::TOTEMIC::SPELL::SURGING_TOTEM), source: params.game_data.require_aura_duration_ms(SpellIdx::from_raw(SPELL::SURGING_TOTEM))?, transform: |value| value },
35        surging_totem_npc_id: u32 { provenance: "Surging Totem's summon effect provides the guardian creature identity.", gate: params.talent_selected(HERO::TOTEMIC::SPELL::SURGING_TOTEM), source: params.game_data.effect_misc_value_0(SpellIdx::from_raw(SPELL::SURGING_TOTEM), 1), transform: wowlab_types::numeric::i32_to_u32_nonnegative },
36        surging_totem_ap_inheritance: f64 { provenance: "Surging Totem's creature attack-power inheritance is frozen during composition.", gate: params.talent_selected(HERO::TOTEMIC::SPELL::SURGING_TOTEM), source: params.game_data.pet_attack_power_inheritance_mult(wowlab_types::numeric::i32_to_u32_nonnegative(params.game_data.effect_misc_value_0(SpellIdx::from_raw(SPELL::SURGING_TOTEM), 1))), transform: |value| value },
37        surging_totem_sp_inheritance: f64 { provenance: "Surging Totem's creature spell-power inheritance is frozen during composition.", gate: params.talent_selected(HERO::TOTEMIC::SPELL::SURGING_TOTEM), source: params.game_data.pet_spell_power_inheritance_mult(wowlab_types::numeric::i32_to_u32_nonnegative(params.game_data.effect_misc_value_0(SpellIdx::from_raw(SPELL::SURGING_TOTEM), 1))), transform: |value| value },
38        storm_unleashed_repeats: bool { provenance: "Storm Unleashed repeat binding enables repeats.", gate: params.talent_picked(TALENT::STORM_UNLEASHED_REPEATS), source: true, transform: |value| value },
39        storm_unleashed_chance_per_stack: f64 { provenance: "Storm Unleashed effect chance per stack.", gate: params.talent_picked(TALENT::STORM_UNLEASHED), source: params.game_data.effect_base_points(EFFECT::STORM_UNLEASHED_CHANCE), transform: |value| value / THOUSAND },
40        ashen_catalyst_cdr_ms: u32 { provenance: "Ashen Catalyst effect cooldown reduction milliseconds.", gate: params.talent_picked(TALENT::ASHEN_CATALYST), source: params.game_data.effect_base_points(EFFECT::ASHEN_CATALYST_CDR), transform: wowlab_types::numeric::f64_to_u32_saturating_trunc },
41        voltaic_blaze_maelstrom_stacks: i32 { provenance: "Voltaic Blaze 470057 e2 resolved Maelstrom Weapon stacks.", gate: params.talent_picked(TALENT::VOLTAIC_BLAZE), source: params.game_data.effect_base_points(EFFECT::VOLTAIC_BLAZE_MAELSTROM_STACKS), transform: wowlab_types::numeric::f64_to_i32_saturating_trunc },
42        mid2_2pc: bool { provenance: "MID2 Enhancement two-piece applies Burning Core from Voltaic Blaze.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_ENHANCEMENT_2PC), source: true, transform: |value| value },
43        mid2_fire_nova_primary_mult: f64 { provenance: "MID2 Enhancement two-piece Fire Nova bonus against Voltaic Blaze's primary target.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_ENHANCEMENT_2PC), source: params.game_data.effect_base_points(EFFECT::MID2_FIRE_NOVA_PRIMARY_BONUS), transform: |value| 1.0 + value / HUNDRED },
44        mid2_fire_nova_crash_cdr_ms: u32 { provenance: "MID2 Enhancement four-piece Crash Lightning cooldown reduction per Fire Nova.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_ENHANCEMENT_4PC), source: params.game_data.effect_base_points(EFFECT::MID2_FIRE_NOVA_CRASH_CDR_MS), transform: wowlab_types::numeric::f64_to_u32_saturating_trunc },
45        mid2_short_circuit_per_stack: f64 { provenance: "Short Circuit 1299991 e1 damage fraction snapshotted into Crash Lightning proc windows.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_ENHANCEMENT_4PC), source: params.game_data.effect_base_points(EFFECT::MID2_SHORT_CIRCUIT_DAMAGE), transform: |value| value / HUNDRED },
46    }
47    accessor pub(super) fn enhancement_config(ctx);
48    register pub(super) fn register_enhancement(built, params) -> Result<(), wowlab_engine_ports::EngineError>;
49    prepare {}
50    auras {}
51    wiring {
52        { provenance: "Totemic Rebound uses its DBC RPPM driver and proc-category recovery.", gate: params.talent_picked(EFFECT::TOTEMIC_REBOUND_RPPM_DRIVER.0), apply: {
53            built.bind_system_rppm(wowlab_engine_combat::generated_identities::system_rppm::TOTEMIC_REBOUND, EFFECT::TOTEMIC_REBOUND_RPPM_DRIVER.0)?;
54        } },
55    }
56    finish |cfg| {
57        built.register_weapon_imbue(SPELL::WINDFURY_WEAPON, "windfury_weapon", true);
58        built.register_weapon_imbue(SPELL::FLAMETONGUE_WEAPON, "flametongue_weapon", true);
59        built.set_spec_config(cfg);
60        built.set_spec_runtime(EnhancementRuntime::default());
61        Ok(())
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use googletest::prelude::*;
68    use wowlab_engine_gamedata::ResolvedGameData;
69    use wowlab_engine_ports::{CombatStats, ConsumableFlags, TalentSelection};
70    use wowlab_types::game::RaceId;
71
72    use super::*;
73
74    const UNUSUAL_VOLTAIC_BLAZE_STACKS: f64 = 4.0;
75
76    fn registered_config(selected: bool) -> Result<EnhancementConfig> {
77        let encounter = wowlab_engine_ports::test_support::introspection_fixture(60.0).or_fail()?;
78        let environment = wowlab_engine_ports::test_support::introspection_game_data(&encounter);
79        let mut data = ResolvedGameData::builder();
80
81        data.set_armor_constant(environment.armor_constant());
82        data.set_creature_armor(environment.creature_armor());
83        data.set_armor_constant_mod(environment.armor_constant_mod());
84        data.insert_base_points(
85            SpellIdx::from_raw(EFFECT::VOLTAIC_BLAZE_MAELSTROM_STACKS.0),
86            EFFECT::VOLTAIC_BLAZE_MAELSTROM_STACKS.1,
87            UNUSUAL_VOLTAIC_BLAZE_STACKS,
88        );
89        let data = data.build();
90        let stats = CombatStats::default();
91        let selected = selected
92            .then(|| TalentSelection {
93                spell_id: TALENT::VOLTAIC_BLAZE,
94                override_spell_id: 0,
95                replaces_spell_id: 0,
96                ranks: 1,
97                precombat_aura: false,
98                hero_tree: None,
99                effect_overrides: Vec::new(),
100            })
101            .into_iter()
102            .collect::<Vec<_>>();
103        let rotation = crate::test_support::wait_rotation();
104        let params =
105            crate::test_support::handler_params(crate::test_support::HandlerParamsFixture {
106                game_data: data.clone(),
107                rotation: &rotation,
108                stats: &stats,
109                talent_selections: &selected,
110                encounter: &encounter,
111                consumables: ConsumableFlags::default(),
112                race: RaceId::Human,
113            })
114            .or_fail()?;
115        let mut draft = crate::test_combat_builder(CombatStats::default())
116            .game_data(data)
117            .into_draft()
118            .or_fail()?;
119
120        register_enhancement(&mut draft, &params)?;
121
122        draft.spec_config::<EnhancementConfig>().copied().or_fail()
123    }
124
125    #[gtest]
126    fn voltaic_blaze_stacks_use_resolved_effect_and_talent_gate() -> Result<()> {
127        verify_that!(
128            registered_config(true)?.voltaic_blaze_maelstrom_stacks,
129            eq(4)
130        )?;
131
132        verify_that!(
133            registered_config(false)?.voltaic_blaze_maelstrom_stacks,
134            eq(0)
135        )
136    }
137}