wowlab_engine_application/game_data/
powers.rs1use 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
48pub(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
89pub(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
115fn 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}