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