renamed fruits to catch

This commit is contained in:
MaxOhn
2022-03-20 17:05:37 +01:00
parent 71eac0ffa3
commit 58b2ffba08
18 changed files with 186 additions and 201 deletions
+2 -1
View File
@@ -1,7 +1,7 @@
## Upcoming
- osu: Fix panic on maps with 0 objects
- fruits: Fixed droplet timings on juicestreams with span count >1
- catch: Fixed droplet timings on juicestreams with span count >1
- Fixed timing point parsing on some (older) maps where "uninherited" value did not coincide with beat length
- Fixed handling .osu files with missing difficulty attributes
- Fixed huge memory allocations caused by incorrectly parsed .osu files by clamping beat length
@@ -11,6 +11,7 @@
- [BREAKING] `BeatmapExt::stars`'s definition was adjusted to use the `AnyStars` builder struct
- [BREAKING] Store `HitObject::sound` in `Beatmap::sounds` instead to reduce the struct size
- [BREAKING] Removed the mode features `osu`, `fruits`, `taiko`, and `mania`. Now all modes are always supported.
- [BREAKING] Renamed the `rosu_pp::fruits` module to `rosu_pp::catch`. Similarly, all structs `Fruits{Name}` were renamed to `Catch{Name}` and enums over the mode have their `Fruits` variant renamed to `Catch`
# v0.4.0
-1
View File
@@ -15,7 +15,6 @@ default = []
async_std = ["async-std"]
async_tokio = ["tokio"]
[dependencies.async-std]
version = "1.9"
optional = true
+1 -5
View File
@@ -162,11 +162,7 @@ println!("PP after the first 11 objects: {}", curr_performance.pp());
| Flag | Description |
|-----|-----|
| `default` | Enable all modes. |
| `osu` | Enable osu!standard. |
| `taiko` | Enable osu!taiko. |
| `fruits` | Enable osu!catch. |
| `mania` | Enable osu!mania. |
| `default` | Beatmap parsing will be non-async |
| `async_tokio` | Beatmap parsing will be async through [tokio](https://github.com/tokio-rs/tokio) |
| `async_std` | Beatmap parsing will be async through [async-std](https://github.com/async-rs/async-std) |
+2 -2
View File
@@ -63,7 +63,7 @@ async fn async_main() {
let mut evaluators = [
Evaluator::new("osu"),
Evaluator::new("taiko"),
Evaluator::new("fruits"),
Evaluator::new("catch"),
Evaluator::new("mania"),
];
@@ -115,7 +115,7 @@ impl Evaluator {
self.pp.push(difference(data.performance.pp, attrs.pp()));
match attrs {
PerformanceAttributes::Fruits(_) => {}
PerformanceAttributes::Catch(_) => {}
PerformanceAttributes::Mania(attrs) => {
if let Some(acc) = data.performance.acc {
let values = self.accuracy.get_or_insert_with(Vec::new);
@@ -6,14 +6,14 @@ use crate::{
Beatmap,
};
use super::{catch_object::CatchObject, slider_state::SliderState, FruitsDifficultyAttributes};
use super::{catch_object::CatchObject, slider_state::SliderState, CatchDifficultyAttributes};
const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
const BASE_SCORING_DISTANCE: f64 = 100.0;
#[derive(Clone, Debug)]
pub(crate) struct FruitParams<'a> {
pub(crate) attributes: FruitsDifficultyAttributes,
pub(crate) attributes: CatchDifficultyAttributes,
pub(crate) curve_bufs: CurveBuffers,
pub(crate) last_pos: Option<f32>,
pub(crate) last_time: f64,
@@ -1,11 +1,11 @@
use std::{iter, slice::Iter};
use crate::{
curve::CurveBuffers,
fruits::{
catch::{
difficulty_object::DifficultyObject, slider_state::SliderState, SECTION_LENGTH,
STAR_SCALING_FACTOR,
},
curve::CurveBuffers,
parse::{HitObject, Pos2},
Beatmap, Mods,
};
@@ -15,24 +15,24 @@ use super::{
catch_object::CatchObject,
fruit_or_juice::{FruitOrJuice, FruitParams},
movement::Movement,
FruitsDifficultyAttributes, ALLOWED_CATCH_RANGE,
CatchDifficultyAttributes, ALLOWED_CATCH_RANGE,
};
/// Gradually calculate the difficulty attributes of an osu!catch map.
///
/// Note that this struct implements [`Iterator`](std::iter::Iterator).
/// On every call of [`Iterator::next`](std::iter::Iterator::next), the map's next fruit or droplet
/// will be processed and the [`FruitsDifficultyAttributes`] will be updated and returned.
/// will be processed and the [`CatchDifficultyAttributes`] will be updated and returned.
///
/// Note that it does not return attributes after a tiny droplet. Only for fruits and droplets.
///
/// If you want to calculate performance attributes, use
/// [`FruitsGradualPerformanceAttributes`](crate::fruits::FruitsGradualPerformanceAttributes) instead.
/// [`CatchGradualPerformanceAttributes`](crate::catch::CatchGradualPerformanceAttributes) instead.
///
/// # Example
///
/// ```
/// use rosu_pp::{Beatmap, fruits::FruitsGradualDifficultyAttributes};
/// use rosu_pp::{Beatmap, catch::CatchGradualDifficultyAttributes};
///
/// # /*
/// let map: Beatmap = ...
@@ -40,7 +40,7 @@ use super::{
/// # let map = Beatmap::default();
///
/// let mods = 64; // DT
/// let mut iter = FruitsGradualDifficultyAttributes::new(&map, mods);
/// let mut iter = CatchGradualDifficultyAttributes::new(&map, mods);
///
/// let attrs1 = iter.next(); // the difficulty of the map after the first hit object
/// let attrs2 = iter.next(); // after the second hit object
@@ -51,10 +51,10 @@ use super::{
/// }
/// ```
#[derive(Clone, Debug)]
pub struct FruitsGradualDifficultyAttributes<'map> {
pub struct CatchGradualDifficultyAttributes<'map> {
pub(crate) idx: usize,
clock_rate: f64,
hit_objects: FruitsObjectIter<'map>,
hit_objects: CatchObjectIter<'map>,
movement: Movement,
prev: CatchObject,
half_catcher_width: f64,
@@ -64,17 +64,17 @@ pub struct FruitsGradualDifficultyAttributes<'map> {
strain_peak_buf: Vec<f64>,
}
impl<'map> FruitsGradualDifficultyAttributes<'map> {
impl<'map> CatchGradualDifficultyAttributes<'map> {
/// Create a new difficulty attributes iterator for osu!catch maps.
pub fn new(map: &'map Beatmap, mods: impl Mods) -> Self {
let map_attributes = map.attributes().mods(mods);
let attributes = FruitsDifficultyAttributes {
let attributes = CatchDifficultyAttributes {
ar: map_attributes.ar,
..Default::default()
};
let hit_objects = FruitsObjectIter::new(map, mods, attributes);
let hit_objects = CatchObjectIter::new(map, mods, attributes);
let half_catcher_width =
(calculate_catch_width(map_attributes.cs as f32) / 2.0 / ALLOWED_CATCH_RANGE) as f64;
@@ -108,8 +108,8 @@ impl<'map> FruitsGradualDifficultyAttributes<'map> {
}
}
impl Iterator for FruitsGradualDifficultyAttributes<'_> {
type Item = FruitsDifficultyAttributes;
impl Iterator for CatchGradualDifficultyAttributes<'_> {
type Item = CatchDifficultyAttributes;
fn next(&mut self) -> Option<Self::Item> {
let curr = self.hit_objects.next()?;
@@ -165,14 +165,14 @@ impl Iterator for FruitsGradualDifficultyAttributes<'_> {
}
#[derive(Clone, Debug)]
struct FruitsObjectIter<'map> {
struct CatchObjectIter<'map> {
last_object: Option<FruitOrJuice>,
hit_objects: Iter<'map, HitObject>,
params: FruitParams<'map>,
}
impl<'map> FruitsObjectIter<'map> {
fn new(map: &'map Beatmap, mods: impl Mods, attributes: FruitsDifficultyAttributes) -> Self {
impl<'map> CatchObjectIter<'map> {
fn new(map: &'map Beatmap, mods: impl Mods, attributes: CatchDifficultyAttributes) -> Self {
let params = FruitParams {
attributes,
curve_bufs: CurveBuffers::default(),
@@ -191,12 +191,12 @@ impl<'map> FruitsObjectIter<'map> {
}
}
fn attributes(&self) -> FruitsDifficultyAttributes {
fn attributes(&self) -> CatchDifficultyAttributes {
self.params.attributes.clone()
}
}
impl Iterator for FruitsObjectIter<'_> {
impl Iterator for CatchObjectIter<'_> {
type Item = CatchObject;
fn next(&mut self) -> Option<Self::Item> {
@@ -221,7 +221,7 @@ mod tests {
#[test]
fn empty_map() {
let map = Beatmap::default();
let mut attributes = FruitsGradualDifficultyAttributes::new(&map, 0);
let mut attributes = CatchGradualDifficultyAttributes::new(&map, 0);
assert!(attributes.next().is_none());
}
@@ -230,9 +230,9 @@ mod tests {
fn iter_end_eq_regular() {
let map = Beatmap::from_path("./maps/2118524.osu").expect("failed to parse map");
let mods = 64;
let regular = crate::FruitsStars::new(&map).mods(mods).calculate();
let regular = crate::CatchStars::new(&map).mods(mods).calculate();
let iter_end = FruitsGradualDifficultyAttributes::new(&map, mods)
let iter_end = CatchGradualDifficultyAttributes::new(&map, mods)
.last()
.expect("empty iter");
@@ -1,13 +1,13 @@
use crate::{Beatmap, FruitsPP};
use crate::{Beatmap, CatchPP};
use super::{FruitsGradualDifficultyAttributes, FruitsPerformanceAttributes};
use super::{CatchGradualDifficultyAttributes, CatchPerformanceAttributes};
/// Aggregation for a score's current state i.e. what was the
/// maximum combo so far and what are the current hitresults.
///
/// This struct is used for [`FruitsGradualPerformanceAttributes`].
/// This struct is used for [`CatchGradualPerformanceAttributes`].
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FruitsScoreState {
pub struct CatchScoreState {
/// Maximum combo that the score has had so far.
/// **Not** the maximum possible combo of the map so far.
///
@@ -25,7 +25,7 @@ pub struct FruitsScoreState {
pub misses: usize,
}
impl FruitsScoreState {
impl CatchScoreState {
/// Create a new empty score state.
pub fn new() -> Self {
Self::default()
@@ -35,24 +35,24 @@ impl FruitsScoreState {
/// Gradually calculate the performance attributes of an osu!catch map.
///
/// After each hit object you can call
/// [`process_next_object`](`FruitsGradualPerformanceAttributes::process_next_object`)
/// and it will return the resulting current [`FruitsPerformanceAttributes`].
/// [`process_next_object`](`CatchGradualPerformanceAttributes::process_next_object`)
/// and it will return the resulting current [`CatchPerformanceAttributes`].
/// To process multiple objects at once, use
/// [`process_next_n_objects`](`FruitsGradualPerformanceAttributes::process_next_n_objects`) instead.
/// [`process_next_n_objects`](`CatchGradualPerformanceAttributes::process_next_n_objects`) instead.
///
/// Both methods require a [`FruitsScoreState`] that contains the current
/// Both methods require a [`CatchScoreState`] that contains the current
/// hitresults as well as the maximum combo so far.
///
/// Note that neither hits nor misses of tiny droplets require
/// to be processed. Only fruits and droplets do.
///
/// If you only want to calculate difficulty attributes use
/// [`FruitsGradualDifficultyAttributes`](crate::fruits::FruitsGradualDifficultyAttributes) instead.
/// [`CatchGradualDifficultyAttributes`](crate::catch::CatchGradualDifficultyAttributes) instead.
///
/// # Example
///
/// ```
/// use rosu_pp::{Beatmap, fruits::{FruitsGradualPerformanceAttributes, FruitsScoreState}};
/// use rosu_pp::{Beatmap, catch::{CatchGradualPerformanceAttributes, CatchScoreState}};
///
/// # /*
/// let map: Beatmap = ...
@@ -60,8 +60,8 @@ impl FruitsScoreState {
/// # let map = Beatmap::default();
///
/// let mods = 64; // DT
/// let mut gradual_perf = FruitsGradualPerformanceAttributes::new(&map, mods);
/// let mut state = FruitsScoreState::new(); // empty state, everything is on 0.
/// let mut gradual_perf = CatchGradualPerformanceAttributes::new(&map, mods);
/// let mut state = CatchScoreState::new(); // empty state, everything is on 0.
///
/// // The first 10 hitresults are only fruits
/// for _ in 0..10 {
@@ -125,16 +125,16 @@ impl FruitsScoreState {
/// assert!(gradual_perf.process_next_object(state).is_none());
/// ```
#[derive(Clone, Debug)]
pub struct FruitsGradualPerformanceAttributes<'map> {
difficulty: FruitsGradualDifficultyAttributes<'map>,
performance: FruitsPP<'map>,
pub struct CatchGradualPerformanceAttributes<'map> {
difficulty: CatchGradualDifficultyAttributes<'map>,
performance: CatchPP<'map>,
}
impl<'map> FruitsGradualPerformanceAttributes<'map> {
impl<'map> CatchGradualPerformanceAttributes<'map> {
/// Create a new gradual performance calculator for osu!standard maps.
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
let difficulty = FruitsGradualDifficultyAttributes::new(map, mods);
let performance = FruitsPP::new(map).mods(mods).passed_objects(0);
let difficulty = CatchGradualDifficultyAttributes::new(map, mods);
let performance = CatchPP::new(map).mods(mods).passed_objects(0);
Self {
difficulty,
@@ -149,12 +149,12 @@ impl<'map> FruitsGradualPerformanceAttributes<'map> {
/// to be processed. Only fruits and droplets do.
pub fn process_next_object(
&mut self,
state: FruitsScoreState,
) -> Option<FruitsPerformanceAttributes> {
state: CatchScoreState,
) -> Option<CatchPerformanceAttributes> {
self.process_next_n_objects(state, 1)
}
/// Same as [`process_next_object`](`FruitsGradualPerformanceAttributes::process_next_object`)
/// Same as [`process_next_object`](`CatchGradualPerformanceAttributes::process_next_object`)
/// but instead of processing only one object it process `n` many.
///
/// If `n` is 0 it will be considered as 1.
@@ -162,9 +162,9 @@ impl<'map> FruitsGradualPerformanceAttributes<'map> {
/// of remaining objects, `n` will be considered as the amount of remaining objects.
pub fn process_next_n_objects(
&mut self,
state: FruitsScoreState,
state: CatchScoreState,
n: usize,
) -> Option<FruitsPerformanceAttributes> {
) -> Option<CatchPerformanceAttributes> {
let mut difficulty = None;
for _ in 0..n.max(1) {
@@ -198,8 +198,8 @@ mod tests {
let map = Beatmap::from_path("./maps/2118524.osu").expect("failed to parse map");
let mods = 64;
let mut gradual = FruitsGradualPerformanceAttributes::new(&map, mods);
let state = FruitsScoreState::default();
let mut gradual = CatchGradualPerformanceAttributes::new(&map, mods);
let state = CatchScoreState::default();
assert!(gradual
.process_next_n_objects(state.clone(), usize::MAX)
@@ -212,10 +212,10 @@ mod tests {
fn next_and_next_n() {
let map = Beatmap::from_path("./maps/2118524.osu").expect("failed to parse map");
let mods = 64;
let state = FruitsScoreState::default();
let state = CatchScoreState::default();
let mut gradual1 = FruitsGradualPerformanceAttributes::new(&map, mods);
let mut gradual2 = FruitsGradualPerformanceAttributes::new(&map, mods);
let mut gradual1 = CatchGradualPerformanceAttributes::new(&map, mods);
let mut gradual2 = CatchGradualPerformanceAttributes::new(&map, mods);
for _ in 0..20 {
let _ = gradual1.process_next_object(state.clone());
@@ -228,7 +228,7 @@ mod tests {
let _ = gradual1.process_next_object(state.clone());
}
let state = FruitsScoreState {
let state = CatchScoreState {
max_combo: 101,
n_fruits: 99,
n_droplets: 2,
@@ -248,11 +248,11 @@ mod tests {
fn gradual_end_eq_regular() {
let map = Beatmap::from_path("./maps/2118524.osu").expect("failed to parse map");
let mods = 64;
let regular = FruitsPP::new(&map).mods(mods).calculate();
let regular = CatchPP::new(&map).mods(mods).calculate();
let mut gradual = FruitsGradualPerformanceAttributes::new(&map, mods);
let mut gradual = CatchGradualPerformanceAttributes::new(&map, mods);
let state = FruitsScoreState {
let state = CatchScoreState {
max_combo: 730,
n_fruits: 728,
n_droplets: 2,
@@ -272,11 +272,11 @@ mod tests {
let map = Beatmap::from_path("./maps/2118524.osu").expect("failed to parse map");
let mods = 64;
let n = 100;
let regular = FruitsPP::new(&map).mods(mods).passed_objects(n).calculate();
let regular = CatchPP::new(&map).mods(mods).passed_objects(n).calculate();
let mut gradual = FruitsGradualPerformanceAttributes::new(&map, mods);
let mut gradual = CatchGradualPerformanceAttributes::new(&map, mods);
let state = FruitsScoreState {
let state = CatchScoreState {
max_combo: 101,
n_fruits: 99,
n_droplets: 2,
+18 -18
View File
@@ -16,7 +16,7 @@ use movement::Movement;
pub use pp::*;
use slider_state::SliderState;
use crate::{curve::CurveBuffers, fruits::fruit_or_juice::FruitParams, Beatmap, Mods, Strains};
use crate::{catch::fruit_or_juice::FruitParams, curve::CurveBuffers, Beatmap, Mods, Strains};
const SECTION_LENGTH: f64 = 750.0;
const STAR_SCALING_FACTOR: f64 = 0.153;
@@ -29,28 +29,28 @@ const CATCHER_SIZE: f32 = 106.75;
/// # Example
///
/// ```
/// use rosu_pp::{FruitsStars, Beatmap};
/// use rosu_pp::{CatchStars, Beatmap};
///
/// # /*
/// let map: Beatmap = ...
/// # */
/// # let map = Beatmap::default();
///
/// let difficulty_attrs = FruitsStars::new(&map)
/// let difficulty_attrs = CatchStars::new(&map)
/// .mods(8 + 64) // HDDT
/// .calculate();
///
/// println!("Stars: {}", difficulty_attrs.stars);
/// ```
#[derive(Clone, Debug)]
pub struct FruitsStars<'map> {
pub struct CatchStars<'map> {
map: &'map Beatmap,
mods: u32,
passed_objects: Option<usize>,
clock_rate: Option<f64>,
}
impl<'map> FruitsStars<'map> {
impl<'map> CatchStars<'map> {
/// Create a new difficulty calculator for osu!catch maps.
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
@@ -75,8 +75,8 @@ impl<'map> FruitsStars<'map> {
/// Amount of passed objects for partial plays, e.g. a fail.
///
/// If you want to calculate the difficulty after every few objects, instead of
/// using [`FruitsStars`] multiple times with different `passed_objects`, you should use
/// [`FruitsGradualDifficultyAttributes`](crate::fruits::FruitsGradualDifficultyAttributes).
/// using [`CatchStars`] multiple times with different `passed_objects`, you should use
/// [`CatchGradualDifficultyAttributes`](crate::catch::CatchGradualDifficultyAttributes).
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects = Some(passed_objects);
@@ -96,7 +96,7 @@ impl<'map> FruitsStars<'map> {
/// Calculate all difficulty related values, including stars.
#[inline]
pub fn calculate(self) -> FruitsDifficultyAttributes {
pub fn calculate(self) -> CatchDifficultyAttributes {
let (mut movement, mut attributes) = calculate_movement(self);
attributes.stars =
Movement::difficulty_value(&mut movement.strain_peaks).sqrt() * STAR_SCALING_FACTOR;
@@ -119,8 +119,8 @@ impl<'map> FruitsStars<'map> {
}
}
fn calculate_movement(params: FruitsStars<'_>) -> (Movement, FruitsDifficultyAttributes) {
let FruitsStars {
fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttributes) {
let CatchStars {
map,
mods,
passed_objects,
@@ -132,7 +132,7 @@ fn calculate_movement(params: FruitsStars<'_>) -> (Movement, FruitsDifficultyAtt
let map_attributes = map.attributes().mods(mods);
let clock_rate = clock_rate.unwrap_or(map_attributes.clock_rate);
let attributes = FruitsDifficultyAttributes {
let attributes = CatchDifficultyAttributes {
ar: map_attributes.ar,
..Default::default()
};
@@ -223,7 +223,7 @@ pub(crate) fn calculate_catch_width(cs: f32) -> f32 {
/// The result of a difficulty calculation on an osu!catch map.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FruitsDifficultyAttributes {
pub struct CatchDifficultyAttributes {
/// The final star rating
pub stars: f64,
/// The approach rate.
@@ -236,7 +236,7 @@ pub struct FruitsDifficultyAttributes {
pub n_tiny_droplets: usize,
}
impl FruitsDifficultyAttributes {
impl CatchDifficultyAttributes {
/// Return the maximum combo.
#[inline]
pub fn max_combo(&self) -> usize {
@@ -246,14 +246,14 @@ impl FruitsDifficultyAttributes {
/// The result of a performance calculation on an osu!catch map.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct FruitsPerformanceAttributes {
pub struct CatchPerformanceAttributes {
/// The difficulty attributes that were used for the performance calculation
pub difficulty: FruitsDifficultyAttributes,
pub difficulty: CatchDifficultyAttributes,
/// The final performance points.
pub pp: f64,
}
impl FruitsPerformanceAttributes {
impl CatchPerformanceAttributes {
/// Return the star value.
#[inline]
pub fn stars(&self) -> f64 {
@@ -273,8 +273,8 @@ impl FruitsPerformanceAttributes {
}
}
impl From<FruitsPerformanceAttributes> for FruitsDifficultyAttributes {
fn from(attributes: FruitsPerformanceAttributes) -> Self {
impl From<CatchPerformanceAttributes> for CatchDifficultyAttributes {
fn from(attributes: CatchPerformanceAttributes) -> Self {
attributes.difficulty
}
}
+41 -43
View File
@@ -1,6 +1,4 @@
use super::{
FruitsDifficultyAttributes, FruitsPerformanceAttributes, FruitsScoreState, FruitsStars,
};
use super::{CatchDifficultyAttributes, CatchPerformanceAttributes, CatchScoreState, CatchStars};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// Performance calculator on osu!catch maps.
@@ -8,14 +6,14 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// # Example
///
/// ```
/// use rosu_pp::{FruitsPP, Beatmap};
/// use rosu_pp::{CatchPP, Beatmap};
///
/// # /*
/// let map: Beatmap = ...
/// # */
/// # let map = Beatmap::default();
///
/// let pp_result = FruitsPP::new(&map)
/// let pp_result = CatchPP::new(&map)
/// .mods(8 + 64) // HDDT
/// .combo(1234)
/// .misses(1)
@@ -24,7 +22,7 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
///
/// println!("PP: {} | Stars: {}", pp_result.pp(), pp_result.stars());
///
/// let next_result = FruitsPP::new(&map)
/// let next_result = CatchPP::new(&map)
/// .attributes(pp_result) // reusing previous results for performance
/// .mods(8 + 64) // has to be the same to reuse attributes
/// .accuracy(99.5)
@@ -34,9 +32,9 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
/// ```
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct FruitsPP<'map> {
pub struct CatchPP<'map> {
map: &'map Beatmap,
attributes: Option<FruitsDifficultyAttributes>,
attributes: Option<CatchDifficultyAttributes>,
mods: u32,
combo: Option<usize>,
@@ -49,7 +47,7 @@ pub struct FruitsPP<'map> {
clock_rate: Option<f64>,
}
impl<'map> FruitsPP<'map> {
impl<'map> CatchPP<'map> {
/// Create a new performance calculator for osu!catch maps.
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
@@ -73,7 +71,7 @@ impl<'map> FruitsPP<'map> {
/// If you already calculated the attributes for the current map-mod combination,
/// be sure to put them in here so that they don't have to be recalculated.
#[inline]
pub fn attributes(mut self, attributes: impl FruitsAttributeProvider) -> Self {
pub fn attributes(mut self, attributes: impl CatchAttributeProvider) -> Self {
if let Some(attributes) = attributes.attributes() {
self.attributes.replace(attributes);
}
@@ -142,8 +140,8 @@ impl<'map> FruitsPP<'map> {
/// Amount of passed objects for partial plays, e.g. a fail.
///
/// If you want to calculate the performance after every few objects, instead of
/// using [`FruitsPP`] multiple times with different `passed_objects`, you should use
/// [`FruitsGradualPerformanceAttributes`](crate::fruits::FruitsGradualPerformanceAttributes).
/// using [`CatchPP`] multiple times with different `passed_objects`, you should use
/// [`CatchGradualPerformanceAttributes`](crate::catch::CatchGradualPerformanceAttributes).
#[inline]
pub fn passed_objects(mut self, passed_objects: usize) -> Self {
self.passed_objects.replace(passed_objects);
@@ -161,10 +159,10 @@ impl<'map> FruitsPP<'map> {
self
}
/// Provide parameters through an [`FruitsScoreState`].
/// Provide parameters through an [`CatchScoreState`].
#[inline]
pub fn state(mut self, state: FruitsScoreState) -> Self {
let FruitsScoreState {
pub fn state(mut self, state: CatchScoreState) -> Self {
let CatchScoreState {
max_combo,
n_fruits,
n_droplets,
@@ -188,7 +186,7 @@ impl<'map> FruitsPP<'map> {
/// Be sure to set `misses` beforehand! Also, if available, set `attributes` beforehand.
pub fn accuracy(mut self, mut acc: f64) -> Self {
if self.attributes.is_none() {
let mut calculator = FruitsStars::new(self.map).mods(self.mods);
let mut calculator = CatchStars::new(self.map).mods(self.mods);
if let Some(passed_objects) = self.passed_objects {
calculator = calculator.passed_objects(passed_objects);
@@ -234,7 +232,7 @@ impl<'map> FruitsPP<'map> {
self
}
fn assert_hitresults(self, attributes: FruitsDifficultyAttributes) -> FruitsPPInner {
fn assert_hitresults(self, attributes: CatchDifficultyAttributes) -> CatchPPInner {
let max_combo = attributes.max_combo();
let correct_combo_hits = self
@@ -281,7 +279,7 @@ impl<'map> FruitsPP<'map> {
.saturating_sub(n_tiny_droplets)
.saturating_sub(n_tiny_droplet_misses);
return FruitsPPInner {
return CatchPPInner {
attributes,
mods: self.mods,
combo: self.combo,
@@ -293,7 +291,7 @@ impl<'map> FruitsPP<'map> {
};
}
FruitsPPInner {
CatchPPInner {
attributes,
mods: self.mods,
combo: self.combo,
@@ -306,9 +304,9 @@ impl<'map> FruitsPP<'map> {
}
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> FruitsPerformanceAttributes {
pub fn calculate(mut self) -> CatchPerformanceAttributes {
let attributes = self.attributes.take().unwrap_or_else(|| {
let mut calculator = FruitsStars::new(self.map).mods(self.mods);
let mut calculator = CatchStars::new(self.map).mods(self.mods);
if let Some(passed_objects) = self.passed_objects {
calculator = calculator.passed_objects(passed_objects);
@@ -325,8 +323,8 @@ impl<'map> FruitsPP<'map> {
}
}
struct FruitsPPInner {
attributes: FruitsDifficultyAttributes,
struct CatchPPInner {
attributes: CatchDifficultyAttributes,
mods: u32,
combo: Option<usize>,
n_fruits: usize,
@@ -336,8 +334,8 @@ struct FruitsPPInner {
n_misses: usize,
}
impl FruitsPPInner {
fn calculate(self) -> FruitsPerformanceAttributes {
impl CatchPPInner {
fn calculate(self) -> CatchPerformanceAttributes {
let attributes = &self.attributes;
let stars = attributes.stars;
let max_combo = attributes.max_combo();
@@ -398,7 +396,7 @@ impl FruitsPPInner {
pp *= 0.9;
}
FruitsPerformanceAttributes {
CatchPerformanceAttributes {
difficulty: self.attributes,
pp,
}
@@ -434,30 +432,30 @@ impl FruitsPPInner {
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait FruitsAttributeProvider {
pub trait CatchAttributeProvider {
/// Provide the actual difficulty attributes.
fn attributes(self) -> Option<FruitsDifficultyAttributes>;
fn attributes(self) -> Option<CatchDifficultyAttributes>;
}
impl FruitsAttributeProvider for FruitsDifficultyAttributes {
impl CatchAttributeProvider for CatchDifficultyAttributes {
#[inline]
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
fn attributes(self) -> Option<CatchDifficultyAttributes> {
Some(self)
}
}
impl FruitsAttributeProvider for FruitsPerformanceAttributes {
impl CatchAttributeProvider for CatchPerformanceAttributes {
#[inline]
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
fn attributes(self) -> Option<CatchDifficultyAttributes> {
Some(self.difficulty)
}
}
impl FruitsAttributeProvider for DifficultyAttributes {
impl CatchAttributeProvider for DifficultyAttributes {
#[inline]
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
fn attributes(self) -> Option<CatchDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Fruits(attributes) = self {
if let Self::Catch(attributes) = self {
Some(attributes)
} else {
None
@@ -465,11 +463,11 @@ impl FruitsAttributeProvider for DifficultyAttributes {
}
}
impl FruitsAttributeProvider for PerformanceAttributes {
impl CatchAttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> Option<FruitsDifficultyAttributes> {
fn attributes(self) -> Option<CatchDifficultyAttributes> {
#[allow(irrefutable_let_patterns)]
if let Self::Fruits(attributes) = self {
if let Self::Catch(attributes) = self {
Some(attributes.difficulty)
} else {
None
@@ -482,8 +480,8 @@ mod test {
use super::*;
use crate::Beatmap;
fn attributes() -> FruitsDifficultyAttributes {
FruitsDifficultyAttributes {
fn attributes() -> CatchDifficultyAttributes {
CatchDifficultyAttributes {
n_fruits: 1234,
n_droplets: 567,
n_tiny_droplets: 2345,
@@ -499,7 +497,7 @@ mod test {
let total_objects = attributes.n_fruits + attributes.n_droplets;
let target_acc = 97.5;
let calculator = FruitsPP::new(&map)
let calculator = CatchPP::new(&map)
.attributes(attributes)
.passed_objects(total_objects)
.accuracy(target_acc);
@@ -529,7 +527,7 @@ mod test {
let n_droplets = 550;
let n_tiny_droplets = 2222;
let calculator = FruitsPP::new(&map)
let calculator = CatchPP::new(&map)
.attributes(attributes)
.passed_objects(total_objects)
.droplets(n_droplets)
@@ -571,7 +569,7 @@ mod test {
let n_tiny_droplet_misses = 20;
let n_misses = 2;
let calculator = FruitsPP::new(&map)
let calculator = CatchPP::new(&map)
.attributes(attributes.clone())
.passed_objects(total_objects)
.fruits(n_fruits)
@@ -57,7 +57,7 @@ mod test {
use super::SliderState;
#[test]
fn fruits_slider_state() {
fn catch_slider_state() {
let map = Beatmap {
timing_points: vec![
TimingPoint {
+12 -14
View File
@@ -1,7 +1,5 @@
use crate::{
fruits::{
FruitsGradualDifficultyAttributes, FruitsGradualPerformanceAttributes, FruitsScoreState,
},
catch::{CatchGradualDifficultyAttributes, CatchGradualPerformanceAttributes, CatchScoreState},
mania::{ManiaGradualDifficultyAttributes, ManiaGradualPerformanceAttributes},
osu::{OsuGradualDifficultyAttributes, OsuGradualPerformanceAttributes, OsuScoreState},
taiko::{TaikoGradualDifficultyAttributes, TaikoGradualPerformanceAttributes, TaikoScoreState},
@@ -40,8 +38,8 @@ use crate::{
/// ```
#[derive(Clone, Debug)]
pub enum GradualDifficultyAttributes<'map> {
/// Gradual osu!fruits difficulty attributes.
Fruits(FruitsGradualDifficultyAttributes<'map>),
/// Gradual osu!catch difficulty attributes.
Catch(CatchGradualDifficultyAttributes<'map>),
/// Gradual osu!mania difficulty attributes.
Mania(ManiaGradualDifficultyAttributes<'map>),
/// Gradual osu!standard difficulty attributes.
@@ -56,7 +54,7 @@ impl<'map> GradualDifficultyAttributes<'map> {
match map.mode {
GameMode::STD => Self::Osu(OsuGradualDifficultyAttributes::new(map, mods)),
GameMode::TKO => Self::Taiko(TaikoGradualDifficultyAttributes::new(map, mods)),
GameMode::CTB => Self::Fruits(FruitsGradualDifficultyAttributes::new(map, mods)),
GameMode::CTB => Self::Catch(CatchGradualDifficultyAttributes::new(map, mods)),
GameMode::MNA => Self::Mania(ManiaGradualDifficultyAttributes::new(map, mods)),
}
}
@@ -68,7 +66,7 @@ impl Iterator for GradualDifficultyAttributes<'_> {
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {
GradualDifficultyAttributes::Fruits(f) => f.next().map(DifficultyAttributes::Fruits),
GradualDifficultyAttributes::Catch(f) => f.next().map(DifficultyAttributes::Catch),
GradualDifficultyAttributes::Mania(m) => m.next().map(DifficultyAttributes::Mania),
GradualDifficultyAttributes::Osu(o) => o.next().map(DifficultyAttributes::Osu),
GradualDifficultyAttributes::Taiko(t) => t.next().map(DifficultyAttributes::Taiko),
@@ -78,7 +76,7 @@ impl Iterator for GradualDifficultyAttributes<'_> {
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
GradualDifficultyAttributes::Fruits(f) => f.size_hint(),
GradualDifficultyAttributes::Catch(f) => f.size_hint(),
GradualDifficultyAttributes::Mania(m) => m.size_hint(),
GradualDifficultyAttributes::Osu(o) => o.size_hint(),
GradualDifficultyAttributes::Taiko(t) => t.size_hint(),
@@ -133,7 +131,7 @@ impl ScoreState {
}
}
impl From<ScoreState> for FruitsScoreState {
impl From<ScoreState> for CatchScoreState {
#[inline]
fn from(state: ScoreState) -> Self {
Self {
@@ -187,7 +185,7 @@ impl From<ScoreState> for TaikoScoreState {
///
/// Alternatively, you can match on the map's mode yourself and use the gradual
/// performance attribute struct for the corresponding mode, i.e.
/// [`FruitsGradualPerformanceAttributes`],
/// [`CatchGradualPerformanceAttributes`],
/// [`ManiaGradualPerformanceAttributes`],
/// [`OsuGradualPerformanceAttributes`], or
/// [`TaikoGradualPerformanceAttributes`].
@@ -272,7 +270,7 @@ impl From<ScoreState> for TaikoScoreState {
#[derive(Clone, Debug)]
pub enum GradualPerformanceAttributes<'map> {
/// Gradual osu!catch performance attributes.
Fruits(FruitsGradualPerformanceAttributes<'map>),
Catch(CatchGradualPerformanceAttributes<'map>),
/// Gradual osu!mania performance attributes.
Mania(ManiaGradualPerformanceAttributes<'map>),
/// Gradual osu!standard performance attributes.
@@ -287,7 +285,7 @@ impl<'map> GradualPerformanceAttributes<'map> {
match map.mode {
GameMode::STD => Self::Osu(OsuGradualPerformanceAttributes::new(map, mods)),
GameMode::TKO => Self::Taiko(TaikoGradualPerformanceAttributes::new(map, mods)),
GameMode::CTB => Self::Fruits(FruitsGradualPerformanceAttributes::new(map, mods)),
GameMode::CTB => Self::Catch(CatchGradualPerformanceAttributes::new(map, mods)),
GameMode::MNA => Self::Mania(ManiaGradualPerformanceAttributes::new(map, mods)),
}
}
@@ -310,9 +308,9 @@ impl<'map> GradualPerformanceAttributes<'map> {
n: usize,
) -> Option<PerformanceAttributes> {
match self {
GradualPerformanceAttributes::Fruits(f) => f
GradualPerformanceAttributes::Catch(f) => f
.process_next_n_objects(state.into(), n)
.map(PerformanceAttributes::Fruits),
.map(PerformanceAttributes::Catch),
GradualPerformanceAttributes::Mania(m) => m
.process_next_n_objects(state.score, n)
.map(PerformanceAttributes::Mania),
+21 -25
View File
@@ -163,11 +163,7 @@
//!
//! | Flag | Description |
//! |-----|-----|
//! | `default` | Enable all modes. |
//! | `osu` | Enable osu!standard. |
//! | `taiko` | Enable osu!taiko. |
//! | `fruits` | Enable osu!catch. |
//! | `mania` | Enable osu!mania. |
//! | `default` | Beatmap parsing will be non-async |
//! | `async_tokio` | Beatmap parsing will be async through [tokio](https://github.com/tokio-rs/tokio) |
//! | `async_std` | Beatmap parsing will be async through [async-std](https://github.com/async-rs/async-std) |
//!
@@ -184,7 +180,7 @@
)]
/// Everything about osu!catch.
pub mod fruits;
pub mod catch;
/// Everything about osu!mania.
pub mod mania;
@@ -214,7 +210,7 @@ pub(crate) mod control_point_iter;
pub(crate) use control_point_iter::{ControlPoint, ControlPointIter};
pub use fruits::{FruitsPP, FruitsStars};
pub use catch::{CatchPP, CatchStars};
pub use mania::{ManiaPP, ManiaStars};
pub use osu::{OsuPP, OsuStars};
pub use taiko::{TaikoPP, TaikoStars};
@@ -263,7 +259,7 @@ impl BeatmapExt for Beatmap {
GameMode::STD => AnyStars::Osu(OsuStars::new(self)),
GameMode::MNA => AnyStars::Mania(ManiaStars::new(self)),
GameMode::TKO => AnyStars::Taiko(TaikoStars::new(self)),
GameMode::CTB => AnyStars::Fruits(FruitsStars::new(self)),
GameMode::CTB => AnyStars::Catch(CatchStars::new(self)),
}
}
@@ -278,7 +274,7 @@ impl BeatmapExt for Beatmap {
PerformanceAttributes::Taiko(TaikoPP::new(self).mods(mods).calculate())
}
GameMode::CTB => {
PerformanceAttributes::Fruits(FruitsPP::new(self).mods(mods).calculate())
PerformanceAttributes::Catch(CatchPP::new(self).mods(mods).calculate())
}
}
}
@@ -294,7 +290,7 @@ impl BeatmapExt for Beatmap {
GameMode::STD => OsuStars::new(self).mods(mods).strains(),
GameMode::MNA => ManiaStars::new(self).mods(mods).strains(),
GameMode::TKO => TaikoStars::new(self).mods(mods).strains(),
GameMode::CTB => FruitsStars::new(self).mods(mods).strains(),
GameMode::CTB => CatchStars::new(self).mods(mods).strains(),
}
}
@@ -323,7 +319,7 @@ pub struct Strains {
#[derive(Clone, Debug)]
pub enum DifficultyAttributes {
/// osu!catch difficulty calculation reseult.
Fruits(fruits::FruitsDifficultyAttributes),
Catch(catch::CatchDifficultyAttributes),
/// osu!mania difficulty calculation reseult.
Mania(mania::ManiaDifficultyAttributes),
/// osu!standard difficulty calculation reseult.
@@ -337,7 +333,7 @@ impl DifficultyAttributes {
#[inline]
pub fn stars(&self) -> f64 {
match self {
Self::Fruits(attributes) => attributes.stars,
Self::Catch(attributes) => attributes.stars,
Self::Mania(attributes) => attributes.stars,
Self::Osu(attributes) => attributes.stars,
Self::Taiko(attributes) => attributes.stars,
@@ -350,7 +346,7 @@ impl DifficultyAttributes {
#[inline]
pub fn max_combo(&self) -> Option<usize> {
match self {
Self::Fruits(attributes) => Some(attributes.max_combo()),
Self::Catch(attributes) => Some(attributes.max_combo()),
Self::Mania(_) => None,
Self::Osu(attributes) => Some(attributes.max_combo),
Self::Taiko(attributes) => Some(attributes.max_combo),
@@ -358,10 +354,10 @@ impl DifficultyAttributes {
}
}
impl From<fruits::FruitsDifficultyAttributes> for DifficultyAttributes {
impl From<catch::CatchDifficultyAttributes> for DifficultyAttributes {
#[inline]
fn from(attributes: fruits::FruitsDifficultyAttributes) -> Self {
Self::Fruits(attributes)
fn from(attributes: catch::CatchDifficultyAttributes) -> Self {
Self::Catch(attributes)
}
}
@@ -390,7 +386,7 @@ impl From<taiko::TaikoDifficultyAttributes> for DifficultyAttributes {
#[derive(Clone, Debug)]
pub enum PerformanceAttributes {
/// osu!catch performance calculation result.
Fruits(fruits::FruitsPerformanceAttributes),
Catch(catch::CatchPerformanceAttributes),
/// osu!mania performance calculation result.
Mania(mania::ManiaPerformanceAttributes),
/// osu!standard performance calculation result.
@@ -404,7 +400,7 @@ impl PerformanceAttributes {
#[inline]
pub fn pp(&self) -> f64 {
match self {
Self::Fruits(attributes) => attributes.pp,
Self::Catch(attributes) => attributes.pp,
Self::Mania(attributes) => attributes.pp,
Self::Osu(attributes) => attributes.pp,
Self::Taiko(attributes) => attributes.pp,
@@ -415,7 +411,7 @@ impl PerformanceAttributes {
#[inline]
pub fn stars(&self) -> f64 {
match self {
Self::Fruits(attributes) => attributes.stars(),
Self::Catch(attributes) => attributes.stars(),
Self::Mania(attributes) => attributes.stars(),
Self::Osu(attributes) => attributes.stars(),
Self::Taiko(attributes) => attributes.stars(),
@@ -426,7 +422,7 @@ impl PerformanceAttributes {
#[inline]
pub fn difficulty_attributes(&self) -> DifficultyAttributes {
match self {
Self::Fruits(attributes) => DifficultyAttributes::Fruits(attributes.difficulty.clone()),
Self::Catch(attributes) => DifficultyAttributes::Catch(attributes.difficulty.clone()),
Self::Mania(attributes) => DifficultyAttributes::Mania(attributes.difficulty),
Self::Osu(attributes) => DifficultyAttributes::Osu(attributes.difficulty.clone()),
Self::Taiko(attributes) => DifficultyAttributes::Taiko(attributes.difficulty),
@@ -439,7 +435,7 @@ impl PerformanceAttributes {
/// This will only be `None` for attributes of osu!mania maps.
pub fn max_combo(&self) -> Option<usize> {
match self {
Self::Fruits(f) => Some(f.difficulty.max_combo()),
Self::Catch(f) => Some(f.difficulty.max_combo()),
Self::Mania(_) => None,
Self::Osu(o) => Some(o.difficulty.max_combo),
Self::Taiko(t) => Some(t.difficulty.max_combo),
@@ -450,7 +446,7 @@ impl PerformanceAttributes {
impl From<PerformanceAttributes> for DifficultyAttributes {
fn from(attributes: PerformanceAttributes) -> Self {
match attributes {
PerformanceAttributes::Fruits(attributes) => Self::Fruits(attributes.difficulty),
PerformanceAttributes::Catch(attributes) => Self::Catch(attributes.difficulty),
PerformanceAttributes::Mania(attributes) => Self::Mania(attributes.difficulty),
PerformanceAttributes::Osu(attributes) => Self::Osu(attributes.difficulty),
PerformanceAttributes::Taiko(attributes) => Self::Taiko(attributes.difficulty),
@@ -458,10 +454,10 @@ impl From<PerformanceAttributes> for DifficultyAttributes {
}
}
impl From<fruits::FruitsPerformanceAttributes> for PerformanceAttributes {
impl From<catch::CatchPerformanceAttributes> for PerformanceAttributes {
#[inline]
fn from(attributes: fruits::FruitsPerformanceAttributes) -> Self {
Self::Fruits(attributes)
fn from(attributes: catch::CatchPerformanceAttributes) -> Self {
Self::Catch(attributes)
}
}
+1 -1
View File
@@ -1033,7 +1033,7 @@ mod tests {
vec![
2785319, // osu
1974394, // mania
2118524, // fruits
2118524, // catch
1028484, // taiko
]
}
+19 -22
View File
@@ -1,5 +1,5 @@
use crate::{
fruits::{FruitsDifficultyAttributes, FruitsPP, FruitsPerformanceAttributes},
catch::{CatchDifficultyAttributes, CatchPP, CatchPerformanceAttributes},
mania::{ManiaDifficultyAttributes, ManiaPP, ManiaPerformanceAttributes},
osu::{OsuDifficultyAttributes, OsuPP, OsuPerformanceAttributes},
taiko::{TaikoDifficultyAttributes, TaikoPP, TaikoPerformanceAttributes},
@@ -39,7 +39,7 @@ use crate::{
#[derive(Clone, Debug)]
pub enum AnyPP<'map> {
/// osu!catch performance calculator
Fruits(FruitsPP<'map>),
Catch(CatchPP<'map>),
/// osu!mania performance calculator
Mania(ManiaPP<'map>),
/// osu!standard performance calculator
@@ -53,7 +53,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
match map.mode {
GameMode::CTB => Self::Fruits(FruitsPP::new(map)),
GameMode::CTB => Self::Catch(CatchPP::new(map)),
GameMode::MNA => Self::Mania(ManiaPP::new(map)),
GameMode::STD => Self::Osu(OsuPP::new(map)),
GameMode::TKO => Self::Taiko(TaikoPP::new(map)),
@@ -65,7 +65,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn calculate(self) -> PerformanceAttributes {
match self {
Self::Fruits(f) => PerformanceAttributes::Fruits(f.calculate()),
Self::Catch(f) => PerformanceAttributes::Catch(f.calculate()),
Self::Mania(m) => PerformanceAttributes::Mania(m.calculate()),
Self::Osu(o) => PerformanceAttributes::Osu(o.calculate()),
Self::Taiko(t) => PerformanceAttributes::Taiko(t.calculate()),
@@ -78,7 +78,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn attributes(self, attributes: impl AttributeProvider) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.attributes(attributes.attributes())),
Self::Catch(f) => Self::Catch(f.attributes(attributes.attributes())),
Self::Mania(m) => Self::Mania(m.attributes(attributes.attributes())),
Self::Osu(o) => Self::Osu(o.attributes(attributes.attributes())),
Self::Taiko(t) => Self::Taiko(t.attributes(attributes.attributes())),
@@ -91,7 +91,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn mods(self, mods: u32) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.mods(mods)),
Self::Catch(f) => Self::Catch(f.mods(mods)),
Self::Mania(m) => Self::Mania(m.mods(mods)),
Self::Osu(o) => Self::Osu(o.mods(mods)),
Self::Taiko(t) => Self::Taiko(t.mods(mods)),
@@ -106,7 +106,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn passed_objects(self, passed_objects: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.passed_objects(passed_objects)),
Self::Catch(f) => Self::Catch(f.passed_objects(passed_objects)),
Self::Mania(m) => Self::Mania(m.passed_objects(passed_objects)),
Self::Osu(o) => Self::Osu(o.passed_objects(passed_objects)),
Self::Taiko(t) => Self::Taiko(t.passed_objects(passed_objects)),
@@ -119,7 +119,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn clock_rate(self, clock_rate: f64) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.clock_rate(clock_rate)),
Self::Catch(f) => Self::Catch(f.clock_rate(clock_rate)),
Self::Mania(m) => Self::Mania(m.clock_rate(clock_rate)),
Self::Osu(o) => Self::Osu(o.clock_rate(clock_rate)),
Self::Taiko(t) => Self::Taiko(t.clock_rate(clock_rate)),
@@ -130,7 +130,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn state(self, state: ScoreState) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.state(state.into())),
Self::Catch(f) => Self::Catch(f.state(state.into())),
Self::Mania(m) => Self::Mania(m.score(state.score)),
Self::Osu(o) => Self::Osu(o.state(state.into())),
Self::Taiko(t) => Self::Taiko(t.state(state.into())),
@@ -147,7 +147,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn accuracy(self, acc: f64) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.accuracy(acc)),
Self::Catch(f) => Self::Catch(f.accuracy(acc)),
Self::Mania(_) => self,
Self::Osu(o) => Self::Osu(o.accuracy(acc)),
Self::Taiko(t) => Self::Taiko(t.accuracy(acc)),
@@ -161,7 +161,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn misses(self, misses: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.misses(misses)),
Self::Catch(f) => Self::Catch(f.misses(misses)),
Self::Mania(_) => self,
Self::Osu(o) => Self::Osu(o.misses(misses)),
Self::Taiko(t) => Self::Taiko(t.misses(misses)),
@@ -175,7 +175,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn combo(self, combo: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.combo(combo)),
Self::Catch(f) => Self::Catch(f.combo(combo)),
Self::Mania(_) => self,
Self::Osu(o) => Self::Osu(o.combo(combo)),
Self::Taiko(t) => Self::Taiko(t.combo(combo)),
@@ -189,7 +189,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn n300(self, n300: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.fruits(n300)),
Self::Catch(f) => Self::Catch(f.fruits(n300)),
Self::Mania(_) => self,
Self::Osu(o) => Self::Osu(o.n300(n300)),
Self::Taiko(t) => Self::Taiko(t.n300(n300)),
@@ -203,7 +203,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn n100(self, n100: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.droplets(n100)),
Self::Catch(f) => Self::Catch(f.droplets(n100)),
Self::Mania(_) => self,
Self::Osu(o) => Self::Osu(o.n100(n100)),
Self::Taiko(t) => Self::Taiko(t.n100(n100)),
@@ -217,7 +217,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn n50(self, n50: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.tiny_droplets(n50)),
Self::Catch(f) => Self::Catch(f.tiny_droplets(n50)),
Self::Mania(_) => self,
Self::Osu(o) => Self::Osu(o.n50(n50)),
Self::Taiko(_) => self,
@@ -232,7 +232,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn n_katu(self, n_katu: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.tiny_droplet_misses(n_katu)),
Self::Catch(f) => Self::Catch(f.tiny_droplet_misses(n_katu)),
Self::Mania(_) => self,
Self::Osu(_) => self,
Self::Taiko(_) => self,
@@ -247,7 +247,7 @@ impl<'map> AnyPP<'map> {
#[inline]
pub fn score(self, score: u32) -> Self {
match self {
Self::Fruits(_) => self,
Self::Catch(_) => self,
Self::Mania(m) => Self::Mania(m.score(score)),
Self::Osu(_) => self,
Self::Taiko(_) => self,
@@ -272,7 +272,7 @@ impl AttributeProvider for PerformanceAttributes {
#[inline]
fn attributes(self) -> DifficultyAttributes {
match self {
Self::Fruits(f) => DifficultyAttributes::Fruits(f.difficulty),
Self::Catch(f) => DifficultyAttributes::Catch(f.difficulty),
Self::Mania(m) => DifficultyAttributes::Mania(m.difficulty),
Self::Osu(o) => DifficultyAttributes::Osu(o.difficulty),
Self::Taiko(t) => DifficultyAttributes::Taiko(t.difficulty),
@@ -298,10 +298,7 @@ macro_rules! impl_attr_provider {
};
}
impl_attr_provider!(
Fruits: FruitsDifficultyAttributes,
FruitsPerformanceAttributes
);
impl_attr_provider!(Catch: CatchDifficultyAttributes, CatchPerformanceAttributes);
impl_attr_provider!(Mania: ManiaDifficultyAttributes, ManiaPerformanceAttributes);
impl_attr_provider!(Osu: OsuDifficultyAttributes, OsuPerformanceAttributes);
impl_attr_provider!(Taiko: TaikoDifficultyAttributes, TaikoPerformanceAttributes);
+8 -8
View File
@@ -1,5 +1,5 @@
use crate::{
Beatmap, DifficultyAttributes, FruitsStars, GameMode, ManiaStars, OsuStars, Strains, TaikoStars,
Beatmap, CatchStars, DifficultyAttributes, GameMode, ManiaStars, OsuStars, Strains, TaikoStars,
};
/// Difficulty calculator on maps of any mode.
@@ -23,7 +23,7 @@ use crate::{
#[derive(Clone, Debug)]
pub enum AnyStars<'map> {
/// osu!catch difficulty calculator
Fruits(FruitsStars<'map>),
Catch(CatchStars<'map>),
/// osu!mania difficulty calculator
Mania(ManiaStars<'map>),
/// osu!standard difficulty calculator
@@ -37,7 +37,7 @@ impl<'map> AnyStars<'map> {
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
match map.mode {
GameMode::CTB => Self::Fruits(FruitsStars::new(map)),
GameMode::CTB => Self::Catch(CatchStars::new(map)),
GameMode::MNA => Self::Mania(ManiaStars::new(map)),
GameMode::STD => Self::Osu(OsuStars::new(map)),
GameMode::TKO => Self::Taiko(TaikoStars::new(map)),
@@ -50,7 +50,7 @@ impl<'map> AnyStars<'map> {
#[inline]
pub fn mods(self, mods: u32) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.mods(mods)),
Self::Catch(f) => Self::Catch(f.mods(mods)),
Self::Mania(m) => Self::Mania(m.mods(mods)),
Self::Osu(o) => Self::Osu(o.mods(mods)),
Self::Taiko(t) => Self::Taiko(t.mods(mods)),
@@ -65,7 +65,7 @@ impl<'map> AnyStars<'map> {
#[inline]
pub fn passed_objects(self, passed_objects: usize) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.passed_objects(passed_objects)),
Self::Catch(f) => Self::Catch(f.passed_objects(passed_objects)),
Self::Mania(m) => Self::Mania(m.passed_objects(passed_objects)),
Self::Osu(o) => Self::Osu(o.passed_objects(passed_objects)),
Self::Taiko(t) => Self::Taiko(t.passed_objects(passed_objects)),
@@ -78,7 +78,7 @@ impl<'map> AnyStars<'map> {
#[inline]
pub fn clock_rate(self, clock_rate: f64) -> Self {
match self {
Self::Fruits(f) => Self::Fruits(f.clock_rate(clock_rate)),
Self::Catch(f) => Self::Catch(f.clock_rate(clock_rate)),
Self::Mania(m) => Self::Mania(m.clock_rate(clock_rate)),
Self::Osu(o) => Self::Osu(o.clock_rate(clock_rate)),
Self::Taiko(t) => Self::Taiko(t.clock_rate(clock_rate)),
@@ -90,7 +90,7 @@ impl<'map> AnyStars<'map> {
#[inline]
pub fn calculate(self) -> DifficultyAttributes {
match self {
Self::Fruits(f) => DifficultyAttributes::Fruits(f.calculate()),
Self::Catch(f) => DifficultyAttributes::Catch(f.calculate()),
Self::Mania(m) => DifficultyAttributes::Mania(m.calculate()),
Self::Osu(o) => DifficultyAttributes::Osu(o.calculate()),
Self::Taiko(t) => DifficultyAttributes::Taiko(t.calculate()),
@@ -104,7 +104,7 @@ impl<'map> AnyStars<'map> {
#[inline]
pub fn strains(self) -> Strains {
match self {
Self::Fruits(f) => f.strains(),
Self::Catch(f) => f.strains(),
Self::Mania(m) => m.strains(),
Self::Osu(o) => o.strains(),
Self::Taiko(t) => t.strains(),