Skip to main content

wowlab_engine_content/hooks/elemental_shaman/
config.rs

1use wowlab_engine_domain::dbc::ResolvedGameDataEffectExt as _;
2use wowlab_engine_ports::HandlerParams;
3use wowlab_types::constants::HUNDRED;
4
5use crate::{
6    generated::specs::elemental_shaman::{DAMAGE_EFFECT, EFFECT, SET_BONUS, TALENT},
7    hooks::gated,
8};
9
10use super::bugs::BUG_STORMWELL_TEMPEST_MAELSTROM;
11
12pub(super) const ELEMENTAL_BLAST_COST: f64 = 90.0;
13
14#[derive(Clone, Copy, Debug, Default)]
15pub(super) struct ElementalRuntime {
16    pub(super) stormkeeper_faded_during_ascendance: bool,
17}
18
19crate::hooks::define_spec_config! {
20    pub(super) struct ElementalConfig {
21        earthquake_ground: Option<wowlab_engine_combat::GroundEffectId> { provenance: "Finalized Earthquake ground-effect plan.", gate: !params.game_data.is_empty(), source: Some(built.register_ground_effect(wowlab_engine_combat::GroundEffectDefinition::from_driver(DAMAGE_EFFECT::EARTHQUAKE_TIMING.action(), DAMAGE_EFFECT::EARTHQUAKE_TIMING, DAMAGE_EFFECT::EARTHQUAKE_DAMAGE, DAMAGE_EFFECT::EARTHQUAKE_DAMAGE).flags(wowlab_engine_combat::DamageFlags::PERIODIC))?), transform: |value| value },
22        supercharge_chance: f64 { provenance: "Supercharge effect proc chance.", gate: params.talent_picked(TALENT::SUPERCHARGE), source: effect_fraction(EFFECT::SUPERCHARGE_CHANCE), transform: |value| value },
23        feedback_loop_chance: f64 { provenance: "Feedback Loop rank-three bonus chance.", gate: params.talent_picked(TALENT::FEEDBACK_LOOP_3), source: effect_fraction(EFFECT::FEEDBACK_LOOP_BONUS), transform: |value| value },
24        fusion_of_elements_mult: f64 { provenance: "Fusion of Elements effect damage fraction.", gate: params.talent_picked(TALENT::FUSION_OF_ELEMENTS), source: effect_fraction(EFFECT::FUSION_OF_ELEMENTS_BONUS), transform: |value| value },
25        eb_overload_mult: f64 { provenance: "Mountains Will Fall Elemental Blast overload fraction.", gate: params.talent_picked(TALENT::MOUNTAINS_WILL_FALL), source: effect_fraction(EFFECT::MOUNTAINS_WILL_FALL_BONUS), transform: |value| value },
26        aftershock_chance: f64 { provenance: "Aftershock effect refund chance.", gate: params.talent_picked(TALENT::AFTERSHOCK), source: effect_fraction(EFFECT::AFTERSHOCK_CHANCE), transform: |value| value },
27        potm_chance: f64 { provenance: "Power of the Maelstrom effect proc chance.", gate: params.talent_picked(TALENT::POWER_OF_THE_MAELSTROM), source: effect_fraction(EFFECT::POWER_OF_THE_MAELSTROM_CHANCE), transform: |value| value },
28        stormwell_tempest_maelstrom: f64 { provenance: "Live bug: Tempest applying a new Stormkeeper via Arc Discharge triggers Stormwell's Maelstrom grant.", gate: params.talent_picked(TALENT::STORMWELL) && params.talent_picked(TALENT::ARC_DISCHARGE), source: params.game_data.effect_base_points(EFFECT::STORMWELL_SK_MAELSTROM), transform: |value| value },
29        stormwell_tempest_bug: bool { provenance: "Frozen live-bug selection for Stormwell's Tempest Maelstrom grant.", gate: params.bugs.enabled(&BUG_STORMWELL_TEMPEST_MAELSTROM), source: true, transform: |value| value },
30        tempest_chance_on_blast: f64 { provenance: "Tempest and Awakening Storms per-point chances folded across Elemental Blast cost.", gate: true, source: tempest_chance_on_blast(params), transform: |value| value },
31        eb_overload: bool { provenance: "Mountains Will Fall enables Elemental Blast overloads.", gate: params.talent_picked(TALENT::MOUNTAINS_WILL_FALL), source: true, transform: |value| value },
32        master_of_the_elements: bool { provenance: "Master of the Elements enables its spender window.", gate: params.talent_picked(TALENT::MASTER_OF_THE_ELEMENTS), source: true, transform: |value| value },
33        arc_discharge: bool { provenance: "Arc Discharge enables its overload rider.", gate: params.talent_picked(TALENT::ARC_DISCHARGE), source: true, transform: |value| value },
34        unlimited_power: bool { provenance: "Unlimited Power enables haste stacking.", gate: params.talent_picked(TALENT::UNLIMITED_POWER), source: true, transform: |value| value },
35        lightning_rod_mult: f64 { provenance: "Lightning Rod effect damage fraction.", gate: params.talent_picked(TALENT::LIGHTNING_ROD), source: effect_fraction(EFFECT::LIGHTNING_ROD_PERCENT), transform: |value| value },
36        summon_storm_elemental: bool { provenance: "Fury of the Storms plus Primal Elementalist enables the primal Storm Elemental.", gate: params.talent_picked(TALENT::FURY_OF_THE_STORMS) && params.talent_picked(TALENT::PRIMAL_ELEMENTALIST), source: true, transform: |value| value },
37        summon_fire_elemental: bool { provenance: "Call of Fire plus Primal Elementalist enables the primal Fire Elemental.", gate: params.talent_picked(TALENT::CALL_OF_FIRE) && params.talent_picked(TALENT::PRIMAL_ELEMENTALIST), source: true, transform: |value| value },
38        tww3_stormbringer_2pc: bool { provenance: "The TWW3 Stormbringer two-piece grants Tempest on Ascendance.", gate: params.set_bonus_auras.contains(&SET_BONUS::TWW3_STORMBRINGER_2PC), source: true, transform: |value| value },
39        tww3_stormbringer_4pc: bool { provenance: "TWW3 Stormbringer four-piece enables its tier rider.", gate: params.set_bonus_auras.contains(&SET_BONUS::TWW3_STORMBRINGER_4PC), source: true, transform: |value| value },
40        mid2_4pc: bool { provenance: "MID2 Elemental four-piece enables Flowing Elements when Stormkeeper or Ascendance fades.", gate: params.set_bonus_auras.contains(&SET_BONUS::MID2_ELEMENTAL_4PC), source: true, transform: |value| value },
41    }
42    accessor pub(super) fn elemental_config(ctx);
43    register pub(super) fn register_elemental_config(built, params) -> Result<(), wowlab_engine_ports::EngineError>;
44    prepare {
45        let effect_fraction = |effect| params.game_data.effect_percent(effect);
46    }
47    auras {}
48    finish |cfg| {
49        built.set_spec_config(cfg);
50        built.set_spec_runtime(ElementalRuntime::default());
51        Ok(())
52    }
53}
54
55fn tempest_chance_on_blast(params: &HandlerParams<'_>) -> f64 {
56    let data = &params.game_data;
57    let tempest = gated(params.talent_picked(TALENT::TEMPEST_TALENT), || {
58        data.effect_base_points(EFFECT::TEMPEST_TALENT_CHANCE) / HUNDRED / HUNDRED
59    });
60    let awakening = gated(params.talent_picked(TALENT::AWAKENING_STORMS), || {
61        data.effect_base_points(EFFECT::AWAKENING_STORMS_CHANCE) / HUNDRED / HUNDRED
62    });
63    let tempest_per_point = tempest + awakening;
64
65    if tempest_per_point > 0.0 {
66        1.0 - (1.0 - tempest_per_point).powf(ELEMENTAL_BLAST_COST)
67    } else {
68        0.0
69    }
70}