wowlab_engine_combat/context/hook/
rppm.rs1use wowlab_engine_rng as stochastic;
2use wowlab_types::sim::SimTime;
3
4use super::{ProcOps, proc_counters};
5use crate::{context::HookCtx, state::LocalRppmIdx, systems};
6
7impl ProcOps for HookCtx<'_> {
8 fn roll_rppm(&mut self, idx: LocalRppmIdx) -> bool {
9 let now_secs = self.now.as_secs_f64();
10 let scaling = systems::current_rppm_scaling(&self.view().combat());
11
12 if let Some(tracker) = self
13 .instance
14 .runtime
15 .procs
16 .rppm_trackers
17 .get_mut(idx.as_usize())
18 {
19 stochastic::roll_rppm(tracker, now_secs, scaling, self.rng)
20 } else {
21 false
22 }
23 }
24
25 fn roll_threshold(
27 &mut self,
28 idx: crate::state::LocalThresholdIdx,
29 increment_scale: f64,
30 ) -> bool {
31 let Some(tracker) = self
32 .instance
33 .runtime
34 .procs
35 .threshold_trackers
36 .get_mut(idx.as_usize())
37 else {
38 return false;
39 };
40
41 if tracker.accumulated.is_nan() {
42 tracker.accumulated = (self.rng)();
43 }
44
45 let increment = (self.rng)() * tracker.increment_max * increment_scale;
46
47 stochastic::roll_threshold(tracker, increment)
48 }
49
50 fn roll_system_rppm(&mut self, binding: crate::SystemRppmBinding) -> bool {
56 let Some(resolved) = self
57 .program
58 .system_rppm_bindings
59 .get(binding.as_usize())
60 .copied()
61 .flatten()
62 else {
63 return false;
64 };
65 let Some(ready_at) = self
66 .instance
67 .runtime
68 .procs
69 .system_rppm_ready_at
70 .get(binding.as_usize())
71 .copied()
72 else {
73 return false;
74 };
75
76 if self.now < ready_at || !self.roll_rppm(resolved.tracker) {
77 return false;
78 }
79
80 if resolved.recovery_ms > 0 {
81 self.instance.runtime.procs.system_rppm_ready_at[binding.as_usize()] = self
83 .now
84 .saturating_add(SimTime::from_millis(resolved.recovery_ms));
85 }
86
87 true
88 }
89
90 fn proc_category_ready(&self, binding: crate::ProcCategoryBinding) -> bool {
92 self.instance
93 .runtime
94 .procs
95 .proc_category_ready_at_dense
96 .get(binding.as_usize())
97 .is_none_or(|ready_at| self.now >= *ready_at)
98 }
99
100 fn start_proc_category_cooldown(&mut self, binding: crate::ProcCategoryBinding) {
102 let recovery_ms = self
103 .program
104 .proc_category_recovery_ms
105 .get(binding.as_usize())
106 .copied()
107 .unwrap_or(0);
108
109 if recovery_ms == 0 {
110 return;
111 }
112
113 if let Some(ready_at) = self
114 .instance
115 .runtime
116 .procs
117 .proc_category_ready_at_dense
118 .get_mut(binding.as_usize())
119 {
120 *ready_at = self.now.saturating_add(SimTime::from_millis(recovery_ms));
121 }
122 }
123
124 fn roll_accumulating_proc(
125 &mut self,
126 binding: crate::ProcCounterBinding,
127 chance_per_attempt: f64,
128 ) -> bool {
129 self.roll_attempt_proc(binding, |attempt| chance_per_attempt * f64::from(attempt))
130 }
131
132 fn roll_attempt_proc(
134 &mut self,
135 binding: crate::ProcCounterBinding,
136 chance_for_attempt: impl FnOnce(u32) -> f64,
137 ) -> bool {
138 let Some(attempts) = self
139 .instance
140 .runtime
141 .procs
142 .proc_counters
143 .get_mut(binding.as_usize())
144 else {
145 return false;
146 };
147 let attempt = attempts.saturating_add(1);
148
149 if stochastic::proc_chance(self.rng, chance_for_attempt(attempt)) {
150 *attempts = 0;
151
152 true
153 } else {
154 *attempts = attempt;
155
156 false
157 }
158 }
159
160 fn add_proc_counter(&mut self, binding: crate::ProcCounterBinding, amount: u32) -> u32 {
162 proc_counters::add(self, binding, amount)
163 }
164
165 fn reset_proc_counter(&mut self, binding: crate::ProcCounterBinding) {
167 proc_counters::reset(self, binding);
168 }
169
170 fn proc_counter(&self, binding: crate::ProcCounterBinding) -> u32 {
172 proc_counters::get(self, binding)
173 }
174
175 fn mark_proc_flags_once(&mut self, binding: crate::ProcCounterBinding, flags: u32) -> bool {
177 proc_counters::mark_once(self, binding, flags)
178 }
179
180 fn consume_proc_counter(&mut self, binding: crate::ProcCounterBinding, amount: u32) -> u32 {
182 proc_counters::consume(self, binding, amount)
183 }
184
185 fn advance_proc_counter_by(
187 &mut self,
188 binding: crate::ProcCounterBinding,
189 amount: u32,
190 threshold: u32,
191 ) -> bool {
192 proc_counters::advance(self, binding, amount, threshold)
193 }
194
195 fn advance_proc_counter(&mut self, binding: crate::ProcCounterBinding, threshold: u32) -> bool {
197 self.advance_proc_counter_by(binding, 1, threshold)
198 }
199}