wip osu pp update
This commit is contained in:
+24
-26
@@ -6,7 +6,7 @@ use crate::{
|
||||
Beatmap,
|
||||
};
|
||||
|
||||
use super::{catch_object::CatchObject, slider_state::SliderState, CatchDifficultyAttributes};
|
||||
use super::{catch_object::CatchObject, CatchDifficultyAttributes};
|
||||
|
||||
const LEGACY_LAST_TICK_OFFSET: f64 = 36.0;
|
||||
const BASE_SCORING_DISTANCE: f64 = 100.0;
|
||||
@@ -18,7 +18,6 @@ pub(crate) struct FruitParams<'a> {
|
||||
pub(crate) last_pos: Option<f32>,
|
||||
pub(crate) last_time: f64,
|
||||
pub(crate) map: &'a Beatmap,
|
||||
pub(crate) slider_state: SliderState<'a>,
|
||||
pub(crate) ticks: Vec<(Pos2, f64)>,
|
||||
pub(crate) with_hr: bool,
|
||||
}
|
||||
@@ -55,31 +54,29 @@ impl FruitOrJuice {
|
||||
params.last_pos = Some(h.pos.x + control_points[control_points.len() - 1].pos.x);
|
||||
params.last_time = h.start_time;
|
||||
|
||||
// Responsible for timing point values
|
||||
params.slider_state.update(h.start_time);
|
||||
let timing_point = params.map.timing_point_at(h.start_time);
|
||||
|
||||
let difficulty_point = params
|
||||
.map
|
||||
.difficulty_point_at(h.start_time)
|
||||
.unwrap_or_default();
|
||||
|
||||
let vel_factor =
|
||||
BASE_SCORING_DISTANCE * params.map.slider_mult / timing_point.beat_len;
|
||||
let tick_dist_factor =
|
||||
BASE_SCORING_DISTANCE * params.map.slider_mult / params.map.tick_rate;
|
||||
|
||||
let vel = vel_factor * difficulty_point.slider_vel;
|
||||
|
||||
let mut tick_dist = tick_dist_factor * difficulty_point.slider_vel;
|
||||
|
||||
let span_count = (*repeats + 1) as f64;
|
||||
|
||||
let mut tick_dist = 100.0 * params.map.slider_mult / params.map.tick_rate;
|
||||
|
||||
if params.map.version >= 8 {
|
||||
tick_dist /= (100.0 / params.slider_state.slider_velocity)
|
||||
.max(10.0)
|
||||
.min(1000.0)
|
||||
/ 100.0;
|
||||
}
|
||||
|
||||
// Build the curve w.r.t. the control points
|
||||
let curve = Curve::new(control_points, *pixel_len, &mut params.curve_bufs);
|
||||
|
||||
let velocity = (BASE_SCORING_DISTANCE
|
||||
* params.map.slider_mult
|
||||
* params.slider_state.slider_velocity)
|
||||
/ params.slider_state.beat_len;
|
||||
|
||||
let end_time = h.start_time + span_count * curve.dist() / velocity;
|
||||
let duration = end_time - h.start_time;
|
||||
let span_duration = duration / span_count;
|
||||
let total_duration = span_count * curve.dist() / vel;
|
||||
let span_duration = total_duration / span_count;
|
||||
|
||||
// * A very lenient maximum length of a slider for ticks to be generated.
|
||||
// * This exists for edge cases such as /b/1573664 where the beatmap has
|
||||
@@ -88,12 +85,13 @@ impl FruitOrJuice {
|
||||
|
||||
let len = curve.dist().min(max_len);
|
||||
tick_dist = tick_dist.clamp(0.0, len);
|
||||
let min_dist_from_end = velocity * 10.0;
|
||||
let min_dist_from_end = vel * 10.0;
|
||||
|
||||
let mut curr_dist = tick_dist;
|
||||
let time_add = duration * tick_dist / (*pixel_len * span_count);
|
||||
let pixel_len = pixel_len.unwrap_or(0.0);
|
||||
let time_add = total_duration * tick_dist / (pixel_len * span_count);
|
||||
|
||||
let target = *pixel_len - tick_dist / 8.0;
|
||||
let target = pixel_len - tick_dist / 8.0;
|
||||
|
||||
let mut slider_objects = vec![(h.pos, h.start_time)];
|
||||
|
||||
@@ -112,7 +110,7 @@ impl FruitOrJuice {
|
||||
params.attributes.n_tiny_droplets += tiny_droplet_count(
|
||||
h.start_time,
|
||||
time_add,
|
||||
duration,
|
||||
total_duration,
|
||||
span_count as usize,
|
||||
¶ms.ticks,
|
||||
);
|
||||
@@ -160,7 +158,7 @@ impl FruitOrJuice {
|
||||
// Slider tail
|
||||
let progress = (*repeats % 2 == 0) as u8 as f64;
|
||||
let pos = h.pos + curve.position_at(progress);
|
||||
slider_objects.push((pos, h.start_time + duration));
|
||||
slider_objects.push((pos, h.start_time + total_duration));
|
||||
|
||||
let new_fruits = 2 + (tick_dist > 0.0) as usize * *repeats;
|
||||
params.attributes.n_fruits += new_fruits;
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
use std::{iter, slice::Iter};
|
||||
|
||||
use crate::{
|
||||
catch::{
|
||||
difficulty_object::DifficultyObject, slider_state::SliderState, SECTION_LENGTH,
|
||||
STAR_SCALING_FACTOR,
|
||||
},
|
||||
catch::{difficulty_object::DifficultyObject, SECTION_LENGTH, STAR_SCALING_FACTOR},
|
||||
curve::CurveBuffers,
|
||||
parse::{HitObject, Pos2},
|
||||
Beatmap, Mods,
|
||||
@@ -179,7 +176,6 @@ impl<'map> CatchObjectIter<'map> {
|
||||
last_pos: None,
|
||||
last_time: 0.0,
|
||||
map,
|
||||
slider_state: SliderState::new(map),
|
||||
ticks: Vec::new(),
|
||||
with_hr: mods.hr(),
|
||||
};
|
||||
|
||||
@@ -5,7 +5,6 @@ mod gradual_difficulty;
|
||||
mod gradual_performance;
|
||||
mod movement;
|
||||
mod pp;
|
||||
mod slider_state;
|
||||
|
||||
use catch_object::CatchObject;
|
||||
use difficulty_object::DifficultyObject;
|
||||
@@ -14,7 +13,6 @@ pub use gradual_difficulty::*;
|
||||
pub use gradual_performance::*;
|
||||
use movement::Movement;
|
||||
pub use pp::*;
|
||||
use slider_state::SliderState;
|
||||
|
||||
use crate::{catch::fruit_or_juice::FruitParams, curve::CurveBuffers, Beatmap, Mods, OsuStars};
|
||||
|
||||
@@ -161,7 +159,6 @@ fn calculate_movement(params: CatchStars<'_>) -> (Movement, CatchDifficultyAttri
|
||||
last_pos: None,
|
||||
last_time: 0.0,
|
||||
map,
|
||||
slider_state: SliderState::new(map),
|
||||
ticks: Vec::new(), // using the same buffer for all sliders
|
||||
with_hr: mods.hr(),
|
||||
};
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
use crate::beatmap::{Beatmap, ControlPoint, ControlPointIter};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SliderState<'p> {
|
||||
control_points: ControlPointIter<'p>,
|
||||
next: Option<ControlPoint>,
|
||||
pub(crate) beat_len: f64,
|
||||
pub(crate) slider_velocity: f64,
|
||||
}
|
||||
|
||||
impl<'p> SliderState<'p> {
|
||||
#[inline]
|
||||
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
||||
let mut control_points = ControlPointIter::new(map);
|
||||
|
||||
let (beat_len, slider_velocity) = match control_points.next() {
|
||||
Some(ControlPoint::Timing(point)) => (point.beat_len, 1.0),
|
||||
Some(ControlPoint::Difficulty(point)) => (1000.0, point.speed_multiplier),
|
||||
None => (1000.0, 1.0),
|
||||
};
|
||||
|
||||
Self {
|
||||
next: control_points.next(),
|
||||
control_points,
|
||||
beat_len,
|
||||
slider_velocity,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn update(&mut self, time: f64) {
|
||||
while let Some(next) = self.next.as_ref().filter(|n| time >= n.time()) {
|
||||
match next {
|
||||
ControlPoint::Timing(point) => {
|
||||
self.beat_len = point.beat_len;
|
||||
self.slider_velocity = 1.0;
|
||||
}
|
||||
ControlPoint::Difficulty(point) => self.slider_velocity = point.speed_multiplier,
|
||||
}
|
||||
|
||||
self.next = self.control_points.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::beatmap::{Beatmap, DifficultyPoint, TimingPoint};
|
||||
|
||||
use super::SliderState;
|
||||
|
||||
#[test]
|
||||
fn catch_slider_state() {
|
||||
let map = Beatmap {
|
||||
timing_points: vec![
|
||||
TimingPoint {
|
||||
time: 1.0,
|
||||
beat_len: 10.0,
|
||||
kiai: false,
|
||||
},
|
||||
TimingPoint {
|
||||
time: 3.0,
|
||||
beat_len: 20.0,
|
||||
kiai: false,
|
||||
},
|
||||
TimingPoint {
|
||||
time: 4.0,
|
||||
beat_len: 30.0,
|
||||
kiai: false,
|
||||
},
|
||||
],
|
||||
difficulty_points: vec![
|
||||
DifficultyPoint {
|
||||
time: 2.0,
|
||||
speed_multiplier: 15.0,
|
||||
kiai: false,
|
||||
},
|
||||
DifficultyPoint {
|
||||
time: 5.0,
|
||||
speed_multiplier: 45.0,
|
||||
kiai: false,
|
||||
},
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut state = SliderState::new(&map);
|
||||
|
||||
state.update(2.0);
|
||||
assert!((state.beat_len - 10.0).abs() <= f64::EPSILON);
|
||||
|
||||
state.update(3.0);
|
||||
assert!((state.beat_len - 20.0).abs() <= f64::EPSILON);
|
||||
assert!((state.slider_velocity - 1.0).abs() <= f64::EPSILON);
|
||||
|
||||
state.update(5.0);
|
||||
assert!((state.beat_len - 30.0).abs() <= f64::EPSILON);
|
||||
assert!((state.slider_velocity - 45.0).abs() <= f64::EPSILON);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user