updated osu tests & more bugfixing for osu
This commit is contained in:
+3
-4
@@ -334,7 +334,7 @@ impl<'m> OsuPP<'m> {
|
||||
}
|
||||
|
||||
// AR bonus
|
||||
let mut ar_factor = if attributes.ar > 10.33 {
|
||||
let ar_factor = if attributes.ar > 10.33 {
|
||||
attributes.ar - 10.33
|
||||
} else if attributes.ar < 8.0 {
|
||||
0.025 * (8.0 - attributes.ar)
|
||||
@@ -343,8 +343,7 @@ impl<'m> OsuPP<'m> {
|
||||
};
|
||||
|
||||
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;
|
||||
let ar_bonus = 1.0 + (0.03 + 0.37 * ar_total_hits_factor) * ar_factor;
|
||||
|
||||
// HD bonus
|
||||
if self.mods.hd() {
|
||||
@@ -360,7 +359,7 @@ impl<'m> OsuPP<'m> {
|
||||
1.0
|
||||
};
|
||||
|
||||
aim_value *= ar_factor.max(fl_bonus);
|
||||
aim_value *= ar_bonus.max(fl_bonus);
|
||||
|
||||
// Scale with accuracy
|
||||
aim_value *= 0.5 + self.acc.unwrap() / 2.0;
|
||||
|
||||
@@ -18,10 +18,9 @@ impl<'h> DifficultyObject<'h> {
|
||||
prev: &OsuObject,
|
||||
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
|
||||
prev_prev: Option<OsuObject>,
|
||||
clock_rate: f32,
|
||||
scaling_factor: f32,
|
||||
) -> Self {
|
||||
let delta = (base.time - prev.time) / clock_rate;
|
||||
let delta = base.time - prev.time;
|
||||
let strain_time = delta.max(50.0);
|
||||
|
||||
let pos = base.pos; // stacked position
|
||||
|
||||
@@ -62,8 +62,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
}
|
||||
|
||||
let time_preempt = difficulty_range_ar(raw_ar);
|
||||
|
||||
let section_len = SECTION_LEN * map_attributes.clock_rate;
|
||||
let scale = (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
|
||||
let radius = OBJECT_RADIUS * scale;
|
||||
let mut scaling_factor = NORMALIZED_RADIUS / radius;
|
||||
@@ -76,23 +74,21 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
let mut slider_state = SliderState::new(map);
|
||||
let mut ticks_buf = Vec::new();
|
||||
|
||||
let mut hit_objects: Vec<_> = map
|
||||
.hit_objects
|
||||
.iter()
|
||||
.take(take)
|
||||
.filter_map(|h| {
|
||||
OsuObject::new(
|
||||
h,
|
||||
map,
|
||||
radius,
|
||||
scaling_factor,
|
||||
hr,
|
||||
&mut ticks_buf,
|
||||
&mut diff_attributes,
|
||||
&mut slider_state,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let hit_objects_iter = map.hit_objects.iter().take(take).filter_map(|h| {
|
||||
OsuObject::new(
|
||||
h,
|
||||
map,
|
||||
radius,
|
||||
scaling_factor,
|
||||
hr,
|
||||
&mut ticks_buf,
|
||||
&mut diff_attributes,
|
||||
&mut slider_state,
|
||||
)
|
||||
});
|
||||
|
||||
let mut hit_objects = Vec::with_capacity(take);
|
||||
hit_objects.extend(hit_objects_iter);
|
||||
|
||||
let stack_threshold = time_preempt * map.stack_leniency;
|
||||
|
||||
@@ -107,6 +103,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
let mut hit_objects = hit_objects.into_iter().map(|mut h| {
|
||||
let stack_offset = h.stack_height * scale_factor;
|
||||
|
||||
h.time /= map_attributes.clock_rate;
|
||||
h.pos += Pos2 {
|
||||
x: stack_offset,
|
||||
y: stack_offset,
|
||||
@@ -118,27 +115,19 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
let mut aim = Skill::new(SkillKind::Aim);
|
||||
let mut speed = Skill::new(SkillKind::Speed);
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end =
|
||||
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
|
||||
|
||||
let mut prev_prev = None;
|
||||
let mut prev = hit_objects.next().unwrap();
|
||||
let mut prev_vals = None;
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
|
||||
// Handle second object separately to remove later if-branching
|
||||
let curr = hit_objects.next().unwrap();
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -150,14 +139,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
|
||||
// Handle all other objects
|
||||
for curr in hit_objects {
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
aim.save_current_peak();
|
||||
@@ -165,7 +147,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
speed.save_current_peak();
|
||||
speed.start_new_section_from(current_section_end);
|
||||
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -222,8 +204,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
}
|
||||
|
||||
let time_preempt = difficulty_range_ar(raw_ar);
|
||||
|
||||
let section_len = SECTION_LEN * map_attributes.clock_rate;
|
||||
let scale = (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
|
||||
let radius = OBJECT_RADIUS * scale;
|
||||
let mut scaling_factor = NORMALIZED_RADIUS / radius;
|
||||
@@ -236,22 +216,21 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
let mut slider_state = SliderState::new(map);
|
||||
let mut ticks_buf = Vec::new();
|
||||
|
||||
let mut hit_objects: Vec<_> = map
|
||||
.hit_objects
|
||||
.iter()
|
||||
.filter_map(|h| {
|
||||
OsuObject::new(
|
||||
h,
|
||||
map,
|
||||
radius,
|
||||
scaling_factor,
|
||||
hr,
|
||||
&mut ticks_buf,
|
||||
&mut diff_attributes,
|
||||
&mut slider_state,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let hit_objects_iter = map.hit_objects.iter().filter_map(|h| {
|
||||
OsuObject::new(
|
||||
h,
|
||||
map,
|
||||
radius,
|
||||
scaling_factor,
|
||||
hr,
|
||||
&mut ticks_buf,
|
||||
&mut diff_attributes,
|
||||
&mut slider_state,
|
||||
)
|
||||
});
|
||||
|
||||
let mut hit_objects = Vec::with_capacity(map.hit_objects.len());
|
||||
hit_objects.extend(hit_objects_iter);
|
||||
|
||||
let stack_threshold = time_preempt * map.stack_leniency;
|
||||
|
||||
@@ -266,6 +245,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
let mut hit_objects = hit_objects.into_iter().map(|mut h| {
|
||||
let stack_offset = h.stack_height * scale_factor;
|
||||
|
||||
h.time /= map_attributes.clock_rate;
|
||||
h.pos += Pos2 {
|
||||
x: stack_offset,
|
||||
y: stack_offset,
|
||||
@@ -277,27 +257,19 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
let mut aim = Skill::new(SkillKind::Aim);
|
||||
let mut speed = Skill::new(SkillKind::Speed);
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end =
|
||||
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
|
||||
|
||||
let mut prev_prev = None;
|
||||
let mut prev = hit_objects.next().unwrap();
|
||||
let mut prev_vals = None;
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
|
||||
// Handle second object separately to remove later if-branching
|
||||
let curr = hit_objects.next().unwrap();
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -309,14 +281,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
|
||||
// Handle all other objects
|
||||
for curr in hit_objects {
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
aim.save_current_peak();
|
||||
@@ -324,7 +289,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
speed.save_current_peak();
|
||||
speed.start_new_section_from(current_section_end);
|
||||
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -346,7 +311,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
.collect();
|
||||
|
||||
Strains {
|
||||
section_length: section_len,
|
||||
section_length: SECTION_LEN,
|
||||
strains,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,9 @@ impl<'h> DifficultyObject<'h> {
|
||||
prev: &OsuObject,
|
||||
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
|
||||
prev_prev: Option<OsuObject>,
|
||||
clock_rate: f32,
|
||||
scaling_factor: f32,
|
||||
) -> Self {
|
||||
let delta = (base.time - prev.time) / clock_rate;
|
||||
let delta = base.time - prev.time;
|
||||
let strain_time = delta.max(50.0);
|
||||
|
||||
let pos = base.pos;
|
||||
|
||||
@@ -52,7 +52,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
return StarResult::Osu(diff_attributes);
|
||||
}
|
||||
|
||||
let section_len = SECTION_LEN * map_attributes.clock_rate;
|
||||
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
|
||||
let mut scaling_factor = NORMALIZED_RADIUS / radius;
|
||||
|
||||
@@ -64,42 +63,43 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
let mut slider_state = SliderState::new(map);
|
||||
let mut ticks_buf = Vec::new();
|
||||
|
||||
let mut hit_objects = map.hit_objects.iter().take(take).filter_map(|h| {
|
||||
OsuObject::new(
|
||||
h,
|
||||
map,
|
||||
radius,
|
||||
scaling_factor,
|
||||
&mut ticks_buf,
|
||||
&mut diff_attributes,
|
||||
&mut slider_state,
|
||||
)
|
||||
});
|
||||
let mut hit_objects = map
|
||||
.hit_objects
|
||||
.iter()
|
||||
.take(take)
|
||||
.filter_map(|h| {
|
||||
OsuObject::new(
|
||||
h,
|
||||
map,
|
||||
radius,
|
||||
scaling_factor,
|
||||
&mut ticks_buf,
|
||||
&mut diff_attributes,
|
||||
&mut slider_state,
|
||||
)
|
||||
})
|
||||
.map(|mut h| {
|
||||
h.time /= map_attributes.clock_rate;
|
||||
|
||||
h
|
||||
});
|
||||
|
||||
let mut aim = Skill::new(SkillKind::Aim);
|
||||
let mut speed = Skill::new(SkillKind::Speed);
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end =
|
||||
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
|
||||
|
||||
let mut prev_prev = None;
|
||||
let mut prev = hit_objects.next().unwrap();
|
||||
let mut prev_vals = None;
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
|
||||
// Handle second object separately to remove later if-branching
|
||||
let curr = hit_objects.next().unwrap();
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -111,14 +111,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
|
||||
// Handle all other objects
|
||||
for curr in hit_objects {
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
aim.save_current_peak();
|
||||
@@ -126,7 +119,7 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
speed.save_current_peak();
|
||||
speed.start_new_section_from(current_section_end);
|
||||
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -173,7 +166,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
return Strains::default();
|
||||
}
|
||||
|
||||
let section_len = SECTION_LEN * map_attributes.clock_rate;
|
||||
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (map_attributes.cs - 5.0) / 5.0) / 2.0;
|
||||
let mut scaling_factor = NORMALIZED_RADIUS / radius;
|
||||
|
||||
@@ -200,27 +192,19 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
let mut aim = Skill::new(SkillKind::Aim);
|
||||
let mut speed = Skill::new(SkillKind::Speed);
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end =
|
||||
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
|
||||
|
||||
let mut prev_prev = None;
|
||||
let mut prev = hit_objects.next().unwrap();
|
||||
let mut prev_vals = None;
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
|
||||
// Handle second object separately to remove later if-branching
|
||||
let curr = hit_objects.next().unwrap();
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -232,14 +216,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
|
||||
// Handle all other objects
|
||||
for curr in hit_objects {
|
||||
let h = DifficultyObject::new(
|
||||
&curr,
|
||||
&prev,
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
map_attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.time > current_section_end {
|
||||
aim.save_current_peak();
|
||||
@@ -247,7 +224,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
speed.save_current_peak();
|
||||
speed.start_new_section_from(current_section_end);
|
||||
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -269,7 +246,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
.collect();
|
||||
|
||||
Strains {
|
||||
section_length: section_len,
|
||||
section_length: SECTION_LEN,
|
||||
strains,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use crate::parse::HitObject;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use super::OsuObject;
|
||||
|
||||
pub(crate) struct DifficultyObject<'h> {
|
||||
pub(crate) base: &'h HitObject,
|
||||
pub(crate) base: &'h OsuObject,
|
||||
pub(crate) prev: Option<(f32, f32)>, // (jump_dist, strain_time)
|
||||
|
||||
pub(crate) jump_dist: f32,
|
||||
@@ -15,17 +13,16 @@ pub(crate) struct DifficultyObject<'h> {
|
||||
|
||||
impl<'h> DifficultyObject<'h> {
|
||||
pub(crate) fn new(
|
||||
base: &'h HitObject,
|
||||
prev: &HitObject,
|
||||
base: &'h OsuObject,
|
||||
prev: &OsuObject,
|
||||
prev_vals: Option<(f32, f32)>, // (jump_dist, strain_time)
|
||||
prev_prev: Option<Cow<HitObject>>,
|
||||
clock_rate: f32,
|
||||
prev_prev: Option<OsuObject>,
|
||||
scaling_factor: f32,
|
||||
) -> Self {
|
||||
let delta = (base.start_time - prev.start_time) / clock_rate;
|
||||
let delta = base.time - prev.time;
|
||||
let strain_time = delta.max(50.0);
|
||||
|
||||
let jump_dist = if base.is_spinner() {
|
||||
let jump_dist = if base.is_spinner {
|
||||
0.0
|
||||
} else {
|
||||
((base.pos - prev.pos) * scaling_factor).length()
|
||||
|
||||
@@ -7,21 +7,18 @@
|
||||
use super::super::DifficultyAttributes;
|
||||
|
||||
mod difficulty_object;
|
||||
mod osu_object;
|
||||
mod skill;
|
||||
mod skill_kind;
|
||||
mod slider_state;
|
||||
|
||||
use difficulty_object::DifficultyObject;
|
||||
use osu_object::OsuObject;
|
||||
use skill::Skill;
|
||||
use skill_kind::SkillKind;
|
||||
use slider_state::SliderState;
|
||||
|
||||
use crate::{
|
||||
parse::{HitObject, HitObjectKind},
|
||||
Beatmap, Mods, StarResult, Strains,
|
||||
};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use crate::{parse::HitObjectKind, Beatmap, Mods, StarResult, Strains};
|
||||
|
||||
const OBJECT_RADIUS: f32 = 64.0;
|
||||
const SECTION_LEN: f32 = 400.0;
|
||||
@@ -50,7 +47,6 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
});
|
||||
}
|
||||
|
||||
let section_len = SECTION_LEN * attributes.clock_rate;
|
||||
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0;
|
||||
let mut scaling_factor = NORMALIZED_RADIUS / radius;
|
||||
|
||||
@@ -59,6 +55,8 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
scaling_factor *= 1.0 + small_circle_bonus;
|
||||
}
|
||||
|
||||
let clock_rate = attributes.clock_rate;
|
||||
|
||||
let mut max_combo = 0;
|
||||
let mut state = SliderState::new(&map);
|
||||
|
||||
@@ -70,24 +68,19 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
HitObjectKind::Circle => {
|
||||
max_combo += 1;
|
||||
|
||||
Some(Cow::Borrowed(h))
|
||||
Some(OsuObject::new(h.pos, h.start_time, false, clock_rate))
|
||||
}
|
||||
HitObjectKind::Slider {
|
||||
pixel_len, repeats, ..
|
||||
} => {
|
||||
max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map);
|
||||
|
||||
Some(Cow::Owned(HitObject {
|
||||
pos: h.pos,
|
||||
start_time: h.start_time,
|
||||
kind: HitObjectKind::Circle,
|
||||
sound: h.sound,
|
||||
}))
|
||||
Some(OsuObject::new(h.pos, h.start_time, false, clock_rate))
|
||||
}
|
||||
HitObjectKind::Spinner { .. } => {
|
||||
max_combo += 1;
|
||||
|
||||
Some(Cow::Borrowed(h))
|
||||
Some(OsuObject::new(h.pos, h.start_time, true, clock_rate))
|
||||
}
|
||||
HitObjectKind::Hold { .. } => None,
|
||||
});
|
||||
@@ -95,27 +88,19 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
let mut aim = Skill::new(SkillKind::Aim);
|
||||
let mut speed = Skill::new(SkillKind::Speed);
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end =
|
||||
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
|
||||
|
||||
let mut prev_prev = None;
|
||||
let mut prev = hit_objects.next().unwrap();
|
||||
let mut prev_vals = None;
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
|
||||
// Handle second object separately to remove later if-branching
|
||||
let curr = hit_objects.next().unwrap();
|
||||
let h = DifficultyObject::new(
|
||||
curr.as_ref(),
|
||||
prev.as_ref(),
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.start_time > current_section_end {
|
||||
current_section_end += section_len;
|
||||
while h.base.time > current_section_end {
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -127,22 +112,15 @@ pub fn stars(map: &Beatmap, mods: impl Mods, passed_objects: Option<usize>) -> S
|
||||
|
||||
// Handle all other objects
|
||||
for curr in hit_objects {
|
||||
let h = DifficultyObject::new(
|
||||
curr.as_ref(),
|
||||
prev.as_ref(),
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.start_time > current_section_end {
|
||||
while h.base.time > current_section_end {
|
||||
aim.save_current_peak();
|
||||
aim.start_new_section_from(current_section_end);
|
||||
speed.save_current_peak();
|
||||
speed.start_new_section_from(current_section_end);
|
||||
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -184,7 +162,6 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
return Strains::default();
|
||||
}
|
||||
|
||||
let section_len = SECTION_LEN * attributes.clock_rate;
|
||||
let radius = OBJECT_RADIUS * (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0;
|
||||
let mut scaling_factor = NORMALIZED_RADIUS / radius;
|
||||
|
||||
@@ -193,42 +170,34 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
scaling_factor *= 1.0 + small_circle_bonus;
|
||||
}
|
||||
|
||||
let clock_rate = attributes.clock_rate;
|
||||
|
||||
let mut hit_objects = map.hit_objects.iter().filter_map(|h| match &h.kind {
|
||||
HitObjectKind::Circle => Some(Cow::Borrowed(h)),
|
||||
HitObjectKind::Slider { .. } => Some(Cow::Owned(HitObject {
|
||||
pos: h.pos,
|
||||
start_time: h.start_time,
|
||||
kind: HitObjectKind::Circle,
|
||||
sound: h.sound,
|
||||
})),
|
||||
HitObjectKind::Spinner { .. } => Some(Cow::Borrowed(h)),
|
||||
HitObjectKind::Circle | HitObjectKind::Slider { .. } => {
|
||||
Some(OsuObject::new(h.pos, h.start_time, false, clock_rate))
|
||||
}
|
||||
HitObjectKind::Spinner { .. } => {
|
||||
Some(OsuObject::new(h.pos, h.start_time, true, clock_rate))
|
||||
}
|
||||
HitObjectKind::Hold { .. } => None,
|
||||
});
|
||||
|
||||
let mut aim = Skill::new(SkillKind::Aim);
|
||||
let mut speed = Skill::new(SkillKind::Speed);
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end =
|
||||
(map.hit_objects[0].start_time / section_len).ceil() * section_len;
|
||||
|
||||
let mut prev_prev = None;
|
||||
let mut prev = hit_objects.next().unwrap();
|
||||
let mut prev_vals = None;
|
||||
|
||||
// First object has no predecessor and thus no strain, handle distinctly
|
||||
let mut current_section_end = (prev.time / SECTION_LEN).ceil() * SECTION_LEN;
|
||||
|
||||
// Handle second object separately to remove later if-branching
|
||||
let curr = hit_objects.next().unwrap();
|
||||
let h = DifficultyObject::new(
|
||||
curr.as_ref(),
|
||||
prev.as_ref(),
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.start_time > current_section_end {
|
||||
current_section_end += section_len;
|
||||
while h.base.time > current_section_end {
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -240,22 +209,15 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
|
||||
// Handle all other objects
|
||||
for curr in hit_objects {
|
||||
let h = DifficultyObject::new(
|
||||
curr.as_ref(),
|
||||
prev.as_ref(),
|
||||
prev_vals,
|
||||
prev_prev,
|
||||
attributes.clock_rate,
|
||||
scaling_factor,
|
||||
);
|
||||
let h = DifficultyObject::new(&curr, &prev, prev_vals, prev_prev, scaling_factor);
|
||||
|
||||
while h.base.start_time > current_section_end {
|
||||
while h.base.time > current_section_end {
|
||||
aim.save_current_peak();
|
||||
aim.start_new_section_from(current_section_end);
|
||||
speed.save_current_peak();
|
||||
speed.start_new_section_from(current_section_end);
|
||||
|
||||
current_section_end += section_len;
|
||||
current_section_end += SECTION_LEN;
|
||||
}
|
||||
|
||||
aim.process(&h);
|
||||
@@ -277,7 +239,7 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
||||
.collect();
|
||||
|
||||
Strains {
|
||||
section_length: section_len,
|
||||
section_length: SECTION_LEN,
|
||||
strains,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
use crate::parse::Pos2;
|
||||
|
||||
pub(crate) struct OsuObject {
|
||||
pub(crate) pos: Pos2,
|
||||
pub(crate) time: f32,
|
||||
pub(crate) is_spinner: bool,
|
||||
}
|
||||
|
||||
impl OsuObject {
|
||||
#[inline]
|
||||
pub(crate) fn new(pos: Pos2, time: f32, is_spinner: bool, clock_rate: f32) -> Self {
|
||||
Self {
|
||||
pos,
|
||||
time: time / clock_rate,
|
||||
is_spinner,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ impl Skill {
|
||||
self.current_strain *= self.strain_decay(current.delta);
|
||||
self.current_strain += self.kind.strain_value_of(¤t) * self.skill_multiplier();
|
||||
self.current_section_peak = self.current_section_peak.max(self.current_strain);
|
||||
self.prev_time.replace(current.base.start_time);
|
||||
self.prev_time.replace(current.base.time);
|
||||
}
|
||||
|
||||
pub(crate) fn difficulty_value(&mut self) -> f32 {
|
||||
|
||||
@@ -28,7 +28,7 @@ impl SkillKind {
|
||||
pub(crate) fn strain_value_of(self, current: &DifficultyObject) -> f32 {
|
||||
match self {
|
||||
Self::Aim => {
|
||||
if current.base.is_spinner() {
|
||||
if current.base.is_spinner {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ impl SkillKind {
|
||||
.max(jump_dist_exp / current.strain_time)
|
||||
}
|
||||
Self::Speed => {
|
||||
if current.base.is_spinner() {
|
||||
if current.base.is_spinner {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
+36
-36
@@ -128,111 +128,111 @@ const RESULTS: &[MapResult] = &[
|
||||
MapResult {
|
||||
map_id: 1851299,
|
||||
mods: 256,
|
||||
stars: 4.23514130038547,
|
||||
pp: 96.59532540603362,
|
||||
stars: 4.19951953364192,
|
||||
pp: 95.35544846090738,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1851299,
|
||||
mods: 0,
|
||||
stars: 5.356786475158158,
|
||||
pp: 191.72186087468594,
|
||||
stars: 5.305946555352317,
|
||||
pp: 188.7611225698759,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1851299,
|
||||
mods: 8,
|
||||
stars: 5.356786475158158,
|
||||
pp: 211.17333597663404,
|
||||
stars: 5.305946555352317,
|
||||
pp: 207.87782991080368,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1851299,
|
||||
mods: 64,
|
||||
stars: 7.450616908751305,
|
||||
pp: 476.39199443787675,
|
||||
stars: 7.352573837272898,
|
||||
pp: 465.60165096277717,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1851299,
|
||||
mods: 16,
|
||||
stars: 5.6834681957637665,
|
||||
pp: 243.32730989490153,
|
||||
stars: 5.628029058321052,
|
||||
pp: 239.33966091681467,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1851299,
|
||||
mods: 2,
|
||||
stars: 4.937817303399699,
|
||||
pp: 110.44350158714633,
|
||||
stars: 4.892665488817249,
|
||||
pp: 108.66545494037493,
|
||||
},
|
||||
// -----
|
||||
MapResult {
|
||||
map_id: 70090,
|
||||
mods: 256,
|
||||
stars: 2.2929922580201803,
|
||||
pp: 17.530571082228978,
|
||||
stars: 2.2531214736733975,
|
||||
pp: 17.064864347414005,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 70090,
|
||||
mods: 0,
|
||||
stars: 2.8322940761833983,
|
||||
pp: 40.611760049886534,
|
||||
stars: 2.7853401027561353,
|
||||
pp: 39.80360462535964,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 70090,
|
||||
mods: 8,
|
||||
stars: 2.8322940761833983,
|
||||
pp: 46.252172598153074,
|
||||
stars: 2.7853401027561353,
|
||||
pp: 45.27724541951056,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 70090,
|
||||
mods: 64,
|
||||
stars: 3.8338563325375485,
|
||||
pp: 110.32389105793393,
|
||||
stars: 3.7775299223395877,
|
||||
pp: 108.27132697867293,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 70090,
|
||||
mods: 16,
|
||||
stars: 3.0617492228478174,
|
||||
pp: 84.67846960014381,
|
||||
stars: 3.0128373294988626,
|
||||
pp: 83.70428900396428,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 70090,
|
||||
mods: 2,
|
||||
stars: 2.698823231324141,
|
||||
pp: 21.861918597227252,
|
||||
stars: 2.673167484837261,
|
||||
pp: 21.254867316318986,
|
||||
},
|
||||
// -----
|
||||
MapResult {
|
||||
map_id: 1241370,
|
||||
mods: 256,
|
||||
stars: 5.662809600985943,
|
||||
pp: 346.1069865511771,
|
||||
stars: 5.558026586611704,
|
||||
pp: 334.51647189180727,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1241370,
|
||||
mods: 0,
|
||||
stars: 7.0367002127481975,
|
||||
pp: 658.4944314112954,
|
||||
stars: 6.983848076867755,
|
||||
pp: 649.1653315906666,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1241370,
|
||||
mods: 8,
|
||||
stars: 7.0367002127481975,
|
||||
pp: 720.8614284211293,
|
||||
stars: 6.983848076867755,
|
||||
pp: 710.594432790455,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1241370,
|
||||
mods: 64,
|
||||
stars: 11.144720506574934,
|
||||
pp: 2414.665180655108,
|
||||
stars: 11.076248857241552,
|
||||
pp: 2378.593642686663,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1241370,
|
||||
mods: 16,
|
||||
stars: 7.641688110458715,
|
||||
pp: 853.7411405318841,
|
||||
stars: 7.616427878599069,
|
||||
pp: 843.421001465897,
|
||||
},
|
||||
MapResult {
|
||||
map_id: 1241370,
|
||||
mods: 2,
|
||||
stars: 6.316288616688052,
|
||||
pp: 357.3089261481221,
|
||||
stars: 6.289772601786212,
|
||||
pp: 354.960619132034,
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user