restructure Difficulty and ModeDifficulty
This commit is contained in:
@@ -27,9 +27,9 @@ News posts of the latest gamemode updates:
|
|||||||
let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap();
|
let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap();
|
||||||
|
|
||||||
// Calculate difficulty attributes
|
// Calculate difficulty attributes
|
||||||
let diff_attrs = map.difficulty()
|
let diff_attrs = rosu_pp::Difficulty::new()
|
||||||
.mods(8 + 16) // HDHR
|
.mods(8 + 16) // HDHR
|
||||||
.calculate();
|
.calculate(&map);
|
||||||
|
|
||||||
let stars = diff_attrs.stars();
|
let stars = diff_attrs.stars();
|
||||||
|
|
||||||
@@ -63,17 +63,20 @@ println!("Stars: {stars} | PP: {pp}/{max_pp}");
|
|||||||
Gradually calculating attributes provides an efficient way to process each hitobject
|
Gradually calculating attributes provides an efficient way to process each hitobject
|
||||||
separately and calculate the attributes only up to that point.
|
separately and calculate the attributes only up to that point.
|
||||||
|
|
||||||
For difficulty attributes, there is [`GradualDifficulty`] which implements [`Iterator`]
|
For difficulty attributes, there is `GradualDifficulty` which implements `Iterator`
|
||||||
and for performance attributes there is [`GradualPerformance`] which requires the current
|
and for performance attributes there is `GradualPerformance` which requires the current
|
||||||
score state.
|
score state.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rosu_pp::{Beatmap, GradualPerformance, ModeDifficulty, any::ScoreState};
|
use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState};
|
||||||
|
|
||||||
let map = Beatmap::from_path("./resources/1028484.osu").unwrap();
|
let map = Beatmap::from_path("./resources/1028484.osu").unwrap();
|
||||||
let difficulty = ModeDifficulty::new().mods(16 + 64).clock_rate(1.2); // HRDT on 1.2x
|
|
||||||
|
|
||||||
let mut gradual = GradualPerformance::new(&difficulty, &map);
|
let mut gradual = Difficulty::new()
|
||||||
|
.mods(16 + 64) // HRDT
|
||||||
|
.clock_rate(1.2)
|
||||||
|
.gradual_performance(&map);
|
||||||
|
|
||||||
let mut state = ScoreState::new(); // empty state, everything is on 0.
|
let mut state = ScoreState::new(); // empty state, everything is on 0.
|
||||||
|
|
||||||
// The first 10 hitresults are 300s
|
// The first 10 hitresults are 300s
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
use std::{
|
||||||
|
fmt::{Debug, Formatter, Result as FmtResult},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
model::{beatmap::Converted, mode::IGameMode},
|
||||||
|
util::generic_fmt::GenericFormatter,
|
||||||
|
Difficulty,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Difficulty calculator on maps of a given mode.
|
||||||
|
#[must_use]
|
||||||
|
pub struct ConvertedDifficulty<M> {
|
||||||
|
inner: Difficulty,
|
||||||
|
_mode: PhantomData<M>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> ConvertedDifficulty<M> {
|
||||||
|
/// Create a new difficulty calculator for a generic mode.
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self::from_difficulty(Difficulty::new())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn from_difficulty(difficulty: Difficulty) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: difficulty,
|
||||||
|
_mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the internal [`Difficulty`].
|
||||||
|
pub const fn into_inner(self) -> Difficulty {
|
||||||
|
self.inner
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Cast from generic mode `M` to `N`.
|
||||||
|
pub fn cast<N: IGameMode>(self) -> ConvertedDifficulty<N> {
|
||||||
|
ConvertedDifficulty {
|
||||||
|
inner: self.inner,
|
||||||
|
_mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specify mods through their bit values.
|
||||||
|
///
|
||||||
|
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
|
||||||
|
pub const fn mods(self, mods: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.mods(mods),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Amount of passed objects for partial plays, e.g. a fail.
|
||||||
|
pub const fn passed_objects(self, passed_objects: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.passed_objects(passed_objects),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adjust the clock rate used in the calculation between 0.01 and 100.0.
|
||||||
|
///
|
||||||
|
/// If none is specified, it will take the clock rate based on the mods
|
||||||
|
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
|
||||||
|
pub fn clock_rate(self, clock_rate: f64) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.clock_rate(clock_rate),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: IGameMode> ConvertedDifficulty<M> {
|
||||||
|
/// Perform the difficulty calculation for a [`Converted`] beatmap and
|
||||||
|
/// process the final skill values.
|
||||||
|
pub fn calculate(&self, map: &Converted<'_, M>) -> M::DifficultyAttributes {
|
||||||
|
M::difficulty(&self.inner, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform a difficulty calculation for a [`Converted`] beatmap without
|
||||||
|
/// processing the final skill values.
|
||||||
|
pub fn strains(&self, map: &Converted<'_, M>) -> M::Strains {
|
||||||
|
M::strains(&self.inner, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a gradual difficulty calculator for a [`Converted`] beatmap.
|
||||||
|
pub fn gradual_difficulty(&self, map: &Converted<'_, M>) -> M::GradualDifficulty {
|
||||||
|
M::gradual_difficulty(&self.inner, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a gradual performance calculator for a [`Converted`] beatmap.
|
||||||
|
pub fn gradual_performance(&self, map: &Converted<'_, M>) -> M::GradualPerformance {
|
||||||
|
M::gradual_performance(&self.inner, map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M: IGameMode> From<Difficulty> for ConvertedDifficulty<M> {
|
||||||
|
fn from(difficulty: Difficulty) -> Self {
|
||||||
|
Self::from_difficulty(difficulty)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> AsRef<Difficulty> for ConvertedDifficulty<M> {
|
||||||
|
fn as_ref(&self) -> &Difficulty {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> Clone for ConvertedDifficulty<M> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
_mode: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> Debug for ConvertedDifficulty<M> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
f.debug_struct("ConvertedDifficulty")
|
||||||
|
.field("inner", &self.inner)
|
||||||
|
.field("mode", &GenericFormatter::<M>::new())
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> PartialEq for ConvertedDifficulty<M> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.inner == other.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,11 +4,12 @@ use rosu_map::section::general::GameMode;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::DifficultyAttributes,
|
any::DifficultyAttributes,
|
||||||
catch::{CatchBeatmap, CatchGradualDifficulty},
|
catch::{Catch, CatchBeatmap, CatchGradualDifficulty},
|
||||||
mania::{ManiaBeatmap, ManiaGradualDifficulty},
|
mania::{Mania, ManiaBeatmap, ManiaGradualDifficulty},
|
||||||
osu::{OsuBeatmap, OsuGradualDifficulty},
|
model::mode::IGameMode,
|
||||||
taiko::{TaikoBeatmap, TaikoGradualDifficulty},
|
osu::{Osu, OsuBeatmap, OsuGradualDifficulty},
|
||||||
Beatmap, ModeDifficulty,
|
taiko::{Taiko, TaikoBeatmap, TaikoGradualDifficulty},
|
||||||
|
Beatmap, Converted, Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Gradually calculate the difficulty attributes on maps of any mode.
|
/// Gradually calculate the difficulty attributes on maps of any mode.
|
||||||
@@ -22,10 +23,10 @@ use crate::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, GradualDifficulty, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, GradualDifficulty, Difficulty};
|
||||||
///
|
///
|
||||||
/// let map = Beatmap::from_path("./resources/2785319.osu").unwrap();
|
/// let map = Beatmap::from_path("./resources/2785319.osu").unwrap();
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut iter = GradualDifficulty::new(&difficulty, &map);
|
/// let mut iter = GradualDifficulty::new(&difficulty, &map);
|
||||||
///
|
///
|
||||||
/// // the difficulty of the map after the first object
|
/// // the difficulty of the map after the first object
|
||||||
@@ -53,22 +54,28 @@ pub enum GradualDifficulty {
|
|||||||
macro_rules! from_converted {
|
macro_rules! from_converted {
|
||||||
( $fn:ident, $mode:ident, $converted:ident ) => {
|
( $fn:ident, $mode:ident, $converted:ident ) => {
|
||||||
#[doc = concat!("Create a [`GradualDifficulty`] for a [`", stringify!($converted), "`]")]
|
#[doc = concat!("Create a [`GradualDifficulty`] for a [`", stringify!($converted), "`]")]
|
||||||
pub fn $fn(difficulty: &ModeDifficulty, converted: &$converted<'_>) -> Self {
|
pub fn $fn(difficulty: &Difficulty, converted: &$converted<'_>) -> Self {
|
||||||
Self::$mode(difficulty.gradual_difficulty(converted))
|
Self::$mode($mode::gradual_difficulty(difficulty, converted))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GradualDifficulty {
|
impl GradualDifficulty {
|
||||||
/// Create a [`GradualDifficulty`] for a map of any mode.
|
/// Create a [`GradualDifficulty`] for a map of any mode.
|
||||||
pub fn new(difficulty: &ModeDifficulty, map: &Beatmap) -> Self {
|
pub fn new(difficulty: &Difficulty, map: &Beatmap) -> Self {
|
||||||
let map = Cow::Borrowed(map);
|
let map = Cow::Borrowed(map);
|
||||||
|
|
||||||
match map.mode {
|
match map.mode {
|
||||||
GameMode::Osu => Self::Osu(difficulty.gradual_difficulty(&OsuBeatmap::new(map))),
|
GameMode::Osu => Self::Osu(Osu::gradual_difficulty(difficulty, &Converted::new(map))),
|
||||||
GameMode::Taiko => Self::Taiko(difficulty.gradual_difficulty(&TaikoBeatmap::new(map))),
|
GameMode::Taiko => {
|
||||||
GameMode::Catch => Self::Catch(difficulty.gradual_difficulty(&CatchBeatmap::new(map))),
|
Self::Taiko(Taiko::gradual_difficulty(difficulty, &Converted::new(map)))
|
||||||
GameMode::Mania => Self::Mania(difficulty.gradual_difficulty(&ManiaBeatmap::new(map))),
|
}
|
||||||
|
GameMode::Catch => {
|
||||||
|
Self::Catch(Catch::gradual_difficulty(difficulty, &Converted::new(map)))
|
||||||
|
}
|
||||||
|
GameMode::Mania => {
|
||||||
|
Self::Mania(Mania::gradual_difficulty(difficulty, &Converted::new(map)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+70
-113
@@ -3,144 +3,79 @@ use std::borrow::Cow;
|
|||||||
use rosu_map::section::general::GameMode;
|
use rosu_map::section::general::GameMode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
catch::{Catch, CatchBeatmap},
|
catch::Catch,
|
||||||
mania::{Mania, ManiaBeatmap},
|
mania::Mania,
|
||||||
model::beatmap::{Beatmap, Converted},
|
model::beatmap::{Beatmap, Converted},
|
||||||
osu::{Osu, OsuBeatmap},
|
osu::Osu,
|
||||||
taiko::{Taiko, TaikoBeatmap},
|
taiko::Taiko,
|
||||||
|
GradualDifficulty, GradualPerformance,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::mode::ModeDifficulty;
|
use self::converted::ConvertedDifficulty;
|
||||||
|
|
||||||
use super::{attributes::DifficultyAttributes, Strains};
|
use super::{attributes::DifficultyAttributes, Strains};
|
||||||
|
|
||||||
|
pub mod converted;
|
||||||
pub mod gradual;
|
pub mod gradual;
|
||||||
pub mod mode;
|
|
||||||
pub mod object;
|
pub mod object;
|
||||||
pub mod skills;
|
pub mod skills;
|
||||||
|
|
||||||
|
use crate::{model::mode::IGameMode, util::mods::Mods};
|
||||||
|
|
||||||
/// Difficulty calculator on maps of any mode.
|
/// Difficulty calculator on maps of any mode.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Difficulty<'map> {
|
pub struct Difficulty {
|
||||||
map: Cow<'map, Beatmap>,
|
mods: u32,
|
||||||
inner: ModeDifficulty,
|
passed_objects: Option<u32>,
|
||||||
|
clock_rate: Option<f64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'map> Difficulty<'map> {
|
impl Difficulty {
|
||||||
/// Create a new difficulty calculator for the given beatmap.
|
/// Create a new difficulty calculator.
|
||||||
pub const fn new(map: &'map Beatmap) -> Self {
|
pub const fn new() -> Self {
|
||||||
Self::new_with_cow(Cow::Borrowed(map))
|
|
||||||
}
|
|
||||||
|
|
||||||
const fn new_with_cow(map: Cow<'map, Beatmap>) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
map,
|
mods: 0,
|
||||||
inner: ModeDifficulty::new(),
|
passed_objects: None,
|
||||||
|
clock_rate: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! impl_from_mode {
|
/// Use this [`&Difficulty`] as a calculator for a specific [`IGameMode`].
|
||||||
( $mode:ident ) => {
|
|
||||||
impl<'a> From<Converted<'a, $mode>> for Difficulty<'a> {
|
|
||||||
fn from(converted: Converted<'a, $mode>) -> Self {
|
|
||||||
Self::new_with_cow(converted.into_inner())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b: 'a> From<&'b Converted<'a, $mode>> for Difficulty<'a> {
|
|
||||||
fn from(converted: &'b Converted<'a, $mode>) -> Self {
|
|
||||||
Self::new(&converted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_from_mode!(Osu);
|
|
||||||
impl_from_mode!(Taiko);
|
|
||||||
impl_from_mode!(Catch);
|
|
||||||
impl_from_mode!(Mania);
|
|
||||||
|
|
||||||
impl Difficulty<'_> {
|
|
||||||
/// Attempt to convert the map to the specified mode.
|
|
||||||
///
|
///
|
||||||
/// If the conversion is incompatible, `None` is returned.
|
/// [`&Difficulty`]: Difficulty
|
||||||
///
|
pub const fn with_mode<M: IGameMode>(&self) -> ConvertedDifficulty<M> {
|
||||||
/// If the given mode should be ignored in case it is incompatible, use
|
let this = Self {
|
||||||
/// [`mode_or_ignore`] instead.
|
mods: self.mods,
|
||||||
///
|
passed_objects: self.passed_objects,
|
||||||
/// [`mode_or_ignore`]: Self::mode_or_ignore
|
clock_rate: self.clock_rate,
|
||||||
pub fn try_mode(&mut self, mode: GameMode) -> Option<&mut Self> {
|
|
||||||
let map = match mode {
|
|
||||||
GameMode::Osu => OsuBeatmap::try_from_ref(self.map.as_ref())?.into_inner(),
|
|
||||||
GameMode::Taiko => TaikoBeatmap::try_from_ref(self.map.as_ref())?.into_inner(),
|
|
||||||
GameMode::Catch => CatchBeatmap::try_from_ref(self.map.as_ref())?.into_inner(),
|
|
||||||
GameMode::Mania => ManiaBeatmap::try_from_ref(self.map.as_ref())?.into_inner(),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if matches!(map, Cow::Owned(_)) {
|
ConvertedDifficulty::from_difficulty(this)
|
||||||
let map = map.into_owned();
|
|
||||||
self.map = Cow::Owned(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempt to convert the map to the specified mode.
|
|
||||||
///
|
|
||||||
/// If the conversion is incompatible, the map won't be modified.
|
|
||||||
///
|
|
||||||
/// To see whether the given mode is incompatible, use [`try_mode`]
|
|
||||||
/// instead.
|
|
||||||
///
|
|
||||||
/// [`try_mode`]: Self::try_mode
|
|
||||||
pub fn mode_or_ignore(&mut self, mode: GameMode) -> &mut Self {
|
|
||||||
let map = self.map.as_ref();
|
|
||||||
|
|
||||||
let map_opt = match mode {
|
|
||||||
GameMode::Osu => OsuBeatmap::try_from_ref(map).map(Converted::into_inner),
|
|
||||||
GameMode::Taiko => TaikoBeatmap::try_from_ref(map).map(Converted::into_inner),
|
|
||||||
GameMode::Catch => CatchBeatmap::try_from_ref(map).map(Converted::into_inner),
|
|
||||||
GameMode::Mania => ManiaBeatmap::try_from_ref(map).map(Converted::into_inner),
|
|
||||||
};
|
|
||||||
|
|
||||||
match map_opt {
|
|
||||||
Some(cow @ Cow::Owned(_)) => {
|
|
||||||
let map = cow.into_owned();
|
|
||||||
self.map = Cow::Owned(map);
|
|
||||||
}
|
|
||||||
Some(Cow::Borrowed(_)) | None => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Specify mods through their bit values.
|
/// Specify mods through their bit values.
|
||||||
///
|
///
|
||||||
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
|
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
|
||||||
pub fn mods(self, mods: u32) -> Self {
|
pub const fn mods(self, mods: u32) -> Self {
|
||||||
Self {
|
Self { mods, ..self }
|
||||||
inner: self.inner.mods(mods),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Amount of passed objects for partial plays, e.g. a fail.
|
/// Amount of passed objects for partial plays, e.g. a fail.
|
||||||
pub fn passed_objects(self, passed_objects: u32) -> Self {
|
pub const fn passed_objects(self, passed_objects: u32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: self.inner.passed_objects(passed_objects),
|
passed_objects: Some(passed_objects),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adjust the clock rate used in the calculation.
|
/// Adjust the clock rate used in the calculation between 0.01 and 100.0.
|
||||||
///
|
///
|
||||||
/// If none is specified, it will take the clock rate based on the mods
|
/// If none is specified, it will take the clock rate based on the mods
|
||||||
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
|
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
|
||||||
pub fn clock_rate(self, clock_rate: f64) -> Self {
|
pub fn clock_rate(self, clock_rate: f64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: self.inner.clock_rate(clock_rate),
|
clock_rate: Some(clock_rate.clamp(0.01, 100.0)),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,19 +83,19 @@ impl Difficulty<'_> {
|
|||||||
/// Perform the difficulty calculation.
|
/// Perform the difficulty calculation.
|
||||||
///
|
///
|
||||||
/// The returned attributes depend on the map's mode.
|
/// The returned attributes depend on the map's mode.
|
||||||
pub fn calculate(&self) -> DifficultyAttributes {
|
pub fn calculate(&self, map: &Beatmap) -> DifficultyAttributes {
|
||||||
let map = Cow::Borrowed(self.map.as_ref());
|
let map = Cow::Borrowed(map);
|
||||||
|
|
||||||
match self.map.mode {
|
match map.mode {
|
||||||
GameMode::Osu => DifficultyAttributes::Osu(self.inner.calculate(&OsuBeatmap::new(map))),
|
GameMode::Osu => DifficultyAttributes::Osu(Osu::difficulty(self, &Converted::new(map))),
|
||||||
GameMode::Taiko => {
|
GameMode::Taiko => {
|
||||||
DifficultyAttributes::Taiko(self.inner.calculate(&TaikoBeatmap::new(map)))
|
DifficultyAttributes::Taiko(Taiko::difficulty(self, &Converted::new(map)))
|
||||||
}
|
}
|
||||||
GameMode::Catch => {
|
GameMode::Catch => {
|
||||||
DifficultyAttributes::Catch(self.inner.calculate(&CatchBeatmap::new(map)))
|
DifficultyAttributes::Catch(Catch::difficulty(self, &Converted::new(map)))
|
||||||
}
|
}
|
||||||
GameMode::Mania => {
|
GameMode::Mania => {
|
||||||
DifficultyAttributes::Mania(self.inner.calculate(&ManiaBeatmap::new(map)))
|
DifficultyAttributes::Mania(Mania::difficulty(self, &Converted::new(map)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,14 +104,36 @@ impl Difficulty<'_> {
|
|||||||
/// strains, return them as is.
|
/// strains, return them as is.
|
||||||
///
|
///
|
||||||
/// Suitable to plot the difficulty of a map over time.
|
/// Suitable to plot the difficulty of a map over time.
|
||||||
pub fn strains(&self) -> Strains {
|
pub fn strains(&self, map: &Beatmap) -> Strains {
|
||||||
let map = Cow::Borrowed(self.map.as_ref());
|
let map = Cow::Borrowed(map);
|
||||||
|
|
||||||
match self.map.mode {
|
match map.mode {
|
||||||
GameMode::Osu => Strains::Osu(self.inner.strains(&OsuBeatmap::new(map))),
|
GameMode::Osu => Strains::Osu(Osu::strains(self, &Converted::new(map))),
|
||||||
GameMode::Taiko => Strains::Taiko(self.inner.strains(&TaikoBeatmap::new(map))),
|
GameMode::Taiko => Strains::Taiko(Taiko::strains(self, &Converted::new(map))),
|
||||||
GameMode::Catch => Strains::Catch(self.inner.strains(&CatchBeatmap::new(map))),
|
GameMode::Catch => Strains::Catch(Catch::strains(self, &Converted::new(map))),
|
||||||
GameMode::Mania => Strains::Mania(self.inner.strains(&ManiaBeatmap::new(map))),
|
GameMode::Mania => Strains::Mania(Mania::strains(self, &Converted::new(map))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a gradual difficulty calculator for a [`Beatmap`].
|
||||||
|
pub fn gradual_difficulty(&self, map: &Beatmap) -> GradualDifficulty {
|
||||||
|
GradualDifficulty::new(self, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a gradual performance calculator for a [`Beatmap`].
|
||||||
|
pub fn gradual_performance(&self, map: &Beatmap) -> GradualPerformance {
|
||||||
|
GradualPerformance::new(self, map)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) const fn get_mods(&self) -> u32 {
|
||||||
|
self.mods
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_clock_rate(&self) -> f64 {
|
||||||
|
self.clock_rate.unwrap_or_else(|| self.mods.clock_rate())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn get_passed_objects(&self) -> usize {
|
||||||
|
self.passed_objects.map_or(usize::MAX, |n| n as usize)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,87 +0,0 @@
|
|||||||
use crate::{
|
|
||||||
model::{beatmap::Converted, mode::IGameMode},
|
|
||||||
util::mods::Mods,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Difficulty calculator on maps of a given mode.
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
|
||||||
#[must_use]
|
|
||||||
pub struct ModeDifficulty {
|
|
||||||
mods: u32,
|
|
||||||
passed_objects: Option<u32>,
|
|
||||||
clock_rate: Option<f64>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ModeDifficulty {
|
|
||||||
/// Create a new difficulty calculator.
|
|
||||||
pub const fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
mods: 0,
|
|
||||||
passed_objects: None,
|
|
||||||
clock_rate: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Specify mods through their bit values.
|
|
||||||
///
|
|
||||||
/// See [https://github.com/ppy/osu-api/wiki#mods](https://github.com/ppy/osu-api/wiki#mods)
|
|
||||||
pub const fn mods(self, mods: u32) -> Self {
|
|
||||||
Self { mods, ..self }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Amount of passed objects for partial plays, e.g. a fail.
|
|
||||||
pub const fn passed_objects(self, passed_objects: u32) -> Self {
|
|
||||||
Self {
|
|
||||||
passed_objects: Some(passed_objects),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adjust the clock rate used in the calculation between 0.01 and 100.0.
|
|
||||||
///
|
|
||||||
/// If none is specified, it will take the clock rate based on the mods
|
|
||||||
/// i.e. 1.5 for DT, 0.75 for HT and 1.0 otherwise.
|
|
||||||
pub fn clock_rate(self, clock_rate: f64) -> Self {
|
|
||||||
Self {
|
|
||||||
clock_rate: Some(clock_rate.clamp(0.01, 100.0)),
|
|
||||||
..self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform the difficulty calculation for a [`Converted`] beatmap and
|
|
||||||
/// process the final skill values.
|
|
||||||
pub fn calculate<M: IGameMode>(&self, map: &Converted<'_, M>) -> M::DifficultyAttributes {
|
|
||||||
M::difficulty(self, map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Perform a difficulty calculation for a [`Converted`] beatmap without
|
|
||||||
/// processing the final skill values.
|
|
||||||
pub fn strains<M: IGameMode>(&self, map: &Converted<'_, M>) -> M::Strains {
|
|
||||||
M::strains(self, map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a gradual difficulty calculator for a [`Converted`] beatmap.
|
|
||||||
pub fn gradual_difficulty<M: IGameMode>(&self, map: &Converted<'_, M>) -> M::GradualDifficulty {
|
|
||||||
M::gradual_difficulty(self, map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a gradual performance calculator for a [`Converted`] beatmap.
|
|
||||||
pub fn gradual_performance<M: IGameMode>(
|
|
||||||
&self,
|
|
||||||
map: &Converted<'_, M>,
|
|
||||||
) -> M::GradualPerformance {
|
|
||||||
M::gradual_performance(self, map)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) const fn get_mods(&self) -> u32 {
|
|
||||||
self.mods
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_clock_rate(&self) -> f64 {
|
|
||||||
self.clock_rate.unwrap_or_else(|| self.mods.clock_rate())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_passed_objects(&self) -> usize {
|
|
||||||
self.passed_objects.map_or(usize::MAX, |n| n as usize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+1
-1
@@ -2,7 +2,7 @@ pub use self::{
|
|||||||
attributes::{
|
attributes::{
|
||||||
AttributeProvider, DifficultyAttributes, ModeAttributeProvider, PerformanceAttributes,
|
AttributeProvider, DifficultyAttributes, ModeAttributeProvider, PerformanceAttributes,
|
||||||
},
|
},
|
||||||
difficulty::{gradual::GradualDifficulty, mode::ModeDifficulty, Difficulty},
|
difficulty::{converted::ConvertedDifficulty, gradual::GradualDifficulty, Difficulty},
|
||||||
performance::{gradual::GradualPerformance, HitResultPriority, Performance},
|
performance::{gradual::GradualPerformance, HitResultPriority, Performance},
|
||||||
score_state::ScoreState,
|
score_state::ScoreState,
|
||||||
strains::Strains,
|
strains::Strains,
|
||||||
|
|||||||
@@ -4,11 +4,12 @@ use rosu_map::section::general::GameMode;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::{PerformanceAttributes, ScoreState},
|
any::{PerformanceAttributes, ScoreState},
|
||||||
catch::{CatchBeatmap, CatchGradualPerformance},
|
catch::{Catch, CatchBeatmap, CatchGradualPerformance},
|
||||||
mania::{ManiaBeatmap, ManiaGradualPerformance},
|
mania::{Mania, ManiaBeatmap, ManiaGradualPerformance},
|
||||||
osu::{OsuBeatmap, OsuGradualPerformance},
|
model::mode::IGameMode,
|
||||||
taiko::{TaikoBeatmap, TaikoGradualPerformance},
|
osu::{Osu, OsuBeatmap, OsuGradualPerformance},
|
||||||
Beatmap, ModeDifficulty,
|
taiko::{Taiko, TaikoBeatmap, TaikoGradualPerformance},
|
||||||
|
Beatmap, Converted, Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Gradually calculate the performance attributes on maps of any mode.
|
/// Gradually calculate the performance attributes on maps of any mode.
|
||||||
@@ -31,10 +32,10 @@ use crate::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, GradualPerformance, ModeDifficulty, any::ScoreState};
|
/// use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState};
|
||||||
///
|
///
|
||||||
/// let map = Beatmap::from_path("./resources/2785319.osu").unwrap();
|
/// let map = Beatmap::from_path("./resources/2785319.osu").unwrap();
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut gradual = GradualPerformance::new(&difficulty, &map);
|
/// let mut gradual = GradualPerformance::new(&difficulty, &map);
|
||||||
/// let mut state = ScoreState::new(); // empty state, everything is on 0.
|
/// let mut state = ScoreState::new(); // empty state, everything is on 0.
|
||||||
///
|
///
|
||||||
@@ -102,22 +103,28 @@ pub enum GradualPerformance {
|
|||||||
macro_rules! from_converted {
|
macro_rules! from_converted {
|
||||||
( $fn:ident, $mode:ident, $converted:ident ) => {
|
( $fn:ident, $mode:ident, $converted:ident ) => {
|
||||||
#[doc = concat!("Create a [`GradualPerformance`] for a [`", stringify!($converted), "`]")]
|
#[doc = concat!("Create a [`GradualPerformance`] for a [`", stringify!($converted), "`]")]
|
||||||
pub fn $fn(difficulty: &ModeDifficulty, converted: &$converted<'_>) -> Self {
|
pub fn $fn(difficulty: &Difficulty, converted: &$converted<'_>) -> Self {
|
||||||
Self::$mode(difficulty.gradual_performance(converted))
|
Self::$mode($mode::gradual_performance(difficulty, converted))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GradualPerformance {
|
impl GradualPerformance {
|
||||||
/// Create a [`GradualPerformance`] for a map of any mode.
|
/// Create a [`GradualPerformance`] for a map of any mode.
|
||||||
pub fn new(difficulty: &ModeDifficulty, map: &Beatmap) -> Self {
|
pub fn new(difficulty: &Difficulty, map: &Beatmap) -> Self {
|
||||||
let map = Cow::Borrowed(map);
|
let map = Cow::Borrowed(map);
|
||||||
|
|
||||||
match map.mode {
|
match map.mode {
|
||||||
GameMode::Osu => Self::Osu(difficulty.gradual_performance(&OsuBeatmap::new(map))),
|
GameMode::Osu => Self::Osu(Osu::gradual_performance(difficulty, &Converted::new(map))),
|
||||||
GameMode::Taiko => Self::Taiko(difficulty.gradual_performance(&TaikoBeatmap::new(map))),
|
GameMode::Taiko => {
|
||||||
GameMode::Catch => Self::Catch(difficulty.gradual_performance(&CatchBeatmap::new(map))),
|
Self::Taiko(Taiko::gradual_performance(difficulty, &Converted::new(map)))
|
||||||
GameMode::Mania => Self::Mania(difficulty.gradual_performance(&ManiaBeatmap::new(map))),
|
}
|
||||||
|
GameMode::Catch => {
|
||||||
|
Self::Catch(Catch::gradual_performance(difficulty, &Converted::new(map)))
|
||||||
|
}
|
||||||
|
GameMode::Mania => {
|
||||||
|
Self::Mania(Mania::gradual_performance(difficulty, &Converted::new(map)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
CatchBeatmap, CatchDifficultyAttributes,
|
CatchBeatmap, CatchDifficultyAttributes,
|
||||||
},
|
},
|
||||||
util::mods::Mods,
|
util::mods::Mods,
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -32,14 +32,14 @@ use super::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::catch::{Catch, CatchGradualDifficulty};
|
/// use rosu_pp::catch::{Catch, CatchGradualDifficulty};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/2118524.osu")
|
/// let converted = Beatmap::from_path("./resources/2118524.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Catch>();
|
/// .unchecked_into_converted::<Catch>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut iter = CatchGradualDifficulty::new(&difficulty, &converted);
|
/// let mut iter = CatchGradualDifficulty::new(&difficulty, &converted);
|
||||||
///
|
///
|
||||||
/// // the difficulty of the map after the first hit object
|
/// // the difficulty of the map after the first hit object
|
||||||
@@ -66,7 +66,7 @@ pub struct CatchGradualDifficulty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CatchGradualDifficulty {
|
impl CatchGradualDifficulty {
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &CatchBeatmap<'_>) -> Self {
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ impl ExactSizeIterator for CatchGradualDifficulty {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::Beatmap;
|
use crate::{any::difficulty::converted::ConvertedDifficulty, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ mod tests {
|
|||||||
fn empty() {
|
fn empty() {
|
||||||
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
let mut gradual = CatchGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = CatchGradualDifficulty::new(&difficulty, &converted);
|
||||||
|
|
||||||
assert!(gradual.next().is_none());
|
assert!(gradual.next().is_none());
|
||||||
@@ -183,7 +183,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted();
|
.unchecked_into_converted();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
|
|
||||||
let mut gradual = CatchGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = CatchGradualDifficulty::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = CatchGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual_2nd = CatchGradualDifficulty::new(&difficulty, &converted);
|
||||||
@@ -207,7 +207,7 @@ mod tests {
|
|||||||
assert_eq!(next_gradual, next_gradual_3rd);
|
assert_eq!(next_gradual, next_gradual_3rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = ModeDifficulty::new()
|
let expected = ConvertedDifficulty::new()
|
||||||
.passed_objects(i as u32)
|
.passed_objects(i as u32)
|
||||||
.calculate(&converted);
|
.calculate(&converted);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
any::difficulty::{mode::ModeDifficulty, skills::Skill},
|
any::difficulty::{skills::Skill, Difficulty},
|
||||||
catch::{
|
catch::{
|
||||||
catcher::Catcher, convert::convert_objects, difficulty::object::CatchDifficultyObject,
|
catcher::Catcher, convert::convert_objects, difficulty::object::CatchDifficultyObject,
|
||||||
},
|
},
|
||||||
@@ -22,7 +22,7 @@ mod skills;
|
|||||||
const STAR_SCALING_FACTOR: f64 = 0.153;
|
const STAR_SCALING_FACTOR: f64 = 0.153;
|
||||||
|
|
||||||
pub fn difficulty(
|
pub fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &CatchBeatmap<'_>,
|
converted: &CatchBeatmap<'_>,
|
||||||
) -> CatchDifficultyAttributes {
|
) -> CatchDifficultyAttributes {
|
||||||
let DifficultyValues {
|
let DifficultyValues {
|
||||||
@@ -41,7 +41,7 @@ pub struct CatchDifficultySetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CatchDifficultySetup {
|
impl CatchDifficultySetup {
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &CatchBeatmap<'_>) -> Self {
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|
||||||
@@ -67,7 +67,7 @@ pub struct DifficultyValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DifficultyValues {
|
impl DifficultyValues {
|
||||||
pub fn calculate(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self {
|
pub fn calculate(difficulty: &Difficulty, converted: &CatchBeatmap<'_>) -> Self {
|
||||||
let take = difficulty.get_passed_objects();
|
let take = difficulty.get_passed_objects();
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
|
||||||
model::{
|
model::{
|
||||||
beatmap::Beatmap,
|
beatmap::Beatmap,
|
||||||
mode::{ConvertStatus, IGameMode},
|
mode::{ConvertStatus, IGameMode},
|
||||||
},
|
},
|
||||||
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@@ -47,13 +47,13 @@ impl IGameMode for Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn difficulty(
|
fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &CatchBeatmap<'_>,
|
converted: &CatchBeatmap<'_>,
|
||||||
) -> Self::DifficultyAttributes {
|
) -> Self::DifficultyAttributes {
|
||||||
difficulty::difficulty(difficulty, converted)
|
difficulty::difficulty(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strains(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self::Strains {
|
fn strains(difficulty: &Difficulty, converted: &CatchBeatmap<'_>) -> Self::Strains {
|
||||||
strains::strains(difficulty, converted)
|
strains::strains(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,14 +62,14 @@ impl IGameMode for Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_difficulty(
|
fn gradual_difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &CatchBeatmap<'_>,
|
map: &CatchBeatmap<'_>,
|
||||||
) -> Self::GradualDifficulty {
|
) -> Self::GradualDifficulty {
|
||||||
CatchGradualDifficulty::new(difficulty, map)
|
CatchGradualDifficulty::new(difficulty, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_performance(
|
fn gradual_performance(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &CatchBeatmap<'_>,
|
map: &CatchBeatmap<'_>,
|
||||||
) -> Self::GradualPerformance {
|
) -> Self::GradualPerformance {
|
||||||
CatchGradualPerformance::new(difficulty, map)
|
CatchGradualPerformance::new(difficulty, map)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
catch::{CatchBeatmap, CatchGradualDifficulty, CatchPerformanceAttributes, CatchScoreState},
|
catch::{CatchBeatmap, CatchGradualDifficulty, CatchPerformanceAttributes, CatchScoreState},
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Gradually calculate the performance attributes of an osu!catch map.
|
/// Gradually calculate the performance attributes of an osu!catch map.
|
||||||
@@ -21,14 +21,14 @@ use crate::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::catch::{Catch, CatchGradualPerformance, CatchScoreState};
|
/// use rosu_pp::catch::{Catch, CatchGradualPerformance, CatchScoreState};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/2118524.osu")
|
/// let converted = Beatmap::from_path("./resources/2118524.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Catch>();
|
/// .unchecked_into_converted::<Catch>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut gradual = CatchGradualPerformance::new(&difficulty, &converted);
|
/// let mut gradual = CatchGradualPerformance::new(&difficulty, &converted);
|
||||||
/// let mut state = CatchScoreState::new(); // empty state, everything is on 0.
|
/// let mut state = CatchScoreState::new(); // empty state, everything is on 0.
|
||||||
///
|
///
|
||||||
@@ -90,7 +90,7 @@ pub struct CatchGradualPerformance {
|
|||||||
|
|
||||||
impl CatchGradualPerformance {
|
impl CatchGradualPerformance {
|
||||||
/// Create a new gradual performance calculator for osu!catch maps.
|
/// Create a new gradual performance calculator for osu!catch maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &CatchBeatmap<'_>) -> Self {
|
||||||
let difficulty = CatchGradualDifficulty::new(difficulty, converted);
|
let difficulty = CatchGradualDifficulty::new(difficulty, converted);
|
||||||
|
|
||||||
Self { difficulty }
|
Self { difficulty }
|
||||||
@@ -144,7 +144,7 @@ mod tests {
|
|||||||
.unchecked_into_converted();
|
.unchecked_into_converted();
|
||||||
|
|
||||||
let mods = 88; // HDHRDT
|
let mods = 88; // HDHRDT
|
||||||
let difficulty = ModeDifficulty::new().mods(88);
|
let difficulty = Difficulty::new().mods(88);
|
||||||
|
|
||||||
let mut gradual = CatchGradualPerformance::new(&difficulty, &converted);
|
let mut gradual = CatchGradualPerformance::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = CatchGradualPerformance::new(&difficulty, &converted);
|
let mut gradual_2nd = CatchGradualPerformance::new(&difficulty, &converted);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::cmp::{self, Ordering};
|
use std::cmp::{self, Ordering};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::ModeAttributeProvider,
|
any::{Difficulty, ModeAttributeProvider},
|
||||||
any::ModeDifficulty,
|
|
||||||
osu::OsuPerformance,
|
osu::OsuPerformance,
|
||||||
util::{map_or_attrs::MapOrAttrs, mods::Mods},
|
util::{map_or_attrs::MapOrAttrs, mods::Mods},
|
||||||
};
|
};
|
||||||
@@ -21,7 +20,7 @@ pub mod gradual;
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct CatchPerformance<'map> {
|
pub struct CatchPerformance<'map> {
|
||||||
map_or_attrs: MapOrAttrs<'map, Catch>,
|
map_or_attrs: MapOrAttrs<'map, Catch>,
|
||||||
difficulty: ModeDifficulty,
|
difficulty: Difficulty,
|
||||||
acc: Option<f64>,
|
acc: Option<f64>,
|
||||||
combo: Option<u32>,
|
combo: Option<u32>,
|
||||||
fruits: Option<u32>,
|
fruits: Option<u32>,
|
||||||
@@ -36,7 +35,7 @@ impl<'map> CatchPerformance<'map> {
|
|||||||
pub const fn new(map: CatchBeatmap<'map>) -> Self {
|
pub const fn new(map: CatchBeatmap<'map>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Map(map),
|
map_or_attrs: MapOrAttrs::Map(map),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
acc: None,
|
acc: None,
|
||||||
combo: None,
|
combo: None,
|
||||||
fruits: None,
|
fruits: None,
|
||||||
@@ -50,7 +49,7 @@ impl<'map> CatchPerformance<'map> {
|
|||||||
pub(crate) const fn from_catch_attributes(attrs: CatchDifficultyAttributes) -> Self {
|
pub(crate) const fn from_catch_attributes(attrs: CatchDifficultyAttributes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
acc: None,
|
acc: None,
|
||||||
combo: None,
|
combo: None,
|
||||||
fruits: None,
|
fruits: None,
|
||||||
@@ -180,7 +179,7 @@ impl<'map> CatchPerformance<'map> {
|
|||||||
pub fn generate_state(&mut self) -> CatchScoreState {
|
pub fn generate_state(&mut self) -> CatchScoreState {
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => {
|
MapOrAttrs::Map(ref map) => {
|
||||||
let attrs = self.generate_attributes(map);
|
let attrs = self.difficulty.with_mode().calculate(map);
|
||||||
|
|
||||||
self.map_or_attrs.insert_attrs(attrs)
|
self.map_or_attrs.insert_attrs(attrs)
|
||||||
}
|
}
|
||||||
@@ -327,7 +326,7 @@ impl<'map> CatchPerformance<'map> {
|
|||||||
let state = self.generate_state();
|
let state = self.generate_state();
|
||||||
|
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => self.generate_attributes(map),
|
MapOrAttrs::Map(ref map) => self.difficulty.with_mode().calculate(map),
|
||||||
MapOrAttrs::Attrs(attrs) => attrs,
|
MapOrAttrs::Attrs(attrs) => attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -340,10 +339,6 @@ impl<'map> CatchPerformance<'map> {
|
|||||||
inner.calculate()
|
inner.calculate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_attributes(&self, map: &CatchBeatmap<'_>) -> CatchDifficultyAttributes {
|
|
||||||
self.difficulty.calculate(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to create [`CatchPerformance`] through a [`ModeAttributeProvider`].
|
/// Try to create [`CatchPerformance`] through a [`ModeAttributeProvider`].
|
||||||
///
|
///
|
||||||
/// If you already calculated the attributes for the current map-mod
|
/// If you already calculated the attributes for the current map-mod
|
||||||
@@ -547,7 +542,7 @@ mod test {
|
|||||||
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use crate::Beatmap;
|
use crate::{any::difficulty::converted::ConvertedDifficulty, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -564,7 +559,7 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Catch>();
|
.unchecked_into_converted::<Catch>();
|
||||||
|
|
||||||
let attrs = ModeDifficulty::new().calculate(&converted);
|
let attrs = ConvertedDifficulty::new().calculate(&converted);
|
||||||
|
|
||||||
assert_eq!(N_FRUITS, attrs.n_fruits);
|
assert_eq!(N_FRUITS, attrs.n_fruits);
|
||||||
assert_eq!(N_DROPLETS, attrs.n_droplets);
|
assert_eq!(N_DROPLETS, attrs.n_droplets);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{any::ModeDifficulty, catch::difficulty::DifficultyValues};
|
use crate::{any::Difficulty, catch::difficulty::DifficultyValues};
|
||||||
|
|
||||||
use super::convert::CatchBeatmap;
|
use super::convert::CatchBeatmap;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ impl CatchStrains {
|
|||||||
pub const SECTION_LEN: f64 = 750.0;
|
pub const SECTION_LEN: f64 = 750.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strains(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> CatchStrains {
|
pub fn strains(difficulty: &Difficulty, converted: &CatchBeatmap<'_>) -> CatchStrains {
|
||||||
let DifficultyValues { movement, .. } = DifficultyValues::calculate(difficulty, converted);
|
let DifficultyValues { movement, .. } = DifficultyValues::calculate(difficulty, converted);
|
||||||
|
|
||||||
CatchStrains {
|
CatchStrains {
|
||||||
|
|||||||
+9
-6
@@ -21,9 +21,9 @@
|
|||||||
//! let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap();
|
//! let map = rosu_pp::Beatmap::from_path("./resources/2785319.osu").unwrap();
|
||||||
//!
|
//!
|
||||||
//! // Calculate difficulty attributes
|
//! // Calculate difficulty attributes
|
||||||
//! let diff_attrs = map.difficulty()
|
//! let diff_attrs = rosu_pp::Difficulty::new()
|
||||||
//! .mods(8 + 16) // HDHR
|
//! .mods(8 + 16) // HDHR
|
||||||
//! .calculate();
|
//! .calculate(&map);
|
||||||
//!
|
//!
|
||||||
//! let stars = diff_attrs.stars();
|
//! let stars = diff_attrs.stars();
|
||||||
//!
|
//!
|
||||||
@@ -62,12 +62,15 @@
|
|||||||
//! score state.
|
//! score state.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use rosu_pp::{Beatmap, GradualPerformance, ModeDifficulty, any::ScoreState};
|
//! use rosu_pp::{Beatmap, GradualPerformance, Difficulty, any::ScoreState};
|
||||||
//!
|
//!
|
||||||
//! let map = Beatmap::from_path("./resources/1028484.osu").unwrap();
|
//! let map = Beatmap::from_path("./resources/1028484.osu").unwrap();
|
||||||
//! let difficulty = ModeDifficulty::new().mods(16 + 64).clock_rate(1.2); // HRDT on 1.2x
|
|
||||||
//!
|
//!
|
||||||
//! let mut gradual = GradualPerformance::new(&difficulty, &map);
|
//! let mut gradual = Difficulty::new()
|
||||||
|
//! .mods(16 + 64) // HRDT
|
||||||
|
//! .clock_rate(1.2)
|
||||||
|
//! .gradual_performance(&map);
|
||||||
|
//!
|
||||||
//! let mut state = ScoreState::new(); // empty state, everything is on 0.
|
//! let mut state = ScoreState::new(); // empty state, everything is on 0.
|
||||||
//!
|
//!
|
||||||
//! // The first 10 hitresults are 300s
|
//! // The first 10 hitresults are 300s
|
||||||
@@ -162,7 +165,7 @@
|
|||||||
|
|
||||||
#[doc(inline)]
|
#[doc(inline)]
|
||||||
pub use self::{
|
pub use self::{
|
||||||
any::{Difficulty, GradualDifficulty, GradualPerformance, ModeDifficulty, Performance},
|
any::{ConvertedDifficulty, Difficulty, GradualDifficulty, GradualPerformance, Performance},
|
||||||
model::beatmap::{Beatmap, Converted},
|
model::beatmap::{Beatmap, Converted},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{
|
|||||||
mania::{object::ObjectParams, ManiaBeatmap},
|
mania::{object::ObjectParams, ManiaBeatmap},
|
||||||
model::{beatmap::HitWindows, hit_object::HitObject},
|
model::{beatmap::HitWindows, hit_object::HitObject},
|
||||||
util::float_ext::FloatExt,
|
util::float_ext::FloatExt,
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -25,14 +25,14 @@ use super::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::mania::ManiaGradualDifficulty;
|
/// use rosu_pp::mania::ManiaGradualDifficulty;
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/1638954.osu")
|
/// let converted = Beatmap::from_path("./resources/1638954.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted();
|
/// .unchecked_into_converted();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut iter = ManiaGradualDifficulty::new(&difficulty, &converted);
|
/// let mut iter = ManiaGradualDifficulty::new(&difficulty, &converted);
|
||||||
///
|
///
|
||||||
/// // the difficulty of the map after the first hit object
|
/// // the difficulty of the map after the first hit object
|
||||||
@@ -61,7 +61,7 @@ pub struct ManiaGradualDifficulty {
|
|||||||
|
|
||||||
impl ManiaGradualDifficulty {
|
impl ManiaGradualDifficulty {
|
||||||
/// Create a new difficulty attributes iterator for osu!mania maps.
|
/// Create a new difficulty attributes iterator for osu!mania maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &ManiaBeatmap<'_>) -> Self {
|
||||||
let take = difficulty.get_passed_objects();
|
let take = difficulty.get_passed_objects();
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let total_columns = converted.cs.round_even().max(1.0);
|
let total_columns = converted.cs.round_even().max(1.0);
|
||||||
@@ -209,7 +209,7 @@ fn increment_combo_raw(is_circle: bool, start_time: f64, end_time: f64, curr_com
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{mania::Mania, Beatmap};
|
use crate::{any::difficulty::converted::ConvertedDifficulty, mania::Mania, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -219,7 +219,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Mania>();
|
.unchecked_into_converted::<Mania>();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
let mut gradual = ManiaGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = ManiaGradualDifficulty::new(&difficulty, &converted);
|
||||||
|
|
||||||
assert!(gradual.next().is_none());
|
assert!(gradual.next().is_none());
|
||||||
@@ -231,7 +231,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Mania>();
|
.unchecked_into_converted::<Mania>();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
|
|
||||||
let mut gradual = ManiaGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = ManiaGradualDifficulty::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = ManiaGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual_2nd = ManiaGradualDifficulty::new(&difficulty, &converted);
|
||||||
@@ -257,7 +257,7 @@ mod tests {
|
|||||||
assert_eq!(next_gradual, next_gradual_3rd);
|
assert_eq!(next_gradual, next_gradual_3rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = ModeDifficulty::new()
|
let expected = ConvertedDifficulty::new()
|
||||||
.passed_objects(i as u32)
|
.passed_objects(i as u32)
|
||||||
.calculate(&converted);
|
.calculate(&converted);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::difficulty::{mode::ModeDifficulty, skills::Skill},
|
any::difficulty::{skills::Skill, Difficulty},
|
||||||
mania::{
|
mania::{
|
||||||
difficulty::{object::ManiaDifficultyObject, skills::strain::Strain},
|
difficulty::{object::ManiaDifficultyObject, skills::strain::Strain},
|
||||||
object::{ManiaObject, ObjectParams},
|
object::{ManiaObject, ObjectParams},
|
||||||
@@ -18,7 +18,7 @@ mod skills;
|
|||||||
const STAR_SCALING_FACTOR: f64 = 0.018;
|
const STAR_SCALING_FACTOR: f64 = 0.018;
|
||||||
|
|
||||||
pub fn difficulty(
|
pub fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &ManiaBeatmap<'_>,
|
converted: &ManiaBeatmap<'_>,
|
||||||
) -> ManiaDifficultyAttributes {
|
) -> ManiaDifficultyAttributes {
|
||||||
let n_objects = cmp::min(difficulty.get_passed_objects(), converted.hit_objects.len()) as u32;
|
let n_objects = cmp::min(difficulty.get_passed_objects(), converted.hit_objects.len()) as u32;
|
||||||
@@ -47,7 +47,7 @@ pub struct DifficultyValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DifficultyValues {
|
impl DifficultyValues {
|
||||||
pub fn calculate(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> Self {
|
pub fn calculate(difficulty: &Difficulty, converted: &ManiaBeatmap<'_>) -> Self {
|
||||||
let take = difficulty.get_passed_objects();
|
let take = difficulty.get_passed_objects();
|
||||||
let total_columns = converted.cs.round_even().max(1.0);
|
let total_columns = converted.cs.round_even().max(1.0);
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
|
||||||
model::{
|
model::{
|
||||||
beatmap::Beatmap,
|
beatmap::Beatmap,
|
||||||
mode::{ConvertStatus, IGameMode},
|
mode::{ConvertStatus, IGameMode},
|
||||||
},
|
},
|
||||||
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@@ -44,13 +44,13 @@ impl IGameMode for Mania {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn difficulty(
|
fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &ManiaBeatmap<'_>,
|
converted: &ManiaBeatmap<'_>,
|
||||||
) -> Self::DifficultyAttributes {
|
) -> Self::DifficultyAttributes {
|
||||||
difficulty::difficulty(difficulty, converted)
|
difficulty::difficulty(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strains(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> Self::Strains {
|
fn strains(difficulty: &Difficulty, converted: &ManiaBeatmap<'_>) -> Self::Strains {
|
||||||
strains::strains(difficulty, converted)
|
strains::strains(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,14 +59,14 @@ impl IGameMode for Mania {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_difficulty(
|
fn gradual_difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &ManiaBeatmap<'_>,
|
map: &ManiaBeatmap<'_>,
|
||||||
) -> Self::GradualDifficulty {
|
) -> Self::GradualDifficulty {
|
||||||
ManiaGradualDifficulty::new(difficulty, map)
|
ManiaGradualDifficulty::new(difficulty, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_performance(
|
fn gradual_performance(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &ManiaBeatmap<'_>,
|
map: &ManiaBeatmap<'_>,
|
||||||
) -> Self::GradualPerformance {
|
) -> Self::GradualPerformance {
|
||||||
ManiaGradualPerformance::new(difficulty, map)
|
ManiaGradualPerformance::new(difficulty, map)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
mania::{ManiaBeatmap, ManiaGradualDifficulty},
|
mania::{ManiaBeatmap, ManiaGradualDifficulty},
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ManiaPerformanceAttributes, ManiaScoreState};
|
use super::{ManiaPerformanceAttributes, ManiaScoreState};
|
||||||
@@ -20,14 +20,14 @@ use super::{ManiaPerformanceAttributes, ManiaScoreState};
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::mania::{Mania, ManiaGradualPerformance, ManiaScoreState};
|
/// use rosu_pp::mania::{Mania, ManiaGradualPerformance, ManiaScoreState};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/1638954.osu")
|
/// let converted = Beatmap::from_path("./resources/1638954.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Mania>();
|
/// .unchecked_into_converted::<Mania>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut gradual = ManiaGradualPerformance::new(&difficulty, &converted);
|
/// let mut gradual = ManiaGradualPerformance::new(&difficulty, &converted);
|
||||||
/// let mut state = ManiaScoreState::new(); // empty state, everything is on 0.
|
/// let mut state = ManiaScoreState::new(); // empty state, everything is on 0.
|
||||||
///
|
///
|
||||||
@@ -75,7 +75,7 @@ pub struct ManiaGradualPerformance {
|
|||||||
|
|
||||||
impl ManiaGradualPerformance {
|
impl ManiaGradualPerformance {
|
||||||
/// Create a new gradual performance calculator for osu!mania maps.
|
/// Create a new gradual performance calculator for osu!mania maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &ManiaBeatmap<'_>) -> Self {
|
||||||
let difficulty = ManiaGradualDifficulty::new(difficulty, converted);
|
let difficulty = ManiaGradualDifficulty::new(difficulty, converted);
|
||||||
|
|
||||||
Self { difficulty }
|
Self { difficulty }
|
||||||
@@ -129,7 +129,7 @@ mod tests {
|
|||||||
.unchecked_into_converted::<Mania>();
|
.unchecked_into_converted::<Mania>();
|
||||||
|
|
||||||
let mods = 88; // HDHRDT
|
let mods = 88; // HDHRDT
|
||||||
let difficulty = ModeDifficulty::new().mods(88);
|
let difficulty = Difficulty::new().mods(88);
|
||||||
|
|
||||||
let mut gradual = ManiaGradualPerformance::new(&difficulty, &converted);
|
let mut gradual = ManiaGradualPerformance::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = ManiaGradualPerformance::new(&difficulty, &converted);
|
let mut gradual_2nd = ManiaGradualPerformance::new(&difficulty, &converted);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
any::{Difficulty, HitResultPriority, ModeAttributeProvider},
|
||||||
any::{HitResultPriority, ModeAttributeProvider},
|
|
||||||
osu::OsuPerformance,
|
osu::OsuPerformance,
|
||||||
util::{map_or_attrs::MapOrAttrs, mods::Mods},
|
util::{map_or_attrs::MapOrAttrs, mods::Mods},
|
||||||
};
|
};
|
||||||
@@ -21,7 +20,7 @@ pub mod gradual;
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct ManiaPerformance<'map> {
|
pub struct ManiaPerformance<'map> {
|
||||||
map_or_attrs: MapOrAttrs<'map, Mania>,
|
map_or_attrs: MapOrAttrs<'map, Mania>,
|
||||||
difficulty: ModeDifficulty,
|
difficulty: Difficulty,
|
||||||
n320: Option<u32>,
|
n320: Option<u32>,
|
||||||
n300: Option<u32>,
|
n300: Option<u32>,
|
||||||
n200: Option<u32>,
|
n200: Option<u32>,
|
||||||
@@ -37,7 +36,7 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
pub const fn new(map: ManiaBeatmap<'map>) -> Self {
|
pub const fn new(map: ManiaBeatmap<'map>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Map(map),
|
map_or_attrs: MapOrAttrs::Map(map),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
n320: None,
|
n320: None,
|
||||||
n300: None,
|
n300: None,
|
||||||
n200: None,
|
n200: None,
|
||||||
@@ -52,7 +51,7 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
pub(crate) const fn from_mania_attributes(attrs: ManiaDifficultyAttributes) -> Self {
|
pub(crate) const fn from_mania_attributes(attrs: ManiaDifficultyAttributes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
n320: None,
|
n320: None,
|
||||||
n300: None,
|
n300: None,
|
||||||
n200: None,
|
n200: None,
|
||||||
@@ -192,7 +191,7 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
pub fn generate_state(&mut self) -> ManiaScoreState {
|
pub fn generate_state(&mut self) -> ManiaScoreState {
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => {
|
MapOrAttrs::Map(ref map) => {
|
||||||
let attrs = self.generate_attributes(map);
|
let attrs = self.difficulty.with_mode().calculate(map);
|
||||||
|
|
||||||
self.map_or_attrs.insert_attrs(attrs)
|
self.map_or_attrs.insert_attrs(attrs)
|
||||||
}
|
}
|
||||||
@@ -718,7 +717,7 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
let state = self.generate_state();
|
let state = self.generate_state();
|
||||||
|
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => self.generate_attributes(map),
|
MapOrAttrs::Map(ref map) => self.difficulty.with_mode().calculate(map),
|
||||||
MapOrAttrs::Attrs(attrs) => attrs,
|
MapOrAttrs::Attrs(attrs) => attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -731,10 +730,6 @@ impl<'map> ManiaPerformance<'map> {
|
|||||||
inner.calculate()
|
inner.calculate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_attributes(&self, map: &ManiaBeatmap<'_>) -> ManiaDifficultyAttributes {
|
|
||||||
self.difficulty.calculate(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to create [`ManiaPerformance`] through a [`ModeAttributeProvider`].
|
/// Try to create [`ManiaPerformance`] through a [`ModeAttributeProvider`].
|
||||||
///
|
///
|
||||||
/// If you already calculated the attributes for the current map-mod
|
/// If you already calculated the attributes for the current map-mod
|
||||||
@@ -921,7 +916,7 @@ mod tests {
|
|||||||
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use crate::Beatmap;
|
use crate::{any::difficulty::converted::ConvertedDifficulty, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -936,7 +931,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Mania>();
|
.unchecked_into_converted::<Mania>();
|
||||||
|
|
||||||
let attrs = ModeDifficulty::new().calculate(&converted);
|
let attrs = ConvertedDifficulty::new().calculate(&converted);
|
||||||
|
|
||||||
assert_eq!(N_OBJECTS, converted.hit_objects.len() as u32);
|
assert_eq!(N_OBJECTS, converted.hit_objects.len() as u32);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{any::ModeDifficulty, mania::difficulty::DifficultyValues};
|
use crate::{any::Difficulty, mania::difficulty::DifficultyValues};
|
||||||
|
|
||||||
use super::convert::ManiaBeatmap;
|
use super::convert::ManiaBeatmap;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ impl ManiaStrains {
|
|||||||
pub const SECTION_LEN: f64 = 400.0;
|
pub const SECTION_LEN: f64 = 400.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strains(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> ManiaStrains {
|
pub fn strains(difficulty: &Difficulty, converted: &ManiaBeatmap<'_>) -> ManiaStrains {
|
||||||
let values = DifficultyValues::calculate(difficulty, converted);
|
let values = DifficultyValues::calculate(difficulty, converted);
|
||||||
|
|
||||||
ManiaStrains {
|
ManiaStrains {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use std::{
|
use std::{
|
||||||
any,
|
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
fmt::{Debug, Formatter, Result as FmtResult},
|
fmt::{Debug, Formatter, Result as FmtResult},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
@@ -8,7 +7,8 @@ use std::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
model::mode::{ConvertStatus, IGameMode},
|
model::mode::{ConvertStatus, IGameMode},
|
||||||
ModeDifficulty,
|
util::generic_fmt::GenericFormatter,
|
||||||
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Beatmap;
|
use super::Beatmap;
|
||||||
@@ -74,13 +74,13 @@ impl<M: IGameMode> Converted<'_, M> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a gradual difficulty calculator for the map.
|
/// Create a gradual difficulty calculator for the map.
|
||||||
pub fn gradual_difficulty(&self, difficulty: &ModeDifficulty) -> M::GradualDifficulty {
|
pub fn gradual_difficulty(&self, difficulty: &Difficulty) -> M::GradualDifficulty {
|
||||||
difficulty.gradual_difficulty(self)
|
M::gradual_difficulty(difficulty, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a gradual performance calculator for the map.
|
/// Create a gradual performance calculator for the map.
|
||||||
pub fn gradual_performance(&self, difficulty: &ModeDifficulty) -> M::GradualPerformance {
|
pub fn gradual_performance(&self, difficulty: &Difficulty) -> M::GradualPerformance {
|
||||||
difficulty.gradual_performance(self)
|
M::gradual_performance(difficulty, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,34 +189,9 @@ impl<M> Clone for Converted<'_, M> {
|
|||||||
|
|
||||||
impl<M> Debug for Converted<'_, M> {
|
impl<M> Debug for Converted<'_, M> {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
struct GenericFormatter<T>(PhantomData<T>);
|
|
||||||
|
|
||||||
impl<T> Default for GenericFormatter<T> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self(PhantomData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Debug for GenericFormatter<T> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
|
||||||
fn fmt_stripped(full_type_name: &str, f: &mut Formatter<'_>) -> FmtResult {
|
|
||||||
// Strip fully qualified syntax
|
|
||||||
if let Some(position) = full_type_name.rfind("::") {
|
|
||||||
if let Some(type_name) = full_type_name.get(position + 2..) {
|
|
||||||
f.write_str(type_name)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt_stripped(any::type_name::<T>(), f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
f.debug_struct("Converted")
|
f.debug_struct("Converted")
|
||||||
.field("map", &self.map)
|
.field("map", &self.map)
|
||||||
.field("mode", &GenericFormatter::<M>::default())
|
.field("mode", &GenericFormatter::<M>::new())
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use rosu_map::{
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
catch::Catch, mania::Mania, osu::Osu, taiko::Taiko, Difficulty, GradualDifficulty,
|
catch::Catch, mania::Mania, osu::Osu, taiko::Taiko, Difficulty, GradualDifficulty,
|
||||||
GradualPerformance, ModeDifficulty, Performance,
|
GradualPerformance, Performance,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@@ -85,23 +85,18 @@ impl Beatmap {
|
|||||||
bpm::bpm(self.hit_objects.last(), &self.timing_points)
|
bpm::bpm(self.hit_objects.last(), &self.timing_points)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a difficulty calculator for this [`Beatmap`].
|
|
||||||
pub const fn difficulty(&self) -> Difficulty<'_> {
|
|
||||||
Difficulty::new(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a performance calculator for this [`Beatmap`].
|
/// Create a performance calculator for this [`Beatmap`].
|
||||||
pub const fn performance(&self) -> Performance<'_> {
|
pub const fn performance(&self) -> Performance<'_> {
|
||||||
Performance::new(self)
|
Performance::new(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a gradual difficulty calculator for this [`Beatmap`].
|
/// Create a gradual difficulty calculator for this [`Beatmap`].
|
||||||
pub fn gradual_difficulty(&self, difficulty: &ModeDifficulty) -> GradualDifficulty {
|
pub fn gradual_difficulty(&self, difficulty: &Difficulty) -> GradualDifficulty {
|
||||||
GradualDifficulty::new(difficulty, self)
|
GradualDifficulty::new(difficulty, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a gradual performance calculator for this [`Beatmap`].
|
/// Create a gradual performance calculator for this [`Beatmap`].
|
||||||
pub fn gradual_performance(&self, difficulty: &ModeDifficulty) -> GradualPerformance {
|
pub fn gradual_performance(&self, difficulty: &Difficulty) -> GradualPerformance {
|
||||||
GradualPerformance::new(difficulty, self)
|
GradualPerformance::new(difficulty, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,7 +121,7 @@ impl Beatmap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a [`&mut Beatmap`] to the specified mode with an argument
|
/// Convert a [`&mut Beatmap`] to the specified mode with an argument
|
||||||
/// instead of a generic parameter and return the [`ConvertStatus`].
|
/// instead of a generic parameter.
|
||||||
///
|
///
|
||||||
/// [`&mut Beatmap`]: Beatmap
|
/// [`&mut Beatmap`]: Beatmap
|
||||||
pub fn convert_inplace(&mut self, mode: GameMode) -> ConvertStatus {
|
pub fn convert_inplace(&mut self, mode: GameMode) -> ConvertStatus {
|
||||||
|
|||||||
+6
-8
@@ -1,6 +1,6 @@
|
|||||||
pub use rosu_map::section::general::GameMode;
|
pub use rosu_map::section::general::GameMode;
|
||||||
|
|
||||||
use crate::any::ModeDifficulty;
|
use crate::Difficulty;
|
||||||
|
|
||||||
use super::beatmap::{Beatmap, Converted};
|
use super::beatmap::{Beatmap, Converted};
|
||||||
|
|
||||||
@@ -40,27 +40,25 @@ pub trait IGameMode: Sized {
|
|||||||
|
|
||||||
/// Perform a difficulty calculation for a [`Converted`] beatmap and
|
/// Perform a difficulty calculation for a [`Converted`] beatmap and
|
||||||
/// process the final skill values.
|
/// process the final skill values.
|
||||||
fn difficulty(
|
fn difficulty(difficulty: &Difficulty, map: &Converted<'_, Self>)
|
||||||
difficulty: &ModeDifficulty,
|
-> Self::DifficultyAttributes;
|
||||||
map: &Converted<'_, Self>,
|
|
||||||
) -> Self::DifficultyAttributes;
|
|
||||||
|
|
||||||
/// Perform a difficulty calculation for a [`Converted`] beatmap without
|
/// Perform a difficulty calculation for a [`Converted`] beatmap without
|
||||||
/// processing the final skill values.
|
/// processing the final skill values.
|
||||||
fn strains(difficulty: &ModeDifficulty, map: &Converted<'_, Self>) -> Self::Strains;
|
fn strains(difficulty: &Difficulty, map: &Converted<'_, Self>) -> Self::Strains;
|
||||||
|
|
||||||
/// Create a performance calculator for a [`Converted`] beatmap.
|
/// Create a performance calculator for a [`Converted`] beatmap.
|
||||||
fn performance(map: Converted<'_, Self>) -> Self::Performance<'_>;
|
fn performance(map: Converted<'_, Self>) -> Self::Performance<'_>;
|
||||||
|
|
||||||
/// Create a gradual difficulty calculator for a [`Converted`] beatmap.
|
/// Create a gradual difficulty calculator for a [`Converted`] beatmap.
|
||||||
fn gradual_difficulty(
|
fn gradual_difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &Converted<'_, Self>,
|
map: &Converted<'_, Self>,
|
||||||
) -> Self::GradualDifficulty;
|
) -> Self::GradualDifficulty;
|
||||||
|
|
||||||
/// Create a gradual performance calculator for a [`Converted`] beatmap.
|
/// Create a gradual performance calculator for a [`Converted`] beatmap.
|
||||||
fn gradual_performance(
|
fn gradual_performance(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &Converted<'_, Self>,
|
map: &Converted<'_, Self>,
|
||||||
) -> Self::GradualPerformance;
|
) -> Self::GradualPerformance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use crate::{
|
|||||||
OsuBeatmap,
|
OsuBeatmap,
|
||||||
},
|
},
|
||||||
util::mods::Mods,
|
util::mods::Mods,
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::osu_objects::OsuObjects;
|
use self::osu_objects::OsuObjects;
|
||||||
@@ -31,14 +31,14 @@ use super::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::osu::{Osu, OsuGradualDifficulty};
|
/// use rosu_pp::osu::{Osu, OsuGradualDifficulty};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/2785319.osu")
|
/// let converted = Beatmap::from_path("./resources/2785319.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Osu>();
|
/// .unchecked_into_converted::<Osu>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut iter = OsuGradualDifficulty::new(&difficulty, &converted);
|
/// let mut iter = OsuGradualDifficulty::new(&difficulty, &converted);
|
||||||
///
|
///
|
||||||
/// // the difficulty of the map after the first hit object
|
/// // the difficulty of the map after the first hit object
|
||||||
@@ -73,7 +73,7 @@ struct NotClonable;
|
|||||||
|
|
||||||
impl OsuGradualDifficulty {
|
impl OsuGradualDifficulty {
|
||||||
/// Create a new difficulty attributes iterator for osu!standard maps.
|
/// Create a new difficulty attributes iterator for osu!standard maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &OsuBeatmap<'_>) -> Self {
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|
||||||
@@ -262,7 +262,7 @@ mod osu_objects {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{osu::Osu, Beatmap};
|
use crate::{any::difficulty::converted::ConvertedDifficulty, osu::Osu, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -272,7 +272,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Osu>();
|
.unchecked_into_converted::<Osu>();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
let mut gradual = OsuGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = OsuGradualDifficulty::new(&difficulty, &converted);
|
||||||
|
|
||||||
assert!(gradual.next().is_none());
|
assert!(gradual.next().is_none());
|
||||||
@@ -284,7 +284,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Osu>();
|
.unchecked_into_converted::<Osu>();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
|
|
||||||
let mut gradual = OsuGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = OsuGradualDifficulty::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = OsuGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual_2nd = OsuGradualDifficulty::new(&difficulty, &converted);
|
||||||
@@ -310,7 +310,7 @@ mod tests {
|
|||||||
assert_eq!(next_gradual, next_gradual_3rd);
|
assert_eq!(next_gradual, next_gradual_3rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = ModeDifficulty::new()
|
let expected = ConvertedDifficulty::new()
|
||||||
.passed_objects(i as u32)
|
.passed_objects(i as u32)
|
||||||
.calculate(&converted);
|
.calculate(&converted);
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use std::{cmp, pin::Pin};
|
use std::{cmp, pin::Pin};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::difficulty::{mode::ModeDifficulty, skills::Skill},
|
any::difficulty::{skills::Skill, Difficulty},
|
||||||
model::beatmap::BeatmapAttributes,
|
model::beatmap::BeatmapAttributes,
|
||||||
osu::{
|
osu::{
|
||||||
convert::convert_objects,
|
convert::convert_objects,
|
||||||
@@ -26,10 +26,7 @@ const DIFFICULTY_MULTIPLIER: f64 = 0.0675;
|
|||||||
const HD_FADE_IN_DURATION_MULTIPLIER: f64 = 0.4;
|
const HD_FADE_IN_DURATION_MULTIPLIER: f64 = 0.4;
|
||||||
const HD_FADE_OUT_DURATION_MULTIPLIER: f64 = 0.3;
|
const HD_FADE_OUT_DURATION_MULTIPLIER: f64 = 0.3;
|
||||||
|
|
||||||
pub fn difficulty(
|
pub fn difficulty(difficulty: &Difficulty, converted: &OsuBeatmap<'_>) -> OsuDifficultyAttributes {
|
||||||
difficulty: &ModeDifficulty,
|
|
||||||
converted: &OsuBeatmap<'_>,
|
|
||||||
) -> OsuDifficultyAttributes {
|
|
||||||
let DifficultyValues {
|
let DifficultyValues {
|
||||||
skills:
|
skills:
|
||||||
OsuSkills {
|
OsuSkills {
|
||||||
@@ -70,7 +67,7 @@ pub struct OsuDifficultySetup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OsuDifficultySetup {
|
impl OsuDifficultySetup {
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &OsuBeatmap) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &OsuBeatmap) -> Self {
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|
||||||
@@ -106,7 +103,7 @@ pub struct DifficultyValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DifficultyValues {
|
impl DifficultyValues {
|
||||||
pub fn calculate(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> Self {
|
pub fn calculate(difficulty: &Difficulty, converted: &OsuBeatmap<'_>) -> Self {
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let take = difficulty.get_passed_objects();
|
let take = difficulty.get_passed_objects();
|
||||||
|
|
||||||
@@ -218,7 +215,7 @@ impl DifficultyValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_difficulty_objects<'a>(
|
pub fn create_difficulty_objects<'a>(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
scaling_factor: &ScalingFactor,
|
scaling_factor: &ScalingFactor,
|
||||||
osu_objects: impl ExactSizeIterator<Item = Pin<&'a mut OsuObject>>,
|
osu_objects: impl ExactSizeIterator<Item = Pin<&'a mut OsuObject>>,
|
||||||
) -> Vec<OsuDifficultyObject<'a>> {
|
) -> Vec<OsuDifficultyObject<'a>> {
|
||||||
|
|||||||
+5
-5
@@ -1,11 +1,11 @@
|
|||||||
use rosu_map::util::Pos;
|
use rosu_map::util::Pos;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
|
||||||
model::{
|
model::{
|
||||||
beatmap::Beatmap,
|
beatmap::Beatmap,
|
||||||
mode::{ConvertStatus, IGameMode},
|
mode::{ConvertStatus, IGameMode},
|
||||||
},
|
},
|
||||||
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@@ -48,13 +48,13 @@ impl IGameMode for Osu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn difficulty(
|
fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &OsuBeatmap<'_>,
|
converted: &OsuBeatmap<'_>,
|
||||||
) -> Self::DifficultyAttributes {
|
) -> Self::DifficultyAttributes {
|
||||||
difficulty::difficulty(difficulty, converted)
|
difficulty::difficulty(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strains(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> Self::Strains {
|
fn strains(difficulty: &Difficulty, converted: &OsuBeatmap<'_>) -> Self::Strains {
|
||||||
strains::strains(difficulty, converted)
|
strains::strains(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,14 +63,14 @@ impl IGameMode for Osu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_difficulty(
|
fn gradual_difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &OsuBeatmap<'_>,
|
map: &OsuBeatmap<'_>,
|
||||||
) -> Self::GradualDifficulty {
|
) -> Self::GradualDifficulty {
|
||||||
OsuGradualDifficulty::new(difficulty, map)
|
OsuGradualDifficulty::new(difficulty, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_performance(
|
fn gradual_performance(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &OsuBeatmap<'_>,
|
map: &OsuBeatmap<'_>,
|
||||||
) -> Self::GradualPerformance {
|
) -> Self::GradualPerformance {
|
||||||
OsuGradualPerformance::new(difficulty, map)
|
OsuGradualPerformance::new(difficulty, map)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
osu::{OsuBeatmap, OsuGradualDifficulty},
|
osu::{OsuBeatmap, OsuGradualDifficulty},
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{OsuPerformanceAttributes, OsuScoreState};
|
use super::{OsuPerformanceAttributes, OsuScoreState};
|
||||||
@@ -20,14 +20,14 @@ use super::{OsuPerformanceAttributes, OsuScoreState};
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::osu::{Osu, OsuGradualPerformance, OsuScoreState};
|
/// use rosu_pp::osu::{Osu, OsuGradualPerformance, OsuScoreState};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/2785319.osu")
|
/// let converted = Beatmap::from_path("./resources/2785319.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Osu>();
|
/// .unchecked_into_converted::<Osu>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut gradual = OsuGradualPerformance::new(&difficulty, &converted);
|
/// let mut gradual = OsuGradualPerformance::new(&difficulty, &converted);
|
||||||
/// let mut state = OsuScoreState::new(); // empty state, everything is on 0.
|
/// let mut state = OsuScoreState::new(); // empty state, everything is on 0.
|
||||||
///
|
///
|
||||||
@@ -85,7 +85,7 @@ pub struct OsuGradualPerformance {
|
|||||||
|
|
||||||
impl OsuGradualPerformance {
|
impl OsuGradualPerformance {
|
||||||
/// Create a new gradual performance calculator for osu!standard maps.
|
/// Create a new gradual performance calculator for osu!standard maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &OsuBeatmap<'_>) -> Self {
|
||||||
let difficulty = OsuGradualDifficulty::new(difficulty, converted);
|
let difficulty = OsuGradualDifficulty::new(difficulty, converted);
|
||||||
|
|
||||||
Self { difficulty }
|
Self { difficulty }
|
||||||
@@ -139,7 +139,7 @@ mod tests {
|
|||||||
.unchecked_into_converted::<Osu>();
|
.unchecked_into_converted::<Osu>();
|
||||||
|
|
||||||
let mods = 88; // HDHRDT
|
let mods = 88; // HDHRDT
|
||||||
let difficulty = ModeDifficulty::new().mods(88);
|
let difficulty = Difficulty::new().mods(88);
|
||||||
|
|
||||||
let mut gradual = OsuGradualPerformance::new(&difficulty, &converted);
|
let mut gradual = OsuGradualPerformance::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = OsuGradualPerformance::new(&difficulty, &converted);
|
let mut gradual_2nd = OsuGradualPerformance::new(&difficulty, &converted);
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use std::cmp;
|
|||||||
use rosu_map::section::general::GameMode;
|
use rosu_map::section::general::GameMode;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
any::{Difficulty, HitResultPriority, ModeAttributeProvider, Performance},
|
||||||
any::{HitResultPriority, ModeAttributeProvider, Performance},
|
|
||||||
catch::CatchPerformance,
|
catch::CatchPerformance,
|
||||||
mania::ManiaPerformance,
|
mania::ManiaPerformance,
|
||||||
taiko::TaikoPerformance,
|
taiko::TaikoPerformance,
|
||||||
@@ -25,7 +24,7 @@ pub mod gradual;
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct OsuPerformance<'map> {
|
pub struct OsuPerformance<'map> {
|
||||||
pub(crate) map_or_attrs: MapOrAttrs<'map, Osu>,
|
pub(crate) map_or_attrs: MapOrAttrs<'map, Osu>,
|
||||||
pub(crate) difficulty: ModeDifficulty,
|
pub(crate) difficulty: Difficulty,
|
||||||
pub(crate) acc: Option<f64>,
|
pub(crate) acc: Option<f64>,
|
||||||
pub(crate) combo: Option<u32>,
|
pub(crate) combo: Option<u32>,
|
||||||
pub(crate) n300: Option<u32>,
|
pub(crate) n300: Option<u32>,
|
||||||
@@ -40,7 +39,7 @@ impl<'map> OsuPerformance<'map> {
|
|||||||
pub const fn new(map: OsuBeatmap<'map>) -> Self {
|
pub const fn new(map: OsuBeatmap<'map>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Map(map),
|
map_or_attrs: MapOrAttrs::Map(map),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
acc: None,
|
acc: None,
|
||||||
combo: None,
|
combo: None,
|
||||||
n300: None,
|
n300: None,
|
||||||
@@ -54,7 +53,7 @@ impl<'map> OsuPerformance<'map> {
|
|||||||
pub(crate) const fn from_osu_attributes(attrs: OsuDifficultyAttributes) -> Self {
|
pub(crate) const fn from_osu_attributes(attrs: OsuDifficultyAttributes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
acc: None,
|
acc: None,
|
||||||
combo: None,
|
combo: None,
|
||||||
n300: None,
|
n300: None,
|
||||||
@@ -233,7 +232,7 @@ impl<'map> OsuPerformance<'map> {
|
|||||||
pub fn generate_state(&mut self) -> OsuScoreState {
|
pub fn generate_state(&mut self) -> OsuScoreState {
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => {
|
MapOrAttrs::Map(ref map) => {
|
||||||
let attrs = self.generate_attributes(map);
|
let attrs = self.difficulty.with_mode().calculate(map);
|
||||||
|
|
||||||
self.map_or_attrs.insert_attrs(attrs)
|
self.map_or_attrs.insert_attrs(attrs)
|
||||||
}
|
}
|
||||||
@@ -417,7 +416,7 @@ impl<'map> OsuPerformance<'map> {
|
|||||||
let state = self.generate_state();
|
let state = self.generate_state();
|
||||||
|
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => self.generate_attributes(map),
|
MapOrAttrs::Map(ref map) => self.difficulty.with_mode().calculate(map),
|
||||||
MapOrAttrs::Attrs(attrs) => attrs,
|
MapOrAttrs::Attrs(attrs) => attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -434,10 +433,6 @@ impl<'map> OsuPerformance<'map> {
|
|||||||
inner.calculate()
|
inner.calculate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_attributes(&self, map: &OsuBeatmap<'_>) -> OsuDifficultyAttributes {
|
|
||||||
self.difficulty.calculate(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to create [`OsuPerformance`] through a [`ModeAttributeProvider`].
|
/// Try to create [`OsuPerformance`] through a [`ModeAttributeProvider`].
|
||||||
///
|
///
|
||||||
/// If you already calculated the attributes for the current map-mod
|
/// If you already calculated the attributes for the current map-mod
|
||||||
@@ -828,7 +823,7 @@ mod test {
|
|||||||
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use crate::Beatmap;
|
use crate::{any::difficulty::converted::ConvertedDifficulty, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -843,7 +838,7 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Osu>();
|
.unchecked_into_converted::<Osu>();
|
||||||
|
|
||||||
let attrs = ModeDifficulty::new().calculate(&converted);
|
let attrs = ConvertedDifficulty::new().calculate(&converted);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
(attrs.n_circles, attrs.n_sliders, attrs.n_spinners),
|
(attrs.n_circles, attrs.n_sliders, attrs.n_spinners),
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
use crate::any::ModeDifficulty;
|
use crate::Difficulty;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
convert::OsuBeatmap,
|
convert::OsuBeatmap,
|
||||||
@@ -25,7 +25,7 @@ impl OsuStrains {
|
|||||||
pub const SECTION_LEN: f64 = 400.0;
|
pub const SECTION_LEN: f64 = 400.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strains(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> OsuStrains {
|
pub fn strains(difficulty: &Difficulty, converted: &OsuBeatmap<'_>) -> OsuStrains {
|
||||||
let DifficultyValues {
|
let DifficultyValues {
|
||||||
skills:
|
skills:
|
||||||
OsuSkills {
|
OsuSkills {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::{
|
|||||||
model::{beatmap::HitWindows, hit_object::HitObject},
|
model::{beatmap::HitWindows, hit_object::HitObject},
|
||||||
taiko::TaikoBeatmap,
|
taiko::TaikoBeatmap,
|
||||||
util::sync::RefCount,
|
util::sync::RefCount,
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@@ -25,14 +25,14 @@ use super::{
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::taiko::{Taiko, TaikoGradualDifficulty};
|
/// use rosu_pp::taiko::{Taiko, TaikoGradualDifficulty};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/1028484.osu")
|
/// let converted = Beatmap::from_path("./resources/1028484.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Taiko>();
|
/// .unchecked_into_converted::<Taiko>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut iter = TaikoGradualDifficulty::new(&difficulty, &converted);
|
/// let mut iter = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||||
///
|
///
|
||||||
/// // the difficulty of the map after the first hit object
|
/// // the difficulty of the map after the first hit object
|
||||||
@@ -69,7 +69,7 @@ enum FirstTwoCombos {
|
|||||||
|
|
||||||
impl TaikoGradualDifficulty {
|
impl TaikoGradualDifficulty {
|
||||||
/// Create a new difficulty attributes iterator for osu!taiko maps.
|
/// Create a new difficulty attributes iterator for osu!taiko maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &TaikoBeatmap<'_>) -> Self {
|
||||||
let take = difficulty.get_passed_objects();
|
let take = difficulty.get_passed_objects();
|
||||||
let mods = difficulty.get_mods();
|
let mods = difficulty.get_mods();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
@@ -260,8 +260,7 @@ impl ExactSizeIterator for TaikoGradualDifficulty {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::{any::difficulty::converted::ConvertedDifficulty, Beatmap};
|
||||||
use crate::Beatmap;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -269,7 +268,7 @@ mod tests {
|
|||||||
fn empty() {
|
fn empty() {
|
||||||
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||||
|
|
||||||
assert!(gradual.next().is_none());
|
assert!(gradual.next().is_none());
|
||||||
@@ -281,7 +280,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted();
|
.unchecked_into_converted();
|
||||||
|
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
|
|
||||||
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = TaikoGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual_2nd = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||||
@@ -313,7 +312,7 @@ mod tests {
|
|||||||
assert_eq!(next_gradual, next_gradual_3rd);
|
assert_eq!(next_gradual, next_gradual_3rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
let expected = ModeDifficulty::new()
|
let expected = ConvertedDifficulty::new()
|
||||||
.passed_objects(i as u32)
|
.passed_objects(i as u32)
|
||||||
.calculate(&converted);
|
.calculate(&converted);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
any::difficulty::mode::ModeDifficulty,
|
|
||||||
taiko::{
|
taiko::{
|
||||||
difficulty::{
|
difficulty::{
|
||||||
color::preprocessor::ColorDifficultyPreprocessor,
|
color::preprocessor::ColorDifficultyPreprocessor,
|
||||||
@@ -8,6 +7,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
object::TaikoObject,
|
object::TaikoObject,
|
||||||
},
|
},
|
||||||
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::skills::peaks::Peaks;
|
use self::skills::peaks::Peaks;
|
||||||
@@ -23,7 +23,7 @@ mod skills;
|
|||||||
const DIFFICULTY_MULTIPLIER: f64 = 1.35;
|
const DIFFICULTY_MULTIPLIER: f64 = 1.35;
|
||||||
|
|
||||||
pub fn difficulty(
|
pub fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &TaikoBeatmap<'_>,
|
converted: &TaikoBeatmap<'_>,
|
||||||
) -> TaikoDifficultyAttributes {
|
) -> TaikoDifficultyAttributes {
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
@@ -74,7 +74,7 @@ pub struct DifficultyValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl DifficultyValues {
|
impl DifficultyValues {
|
||||||
pub fn calculate(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> Self {
|
pub fn calculate(difficulty: &Difficulty, converted: &TaikoBeatmap<'_>) -> Self {
|
||||||
let take = difficulty.get_passed_objects();
|
let take = difficulty.get_passed_objects();
|
||||||
let clock_rate = difficulty.get_clock_rate();
|
let clock_rate = difficulty.get_clock_rate();
|
||||||
|
|
||||||
|
|||||||
+5
-5
@@ -1,9 +1,9 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
|
||||||
model::{
|
model::{
|
||||||
beatmap::Beatmap,
|
beatmap::Beatmap,
|
||||||
mode::{ConvertStatus, IGameMode},
|
mode::{ConvertStatus, IGameMode},
|
||||||
},
|
},
|
||||||
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
@@ -44,13 +44,13 @@ impl IGameMode for Taiko {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn difficulty(
|
fn difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
converted: &TaikoBeatmap<'_>,
|
converted: &TaikoBeatmap<'_>,
|
||||||
) -> Self::DifficultyAttributes {
|
) -> Self::DifficultyAttributes {
|
||||||
difficulty::difficulty(difficulty, converted)
|
difficulty::difficulty(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn strains(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> Self::Strains {
|
fn strains(difficulty: &Difficulty, converted: &TaikoBeatmap<'_>) -> Self::Strains {
|
||||||
strains::strains(difficulty, converted)
|
strains::strains(difficulty, converted)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,14 +59,14 @@ impl IGameMode for Taiko {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_difficulty(
|
fn gradual_difficulty(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &TaikoBeatmap<'_>,
|
map: &TaikoBeatmap<'_>,
|
||||||
) -> Self::GradualDifficulty {
|
) -> Self::GradualDifficulty {
|
||||||
TaikoGradualDifficulty::new(difficulty, map)
|
TaikoGradualDifficulty::new(difficulty, map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gradual_performance(
|
fn gradual_performance(
|
||||||
difficulty: &ModeDifficulty,
|
difficulty: &Difficulty,
|
||||||
map: &TaikoBeatmap<'_>,
|
map: &TaikoBeatmap<'_>,
|
||||||
) -> Self::GradualPerformance {
|
) -> Self::GradualPerformance {
|
||||||
TaikoGradualPerformance::new(difficulty, map)
|
TaikoGradualPerformance::new(difficulty, map)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
taiko::{difficulty::gradual::TaikoGradualDifficulty, TaikoBeatmap, TaikoScoreState},
|
taiko::{difficulty::gradual::TaikoGradualDifficulty, TaikoBeatmap, TaikoScoreState},
|
||||||
ModeDifficulty,
|
Difficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::TaikoPerformanceAttributes;
|
use super::TaikoPerformanceAttributes;
|
||||||
@@ -20,14 +20,14 @@ use super::TaikoPerformanceAttributes;
|
|||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use rosu_pp::{Beatmap, ModeDifficulty};
|
/// use rosu_pp::{Beatmap, Difficulty};
|
||||||
/// use rosu_pp::taiko::{Taiko, TaikoGradualPerformance, TaikoScoreState};
|
/// use rosu_pp::taiko::{Taiko, TaikoGradualPerformance, TaikoScoreState};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_path("./resources/1028484.osu")
|
/// let converted = Beatmap::from_path("./resources/1028484.osu")
|
||||||
/// .unwrap()
|
/// .unwrap()
|
||||||
/// .unchecked_into_converted::<Taiko>();
|
/// .unchecked_into_converted::<Taiko>();
|
||||||
///
|
///
|
||||||
/// let difficulty = ModeDifficulty::new().mods(64); // DT
|
/// let difficulty = Difficulty::new().mods(64); // DT
|
||||||
/// let mut gradual = TaikoGradualPerformance::new(&difficulty, &converted);
|
/// let mut gradual = TaikoGradualPerformance::new(&difficulty, &converted);
|
||||||
/// let mut state = TaikoScoreState::new(); // empty state, everything is on 0.
|
/// let mut state = TaikoScoreState::new(); // empty state, everything is on 0.
|
||||||
///
|
///
|
||||||
@@ -84,7 +84,7 @@ pub struct TaikoGradualPerformance {
|
|||||||
|
|
||||||
impl TaikoGradualPerformance {
|
impl TaikoGradualPerformance {
|
||||||
/// Create a new gradual performance calculator for osu!taiko maps.
|
/// Create a new gradual performance calculator for osu!taiko maps.
|
||||||
pub fn new(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> Self {
|
pub fn new(difficulty: &Difficulty, converted: &TaikoBeatmap<'_>) -> Self {
|
||||||
let difficulty = TaikoGradualDifficulty::new(difficulty, converted);
|
let difficulty = TaikoGradualDifficulty::new(difficulty, converted);
|
||||||
|
|
||||||
Self { difficulty }
|
Self { difficulty }
|
||||||
@@ -135,7 +135,7 @@ mod tests {
|
|||||||
.unchecked_into_converted();
|
.unchecked_into_converted();
|
||||||
|
|
||||||
let mods = 88; // HDHRDT
|
let mods = 88; // HDHRDT
|
||||||
let difficulty = ModeDifficulty::new().mods(88);
|
let difficulty = Difficulty::new().mods(88);
|
||||||
|
|
||||||
let mut gradual = TaikoGradualPerformance::new(&difficulty, &converted);
|
let mut gradual = TaikoGradualPerformance::new(&difficulty, &converted);
|
||||||
let mut gradual_2nd = TaikoGradualPerformance::new(&difficulty, &converted);
|
let mut gradual_2nd = TaikoGradualPerformance::new(&difficulty, &converted);
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
any::ModeDifficulty,
|
any::{Difficulty, HitResultPriority, ModeAttributeProvider},
|
||||||
any::{HitResultPriority, ModeAttributeProvider},
|
|
||||||
osu::OsuPerformance,
|
osu::OsuPerformance,
|
||||||
util::{map_or_attrs::MapOrAttrs, mods::Mods},
|
util::{map_or_attrs::MapOrAttrs, mods::Mods},
|
||||||
};
|
};
|
||||||
@@ -21,7 +20,7 @@ pub mod gradual;
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct TaikoPerformance<'map> {
|
pub struct TaikoPerformance<'map> {
|
||||||
pub(crate) map_or_attrs: MapOrAttrs<'map, Taiko>,
|
pub(crate) map_or_attrs: MapOrAttrs<'map, Taiko>,
|
||||||
difficulty: ModeDifficulty,
|
difficulty: Difficulty,
|
||||||
combo: Option<u32>,
|
combo: Option<u32>,
|
||||||
acc: Option<f64>,
|
acc: Option<f64>,
|
||||||
hitresult_priority: HitResultPriority,
|
hitresult_priority: HitResultPriority,
|
||||||
@@ -35,7 +34,7 @@ impl<'map> TaikoPerformance<'map> {
|
|||||||
pub const fn new(map: TaikoBeatmap<'map>) -> Self {
|
pub const fn new(map: TaikoBeatmap<'map>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Map(map),
|
map_or_attrs: MapOrAttrs::Map(map),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
combo: None,
|
combo: None,
|
||||||
acc: None,
|
acc: None,
|
||||||
misses: None,
|
misses: None,
|
||||||
@@ -48,7 +47,7 @@ impl<'map> TaikoPerformance<'map> {
|
|||||||
pub(crate) const fn from_taiko_attributes(attrs: TaikoDifficultyAttributes) -> Self {
|
pub(crate) const fn from_taiko_attributes(attrs: TaikoDifficultyAttributes) -> Self {
|
||||||
Self {
|
Self {
|
||||||
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
map_or_attrs: MapOrAttrs::Attrs(attrs),
|
||||||
difficulty: ModeDifficulty::new(),
|
difficulty: Difficulty::new(),
|
||||||
combo: None,
|
combo: None,
|
||||||
acc: None,
|
acc: None,
|
||||||
misses: None,
|
misses: None,
|
||||||
@@ -167,7 +166,7 @@ impl<'map> TaikoPerformance<'map> {
|
|||||||
pub fn generate_state(&mut self) -> TaikoScoreState {
|
pub fn generate_state(&mut self) -> TaikoScoreState {
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => {
|
MapOrAttrs::Map(ref map) => {
|
||||||
let attrs = self.generate_attributes(map);
|
let attrs = self.difficulty.with_mode().calculate(map);
|
||||||
|
|
||||||
self.map_or_attrs.insert_attrs(attrs)
|
self.map_or_attrs.insert_attrs(attrs)
|
||||||
}
|
}
|
||||||
@@ -255,7 +254,7 @@ impl<'map> TaikoPerformance<'map> {
|
|||||||
let state = self.generate_state();
|
let state = self.generate_state();
|
||||||
|
|
||||||
let attrs = match self.map_or_attrs {
|
let attrs = match self.map_or_attrs {
|
||||||
MapOrAttrs::Map(ref map) => self.generate_attributes(map),
|
MapOrAttrs::Map(ref map) => self.difficulty.with_mode().calculate(map),
|
||||||
MapOrAttrs::Attrs(attrs) => attrs,
|
MapOrAttrs::Attrs(attrs) => attrs,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -268,10 +267,6 @@ impl<'map> TaikoPerformance<'map> {
|
|||||||
inner.calculate()
|
inner.calculate()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_attributes(&self, map: &TaikoBeatmap<'_>) -> TaikoDifficultyAttributes {
|
|
||||||
self.difficulty.calculate(map)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Try to create [`TaikoPerformance`] through a [`ModeAttributeProvider`].
|
/// Try to create [`TaikoPerformance`] through a [`ModeAttributeProvider`].
|
||||||
///
|
///
|
||||||
/// If you already calculated the attributes for the current map-mod
|
/// If you already calculated the attributes for the current map-mod
|
||||||
@@ -506,7 +501,7 @@ mod test {
|
|||||||
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use crate::Beatmap;
|
use crate::{any::difficulty::converted::ConvertedDifficulty, Beatmap};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
@@ -521,7 +516,7 @@ mod test {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unchecked_into_converted::<Taiko>();
|
.unchecked_into_converted::<Taiko>();
|
||||||
|
|
||||||
let attrs = ModeDifficulty::new().calculate(&converted);
|
let attrs = ConvertedDifficulty::new().calculate(&converted);
|
||||||
|
|
||||||
assert_eq!(MAX_COMBO, attrs.max_combo);
|
assert_eq!(MAX_COMBO, attrs.max_combo);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::{any::ModeDifficulty, taiko::difficulty::DifficultyValues};
|
use crate::{taiko::difficulty::DifficultyValues, Difficulty};
|
||||||
|
|
||||||
use super::convert::TaikoBeatmap;
|
use super::convert::TaikoBeatmap;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ impl TaikoStrains {
|
|||||||
pub const SECTION_LEN: f64 = 400.0;
|
pub const SECTION_LEN: f64 = 400.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn strains(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> TaikoStrains {
|
pub fn strains(difficulty: &Difficulty, converted: &TaikoBeatmap<'_>) -> TaikoStrains {
|
||||||
let values = DifficultyValues::calculate(difficulty, converted);
|
let values = DifficultyValues::calculate(difficulty, converted);
|
||||||
|
|
||||||
TaikoStrains {
|
TaikoStrains {
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
use std::{
|
||||||
|
any,
|
||||||
|
fmt::{Debug, Formatter, Result as FmtResult},
|
||||||
|
marker::PhantomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct GenericFormatter<T>(PhantomData<T>);
|
||||||
|
|
||||||
|
impl<T> GenericFormatter<T> {
|
||||||
|
pub const fn new() -> Self {
|
||||||
|
Self(PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Debug for GenericFormatter<T> {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
fn fmt_stripped(full_type_name: &str, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
|
// Strip fully qualified syntax
|
||||||
|
if let Some(position) = full_type_name.rfind("::") {
|
||||||
|
if let Some(type_name) = full_type_name.get(position + 2..) {
|
||||||
|
f.write_str(type_name)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt_stripped(any::type_name::<T>(), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
pub mod float_ext;
|
pub mod float_ext;
|
||||||
|
pub mod generic_fmt;
|
||||||
pub mod limited_queue;
|
pub mod limited_queue;
|
||||||
pub mod map_or_attrs;
|
pub mod map_or_attrs;
|
||||||
pub mod mods;
|
pub mod mods;
|
||||||
|
|||||||
+4
-4
@@ -133,10 +133,10 @@ impl<T: fmt::Debug> fmt::Debug for Weak<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// ```compile_fail
|
/// ```compile_fail
|
||||||
/// use rosu_pp::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
|
/// use rosu_pp::{taiko::TaikoGradualDifficulty, Beatmap, Difficulty};
|
||||||
///
|
///
|
||||||
/// let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
/// let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
||||||
/// let difficulty = ModeDifficulty::new();
|
/// let difficulty = Difficulty::new();
|
||||||
/// let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
/// let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||||
///
|
///
|
||||||
/// // Rc<RefCell<_>> cannot be shared across threads so compilation should fail
|
/// // Rc<RefCell<_>> cannot be shared across threads so compilation should fail
|
||||||
@@ -149,10 +149,10 @@ const fn _share_gradual_taiko() {}
|
|||||||
mod tests {
|
mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn share_gradual_taiko() {
|
fn share_gradual_taiko() {
|
||||||
use crate::{taiko::TaikoGradualDifficulty, Beatmap, ModeDifficulty};
|
use crate::{taiko::TaikoGradualDifficulty, Beatmap, Difficulty};
|
||||||
|
|
||||||
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
let converted = Beatmap::from_bytes(&[]).unwrap().unchecked_into_converted();
|
||||||
let difficulty = ModeDifficulty::new();
|
let difficulty = Difficulty::new();
|
||||||
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
let mut gradual = TaikoGradualDifficulty::new(&difficulty, &converted);
|
||||||
|
|
||||||
// Arc<RwLock<_>> *can* be shared across threads so this should compile
|
// Arc<RwLock<_>> *can* be shared across threads so this should compile
|
||||||
|
|||||||
+2
-2
@@ -5,7 +5,7 @@ use rosu_pp::{
|
|||||||
mania::{Mania, ManiaDifficultyAttributes},
|
mania::{Mania, ManiaDifficultyAttributes},
|
||||||
osu::{Osu, OsuDifficultyAttributes},
|
osu::{Osu, OsuDifficultyAttributes},
|
||||||
taiko::{Taiko, TaikoDifficultyAttributes},
|
taiko::{Taiko, TaikoDifficultyAttributes},
|
||||||
Beatmap, ModeDifficulty,
|
Beatmap, ConvertedDifficulty,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::common::*;
|
use self::common::*;
|
||||||
@@ -25,7 +25,7 @@ macro_rules! test_cases {
|
|||||||
$(
|
$(
|
||||||
let mods = 0 $( + $mods )*;
|
let mods = 0 $( + $mods )*;
|
||||||
let expected = test_cases!(@$mode { $( $key: $value, )* });
|
let expected = test_cases!(@$mode { $( $key: $value, )* });
|
||||||
let actual = ModeDifficulty::new().mods(mods).calculate(&map);
|
let actual = ConvertedDifficulty::new().mods(mods).calculate(&map);
|
||||||
run(&actual, &expected, mods);
|
run(&actual, &expected, mods);
|
||||||
)*
|
)*
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user