peace_performance/
pp.rs

1use crate::{Beatmap, GameMode, PpResult, StarResult};
2
3#[cfg(feature = "fruits")]
4use crate::FruitsPP;
5
6#[cfg(feature = "mania")]
7use crate::ManiaPP;
8
9#[cfg(feature = "osu")]
10use crate::OsuPP;
11
12#[cfg(feature = "taiko")]
13use crate::TaikoPP;
14
15/// Calculator for pp on maps of any mode.
16#[derive(Clone)]
17#[allow(clippy::upper_case_acronyms)]
18pub enum AnyPP<'m> {
19    #[cfg(feature = "fruits")]
20    Fruits(FruitsPP<'m>),
21    #[cfg(feature = "mania")]
22    Mania(ManiaPP<'m>),
23    #[cfg(feature = "osu")]
24    Osu(OsuPP<'m>),
25    #[cfg(feature = "taiko")]
26    Taiko(TaikoPP<'m>),
27}
28
29impl<'m> AnyPP<'m> {
30    #[inline]
31    pub fn new(map: &'m Beatmap) -> Self {
32        match map.mode {
33            #[cfg(feature = "fruits")]
34            GameMode::CTB => Self::Fruits(FruitsPP::new(map)),
35            #[cfg(feature = "mania")]
36            GameMode::MNA => Self::Mania(ManiaPP::new(map)),
37            #[cfg(feature = "osu")]
38            GameMode::STD => Self::Osu(OsuPP::new(map)),
39            #[cfg(feature = "taiko")]
40            GameMode::TKO => Self::Taiko(TaikoPP::new(map)),
41            #[allow(unreachable_patterns)]
42            _ => panic!("feature for mode {:?} is not enabled", map.mode),
43        }
44    }
45
46    #[cfg(any(feature = "async_std", feature = "async_tokio"))]
47    #[inline]
48    pub async fn calculate(&mut self) -> PpResult {
49        match self {
50            #[cfg(feature = "fruits")]
51            Self::Fruits(f) => f.calculate_async().await,
52            #[cfg(feature = "mania")]
53            Self::Mania(m) => m.calculate_async().await,
54            #[cfg(feature = "osu")]
55            Self::Osu(o) => o.calculate_async().await,
56            #[cfg(feature = "taiko")]
57            Self::Taiko(t) => t.calculate_async().await,
58        }
59    }
60
61    #[cfg(not(any(feature = "async_std", feature = "async_tokio")))]
62    #[inline]
63    pub fn calculate(&mut self) -> PpResult {
64        match self {
65            #[cfg(feature = "fruits")]
66            Self::Fruits(f) => f.calculate(),
67            #[cfg(feature = "mania")]
68            Self::Mania(m) => m.calculate(),
69            #[cfg(feature = "osu")]
70            Self::Osu(o) => o.calculate(),
71            #[cfg(feature = "taiko")]
72            Self::Taiko(t) => t.calculate(),
73        }
74    }
75
76    /// [`AttributeProvider`] is implemented by [`StarResult`](crate::StarResult)
77    /// and [`PpResult`](crate::PpResult) meaning you can give the result of a \
78    /// star calculation or the result of a pp calculation.
79    /// If you already calculated the stars for the current map-mod combination,
80    /// be sure to put them in here so that they don't have to be recalculated.
81    #[inline]
82    pub fn attributes(self, attributes: impl AttributeProvider) -> Self {
83        match self {
84            #[cfg(feature = "fruits")]
85            Self::Fruits(f) => Self::Fruits(f.attributes(attributes.attributes())),
86            #[cfg(feature = "mania")]
87            Self::Mania(m) => Self::Mania(m.attributes(attributes.attributes())),
88            #[cfg(feature = "osu")]
89            Self::Osu(o) => Self::Osu(o.attributes(attributes.attributes())),
90            #[cfg(feature = "taiko")]
91            Self::Taiko(t) => Self::Taiko(t.attributes(attributes.attributes())),
92        }
93    }
94
95    /// Specify mods through their bit values.
96    ///
97    /// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
98    #[inline]
99    pub fn mods(self, mods: u32) -> Self {
100        match self {
101            #[cfg(feature = "fruits")]
102            Self::Fruits(f) => Self::Fruits(f.mods(mods)),
103            #[cfg(feature = "mania")]
104            Self::Mania(m) => Self::Mania(m.mods(mods)),
105            #[cfg(feature = "osu")]
106            Self::Osu(o) => Self::Osu(o.mods(mods)),
107            #[cfg(feature = "taiko")]
108            Self::Taiko(t) => Self::Taiko(t.mods(mods)),
109        }
110    }
111
112    /// Amount of passed objects for partial plays, e.g. a fail.
113    #[inline]
114    pub fn passed_objects(self, passed_objects: usize) -> Self {
115        match self {
116            #[cfg(feature = "fruits")]
117            Self::Fruits(f) => Self::Fruits(f.passed_objects(passed_objects)),
118            #[cfg(feature = "mania")]
119            Self::Mania(m) => Self::Mania(m.passed_objects(passed_objects)),
120            #[cfg(feature = "osu")]
121            Self::Osu(o) => Self::Osu(o.passed_objects(passed_objects)),
122            #[cfg(feature = "taiko")]
123            Self::Taiko(t) => Self::Taiko(t.passed_objects(passed_objects)),
124        }
125    }
126
127    /// Set the accuracy between 0.0 and 100.0.
128    ///
129    /// For some modes this method depends on previously set values
130    /// be sure to call this last before calling `calculate`.
131    ///
132    /// Irrelevant for osu!mania.
133    #[allow(unused_variables)]
134    #[inline]
135    pub fn accuracy(self, acc: f32) -> Self {
136        match self {
137            #[cfg(feature = "fruits")]
138            Self::Fruits(f) => Self::Fruits(f.accuracy(acc)),
139            #[cfg(feature = "mania")]
140            Self::Mania(_) => self,
141            #[cfg(feature = "osu")]
142            Self::Osu(o) => Self::Osu(o.accuracy(acc)),
143            #[cfg(feature = "taiko")]
144            Self::Taiko(t) => Self::Taiko(t.accuracy(acc)),
145        }
146    }
147
148
149    #[allow(unused_variables)]
150    #[inline(always)]
151    /// Set the accuracy between 0.0 and 100.0.
152    ///
153    /// For some modes this method depends on previously set values
154    /// be sure to call this last before calling `calculate`.
155    ///
156    /// Irrelevant for osu!mania.
157    /// 
158    /// If it is used to calculate the PP of multiple different ACCs, 
159    /// it should be called from high to low according to the ACC value, otherwise it is invalid.
160    /// 
161    /// Examples:
162    /// ```
163    /// // valid
164    /// let acc_100 = {
165    ///     c.set_accuracy(100.0);
166    ///     c.calculate().await
167    /// };
168    /// let acc_99 = {
169    ///     c.set_accuracy(99.0);
170    ///     c.calculate().await
171    /// };
172    /// let acc_98 = {
173    ///     c.set_accuracy(98.0);
174    ///     c.calculate().await
175    /// };
176    /// let acc_95 = {
177    ///     c.set_accuracy(95.0);
178    ///     c.calculate().await
179    /// };
180    /// 
181    /// // invalid
182    /// let acc_95 = {
183    ///     c.set_accuracy(95.0);
184    ///     c.calculate().await
185    /// };
186    /// let acc_98 = {
187    ///     c.set_accuracy(98.0);
188    ///     c.calculate().await
189    /// };
190    /// let acc_99 = {
191    ///     c.set_accuracy(99.0);
192    ///     c.calculate().await
193    /// };
194    /// let acc_100 = {
195    ///     c.set_accuracy(100.0);
196    ///     c.calculate().await
197    /// };
198    /// ```
199    /// 
200    pub fn set_accuracy(&mut self, acc: f32) {
201        match self {
202            #[cfg(feature = "fruits")]
203            Self::Fruits(f) => f.set_accuracy(acc),
204            #[cfg(feature = "mania")]
205            Self::Mania(_) => {}
206            #[cfg(feature = "osu")]
207            Self::Osu(o) => o.set_accuracy(acc),
208            #[cfg(feature = "taiko")]
209            Self::Taiko(t) => t.set_accuracy(acc),
210        }
211    }
212
213    /// Specify the amount of misses of a play.
214    ///
215    /// Irrelevant for osu!mania.
216    #[allow(unused_variables)]
217    #[inline]
218    pub fn misses(self, misses: usize) -> Self {
219        match self {
220            #[cfg(feature = "fruits")]
221            Self::Fruits(f) => Self::Fruits(f.misses(misses)),
222            #[cfg(feature = "mania")]
223            Self::Mania(_) => self,
224            #[cfg(feature = "osu")]
225            Self::Osu(o) => Self::Osu(o.misses(misses)),
226            #[cfg(feature = "taiko")]
227            Self::Taiko(t) => Self::Taiko(t.misses(misses)),
228        }
229    }
230
231    /// Specify the max combo of the play.
232    ///
233    /// Irrelevant for osu!mania.
234    #[allow(unused_variables)]
235    #[inline]
236    pub fn combo(self, combo: usize) -> Self {
237        match self {
238            #[cfg(feature = "fruits")]
239            Self::Fruits(f) => Self::Fruits(f.combo(combo)),
240            #[cfg(feature = "mania")]
241            Self::Mania(_) => self,
242            #[cfg(feature = "osu")]
243            Self::Osu(o) => Self::Osu(o.combo(combo)),
244            #[cfg(feature = "taiko")]
245            Self::Taiko(t) => Self::Taiko(t.combo(combo)),
246        }
247    }
248
249    /// Specify the amount of 300s of a play.
250    ///
251    /// Irrelevant for osu!mania.
252    #[allow(unused_variables)]
253    #[inline]
254    pub fn n300(self, n300: usize) -> Self {
255        match self {
256            #[cfg(feature = "fruits")]
257            Self::Fruits(f) => Self::Fruits(f.fruits(n300)),
258            #[cfg(feature = "mania")]
259            Self::Mania(_) => self,
260            #[cfg(feature = "osu")]
261            Self::Osu(o) => Self::Osu(o.n300(n300)),
262            #[cfg(feature = "taiko")]
263            Self::Taiko(t) => Self::Taiko(t.n300(n300)),
264        }
265    }
266
267    /// Specify the amount of 100s of a play.
268    ///
269    /// Irrelevant for osu!mania.
270    #[allow(unused_variables)]
271    #[inline]
272    pub fn n100(self, n100: usize) -> Self {
273        match self {
274            #[cfg(feature = "fruits")]
275            Self::Fruits(f) => Self::Fruits(f.droplets(n100)),
276            #[cfg(feature = "mania")]
277            Self::Mania(_) => self,
278            #[cfg(feature = "osu")]
279            Self::Osu(o) => Self::Osu(o.n100(n100)),
280            #[cfg(feature = "taiko")]
281            Self::Taiko(t) => Self::Taiko(t.n100(n100)),
282        }
283    }
284
285    /// Specify the amount of 50s of a play.
286    ///
287    /// Irrelevant for osu!mania and osu!taiko.
288    #[allow(unused_variables)]
289    #[inline]
290    pub fn n50(self, n50: usize) -> Self {
291        match self {
292            #[cfg(feature = "fruits")]
293            Self::Fruits(f) => Self::Fruits(f.tiny_droplets(n50)),
294            #[cfg(feature = "mania")]
295            Self::Mania(_) => self,
296            #[cfg(feature = "osu")]
297            Self::Osu(o) => Self::Osu(o.n50(n50)),
298            #[cfg(feature = "taiko")]
299            Self::Taiko(_) => self,
300        }
301    }
302
303    /// Specify the amount of katus of a play.
304    ///
305    /// This value is only relevant for osu!ctb for which it represent
306    /// the amount of tiny droplet misses.
307    #[allow(unused_variables)]
308    #[inline]
309    pub fn n_katu(self, n_katu: usize) -> Self {
310        match self {
311            #[cfg(feature = "fruits")]
312            Self::Fruits(f) => Self::Fruits(f.tiny_droplet_misses(n_katu)),
313            #[cfg(feature = "mania")]
314            Self::Mania(_) => self,
315            #[cfg(feature = "osu")]
316            Self::Osu(_) => self,
317            #[cfg(feature = "taiko")]
318            Self::Taiko(_) => self,
319        }
320    }
321
322    /// Specify the score of a play.
323    ///
324    /// This value is only relevant for osu!mania.
325    ///
326    /// On `NoMod` its between 0 and 1,000,000, on `Easy` between 0 and 500,000, etc.
327    #[allow(unused_variables)]
328    #[inline]
329    pub fn score(self, score: u32) -> Self {
330        match self {
331            #[cfg(feature = "fruits")]
332            Self::Fruits(_) => self,
333            #[cfg(feature = "mania")]
334            Self::Mania(m) => Self::Mania(m.score(score)),
335            #[cfg(feature = "osu")]
336            Self::Osu(_) => self,
337            #[cfg(feature = "taiko")]
338            Self::Taiko(_) => self,
339        }
340    }
341}
342
343pub trait AttributeProvider {
344    fn attributes(self) -> StarResult;
345}
346
347impl AttributeProvider for StarResult {
348    #[inline]
349    fn attributes(self) -> StarResult {
350        self
351    }
352}
353
354impl AttributeProvider for PpResult {
355    #[inline]
356    fn attributes(self) -> StarResult {
357        self.attributes
358    }
359}