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