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