Skip to main content

wowlab_engine_combat/systems/buffs/
haste.rs

1#[cfg(test)]
2use super::CombatInstance;
3use super::{
4    ActorId, ActorView, BuffEffect, CombatView, EffectFamily, HUNDRED, MIN_TIME_RATE_DELTA,
5    actor_only_buff_totals, for_each_planned_actor_effect_damage_binding, player_scaling_buffs,
6};
7
8pub(crate) fn current_haste_mult(view: &CombatView<'_>) -> f64 {
9    let totals = player_scaling_buffs(view);
10
11    (1.0 + (view.instance.buffer.player().haste + totals.haste_pct) / HUNDRED)
12        * totals.haste_mult
13        * totals.spell_cast_speed_mult
14}
15
16pub(crate) fn current_stat_haste_mult(view: &CombatView<'_>) -> f64 {
17    let totals = player_scaling_buffs(view);
18
19    (1.0 + (view.instance.buffer.player().haste + totals.haste_pct) / HUNDRED) * totals.haste_mult
20}
21
22/// Live player scaling shared by every RPPM entry point.
23pub(crate) fn current_rppm_scaling(view: &CombatView<'_>) -> wowlab_engine_rng::RppmScaling {
24    let totals = player_scaling_buffs(view);
25    let inherited_haste = (1.0
26        + (view.instance.buffer.player().haste + totals.haste_pct) / HUNDRED)
27        * totals.haste_mult;
28
29    wowlab_engine_rng::RppmScaling {
30        haste_pct: (inherited_haste - 1.0) * HUNDRED,
31        crit_pct: view.instance.buffer.player().crit + totals.crit_pct,
32        auto_attack_speed_multiplier: inherited_haste * totals.attack_speed_mult,
33    }
34}
35
36/// Wall-clock factor for an aura on `actor`.
37pub(crate) fn aura_time_rate_multiplier(
38    view: ActorView<'_>,
39    local: crate::state::LocalAuraIdx,
40) -> f64 {
41    let ActorView { program, .. } = view;
42    let mut multiplier = 1.0;
43    let target = program.damage.aura_action(local);
44
45    for_each_planned_actor_effect_damage_binding(
46        view,
47        EffectFamily::AURA_TIME_RATE,
48        |stacks, effect, binding| {
49            let BuffEffect::AuraTimeRateFromEffect { percent, .. } = effect else {
50                return;
51            };
52
53            if binding
54                .source_effect
55                .is_some_and(|source| program.damage.effect_affects_action(source, target))
56            {
57                multiplier *= time_rate_factor(*percent).powf(stacks);
58            }
59        },
60    );
61
62    multiplier
63}
64
65#[must_use]
66pub(crate) fn time_rate_factor(percent: f64) -> f64 {
67    1.0 / (1.0 + (percent / HUNDRED).max(MIN_TIME_RATE_DELTA))
68}
69
70pub(crate) fn current_mastery_points(view: &CombatView<'_>) -> f64 {
71    view.instance.buffer.player().mastery + view.buff_totals().mastery_pct
72}
73
74pub(crate) fn current_haste_mult_for(view: ActorView<'_>) -> f64 {
75    let ActorView {
76        instance: state,
77        actor,
78        ..
79    } = view;
80    let combat = view.combat();
81    let totals = player_scaling_buffs(&combat);
82    let haste_pct = state.buffer.player().haste + totals.haste_pct;
83    let inherited_haste = (1.0 + haste_pct / HUNDRED) * totals.haste_mult;
84
85    if actor == ActorId::Player {
86        return inherited_haste * totals.spell_cast_speed_mult;
87    }
88
89    let actor_totals = actor_only_buff_totals(view, None);
90
91    inherited_haste
92        * totals.spell_cast_speed_mult
93        * (1.0 + actor_totals.haste_pct / HUNDRED)
94        * actor_totals.haste_mult
95        * actor_totals.spell_cast_speed_mult
96}
97
98pub(crate) fn current_attack_haste_mult(view: &CombatView<'_>) -> f64 {
99    current_stat_haste_mult(view)
100}
101
102/// Returns the swing-frequency multiplier without affecting haste-scaled paths.
103#[cfg(test)]
104pub(crate) fn current_auto_attack_mult(
105    program: &crate::state::CombatProgram,
106    state: &CombatInstance,
107) -> f64 {
108    current_auto_attack_mult_for(ActorView::new(program, state, ActorId::Player))
109}
110
111pub(crate) fn current_auto_attack_mult_for(view: ActorView<'_>) -> f64 {
112    let ActorView {
113        instance: state,
114        actor,
115        ..
116    } = view;
117    let combat = view.combat();
118    let owner = player_scaling_buffs(&combat);
119    let haste_pct = state.buffer.player().haste + owner.haste_pct;
120    let inherited_haste = (1.0 + haste_pct / HUNDRED) * owner.haste_mult;
121
122    if actor == ActorId::Player {
123        return inherited_haste * owner.attack_speed_mult;
124    }
125
126    let actor_buffs = actor_only_buff_totals(view, None);
127
128    inherited_haste * actor_buffs.attack_speed_mult
129}