Skip to content

Commit

Permalink
feat: use rosu-mods (#36)
Browse files Browse the repository at this point in the history
* use rosu-mods

* apply values from DifficultyAdjust mod

* minor doc addition

* consider Blinds mod in perf calc
  • Loading branch information
MaxOhn authored Jul 8, 2024
1 parent 5c951b8 commit 26547d7
Show file tree
Hide file tree
Showing 19 changed files with 572 additions and 234 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ tracing = ["rosu-map/tracing"]

[dependencies]
rosu-map = { version = "0.1.1" }
rosu-mods = { version = "0.1.0" }

[dev-dependencies]
proptest = "1.4.0"
Expand Down
8 changes: 3 additions & 5 deletions src/any/difficulty/inspect.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use crate::Difficulty;
use crate::{model::mods::GameMods, Difficulty};

use super::ModsDependent;

/// [`Difficulty`] but all fields are public for inspection.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct InspectDifficulty {
/// Specify mods through their bit values.
///
/// See <https://github.com/ppy/osu-api/wiki#mods>
pub mods: u32,
/// Specify mods.
pub mods: GameMods,
/// Amount of passed objects for partial plays, e.g. a fail.
pub passed_objects: Option<u32>,
/// Adjust the clock rate used in the calculation.
Expand Down
67 changes: 41 additions & 26 deletions src/any/difficulty/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ use rosu_map::section::general::GameMode;
use crate::{
catch::Catch,
mania::Mania,
model::beatmap::{Beatmap, Converted},
model::{
beatmap::{Beatmap, Converted},
mods::GameMods,
},
osu::Osu,
taiko::Taiko,
GradualDifficulty, GradualPerformance,
Expand All @@ -25,7 +28,7 @@ pub mod inspect;
pub mod object;
pub mod skills;

use crate::{model::mode::IGameMode, util::mods::Mods};
use crate::model::mode::IGameMode;

/// Difficulty calculator on maps of any mode.
///
Expand All @@ -43,7 +46,7 @@ use crate::{model::mode::IGameMode, util::mods::Mods};
#[derive(Clone, PartialEq)]
#[must_use]
pub struct Difficulty {
mods: u32,
mods: GameMods,
passed_objects: Option<u32>,
/// Clock rate will be clamped internally between 0.01 and 100.0.
///
Expand Down Expand Up @@ -86,7 +89,7 @@ impl Difficulty {
/// Create a new difficulty calculator.
pub const fn new() -> Self {
Self {
mods: 0,
mods: GameMods::DEFAULT,
passed_objects: None,
clock_rate: None,
ar: None,
Expand Down Expand Up @@ -122,7 +125,7 @@ impl Difficulty {
InspectDifficulty {
mods,
passed_objects,
clock_rate: clock_rate.map(non_zero_u32_to_f64),
clock_rate: clock_rate.map(non_zero_u32_to_f32).map(f64::from),
ar,
cs,
hp,
Expand All @@ -131,21 +134,30 @@ impl Difficulty {
}
}

/// Specify mods through their bit values.
/// Specify mods.
///
/// Accepted types are
/// - `u32`
/// - [`rosu_mods::GameModsLegacy`]
/// - [`rosu_mods::GameMods`]
/// - [`rosu_mods::GameModsIntermode`]
/// - [`&rosu_mods::GameModsIntermode`](rosu_mods::GameModsIntermode)
///
/// See <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 {
pub fn mods(self, mods: impl Into<GameMods>) -> Self {
Self {
passed_objects: Some(passed_objects),
mods: mods.into(),
..self
}
}

/// Amount of passed objects for partial plays, e.g. a fail.
pub const fn passed_objects(mut self, passed_objects: u32) -> Self {
self.passed_objects = Some(passed_objects);

self
}

/// Adjust the clock rate used in the calculation.
///
/// If none is specified, it will take the clock rate based on the mods
Expand Down Expand Up @@ -250,11 +262,10 @@ impl Difficulty {
/// Adjust patterns as if the HR mod is enabled.
///
/// Only relevant for osu!catch.
pub const fn hardrock_offsets(self, hardrock_offsets: bool) -> Self {
Self {
hardrock_offsets: Some(hardrock_offsets),
..self
}
pub const fn hardrock_offsets(mut self, hardrock_offsets: bool) -> Self {
self.hardrock_offsets = Some(hardrock_offsets);

self
}

/// Perform the difficulty calculation.
Expand Down Expand Up @@ -300,13 +311,16 @@ impl Difficulty {
GradualPerformance::new(self, map)
}

pub(crate) const fn get_mods(&self) -> u32 {
self.mods
pub(crate) const fn get_mods(&self) -> &GameMods {
&self.mods
}

pub(crate) fn get_clock_rate(&self) -> f64 {
self.clock_rate
.map_or(self.mods.clock_rate(), non_zero_u32_to_f64)
let clock_rate = self
.clock_rate
.map_or(self.mods.clock_rate(), non_zero_u32_to_f32);

f64::from(clock_rate)
}

pub(crate) fn get_passed_objects(&self) -> usize {
Expand All @@ -330,12 +344,13 @@ impl Difficulty {
}

pub(crate) fn get_hardrock_offsets(&self) -> bool {
self.hardrock_offsets.unwrap_or(self.mods.hr())
self.hardrock_offsets
.unwrap_or_else(|| self.mods.hardrock_offsets())
}
}

fn non_zero_u32_to_f64(n: NonZeroU32) -> f64 {
f64::from(f32::from_bits(n.get()))
fn non_zero_u32_to_f32(n: NonZeroU32) -> f32 {
f32::from_bits(n.get())
}

impl Debug for Difficulty {
Expand All @@ -354,7 +369,7 @@ impl Debug for Difficulty {
f.debug_struct("Difficulty")
.field("mods", mods)
.field("passed_objects", passed_objects)
.field("clock_rate", &clock_rate.map(non_zero_u32_to_f64))
.field("clock_rate", &clock_rate.map(non_zero_u32_to_f32))
.field("ar", ar)
.field("cs", cs)
.field("hp", hp)
Expand Down
9 changes: 8 additions & 1 deletion src/any/performance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,14 @@ impl<'map> Performance<'map> {
}
}

/// Specify mods through their bit values.
/// Specify mods.
///
/// Accepted types are
/// - `u32`
/// - [`rosu_mods::GameModsLegacy`]
/// - [`rosu_mods::GameMods`]
/// - [`rosu_mods::GameModsIntermode`]
/// - [`&rosu_mods::GameModsIntermode`](rosu_mods::GameModsIntermode)
///
/// See <https://github.com/ppy/osu-api/wiki#mods>
pub fn mods(self, mods: u32) -> Self {
Expand Down
26 changes: 17 additions & 9 deletions src/catch/performance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::cmp::{self, Ordering};

use crate::{
any::{Difficulty, IntoModePerformance, IntoPerformance},
model::mods::GameMods,
osu::OsuPerformance,
util::{map_or_attrs::MapOrAttrs, mods::Mods},
util::map_or_attrs::MapOrAttrs,
Performance,
};

Expand Down Expand Up @@ -70,10 +71,17 @@ impl<'map> CatchPerformance<'map> {
}
}

/// Specify mods through their bit values.
/// Specify mods.
///
/// Accepted types are
/// - `u32`
/// - [`rosu_mods::GameModsLegacy`]
/// - [`rosu_mods::GameMods`]
/// - [`rosu_mods::GameModsIntermode`]
/// - [`&rosu_mods::GameModsIntermode`](rosu_mods::GameModsIntermode)
///
/// See <https://github.com/ppy/osu-api/wiki#mods>
pub const fn mods(mut self, mods: u32) -> Self {
pub fn mods(mut self, mods: impl Into<GameMods>) -> Self {
self.difficulty = self.difficulty.mods(mods);

self
Expand Down Expand Up @@ -122,7 +130,7 @@ impl<'map> CatchPerformance<'map> {
}

/// Use the specified settings of the given [`Difficulty`].
pub const fn difficulty(mut self, difficulty: Difficulty) -> Self {
pub fn difficulty(mut self, difficulty: Difficulty) -> Self {
self.difficulty = difficulty;

self
Expand All @@ -135,7 +143,7 @@ impl<'map> CatchPerformance<'map> {
/// `passed_objects`, you should use [`CatchGradualPerformance`].
///
/// [`CatchGradualPerformance`]: crate::catch::CatchGradualPerformance
pub const fn passed_objects(mut self, passed_objects: u32) -> Self {
pub fn passed_objects(mut self, passed_objects: u32) -> Self {
self.difficulty = self.difficulty.passed_objects(passed_objects);

self
Expand Down Expand Up @@ -216,7 +224,7 @@ impl<'map> CatchPerformance<'map> {
}

/// Adjust patterns as if the HR mod is enabled.
pub const fn hardrock_offsets(mut self, hardrock_offsets: bool) -> Self {
pub fn hardrock_offsets(mut self, hardrock_offsets: bool) -> Self {
self.difficulty = self.difficulty.hardrock_offsets(hardrock_offsets);

self
Expand Down Expand Up @@ -486,13 +494,13 @@ impl<'map, T: IntoModePerformance<'map, Catch>> From<T> for CatchPerformance<'ma
}
}

struct CatchPerformanceInner {
struct CatchPerformanceInner<'mods> {
attrs: CatchDifficultyAttributes,
mods: u32,
mods: &'mods GameMods,
state: CatchScoreState,
}

impl CatchPerformanceInner {
impl CatchPerformanceInner<'_> {
fn calculate(self) -> CatchPerformanceAttributes {
let attributes = &self.attrs;
let stars = attributes.stars;
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,10 @@
#[doc(inline)]
pub use self::{
any::{Difficulty, GradualDifficulty, GradualPerformance, Performance},
model::beatmap::{Beatmap, Converted},
model::{
beatmap::{Beatmap, Converted},
mods::GameMods,
},
};

/// Types for calculations of any mode.
Expand Down
24 changes: 16 additions & 8 deletions src/mania/performance/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use std::cmp;

use crate::{
any::{Difficulty, HitResultPriority, IntoModePerformance, IntoPerformance},
model::mods::GameMods,
osu::OsuPerformance,
util::{map_or_attrs::MapOrAttrs, mods::Mods},
util::map_or_attrs::MapOrAttrs,
Performance,
};

Expand Down Expand Up @@ -71,17 +72,24 @@ impl<'map> ManiaPerformance<'map> {
}
}

/// Specify mods through their bit values.
/// Specify mods.
///
/// Accepted types are
/// - `u32`
/// - [`rosu_mods::GameModsLegacy`]
/// - [`rosu_mods::GameMods`]
/// - [`rosu_mods::GameModsIntermode`]
/// - [`&rosu_mods::GameModsIntermode`](rosu_mods::GameModsIntermode)
///
/// See <https://github.com/ppy/osu-api/wiki#mods>
pub const fn mods(mut self, mods: u32) -> Self {
pub fn mods(mut self, mods: impl Into<GameMods>) -> Self {
self.difficulty = self.difficulty.mods(mods);

self
}

/// Use the specified settings of the given [`Difficulty`].
pub const fn difficulty(mut self, difficulty: Difficulty) -> Self {
pub fn difficulty(mut self, difficulty: Difficulty) -> Self {
self.difficulty = difficulty;

self
Expand All @@ -94,7 +102,7 @@ impl<'map> ManiaPerformance<'map> {
/// `passed_objects`, you should use [`ManiaGradualPerformance`].
///
/// [`ManiaGradualPerformance`]: crate::mania::ManiaGradualPerformance
pub const fn passed_objects(mut self, passed_objects: u32) -> Self {
pub fn passed_objects(mut self, passed_objects: u32) -> Self {
self.difficulty = self.difficulty.passed_objects(passed_objects);

self
Expand Down Expand Up @@ -840,13 +848,13 @@ impl<'map, T: IntoModePerformance<'map, Mania>> From<T> for ManiaPerformance<'ma
}
}

struct ManiaPerformanceInner {
struct ManiaPerformanceInner<'mods> {
attrs: ManiaDifficultyAttributes,
mods: u32,
mods: &'mods GameMods,
state: ManiaScoreState,
}

impl ManiaPerformanceInner {
impl ManiaPerformanceInner<'_> {
fn calculate(self) -> ManiaPerformanceAttributes {
// * Arbitrary initial value for scaling pp in order to standardize distributions across game modes.
// * The specific number has no intrinsic meaning and can be adjusted as needed.
Expand Down
Loading

0 comments on commit 26547d7

Please sign in to comment.