finished mania + restructured a little
This commit is contained in:
@@ -10,24 +10,13 @@ pub(crate) struct ManiaDifficultyObject {
|
||||
}
|
||||
|
||||
impl ManiaDifficultyObject {
|
||||
pub(crate) fn new(
|
||||
base: ManiaObject<'_>,
|
||||
last: ManiaObject<'_>,
|
||||
clock_rate: f64,
|
||||
total_columns: f32,
|
||||
idx: usize,
|
||||
) -> Self {
|
||||
let delta_time = (base.start_time() - last.start_time()) / clock_rate;
|
||||
let start_time = base.start_time() / clock_rate;
|
||||
let end_time = base.end_time() / clock_rate;
|
||||
let base_column = base.column(total_columns);
|
||||
|
||||
pub(crate) fn new(base: &ManiaObject, last: &ManiaObject, clock_rate: f64, idx: usize) -> Self {
|
||||
Self {
|
||||
idx,
|
||||
base_column,
|
||||
delta_time,
|
||||
start_time,
|
||||
end_time,
|
||||
base_column: base.column,
|
||||
delta_time: (base.start_time - last.start_time) / clock_rate,
|
||||
start_time: base.start_time / clock_rate,
|
||||
end_time: base.end_time / clock_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::{beatmap::BeatmapHitWindows, parse::HitObjectKind, Beatmap, Mods};
|
||||
use crate::{beatmap::BeatmapHitWindows, parse::HitObjectKind, util::FloatExt, Beatmap, Mods};
|
||||
|
||||
use super::{
|
||||
difficulty_object::ManiaDifficultyObject,
|
||||
mania_object::ObjectParameters,
|
||||
skills::{Skill, Strain},
|
||||
ManiaDifficultyAttributes, ManiaObject, STAR_SCALING_FACTOR,
|
||||
};
|
||||
@@ -44,12 +45,13 @@ pub struct ManiaGradualDifficultyAttributes<'map> {
|
||||
strain: Strain,
|
||||
diff_objects: Vec<ManiaDifficultyObject>,
|
||||
curr_combo: usize,
|
||||
clock_rate: f64,
|
||||
}
|
||||
|
||||
impl<'map> ManiaGradualDifficultyAttributes<'map> {
|
||||
/// Create a new difficulty attributes iterator for osu!mania maps.
|
||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||
let total_columns = map.cs.round().max(1.0);
|
||||
let total_columns = map.cs.round_even().max(1.0);
|
||||
|
||||
let clock_rate = mods.clock_rate();
|
||||
let strain = Strain::new(total_columns as usize);
|
||||
@@ -62,16 +64,31 @@ impl<'map> ManiaGradualDifficultyAttributes<'map> {
|
||||
.clock_rate(clock_rate)
|
||||
.hit_windows();
|
||||
|
||||
let diff_objects_iter = map
|
||||
.hit_objects
|
||||
.iter()
|
||||
.skip(1)
|
||||
.map(ManiaObject::new)
|
||||
.enumerate()
|
||||
.zip(map.hit_objects.iter().map(ManiaObject::new))
|
||||
.map(|((i, base), prev)| {
|
||||
ManiaDifficultyObject::new(base, prev, clock_rate, total_columns, i)
|
||||
});
|
||||
let mut params = ObjectParameters::new(map);
|
||||
let mut hit_objects = map.hit_objects.iter();
|
||||
|
||||
let first = match hit_objects.next() {
|
||||
Some(h) => ManiaObject::new(h, total_columns, &mut params),
|
||||
None => {
|
||||
return Self {
|
||||
idx: 0,
|
||||
map,
|
||||
hit_window,
|
||||
strain,
|
||||
diff_objects: Vec::new(),
|
||||
curr_combo: 0,
|
||||
clock_rate,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let diff_objects_iter = hit_objects.enumerate().scan(first, |last, (i, h)| {
|
||||
let base = ManiaObject::new(h, total_columns, &mut params);
|
||||
let diff_object = ManiaDifficultyObject::new(&base, &*last, clock_rate, i);
|
||||
*last = base;
|
||||
|
||||
Some(diff_object)
|
||||
});
|
||||
|
||||
let curr_combo = if let Some(h) = map.hit_objects.first() {
|
||||
match &h.kind {
|
||||
@@ -94,6 +111,7 @@ impl<'map> ManiaGradualDifficultyAttributes<'map> {
|
||||
strain,
|
||||
diff_objects,
|
||||
curr_combo,
|
||||
clock_rate,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,10 +125,14 @@ impl Iterator for ManiaGradualDifficultyAttributes<'_> {
|
||||
|
||||
if let Some(h) = self.map.hit_objects.get(self.idx) {
|
||||
match &h.kind {
|
||||
HitObjectKind::Hold { end_time } => {
|
||||
self.curr_combo += 1 + ((*end_time - h.start_time) / 100.0) as usize
|
||||
HitObjectKind::Circle => self.curr_combo += 1,
|
||||
_ => {
|
||||
let start_time = curr.start_time * self.clock_rate;
|
||||
let end_time = curr.end_time * self.clock_rate;
|
||||
let duration = end_time - start_time;
|
||||
|
||||
self.curr_combo += 1 + (duration / 100.0) as usize;
|
||||
}
|
||||
_ => self.curr_combo += 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+88
-17
@@ -1,27 +1,98 @@
|
||||
use crate::parse::HitObject;
|
||||
use crate::{
|
||||
curve::{Curve, CurveBuffers},
|
||||
parse::{HitObject, HitObjectKind},
|
||||
Beatmap,
|
||||
};
|
||||
|
||||
pub(crate) struct ManiaObject<'h> {
|
||||
hit_object: &'h HitObject,
|
||||
const BASE_SCORING_DISTANCE: f64 = 100.0;
|
||||
|
||||
pub(crate) struct ObjectParameters<'a> {
|
||||
pub(crate) map: &'a Beatmap,
|
||||
pub(crate) max_combo: usize,
|
||||
pub(crate) curve_bufs: CurveBuffers,
|
||||
}
|
||||
|
||||
impl<'h> ManiaObject<'h> {
|
||||
pub(crate) fn new(hit_object: &'h HitObject) -> Self {
|
||||
Self { hit_object }
|
||||
impl<'a> ObjectParameters<'a> {
|
||||
pub(crate) fn new(map: &'a Beatmap) -> Self {
|
||||
Self {
|
||||
map,
|
||||
max_combo: 0,
|
||||
curve_bufs: CurveBuffers::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn start_time(&self) -> f64 {
|
||||
self.hit_object.start_time
|
||||
}
|
||||
pub(crate) struct ManiaObject {
|
||||
pub(crate) start_time: f64,
|
||||
pub(crate) end_time: f64,
|
||||
pub(crate) column: usize,
|
||||
}
|
||||
|
||||
pub(crate) fn end_time(&self) -> f64 {
|
||||
self.hit_object.end_time()
|
||||
}
|
||||
|
||||
pub(crate) fn column(&self, total_columns: f32) -> usize {
|
||||
impl ManiaObject {
|
||||
pub(crate) fn column(x: f32, total_columns: f32) -> usize {
|
||||
let x_divisor = 512.0 / total_columns;
|
||||
|
||||
(self.hit_object.pos.x / x_divisor)
|
||||
.floor()
|
||||
.min(total_columns - 1.0) as usize
|
||||
(x / x_divisor).floor().min(total_columns - 1.0) as usize
|
||||
}
|
||||
|
||||
pub(crate) fn new(
|
||||
h: &HitObject,
|
||||
total_columns: f32,
|
||||
params: &mut ObjectParameters<'_>,
|
||||
) -> Self {
|
||||
let ObjectParameters {
|
||||
map,
|
||||
max_combo,
|
||||
curve_bufs,
|
||||
} = params;
|
||||
|
||||
let column = Self::column(h.pos.x, total_columns);
|
||||
*max_combo += 1;
|
||||
|
||||
match &h.kind {
|
||||
HitObjectKind::Circle => Self {
|
||||
start_time: h.start_time,
|
||||
end_time: h.start_time,
|
||||
column,
|
||||
},
|
||||
HitObjectKind::Slider {
|
||||
pixel_len,
|
||||
repeats,
|
||||
control_points,
|
||||
..
|
||||
} => {
|
||||
let span_count = *repeats as f64 + 1.0;
|
||||
|
||||
let curve = Curve::new(control_points, *pixel_len, curve_bufs);
|
||||
let dist = curve.dist();
|
||||
|
||||
let timing_point = map.timing_point_at(h.start_time);
|
||||
let difficulty_point = map.difficulty_point_at(h.start_time).unwrap_or_default();
|
||||
|
||||
let scoring_dist =
|
||||
BASE_SCORING_DISTANCE * map.slider_mult * difficulty_point.slider_vel;
|
||||
|
||||
let vel = scoring_dist / timing_point.beat_len;
|
||||
let duration = span_count * dist / vel;
|
||||
let end_time = h.start_time + duration;
|
||||
|
||||
*max_combo += (duration / 100.0) as usize;
|
||||
|
||||
Self {
|
||||
start_time: h.start_time,
|
||||
end_time,
|
||||
column,
|
||||
}
|
||||
}
|
||||
HitObjectKind::Spinner { end_time } | HitObjectKind::Hold { end_time } => {
|
||||
*max_combo += ((*end_time - h.start_time) / 100.0) as usize;
|
||||
|
||||
Self {
|
||||
start_time: h.start_time,
|
||||
end_time: *end_time,
|
||||
column,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-20
@@ -7,7 +7,7 @@ mod skills;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::{beatmap::BeatmapHitWindows, parse::HitObjectKind, Beatmap, GameMode, Mods, OsuStars};
|
||||
use crate::{beatmap::BeatmapHitWindows, util::FloatExt, Beatmap, GameMode, Mods, OsuStars};
|
||||
|
||||
pub use self::{gradual_difficulty::*, gradual_performance::*, pp::*};
|
||||
|
||||
@@ -15,6 +15,7 @@ pub(crate) use self::mania_object::ManiaObject;
|
||||
|
||||
use self::{
|
||||
difficulty_object::ManiaDifficultyObject,
|
||||
mania_object::ObjectParameters,
|
||||
skills::{Skill, Strain},
|
||||
};
|
||||
|
||||
@@ -169,29 +170,30 @@ fn calculate_result(params: ManiaStars<'_>) -> ManiaResult {
|
||||
} = params;
|
||||
|
||||
let take = passed_objects.unwrap_or(map.hit_objects.len());
|
||||
let total_columns = map.cs.round().max(1.0);
|
||||
let total_columns = map.cs.round_even().max(1.0);
|
||||
|
||||
let clock_rate = clock_rate.unwrap_or_else(|| mods.clock_rate());
|
||||
let mut strain = Strain::new(total_columns as usize);
|
||||
let mut max_combo = 0;
|
||||
let mut params = ObjectParameters::new(map.as_ref());
|
||||
let mut hit_objects = map.hit_objects.iter();
|
||||
|
||||
let diff_objects_iter = map
|
||||
.hit_objects
|
||||
.iter()
|
||||
.take(take)
|
||||
.inspect(|h| match &h.kind {
|
||||
HitObjectKind::Hold { end_time } => {
|
||||
max_combo += 1 + ((*end_time - h.start_time) / 100.0) as usize
|
||||
let first = match hit_objects.next() {
|
||||
Some(h) => ManiaObject::new(h, total_columns, &mut params),
|
||||
None => {
|
||||
return ManiaResult {
|
||||
strain,
|
||||
max_combo: 0,
|
||||
}
|
||||
_ => max_combo += 1,
|
||||
})
|
||||
.skip(1)
|
||||
.map(ManiaObject::new)
|
||||
.enumerate()
|
||||
.zip(map.hit_objects.iter().map(ManiaObject::new))
|
||||
.map(|((i, base), prev)| {
|
||||
ManiaDifficultyObject::new(base, prev, clock_rate, total_columns, i)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
let diff_objects_iter = hit_objects.enumerate().scan(first, |last, (i, h)| {
|
||||
let base = ManiaObject::new(h, total_columns, &mut params);
|
||||
let diff_object = ManiaDifficultyObject::new(&base, &*last, clock_rate, i);
|
||||
*last = base;
|
||||
|
||||
Some(diff_object)
|
||||
});
|
||||
|
||||
let mut diff_objects = Vec::with_capacity(map.hit_objects.len().min(take).saturating_sub(1));
|
||||
diff_objects.extend(diff_objects_iter);
|
||||
@@ -200,7 +202,10 @@ fn calculate_result(params: ManiaStars<'_>) -> ManiaResult {
|
||||
strain.process(curr, &diff_objects);
|
||||
}
|
||||
|
||||
ManiaResult { strain, max_combo }
|
||||
ManiaResult {
|
||||
strain,
|
||||
max_combo: params.max_combo,
|
||||
}
|
||||
}
|
||||
|
||||
struct ManiaResult {
|
||||
|
||||
@@ -96,12 +96,8 @@ impl<'map> ManiaPP<'map> {
|
||||
self
|
||||
}
|
||||
|
||||
// TODO: update
|
||||
/// Amount of passed objects for partial plays, e.g. a fail.
|
||||
///
|
||||
/// Be sure you also set [`score`](ManiaPP::score) or the final values
|
||||
/// won't be correct because it will incorrectly assume a score of 1,000,000.
|
||||
///
|
||||
/// If you want to calculate the performance after every few objects, instead of
|
||||
/// using [`ManiaPP`] multiple times with different `passed_objects`, you should use
|
||||
/// [`ManiaGradualPerformanceAttributes`](crate::mania::ManiaGradualPerformanceAttributes).
|
||||
|
||||
Reference in New Issue
Block a user