wowlab_engine_combat/systems/buffs/
haste.rs1use super::*;
2
3pub(crate) fn current_haste_mult(view: &CombatView<'_>) -> f64 {
4 let totals = view.buff_totals();
5
6 (1.0 + (view.buf.player().haste + totals.haste_pct) / HUNDRED)
7 * totals.haste_mult
8 * totals.spell_cast_speed_mult
9}
10
11pub(crate) fn current_stat_haste_mult(view: &CombatView<'_>) -> f64 {
12 let totals = view.buff_totals();
13
14 (1.0 + (view.buf.player().haste + totals.haste_pct) / HUNDRED) * totals.haste_mult
15}
16
17pub(crate) fn current_rppm_scaling(view: &CombatView<'_>) -> wowlab_engine_rng::RppmScaling {
19 let totals = view.buff_totals();
20 let inherited_haste =
21 (1.0 + (view.buf.player().haste + totals.haste_pct) / HUNDRED) * totals.haste_mult;
22
23 wowlab_engine_rng::RppmScaling {
24 haste_pct: (inherited_haste - 1.0) * HUNDRED,
25 crit_pct: view.buf.player().crit + totals.crit_pct,
26 auto_attack_speed_multiplier: inherited_haste * totals.attack_speed_mult,
27 }
28}
29
30pub(crate) fn aura_time_rate_multiplier(view: ActorView<'_>, aura_spell_id: u32) -> f64 {
32 let ActorView { state, .. } = view;
33 let mut multiplier = 1.0;
34 let target = wowlab_types::sim::SpellIdx::from_raw(aura_spell_id);
35
36 for_each_active_actor_aura_effect(view, |stacks, effect| {
37 let BuffEffect::AuraTimeRateFromEffect {
38 percent,
39 source_spell_id,
40 effect_index,
41 } = effect
42 else {
43 return;
44 };
45
46 if state.config.game_data.effect_affects_spell(
47 wowlab_types::sim::SpellIdx::from_raw(*source_spell_id),
48 *effect_index,
49 target,
50 ) {
51 multiplier *= time_rate_factor(*percent).powf(stacks);
52 }
53 });
54
55 multiplier
56}
57
58#[must_use]
59pub(crate) fn time_rate_factor(percent: f64) -> f64 {
60 1.0 / (1.0 + (percent / HUNDRED).max(MIN_TIME_RATE_DELTA))
61}
62
63pub(crate) fn current_mastery_points(view: &CombatView<'_>) -> f64 {
64 view.buf.player().mastery + view.buff_totals().mastery_pct
65}
66
67pub(crate) fn current_haste_mult_for(view: ActorView<'_>) -> f64 {
68 let ActorView { buf, actor, .. } = view;
69 let combat = view.combat();
70 let totals = combat.buff_totals();
71 let haste_pct = buf.player().haste + totals.haste_pct;
72 let inherited_haste = (1.0 + haste_pct / HUNDRED) * totals.haste_mult;
73
74 if actor == ActorId::Player {
75 return inherited_haste * totals.spell_cast_speed_mult;
76 }
77
78 let actor_totals = accumulate_actor_only_buffs(view, None);
79
80 inherited_haste
81 * totals.spell_cast_speed_mult
82 * (1.0 + actor_totals.haste_pct / HUNDRED)
83 * actor_totals.haste_mult
84 * actor_totals.spell_cast_speed_mult
85}
86
87pub(crate) fn current_attack_haste_mult(view: &CombatView<'_>) -> f64 {
88 current_stat_haste_mult(view)
89}
90
91#[cfg(test)]
93pub(crate) fn current_auto_attack_mult(state: &CombatState, buf: &DenseBuffer) -> f64 {
94 current_auto_attack_mult_for(ActorView::new(state, buf, ActorId::Player))
95}
96
97pub(crate) fn current_auto_attack_mult_for(view: ActorView<'_>) -> f64 {
98 let ActorView { buf, actor, .. } = view;
99 let combat = view.combat();
100 let owner = combat.buff_totals();
101 let haste_pct = buf.player().haste + owner.haste_pct;
102 let inherited_haste = (1.0 + haste_pct / HUNDRED) * owner.haste_mult;
103
104 if actor == ActorId::Player {
105 return inherited_haste * owner.attack_speed_mult;
106 }
107
108 let actor_buffs = accumulate_actor_only_buffs(view, None);
109
110 inherited_haste * actor_buffs.attack_speed_mult
111}