applied recent changes to all mode feature combinations
This commit is contained in:
@@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
|
use self::osu_object::ObjectParameters;
|
||||||
|
|
||||||
use super::super::DifficultyAttributes;
|
use super::super::DifficultyAttributes;
|
||||||
use crate::parse::Pos2;
|
use crate::{curve::CurveBuffers, parse::Pos2};
|
||||||
|
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod osu_object;
|
mod osu_object;
|
||||||
@@ -79,21 +81,21 @@ pub fn stars(
|
|||||||
scaling_factor *= 1.0 + small_circle_bonus;
|
scaling_factor *= 1.0 + small_circle_bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut slider_state = SliderState::new(map);
|
let mut params = ObjectParameters {
|
||||||
let mut ticks_buf = Vec::new();
|
|
||||||
|
|
||||||
let hit_objects_iter = map.hit_objects.iter().take(take).filter_map(|h| {
|
|
||||||
OsuObject::new(
|
|
||||||
h,
|
|
||||||
map,
|
map,
|
||||||
radius,
|
radius,
|
||||||
scaling_factor,
|
scaling_factor,
|
||||||
hr,
|
attributes: &mut diff_attributes,
|
||||||
&mut ticks_buf,
|
slider_state: SliderState::new(map),
|
||||||
&mut diff_attributes,
|
ticks: Vec::new(),
|
||||||
&mut slider_state,
|
curve_bufs: CurveBuffers::default(),
|
||||||
)
|
};
|
||||||
});
|
|
||||||
|
let hit_objects_iter = map
|
||||||
|
.hit_objects
|
||||||
|
.iter()
|
||||||
|
.take(take)
|
||||||
|
.filter_map(|h| OsuObject::new(h, hr, &mut params));
|
||||||
|
|
||||||
let mut hit_objects = Vec::with_capacity(take);
|
let mut hit_objects = Vec::with_capacity(take);
|
||||||
hit_objects.extend(hit_objects_iter);
|
hit_objects.extend(hit_objects_iter);
|
||||||
@@ -277,21 +279,20 @@ pub fn strains(map: &Beatmap, mods: impl Mods) -> Strains {
|
|||||||
scaling_factor *= 1.0 + small_circle_bonus;
|
scaling_factor *= 1.0 + small_circle_bonus;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut slider_state = SliderState::new(map);
|
let mut params = ObjectParameters {
|
||||||
let mut ticks_buf = Vec::new();
|
|
||||||
|
|
||||||
let hit_objects_iter = map.hit_objects.iter().filter_map(|h| {
|
|
||||||
OsuObject::new(
|
|
||||||
h,
|
|
||||||
map,
|
map,
|
||||||
radius,
|
radius,
|
||||||
scaling_factor,
|
scaling_factor,
|
||||||
hr,
|
attributes: &mut diff_attributes,
|
||||||
&mut ticks_buf,
|
slider_state: SliderState::new(map),
|
||||||
&mut diff_attributes,
|
ticks: Vec::new(),
|
||||||
&mut slider_state,
|
curve_bufs: CurveBuffers::default(),
|
||||||
)
|
};
|
||||||
});
|
|
||||||
|
let hit_objects_iter = map
|
||||||
|
.hit_objects
|
||||||
|
.iter()
|
||||||
|
.filter_map(|h| OsuObject::new(h, hr, &mut params));
|
||||||
|
|
||||||
let mut hit_objects = Vec::with_capacity(map.hit_objects.len());
|
let mut hit_objects = Vec::with_capacity(map.hit_objects.len());
|
||||||
hit_objects.extend(hit_objects_iter);
|
hit_objects.extend(hit_objects_iter);
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ use super::super::super::DifficultyAttributes;
|
|||||||
use super::slider_state::SliderState;
|
use super::slider_state::SliderState;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
curve::Curve,
|
curve::{Curve, CurveBuffers},
|
||||||
parse::{HitObject, HitObjectKind, Pos2},
|
parse::{HitObject, HitObjectKind, Pos2},
|
||||||
Beatmap,
|
Beatmap,
|
||||||
};
|
};
|
||||||
|
|
||||||
const LEGACY_LAST_TICK_OFFSET: f32 = 36.0;
|
const LEGACY_LAST_TICK_OFFSET: f32 = 36.0;
|
||||||
|
const BASE_SCORING_DISTANCE: f32 = 100.0;
|
||||||
|
|
||||||
pub(crate) struct OsuObject {
|
pub(crate) struct OsuObject {
|
||||||
pub(crate) time: f32,
|
pub(crate) time: f32,
|
||||||
@@ -29,18 +30,29 @@ enum OsuObjectKind {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) struct ObjectParameters<'a> {
|
||||||
|
pub(crate) map: &'a Beatmap,
|
||||||
|
pub(crate) radius: f32,
|
||||||
|
pub(crate) scaling_factor: f32,
|
||||||
|
pub(crate) attributes: &'a mut DifficultyAttributes,
|
||||||
|
pub(crate) ticks: Vec<f32>,
|
||||||
|
pub(crate) slider_state: SliderState<'a>,
|
||||||
|
pub(crate) curve_bufs: CurveBuffers,
|
||||||
|
}
|
||||||
|
|
||||||
impl OsuObject {
|
impl OsuObject {
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(h: &HitObject, hr: bool, params: &mut ObjectParameters) -> Option<Self> {
|
||||||
h: &HitObject,
|
let ObjectParameters {
|
||||||
map: &Beatmap,
|
map,
|
||||||
radius: f32,
|
radius,
|
||||||
scaling_factor: f32,
|
scaling_factor,
|
||||||
hr: bool,
|
attributes,
|
||||||
ticks: &mut Vec<f32>,
|
ticks,
|
||||||
attributes: &mut DifficultyAttributes,
|
slider_state,
|
||||||
slider_state: &mut SliderState,
|
curve_bufs,
|
||||||
) -> Option<Self> {
|
} = params;
|
||||||
|
|
||||||
attributes.max_combo += 1; // hitcircle, slider head, or spinner
|
attributes.max_combo += 1; // hitcircle, slider head, or spinner
|
||||||
let mut pos = h.pos;
|
let mut pos = h.pos;
|
||||||
|
|
||||||
@@ -58,8 +70,7 @@ impl OsuObject {
|
|||||||
HitObjectKind::Slider {
|
HitObjectKind::Slider {
|
||||||
pixel_len,
|
pixel_len,
|
||||||
repeats,
|
repeats,
|
||||||
curve_points,
|
control_points,
|
||||||
path_type,
|
|
||||||
} => {
|
} => {
|
||||||
// Key values which are computed here
|
// Key values which are computed here
|
||||||
let mut lazy_end_pos = pos;
|
let mut lazy_end_pos = pos;
|
||||||
@@ -68,21 +79,26 @@ impl OsuObject {
|
|||||||
// Responsible for timing point values
|
// Responsible for timing point values
|
||||||
slider_state.update(h.start_time);
|
slider_state.update(h.start_time);
|
||||||
|
|
||||||
let approx_follow_circle_radius = radius * 3.0;
|
let span_count = (*repeats + 1) as f32;
|
||||||
let mut tick_distance = 100.0 * map.sv / map.tick_rate;
|
|
||||||
|
let approx_follow_circle_radius = *radius * 3.0;
|
||||||
|
let mut tick_dist = 100.0 * map.slider_mult / map.tick_rate;
|
||||||
|
|
||||||
if map.version >= 8 {
|
if map.version >= 8 {
|
||||||
tick_distance /=
|
tick_dist /=
|
||||||
(100.0 / slider_state.speed_mult).max(10.0).min(1000.0) / 100.0;
|
(100.0 / slider_state.slider_velocity).max(10.0).min(1000.0) / 100.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration = *repeats as f32 * slider_state.beat_len * pixel_len
|
|
||||||
/ (map.sv * slider_state.speed_mult)
|
|
||||||
/ 100.0;
|
|
||||||
let span_duration = duration / *repeats as f32;
|
|
||||||
|
|
||||||
// Build the curve w.r.t. the curve points
|
// Build the curve w.r.t. the curve points
|
||||||
let curve = Curve::new(curve_points, *path_type);
|
let curve = Curve::new(control_points, *pixel_len, curve_bufs);
|
||||||
|
|
||||||
|
let velocity =
|
||||||
|
(BASE_SCORING_DISTANCE * map.slider_mult * slider_state.slider_velocity)
|
||||||
|
/ 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;
|
||||||
|
|
||||||
// Called on each slider object except for the head.
|
// Called on each slider object except for the head.
|
||||||
// Increases combo and adjusts `end_pos` and `travel_dist`
|
// Increases combo and adjusts `end_pos` and `travel_dist`
|
||||||
@@ -98,8 +114,8 @@ impl OsuObject {
|
|||||||
progress %= 1.0;
|
progress %= 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let curr_dist = pixel_len * progress;
|
// TODO: Correct addition?
|
||||||
let mut curr_pos = curve.point_at_distance(curr_dist);
|
let mut curr_pos = h.pos + curve.position_at(progress);
|
||||||
|
|
||||||
if hr {
|
if hr {
|
||||||
curr_pos.y = 384.0 - curr_pos.y;
|
curr_pos.y = 384.0 - curr_pos.y;
|
||||||
@@ -109,17 +125,18 @@ impl OsuObject {
|
|||||||
let mut dist = diff.length();
|
let mut dist = diff.length();
|
||||||
|
|
||||||
if dist > approx_follow_circle_radius {
|
if dist > approx_follow_circle_radius {
|
||||||
|
// * The cursor would be outside the follow circle, we need to move it
|
||||||
dist -= approx_follow_circle_radius;
|
dist -= approx_follow_circle_radius;
|
||||||
lazy_end_pos += diff.normalize() * dist;
|
lazy_end_pos += diff.normalize() * dist;
|
||||||
travel_dist += dist;
|
travel_dist += dist;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut current_distance = tick_distance;
|
let mut current_distance = tick_dist;
|
||||||
let time_add = duration * (tick_distance / (pixel_len * *repeats as f32));
|
let time_add = duration * (tick_dist / (pixel_len * span_count));
|
||||||
|
|
||||||
let target = pixel_len - tick_distance / 8.0;
|
let target = pixel_len - tick_dist / 8.0;
|
||||||
ticks.reserve((target / tick_distance) as usize);
|
ticks.reserve((target / tick_dist) as usize);
|
||||||
|
|
||||||
// Tick of the first span
|
// Tick of the first span
|
||||||
if current_distance < target {
|
if current_distance < target {
|
||||||
@@ -127,7 +144,7 @@ impl OsuObject {
|
|||||||
let time = h.start_time + time_add * tick_idx as f32;
|
let time = h.start_time + time_add * tick_idx as f32;
|
||||||
compute_vertex(time);
|
compute_vertex(time);
|
||||||
ticks.push(time);
|
ticks.push(time);
|
||||||
current_distance += tick_distance;
|
current_distance += tick_dist;
|
||||||
|
|
||||||
if current_distance >= target {
|
if current_distance >= target {
|
||||||
break;
|
break;
|
||||||
@@ -161,9 +178,10 @@ impl OsuObject {
|
|||||||
|
|
||||||
ticks.clear();
|
ticks.clear();
|
||||||
|
|
||||||
travel_dist *= scaling_factor;
|
// TODO: what if reversing odd amount?
|
||||||
|
// TODO: Correct addition?
|
||||||
let mut end_pos = curve.point_at_distance(*pixel_len);
|
let mut end_pos = h.pos + curve.position_at(1.0);
|
||||||
|
travel_dist *= *scaling_factor;
|
||||||
|
|
||||||
if hr {
|
if hr {
|
||||||
end_pos.y = 384.0 - end_pos.y;
|
end_pos.y = 384.0 - end_pos.y;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ pub(crate) struct SliderState<'p> {
|
|||||||
control_points: ControlPointIter<'p>,
|
control_points: ControlPointIter<'p>,
|
||||||
next: Option<ControlPoint>,
|
next: Option<ControlPoint>,
|
||||||
pub(crate) beat_len: f32,
|
pub(crate) beat_len: f32,
|
||||||
pub(crate) speed_mult: f32,
|
pub(crate) slider_velocity: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'p> SliderState<'p> {
|
impl<'p> SliderState<'p> {
|
||||||
@@ -12,9 +12,11 @@ impl<'p> SliderState<'p> {
|
|||||||
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
||||||
let mut control_points = ControlPointIter::new(map);
|
let mut control_points = ControlPointIter::new(map);
|
||||||
|
|
||||||
let (beat_len, speed_mult) = match control_points.next() {
|
let (beat_len, slider_velocity) = match control_points.next() {
|
||||||
Some(ControlPoint::Timing { beat_len, .. }) => (beat_len, 1.0),
|
Some(ControlPoint::Timing { beat_len, .. }) => (beat_len, 1.0),
|
||||||
Some(ControlPoint::Difficulty { speed_mult, .. }) => (1000.0, speed_mult),
|
Some(ControlPoint::Difficulty {
|
||||||
|
slider_velocity, ..
|
||||||
|
}) => (1000.0, slider_velocity),
|
||||||
None => (1000.0, 1.0),
|
None => (1000.0, 1.0),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,7 +24,7 @@ impl<'p> SliderState<'p> {
|
|||||||
next: control_points.next(),
|
next: control_points.next(),
|
||||||
control_points,
|
control_points,
|
||||||
beat_len,
|
beat_len,
|
||||||
speed_mult,
|
slider_velocity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,9 +34,11 @@ impl<'p> SliderState<'p> {
|
|||||||
match next {
|
match next {
|
||||||
ControlPoint::Timing { beat_len, .. } => {
|
ControlPoint::Timing { beat_len, .. } => {
|
||||||
self.beat_len = *beat_len;
|
self.beat_len = *beat_len;
|
||||||
self.speed_mult = 1.0;
|
self.slider_velocity = 1.0;
|
||||||
}
|
}
|
||||||
ControlPoint::Difficulty { speed_mult, .. } => self.speed_mult = *speed_mult,
|
ControlPoint::Difficulty {
|
||||||
|
slider_velocity, ..
|
||||||
|
} => self.slider_velocity = *slider_velocity,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.next = self.control_points.next();
|
self.next = self.control_points.next();
|
||||||
@@ -88,10 +92,10 @@ mod test {
|
|||||||
|
|
||||||
state.update(3.0);
|
state.update(3.0);
|
||||||
assert_eq!(state.beat_len, 20.0);
|
assert_eq!(state.beat_len, 20.0);
|
||||||
assert_eq!(state.speed_mult, 1.0);
|
assert_eq!(state.slider_velocity, 1.0);
|
||||||
|
|
||||||
state.update(5.0);
|
state.update(5.0);
|
||||||
assert_eq!(state.beat_len, 30.0);
|
assert_eq!(state.beat_len, 30.0);
|
||||||
assert_eq!(state.speed_mult, 45.0);
|
assert_eq!(state.slider_velocity, 45.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -161,7 +161,9 @@ impl OsuObject {
|
|||||||
|
|
||||||
ticks.clear();
|
ticks.clear();
|
||||||
|
|
||||||
let end_pos = curve.position_at(1.0); // TODO: what if reversing odd amount?
|
// TODO: what if reversing odd amount?
|
||||||
|
// TODO: + h.pos?
|
||||||
|
let end_pos = curve.position_at(1.0);
|
||||||
travel_dist *= *scaling_factor;
|
travel_dist *= *scaling_factor;
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::f32::NEG_INFINITY;
|
||||||
|
|
||||||
use crate::{Beatmap, ControlPoint, ControlPointIter};
|
use crate::{Beatmap, ControlPoint, ControlPointIter};
|
||||||
|
|
||||||
pub(crate) struct SliderState<'p> {
|
pub(crate) struct SliderState<'p> {
|
||||||
@@ -12,7 +14,7 @@ impl<'p> SliderState<'p> {
|
|||||||
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
pub(crate) fn new(map: &'p Beatmap) -> Self {
|
||||||
Self {
|
Self {
|
||||||
control_points: ControlPointIter::new(map),
|
control_points: ControlPointIter::new(map),
|
||||||
next_time: std::f32::NEG_INFINITY,
|
next_time: NEG_INFINITY,
|
||||||
px_per_beat: 1.0,
|
px_per_beat: 1.0,
|
||||||
prev_sv: 1.0,
|
prev_sv: 1.0,
|
||||||
}
|
}
|
||||||
@@ -26,16 +28,19 @@ impl<'p> SliderState<'p> {
|
|||||||
map: &Beatmap,
|
map: &Beatmap,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
while time >= self.next_time {
|
while time >= self.next_time {
|
||||||
self.px_per_beat = map.sv * 100.0 * self.prev_sv;
|
self.px_per_beat = map.slider_mult * 100.0 * self.prev_sv;
|
||||||
|
|
||||||
match self.control_points.next() {
|
match self.control_points.next() {
|
||||||
Some(ControlPoint::Timing { time, .. }) => {
|
Some(ControlPoint::Timing { time, .. }) => {
|
||||||
self.next_time = time;
|
self.next_time = time;
|
||||||
self.prev_sv = 1.0;
|
self.prev_sv = 1.0;
|
||||||
}
|
}
|
||||||
Some(ControlPoint::Difficulty { time, speed_mult }) => {
|
Some(ControlPoint::Difficulty {
|
||||||
|
time,
|
||||||
|
slider_velocity,
|
||||||
|
}) => {
|
||||||
self.next_time = time;
|
self.next_time = time;
|
||||||
self.prev_sv = speed_mult;
|
self.prev_sv = slider_velocity;
|
||||||
}
|
}
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
use super::{PathControlPoint, Pos2};
|
use super::Pos2;
|
||||||
|
|
||||||
#[cfg(any(
|
#[cfg(any(
|
||||||
feature = "fruits",
|
feature = "fruits",
|
||||||
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
||||||
))]
|
))]
|
||||||
|
use super::PathControlPoint;
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
/// "Intermediate" hitobject created through parsing.
|
/// "Intermediate" hitobject created through parsing.
|
||||||
|
|||||||
+59
-39
@@ -1,5 +1,3 @@
|
|||||||
use crate::math_util::is_linear;
|
|
||||||
|
|
||||||
mod attributes;
|
mod attributes;
|
||||||
mod control_point;
|
mod control_point;
|
||||||
mod error;
|
mod error;
|
||||||
@@ -16,7 +14,7 @@ pub use hitsound::HitSound;
|
|||||||
pub use pos2::Pos2;
|
pub use pos2::Pos2;
|
||||||
use sort::legacy_sort;
|
use sort::legacy_sort;
|
||||||
|
|
||||||
use std::{cmp::Ordering, mem};
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
#[cfg(not(any(feature = "async_std", feature = "async_tokio")))]
|
#[cfg(not(any(feature = "async_std", feature = "async_tokio")))]
|
||||||
use std::io::{BufRead, BufReader, Read};
|
use std::io::{BufRead, BufReader, Read};
|
||||||
@@ -27,6 +25,12 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, BufReader};
|
|||||||
#[cfg(feature = "async_std")]
|
#[cfg(feature = "async_std")]
|
||||||
use async_std::io::{prelude::BufReadExt, BufReader as AsyncBufReader, Read as AsyncRead};
|
use async_std::io::{prelude::BufReadExt, BufReader as AsyncBufReader, Read as AsyncRead};
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "fruits",
|
||||||
|
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
||||||
|
))]
|
||||||
|
pub use osu_fruits::*;
|
||||||
|
|
||||||
fn sort_unstable<T: PartialOrd>(slice: &mut [T]) {
|
fn sort_unstable<T: PartialOrd>(slice: &mut [T]) {
|
||||||
slice.sort_unstable_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal));
|
slice.sort_unstable_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal));
|
||||||
}
|
}
|
||||||
@@ -722,12 +726,6 @@ pub struct Beatmap {
|
|||||||
|
|
||||||
pub(crate) const OSU_FILE_HEADER: &str = "osu file format v";
|
pub(crate) const OSU_FILE_HEADER: &str = "osu file format v";
|
||||||
|
|
||||||
#[cfg(any(
|
|
||||||
feature = "fruits",
|
|
||||||
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
|
||||||
))]
|
|
||||||
const MAX_COORDINATE_VALUE: f32 = 131_072.0;
|
|
||||||
|
|
||||||
impl Beatmap {
|
impl Beatmap {
|
||||||
const CIRCLE_FLAG: u8 = 1 << 0;
|
const CIRCLE_FLAG: u8 = 1 << 0;
|
||||||
const SLIDER_FLAG: u8 = 1 << 1;
|
const SLIDER_FLAG: u8 = 1 << 1;
|
||||||
@@ -763,12 +761,20 @@ impl Beatmap {
|
|||||||
let mut prev_time = 0.0;
|
let mut prev_time = 0.0;
|
||||||
let mut empty = true;
|
let mut empty = true;
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "fruits",
|
||||||
|
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
||||||
|
))]
|
||||||
// `point_split` will be of type `Vec<&str>
|
// `point_split` will be of type `Vec<&str>
|
||||||
// with each element having its lifetime bound to `buf`.
|
// with each element having its lifetime bound to `buf`.
|
||||||
// To cirvumvent this, `point_split_raw` will contain
|
// To cirvumvent this, `point_split_raw` will contain
|
||||||
// the actual `&str` elements transmuted into `usize`.
|
// the actual `&str` elements transmuted into `usize`.
|
||||||
let mut point_split_raw: Vec<usize> = Vec::new();
|
let mut point_split_raw: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "fruits",
|
||||||
|
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
||||||
|
))]
|
||||||
// Buffer to re-use for all sliders
|
// Buffer to re-use for all sliders
|
||||||
let mut vertices = Vec::new();
|
let mut vertices = Vec::new();
|
||||||
|
|
||||||
@@ -834,7 +840,7 @@ impl Beatmap {
|
|||||||
|
|
||||||
// SAFETY: `Vec<usize>` and `Vec<&str>` have the same size and layout.
|
// SAFETY: `Vec<usize>` and `Vec<&str>` have the same size and layout.
|
||||||
let point_split: &mut Vec<&str> =
|
let point_split: &mut Vec<&str> =
|
||||||
unsafe { mem::transmute(&mut point_split_raw) };
|
unsafe { std::mem::transmute(&mut point_split_raw) };
|
||||||
|
|
||||||
point_split.clear();
|
point_split.clear();
|
||||||
point_split.extend(control_point_iter);
|
point_split.extend(control_point_iter);
|
||||||
@@ -903,8 +909,8 @@ impl Beatmap {
|
|||||||
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
||||||
)))]
|
)))]
|
||||||
{
|
{
|
||||||
let repeats: usize = next_field!(split.nth(1), "repeats").parse()?;
|
let repeats = split.nth(1).next_field("repeats")?.parse()?;
|
||||||
let pixel_len: f32 = next_field!(split.next(), "pixel len").parse()?;
|
let pixel_len = split.next().next_field("pixel len")?.parse()?;
|
||||||
|
|
||||||
HitObjectKind::Slider { repeats, pixel_len }
|
HitObjectKind::Slider { repeats, pixel_len }
|
||||||
}
|
}
|
||||||
@@ -954,7 +960,19 @@ impl Beatmap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_points(
|
// TODO: Replace with `sliders` auxiliary feature
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "fruits",
|
||||||
|
all(feature = "osu", not(feature = "no_sliders_no_leniency"))
|
||||||
|
))]
|
||||||
|
mod osu_fruits {
|
||||||
|
use crate::{math_util::is_linear, ParseError};
|
||||||
|
|
||||||
|
use super::Pos2;
|
||||||
|
|
||||||
|
pub(super) const MAX_COORDINATE_VALUE: f32 = 131_072.0;
|
||||||
|
|
||||||
|
pub(super) fn convert_points(
|
||||||
points: &[&str],
|
points: &[&str],
|
||||||
end_point: Option<&str>,
|
end_point: Option<&str>,
|
||||||
first: bool,
|
first: bool,
|
||||||
@@ -1043,7 +1061,7 @@ fn convert_points(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_point(value: &str, start_pos: Pos2) -> Result<PathControlPoint, ParseError> {
|
pub(super) fn read_point(value: &str, start_pos: Pos2) -> Result<PathControlPoint, ParseError> {
|
||||||
let mut v = value.split(':').map(str::parse);
|
let mut v = value.split(':').map(str::parse);
|
||||||
|
|
||||||
match (v.next(), v.next()) {
|
match (v.next(), v.next()) {
|
||||||
@@ -1052,31 +1070,7 @@ fn read_point(value: &str, start_pos: Pos2) -> Result<PathControlPoint, ParseErr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "async_tokio")]
|
/// Control point for slider curve calculation
|
||||||
impl Beatmap {
|
|
||||||
parse!(async BufReader<AsyncRead>);
|
|
||||||
parse_general!(async BufReader<AsyncRead>);
|
|
||||||
parse_difficulty!(async BufReader<AsyncRead>);
|
|
||||||
parse_timingpoints!(async BufReader<AsyncRead>);
|
|
||||||
parse_hitobjects!(async BufReader<AsyncRead>);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "async_std")]
|
|
||||||
impl Beatmap {
|
|
||||||
parse!(async AsyncBufReader<AsyncRead>);
|
|
||||||
parse_general!(async AsyncBufReader<AsyncRead>);
|
|
||||||
parse_difficulty!(async AsyncBufReader<AsyncRead>);
|
|
||||||
parse_timingpoints!(async AsyncBufReader<AsyncRead>);
|
|
||||||
parse_hitobjects!(async AsyncBufReader<AsyncRead>);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn split_colon(line: &str) -> Option<(&str, &str)> {
|
|
||||||
let mut split = line.split(':');
|
|
||||||
|
|
||||||
Some((split.next()?, split.next()?.trim()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
pub struct PathControlPoint {
|
pub struct PathControlPoint {
|
||||||
pub pos: Pos2,
|
pub pos: Pos2,
|
||||||
@@ -1110,6 +1104,32 @@ impl PathType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async_tokio")]
|
||||||
|
impl Beatmap {
|
||||||
|
parse!(async BufReader<AsyncRead>);
|
||||||
|
parse_general!(async BufReader<AsyncRead>);
|
||||||
|
parse_difficulty!(async BufReader<AsyncRead>);
|
||||||
|
parse_timingpoints!(async BufReader<AsyncRead>);
|
||||||
|
parse_hitobjects!(async BufReader<AsyncRead>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "async_std")]
|
||||||
|
impl Beatmap {
|
||||||
|
parse!(async AsyncBufReader<AsyncRead>);
|
||||||
|
parse_general!(async AsyncBufReader<AsyncRead>);
|
||||||
|
parse_difficulty!(async AsyncBufReader<AsyncRead>);
|
||||||
|
parse_timingpoints!(async AsyncBufReader<AsyncRead>);
|
||||||
|
parse_hitobjects!(async AsyncBufReader<AsyncRead>);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn split_colon(line: &str) -> Option<(&str, &str)> {
|
||||||
|
let mut split = line.split(':');
|
||||||
|
|
||||||
|
Some((split.next()?, split.next()?.trim()))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
enum Section {
|
enum Section {
|
||||||
|
|||||||
Reference in New Issue
Block a user