parse: refactor
This commit is contained in:
@@ -0,0 +1,86 @@
|
|||||||
|
use super::Mods;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct BeatmapAttributes {
|
||||||
|
pub ar: f32,
|
||||||
|
pub od: f32,
|
||||||
|
pub cs: f32,
|
||||||
|
pub hp: f32,
|
||||||
|
pub clock_rate: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BeatmapAttributes {
|
||||||
|
const AR0_MS: f32 = 1800.0;
|
||||||
|
const AR5_MS: f32 = 1200.0;
|
||||||
|
const AR10_MS: f32 = 450.0;
|
||||||
|
const AR_MS_STEP_1: f32 = (Self::AR0_MS - Self::AR5_MS) / 5.0;
|
||||||
|
const AR_MS_STEP_2: f32 = (Self::AR5_MS - Self::AR10_MS) / 5.0;
|
||||||
|
|
||||||
|
const OD0_MS: f32 = 80.0;
|
||||||
|
const OD10_MS: f32 = 20.0;
|
||||||
|
const OD_MS_STEP: f32 = (Self::OD0_MS - Self::OD10_MS) / 10.0;
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn new(ar: f32, od: f32, cs: f32, hp: f32) -> Self {
|
||||||
|
Self {
|
||||||
|
ar,
|
||||||
|
od,
|
||||||
|
cs,
|
||||||
|
hp,
|
||||||
|
clock_rate: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mods(self, mods: impl Mods) -> Self {
|
||||||
|
if !mods.change_map() {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clock_rate = mods.speed();
|
||||||
|
let multiplier = mods.od_ar_hp_multiplier();
|
||||||
|
|
||||||
|
// AR
|
||||||
|
let mut ar = self.ar * multiplier;
|
||||||
|
let mut ar_ms = if ar <= 5.0 {
|
||||||
|
Self::AR0_MS - Self::AR_MS_STEP_1 * ar
|
||||||
|
} else {
|
||||||
|
Self::AR5_MS - Self::AR_MS_STEP_2 * (ar - 5.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
ar_ms = ar_ms.max(Self::AR10_MS).min(Self::AR0_MS);
|
||||||
|
ar_ms /= clock_rate;
|
||||||
|
|
||||||
|
ar = if ar_ms > Self::AR5_MS {
|
||||||
|
(Self::AR0_MS - ar_ms) / Self::AR_MS_STEP_1
|
||||||
|
} else {
|
||||||
|
5.0 + (Self::AR5_MS - ar_ms) / Self::AR_MS_STEP_2
|
||||||
|
};
|
||||||
|
|
||||||
|
// OD
|
||||||
|
let mut od = self.od * multiplier;
|
||||||
|
let mut od_ms = Self::OD0_MS - (Self::OD_MS_STEP * od).ceil();
|
||||||
|
od_ms = od_ms.max(Self::OD10_MS).min(Self::OD0_MS);
|
||||||
|
od_ms /= clock_rate;
|
||||||
|
od = (Self::OD0_MS - od_ms) / Self::OD_MS_STEP;
|
||||||
|
|
||||||
|
// CS
|
||||||
|
let mut cs = self.cs;
|
||||||
|
if mods.hr() {
|
||||||
|
cs *= 1.3;
|
||||||
|
} else if mods.ez() {
|
||||||
|
cs *= 0.5;
|
||||||
|
}
|
||||||
|
cs = cs.min(10.0);
|
||||||
|
|
||||||
|
// HP
|
||||||
|
let hp = (self.hp * multiplier).min(10.0);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
ar,
|
||||||
|
od,
|
||||||
|
cs,
|
||||||
|
hp,
|
||||||
|
clock_rate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct TimingPoint {
|
pub struct TimingPoint {
|
||||||
pub beat_len: f32,
|
pub beat_len: f32,
|
||||||
pub bpm: f32,
|
pub bpm: f32,
|
||||||
@@ -13,7 +13,7 @@ impl PartialOrd for TimingPoint {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct DifficultyPoint {
|
pub struct DifficultyPoint {
|
||||||
pub time: f32,
|
pub time: f32,
|
||||||
pub speed_multiplier: f32,
|
pub speed_multiplier: f32,
|
||||||
|
|||||||
+18
-1
@@ -1,4 +1,4 @@
|
|||||||
use super::{HitObjectKind, Pos2};
|
use super::{PathType, Pos2};
|
||||||
|
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
@@ -42,3 +42,20 @@ impl PartialOrd for HitObject {
|
|||||||
self.start_time.partial_cmp(&other.start_time)
|
self.start_time.partial_cmp(&other.start_time)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum HitObjectKind {
|
||||||
|
Circle,
|
||||||
|
Slider {
|
||||||
|
pixel_len: f32,
|
||||||
|
repeats: usize,
|
||||||
|
curve_points: Vec<Pos2>,
|
||||||
|
path_type: PathType,
|
||||||
|
},
|
||||||
|
Spinner {
|
||||||
|
end_time: f32,
|
||||||
|
},
|
||||||
|
Hold {
|
||||||
|
end_time: f32,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|||||||
+45
-144
@@ -1,3 +1,4 @@
|
|||||||
|
mod attributes;
|
||||||
mod control_point;
|
mod control_point;
|
||||||
mod error;
|
mod error;
|
||||||
mod hitobject;
|
mod hitobject;
|
||||||
@@ -6,9 +7,10 @@ mod mods;
|
|||||||
mod pos2;
|
mod pos2;
|
||||||
mod sort;
|
mod sort;
|
||||||
|
|
||||||
|
pub use attributes::BeatmapAttributes;
|
||||||
pub use control_point::{DifficultyPoint, TimingPoint};
|
pub use control_point::{DifficultyPoint, TimingPoint};
|
||||||
pub use error::{ParseError, ParseResult};
|
pub use error::{ParseError, ParseResult};
|
||||||
pub use hitobject::HitObject;
|
pub use hitobject::{HitObject, HitObjectKind};
|
||||||
pub use hitsound::HitSound;
|
pub use hitsound::HitSound;
|
||||||
pub use mods::Mods;
|
pub use mods::Mods;
|
||||||
pub use pos2::Pos2;
|
pub use pos2::Pos2;
|
||||||
@@ -18,56 +20,6 @@ use std::cmp::Ordering;
|
|||||||
use std::io::{BufRead, BufReader, Read};
|
use std::io::{BufRead, BufReader, Read};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use std::fs::File;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parsing_works() {
|
|
||||||
let file = match File::open("E:/Games/osu!/beatmaps/2223745.osu") {
|
|
||||||
Ok(file) => file,
|
|
||||||
Err(why) => panic!("Could not read file: {}", why),
|
|
||||||
};
|
|
||||||
|
|
||||||
let map = match Beatmap::parse(file) {
|
|
||||||
Ok(map) => map,
|
|
||||||
Err(why) => panic!("Error while parsing map: {}", why),
|
|
||||||
};
|
|
||||||
|
|
||||||
println!("Mode: {}", map.mode as u8);
|
|
||||||
println!("n_circles: {}", map.n_circles);
|
|
||||||
println!("n_sliders: {}", map.n_sliders);
|
|
||||||
println!("n_spinners: {}", map.n_spinners);
|
|
||||||
println!("ar: {}", map.ar);
|
|
||||||
println!("od: {}", map.od);
|
|
||||||
println!("cs: {}", map.cs);
|
|
||||||
println!("hp: {}", map.hp);
|
|
||||||
println!("sv: {}", map.sv);
|
|
||||||
println!("tick_rate: {}", map.tick_rate);
|
|
||||||
println!("stack_leniency: {}", map.stack_leniency);
|
|
||||||
println!("hit_objects: {}", map.hit_objects.len());
|
|
||||||
println!("timing_points: {}", map.timing_points.len());
|
|
||||||
println!("difficulty_points: {}", map.difficulty_points.len());
|
|
||||||
|
|
||||||
assert_eq!(2 + 2, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub enum GameMode {
|
|
||||||
STD = 0,
|
|
||||||
TKO = 1,
|
|
||||||
CTB = 2,
|
|
||||||
MNA = 3,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GameMode {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::STD
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! sort {
|
macro_rules! sort {
|
||||||
($slice:expr) => {
|
($slice:expr) => {
|
||||||
$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))
|
||||||
@@ -90,7 +42,21 @@ macro_rules! validate_float {
|
|||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum GameMode {
|
||||||
|
STD = 0,
|
||||||
|
TKO = 1,
|
||||||
|
CTB = 2,
|
||||||
|
MNA = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GameMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::STD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Default, Debug)]
|
||||||
pub struct Beatmap {
|
pub struct Beatmap {
|
||||||
pub mode: GameMode,
|
pub mode: GameMode,
|
||||||
pub version: u8,
|
pub version: u8,
|
||||||
@@ -413,23 +379,6 @@ impl FromStr for PathType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
|
||||||
pub enum HitObjectKind {
|
|
||||||
Circle,
|
|
||||||
Slider {
|
|
||||||
pixel_len: f32,
|
|
||||||
repeats: usize,
|
|
||||||
curve_points: Vec<Pos2>,
|
|
||||||
path_type: PathType,
|
|
||||||
},
|
|
||||||
Spinner {
|
|
||||||
end_time: f32,
|
|
||||||
},
|
|
||||||
Hold {
|
|
||||||
end_time: f32,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
enum Section {
|
enum Section {
|
||||||
None,
|
None,
|
||||||
@@ -452,86 +401,38 @@ impl Section {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[cfg(test)]
|
||||||
pub struct BeatmapAttributes {
|
mod tests {
|
||||||
pub ar: f32,
|
use super::*;
|
||||||
pub od: f32,
|
use std::fs::File;
|
||||||
pub cs: f32,
|
|
||||||
pub hp: f32,
|
|
||||||
pub clock_rate: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BeatmapAttributes {
|
#[test]
|
||||||
const AR0_MS: f32 = 1800.0;
|
fn parsing_works() {
|
||||||
const AR5_MS: f32 = 1200.0;
|
let file = match File::open("E:/Games/osu!/beatmaps/2223745.osu") {
|
||||||
const AR10_MS: f32 = 450.0;
|
Ok(file) => file,
|
||||||
const AR_MS_STEP_1: f32 = (Self::AR0_MS - Self::AR5_MS) / 5.0;
|
Err(why) => panic!("Could not read file: {}", why),
|
||||||
const AR_MS_STEP_2: f32 = (Self::AR5_MS - Self::AR10_MS) / 5.0;
|
|
||||||
|
|
||||||
const OD0_MS: f32 = 80.0;
|
|
||||||
const OD10_MS: f32 = 20.0;
|
|
||||||
const OD_MS_STEP: f32 = (Self::OD0_MS - Self::OD10_MS) / 10.0;
|
|
||||||
|
|
||||||
fn new(ar: f32, od: f32, cs: f32, hp: f32) -> Self {
|
|
||||||
Self {
|
|
||||||
ar,
|
|
||||||
od,
|
|
||||||
cs,
|
|
||||||
hp,
|
|
||||||
clock_rate: 1.0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn mods(self, mods: impl Mods) -> Self {
|
|
||||||
if !mods.change_map() {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
let clock_rate = mods.speed();
|
|
||||||
let multiplier = mods.od_ar_hp_multiplier();
|
|
||||||
|
|
||||||
// AR
|
|
||||||
let mut ar = self.ar * multiplier;
|
|
||||||
let mut ar_ms = if ar <= 5.0 {
|
|
||||||
Self::AR0_MS - Self::AR_MS_STEP_1 * ar
|
|
||||||
} else {
|
|
||||||
Self::AR5_MS - Self::AR_MS_STEP_2 * (ar - 5.0)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ar_ms = ar_ms.max(Self::AR10_MS).min(Self::AR0_MS);
|
let map = match Beatmap::parse(file) {
|
||||||
ar_ms /= clock_rate;
|
Ok(map) => map,
|
||||||
|
Err(why) => panic!("Error while parsing map: {}", why),
|
||||||
ar = if ar_ms > Self::AR5_MS {
|
|
||||||
(Self::AR0_MS - ar_ms) / Self::AR_MS_STEP_1
|
|
||||||
} else {
|
|
||||||
5.0 + (Self::AR5_MS - ar_ms) / Self::AR_MS_STEP_2
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// OD
|
println!("Mode: {}", map.mode as u8);
|
||||||
let mut od = self.od * multiplier;
|
println!("n_circles: {}", map.n_circles);
|
||||||
let mut od_ms = Self::OD0_MS - (Self::OD_MS_STEP * od).ceil();
|
println!("n_sliders: {}", map.n_sliders);
|
||||||
od_ms = od_ms.max(Self::OD10_MS).min(Self::OD0_MS);
|
println!("n_spinners: {}", map.n_spinners);
|
||||||
od_ms /= clock_rate;
|
println!("ar: {}", map.ar);
|
||||||
od = (Self::OD0_MS - od_ms) / Self::OD_MS_STEP;
|
println!("od: {}", map.od);
|
||||||
|
println!("cs: {}", map.cs);
|
||||||
|
println!("hp: {}", map.hp);
|
||||||
|
println!("sv: {}", map.sv);
|
||||||
|
println!("tick_rate: {}", map.tick_rate);
|
||||||
|
println!("stack_leniency: {}", map.stack_leniency);
|
||||||
|
println!("hit_objects: {}", map.hit_objects.len());
|
||||||
|
println!("timing_points: {}", map.timing_points.len());
|
||||||
|
println!("difficulty_points: {}", map.difficulty_points.len());
|
||||||
|
|
||||||
// CS
|
assert_eq!(2 + 2, 4);
|
||||||
let mut cs = self.cs;
|
|
||||||
if mods.hr() {
|
|
||||||
cs *= 1.3;
|
|
||||||
} else if mods.ez() {
|
|
||||||
cs *= 0.5;
|
|
||||||
}
|
|
||||||
cs = cs.min(10.0);
|
|
||||||
|
|
||||||
// HP
|
|
||||||
let hp = (self.hp * multiplier).min(10.0);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
ar,
|
|
||||||
od,
|
|
||||||
cs,
|
|
||||||
hp,
|
|
||||||
clock_rate,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user