apply new settings of Difficulty
This commit is contained in:
@@ -69,12 +69,12 @@ impl<M: IGameMode> ConvertedDifficulty<'_, M> {
|
||||
|
||||
/// 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)
|
||||
M::gradual_difficulty(self.inner.to_owned(), 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)
|
||||
M::gradual_performance(self.inner.to_owned(), map)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ pub enum GradualDifficulty {
|
||||
macro_rules! from_converted {
|
||||
( $fn:ident, $mode:ident, $converted:ident ) => {
|
||||
#[doc = concat!("Create a [`GradualDifficulty`] for a [`", stringify!($converted), "`]")]
|
||||
pub fn $fn(difficulty: &Difficulty, converted: &$converted<'_>) -> Self {
|
||||
pub fn $fn(difficulty: Difficulty, converted: &$converted<'_>) -> Self {
|
||||
Self::$mode($mode::gradual_difficulty(difficulty, converted))
|
||||
}
|
||||
};
|
||||
@@ -62,7 +62,7 @@ macro_rules! from_converted {
|
||||
|
||||
impl GradualDifficulty {
|
||||
/// Create a [`GradualDifficulty`] for a map of any mode.
|
||||
pub fn new(difficulty: &Difficulty, map: &Beatmap) -> Self {
|
||||
pub fn new(difficulty: Difficulty, map: &Beatmap) -> Self {
|
||||
let map = Cow::Borrowed(map);
|
||||
|
||||
match map.mode {
|
||||
|
||||
+15
-43
@@ -1,7 +1,4 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
num::{NonZeroU32, NonZeroU64},
|
||||
};
|
||||
use std::{borrow::Cow, num::NonZeroU32};
|
||||
|
||||
use rosu_map::section::general::GameMode;
|
||||
|
||||
@@ -56,22 +53,24 @@ pub struct Difficulty {
|
||||
cs: Option<ModsDependent>,
|
||||
hp: Option<ModsDependent>,
|
||||
od: Option<ModsDependent>,
|
||||
/// The slider multiplier will be clamped internally between 0.05 and 50.0.
|
||||
///
|
||||
/// Since its minimum value is 0.05, its bits are never zero.
|
||||
///
|
||||
/// This allows for an optimization to reduce the struct size by storing its
|
||||
/// bits as a [`NonZeroU64`].
|
||||
slider_multiplier: Option<NonZeroU64>,
|
||||
hardrock_offsets: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||
pub struct ModsDependent {
|
||||
pub value: f32,
|
||||
pub with_mods: bool,
|
||||
}
|
||||
|
||||
impl ModsDependent {
|
||||
pub const fn new(value: f32) -> Self {
|
||||
Self {
|
||||
value,
|
||||
with_mods: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Difficulty {
|
||||
/// Create a new difficulty calculator.
|
||||
pub const fn new() -> Self {
|
||||
@@ -83,7 +82,6 @@ impl Difficulty {
|
||||
cs: None,
|
||||
hp: None,
|
||||
od: None,
|
||||
slider_multiplier: None,
|
||||
hardrock_offsets: None,
|
||||
}
|
||||
}
|
||||
@@ -212,26 +210,6 @@ impl Difficulty {
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust a beatmap's set scroll speed.
|
||||
///
|
||||
/// Only relevant for osu!taiko.
|
||||
///
|
||||
/// | Minimum | Maximum |
|
||||
/// | :-----: | :-----: |
|
||||
/// | 0.05 | 50 |
|
||||
pub fn slider_multiplier(self, slider_multiplier: f64) -> Self {
|
||||
let slider_multiplier = slider_multiplier.clamp(0.05, 50.0).to_bits();
|
||||
|
||||
// SAFETY: The minimum value is 0.05 so its bits can never be fully
|
||||
// zero.
|
||||
let non_zero = unsafe { NonZeroU64::new_unchecked(slider_multiplier) };
|
||||
|
||||
Self {
|
||||
slider_multiplier: Some(non_zero),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust patterns as if the HR mod is enabled.
|
||||
///
|
||||
/// Only relevant for osu!catch.
|
||||
@@ -276,12 +254,12 @@ impl Difficulty {
|
||||
}
|
||||
|
||||
/// Create a gradual difficulty calculator for a [`Beatmap`].
|
||||
pub fn gradual_difficulty(&self, map: &Beatmap) -> GradualDifficulty {
|
||||
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 {
|
||||
pub fn gradual_performance(self, map: &Beatmap) -> GradualPerformance {
|
||||
GradualPerformance::new(self, map)
|
||||
}
|
||||
|
||||
@@ -316,14 +294,8 @@ impl Difficulty {
|
||||
self.od
|
||||
}
|
||||
|
||||
pub(crate) fn get_slider_multiplier(&self) -> f64 {
|
||||
self.slider_multiplier
|
||||
.map(NonZeroU64::get)
|
||||
.map_or(1.0, f64::from_bits)
|
||||
}
|
||||
|
||||
pub(crate) const fn get_hardrock_offsets(&self) -> Option<bool> {
|
||||
self.hardrock_offsets
|
||||
pub(crate) fn get_hardrock_offsets(&self) -> bool {
|
||||
self.hardrock_offsets.unwrap_or(self.mods.hr())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ pub enum GradualPerformance {
|
||||
macro_rules! from_converted {
|
||||
( $fn:ident, $mode:ident, $converted:ident ) => {
|
||||
#[doc = concat!("Create a [`GradualPerformance`] for a [`", stringify!($converted), "`]")]
|
||||
pub fn $fn(difficulty: &Difficulty, converted: &$converted<'_>) -> Self {
|
||||
pub fn $fn(difficulty: Difficulty, converted: &$converted<'_>) -> Self {
|
||||
Self::$mode($mode::gradual_performance(difficulty, converted))
|
||||
}
|
||||
};
|
||||
@@ -111,7 +111,7 @@ macro_rules! from_converted {
|
||||
|
||||
impl GradualPerformance {
|
||||
/// Create a [`GradualPerformance`] for a map of any mode.
|
||||
pub fn new(difficulty: &Difficulty, map: &Beatmap) -> Self {
|
||||
pub fn new(difficulty: Difficulty, map: &Beatmap) -> Self {
|
||||
let map = Cow::Borrowed(map);
|
||||
|
||||
match map.mode {
|
||||
|
||||
+111
-9
@@ -9,6 +9,7 @@ use crate::{
|
||||
model::beatmap::{Beatmap, Converted},
|
||||
osu::{Osu, OsuPerformance},
|
||||
taiko::{Taiko, TaikoPerformance},
|
||||
Difficulty,
|
||||
};
|
||||
|
||||
use super::{
|
||||
@@ -45,8 +46,8 @@ impl<'map> Performance<'map> {
|
||||
/// Create a new performance calculator through previously calculated
|
||||
/// attributes.
|
||||
///
|
||||
/// Note that the map, mods, and passed object count should be the same
|
||||
/// as when the attributes were calculated.
|
||||
/// Note that the [`Beatmap`] and [`Difficulty`] should be the same as when
|
||||
/// the attributes were calculated.
|
||||
pub fn from_attributes(attributes: impl AttributeProvider) -> Self {
|
||||
Self::from(attributes)
|
||||
}
|
||||
@@ -63,8 +64,10 @@ impl<'map> Performance<'map> {
|
||||
}
|
||||
|
||||
/// Provide the result of a previous difficulty or performance calculation.
|
||||
/// If you already calculated the attributes for the current map-mod combination,
|
||||
/// be sure to put them in here so that they don't have to be recalculated.
|
||||
///
|
||||
/// If you already calculated the attributes for the current [`Beatmap`]
|
||||
/// and [`Difficulty`], be sure to put them in here so that they don't have
|
||||
/// to be recalculated.
|
||||
pub fn attributes(self, attributes: impl AttributeProvider) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.attributes(attributes.attributes())),
|
||||
@@ -124,6 +127,16 @@ impl<'map> Performance<'map> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Use the specified settings of the given [`Difficulty`].
|
||||
pub fn difficulty(self, difficulty: Difficulty) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.difficulty(difficulty)),
|
||||
Self::Taiko(t) => Self::Taiko(t.difficulty(difficulty)),
|
||||
Self::Catch(f) => Self::Catch(f.difficulty(difficulty)),
|
||||
Self::Mania(m) => Self::Mania(m.difficulty(difficulty)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Amount of passed objects for partial plays, e.g. a fail.
|
||||
///
|
||||
/// If you want to calculate the performance after every few objects,
|
||||
@@ -141,8 +154,13 @@ impl<'map> Performance<'map> {
|
||||
}
|
||||
|
||||
/// Adjust the clock rate used in the calculation.
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
/// | Minimum | Maximum |
|
||||
/// | :-----: | :-----: |
|
||||
/// | 0.01 | 100 |
|
||||
pub fn clock_rate(self, clock_rate: f64) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.clock_rate(clock_rate)),
|
||||
@@ -152,6 +170,91 @@ impl<'map> Performance<'map> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Override a beatmap's set AR.
|
||||
///
|
||||
/// Only relevant for osu! and osu!catch.
|
||||
///
|
||||
/// `with_mods` determines if the given value should be used before
|
||||
/// or after accounting for mods, e.g. on `true` the value will be
|
||||
/// used as is and on `false` it will be modified based on the mods.
|
||||
///
|
||||
/// | Minimum | Maximum |
|
||||
/// | :-----: | :-----: |
|
||||
/// | -20 | 20 |
|
||||
pub fn ar(self, ar: f32, with_mods: bool) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.ar(ar, with_mods)),
|
||||
Self::Catch(c) => Self::Catch(c.ar(ar, with_mods)),
|
||||
Self::Taiko(_) | Self::Mania(_) => self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Override a beatmap's set CS.
|
||||
///
|
||||
/// Only relevant for osu! and osu!catch.
|
||||
///
|
||||
/// `with_mods` determines if the given value should be used before
|
||||
/// or after accounting for mods, e.g. on `true` the value will be
|
||||
/// used as is and on `false` it will be modified based on the mods.
|
||||
///
|
||||
/// | Minimum | Maximum |
|
||||
/// | :-----: | :-----: |
|
||||
/// | -20 | 20 |
|
||||
pub fn cs(self, cs: f32, with_mods: bool) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.cs(cs, with_mods)),
|
||||
Self::Catch(c) => Self::Catch(c.cs(cs, with_mods)),
|
||||
Self::Taiko(_) | Self::Mania(_) => self,
|
||||
}
|
||||
}
|
||||
|
||||
/// Override a beatmap's set HP.
|
||||
///
|
||||
/// `with_mods` determines if the given value should be used before
|
||||
/// or after accounting for mods, e.g. on `true` the value will be
|
||||
/// used as is and on `false` it will be modified based on the mods.
|
||||
///
|
||||
/// | Minimum | Maximum |
|
||||
/// | :-----: | :-----: |
|
||||
/// | -20 | 20 |
|
||||
pub fn hp(self, hp: f32, with_mods: bool) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.hp(hp, with_mods)),
|
||||
Self::Taiko(t) => Self::Taiko(t.hp(hp, with_mods)),
|
||||
Self::Catch(c) => Self::Catch(c.hp(hp, with_mods)),
|
||||
Self::Mania(m) => Self::Mania(m.hp(hp, with_mods)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Override a beatmap's set OD.
|
||||
///
|
||||
/// `with_mods` determines if the given value should be used before
|
||||
/// or after accounting for mods, e.g. on `true` the value will be
|
||||
/// used as is and on `false` it will be modified based on the mods.
|
||||
///
|
||||
/// | Minimum | Maximum |
|
||||
/// | :-----: | :-----: |
|
||||
/// | -20 | 20 |
|
||||
pub fn od(self, od: f32, with_mods: bool) -> Self {
|
||||
match self {
|
||||
Self::Osu(o) => Self::Osu(o.od(od, with_mods)),
|
||||
Self::Taiko(t) => Self::Taiko(t.od(od, with_mods)),
|
||||
Self::Catch(c) => Self::Catch(c.od(od, with_mods)),
|
||||
Self::Mania(m) => Self::Mania(m.od(od, with_mods)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adjust patterns as if the HR mod is enabled.
|
||||
///
|
||||
/// Only relevant for osu!catch.
|
||||
pub fn hardrock_offsets(self, hardrock_offsets: bool) -> Self {
|
||||
if let Self::Catch(catch) = self {
|
||||
Self::Catch(catch.hardrock_offsets(hardrock_offsets))
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide parameters through a [`ScoreState`].
|
||||
pub fn state(self, state: ScoreState) -> Self {
|
||||
match self {
|
||||
@@ -240,9 +343,8 @@ impl<'map> Performance<'map> {
|
||||
|
||||
/// Specify the amount of katus of a play.
|
||||
///
|
||||
/// This value is only relevant for osu!catch for which it represents
|
||||
/// the amount of tiny droplet misses and osu!mania for which it.
|
||||
/// repesents the amount of n200.
|
||||
/// Only relevant for osu!catch for which it represents the amount of tiny
|
||||
/// droplet misses and osu!mania for which it repesents the amount of n200.
|
||||
pub fn n_katu(self, n_katu: u32) -> Self {
|
||||
match self {
|
||||
Self::Osu(_) | Self::Taiko(_) => self,
|
||||
@@ -253,8 +355,8 @@ impl<'map> Performance<'map> {
|
||||
|
||||
/// Specify the amount of gekis of a play.
|
||||
///
|
||||
/// This value is only relevant for osu!mania for which it.
|
||||
/// repesents the amount of n320.
|
||||
/// Only relevant for osu!mania for which it repesents the
|
||||
/// amount of n320.
|
||||
pub fn n_geki(self, n_geki: u32) -> Self {
|
||||
match self {
|
||||
Self::Osu(_) | Self::Taiko(_) | Self::Catch(_) => self,
|
||||
|
||||
Reference in New Issue
Block a user