osu: ignore Hold objects instead of panicking

This commit is contained in:
MaxOhn
2021-01-18 08:45:22 +01:00
parent f75215dd83
commit 6c1a204b41
8 changed files with 153 additions and 145 deletions
+53 -55
View File
@@ -9,69 +9,67 @@ Conversions are generally not supported.
use std::fs::File; use std::fs::File;
use rosu_pp::{Beatmap, BeatmapExt, GameMode, OsuPP, TaikoPP}; use rosu_pp::{Beatmap, BeatmapExt, GameMode, OsuPP, TaikoPP};
fn main() { let file = match File::open("/path/to/file.osu") {
let file = match File::open("/path/to/file.osu") { Ok(file) => file,
Ok(file) => file, Err(why) => panic!("Could not open file: {}", why),
Err(why) => panic!("Could not open file: {}", why), };
};
// Parse the map yourself // Parse the map yourself
let map = match Beatmap::parse(file) { let map = match Beatmap::parse(file) {
Ok(map) => map, Ok(map) => map,
Err(why) => panic!("Error while parsing map: {}", why), Err(why) => panic!("Error while parsing map: {}", why),
}; };
// The different modes make things annoying because their // The different modes make things annoying because their
// pp calculations require different parameters. // pp calculations require different parameters.
// For now, you will have to match on the mode yourself // For now, you will have to match on the mode yourself
// to be able to set all options for pp calculation. // to be able to set all options for pp calculation.
match map.mode { match map.mode {
GameMode::STD => { GameMode::STD => {
let result = OsuPP::new(&map) let result = OsuPP::new(&map)
.mods(24) // HDHR .mods(24) // HDHR
.combo(1234) .combo(1234)
.misses(2) .misses(2)
.accuracy(99.2) .accuracy(99.2)
// `no_leniency` is the suggested default // `no_leniency` is the suggested default
.calculate(rosu_pp::osu::no_leniency::stars); .calculate(rosu_pp::osu::no_leniency::stars);
println!("PP: {}", result.pp()); println!("PP: {}", result.pp());
// If you intend to reuse the current map-mod combination, // If you intend to reuse the current map-mod combination,
// make use of the previous result! // make use of the previous result!
// If attributes are given, then stars & co don't have to be recalculated. // If attributes are given, then stars & co don't have to be recalculated.
let next_result = OsuPP::new(&map) let next_result = OsuPP::new(&map)
.mods(24) // HDHR .mods(24) // HDHR
.attributes(result) .attributes(result)
.combo(543) .combo(543)
.misses(5) .misses(5)
.n50(3) .n50(3)
.accuracy(97.5) .accuracy(97.5)
.calculate(rosu_pp::osu::no_leniency::stars); .calculate(rosu_pp::osu::no_leniency::stars);
println!("Next PP: {}", next_result.pp()); println!("Next PP: {}", next_result.pp());
}, },
GameMode::TKO => { GameMode::TKO => {
let result = TaikoPP::new(&map) let result = TaikoPP::new(&map)
.mods(64) // DT .mods(64) // DT
.combo(555) .combo(555)
.misses(10) .misses(10)
.passed_objects(600) .passed_objects(600)
.accuracy(95.12345) .accuracy(95.12345)
.calculate(); .calculate();
println!("Stars: {} | PP: {}", result.stars(), result.pp()); println!("Stars: {} | PP: {}", result.stars(), result.pp());
}
GameMode::MNA | GameMode::CTB => panic!("do your thing"),
} }
GameMode::MNA | GameMode::CTB => panic!("do your thing"),
// If all you want is the map's stars or max pp,
// you can make use of the BeatmapExt trait.
let stars = map.stars(16, None); // HR
let max_pp = map.max_pp(16);
println!("Stars: {} | Max PP: {}", stars, max_pp);
} }
// If all you want is the map's stars or max pp,
// you can make use of the BeatmapExt trait.
let stars = map.stars(16, None); // HR
let max_pp = map.max_pp(16);
println!("Stars: {} | Max PP: {}", stars, max_pp);
``` ```
#### osu!standard versions #### osu!standard versions
+53 -55
View File
@@ -7,69 +7,67 @@
//! use std::fs::File; //! use std::fs::File;
//! use rosu_pp::{Beatmap, BeatmapExt, GameMode, OsuPP, TaikoPP}; //! use rosu_pp::{Beatmap, BeatmapExt, GameMode, OsuPP, TaikoPP};
//! //!
//! fn main() { //! let file = match File::open("/path/to/file.osu") {
//! let file = match File::open("/path/to/file.osu") { //! Ok(file) => file,
//! Ok(file) => file, //! Err(why) => panic!("Could not open file: {}", why),
//! Err(why) => panic!("Could not open file: {}", why), //! };
//! };
//! //!
//! // Parse the map yourself //! // Parse the map yourself
//! let map = match Beatmap::parse(file) { //! let map = match Beatmap::parse(file) {
//! Ok(map) => map, //! Ok(map) => map,
//! Err(why) => panic!("Error while parsing map: {}", why), //! Err(why) => panic!("Error while parsing map: {}", why),
//! }; //! };
//! //!
//! // The different modes make things annoying because their //! // The different modes make things annoying because their
//! // pp calculations require different parameters. //! // pp calculations require different parameters.
//! // For now, you will have to match on the mode yourself //! // For now, you will have to match on the mode yourself
//! // to be able to set all options for pp calculation. //! // to be able to set all options for pp calculation.
//! match map.mode { //! match map.mode {
//! GameMode::STD => { //! GameMode::STD => {
//! let result = OsuPP::new(&map) //! let result = OsuPP::new(&map)
//! .mods(24) // HDHR //! .mods(24) // HDHR
//! .combo(1234) //! .combo(1234)
//! .misses(2) //! .misses(2)
//! .accuracy(99.2) //! .accuracy(99.2)
//! // `no_leniency` is the suggested default //! // `no_leniency` is the suggested default
//! .calculate(rosu_pp::osu::no_leniency::stars); //! .calculate(rosu_pp::osu::no_leniency::stars);
//! //!
//! println!("PP: {}", result.pp()); //! println!("PP: {}", result.pp());
//! //!
//! // If you intend to reuse the current map-mod combination, //! // If you intend to reuse the current map-mod combination,
//! // make use of the previous result! //! // make use of the previous result!
//! // If attributes are given, then stars & co don't have to be recalculated. //! // If attributes are given, then stars & co don't have to be recalculated.
//! let next_result = OsuPP::new(&map) //! let next_result = OsuPP::new(&map)
//! .mods(24) // HDHR //! .mods(24) // HDHR
//! .attributes(result) //! .attributes(result)
//! .combo(543) //! .combo(543)
//! .misses(5) //! .misses(5)
//! .n50(3) //! .n50(3)
//! .accuracy(97.5) //! .accuracy(97.5)
//! .calculate(rosu_pp::osu::no_leniency::stars); //! .calculate(rosu_pp::osu::no_leniency::stars);
//! //!
//! println!("Next PP: {}", next_result.pp()); //! println!("Next PP: {}", next_result.pp());
//! }, //! },
//! GameMode::TKO => { //! GameMode::TKO => {
//! let result = TaikoPP::new(&map) //! let result = TaikoPP::new(&map)
//! .mods(64) // DT //! .mods(64) // DT
//! .combo(555) //! .combo(555)
//! .misses(10) //! .misses(10)
//! .passed_objects(600) //! .passed_objects(600)
//! .accuracy(95.12345) //! .accuracy(95.12345)
//! .calculate(); //! .calculate();
//! //!
//! println!("Stars: {} | PP: {}", result.stars(), result.pp()); //! println!("Stars: {} | PP: {}", result.stars(), result.pp());
//! }
//! GameMode::MNA | GameMode::CTB => panic!("do your thing"),
//! } //! }
//! //! GameMode::MNA | GameMode::CTB => panic!("do your thing"),
//! // If all you want is the map's stars or max pp,
//! // you can make use of the BeatmapExt trait.
//! let stars = map.stars(16, None); // HR
//! let max_pp = map.max_pp(16);
//!
//! println!("Stars: {} | Max PP: {}", stars, max_pp);
//! } //! }
//!
//! // If all you want is the map's stars or max pp,
//! // you can make use of the BeatmapExt trait.
//! let stars = map.stars(16, None); // HR
//! let max_pp = map.max_pp(16);
//!
//! println!("Stars: {} | Max PP: {}", stars, max_pp);
//! ``` //! ```
//! //!
//! ### osu!standard versions //! ### osu!standard versions
+1 -1
View File
@@ -44,7 +44,7 @@ pub fn stars(
.hit_objects .hit_objects
.iter() .iter()
.take(take) .take(take)
.map(|h| OsuObject::new(h, map, &attributes)); .filter_map(|h| OsuObject::new(h, map, &attributes));
let mut skills = vec![Skill::new(SkillKind::Aim), Skill::new(SkillKind::Speed)]; let mut skills = vec![Skill::new(SkillKind::Aim), Skill::new(SkillKind::Speed)];
+10 -4
View File
@@ -42,14 +42,18 @@ pub(crate) enum OsuObject {
} }
impl OsuObject { impl OsuObject {
pub(crate) fn new(h: &HitObject, map: &Beatmap, attributes: &BeatmapAttributes) -> Self { pub(crate) fn new(
h: &HitObject,
map: &Beatmap,
attributes: &BeatmapAttributes,
) -> Option<Self> {
let pos = h.pos; let pos = h.pos;
let time = h.start_time; let time = h.start_time;
let scale = (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0; let scale = (1.0 - 0.7 * (attributes.cs - 5.0) / 5.0) / 2.0;
let mut stack_height = 0.0; let mut stack_height = 0.0;
match &h.kind { let obj = match &h.kind {
HitObjectKind::Circle => Self::Circle { HitObjectKind::Circle => Self::Circle {
pos, pos,
time, time,
@@ -303,8 +307,10 @@ impl OsuObject {
} }
} }
HitObjectKind::Spinner { .. } => Self::Spinner { pos, time }, HitObjectKind::Spinner { .. } => Self::Spinner { pos, time },
HitObjectKind::Hold { .. } => panic!("found Hold object in osu!standard file"), HitObjectKind::Hold { .. } => return None,
} };
Some(obj)
} }
#[inline] #[inline]
+1 -1
View File
@@ -66,7 +66,7 @@ pub fn stars(
let mut slider_state = SliderState::new(map); let mut slider_state = SliderState::new(map);
let mut ticks_buf = Vec::new(); let mut ticks_buf = Vec::new();
let mut hit_objects = map.hit_objects.iter().take(take).map(|h| { let mut hit_objects = map.hit_objects.iter().take(take).filter_map(|h| {
OsuObject::new( OsuObject::new(
h, h,
map, map,
+6 -4
View File
@@ -21,10 +21,10 @@ impl OsuObject {
ticks: &mut Vec<f32>, ticks: &mut Vec<f32>,
attributes: &mut DifficultyAttributes, attributes: &mut DifficultyAttributes,
slider_state: &mut SliderState, slider_state: &mut SliderState,
) -> Self { ) -> Option<Self> {
attributes.max_combo += 1; // hitcircle, slider head, or spinner attributes.max_combo += 1; // hitcircle, slider head, or spinner
match &h.kind { let obj = match &h.kind {
HitObjectKind::Circle => { HitObjectKind::Circle => {
attributes.n_circles += 1; attributes.n_circles += 1;
@@ -168,8 +168,10 @@ impl OsuObject {
travel_dist: None, travel_dist: None,
} }
} }
HitObjectKind::Hold { .. } => panic!("found Hold object in osu!standard file"), HitObjectKind::Hold { .. } => return None,
} };
Some(obj)
} }
#[inline] #[inline]
+28 -24
View File
@@ -64,33 +64,37 @@ pub fn stars(
let mut n_spinners = 0; let mut n_spinners = 0;
let mut state = SliderState::new(&map); let mut state = SliderState::new(&map);
let mut hit_objects = map.hit_objects.iter().take(take).map(|h| match &h.kind { let mut hit_objects = map
HitObjectKind::Circle => { .hit_objects
max_combo += 1; .iter()
n_circles += 1; .take(take)
.filter_map(|h| match &h.kind {
HitObjectKind::Circle => {
max_combo += 1;
n_circles += 1;
Cow::Borrowed(h) Some(Cow::Borrowed(h))
} }
HitObjectKind::Slider { HitObjectKind::Slider {
pixel_len, repeats, .. pixel_len, repeats, ..
} => { } => {
max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map); max_combo += state.count_ticks(h.start_time, *pixel_len, *repeats, &map);
Cow::Owned(HitObject { Some(Cow::Owned(HitObject {
pos: h.pos, pos: h.pos,
start_time: h.start_time, start_time: h.start_time,
kind: HitObjectKind::Circle, kind: HitObjectKind::Circle,
sound: h.sound, sound: h.sound,
}) }))
} }
HitObjectKind::Spinner { .. } => { HitObjectKind::Spinner { .. } => {
max_combo += 1; max_combo += 1;
n_spinners += 1; n_spinners += 1;
Cow::Borrowed(h) Some(Cow::Borrowed(h))
} }
HitObjectKind::Hold { .. } => panic!("found Hold object in osu!standard map"), HitObjectKind::Hold { .. } => None,
}); });
let mut aim = Skill::new(SkillKind::Aim); let mut aim = Skill::new(SkillKind::Aim);
let mut speed = Skill::new(SkillKind::Speed); let mut speed = Skill::new(SkillKind::Speed);
+1 -1
View File
@@ -4,7 +4,7 @@ use std::cmp::Ordering;
static COMMON_RHYTHMS: [HitObjectRhythm; 9] = [ static COMMON_RHYTHMS: [HitObjectRhythm; 9] = [
HitObjectRhythm { HitObjectRhythm {
ratio: 1.0 / 1.0, ratio: 1.0,
difficulty: 0.0, difficulty: 0.0,
}, },
HitObjectRhythm { HitObjectRhythm {