diff --git a/src/any/difficulty/inspect.rs b/src/any/difficulty/inspect.rs index 1f76ac94..f412fc61 100644 --- a/src/any/difficulty/inspect.rs +++ b/src/any/difficulty/inspect.rs @@ -27,6 +27,11 @@ pub struct InspectDifficulty { /// /// Only relevant for osu!catch. pub hardrock_offsets: Option, + /// Whether the calculated attributes belong to an osu!lazer or osu!stable + /// score. + /// + /// Defaults to `true`. + pub lazer: Option, } impl InspectDifficulty { @@ -41,6 +46,7 @@ impl InspectDifficulty { hp, od, hardrock_offsets, + lazer, } = self; let mut difficulty = Difficulty::new().mods(mods); @@ -73,6 +79,10 @@ impl InspectDifficulty { difficulty = difficulty.hardrock_offsets(hardrock_offsets); } + if let Some(lazer) = lazer { + difficulty = difficulty.lazer(lazer); + } + difficulty } } diff --git a/src/any/difficulty/mod.rs b/src/any/difficulty/mod.rs index fa55fc7a..9a42d244 100644 --- a/src/any/difficulty/mod.rs +++ b/src/any/difficulty/mod.rs @@ -62,6 +62,7 @@ pub struct Difficulty { hp: Option, od: Option, hardrock_offsets: Option, + lazer: Option, } /// Wrapper for beatmap attributes in [`Difficulty`]. @@ -97,6 +98,7 @@ impl Difficulty { hp: None, od: None, hardrock_offsets: None, + lazer: None, } } @@ -120,6 +122,7 @@ impl Difficulty { hp, od, hardrock_offsets, + lazer, } = self; InspectDifficulty { @@ -131,6 +134,7 @@ impl Difficulty { hp, od, hardrock_offsets, + lazer, } } @@ -268,6 +272,18 @@ impl Difficulty { self } + /// Whether the calculated attributes belong to an osu!lazer or osu!stable + /// score. + /// + /// Defaults to `true`. + /// + /// Only relevant for osu!standard performance calculation. + pub const fn lazer(mut self, lazer: bool) -> Self { + self.lazer = Some(lazer); + + self + } + /// Perform the difficulty calculation. pub fn calculate(&self, map: &Beatmap) -> DifficultyAttributes { let map = Cow::Borrowed(map); @@ -347,6 +363,10 @@ impl Difficulty { self.hardrock_offsets .unwrap_or_else(|| self.mods.hardrock_offsets()) } + + pub(crate) fn get_lazer(&self) -> bool { + self.lazer.unwrap_or(true) + } } fn non_zero_u32_to_f32(n: NonZeroU32) -> f32 { @@ -364,6 +384,7 @@ impl Debug for Difficulty { hp, od, hardrock_offsets, + lazer, } = self; f.debug_struct("Difficulty") @@ -375,6 +396,7 @@ impl Debug for Difficulty { .field("hp", hp) .field("od", od) .field("hardrock_offsets", hardrock_offsets) + .field("lazer", lazer) .finish() } } diff --git a/src/any/performance/mod.rs b/src/any/performance/mod.rs index 36e48352..6acf60cb 100644 --- a/src/any/performance/mod.rs +++ b/src/any/performance/mod.rs @@ -302,7 +302,7 @@ impl<'map> Performance<'map> { /// Whether the calculated attributes belong to an osu!lazer or osu!stable /// score. /// - /// Defaults to lazer. + /// Defaults to `true`. /// /// This affects internal accuracy calculation because lazer considers /// slider heads for accuracy whereas stable does not. diff --git a/src/catch/performance/mod.rs b/src/catch/performance/mod.rs index 8ceb4ac6..e384e6fa 100644 --- a/src/catch/performance/mod.rs +++ b/src/catch/performance/mod.rs @@ -481,7 +481,6 @@ impl<'map> TryFrom> for CatchPerformance<'map> { n50, misses, hitresult_priority: _, - lazer: _, } = osu; Ok(Self { diff --git a/src/mania/performance/mod.rs b/src/mania/performance/mod.rs index 3e8f0112..582a40ef 100644 --- a/src/mania/performance/mod.rs +++ b/src/mania/performance/mod.rs @@ -834,7 +834,6 @@ impl<'map> TryFrom> for ManiaPerformance<'map> { n50, misses, hitresult_priority, - lazer: _, } = osu; Ok(Self { diff --git a/src/osu/convert.rs b/src/osu/convert.rs index a3227f57..c5269b10 100644 --- a/src/osu/convert.rs +++ b/src/osu/convert.rs @@ -55,18 +55,8 @@ pub fn convert_objects( OsuObjectKind::Circle => attrs.n_circles += 1, OsuObjectKind::Slider(ref slider) => { attrs.n_sliders += 1; + attrs.n_slider_ticks += slider.tick_count() as u32; attrs.max_combo += slider.nested_objects.len() as u32; - - attrs.n_slider_ticks += slider - .nested_objects - .iter() - .filter(|nested| { - matches!( - nested.kind, - NestedSliderObjectKind::Tick | NestedSliderObjectKind::Repeat - ) - }) - .count() as u32; } OsuObjectKind::Spinner(_) => attrs.n_spinners += 1, } diff --git a/src/osu/difficulty/gradual.rs b/src/osu/difficulty/gradual.rs index fc724ae5..3e3b7548 100644 --- a/src/osu/difficulty/gradual.rs +++ b/src/osu/difficulty/gradual.rs @@ -92,6 +92,7 @@ impl OsuGradualDifficulty { attrs.n_circles = 0; attrs.n_sliders = 0; + attrs.n_slider_ticks = 0; attrs.n_spinners = 0; attrs.max_combo = 0; @@ -128,6 +129,7 @@ impl OsuGradualDifficulty { OsuObjectKind::Circle => attrs.n_circles += 1, OsuObjectKind::Slider(slider) => { attrs.n_sliders += 1; + attrs.n_slider_ticks += slider.tick_count() as u32; attrs.max_combo += slider.nested_objects.len() as u32; } OsuObjectKind::Spinner { .. } => attrs.n_spinners += 1, diff --git a/src/osu/object.rs b/src/osu/object.rs index cb078638..6d929c51 100644 --- a/src/osu/object.rs +++ b/src/osu/object.rs @@ -308,6 +308,19 @@ impl OsuSlider { .count() } + /// Counts both ticks and repeats + pub fn tick_count(&self) -> usize { + self.nested_objects + .iter() + .filter(|nested| { + matches!( + nested.kind, + NestedSliderObjectKind::Tick | NestedSliderObjectKind::Repeat + ) + }) + .count() + } + pub fn tail(&self) -> Option<&NestedSliderObject> { self.nested_objects .iter() diff --git a/src/osu/performance/gradual.rs b/src/osu/performance/gradual.rs index 749ef5d2..f3d43c1b 100644 --- a/src/osu/performance/gradual.rs +++ b/src/osu/performance/gradual.rs @@ -80,15 +80,17 @@ use super::{OsuPerformanceAttributes, OsuScoreState}; /// [`next`]: OsuGradualPerformance::next /// [`nth`]: OsuGradualPerformance::nth pub struct OsuGradualPerformance { + lazer: bool, difficulty: OsuGradualDifficulty, } impl OsuGradualPerformance { /// Create a new gradual performance calculator for osu!standard maps. pub fn new(difficulty: Difficulty, converted: &OsuBeatmap<'_>) -> Self { + let lazer = difficulty.get_lazer(); let difficulty = OsuGradualDifficulty::new(difficulty, converted); - Self { difficulty } + Self { lazer, difficulty } } /// Process the next hit object and calculate the performance attributes @@ -113,6 +115,7 @@ impl OsuGradualPerformance { .difficulty .nth(n)? .performance() + .lazer(self.lazer) .state(state) .difficulty(self.difficulty.difficulty.clone()) .passed_objects(self.difficulty.idx as u32) diff --git a/src/osu/performance/mod.rs b/src/osu/performance/mod.rs index 47228420..6c1074ae 100644 --- a/src/osu/performance/mod.rs +++ b/src/osu/performance/mod.rs @@ -35,7 +35,6 @@ pub struct OsuPerformance<'map> { pub(crate) n50: Option, pub(crate) misses: Option, pub(crate) hitresult_priority: HitResultPriority, - pub(crate) lazer: Option, } impl<'map> OsuPerformance<'map> { @@ -158,12 +157,12 @@ impl<'map> OsuPerformance<'map> { /// Whether the calculated attributes belong to an osu!lazer or osu!stable /// score. /// - /// Defaults to lazer. + /// Defaults to `true`. /// /// This affects internal accuracy calculation because lazer considers /// slider heads for accuracy whereas stable does not. - pub const fn lazer(mut self, lazer: bool) -> Self { - self.lazer = Some(lazer); + pub fn lazer(mut self, lazer: bool) -> Self { + self.difficulty = self.difficulty.lazer(lazer); self } @@ -366,7 +365,7 @@ impl<'map> OsuPerformance<'map> { let mut n100 = self.n100.map_or(0, |n| cmp::min(n, n_remaining)); let mut n50 = self.n50.map_or(0, |n| cmp::min(n, n_remaining)); - let lazer = self.lazer.unwrap_or(true); + let lazer = self.difficulty.get_lazer(); let (n_slider_ends, n_slider_ticks, max_slider_ends, max_slider_ticks) = if lazer { let n_slider_ends = self @@ -627,7 +626,7 @@ impl<'map> OsuPerformance<'map> { let effective_miss_count = calculate_effective_misses(&attrs, &state); - let lazer = self.lazer.unwrap_or(true); + let lazer = self.difficulty.get_lazer(); let (n_slider_ends, n_slider_ticks) = if lazer { (attrs.n_sliders, attrs.n_slider_ticks) @@ -660,7 +659,6 @@ impl<'map> OsuPerformance<'map> { n50: None, misses: None, hitresult_priority: HitResultPriority::DEFAULT, - lazer: None, } } } diff --git a/src/taiko/performance/mod.rs b/src/taiko/performance/mod.rs index 95e1ce81..a5067dd4 100644 --- a/src/taiko/performance/mod.rs +++ b/src/taiko/performance/mod.rs @@ -371,7 +371,6 @@ impl<'map> TryFrom> for TaikoPerformance<'map> { n50: _, misses, hitresult_priority, - lazer: _, } = osu; Ok(Self {