implemented update up to osu commit cde8ae6
This commit is contained in:
@@ -106,3 +106,8 @@ pub(crate) fn rotate(center: Pos2, origin: Pos2, theta: f32) -> Pos2 {
|
||||
|
||||
center + offset
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn lerp(start: f32, end: f32, percent: f32) -> f32 {
|
||||
start + (end - start) * percent
|
||||
}
|
||||
|
||||
+27
-15
@@ -334,13 +334,17 @@ impl<'m> OsuPP<'m> {
|
||||
}
|
||||
|
||||
// AR bonus
|
||||
let mut ar_factor = 0.0;
|
||||
if attributes.ar > 10.33 {
|
||||
ar_factor += 0.4 * (attributes.ar - 10.33);
|
||||
let mut ar_factor = if attributes.ar > 10.33 {
|
||||
attributes.ar - 10.33
|
||||
} else if attributes.ar < 8.0 {
|
||||
ar_factor += 0.01 * (8.0 - attributes.ar);
|
||||
}
|
||||
aim_value *= 1.0 + ar_factor.min(ar_factor * total_hits / 1000.0);
|
||||
0.025 * (8.0 - attributes.ar)
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let ar_total_hits_factor = (1.0 + (-(0.007 * (total_hits - 400.0))).exp()).recip();
|
||||
|
||||
ar_factor *= 1.0 + (0.03 + 0.37 * ar_total_hits_factor) * ar_factor;
|
||||
|
||||
// HD bonus
|
||||
if self.mods.hd() {
|
||||
@@ -348,12 +352,15 @@ impl<'m> OsuPP<'m> {
|
||||
}
|
||||
|
||||
// FL bonus
|
||||
if self.mods.fl() {
|
||||
aim_value *= 1.0
|
||||
+ 0.35 * (total_hits / 200.0).min(1.0)
|
||||
let fl_bonus = if self.mods.fl() {
|
||||
1.0 + 0.35 * (total_hits / 200.0).min(1.0)
|
||||
+ (total_hits > 200.0) as u8 as f32 * 0.3 * ((total_hits - 200.0) / 300.0).min(1.0)
|
||||
+ (total_hits > 500.0) as u8 as f32 * (total_hits - 500.0) / 1200.0;
|
||||
}
|
||||
+ (total_hits > 500.0) as u8 as f32 * (total_hits - 500.0) / 1200.0
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
aim_value *= ar_factor.max(fl_bonus);
|
||||
|
||||
// Scale with accuracy
|
||||
aim_value *= 0.5 + self.acc.unwrap() / 2.0;
|
||||
@@ -387,10 +394,15 @@ impl<'m> OsuPP<'m> {
|
||||
}
|
||||
|
||||
// AR bonus
|
||||
if attributes.ar > 10.33 {
|
||||
let ar_factor = 0.4 * (attributes.ar - 10.33);
|
||||
speed_value *= 1.0 + ar_factor.min(ar_factor * total_hits / 1000.0);
|
||||
}
|
||||
let ar_factor = if attributes.ar > 10.33 {
|
||||
attributes.ar - 10.33
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let ar_total_hits_factor = (1.0 + (-(0.007 * (total_hits - 400.0))).exp()).recip();
|
||||
|
||||
speed_value *= 1.0 + (0.03 + 0.37 * ar_total_hits_factor) * ar_factor;
|
||||
|
||||
// HD bonus
|
||||
if self.mods.hd() {
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use crate::math_util;
|
||||
|
||||
use super::{DifficultyObject, SkillKind};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
const SPEED_SKILL_MULTIPLIER: f32 = 1400.0;
|
||||
const SPEED_STRAIN_DECAY_BASE: f32 = 0.3;
|
||||
const REDUCED_SECTION_COUNT: f32 = 10.0;
|
||||
const REDUCED_STRAIN_BASELINE: f32 = 0.75;
|
||||
const DIFFICULTY_MULTIPLIER: f32 = 1.06;
|
||||
|
||||
const AIM_SKILL_MULTIPLIER: f32 = 26.25;
|
||||
const AIM_STRAIN_DECAY_BASE: f32 = 0.15;
|
||||
@@ -56,6 +61,15 @@ impl Skill {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
self.strain_peaks
|
||||
.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
for (i, strain) in self.strain_peaks.iter_mut().enumerate() {
|
||||
let clamped = (i as f32 / REDUCED_SECTION_COUNT).clamp(0.0, 1.0);
|
||||
let scale = (math_util::lerp(1.0, 10.0, clamped)).log10();
|
||||
*strain *= math_util::lerp(REDUCED_STRAIN_BASELINE, 1.0, scale);
|
||||
}
|
||||
|
||||
self.strain_peaks
|
||||
.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
@@ -64,7 +78,7 @@ impl Skill {
|
||||
weight *= DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
difficulty * DIFFICULTY_MULTIPLIER
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -37,7 +37,7 @@ impl SkillKind {
|
||||
* (current.jump_dist - scale).max(0.0))
|
||||
.sqrt();
|
||||
|
||||
result = 1.5 * apply_diminishing_exp(angle_bonus.max(0.0))
|
||||
result = 1.4 * apply_diminishing_exp(angle_bonus.max(0.0))
|
||||
/ (TIMING_THRESHOLD).max(prev_strain_time)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,16 +140,16 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
aim.save_current_peak();
|
||||
speed.save_current_peak();
|
||||
|
||||
let aim_strain = aim.difficulty_value().sqrt() * DIFFICULTY_MULTIPLIER;
|
||||
let speed_strain = speed.difficulty_value().sqrt() * DIFFICULTY_MULTIPLIER;
|
||||
let aim_rating = aim.difficulty_value().sqrt() * DIFFICULTY_MULTIPLIER;
|
||||
let speed_rating = speed.difficulty_value().sqrt() * DIFFICULTY_MULTIPLIER;
|
||||
|
||||
let stars = aim_strain + speed_strain + (aim_strain - speed_strain).abs() / 2.0;
|
||||
let stars = aim_rating + speed_rating + (aim_rating - speed_rating).abs() / 2.0;
|
||||
|
||||
diff_attributes.n_circles = map.n_circles as usize;
|
||||
diff_attributes.n_spinners = map.n_spinners as usize;
|
||||
diff_attributes.stars = stars;
|
||||
diff_attributes.speed_strain = speed_strain;
|
||||
diff_attributes.aim_strain = aim_strain;
|
||||
diff_attributes.speed_strain = speed_rating;
|
||||
diff_attributes.aim_strain = aim_rating;
|
||||
|
||||
StarResult::Osu(diff_attributes)
|
||||
}
|
||||
@@ -273,3 +273,20 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
strains,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn custom() {
|
||||
let map_id = 1579923;
|
||||
let file = std::fs::File::open(format!(
|
||||
"C:/Users/Max/Desktop/Coding/C#/osu-tools/cache/{}_.osu",
|
||||
map_id
|
||||
))
|
||||
.unwrap();
|
||||
let map = crate::Beatmap::parse(file).unwrap();
|
||||
let result = crate::OsuPP::new(&map).mods((1 << 10) + 24).calculate();
|
||||
|
||||
println!("Stars: {} | PP: {}", result.stars(), result.pp());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use crate::math_util;
|
||||
|
||||
use super::{DifficultyObject, SkillKind};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
const SPEED_SKILL_MULTIPLIER: f32 = 1400.0;
|
||||
const SPEED_STRAIN_DECAY_BASE: f32 = 0.3;
|
||||
const REDUCED_STRAIN_BASELINE: f32 = 0.75;
|
||||
const DIFFICULTY_MULTIPLIER: f32 = 1.06;
|
||||
|
||||
const AIM_SKILL_MULTIPLIER: f32 = 26.25;
|
||||
const AIM_STRAIN_DECAY_BASE: f32 = 0.15;
|
||||
@@ -56,6 +60,23 @@ impl Skill {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
let reduced_section_count = self.kind.reduced_section_count();
|
||||
let reduced_section_count_f32 = reduced_section_count as f32;
|
||||
|
||||
self.strain_peaks
|
||||
.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
for (i, strain) in self
|
||||
.strain_peaks
|
||||
.iter_mut()
|
||||
.take(reduced_section_count)
|
||||
.enumerate()
|
||||
{
|
||||
let clamped = (i as f32 / reduced_section_count_f32).clamp(0.0, 1.0);
|
||||
let scale = (math_util::lerp(1.0, 10.0, clamped)).log10();
|
||||
*strain *= math_util::lerp(REDUCED_STRAIN_BASELINE, 1.0, scale);
|
||||
}
|
||||
|
||||
self.strain_peaks
|
||||
.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
@@ -64,7 +85,7 @@ impl Skill {
|
||||
weight *= DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
difficulty * DIFFICULTY_MULTIPLIER
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -12,6 +12,9 @@ const SPEED_BALANCING_FACTOR: f32 = 40.0;
|
||||
const AIM_ANGLE_BONUS_BEGIN: f32 = std::f32::consts::FRAC_PI_3;
|
||||
const TIMING_THRESHOLD: f32 = 107.0;
|
||||
|
||||
const AIM_REDUCED_SECTION_COUNT: usize = 10;
|
||||
const SPEED_REDUCED_SECTION_COUNT: usize = 5;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) enum SkillKind {
|
||||
Aim,
|
||||
@@ -37,7 +40,7 @@ impl SkillKind {
|
||||
* (current.jump_dist - scale).max(0.0))
|
||||
.sqrt();
|
||||
|
||||
result = 1.5 * apply_diminishing_exp(angle_bonus.max(0.0))
|
||||
result = 1.4 * apply_diminishing_exp(angle_bonus.max(0.0))
|
||||
/ (TIMING_THRESHOLD).max(prev_strain_time)
|
||||
}
|
||||
}
|
||||
@@ -92,6 +95,14 @@ impl SkillKind {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn reduced_section_count(&self) -> usize {
|
||||
match self {
|
||||
Self::Aim => AIM_REDUCED_SECTION_COUNT,
|
||||
Self::Speed => SPEED_REDUCED_SECTION_COUNT,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
use crate::math_util;
|
||||
|
||||
use super::{DifficultyObject, SkillKind};
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
const SPEED_SKILL_MULTIPLIER: f32 = 1400.0;
|
||||
const SPEED_STRAIN_DECAY_BASE: f32 = 0.3;
|
||||
const REDUCED_SECTION_COUNT: f32 = 10.0;
|
||||
const REDUCED_STRAIN_BASELINE: f32 = 0.75;
|
||||
const DIFFICULTY_MULTIPLIER: f32 = 1.06;
|
||||
|
||||
const AIM_SKILL_MULTIPLIER: f32 = 26.25;
|
||||
const AIM_STRAIN_DECAY_BASE: f32 = 0.15;
|
||||
@@ -56,6 +61,15 @@ impl Skill {
|
||||
let mut difficulty = 0.0;
|
||||
let mut weight = 1.0;
|
||||
|
||||
self.strain_peaks
|
||||
.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
for (i, strain) in self.strain_peaks.iter_mut().enumerate() {
|
||||
let clamped = (i as f32 / REDUCED_SECTION_COUNT).clamp(0.0, 1.0);
|
||||
let scale = (math_util::lerp(1.0, 10.0, clamped)).log10();
|
||||
*strain *= math_util::lerp(REDUCED_STRAIN_BASELINE, 1.0, scale);
|
||||
}
|
||||
|
||||
self.strain_peaks
|
||||
.sort_unstable_by(|a, b| b.partial_cmp(a).unwrap_or(Ordering::Equal));
|
||||
|
||||
@@ -64,7 +78,7 @@ impl Skill {
|
||||
weight *= DECAY_WEIGHT;
|
||||
}
|
||||
|
||||
difficulty
|
||||
difficulty * DIFFICULTY_MULTIPLIER
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
@@ -37,7 +37,7 @@ impl SkillKind {
|
||||
* (current.jump_dist - scale).max(0.0))
|
||||
.sqrt();
|
||||
|
||||
result = 1.5 * apply_diminishing_exp(angle_bonus.max(0.0))
|
||||
result = 1.4 * apply_diminishing_exp(angle_bonus.max(0.0))
|
||||
/ (TIMING_THRESHOLD).max(prev_strain_time)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user