renamed fruits to catch
This commit is contained in:
+2
-1
@@ -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
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ default = []
|
||||
async_std = ["async-std"]
|
||||
async_tokio = ["tokio"]
|
||||
|
||||
|
||||
[dependencies.async-std]
|
||||
version = "1.9"
|
||||
optional = true
|
||||
|
||||
@@ -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
@@ -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,
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -1033,7 +1033,7 @@ mod tests {
|
||||
vec![
|
||||
2785319, // osu
|
||||
1974394, // mania
|
||||
2118524, // fruits
|
||||
2118524, // catch
|
||||
1028484, // taiko
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user