added InspectDifficulty type

This commit is contained in:
MaxOhn
2024-03-06 21:40:54 +01:00
parent bf0afc8809
commit 9c0fc2eea9
4 changed files with 169 additions and 9 deletions
+92
View File
@@ -0,0 +1,92 @@
use crate::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,
/// Amount of passed objects for partial plays, e.g. a fail.
pub passed_objects: Option<u32>,
/// Adjust the clock rate used in the calculation.
pub clock_rate: Option<f64>,
/// Override a beatmap's set AR.
///
/// Only relevant for osu! and osu!catch.
pub ar: Option<ModsDependent>,
/// Override a beatmap's set CS.
///
/// Only relevant for osu! and osu!catch.
pub cs: Option<ModsDependent>,
/// Override a beatmap's set HP.
pub hp: Option<ModsDependent>,
/// Override a beatmap's set OD.
pub od: Option<ModsDependent>,
/// Adjust patterns as if the HR mod is enabled.
///
/// Only relevant for osu!catch.
pub hardrock_offsets: Option<bool>,
}
impl InspectDifficulty {
/// Convert `self` into a [`Difficulty`].
pub fn into_difficulty(self) -> Difficulty {
let Self {
mods,
passed_objects,
clock_rate,
ar,
cs,
hp,
od,
hardrock_offsets,
} = self;
let mut difficulty = Difficulty::new().mods(mods);
if let Some(passed_objects) = passed_objects {
difficulty = difficulty.passed_objects(passed_objects);
}
if let Some(clock_rate) = clock_rate {
difficulty = difficulty.clock_rate(clock_rate);
}
if let Some(ar) = ar {
difficulty = difficulty.ar(ar.value, ar.with_mods);
}
if let Some(cs) = cs {
difficulty = difficulty.cs(cs.value, cs.with_mods);
}
if let Some(hp) = hp {
difficulty = difficulty.hp(hp.value, hp.with_mods);
}
if let Some(od) = od {
difficulty = difficulty.od(od.value, od.with_mods);
}
if let Some(hardrock_offsets) = hardrock_offsets {
difficulty = difficulty.hardrock_offsets(hardrock_offsets);
}
difficulty
}
}
impl From<InspectDifficulty> for Difficulty {
fn from(difficulty: InspectDifficulty) -> Self {
difficulty.into_difficulty()
}
}
impl From<Difficulty> for InspectDifficulty {
fn from(difficulty: Difficulty) -> Self {
difficulty.inspect()
}
}
+72 -7
View File
@@ -1,4 +1,8 @@
use std::{borrow::Cow, num::NonZeroU32};
use std::{
borrow::Cow,
fmt::{Debug, Formatter, Result as FmtResult},
num::NonZeroU32,
};
use rosu_map::section::general::GameMode;
@@ -13,10 +17,11 @@ use crate::{
use self::converted::ConvertedDifficulty;
use super::{attributes::DifficultyAttributes, Strains};
use super::{attributes::DifficultyAttributes, InspectDifficulty, Strains};
pub mod converted;
pub mod gradual;
pub mod inspect;
pub mod object;
pub mod skills;
@@ -35,7 +40,7 @@ use crate::{model::mode::IGameMode, util::mods::Mods};
/// .mods(8 + 1024) // HDFL
/// .calculate(&map);
/// ```
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, PartialEq)]
#[must_use]
pub struct Difficulty {
mods: u32,
@@ -56,9 +61,15 @@ pub struct Difficulty {
hardrock_offsets: Option<bool>,
}
/// Wrapper for beatmap attributes in [`Difficulty`].
#[derive(Copy, Clone, Debug, Default, PartialEq)]
pub struct ModsDependent {
/// Value of the beatmap attribute.
pub value: f32,
/// Whether `value` should be used as is or modified based on mods.
///
/// `true` means "value already considers mods" i.e. use as is;
/// `false` means modify with mods.
pub with_mods: bool,
}
@@ -94,9 +105,35 @@ impl Difficulty {
ConvertedDifficulty::new(self)
}
/// Turn this [`Difficulty`] into a [`InspectDifficulty`] to inspect its
/// configured values.
pub fn inspect(self) -> InspectDifficulty {
let Self {
mods,
passed_objects,
clock_rate,
ar,
cs,
hp,
od,
hardrock_offsets,
} = self;
InspectDifficulty {
mods,
passed_objects,
clock_rate: clock_rate.map(non_zero_u32_to_f64),
ar,
cs,
hp,
od,
hardrock_offsets,
}
}
/// 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>
pub const fn mods(self, mods: u32) -> Self {
Self { mods, ..self }
}
@@ -269,9 +306,7 @@ impl Difficulty {
pub(crate) fn get_clock_rate(&self) -> f64 {
self.clock_rate
.map(NonZeroU32::get)
.map(u64::from)
.map_or_else(|| self.mods.clock_rate(), f64::from_bits)
.map_or(self.mods.clock_rate(), non_zero_u32_to_f64)
}
pub(crate) fn get_passed_objects(&self) -> usize {
@@ -299,6 +334,36 @@ impl Difficulty {
}
}
fn non_zero_u32_to_f64(n: NonZeroU32) -> f64 {
f64::from_bits(u64::from(n.get()))
}
impl Debug for Difficulty {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
let Self {
mods,
passed_objects,
clock_rate,
ar,
cs,
hp,
od,
hardrock_offsets,
} = self;
f.debug_struct("Difficulty")
.field("mods", mods)
.field("passed_objects", passed_objects)
.field("clock_rate", &clock_rate.map(non_zero_u32_to_f64))
.field("ar", ar)
.field("cs", cs)
.field("hp", hp)
.field("od", od)
.field("hardrock_offsets", hardrock_offsets)
.finish()
}
}
impl Default for Difficulty {
fn default() -> Self {
Self::new()
+4 -1
View File
@@ -2,7 +2,10 @@ pub use self::{
attributes::{
AttributeProvider, DifficultyAttributes, ModeAttributeProvider, PerformanceAttributes,
},
difficulty::{converted::ConvertedDifficulty, gradual::GradualDifficulty, Difficulty},
difficulty::{
converted::ConvertedDifficulty, gradual::GradualDifficulty, inspect::InspectDifficulty,
Difficulty, ModsDependent,
},
performance::{gradual::GradualPerformance, HitResultPriority, Performance},
score_state::ScoreState,
strains::Strains,
+1 -1
View File
@@ -117,7 +117,7 @@ impl<'map> Performance<'map> {
/// 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>
pub fn mods(self, mods: u32) -> Self {
match self {
Self::Osu(o) => Self::Osu(o.mods(mods)),