Skip to main content

wowlab_engine_application/game_data/
powers.rs

1//! Power-type initialization and talent-driven resource modifiers.
2
3use wowlab_engine_domain::dbc::{
4    PowerModifiers, PowerType, ResourcePassive, talent_resource_passives,
5};
6use wowlab_engine_gamedata::{
7    PowerTypeProps, ResolvedCurves, ResolvedGameDataBuilder, ResolvedGameTables,
8};
9use wowlab_engine_ports::{DataResolver, DynDataResolver, EngineError};
10use wowlab_types::{
11    constants::HUNDRED,
12    data::{PowerTypeFlat, SpellDataFlat},
13};
14
15pub(super) fn normalized_power_type_props(
16    power: &PowerTypeFlat,
17    modifiers: &PowerModifiers,
18) -> PowerTypeProps {
19    PowerTypeProps {
20        display_modifier: f64::from(power.display_modifier).max(1.0),
21        max_base_power: wowlab_engine_domain::dbc::scale_power_amount(
22            f64::from(power.max_base_power),
23            power.power_type_enum,
24            modifiers,
25        ),
26        default_power: wowlab_engine_domain::dbc::scale_power_amount(
27            f64::from(power.default_power),
28            power.power_type_enum,
29            modifiers,
30        ),
31        regen_combat: wowlab_engine_domain::dbc::scale_power_amount(
32            f64::from(power.regen_combat),
33            power.power_type_enum,
34            modifiers,
35        ),
36        regen_percent_of_max: if PowerType::try_from(power.power_type_enum).ok()
37            == Some(PowerType::Mana)
38        {
39            1.0
40        } else {
41            0.0
42        },
43        auto_attack_gain_percent: 0.0,
44        cost_percent: 0.0,
45    }
46}
47
48// #t(fn: rust_builder_param) this mutates the resolved-data accumulator; it is not a factory.
49pub(super) fn apply_resource_passives(
50    builder: &mut ResolvedGameDataBuilder,
51    power_modifiers: &PowerModifiers,
52    spell: &SpellDataFlat,
53    ranks: u8,
54    specialization_order: Option<i32>,
55) {
56    for (power_type, passive) in talent_resource_passives(spell, ranks, specialization_order) {
57        let Some(props) = builder.power_type_mut(power_type) else {
58            continue;
59        };
60
61        match passive {
62            ResourcePassive::MaxFlat(amount) => {
63                props.max_base_power += wowlab_engine_domain::dbc::scale_power_amount(
64                    amount,
65                    power_type,
66                    power_modifiers,
67                );
68            }
69            ResourcePassive::RegenPercent(pct) => {
70                props.regen_combat *= 1.0 + pct / HUNDRED;
71            }
72            ResourcePassive::RegenPerFiveSeconds(amount) => {
73                props.regen_combat += wowlab_engine_domain::dbc::scale_power_amount(
74                    amount / wowlab_engine_domain::dbc::POWER_REGEN_PERIOD_SECONDS,
75                    power_type,
76                    power_modifiers,
77                );
78            }
79            ResourcePassive::AutoAttackGainPercent(pct) => {
80                props.auto_attack_gain_percent += pct;
81            }
82            ResourcePassive::CostPercent(pct) => {
83                props.cost_percent += pct;
84            }
85        }
86    }
87}
88
89// #t(fn: rust_builder_param) this mutates the resolved-data accumulator; it is not a factory.
90pub(super) async fn resolve_scaling_and_power_types(
91    builder: &mut ResolvedGameDataBuilder,
92    resolver: &DynDataResolver<'_>,
93) -> Result<PowerModifiers, EngineError> {
94    let scaling_data = resolver.get_scaling_data().await.map_err(|error| {
95        EngineError::spec_construction(format!("failed to resolve scaling data: {error}"))
96    })?;
97
98    builder.set_curves(ResolvedCurves::from_scaling(&scaling_data.curve_points));
99    builder.set_game_tables(ResolvedGameTables::from_scaling(&scaling_data));
100
101    let power_types = resolver.get_power_types().await.map_err(|error| {
102        EngineError::spec_construction(format!("failed to resolve power types: {error}"))
103    })?;
104    let modifiers = PowerModifiers::from_pairs(
105        power_types
106            .iter()
107            .map(|power| (power.power_type_enum, f64::from(power.display_modifier))),
108    );
109
110    insert_power_types(builder, &power_types, &modifiers);
111
112    Ok(modifiers)
113}
114
115// #t(fn: rust_builder_param) this mutates the resolved-data accumulator; it is not a factory.
116fn insert_power_types(
117    builder: &mut ResolvedGameDataBuilder,
118    power_types: &[PowerTypeFlat],
119    modifiers: &PowerModifiers,
120) {
121    for power in power_types {
122        builder.insert_power_type(
123            power.power_type_enum,
124            normalized_power_type_props(power, modifiers),
125        );
126    }
127}