wowlab_engine_combat/systems/buffs/
haste.rs1#[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
22pub(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
36pub(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#[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}