added some convenience methods

This commit is contained in:
MaxOhn
2024-02-26 17:10:54 +01:00
parent 8d2132c124
commit 1a5f9b9681
11 changed files with 211 additions and 70 deletions
+17 -23
View File
@@ -8,7 +8,7 @@ use crate::{
mania::{ManiaBeatmap, ManiaGradualDifficulty}, mania::{ManiaBeatmap, ManiaGradualDifficulty},
osu::{OsuBeatmap, OsuGradualDifficulty}, osu::{OsuBeatmap, OsuGradualDifficulty},
taiko::{TaikoBeatmap, TaikoGradualDifficulty}, taiko::{TaikoBeatmap, TaikoGradualDifficulty},
Beatmap, Converted, ModeDifficulty, Beatmap, ModeDifficulty,
}; };
/// Gradually calculate the difficulty attributes on maps of any mode. /// Gradually calculate the difficulty attributes on maps of any mode.
@@ -48,10 +48,10 @@ pub enum GradualDifficulty {
} }
macro_rules! from_converted { macro_rules! from_converted {
( $fn:ident, $mode:ident, $converted:ident, $gradual: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: &ModeDifficulty, converted: &$converted<'_>) -> Self {
Self::$mode($gradual::new(difficulty, converted)) Self::$mode(difficulty.gradual_difficulty(converted))
} }
}; };
} }
@@ -62,29 +62,23 @@ impl GradualDifficulty {
let map = Cow::Borrowed(map); let map = Cow::Borrowed(map);
match map.mode { match map.mode {
GameMode::Osu => Self::Osu(OsuGradualDifficulty::new( GameMode::Osu => Self::Osu(difficulty.gradual_difficulty(&OsuBeatmap::new(map, false))),
difficulty, GameMode::Taiko => {
&Converted::new(map, false), Self::Taiko(difficulty.gradual_difficulty(&TaikoBeatmap::new(map, false)))
)), }
GameMode::Taiko => Self::Taiko(TaikoGradualDifficulty::new( GameMode::Catch => {
difficulty, Self::Catch(difficulty.gradual_difficulty(&CatchBeatmap::new(map, false)))
&Converted::new(map, false), }
)), GameMode::Mania => {
GameMode::Catch => Self::Catch(CatchGradualDifficulty::new( Self::Mania(difficulty.gradual_difficulty(&ManiaBeatmap::new(map, false)))
difficulty, }
&Converted::new(map, false),
)),
GameMode::Mania => Self::Mania(ManiaGradualDifficulty::new(
difficulty,
&Converted::new(map, false),
)),
} }
} }
from_converted!(from_osu_map, Osu, OsuBeatmap, OsuGradualDifficulty); from_converted!(from_osu_map, Osu, OsuBeatmap);
from_converted!(from_taiko_map, Taiko, TaikoBeatmap, TaikoGradualDifficulty); from_converted!(from_taiko_map, Taiko, TaikoBeatmap);
from_converted!(from_catch_map, Catch, CatchBeatmap, CatchGradualDifficulty); from_converted!(from_catch_map, Catch, CatchBeatmap);
from_converted!(from_mania_map, Mania, ManiaBeatmap, ManiaGradualDifficulty); from_converted!(from_mania_map, Mania, ManiaBeatmap);
} }
impl Iterator for GradualDifficulty { impl Iterator for GradualDifficulty {
+13
View File
@@ -56,6 +56,19 @@ impl ModeDifficulty {
M::strains(self, map) 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 { pub(crate) const fn get_mods(&self) -> u32 {
self.mods self.mods
} }
+19 -23
View File
@@ -8,7 +8,7 @@ use crate::{
mania::{ManiaBeatmap, ManiaGradualPerformance}, mania::{ManiaBeatmap, ManiaGradualPerformance},
osu::{OsuBeatmap, OsuGradualPerformance}, osu::{OsuBeatmap, OsuGradualPerformance},
taiko::{TaikoBeatmap, TaikoGradualPerformance}, taiko::{TaikoBeatmap, TaikoGradualPerformance},
Beatmap, Converted, ModeDifficulty, Beatmap, ModeDifficulty,
}; };
/// Gradually calculate the performance attributes on maps of any mode. /// Gradually calculate the performance attributes on maps of any mode.
@@ -97,10 +97,10 @@ pub enum GradualPerformance {
} }
macro_rules! from_converted { macro_rules! from_converted {
( $fn:ident, $mode:ident, $converted:ident, $gradual: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: &ModeDifficulty, converted: &$converted<'_>) -> Self {
Self::$mode($gradual::new(difficulty, converted)) Self::$mode(difficulty.gradual_performance(converted))
} }
}; };
} }
@@ -111,29 +111,25 @@ impl GradualPerformance {
let map = Cow::Borrowed(map); let map = Cow::Borrowed(map);
match map.mode { match map.mode {
GameMode::Osu => Self::Osu(OsuGradualPerformance::new( GameMode::Osu => {
difficulty, Self::Osu(difficulty.gradual_performance(&OsuBeatmap::new(map, false)))
&Converted::new(map, false), }
)), GameMode::Taiko => {
GameMode::Taiko => Self::Taiko(TaikoGradualPerformance::new( Self::Taiko(difficulty.gradual_performance(&TaikoBeatmap::new(map, false)))
difficulty, }
&Converted::new(map, false), GameMode::Catch => {
)), Self::Catch(difficulty.gradual_performance(&CatchBeatmap::new(map, false)))
GameMode::Catch => Self::Catch(CatchGradualPerformance::new( }
difficulty, GameMode::Mania => {
&Converted::new(map, false), Self::Mania(difficulty.gradual_performance(&ManiaBeatmap::new(map, false)))
)), }
GameMode::Mania => Self::Mania(ManiaGradualPerformance::new(
difficulty,
&Converted::new(map, false),
)),
} }
} }
from_converted!(from_osu_map, Osu, OsuBeatmap, OsuGradualPerformance); from_converted!(from_osu_map, Osu, OsuBeatmap);
from_converted!(from_taiko_map, Taiko, TaikoBeatmap, TaikoGradualPerformance); from_converted!(from_taiko_map, Taiko, TaikoBeatmap);
from_converted!(from_catch_map, Catch, CatchBeatmap, CatchGradualPerformance); from_converted!(from_catch_map, Catch, CatchBeatmap);
from_converted!(from_mania_map, Mania, ManiaBeatmap, ManiaGradualPerformance); from_converted!(from_mania_map, Mania, ManiaBeatmap);
/// Process the next hit object and calculate the performance attributes /// Process the next hit object and calculate the performance attributes
/// for the resulting score state. /// for the resulting score state.
+7 -7
View File
@@ -288,25 +288,25 @@ impl<A: AttributeProvider> From<A> for Performance<'_> {
} }
macro_rules! impl_from_converted { macro_rules! impl_from_converted {
( $mode:ident: $performance:ident ) => { ( $mode:ident ) => {
impl<'a> From<Converted<'a, $mode>> for Performance<'a> { impl<'a> From<Converted<'a, $mode>> for Performance<'a> {
fn from(converted: Converted<'a, $mode>) -> Self { fn from(converted: Converted<'a, $mode>) -> Self {
Self::$mode($performance::new(converted)) Self::$mode(converted.into())
} }
} }
impl<'a, 'b: 'a> From<&'b Converted<'a, $mode>> for Performance<'a> { impl<'a, 'b: 'a> From<&'b Converted<'a, $mode>> for Performance<'a> {
fn from(converted: &'b Converted<'a, $mode>) -> Self { fn from(converted: &'b Converted<'a, $mode>) -> Self {
Self::$mode($performance::new(converted.as_owned())) Self::$mode(converted.as_owned().into())
} }
} }
}; };
} }
impl_from_converted!(Osu: OsuPerformance); impl_from_converted!(Osu);
impl_from_converted!(Taiko: TaikoPerformance); impl_from_converted!(Taiko);
impl_from_converted!(Catch: CatchPerformance); impl_from_converted!(Catch);
impl_from_converted!(Mania: ManiaPerformance); impl_from_converted!(Mania);
/// While generating remaining hitresults, decide how they should be distributed. /// While generating remaining hitresults, decide how they should be distributed.
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] #[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
+21
View File
@@ -36,6 +36,9 @@ pub struct Catch;
impl IGameMode for Catch { impl IGameMode for Catch {
type DifficultyAttributes = CatchDifficultyAttributes; type DifficultyAttributes = CatchDifficultyAttributes;
type Strains = CatchStrains; type Strains = CatchStrains;
type Performance<'map> = CatchPerformance<'map>;
type GradualDifficulty = CatchGradualDifficulty;
type GradualPerformance = CatchGradualPerformance;
fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus { fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus {
convert::try_convert(map) convert::try_convert(map)
@@ -51,4 +54,22 @@ impl IGameMode for Catch {
fn strains(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self::Strains { fn strains(difficulty: &ModeDifficulty, converted: &CatchBeatmap<'_>) -> Self::Strains {
strains::strains(difficulty, converted) strains::strains(difficulty, converted)
} }
fn performance(map: CatchBeatmap<'_>) -> Self::Performance<'_> {
CatchPerformance::new(map)
}
fn gradual_difficulty(
difficulty: &ModeDifficulty,
map: &CatchBeatmap<'_>,
) -> Self::GradualDifficulty {
CatchGradualDifficulty::new(difficulty, map)
}
fn gradual_performance(
difficulty: &ModeDifficulty,
map: &CatchBeatmap<'_>,
) -> Self::GradualPerformance {
CatchGradualPerformance::new(difficulty, map)
}
} }
+21
View File
@@ -33,6 +33,9 @@ pub struct Mania;
impl IGameMode for Mania { impl IGameMode for Mania {
type DifficultyAttributes = ManiaDifficultyAttributes; type DifficultyAttributes = ManiaDifficultyAttributes;
type Strains = ManiaStrains; type Strains = ManiaStrains;
type Performance<'map> = ManiaPerformance<'map>;
type GradualDifficulty = ManiaGradualDifficulty;
type GradualPerformance = ManiaGradualPerformance;
fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus { fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus {
convert::try_convert(map) convert::try_convert(map)
@@ -48,4 +51,22 @@ impl IGameMode for Mania {
fn strains(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> Self::Strains { fn strains(difficulty: &ModeDifficulty, converted: &ManiaBeatmap<'_>) -> Self::Strains {
strains::strains(difficulty, converted) strains::strains(difficulty, converted)
} }
fn performance(map: ManiaBeatmap<'_>) -> Self::Performance<'_> {
ManiaPerformance::new(map)
}
fn gradual_difficulty(
difficulty: &ModeDifficulty,
map: &ManiaBeatmap<'_>,
) -> Self::GradualDifficulty {
ManiaGradualDifficulty::new(difficulty, map)
}
fn gradual_performance(
difficulty: &ModeDifficulty,
map: &ManiaBeatmap<'_>,
) -> Self::GradualPerformance {
ManiaGradualPerformance::new(difficulty, map)
}
} }
+34 -14
View File
@@ -5,7 +5,10 @@ use std::{
marker::PhantomData, marker::PhantomData,
}; };
use crate::model::mode::{ConvertStatus, IGameMode}; use crate::{
model::mode::{ConvertStatus, IGameMode},
ModeDifficulty,
};
use super::{attributes::BeatmapAttributesBuilder, Beatmap}; use super::{attributes::BeatmapAttributesBuilder, Beatmap};
@@ -43,6 +46,24 @@ impl<'a, M> Converted<'a, M> {
} }
} }
impl<M> Converted<'_, M> {
/// Sum up the duration of all breaks (in milliseconds).
pub fn total_break_time(&self) -> f64 {
self.map.total_break_time()
}
/// Returns a [`BeatmapAttributesBuilder`] to calculate modified beatmap
/// attributes.
pub fn attributes(&self) -> BeatmapAttributesBuilder {
self.into()
}
/// The beats per minute of the map.
pub fn bpm(&self) -> f64 {
self.map.bpm()
}
}
impl<M: IGameMode> Converted<'_, M> { impl<M: IGameMode> Converted<'_, M> {
/// Attempt to convert a [`Beatmap`] to the specified mode. /// Attempt to convert a [`Beatmap`] to the specified mode.
/// ///
@@ -72,20 +93,14 @@ impl<M: IGameMode> Converted<'_, M> {
Self::try_from_owned(map).unwrap_or_else(|_| panic!("{}", INCOMPATIBLE_MODES)) Self::try_from_owned(map).unwrap_or_else(|_| panic!("{}", INCOMPATIBLE_MODES))
} }
/// Sum up the duration of all breaks (in milliseconds). /// Create a gradual difficulty calculator for the map.
pub fn total_break_time(&self) -> f64 { pub fn gradual_difficulty(&self, difficulty: &ModeDifficulty) -> M::GradualDifficulty {
self.map.total_break_time() difficulty.gradual_difficulty(self)
} }
/// Returns a [`BeatmapAttributesBuilder`] to calculate modified beatmap /// Create a gradual performance calculator for the map.
/// attributes. pub fn gradual_performance(&self, difficulty: &ModeDifficulty) -> M::GradualPerformance {
pub fn attributes(&self) -> BeatmapAttributesBuilder { difficulty.gradual_performance(self)
self.into()
}
/// The beats per minute of the map.
pub fn bpm(&self) -> f64 {
self.map.bpm()
} }
} }
@@ -93,13 +108,18 @@ impl<'a, M: IGameMode> Converted<'a, M> {
/// Borrow the contained [`Beatmap`] to cheaply create a new owned /// Borrow the contained [`Beatmap`] to cheaply create a new owned
/// [`Converted`]. /// [`Converted`].
/// ///
/// This is the same as `.clone()` except cheap but its lifetime might be /// This is the same as `.clone()` except cheap - but its lifetime might be
/// shorter. /// shorter.
#[must_use] #[must_use]
pub fn as_owned(&'a self) -> Self { pub fn as_owned(&'a self) -> Self {
Self::new(Cow::Borrowed(self.map.as_ref()), self.is_convert) Self::new(Cow::Borrowed(self.map.as_ref()), self.is_convert)
} }
/// Create a performance calculator for the map.
pub fn performance(self) -> M::Performance<'a> {
M::performance(self)
}
/// Attempt to convert a [`&Beatmap`] to the specified mode. /// Attempt to convert a [`&Beatmap`] to the specified mode.
/// ///
/// If the conversion is incompatible, `None` is returned. /// If the conversion is incompatible, `None` is returned.
+13 -3
View File
@@ -5,7 +5,7 @@ use rosu_map::{
LATEST_FORMAT_VERSION, LATEST_FORMAT_VERSION,
}; };
use crate::{Difficulty, Performance}; use crate::{Difficulty, GradualDifficulty, GradualPerformance, ModeDifficulty, Performance};
pub use self::{ pub use self::{
attributes::{BeatmapAttributes, BeatmapAttributesBuilder, HitWindows}, attributes::{BeatmapAttributes, BeatmapAttributesBuilder, HitWindows},
@@ -70,16 +70,26 @@ impl Beatmap {
rosu_map::from_bytes(bytes) rosu_map::from_bytes(bytes)
} }
/// Create a new difficulty calculator for this [`Beatmap`]. /// Create a difficulty calculator for this [`Beatmap`].
pub fn difficulty(&self) -> Difficulty<'_> { pub fn difficulty(&self) -> Difficulty<'_> {
Difficulty::new(self) Difficulty::new(self)
} }
/// Create a new performance calculator for this [`Beatmap`]. /// Create a performance calculator for this [`Beatmap`].
pub fn performance(&self) -> Performance<'_> { pub fn performance(&self) -> Performance<'_> {
Performance::new(self) Performance::new(self)
} }
/// Create a gradual difficulty calculator for this [`Beatmap`].
pub fn gradual_difficulty(&self, difficulty: &ModeDifficulty) -> GradualDifficulty {
GradualDifficulty::new(difficulty, self)
}
/// Create a gradual performance calculator for this [`Beatmap`].
pub fn gradual_performance(&self, difficulty: &ModeDifficulty) -> GradualPerformance {
GradualPerformance::new(difficulty, self)
}
/// Finds the [`TimingPoint`] that is active at the given time. /// Finds the [`TimingPoint`] that is active at the given time.
pub(crate) fn timing_point_at(&self, time: f64) -> Option<&TimingPoint> { pub(crate) fn timing_point_at(&self, time: f64) -> Option<&TimingPoint> {
timing_point_at(&self.timing_points, time) timing_point_at(&self.timing_points, time)
+24
View File
@@ -22,6 +22,15 @@ pub trait IGameMode: Sized {
/// The resulting type of a strain calculation. /// The resulting type of a strain calculation.
type Strains; type Strains;
/// The type of a performance calculator.
type Performance<'map>;
/// The type of a gradual difficulty calculator.
type GradualDifficulty;
/// The type of a gradual performance calculator.
type GradualPerformance;
/// Attempt to convert a beatmap. /// Attempt to convert a beatmap.
/// ///
/// In case [`ConvertStatus::Incompatible`] is returned, the map should /// In case [`ConvertStatus::Incompatible`] is returned, the map should
@@ -38,6 +47,21 @@ pub trait IGameMode: Sized {
/// 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: &ModeDifficulty, map: &Converted<'_, Self>) -> Self::Strains;
/// Create a performance calculator for a [`Converted`] beatmap.
fn performance(map: Converted<'_, Self>) -> Self::Performance<'_>;
/// Create a gradual difficulty calculator for a [`Converted`] beatmap.
fn gradual_difficulty(
difficulty: &ModeDifficulty,
map: &Converted<'_, Self>,
) -> Self::GradualDifficulty;
/// Create a gradual performance calculator for a [`Converted`] beatmap.
fn gradual_performance(
difficulty: &ModeDifficulty,
map: &Converted<'_, Self>,
) -> Self::GradualPerformance;
} }
/// The status of a conversion through [`IGameMode::try_convert`]. /// The status of a conversion through [`IGameMode::try_convert`].
+21
View File
@@ -37,6 +37,9 @@ pub struct Osu;
impl IGameMode for Osu { impl IGameMode for Osu {
type DifficultyAttributes = OsuDifficultyAttributes; type DifficultyAttributes = OsuDifficultyAttributes;
type Strains = OsuStrains; type Strains = OsuStrains;
type Performance<'map> = OsuPerformance<'map>;
type GradualDifficulty = OsuGradualDifficulty;
type GradualPerformance = OsuGradualPerformance;
fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus { fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus {
convert::try_convert(map) convert::try_convert(map)
@@ -52,4 +55,22 @@ impl IGameMode for Osu {
fn strains(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> Self::Strains { fn strains(difficulty: &ModeDifficulty, converted: &OsuBeatmap<'_>) -> Self::Strains {
strains::strains(difficulty, converted) strains::strains(difficulty, converted)
} }
fn performance(map: OsuBeatmap<'_>) -> Self::Performance<'_> {
OsuPerformance::new(map)
}
fn gradual_difficulty(
difficulty: &ModeDifficulty,
map: &OsuBeatmap<'_>,
) -> Self::GradualDifficulty {
OsuGradualDifficulty::new(difficulty, map)
}
fn gradual_performance(
difficulty: &ModeDifficulty,
map: &OsuBeatmap<'_>,
) -> Self::GradualPerformance {
OsuGradualPerformance::new(difficulty, map)
}
} }
+21
View File
@@ -33,6 +33,9 @@ pub struct Taiko;
impl IGameMode for Taiko { impl IGameMode for Taiko {
type DifficultyAttributes = TaikoDifficultyAttributes; type DifficultyAttributes = TaikoDifficultyAttributes;
type Strains = TaikoStrains; type Strains = TaikoStrains;
type Performance<'map> = TaikoPerformance<'map>;
type GradualDifficulty = TaikoGradualDifficulty;
type GradualPerformance = TaikoGradualPerformance;
fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus { fn try_convert(map: &mut Cow<'_, Beatmap>) -> ConvertStatus {
convert::try_convert(map) convert::try_convert(map)
@@ -48,4 +51,22 @@ impl IGameMode for Taiko {
fn strains(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> Self::Strains { fn strains(difficulty: &ModeDifficulty, converted: &TaikoBeatmap<'_>) -> Self::Strains {
strains::strains(difficulty, converted) strains::strains(difficulty, converted)
} }
fn performance(map: TaikoBeatmap<'_>) -> Self::Performance<'_> {
TaikoPerformance::new(map)
}
fn gradual_difficulty(
difficulty: &ModeDifficulty,
map: &TaikoBeatmap<'_>,
) -> Self::GradualDifficulty {
TaikoGradualDifficulty::new(difficulty, map)
}
fn gradual_performance(
difficulty: &ModeDifficulty,
map: &TaikoBeatmap<'_>,
) -> Self::GradualPerformance {
TaikoGradualPerformance::new(difficulty, map)
}
} }