finished mania + restructured a little

This commit is contained in:
MaxOhn
2022-10-19 11:48:11 +02:00
parent 044fe986c9
commit 6ff46367ca
24 changed files with 737 additions and 350 deletions
+5 -16
View File
@@ -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,
}
}
}
+37 -15
View File
@@ -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
View File
@@ -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
View File
@@ -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 {
-4
View File
@@ -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).