added GradualDifficultyAttributes and GradualPerformanceAttributes
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
- Added methods `TaikoDifficultyAttributes::max_combo` and `OsuDifficultyAttributes::max_combo`
|
||||
- Added structs `{Mode}GradualDifficultyAttributes`. Suitable to calculate a map's difficulty after every or every few objects instead of calling the mode's `stars` function over and over.
|
||||
- Added structs `{Mode}GradualPerformanceAttributes`. Suitable to calculate the performance on a map after every or every few objects instead of using `{Mode}PP` over and over.
|
||||
- Added `BeatmapExt::gradual_difficulty` and `BeatmapExt::gradual_performance` to gradually calculate the difficulty or performance on maps of any mode.
|
||||
|
||||
# v0.3.0
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ use super::{FruitsGradualDifficultyAttributes, FruitsPerformanceAttributes};
|
||||
// TODO: Benchmark if Copy is faster than Clone
|
||||
/// 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`].
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct FruitsScoreState {
|
||||
/// Maximum combo that the score has had so far.
|
||||
@@ -39,7 +41,7 @@ impl FruitsScoreState {
|
||||
/// To process multiple objects at once, use
|
||||
/// [`process_next_n_objects`](`FruitsGradualPerformanceAttributes::process_next_n_objects`) instead.
|
||||
///
|
||||
/// Both methods require an [`FruitsScoreState`] that contains the current
|
||||
/// Both methods require a [`FruitsScoreState`] that contains the current
|
||||
/// hitresults as well as the maximum combo so far.
|
||||
///
|
||||
/// Note that neither hits nor misses of tiny droplets require
|
||||
|
||||
+372
@@ -0,0 +1,372 @@
|
||||
use crate::{
|
||||
fruits::{
|
||||
FruitsGradualDifficultyAttributes, FruitsGradualPerformanceAttributes, FruitsScoreState,
|
||||
},
|
||||
mania::{ManiaGradualDifficultyAttributes, ManiaGradualPerformanceAttributes},
|
||||
osu::{OsuGradualDifficultyAttributes, OsuGradualPerformanceAttributes, OsuScoreState},
|
||||
taiko::{TaikoGradualDifficultyAttributes, TaikoGradualPerformanceAttributes, TaikoScoreState},
|
||||
Beatmap, DifficultyAttributes, GameMode, Mods, PerformanceAttributes,
|
||||
};
|
||||
|
||||
/// Gradually calculate the difficulty attributes on maps of any mode.
|
||||
///
|
||||
/// Note that this struct implements [`Iterator`](std::iter::Iterator).
|
||||
/// On every call of [`Iterator::next`](std::iter::Iterator::next), the map's next hit object will
|
||||
/// be processed and the [`DifficultyAttributes`] will be updated and returned.
|
||||
///
|
||||
/// If you want to calculate performance attributes, use
|
||||
/// [`GradualPerformanceAttributes`](crate::GradualPerformanceAttributes) instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rosu_pp::{Beatmap, GradualDifficultyAttributes};
|
||||
///
|
||||
/// # /*
|
||||
/// let map: Beatmap = ...
|
||||
/// # */
|
||||
/// # let map = Beatmap::default();
|
||||
///
|
||||
/// let mods = 64; // DT
|
||||
/// let mut iter = GradualDifficultyAttributes::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
|
||||
///
|
||||
/// // Remaining hit objects
|
||||
/// for difficulty in iter {
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GradualDifficultyAttributes<'map> {
|
||||
#[cfg(feature = "fruits")]
|
||||
/// Gradual osu!fruits difficulty attributes.
|
||||
Fruits(FruitsGradualDifficultyAttributes<'map>),
|
||||
#[cfg(feature = "mania")]
|
||||
/// Gradual osu!mania difficulty attributes.
|
||||
Mania(ManiaGradualDifficultyAttributes<'map>),
|
||||
#[cfg(feature = "osu")]
|
||||
/// Gradual osu!standard difficulty attributes.
|
||||
Osu(OsuGradualDifficultyAttributes),
|
||||
#[cfg(feature = "taiko")]
|
||||
/// Gradual osu!taiko difficulty attributes.
|
||||
Taiko(TaikoGradualDifficultyAttributes<'map>),
|
||||
}
|
||||
|
||||
impl<'map> GradualDifficultyAttributes<'map> {
|
||||
/// Create a new gradual difficulty calculator for maps of any mode.
|
||||
pub fn new(map: &'map Beatmap, mods: impl Mods) -> Self {
|
||||
match map.mode {
|
||||
#[cfg(feature = "osu")]
|
||||
GameMode::STD => Self::Osu(OsuGradualDifficultyAttributes::new(map, mods)),
|
||||
#[cfg(feature = "taiko")]
|
||||
GameMode::TKO => Self::Taiko(TaikoGradualDifficultyAttributes::new(map, mods)),
|
||||
#[cfg(feature = "fruits")]
|
||||
GameMode::CTB => Self::Fruits(FruitsGradualDifficultyAttributes::new(map, mods)),
|
||||
#[cfg(feature = "mania")]
|
||||
GameMode::MNA => Self::Mania(ManiaGradualDifficultyAttributes::new(map, mods)),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("feature for mode {:?} is not enabled", map.mode),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for GradualDifficultyAttributes<'_> {
|
||||
type Item = DifficultyAttributes;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
match self {
|
||||
#[cfg(feature = "fruits")]
|
||||
GradualDifficultyAttributes::Fruits(f) => f.next().map(DifficultyAttributes::Fruits),
|
||||
#[cfg(feature = "mania")]
|
||||
GradualDifficultyAttributes::Mania(m) => m.next().map(DifficultyAttributes::Mania),
|
||||
#[cfg(feature = "osu")]
|
||||
GradualDifficultyAttributes::Osu(o) => o.next().map(DifficultyAttributes::Osu),
|
||||
#[cfg(feature = "taiko")]
|
||||
GradualDifficultyAttributes::Taiko(t) => t.next().map(DifficultyAttributes::Taiko),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
match self {
|
||||
#[cfg(feature = "fruits")]
|
||||
GradualDifficultyAttributes::Fruits(f) => f.size_hint(),
|
||||
#[cfg(feature = "mania")]
|
||||
GradualDifficultyAttributes::Mania(m) => m.size_hint(),
|
||||
#[cfg(feature = "osu")]
|
||||
GradualDifficultyAttributes::Osu(o) => o.size_hint(),
|
||||
#[cfg(feature = "taiko")]
|
||||
GradualDifficultyAttributes::Taiko(t) => t.size_hint(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Benchmark if Copy is faster than Clone
|
||||
/// Aggregation for a score's current state i.e. what wa
|
||||
/// the maximum combo so far, what are the current
|
||||
/// hitresults and what is the current score.
|
||||
///
|
||||
/// This struct is used for [`GradualPerformanceAttributes`].
|
||||
///
|
||||
/// Depending on your mode features, some fields might be optimized out.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct ScoreState {
|
||||
#[cfg(any(feature = "osu", feature = "fruits", feature = "taiko"))]
|
||||
/// Maximum combo that the score has had so far.
|
||||
/// **Not** the maximum possible combo of the map so far.
|
||||
///
|
||||
/// Note that for osu!ctb only fruits and droplets are considered for combo.
|
||||
///
|
||||
/// Irrelevant for osu!mania.
|
||||
pub max_combo: usize,
|
||||
#[cfg(feature = "fruits")]
|
||||
/// Amount of current katus (tiny droplet misses for osu!ctb).
|
||||
///
|
||||
/// Only relevant for osu!ctb.
|
||||
pub n_katu: usize,
|
||||
#[cfg(any(feature = "osu", feature = "fruits", feature = "taiko"))]
|
||||
/// Amount of current 300s (fruits for osu!ctb).
|
||||
///
|
||||
/// Irrelevant for osu!mania.
|
||||
pub n300: usize,
|
||||
#[cfg(any(feature = "osu", feature = "fruits", feature = "taiko"))]
|
||||
/// Amount of current 100s (droplets for osu!ctb).
|
||||
///
|
||||
/// Irrelevant for osu!mania.
|
||||
pub n100: usize,
|
||||
#[cfg(any(feature = "osu", feature = "fruits"))]
|
||||
/// Amount of current 50s (tiny droplets for osu!ctb).
|
||||
///
|
||||
/// Irrelevant for osu!taiko and osu!mania.
|
||||
pub n50: usize,
|
||||
#[cfg(any(feature = "osu", feature = "fruits", feature = "taiko"))]
|
||||
/// Amount of current misses (fruits + droplets for osu!ctb).
|
||||
///
|
||||
/// Irrelevant for osu!mania.
|
||||
pub misses: usize,
|
||||
#[cfg(feature = "mania")]
|
||||
/// The current score.
|
||||
///
|
||||
/// Only relevant for osu!mania.
|
||||
pub score: u32,
|
||||
}
|
||||
|
||||
impl ScoreState {
|
||||
/// Create a new empty score state.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "fruits")]
|
||||
impl From<ScoreState> for FruitsScoreState {
|
||||
#[inline]
|
||||
fn from(state: ScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: state.max_combo,
|
||||
n_fruits: state.n300,
|
||||
n_droplets: state.n100,
|
||||
n_tiny_droplets: state.n50,
|
||||
n_tiny_droplet_misses: state.n_katu,
|
||||
misses: state.misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "osu")]
|
||||
impl From<ScoreState> for OsuScoreState {
|
||||
#[inline]
|
||||
fn from(state: ScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: state.max_combo,
|
||||
n300: state.n300,
|
||||
n100: state.n100,
|
||||
n50: state.n50,
|
||||
misses: state.misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "taiko")]
|
||||
impl From<ScoreState> for TaikoScoreState {
|
||||
#[inline]
|
||||
fn from(state: ScoreState) -> Self {
|
||||
Self {
|
||||
max_combo: state.max_combo,
|
||||
n300: state.n300,
|
||||
n100: state.n100,
|
||||
misses: state.misses,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gradually calculate the performance attributes on maps of any mode.
|
||||
///
|
||||
/// After each hit object you can call
|
||||
/// [`process_next_object`](`GradualPerformanceAttributes::process_next_object`)
|
||||
/// and it will return the resulting current [`PerformanceAttributes`].
|
||||
/// To process multiple objects at once, use
|
||||
/// [`process_next_n_objects`](`GradualPerformanceAttributes::process_next_n_objects`) instead.
|
||||
///
|
||||
/// Both methods require a [`ScoreState`] that contains the current hitresults
|
||||
/// as well as the maximum combo so far or just the current score for osu!mania.
|
||||
/// Since the map could have any mode, all fields of `ScoreState` could be of use
|
||||
/// and should be updated properly.
|
||||
///
|
||||
/// Alternatively, you can match on the map's mode yourself and use the gradual
|
||||
/// performance attribute struct for the corresponding mode, i.e.
|
||||
/// [`FruitsGradualPerformanceAttributes`],
|
||||
/// [`ManiaGradualPerformanceAttributes`],
|
||||
/// [`OsuGradualPerformanceAttributes`], or
|
||||
/// [`TaikoGradualPerformanceAttributes`].
|
||||
///
|
||||
/// If you only want to calculate difficulty attributes use
|
||||
/// [`GradualDifficultyAttributes`](crate::GradualDifficultyAttributes) instead.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use rosu_pp::{Beatmap, GradualPerformanceAttributes, ScoreState};
|
||||
///
|
||||
/// # /*
|
||||
/// let map: Beatmap = ...
|
||||
/// # */
|
||||
/// # let map = Beatmap::default();
|
||||
///
|
||||
/// let mods = 64; // DT
|
||||
/// let mut gradual_perf = GradualPerformanceAttributes::new(&map, mods);
|
||||
/// let mut state = ScoreState::new(); // empty state, everything is on 0.
|
||||
///
|
||||
/// // The first 10 hitresults are 300s and increase the score by 123 each.
|
||||
/// for _ in 0..10 {
|
||||
/// state.n300 += 1;
|
||||
/// state.max_combo += 1;
|
||||
/// state.score += 123;
|
||||
///
|
||||
/// # /*
|
||||
/// let performance = gradual_perf.process_next_object(state).unwrap();
|
||||
/// println!("PP: {}", performance.pp);
|
||||
/// # */
|
||||
/// # let _ = gradual_perf.process_next_object(state);
|
||||
/// }
|
||||
///
|
||||
/// // Then comes a miss.
|
||||
/// // Note that state's max combo won't be incremented for
|
||||
/// // the next few objects because the combo is reset.
|
||||
/// state.misses += 1;
|
||||
/// # /*
|
||||
/// let performance = gradual_perf.process_next_object(state).unwrap();
|
||||
/// println!("PP: {}", performance.pp);
|
||||
/// # */
|
||||
/// # let _ = gradual_perf.process_next_object(state);
|
||||
///
|
||||
/// // The next 10 objects will be a mixture of 300s, 100s, and 50s.
|
||||
/// // Notice how all 10 objects will be processed in one go.
|
||||
/// state.n300 += 2;
|
||||
/// state.n100 += 7;
|
||||
/// state.n50 += 1;
|
||||
/// state.score += 987;
|
||||
/// // Don't forget state.n_katu
|
||||
/// # /*
|
||||
/// let performance = gradual_perf.process_next_n_objects(state, 10).unwrap();
|
||||
/// println!("PP: {}", performance.pp);
|
||||
/// # */
|
||||
/// # let _ = gradual_perf.process_next_n_objects(state, 10);
|
||||
///
|
||||
/// // Now comes another 300. Note that the max combo gets incremented again.
|
||||
/// state.n300 += 1;
|
||||
/// state.max_combo += 1;
|
||||
/// state.score += 123;
|
||||
/// # /*
|
||||
/// let performance = gradual_perf.process_next_object(state).unwrap();
|
||||
/// println!("PP: {}", performance.pp);
|
||||
/// # */
|
||||
/// # let _ = gradual_perf.process_next_object(state);
|
||||
///
|
||||
/// // Skip to the end
|
||||
/// # /*
|
||||
/// state.max_combo = ...
|
||||
/// state.n300 = ...
|
||||
/// ...
|
||||
/// let final_performance = gradual_perf.process_next_n_objects(state, usize::MAX).unwrap();
|
||||
/// println!("PP: {}", performance.pp);
|
||||
/// # */
|
||||
/// # let _ = gradual_perf.process_next_n_objects(state, usize::MAX);
|
||||
///
|
||||
/// // Once the final performance was calculated,
|
||||
/// // attempting to process further objects will return `None`.
|
||||
/// assert!(gradual_perf.process_next_object(state).is_none());
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum GradualPerformanceAttributes<'map> {
|
||||
#[cfg(feature = "fruits")]
|
||||
/// Gradual osu!ctb performance attributes.
|
||||
Fruits(FruitsGradualPerformanceAttributes<'map>),
|
||||
#[cfg(feature = "mania")]
|
||||
/// Gradual osu!mania performance attributes.
|
||||
Mania(ManiaGradualPerformanceAttributes<'map>),
|
||||
#[cfg(feature = "osu")]
|
||||
/// Gradual osu!standard performance attributes.
|
||||
Osu(OsuGradualPerformanceAttributes<'map>),
|
||||
#[cfg(feature = "taiko")]
|
||||
/// Gradual osu!taiko performance attributes.
|
||||
Taiko(TaikoGradualPerformanceAttributes<'map>),
|
||||
}
|
||||
|
||||
impl<'map> GradualPerformanceAttributes<'map> {
|
||||
/// Create a new gradual performance calculator for maps of any mode.
|
||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||
match map.mode {
|
||||
#[cfg(feature = "osu")]
|
||||
GameMode::STD => Self::Osu(OsuGradualPerformanceAttributes::new(map, mods)),
|
||||
#[cfg(feature = "taiko")]
|
||||
GameMode::TKO => Self::Taiko(TaikoGradualPerformanceAttributes::new(map, mods)),
|
||||
#[cfg(feature = "fruits")]
|
||||
GameMode::CTB => Self::Fruits(FruitsGradualPerformanceAttributes::new(map, mods)),
|
||||
#[cfg(feature = "mania")]
|
||||
GameMode::MNA => Self::Mania(ManiaGradualPerformanceAttributes::new(map, mods)),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => panic!("feature for mode {:?} is not enabled", map.mode),
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the next hit object and calculate the
|
||||
/// performance attributes for the resulting score.
|
||||
pub fn process_next_object(&mut self, state: ScoreState) -> Option<PerformanceAttributes> {
|
||||
self.process_next_n_objects(state, 1)
|
||||
}
|
||||
|
||||
/// Same as [`process_next_object`](`GradualPerformanceAttributes::process_next_object`)
|
||||
/// but instead of processing only one object it process `n` many.
|
||||
///
|
||||
/// If `n` is 0 it will be considered as 1.
|
||||
/// If there are still objects to be processed but `n` is larger than the amount
|
||||
/// of remaining objects, `n` will be considered as the amount of remaining objects.
|
||||
pub fn process_next_n_objects(
|
||||
&mut self,
|
||||
state: ScoreState,
|
||||
n: usize,
|
||||
) -> Option<PerformanceAttributes> {
|
||||
match self {
|
||||
#[cfg(feature = "fruits")]
|
||||
GradualPerformanceAttributes::Fruits(f) => f
|
||||
.process_next_n_objects(state.into(), n)
|
||||
.map(PerformanceAttributes::Fruits),
|
||||
#[cfg(feature = "mania")]
|
||||
GradualPerformanceAttributes::Mania(m) => m
|
||||
.process_next_n_objects(state.score, n)
|
||||
.map(PerformanceAttributes::Mania),
|
||||
#[cfg(feature = "osu")]
|
||||
GradualPerformanceAttributes::Osu(o) => o
|
||||
.process_next_n_objects(state.into(), n)
|
||||
.map(PerformanceAttributes::Osu),
|
||||
#[cfg(feature = "taiko")]
|
||||
GradualPerformanceAttributes::Taiko(t) => t
|
||||
.process_next_n_objects(state.into(), n)
|
||||
.map(PerformanceAttributes::Taiko),
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -120,6 +120,9 @@ pub mod taiko;
|
||||
/// Beatmap parsing and the contained types.
|
||||
pub mod parse;
|
||||
|
||||
mod gradual;
|
||||
pub use gradual::{GradualDifficultyAttributes, GradualPerformanceAttributes, ScoreState};
|
||||
|
||||
mod pp;
|
||||
pub use pp::{AnyPP, AttributeProvider};
|
||||
|
||||
@@ -168,9 +171,21 @@ pub trait BeatmapExt {
|
||||
///
|
||||
/// Suitable to plot the difficulty of a map over time.
|
||||
fn strains(&self, mods: impl Mods) -> Strains;
|
||||
|
||||
/// Return an iterator that gives you the `DifficultyAttributes` after each hit object.
|
||||
///
|
||||
/// Suitable to efficiently get the map's star rating after multiple different locations.
|
||||
fn gradual_difficulty(&self, mods: impl Mods) -> GradualDifficultyAttributes<'_>;
|
||||
|
||||
/// Return a struct that gives you the `PerformanceAttributes` after every (few) hit object(s).
|
||||
///
|
||||
/// Suitable to efficiently get a score's performance after multiple different locations,
|
||||
/// i.e. live update a score's pp.
|
||||
fn gradual_performance(&self, mods: u32) -> GradualPerformanceAttributes<'_>;
|
||||
}
|
||||
|
||||
impl BeatmapExt for Beatmap {
|
||||
#[inline]
|
||||
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> DifficultyAttributes {
|
||||
match self.mode {
|
||||
GameMode::STD => {
|
||||
@@ -204,6 +219,7 @@ impl BeatmapExt for Beatmap {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_pp(&self, mods: u32) -> PerformanceAttributes {
|
||||
match self.mode {
|
||||
GameMode::STD => {
|
||||
@@ -242,6 +258,7 @@ impl BeatmapExt for Beatmap {
|
||||
AnyPP::new(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn strains(&self, mods: impl Mods) -> Strains {
|
||||
match self.mode {
|
||||
GameMode::STD => {
|
||||
@@ -274,6 +291,16 @@ impl BeatmapExt for Beatmap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gradual_difficulty(&self, mods: impl Mods) -> GradualDifficultyAttributes<'_> {
|
||||
GradualDifficultyAttributes::new(self, mods)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn gradual_performance(&self, mods: u32) -> GradualPerformanceAttributes<'_> {
|
||||
GradualPerformanceAttributes::new(self, mods)
|
||||
}
|
||||
}
|
||||
|
||||
/// The result of calculating the strains on a map.
|
||||
@@ -353,6 +380,7 @@ impl DifficultyAttributes {
|
||||
|
||||
#[cfg(feature = "fruits")]
|
||||
impl From<fruits::FruitsDifficultyAttributes> for DifficultyAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: fruits::FruitsDifficultyAttributes) -> Self {
|
||||
Self::Fruits(attributes)
|
||||
}
|
||||
@@ -360,6 +388,7 @@ impl From<fruits::FruitsDifficultyAttributes> for DifficultyAttributes {
|
||||
|
||||
#[cfg(feature = "mania")]
|
||||
impl From<mania::ManiaDifficultyAttributes> for DifficultyAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: mania::ManiaDifficultyAttributes) -> Self {
|
||||
Self::Mania(attributes)
|
||||
}
|
||||
@@ -367,6 +396,7 @@ impl From<mania::ManiaDifficultyAttributes> for DifficultyAttributes {
|
||||
|
||||
#[cfg(feature = "osu")]
|
||||
impl From<osu::OsuDifficultyAttributes> for DifficultyAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: osu::OsuDifficultyAttributes) -> Self {
|
||||
Self::Osu(attributes)
|
||||
}
|
||||
@@ -374,6 +404,7 @@ impl From<osu::OsuDifficultyAttributes> for DifficultyAttributes {
|
||||
|
||||
#[cfg(feature = "taiko")]
|
||||
impl From<taiko::TaikoDifficultyAttributes> for DifficultyAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: taiko::TaikoDifficultyAttributes) -> Self {
|
||||
Self::Taiko(attributes)
|
||||
}
|
||||
@@ -491,6 +522,7 @@ impl From<PerformanceAttributes> for DifficultyAttributes {
|
||||
|
||||
#[cfg(feature = "fruits")]
|
||||
impl From<fruits::FruitsPerformanceAttributes> for PerformanceAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: fruits::FruitsPerformanceAttributes) -> Self {
|
||||
Self::Fruits(attributes)
|
||||
}
|
||||
@@ -498,6 +530,7 @@ impl From<fruits::FruitsPerformanceAttributes> for PerformanceAttributes {
|
||||
|
||||
#[cfg(feature = "mania")]
|
||||
impl From<mania::ManiaPerformanceAttributes> for PerformanceAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: mania::ManiaPerformanceAttributes) -> Self {
|
||||
Self::Mania(attributes)
|
||||
}
|
||||
@@ -505,6 +538,7 @@ impl From<mania::ManiaPerformanceAttributes> for PerformanceAttributes {
|
||||
|
||||
#[cfg(feature = "osu")]
|
||||
impl From<osu::OsuPerformanceAttributes> for PerformanceAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: osu::OsuPerformanceAttributes) -> Self {
|
||||
Self::Osu(attributes)
|
||||
}
|
||||
@@ -512,6 +546,7 @@ impl From<osu::OsuPerformanceAttributes> for PerformanceAttributes {
|
||||
|
||||
#[cfg(feature = "taiko")]
|
||||
impl From<taiko::TaikoPerformanceAttributes> for PerformanceAttributes {
|
||||
#[inline]
|
||||
fn from(attributes: taiko::TaikoPerformanceAttributes) -> Self {
|
||||
Self::Taiko(attributes)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ use super::{OsuGradualDifficultyAttributes, OsuPerformanceAttributes};
|
||||
// TODO: Benchmark if Copy is faster than Clone
|
||||
/// 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 [`OsuGradualPerformanceAttributes`].
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct OsuScoreState {
|
||||
/// Maximum combo that the score has had so far.
|
||||
|
||||
@@ -5,6 +5,8 @@ use super::{TaikoGradualDifficultyAttributes, TaikoPerformanceAttributes};
|
||||
// TODO: Benchmark if Copy is faster than Clone
|
||||
/// 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 [`TaikoGradualPerformanceAttributes`].
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct TaikoScoreState {
|
||||
/// Maximum combo that the score has had so far.
|
||||
@@ -33,7 +35,7 @@ impl TaikoScoreState {
|
||||
/// To process multiple objects at once, use
|
||||
/// [`process_next_n_objects`](`TaikoGradualPerformanceAttributes::process_next_n_objects`) instead.
|
||||
///
|
||||
/// Both methods require an [`TaikoScoreState`] that contains the current
|
||||
/// Both methods require a [`TaikoScoreState`] that contains the current
|
||||
/// hitresults as well as the maximum combo so far.
|
||||
///
|
||||
/// If you only want to calculate difficulty attributes use
|
||||
|
||||
Reference in New Issue
Block a user