added convert support (#12)

* added taiko conversion

* finished converts

* made clippy happy

* optimized allocations for patterns

* taiko convert fixes

* added mode methods for osu calculators

* from CRLF back to LF

* updated changelog

* updated readme

* undo normal hitsound change

* added edge_sounds to changelog
This commit is contained in:
Max
2022-07-04 14:17:15 +02:00
committed by GitHub
parent c2e96ce95b
commit 7ed1ac5226
38 changed files with 2636 additions and 284 deletions
+26 -7
View File
@@ -3,12 +3,14 @@ mod gradual_performance;
mod pp;
mod strain;
use std::borrow::Cow;
pub use gradual_difficulty::*;
pub use gradual_performance::*;
pub use pp::*;
use strain::Strain;
use crate::{parse::HitObject, Beatmap, GameMode, Mods, Strains};
use crate::{parse::HitObject, Beatmap, GameMode, Mods, OsuStars, Strains};
const SECTION_LEN: f64 = 400.0;
const STAR_SCALING_FACTOR: f64 = 0.018;
@@ -33,7 +35,7 @@ const STAR_SCALING_FACTOR: f64 = 0.018;
/// ```
#[derive(Clone, Debug)]
pub struct ManiaStars<'map> {
map: &'map Beatmap,
map: Cow<'map, Beatmap>,
mods: u32,
passed_objects: Option<usize>,
clock_rate: Option<f64>,
@@ -44,7 +46,7 @@ impl<'map> ManiaStars<'map> {
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
Self {
map,
map: Cow::Borrowed(map),
mods: 0,
passed_objects: None,
clock_rate: None,
@@ -189,12 +191,9 @@ pub(crate) struct DifficultyHitObject<'o> {
impl<'o> DifficultyHitObject<'o> {
#[inline]
fn new(base: &'o HitObject, prev: &'o HitObject, columns: f32, clock_rate: f64) -> Self {
let x_divisor = 512.0 / columns;
let column = (base.pos.x / x_divisor).floor().min(columns - 1.0) as usize;
Self {
base,
column,
column: base.column(columns) as usize,
delta: (base.start_time - prev.start_time) / clock_rate,
start_time: base.start_time / clock_rate,
}
@@ -236,7 +235,27 @@ impl ManiaPerformanceAttributes {
}
impl From<ManiaPerformanceAttributes> for ManiaDifficultyAttributes {
#[inline]
fn from(attributes: ManiaPerformanceAttributes) -> Self {
attributes.difficulty
}
}
impl<'map> From<OsuStars<'map>> for ManiaStars<'map> {
#[inline]
fn from(osu: OsuStars<'map>) -> Self {
let OsuStars {
map,
mods,
passed_objects,
clock_rate,
} = osu;
Self {
map: map.convert_mode(GameMode::MNA),
mods,
passed_objects,
clock_rate,
}
}
}
+41 -5
View File
@@ -1,5 +1,7 @@
use std::borrow::Cow;
use super::{ManiaDifficultyAttributes, ManiaPerformanceAttributes, ManiaStars};
use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
use crate::{Beatmap, DifficultyAttributes, GameMode, Mods, OsuPP, PerformanceAttributes};
/// Performance calculator on osu!mania maps.
///
@@ -31,7 +33,7 @@ use crate::{Beatmap, DifficultyAttributes, Mods, PerformanceAttributes};
#[derive(Clone, Debug)]
#[allow(clippy::upper_case_acronyms)]
pub struct ManiaPP<'map> {
map: &'map Beatmap,
map: Cow<'map, Beatmap>,
stars: Option<f64>,
mods: u32,
pub(crate) score: Option<f64>,
@@ -44,7 +46,7 @@ impl<'map> ManiaPP<'map> {
#[inline]
pub fn new(map: &'map Beatmap) -> Self {
Self {
map,
map: Cow::Borrowed(map),
stars: None,
mods: 0,
score: None,
@@ -112,7 +114,7 @@ impl<'map> ManiaPP<'map> {
/// Calculate all performance related values, including pp and stars.
pub fn calculate(self) -> ManiaPerformanceAttributes {
let stars = self.stars.unwrap_or_else(|| {
let mut calculator = ManiaStars::new(self.map).mods(self.mods);
let mut calculator = ManiaStars::new(self.map.as_ref()).mods(self.mods);
if let Some(passed_objects) = self.passed_objects {
calculator = calculator.passed_objects(passed_objects);
@@ -154,7 +156,19 @@ impl<'map> ManiaPP<'map> {
od *= 1.4;
}
let hit_window = ((od * clock_rate).floor() / clock_rate).ceil();
let hit_window = {
let not_converted = matches!(self.map, Cow::Borrowed(_));
let value = if not_converted {
od
} else if self.map.od > 4.0 {
34.0
} else {
47.0
};
((value * clock_rate).floor() / clock_rate).ceil()
};
let strain_value = self.compute_strain(scaled_score, stars);
let acc_value = self.compute_accuracy_value(scaled_score, strain_value, hit_window);
@@ -199,6 +213,28 @@ impl<'map> ManiaPP<'map> {
}
}
impl<'map> From<OsuPP<'map>> for ManiaPP<'map> {
#[inline]
fn from(osu: OsuPP<'map>) -> Self {
let OsuPP {
map,
mods,
passed_objects,
clock_rate,
..
} = osu;
Self {
map: map.convert_mode(GameMode::MNA),
stars: None,
mods,
score: None,
passed_objects,
clock_rate,
}
}
}
/// Abstract type to provide flexibility when passing difficulty attributes to a performance calculation.
pub trait ManiaAttributeProvider {
/// Provide the star rating (only difficulty attribute for osu!mania).