allow custom clock rates

This commit is contained in:
MaxOhn
2022-03-20 15:17:04 +01:00
parent 6c1250fff3
commit 71eac0ffa3
11 changed files with 203 additions and 54 deletions
+22 -9
View File
@@ -52,6 +52,7 @@ pub struct OsuStars<'map> {
map: &'map Beatmap,
mods: u32,
passed_objects: Option<usize>,
clock_rate: Option<f64>,
}
impl<'map> OsuStars<'map> {
@@ -62,6 +63,7 @@ impl<'map> OsuStars<'map> {
map,
mods: 0,
passed_objects: None,
clock_rate: None,
}
}
@@ -87,6 +89,16 @@ impl<'map> OsuStars<'map> {
self
}
/// 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.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
}
/// Calculate all difficulty related values, including stars.
#[inline]
pub fn calculate(self) -> OsuDifficultyAttributes {
@@ -151,7 +163,7 @@ impl<'map> OsuStars<'map> {
/// Suitable to plot the difficulty of a map over time.
#[inline]
pub fn strains(self) -> Strains {
let mods = self.mods;
let clock_rate = self.clock_rate.unwrap_or_else(|| self.mods.speed());
let (mut skills, _) = calculate_skills(self);
let mut aim = mem::take(&mut skills.aim().strain_peaks);
@@ -180,7 +192,7 @@ impl<'map> OsuStars<'map> {
};
Strains {
section_length: SECTION_LEN * mods.speed(),
section_length: SECTION_LEN * clock_rate,
strains,
}
}
@@ -220,12 +232,14 @@ fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
map,
mods,
passed_objects,
clock_rate,
} = params;
let take = passed_objects.unwrap_or_else(|| map.hit_objects.len());
let clock_rate = clock_rate.unwrap_or_else(|| mods.speed());
let map_attributes = map.attributes().mods(mods);
let hit_window = difficulty_range_od(map_attributes.od) / map_attributes.clock_rate;
let hit_window = difficulty_range_od(map_attributes.od) / clock_rate;
let od = (80.0 - hit_window) / 6.0;
let mut raw_ar = map.ar as f64;
@@ -290,8 +304,7 @@ fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
let mut prev_prev = None;
// First object has no predecessor and thus no strain, handle distinctly
let mut curr_section_end =
(prev.time / map_attributes.clock_rate / SECTION_LEN).ceil() * SECTION_LEN;
let mut curr_section_end = (prev.time / clock_rate / SECTION_LEN).ceil() * SECTION_LEN;
// Handle second object separately to remove later if-branching
let h = DifficultyObject::new(
@@ -299,10 +312,10 @@ fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
&mut prev,
prev_prev.as_ref(),
&scaling_factor,
map_attributes.clock_rate,
clock_rate,
);
let base_time = h.base.time / map_attributes.clock_rate;
let base_time = h.base.time / clock_rate;
while base_time > curr_section_end {
skills.start_new_section_from(curr_section_end);
@@ -319,10 +332,10 @@ fn calculate_skills(params: OsuStars<'_>) -> (Skills, OsuDifficultyAttributes) {
&mut prev,
prev_prev.as_ref(),
&scaling_factor,
map_attributes.clock_rate,
clock_rate,
);
let base_time = h.base.time / map_attributes.clock_rate;
let base_time = h.base.time / clock_rate;
while base_time > curr_section_end {
skills.save_peak_and_start_new_section(curr_section_end);
+23 -4
View File
@@ -44,6 +44,7 @@ pub struct OsuPP<'map> {
pub(crate) n50: Option<usize>,
pub(crate) n_misses: usize,
pub(crate) passed_objects: Option<usize>,
clock_rate: Option<f64>,
}
impl<'map> OsuPP<'map> {
@@ -62,6 +63,7 @@ impl<'map> OsuPP<'map> {
n50: None,
n_misses: 0,
passed_objects: None,
clock_rate: None,
}
}
@@ -139,6 +141,16 @@ impl<'map> OsuPP<'map> {
self
}
/// 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.
#[inline]
pub fn clock_rate(mut self, clock_rate: f64) -> Self {
self.clock_rate = Some(clock_rate);
self
}
/// Provide parameters through an [`OsuScoreState`].
#[inline]
pub fn state(mut self, state: OsuScoreState) -> Self {
@@ -313,10 +325,17 @@ impl<'map> OsuPP<'map> {
/// Calculate all performance related values, including pp and stars.
pub fn calculate(mut self) -> OsuPerformanceAttributes {
let attributes = self.attributes.take().unwrap_or_else(|| {
OsuStars::new(self.map)
.mods(self.mods)
.passed_objects(self.passed_objects.unwrap_or(usize::MAX))
.calculate()
let mut calculator = OsuStars::new(self.map).mods(self.mods);
if let Some(passed_objects) = self.passed_objects {
calculator = calculator.passed_objects(passed_objects);
}
if let Some(clock_rate) = self.clock_rate {
calculator = calculator.clock_rate(clock_rate);
}
calculator.calculate()
});
self.assert_hitresults(attributes).calculate()