remove all mode features
minor adjustments
This commit is contained in:
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
## Upcoming
|
## Upcoming
|
||||||
|
|
||||||
- osu: Fix panic on maps with 0 objects
|
- osu: Fix panic on maps with 0 objects
|
||||||
- [BREAKING] Store `HitObject::sound` in `Beatmap::sounds` instead to reduce the struct size
|
|
||||||
- Added `AttributeProvider` impl for `{Mode}PerformanceAttributes`
|
- Added `AttributeProvider` impl for `{Mode}PerformanceAttributes`
|
||||||
|
- [BREAKING] Store `HitObject::sound` in `Beatmap::sounds` instead to reduce the struct size
|
||||||
|
- [BREAKING] Removed the mode features `osu`, `fruits`, `taiko`, and `mania`. Now all modes are always supported.
|
||||||
|
|
||||||
# v0.4.0
|
# v0.4.0
|
||||||
|
|
||||||
|
|||||||
+1
-11
@@ -11,20 +11,10 @@ description = "osu! difficulty and pp calculation for all modes"
|
|||||||
keywords = ["osu", "pp", "stars", "async"]
|
keywords = ["osu", "pp", "stars", "async"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["osu", "taiko", "fruits", "mania"]
|
default = []
|
||||||
|
|
||||||
# game modes
|
|
||||||
osu = ["sliders"]
|
|
||||||
taiko = []
|
|
||||||
fruits = ["sliders"]
|
|
||||||
mania = []
|
|
||||||
|
|
||||||
# async version
|
|
||||||
async_std = ["async-std"]
|
async_std = ["async-std"]
|
||||||
async_tokio = ["tokio"]
|
async_tokio = ["tokio"]
|
||||||
|
|
||||||
# auxiliary, no need to set yourself
|
|
||||||
sliders = []
|
|
||||||
|
|
||||||
[dependencies.async-std]
|
[dependencies.async-std]
|
||||||
version = "1.9"
|
version = "1.9"
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "sliders")]
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parse::{DifficultyPoint, TimingPoint},
|
parse::{DifficultyPoint, TimingPoint},
|
||||||
Beatmap,
|
Beatmap,
|
||||||
@@ -44,7 +42,6 @@ pub(crate) enum ControlPoint {
|
|||||||
Difficulty { time: f64, slider_velocity: f64 },
|
Difficulty { time: f64, slider_velocity: f64 },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
impl ControlPoint {
|
impl ControlPoint {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn time(&self) -> f64 {
|
pub(crate) fn time(&self) -> f64 {
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "sliders")]
|
|
||||||
|
|
||||||
use std::{borrow::Cow, cmp::Ordering, convert::identity, f32::consts::PI, iter};
|
use std::{borrow::Cow, cmp::Ordering, convert::identity, f32::consts::PI, iter};
|
||||||
|
|
||||||
use crate::parse::{PathControlPoint, PathType, Pos2};
|
use crate::parse::{PathControlPoint, PathType, Pos2};
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "fruits")]
|
|
||||||
|
|
||||||
mod catch_object;
|
mod catch_object;
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod fruit_or_juice;
|
mod fruit_or_juice;
|
||||||
|
|||||||
+7
-50
@@ -1,19 +1,11 @@
|
|||||||
use crate::{Beatmap, DifficultyAttributes, GameMode, Mods, PerformanceAttributes};
|
use crate::{
|
||||||
|
fruits::{
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
use crate::fruits::{
|
|
||||||
FruitsGradualDifficultyAttributes, FruitsGradualPerformanceAttributes, FruitsScoreState,
|
FruitsGradualDifficultyAttributes, FruitsGradualPerformanceAttributes, FruitsScoreState,
|
||||||
};
|
},
|
||||||
|
mania::{ManiaGradualDifficultyAttributes, ManiaGradualPerformanceAttributes},
|
||||||
#[cfg(feature = "mania")]
|
osu::{OsuGradualDifficultyAttributes, OsuGradualPerformanceAttributes, OsuScoreState},
|
||||||
use crate::mania::{ManiaGradualDifficultyAttributes, ManiaGradualPerformanceAttributes};
|
taiko::{TaikoGradualDifficultyAttributes, TaikoGradualPerformanceAttributes, TaikoScoreState},
|
||||||
|
Beatmap, DifficultyAttributes, GameMode, Mods, PerformanceAttributes,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
use crate::osu::{OsuGradualDifficultyAttributes, OsuGradualPerformanceAttributes, OsuScoreState};
|
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
use crate::taiko::{
|
|
||||||
TaikoGradualDifficultyAttributes, TaikoGradualPerformanceAttributes, TaikoScoreState,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Gradually calculate the difficulty attributes on maps of any mode.
|
/// Gradually calculate the difficulty attributes on maps of any mode.
|
||||||
@@ -48,16 +40,12 @@ use crate::taiko::{
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum GradualDifficultyAttributes<'map> {
|
pub enum GradualDifficultyAttributes<'map> {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
/// Gradual osu!fruits difficulty attributes.
|
/// Gradual osu!fruits difficulty attributes.
|
||||||
Fruits(FruitsGradualDifficultyAttributes<'map>),
|
Fruits(FruitsGradualDifficultyAttributes<'map>),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
/// Gradual osu!mania difficulty attributes.
|
/// Gradual osu!mania difficulty attributes.
|
||||||
Mania(ManiaGradualDifficultyAttributes<'map>),
|
Mania(ManiaGradualDifficultyAttributes<'map>),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
/// Gradual osu!standard difficulty attributes.
|
/// Gradual osu!standard difficulty attributes.
|
||||||
Osu(OsuGradualDifficultyAttributes),
|
Osu(OsuGradualDifficultyAttributes),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
/// Gradual osu!taiko difficulty attributes.
|
/// Gradual osu!taiko difficulty attributes.
|
||||||
Taiko(TaikoGradualDifficultyAttributes<'map>),
|
Taiko(TaikoGradualDifficultyAttributes<'map>),
|
||||||
}
|
}
|
||||||
@@ -66,16 +54,10 @@ impl<'map> GradualDifficultyAttributes<'map> {
|
|||||||
/// Create a new gradual difficulty calculator for maps of any mode.
|
/// Create a new gradual difficulty calculator for maps of any mode.
|
||||||
pub fn new(map: &'map Beatmap, mods: impl Mods) -> Self {
|
pub fn new(map: &'map Beatmap, mods: impl Mods) -> Self {
|
||||||
match map.mode {
|
match map.mode {
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
GameMode::STD => Self::Osu(OsuGradualDifficultyAttributes::new(map, mods)),
|
GameMode::STD => Self::Osu(OsuGradualDifficultyAttributes::new(map, mods)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
GameMode::TKO => Self::Taiko(TaikoGradualDifficultyAttributes::new(map, mods)),
|
GameMode::TKO => Self::Taiko(TaikoGradualDifficultyAttributes::new(map, mods)),
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
GameMode::CTB => Self::Fruits(FruitsGradualDifficultyAttributes::new(map, mods)),
|
GameMode::CTB => Self::Fruits(FruitsGradualDifficultyAttributes::new(map, mods)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
GameMode::MNA => Self::Mania(ManiaGradualDifficultyAttributes::new(map, mods)),
|
GameMode::MNA => Self::Mania(ManiaGradualDifficultyAttributes::new(map, mods)),
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => panic!("feature for mode {:?} is not enabled", map.mode),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,13 +68,9 @@ impl Iterator for GradualDifficultyAttributes<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
GradualDifficultyAttributes::Fruits(f) => f.next().map(DifficultyAttributes::Fruits),
|
GradualDifficultyAttributes::Fruits(f) => f.next().map(DifficultyAttributes::Fruits),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
GradualDifficultyAttributes::Mania(m) => m.next().map(DifficultyAttributes::Mania),
|
GradualDifficultyAttributes::Mania(m) => m.next().map(DifficultyAttributes::Mania),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
GradualDifficultyAttributes::Osu(o) => o.next().map(DifficultyAttributes::Osu),
|
GradualDifficultyAttributes::Osu(o) => o.next().map(DifficultyAttributes::Osu),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
GradualDifficultyAttributes::Taiko(t) => t.next().map(DifficultyAttributes::Taiko),
|
GradualDifficultyAttributes::Taiko(t) => t.next().map(DifficultyAttributes::Taiko),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,13 +78,9 @@ impl Iterator for GradualDifficultyAttributes<'_> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
GradualDifficultyAttributes::Fruits(f) => f.size_hint(),
|
GradualDifficultyAttributes::Fruits(f) => f.size_hint(),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
GradualDifficultyAttributes::Mania(m) => m.size_hint(),
|
GradualDifficultyAttributes::Mania(m) => m.size_hint(),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
GradualDifficultyAttributes::Osu(o) => o.size_hint(),
|
GradualDifficultyAttributes::Osu(o) => o.size_hint(),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
GradualDifficultyAttributes::Taiko(t) => t.size_hint(),
|
GradualDifficultyAttributes::Taiko(t) => t.size_hint(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,7 +133,6 @@ impl ScoreState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
impl From<ScoreState> for FruitsScoreState {
|
impl From<ScoreState> for FruitsScoreState {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(state: ScoreState) -> Self {
|
fn from(state: ScoreState) -> Self {
|
||||||
@@ -174,7 +147,6 @@ impl From<ScoreState> for FruitsScoreState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
impl From<ScoreState> for OsuScoreState {
|
impl From<ScoreState> for OsuScoreState {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(state: ScoreState) -> Self {
|
fn from(state: ScoreState) -> Self {
|
||||||
@@ -188,7 +160,6 @@ impl From<ScoreState> for OsuScoreState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
impl From<ScoreState> for TaikoScoreState {
|
impl From<ScoreState> for TaikoScoreState {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(state: ScoreState) -> Self {
|
fn from(state: ScoreState) -> Self {
|
||||||
@@ -300,16 +271,12 @@ impl From<ScoreState> for TaikoScoreState {
|
|||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum GradualPerformanceAttributes<'map> {
|
pub enum GradualPerformanceAttributes<'map> {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
/// Gradual osu!ctb performance attributes.
|
/// Gradual osu!ctb performance attributes.
|
||||||
Fruits(FruitsGradualPerformanceAttributes<'map>),
|
Fruits(FruitsGradualPerformanceAttributes<'map>),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
/// Gradual osu!mania performance attributes.
|
/// Gradual osu!mania performance attributes.
|
||||||
Mania(ManiaGradualPerformanceAttributes<'map>),
|
Mania(ManiaGradualPerformanceAttributes<'map>),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
/// Gradual osu!standard performance attributes.
|
/// Gradual osu!standard performance attributes.
|
||||||
Osu(OsuGradualPerformanceAttributes<'map>),
|
Osu(OsuGradualPerformanceAttributes<'map>),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
/// Gradual osu!taiko performance attributes.
|
/// Gradual osu!taiko performance attributes.
|
||||||
Taiko(TaikoGradualPerformanceAttributes<'map>),
|
Taiko(TaikoGradualPerformanceAttributes<'map>),
|
||||||
}
|
}
|
||||||
@@ -318,16 +285,10 @@ impl<'map> GradualPerformanceAttributes<'map> {
|
|||||||
/// Create a new gradual performance calculator for maps of any mode.
|
/// Create a new gradual performance calculator for maps of any mode.
|
||||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||||
match map.mode {
|
match map.mode {
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
GameMode::STD => Self::Osu(OsuGradualPerformanceAttributes::new(map, mods)),
|
GameMode::STD => Self::Osu(OsuGradualPerformanceAttributes::new(map, mods)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
GameMode::TKO => Self::Taiko(TaikoGradualPerformanceAttributes::new(map, mods)),
|
GameMode::TKO => Self::Taiko(TaikoGradualPerformanceAttributes::new(map, mods)),
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
GameMode::CTB => Self::Fruits(FruitsGradualPerformanceAttributes::new(map, mods)),
|
GameMode::CTB => Self::Fruits(FruitsGradualPerformanceAttributes::new(map, mods)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
GameMode::MNA => Self::Mania(ManiaGradualPerformanceAttributes::new(map, mods)),
|
GameMode::MNA => Self::Mania(ManiaGradualPerformanceAttributes::new(map, mods)),
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => panic!("feature for mode {:?} is not enabled", map.mode),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,19 +310,15 @@ impl<'map> GradualPerformanceAttributes<'map> {
|
|||||||
n: usize,
|
n: usize,
|
||||||
) -> Option<PerformanceAttributes> {
|
) -> Option<PerformanceAttributes> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
GradualPerformanceAttributes::Fruits(f) => f
|
GradualPerformanceAttributes::Fruits(f) => f
|
||||||
.process_next_n_objects(state.into(), n)
|
.process_next_n_objects(state.into(), n)
|
||||||
.map(PerformanceAttributes::Fruits),
|
.map(PerformanceAttributes::Fruits),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
GradualPerformanceAttributes::Mania(m) => m
|
GradualPerformanceAttributes::Mania(m) => m
|
||||||
.process_next_n_objects(state.score, n)
|
.process_next_n_objects(state.score, n)
|
||||||
.map(PerformanceAttributes::Mania),
|
.map(PerformanceAttributes::Mania),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
GradualPerformanceAttributes::Osu(o) => o
|
GradualPerformanceAttributes::Osu(o) => o
|
||||||
.process_next_n_objects(state.into(), n)
|
.process_next_n_objects(state.into(), n)
|
||||||
.map(PerformanceAttributes::Osu),
|
.map(PerformanceAttributes::Osu),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
GradualPerformanceAttributes::Taiko(t) => t
|
GradualPerformanceAttributes::Taiko(t) => t
|
||||||
.process_next_n_objects(state.into(), n)
|
.process_next_n_objects(state.into(), n)
|
||||||
.map(PerformanceAttributes::Taiko),
|
.map(PerformanceAttributes::Taiko),
|
||||||
|
|||||||
+8
-170
@@ -179,23 +179,15 @@
|
|||||||
missing_docs
|
missing_docs
|
||||||
)]
|
)]
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "fruits")))]
|
|
||||||
/// Everything about osu!ctb.
|
/// Everything about osu!ctb.
|
||||||
pub mod fruits;
|
pub mod fruits;
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "mania")))]
|
|
||||||
/// Everything about osu!mania.
|
/// Everything about osu!mania.
|
||||||
pub mod mania;
|
pub mod mania;
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "osu")))]
|
|
||||||
/// Everything about osu!standard.
|
/// Everything about osu!standard.
|
||||||
pub mod osu;
|
pub mod osu;
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "taiko")))]
|
|
||||||
/// Everything about osu!taiko.
|
/// Everything about osu!taiko.
|
||||||
pub mod taiko;
|
pub mod taiko;
|
||||||
|
|
||||||
@@ -211,22 +203,13 @@ pub use pp::{AnyPP, AttributeProvider};
|
|||||||
mod curve;
|
mod curve;
|
||||||
mod mods;
|
mod mods;
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
pub(crate) mod control_point_iter;
|
pub(crate) mod control_point_iter;
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
pub(crate) use control_point_iter::{ControlPoint, ControlPointIter};
|
pub(crate) use control_point_iter::{ControlPoint, ControlPointIter};
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
pub use fruits::FruitsPP;
|
pub use fruits::FruitsPP;
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
pub use mania::ManiaPP;
|
pub use mania::ManiaPP;
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
pub use osu::OsuPP;
|
pub use osu::OsuPP;
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
pub use taiko::TaikoPP;
|
pub use taiko::TaikoPP;
|
||||||
|
|
||||||
pub use mods::Mods;
|
pub use mods::Mods;
|
||||||
@@ -270,32 +253,10 @@ impl BeatmapExt for Beatmap {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> DifficultyAttributes {
|
fn stars(&self, mods: impl Mods, passed_objects: Option<usize>) -> DifficultyAttributes {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
GameMode::STD => {
|
GameMode::STD => DifficultyAttributes::Osu(osu::stars(self, mods, passed_objects)),
|
||||||
#[cfg(not(feature = "osu"))]
|
GameMode::MNA => DifficultyAttributes::Mania(mania::stars(self, mods, passed_objects)),
|
||||||
panic!("`osu` feature is not enabled");
|
GameMode::TKO => DifficultyAttributes::Taiko(taiko::stars(self, mods, passed_objects)),
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
DifficultyAttributes::Osu(osu::stars(self, mods, passed_objects))
|
|
||||||
}
|
|
||||||
GameMode::MNA => {
|
|
||||||
#[cfg(not(feature = "mania"))]
|
|
||||||
panic!("`mania` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
DifficultyAttributes::Mania(mania::stars(self, mods, passed_objects))
|
|
||||||
}
|
|
||||||
GameMode::TKO => {
|
|
||||||
#[cfg(not(feature = "taiko"))]
|
|
||||||
panic!("`taiko` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
DifficultyAttributes::Taiko(taiko::stars(self, mods, passed_objects))
|
|
||||||
}
|
|
||||||
GameMode::CTB => {
|
GameMode::CTB => {
|
||||||
#[cfg(not(feature = "fruits"))]
|
|
||||||
panic!("`fruits` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
DifficultyAttributes::Fruits(fruits::stars(self, mods, passed_objects))
|
DifficultyAttributes::Fruits(fruits::stars(self, mods, passed_objects))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -304,32 +265,14 @@ impl BeatmapExt for Beatmap {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn max_pp(&self, mods: u32) -> PerformanceAttributes {
|
fn max_pp(&self, mods: u32) -> PerformanceAttributes {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
GameMode::STD => {
|
GameMode::STD => PerformanceAttributes::Osu(OsuPP::new(self).mods(mods).calculate()),
|
||||||
#[cfg(not(feature = "osu"))]
|
|
||||||
panic!("`osu` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
PerformanceAttributes::Osu(OsuPP::new(self).mods(mods).calculate())
|
|
||||||
}
|
|
||||||
GameMode::MNA => {
|
GameMode::MNA => {
|
||||||
#[cfg(not(feature = "mania"))]
|
|
||||||
panic!("`mania` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
PerformanceAttributes::Mania(ManiaPP::new(self).mods(mods).calculate())
|
PerformanceAttributes::Mania(ManiaPP::new(self).mods(mods).calculate())
|
||||||
}
|
}
|
||||||
GameMode::TKO => {
|
GameMode::TKO => {
|
||||||
#[cfg(not(feature = "taiko"))]
|
|
||||||
panic!("`taiko` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
PerformanceAttributes::Taiko(TaikoPP::new(self).mods(mods).calculate())
|
PerformanceAttributes::Taiko(TaikoPP::new(self).mods(mods).calculate())
|
||||||
}
|
}
|
||||||
GameMode::CTB => {
|
GameMode::CTB => {
|
||||||
#[cfg(not(feature = "fruits"))]
|
|
||||||
panic!("`fruits` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
PerformanceAttributes::Fruits(FruitsPP::new(self).mods(mods).calculate())
|
PerformanceAttributes::Fruits(FruitsPP::new(self).mods(mods).calculate())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,34 +286,10 @@ impl BeatmapExt for Beatmap {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn strains(&self, mods: impl Mods) -> Strains {
|
fn strains(&self, mods: impl Mods) -> Strains {
|
||||||
match self.mode {
|
match self.mode {
|
||||||
GameMode::STD => {
|
GameMode::STD => osu::strains(self, mods),
|
||||||
#[cfg(not(feature = "osu"))]
|
GameMode::MNA => mania::strains(self, mods),
|
||||||
panic!("`osu` feature is not enabled");
|
GameMode::TKO => taiko::strains(self, mods),
|
||||||
|
GameMode::CTB => fruits::strains(self, mods),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
osu::strains(self, mods)
|
|
||||||
}
|
|
||||||
GameMode::MNA => {
|
|
||||||
#[cfg(not(feature = "mania"))]
|
|
||||||
panic!("`mania` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
mania::strains(self, mods)
|
|
||||||
}
|
|
||||||
GameMode::TKO => {
|
|
||||||
#[cfg(not(feature = "taiko"))]
|
|
||||||
panic!("`taiko` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
taiko::strains(self, mods)
|
|
||||||
}
|
|
||||||
GameMode::CTB => {
|
|
||||||
#[cfg(not(feature = "fruits"))]
|
|
||||||
panic!("`fruits` feature is not enabled");
|
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
fruits::strains(self, mods)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,16 +317,12 @@ pub struct Strains {
|
|||||||
/// The result of a difficulty calculation based on the mode.
|
/// The result of a difficulty calculation based on the mode.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum DifficultyAttributes {
|
pub enum DifficultyAttributes {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
/// osu!ctb difficulty calculation reseult.
|
/// osu!ctb difficulty calculation reseult.
|
||||||
Fruits(fruits::FruitsDifficultyAttributes),
|
Fruits(fruits::FruitsDifficultyAttributes),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
/// osu!mania difficulty calculation reseult.
|
/// osu!mania difficulty calculation reseult.
|
||||||
Mania(mania::ManiaDifficultyAttributes),
|
Mania(mania::ManiaDifficultyAttributes),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
/// osu!standard difficulty calculation reseult.
|
/// osu!standard difficulty calculation reseult.
|
||||||
Osu(osu::OsuDifficultyAttributes),
|
Osu(osu::OsuDifficultyAttributes),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
/// osu!taiko difficulty calculation reseult.
|
/// osu!taiko difficulty calculation reseult.
|
||||||
Taiko(taiko::TaikoDifficultyAttributes),
|
Taiko(taiko::TaikoDifficultyAttributes),
|
||||||
}
|
}
|
||||||
@@ -417,13 +332,9 @@ impl DifficultyAttributes {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(attributes) => attributes.stars,
|
Self::Fruits(attributes) => attributes.stars,
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(attributes) => attributes.stars,
|
Self::Mania(attributes) => attributes.stars,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(attributes) => attributes.stars,
|
Self::Osu(attributes) => attributes.stars,
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(attributes) => attributes.stars,
|
Self::Taiko(attributes) => attributes.stars,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -432,35 +343,16 @@ impl DifficultyAttributes {
|
|||||||
///
|
///
|
||||||
/// This will only be `None` for attributes of osu!mania maps.
|
/// This will only be `None` for attributes of osu!mania maps.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
pub fn max_combo(&self) -> Option<usize> {
|
pub fn max_combo(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(attributes) => Some(attributes.max_combo()),
|
Self::Fruits(attributes) => Some(attributes.max_combo()),
|
||||||
Self::Mania(_) => None,
|
Self::Mania(_) => None,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(attributes) => Some(attributes.max_combo),
|
Self::Osu(attributes) => Some(attributes.max_combo),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(attributes) => Some(attributes.max_combo),
|
Self::Taiko(attributes) => Some(attributes.max_combo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "mania"))]
|
|
||||||
#[inline]
|
|
||||||
/// The maximum combo of the map.
|
|
||||||
pub fn max_combo(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(attributes) => attributes.max_combo,
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(attributes) => attributes.max_combo,
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(attributes) => attributes.max_combo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
impl From<fruits::FruitsDifficultyAttributes> for DifficultyAttributes {
|
impl From<fruits::FruitsDifficultyAttributes> for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: fruits::FruitsDifficultyAttributes) -> Self {
|
fn from(attributes: fruits::FruitsDifficultyAttributes) -> Self {
|
||||||
@@ -468,7 +360,6 @@ impl From<fruits::FruitsDifficultyAttributes> for DifficultyAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
impl From<mania::ManiaDifficultyAttributes> for DifficultyAttributes {
|
impl From<mania::ManiaDifficultyAttributes> for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: mania::ManiaDifficultyAttributes) -> Self {
|
fn from(attributes: mania::ManiaDifficultyAttributes) -> Self {
|
||||||
@@ -476,7 +367,6 @@ impl From<mania::ManiaDifficultyAttributes> for DifficultyAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
impl From<osu::OsuDifficultyAttributes> for DifficultyAttributes {
|
impl From<osu::OsuDifficultyAttributes> for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: osu::OsuDifficultyAttributes) -> Self {
|
fn from(attributes: osu::OsuDifficultyAttributes) -> Self {
|
||||||
@@ -484,7 +374,6 @@ impl From<osu::OsuDifficultyAttributes> for DifficultyAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
impl From<taiko::TaikoDifficultyAttributes> for DifficultyAttributes {
|
impl From<taiko::TaikoDifficultyAttributes> for DifficultyAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: taiko::TaikoDifficultyAttributes) -> Self {
|
fn from(attributes: taiko::TaikoDifficultyAttributes) -> Self {
|
||||||
@@ -495,16 +384,12 @@ impl From<taiko::TaikoDifficultyAttributes> for DifficultyAttributes {
|
|||||||
/// The result of a performance calculation based on the mode.
|
/// The result of a performance calculation based on the mode.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum PerformanceAttributes {
|
pub enum PerformanceAttributes {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
/// osu!ctb performance calculation result.
|
/// osu!ctb performance calculation result.
|
||||||
Fruits(fruits::FruitsPerformanceAttributes),
|
Fruits(fruits::FruitsPerformanceAttributes),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
/// osu!mania performance calculation result.
|
/// osu!mania performance calculation result.
|
||||||
Mania(mania::ManiaPerformanceAttributes),
|
Mania(mania::ManiaPerformanceAttributes),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
/// osu!standard performance calculation result.
|
/// osu!standard performance calculation result.
|
||||||
Osu(osu::OsuPerformanceAttributes),
|
Osu(osu::OsuPerformanceAttributes),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
/// osu!taiko performance calculation result.
|
/// osu!taiko performance calculation result.
|
||||||
Taiko(taiko::TaikoPerformanceAttributes),
|
Taiko(taiko::TaikoPerformanceAttributes),
|
||||||
}
|
}
|
||||||
@@ -514,13 +399,9 @@ impl PerformanceAttributes {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn pp(&self) -> f64 {
|
pub fn pp(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(attributes) => attributes.pp,
|
Self::Fruits(attributes) => attributes.pp,
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(attributes) => attributes.pp,
|
Self::Mania(attributes) => attributes.pp,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(attributes) => attributes.pp,
|
Self::Osu(attributes) => attributes.pp,
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(attributes) => attributes.pp,
|
Self::Taiko(attributes) => attributes.pp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -529,13 +410,9 @@ impl PerformanceAttributes {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn stars(&self) -> f64 {
|
pub fn stars(&self) -> f64 {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(attributes) => attributes.stars(),
|
Self::Fruits(attributes) => attributes.stars(),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(attributes) => attributes.stars(),
|
Self::Mania(attributes) => attributes.stars(),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(attributes) => attributes.stars(),
|
Self::Osu(attributes) => attributes.stars(),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(attributes) => attributes.stars(),
|
Self::Taiko(attributes) => attributes.stars(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,65 +421,38 @@ impl PerformanceAttributes {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn difficulty_attributes(&self) -> DifficultyAttributes {
|
pub fn difficulty_attributes(&self) -> DifficultyAttributes {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(attributes) => DifficultyAttributes::Fruits(attributes.difficulty.clone()),
|
Self::Fruits(attributes) => DifficultyAttributes::Fruits(attributes.difficulty.clone()),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(attributes) => DifficultyAttributes::Mania(attributes.difficulty),
|
Self::Mania(attributes) => DifficultyAttributes::Mania(attributes.difficulty),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(attributes) => DifficultyAttributes::Osu(attributes.difficulty.clone()),
|
Self::Osu(attributes) => DifficultyAttributes::Osu(attributes.difficulty.clone()),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(attributes) => DifficultyAttributes::Taiko(attributes.difficulty),
|
Self::Taiko(attributes) => DifficultyAttributes::Taiko(attributes.difficulty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// The maximum combo of the map.
|
/// The maximum combo of the map.
|
||||||
///
|
///
|
||||||
/// This will only be `None` for attributes of osu!mania maps.
|
/// This will only be `None` for attributes of osu!mania maps.
|
||||||
pub fn max_combo(&self) -> Option<usize> {
|
pub fn max_combo(&self) -> Option<usize> {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Some(f.difficulty.max_combo()),
|
Self::Fruits(f) => Some(f.difficulty.max_combo()),
|
||||||
Self::Mania(_) => None,
|
Self::Mania(_) => None,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Some(o.difficulty.max_combo),
|
Self::Osu(o) => Some(o.difficulty.max_combo),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Some(t.difficulty.max_combo),
|
Self::Taiko(t) => Some(t.difficulty.max_combo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "mania"))]
|
|
||||||
#[inline]
|
|
||||||
/// The maximum combo of the map.
|
|
||||||
pub fn max_combo(&self) -> usize {
|
|
||||||
match self {
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => f.difficulty.max_combo,
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => o.difficulty.max_combo,
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => t.difficulty.max_combo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PerformanceAttributes> for DifficultyAttributes {
|
impl From<PerformanceAttributes> for DifficultyAttributes {
|
||||||
fn from(attributes: PerformanceAttributes) -> Self {
|
fn from(attributes: PerformanceAttributes) -> Self {
|
||||||
match attributes {
|
match attributes {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
PerformanceAttributes::Fruits(attributes) => Self::Fruits(attributes.difficulty),
|
PerformanceAttributes::Fruits(attributes) => Self::Fruits(attributes.difficulty),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
PerformanceAttributes::Mania(attributes) => Self::Mania(attributes.difficulty),
|
PerformanceAttributes::Mania(attributes) => Self::Mania(attributes.difficulty),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
PerformanceAttributes::Osu(attributes) => Self::Osu(attributes.difficulty),
|
PerformanceAttributes::Osu(attributes) => Self::Osu(attributes.difficulty),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
PerformanceAttributes::Taiko(attributes) => Self::Taiko(attributes.difficulty),
|
PerformanceAttributes::Taiko(attributes) => Self::Taiko(attributes.difficulty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
impl From<fruits::FruitsPerformanceAttributes> for PerformanceAttributes {
|
impl From<fruits::FruitsPerformanceAttributes> for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: fruits::FruitsPerformanceAttributes) -> Self {
|
fn from(attributes: fruits::FruitsPerformanceAttributes) -> Self {
|
||||||
@@ -610,7 +460,6 @@ impl From<fruits::FruitsPerformanceAttributes> for PerformanceAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
impl From<mania::ManiaPerformanceAttributes> for PerformanceAttributes {
|
impl From<mania::ManiaPerformanceAttributes> for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: mania::ManiaPerformanceAttributes) -> Self {
|
fn from(attributes: mania::ManiaPerformanceAttributes) -> Self {
|
||||||
@@ -618,7 +467,6 @@ impl From<mania::ManiaPerformanceAttributes> for PerformanceAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
impl From<osu::OsuPerformanceAttributes> for PerformanceAttributes {
|
impl From<osu::OsuPerformanceAttributes> for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: osu::OsuPerformanceAttributes) -> Self {
|
fn from(attributes: osu::OsuPerformanceAttributes) -> Self {
|
||||||
@@ -626,7 +474,6 @@ impl From<osu::OsuPerformanceAttributes> for PerformanceAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
impl From<taiko::TaikoPerformanceAttributes> for PerformanceAttributes {
|
impl From<taiko::TaikoPerformanceAttributes> for PerformanceAttributes {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(attributes: taiko::TaikoPerformanceAttributes) -> Self {
|
fn from(attributes: taiko::TaikoPerformanceAttributes) -> Self {
|
||||||
@@ -634,7 +481,6 @@ impl From<taiko::TaikoPerformanceAttributes> for PerformanceAttributes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(feature = "osu", feature = "taiko"))]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn difficulty_range(val: f64, max: f64, avg: f64, min: f64) -> f64 {
|
fn difficulty_range(val: f64, max: f64, avg: f64, min: f64) -> f64 {
|
||||||
if val > 5.0 {
|
if val > 5.0 {
|
||||||
@@ -646,13 +492,5 @@ fn difficulty_range(val: f64, max: f64, avg: f64, min: f64) -> f64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(any(
|
|
||||||
feature = "osu",
|
|
||||||
feature = "taiko",
|
|
||||||
feature = "fruits",
|
|
||||||
feature = "mania"
|
|
||||||
)))]
|
|
||||||
compile_error!("At least one of the features `osu`, `taiko`, `fruits`, `mania` must be enabled");
|
|
||||||
|
|
||||||
#[cfg(all(feature = "async_tokio", feature = "async_std"))]
|
#[cfg(all(feature = "async_tokio", feature = "async_std"))]
|
||||||
compile_error!("Only one of the features `async_tokio` and `async_std` should be enabled");
|
compile_error!("Only one of the features `async_tokio` and `async_std` should be enabled");
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "mania")]
|
|
||||||
|
|
||||||
mod gradual_difficulty;
|
mod gradual_difficulty;
|
||||||
mod gradual_performance;
|
mod gradual_performance;
|
||||||
mod pp;
|
mod pp;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "osu")]
|
|
||||||
|
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod gradual_difficulty;
|
mod gradual_difficulty;
|
||||||
mod gradual_performance;
|
mod gradual_performance;
|
||||||
|
|||||||
+6
-41
@@ -1,17 +1,11 @@
|
|||||||
use super::OSU_FILE_HEADER;
|
use super::OSU_FILE_HEADER;
|
||||||
|
|
||||||
#[cfg(not(all(
|
use std::{
|
||||||
feature = "osu",
|
error::Error as StdError,
|
||||||
feature = "taiko",
|
fmt,
|
||||||
feature = "fruits",
|
io::Error as IOError,
|
||||||
feature = "mania"
|
num::{ParseFloatError, ParseIntError},
|
||||||
)))]
|
};
|
||||||
use super::GameMode;
|
|
||||||
|
|
||||||
use std::error::Error as StdError;
|
|
||||||
use std::fmt;
|
|
||||||
use std::io::Error as IOError;
|
|
||||||
use std::num::{ParseFloatError, ParseIntError};
|
|
||||||
|
|
||||||
/// `Result<_, ParseError>`
|
/// `Result<_, ParseError>`
|
||||||
pub type ParseResult<T> = Result<T, ParseError>;
|
pub type ParseResult<T> = Result<T, ParseError>;
|
||||||
@@ -40,15 +34,6 @@ pub enum ParseError {
|
|||||||
TooManyRepeats,
|
TooManyRepeats,
|
||||||
/// Failed to recognized specified type for hitobjects.
|
/// Failed to recognized specified type for hitobjects.
|
||||||
UnknownHitObjectKind,
|
UnknownHitObjectKind,
|
||||||
|
|
||||||
#[cfg(not(all(
|
|
||||||
feature = "osu",
|
|
||||||
feature = "taiko",
|
|
||||||
feature = "fruits",
|
|
||||||
feature = "mania"
|
|
||||||
)))]
|
|
||||||
/// Tried to parse a map with a game mode who's feature was not enabled
|
|
||||||
UnincludedMode(GameMode),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ParseError {
|
impl fmt::Display for ParseError {
|
||||||
@@ -66,18 +51,6 @@ impl fmt::Display for ParseError {
|
|||||||
Self::MissingField(field) => write!(f, "missing field `{}`", field),
|
Self::MissingField(field) => write!(f, "missing field `{}`", field),
|
||||||
Self::TooManyRepeats => f.write_str("repeat count is way too high"),
|
Self::TooManyRepeats => f.write_str("repeat count is way too high"),
|
||||||
Self::UnknownHitObjectKind => f.write_str("unsupported hitobject kind"),
|
Self::UnknownHitObjectKind => f.write_str("unsupported hitobject kind"),
|
||||||
|
|
||||||
#[cfg(not(all(
|
|
||||||
feature = "osu",
|
|
||||||
feature = "taiko",
|
|
||||||
feature = "fruits",
|
|
||||||
feature = "mania"
|
|
||||||
)))]
|
|
||||||
Self::UnincludedMode(mode) => write!(
|
|
||||||
f,
|
|
||||||
"cannot process {:?} map; its mode's feature has not been included",
|
|
||||||
mode
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,14 +68,6 @@ impl StdError for ParseError {
|
|||||||
Self::MissingField(_) => None,
|
Self::MissingField(_) => None,
|
||||||
Self::TooManyRepeats => None,
|
Self::TooManyRepeats => None,
|
||||||
Self::UnknownHitObjectKind => None,
|
Self::UnknownHitObjectKind => None,
|
||||||
|
|
||||||
#[cfg(not(all(
|
|
||||||
feature = "osu",
|
|
||||||
feature = "taiko",
|
|
||||||
feature = "fruits",
|
|
||||||
feature = "mania"
|
|
||||||
)))]
|
|
||||||
Self::UnincludedMode(_) => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ impl PartialOrd for HitObject {
|
|||||||
pub enum HitObjectKind {
|
pub enum HitObjectKind {
|
||||||
/// A circle object.
|
/// A circle object.
|
||||||
Circle,
|
Circle,
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
/// A full slider object.
|
/// A full slider object.
|
||||||
Slider {
|
Slider {
|
||||||
/// Total length of the slider in pixels.
|
/// Total length of the slider in pixels.
|
||||||
@@ -68,14 +67,6 @@ pub enum HitObjectKind {
|
|||||||
/// The control points of the slider.
|
/// The control points of the slider.
|
||||||
control_points: Vec<super::PathControlPoint>,
|
control_points: Vec<super::PathControlPoint>,
|
||||||
},
|
},
|
||||||
#[cfg(not(feature = "sliders"))]
|
|
||||||
/// A partial slider object.
|
|
||||||
Slider {
|
|
||||||
/// Total length of the slider in pixels.
|
|
||||||
pixel_len: f64,
|
|
||||||
/// The amount of spans of the slider.
|
|
||||||
span_count: usize,
|
|
||||||
},
|
|
||||||
/// A spinner object.
|
/// A spinner object.
|
||||||
Spinner {
|
Spinner {
|
||||||
/// The end time of the spinner.
|
/// The end time of the spinner.
|
||||||
|
|||||||
+11
-162
@@ -38,8 +38,7 @@ use async_std::{
|
|||||||
path::Path,
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
pub use slider_parsing::*;
|
||||||
pub use osu_fruits::*;
|
|
||||||
|
|
||||||
fn sort_unstable<T: PartialOrd>(slice: &mut [T]) {
|
fn sort_unstable<T: PartialOrd>(slice: &mut [T]) {
|
||||||
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));
|
||||||
@@ -116,8 +115,6 @@ macro_rules! parse_general_body {
|
|||||||
($self:ident, $reader:ident, $buf:ident, $section:ident) => {{
|
($self:ident, $reader:ident, $buf:ident, $section:ident) => {{
|
||||||
let mut mode = None;
|
let mut mode = None;
|
||||||
let mut empty = true;
|
let mut empty = true;
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
let mut stack_leniency = None;
|
let mut stack_leniency = None;
|
||||||
|
|
||||||
while read_line!($reader, $buf)? != 0 {
|
while read_line!($reader, $buf)? != 0 {
|
||||||
@@ -142,7 +139,6 @@ macro_rules! parse_general_body {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
if key == "StackLeniency" {
|
if key == "StackLeniency" {
|
||||||
stack_leniency = Some(value.parse()?);
|
stack_leniency = Some(value.parse()?);
|
||||||
}
|
}
|
||||||
@@ -151,31 +147,7 @@ macro_rules! parse_general_body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$self.mode = mode.unwrap_or(GameMode::STD);
|
$self.mode = mode.unwrap_or(GameMode::STD);
|
||||||
|
|
||||||
#[cfg(not(feature = "osu"))]
|
|
||||||
if $self.mode == GameMode::STD {
|
|
||||||
return Err(ParseError::UnincludedMode(GameMode::STD));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "taiko"))]
|
|
||||||
if $self.mode == GameMode::TKO {
|
|
||||||
return Err(ParseError::UnincludedMode(GameMode::TKO));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "fruits"))]
|
|
||||||
if $self.mode == GameMode::CTB {
|
|
||||||
return Err(ParseError::UnincludedMode(GameMode::CTB));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "mania"))]
|
|
||||||
if $self.mode == GameMode::MNA {
|
|
||||||
return Err(ParseError::UnincludedMode(GameMode::MNA));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
{
|
|
||||||
$self.stack_leniency = stack_leniency.unwrap_or(0.7);
|
$self.stack_leniency = stack_leniency.unwrap_or(0.7);
|
||||||
}
|
|
||||||
|
|
||||||
Ok(empty)
|
Ok(empty)
|
||||||
}};
|
}};
|
||||||
@@ -277,61 +249,6 @@ macro_rules! parse_difficulty {
|
|||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! parse_timingpoints_body {
|
macro_rules! parse_timingpoints_body {
|
||||||
(short => $self:ident, $reader:ident, $buf:ident, $section:ident) => {{
|
|
||||||
let mut empty = true;
|
|
||||||
|
|
||||||
// Only parse the first timing point to calculate the bpm
|
|
||||||
if read_line!($reader, $buf)? != 0 {
|
|
||||||
let line = {
|
|
||||||
let mut line = $buf.trim_end();
|
|
||||||
|
|
||||||
if skip_line(line) {
|
|
||||||
$buf.clear();
|
|
||||||
return Ok(empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(idx) = line.find("//") {
|
|
||||||
line = &line[..idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
line
|
|
||||||
};
|
|
||||||
|
|
||||||
if line.starts_with('[') && line.ends_with(']') {
|
|
||||||
*$section = Section::from_str(&line[1..line.len() - 1]);
|
|
||||||
empty = false;
|
|
||||||
$buf.clear();
|
|
||||||
return Ok(empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
let beat_len = line
|
|
||||||
.split(',')
|
|
||||||
.nth(1)
|
|
||||||
.next_field("beat_len")?
|
|
||||||
.trim()
|
|
||||||
.parse()?;
|
|
||||||
|
|
||||||
$self.bpm = bpm(beat_len);
|
|
||||||
|
|
||||||
$buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
while read_line!($reader, $buf)? != 0 {
|
|
||||||
let line = line_prepare!($buf);
|
|
||||||
|
|
||||||
if line.starts_with('[') && line.ends_with(']') {
|
|
||||||
*$section = Section::from_str(&line[1..line.len() - 1]);
|
|
||||||
empty = false;
|
|
||||||
$buf.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
$buf.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(empty)
|
|
||||||
}};
|
|
||||||
|
|
||||||
($self:ident, $reader:ident, $buf:ident, $section:ident) => {{
|
($self:ident, $reader:ident, $buf:ident, $section:ident) => {{
|
||||||
let mut unsorted_timings = false;
|
let mut unsorted_timings = false;
|
||||||
let mut unsorted_difficulties = false;
|
let mut unsorted_difficulties = false;
|
||||||
@@ -408,12 +325,6 @@ macro_rules! parse_timingpoints {
|
|||||||
buf: &mut String,
|
buf: &mut String,
|
||||||
section: &mut Section,
|
section: &mut Section,
|
||||||
) -> ParseResult<bool> {
|
) -> ParseResult<bool> {
|
||||||
#[cfg(not(feature = "sliders"))]
|
|
||||||
{
|
|
||||||
parse_timingpoints_body!(short => self, reader, buf, section)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
parse_timingpoints_body!(self, reader, buf, section)
|
parse_timingpoints_body!(self, reader, buf, section)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -425,12 +336,6 @@ macro_rules! parse_timingpoints {
|
|||||||
buf: &mut String,
|
buf: &mut String,
|
||||||
section: &mut Section,
|
section: &mut Section,
|
||||||
) -> ParseResult<bool> {
|
) -> ParseResult<bool> {
|
||||||
#[cfg(not(feature = "sliders"))]
|
|
||||||
{
|
|
||||||
parse_timingpoints_body!(short => self, reader, buf, section)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
parse_timingpoints_body!(self, reader, buf, section)
|
parse_timingpoints_body!(self, reader, buf, section)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -446,11 +351,9 @@ macro_rules! parse_hitobjects_body {
|
|||||||
// with each element having its lifetime bound to `buf`.
|
// with each element having its lifetime bound to `buf`.
|
||||||
// To circumvent this, `point_split_raw` will contain
|
// To circumvent this, `point_split_raw` will contain
|
||||||
// the actual `&str` elements transmuted into `usize`.
|
// the actual `&str` elements transmuted into `usize`.
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
let mut point_split_raw: Vec<usize> = Vec::new();
|
let mut point_split_raw: Vec<usize> = Vec::new();
|
||||||
|
|
||||||
// Buffer to re-use for all sliders
|
// Buffer to re-use for all sliders
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
let mut vertices = Vec::new();
|
let mut vertices = Vec::new();
|
||||||
|
|
||||||
while read_line!($reader, $buf)? != 0 {
|
while read_line!($reader, $buf)? != 0 {
|
||||||
@@ -491,8 +394,6 @@ macro_rules! parse_hitobjects_body {
|
|||||||
} else if kind & Self::SLIDER_FLAG > 0 {
|
} else if kind & Self::SLIDER_FLAG > 0 {
|
||||||
$self.n_sliders += 1;
|
$self.n_sliders += 1;
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
{
|
|
||||||
let mut control_points = Vec::new();
|
let mut control_points = Vec::new();
|
||||||
|
|
||||||
let control_point_iter = split.next().next_field("control points")?.split('|');
|
let control_point_iter = split.next().next_field("control points")?.split('|');
|
||||||
@@ -574,18 +475,6 @@ macro_rules! parse_hitobjects_body {
|
|||||||
control_points,
|
control_points,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "sliders"))]
|
|
||||||
{
|
|
||||||
let span_count = split.nth(1).next_field("repeats")?.parse()?;
|
|
||||||
let pixel_len = split.next().next_field("pixel len")?.parse()?;
|
|
||||||
|
|
||||||
HitObjectKind::Slider {
|
|
||||||
span_count,
|
|
||||||
pixel_len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if kind & Self::SPINNER_FLAG > 0 {
|
} else if kind & Self::SPINNER_FLAG > 0 {
|
||||||
$self.n_spinners += 1;
|
$self.n_spinners += 1;
|
||||||
let end_time = split.next().next_field("spinner endtime")?.parse()?;
|
let end_time = split.next().next_field("spinner endtime")?.parse()?;
|
||||||
@@ -818,19 +707,12 @@ pub struct Beatmap {
|
|||||||
/// Hitsounds are only used in osu!taiko in which they represent color.
|
/// Hitsounds are only used in osu!taiko in which they represent color.
|
||||||
pub sounds: Vec<u8>,
|
pub sounds: Vec<u8>,
|
||||||
|
|
||||||
#[cfg(not(feature = "sliders"))]
|
|
||||||
/// Beats per minute
|
|
||||||
pub bpm: f64,
|
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
/// Timing points that indicate a new timing section.
|
/// Timing points that indicate a new timing section.
|
||||||
pub timing_points: Vec<TimingPoint>,
|
pub timing_points: Vec<TimingPoint>,
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
/// Timing point for the current timing section.
|
/// Timing point for the current timing section.
|
||||||
pub difficulty_points: Vec<DifficultyPoint>,
|
pub difficulty_points: Vec<DifficultyPoint>,
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
/// The stack leniency that is used to calculate
|
/// The stack leniency that is used to calculate
|
||||||
/// the stack offset for stacked positions.
|
/// the stack offset for stacked positions.
|
||||||
pub stack_leniency: f32,
|
pub stack_leniency: f32,
|
||||||
@@ -853,25 +735,16 @@ impl Beatmap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The beats per minute of the map.
|
/// The beats per minute of the map.
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn bpm(&self) -> f64 {
|
pub fn bpm(&self) -> f64 {
|
||||||
match self.timing_points.first() {
|
match self.timing_points.first() {
|
||||||
Some(point) => bpm(point.beat_len),
|
Some(point) => point.beat_len.recip() * 1000.0 * 60.0,
|
||||||
None => 0.0,
|
None => 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The beats per minute of the map.
|
|
||||||
#[cfg(not(feature = "sliders"))]
|
|
||||||
#[inline]
|
|
||||||
pub fn bpm(&self) -> f64 {
|
|
||||||
self.bpm
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
mod slider_parsing {
|
||||||
mod osu_fruits {
|
|
||||||
use crate::ParseError;
|
use crate::ParseError;
|
||||||
|
|
||||||
use super::Pos2;
|
use super::Pos2;
|
||||||
@@ -1053,19 +926,14 @@ impl Beatmap {
|
|||||||
from_path!(async Path);
|
from_path!(async Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bpm(beat_len: f64) -> f64 {
|
|
||||||
beat_len.recip() * 1000.0 * 60.0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_line(line: &str) -> bool {
|
fn skip_line(line: &str) -> bool {
|
||||||
line.is_empty() || line.starts_with("//") || line.starts_with(' ') || line.starts_with('_')
|
line.is_empty() || line.starts_with("//") || line.starts_with(' ') || line.starts_with('_')
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn split_colon(line: &str) -> Option<(&str, &str)> {
|
fn split_colon(line: &str) -> Option<(&str, &str)> {
|
||||||
let mut split = line.split(':');
|
let (front, back) = line.split_once(':')?;
|
||||||
|
|
||||||
Some((split.next()?, split.next()?.trim()))
|
Some((front, back.trim()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
@@ -1152,25 +1020,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn map_ids() -> Vec<i32> {
|
fn map_ids() -> Vec<i32> {
|
||||||
let mut map_ids = Vec::new();
|
vec![
|
||||||
|
2785319, // osu
|
||||||
if cfg!(feature = "osu") {
|
1974394, // mania
|
||||||
map_ids.push(2785319);
|
2118524, // fruits
|
||||||
}
|
1028484, // taiko
|
||||||
|
]
|
||||||
if cfg!(feature = "mania") {
|
|
||||||
map_ids.push(1974394);
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg!(feature = "fruits") {
|
|
||||||
map_ids.push(2118524);
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg!(feature = "taiko") {
|
|
||||||
map_ids.push(1028484);
|
|
||||||
}
|
|
||||||
|
|
||||||
map_ids
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_info(map: Beatmap) {
|
fn print_info(map: Beatmap) {
|
||||||
@@ -1185,14 +1040,8 @@ mod tests {
|
|||||||
println!("slider_mult: {}", map.slider_mult);
|
println!("slider_mult: {}", map.slider_mult);
|
||||||
println!("tick_rate: {}", map.tick_rate);
|
println!("tick_rate: {}", map.tick_rate);
|
||||||
println!("hit_objects: {}", map.hit_objects.len());
|
println!("hit_objects: {}", map.hit_objects.len());
|
||||||
|
|
||||||
#[cfg(feature = "sliders")]
|
|
||||||
{
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
println!("stack_leniency: {}", map.stack_leniency);
|
println!("stack_leniency: {}", map.stack_leniency);
|
||||||
|
|
||||||
println!("timing_points: {}", map.timing_points.len());
|
println!("timing_points: {}", map.timing_points.len());
|
||||||
println!("difficulty_points: {}", map.difficulty_points.len());
|
println!("difficulty_points: {}", map.difficulty_points.len());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
use crate::{Beatmap, DifficultyAttributes, GameMode, PerformanceAttributes, ScoreState};
|
use crate::{
|
||||||
|
fruits::{FruitsDifficultyAttributes, FruitsPP, FruitsPerformanceAttributes},
|
||||||
#[cfg(feature = "fruits")]
|
mania::{ManiaDifficultyAttributes, ManiaPP, ManiaPerformanceAttributes},
|
||||||
use crate::fruits::{FruitsDifficultyAttributes, FruitsPP, FruitsPerformanceAttributes};
|
osu::{OsuDifficultyAttributes, OsuPP, OsuPerformanceAttributes},
|
||||||
|
taiko::{TaikoDifficultyAttributes, TaikoPP, TaikoPerformanceAttributes},
|
||||||
#[cfg(feature = "mania")]
|
Beatmap, DifficultyAttributes, GameMode, PerformanceAttributes, ScoreState,
|
||||||
use crate::mania::{ManiaDifficultyAttributes, ManiaPP, ManiaPerformanceAttributes};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
use crate::osu::{OsuDifficultyAttributes, OsuPP, OsuPerformanceAttributes};
|
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
use crate::taiko::{TaikoDifficultyAttributes, TaikoPP, TaikoPerformanceAttributes};
|
|
||||||
|
|
||||||
/// Performance calculator on maps of any mode.
|
/// Performance calculator on maps of any mode.
|
||||||
///
|
///
|
||||||
@@ -44,16 +38,12 @@ use crate::taiko::{TaikoDifficultyAttributes, TaikoPP, TaikoPerformanceAttribute
|
|||||||
#[allow(clippy::upper_case_acronyms)]
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum AnyPP<'map> {
|
pub enum AnyPP<'map> {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
/// osu!ctb performance calculator
|
/// osu!ctb performance calculator
|
||||||
Fruits(FruitsPP<'map>),
|
Fruits(FruitsPP<'map>),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
/// osu!mania performance calculator
|
/// osu!mania performance calculator
|
||||||
Mania(ManiaPP<'map>),
|
Mania(ManiaPP<'map>),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
/// osu!standard performance calculator
|
/// osu!standard performance calculator
|
||||||
Osu(OsuPP<'map>),
|
Osu(OsuPP<'map>),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
/// osu!taiko performance calculator
|
/// osu!taiko performance calculator
|
||||||
Taiko(TaikoPP<'map>),
|
Taiko(TaikoPP<'map>),
|
||||||
}
|
}
|
||||||
@@ -63,16 +53,10 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(map: &'map Beatmap) -> Self {
|
pub fn new(map: &'map Beatmap) -> Self {
|
||||||
match map.mode {
|
match map.mode {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
GameMode::CTB => Self::Fruits(FruitsPP::new(map)),
|
GameMode::CTB => Self::Fruits(FruitsPP::new(map)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
GameMode::MNA => Self::Mania(ManiaPP::new(map)),
|
GameMode::MNA => Self::Mania(ManiaPP::new(map)),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
GameMode::STD => Self::Osu(OsuPP::new(map)),
|
GameMode::STD => Self::Osu(OsuPP::new(map)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
GameMode::TKO => Self::Taiko(TaikoPP::new(map)),
|
GameMode::TKO => Self::Taiko(TaikoPP::new(map)),
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
_ => panic!("feature for mode {:?} is not enabled", map.mode),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,13 +65,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn calculate(self) -> PerformanceAttributes {
|
pub fn calculate(self) -> PerformanceAttributes {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => PerformanceAttributes::Fruits(f.calculate()),
|
Self::Fruits(f) => PerformanceAttributes::Fruits(f.calculate()),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => PerformanceAttributes::Mania(m.calculate()),
|
Self::Mania(m) => PerformanceAttributes::Mania(m.calculate()),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => PerformanceAttributes::Osu(o.calculate()),
|
Self::Osu(o) => PerformanceAttributes::Osu(o.calculate()),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => PerformanceAttributes::Taiko(t.calculate()),
|
Self::Taiko(t) => PerformanceAttributes::Taiko(t.calculate()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -98,13 +78,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn attributes(self, attributes: impl AttributeProvider) -> Self {
|
pub fn attributes(self, attributes: impl AttributeProvider) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.attributes(attributes.attributes())),
|
Self::Fruits(f) => Self::Fruits(f.attributes(attributes.attributes())),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => Self::Mania(m.attributes(attributes.attributes())),
|
Self::Mania(m) => Self::Mania(m.attributes(attributes.attributes())),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.attributes(attributes.attributes())),
|
Self::Osu(o) => Self::Osu(o.attributes(attributes.attributes())),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.attributes(attributes.attributes())),
|
Self::Taiko(t) => Self::Taiko(t.attributes(attributes.attributes())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -115,13 +91,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn mods(self, mods: u32) -> Self {
|
pub fn mods(self, mods: u32) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.mods(mods)),
|
Self::Fruits(f) => Self::Fruits(f.mods(mods)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => Self::Mania(m.mods(mods)),
|
Self::Mania(m) => Self::Mania(m.mods(mods)),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.mods(mods)),
|
Self::Osu(o) => Self::Osu(o.mods(mods)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.mods(mods)),
|
Self::Taiko(t) => Self::Taiko(t.mods(mods)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,13 +106,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn passed_objects(self, passed_objects: usize) -> Self {
|
pub fn passed_objects(self, passed_objects: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.passed_objects(passed_objects)),
|
Self::Fruits(f) => Self::Fruits(f.passed_objects(passed_objects)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => Self::Mania(m.passed_objects(passed_objects)),
|
Self::Mania(m) => Self::Mania(m.passed_objects(passed_objects)),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.passed_objects(passed_objects)),
|
Self::Osu(o) => Self::Osu(o.passed_objects(passed_objects)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.passed_objects(passed_objects)),
|
Self::Taiko(t) => Self::Taiko(t.passed_objects(passed_objects)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -149,13 +117,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn state(self, state: ScoreState) -> Self {
|
pub fn state(self, state: ScoreState) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.state(state.into())),
|
Self::Fruits(f) => Self::Fruits(f.state(state.into())),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => Self::Mania(m.score(state.score)),
|
Self::Mania(m) => Self::Mania(m.score(state.score)),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.state(state.into())),
|
Self::Osu(o) => Self::Osu(o.state(state.into())),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.state(state.into())),
|
Self::Taiko(t) => Self::Taiko(t.state(state.into())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -170,13 +134,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn accuracy(self, acc: f64) -> Self {
|
pub fn accuracy(self, acc: f64) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.accuracy(acc)),
|
Self::Fruits(f) => Self::Fruits(f.accuracy(acc)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.accuracy(acc)),
|
Self::Osu(o) => Self::Osu(o.accuracy(acc)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.accuracy(acc)),
|
Self::Taiko(t) => Self::Taiko(t.accuracy(acc)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,13 +148,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn misses(self, misses: usize) -> Self {
|
pub fn misses(self, misses: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.misses(misses)),
|
Self::Fruits(f) => Self::Fruits(f.misses(misses)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.misses(misses)),
|
Self::Osu(o) => Self::Osu(o.misses(misses)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.misses(misses)),
|
Self::Taiko(t) => Self::Taiko(t.misses(misses)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -206,13 +162,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn combo(self, combo: usize) -> Self {
|
pub fn combo(self, combo: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.combo(combo)),
|
Self::Fruits(f) => Self::Fruits(f.combo(combo)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.combo(combo)),
|
Self::Osu(o) => Self::Osu(o.combo(combo)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.combo(combo)),
|
Self::Taiko(t) => Self::Taiko(t.combo(combo)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,13 +176,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn n300(self, n300: usize) -> Self {
|
pub fn n300(self, n300: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.fruits(n300)),
|
Self::Fruits(f) => Self::Fruits(f.fruits(n300)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.n300(n300)),
|
Self::Osu(o) => Self::Osu(o.n300(n300)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.n300(n300)),
|
Self::Taiko(t) => Self::Taiko(t.n300(n300)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,13 +190,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn n100(self, n100: usize) -> Self {
|
pub fn n100(self, n100: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.droplets(n100)),
|
Self::Fruits(f) => Self::Fruits(f.droplets(n100)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.n100(n100)),
|
Self::Osu(o) => Self::Osu(o.n100(n100)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => Self::Taiko(t.n100(n100)),
|
Self::Taiko(t) => Self::Taiko(t.n100(n100)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,13 +204,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn n50(self, n50: usize) -> Self {
|
pub fn n50(self, n50: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.tiny_droplets(n50)),
|
Self::Fruits(f) => Self::Fruits(f.tiny_droplets(n50)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => Self::Osu(o.n50(n50)),
|
Self::Osu(o) => Self::Osu(o.n50(n50)),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(_) => self,
|
Self::Taiko(_) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,13 +219,9 @@ impl<'map> AnyPP<'map> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn n_katu(self, n_katu: usize) -> Self {
|
pub fn n_katu(self, n_katu: usize) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => Self::Fruits(f.tiny_droplet_misses(n_katu)),
|
Self::Fruits(f) => Self::Fruits(f.tiny_droplet_misses(n_katu)),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(_) => self,
|
Self::Mania(_) => self,
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(_) => self,
|
Self::Osu(_) => self,
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(_) => self,
|
Self::Taiko(_) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,17 +231,12 @@ impl<'map> AnyPP<'map> {
|
|||||||
/// This value is only relevant for osu!mania.
|
/// This value is only relevant for osu!mania.
|
||||||
///
|
///
|
||||||
/// On `NoMod` its between 0 and 1,000,000, on `Easy` between 0 and 500,000, etc.
|
/// On `NoMod` its between 0 and 1,000,000, on `Easy` between 0 and 500,000, etc.
|
||||||
#[allow(unused_variables)]
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn score(self, score: u32) -> Self {
|
pub fn score(self, score: u32) -> Self {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(_) => self,
|
Self::Fruits(_) => self,
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => Self::Mania(m.score(score)),
|
Self::Mania(m) => Self::Mania(m.score(score)),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(_) => self,
|
Self::Osu(_) => self,
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(_) => self,
|
Self::Taiko(_) => self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -328,70 +259,36 @@ impl AttributeProvider for PerformanceAttributes {
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
fn attributes(self) -> DifficultyAttributes {
|
||||||
match self {
|
match self {
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
Self::Fruits(f) => DifficultyAttributes::Fruits(f.difficulty),
|
Self::Fruits(f) => DifficultyAttributes::Fruits(f.difficulty),
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
Self::Mania(m) => DifficultyAttributes::Mania(m.difficulty),
|
Self::Mania(m) => DifficultyAttributes::Mania(m.difficulty),
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
Self::Osu(o) => DifficultyAttributes::Osu(o.difficulty),
|
Self::Osu(o) => DifficultyAttributes::Osu(o.difficulty),
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
Self::Taiko(t) => DifficultyAttributes::Taiko(t.difficulty),
|
Self::Taiko(t) => DifficultyAttributes::Taiko(t.difficulty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
macro_rules! impl_attr_provider {
|
||||||
impl AttributeProvider for FruitsDifficultyAttributes {
|
($mode:ident: $difficulty:ident, $performance:ident) => {
|
||||||
|
impl AttributeProvider for $difficulty {
|
||||||
|
#[inline]
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
fn attributes(self) -> DifficultyAttributes {
|
||||||
DifficultyAttributes::Fruits(self)
|
DifficultyAttributes::$mode(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
impl AttributeProvider for $performance {
|
||||||
impl AttributeProvider for ManiaDifficultyAttributes {
|
#[inline]
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
fn attributes(self) -> DifficultyAttributes {
|
||||||
DifficultyAttributes::Mania(self)
|
DifficultyAttributes::$mode(self.difficulty)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
impl_attr_provider!(
|
||||||
impl AttributeProvider for OsuDifficultyAttributes {
|
Fruits: FruitsDifficultyAttributes,
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
FruitsPerformanceAttributes
|
||||||
DifficultyAttributes::Osu(self)
|
);
|
||||||
}
|
impl_attr_provider!(Mania: ManiaDifficultyAttributes, ManiaPerformanceAttributes);
|
||||||
}
|
impl_attr_provider!(Osu: OsuDifficultyAttributes, OsuPerformanceAttributes);
|
||||||
|
impl_attr_provider!(Taiko: TaikoDifficultyAttributes, TaikoPerformanceAttributes);
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
impl AttributeProvider for TaikoDifficultyAttributes {
|
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
|
||||||
DifficultyAttributes::Taiko(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "fruits")]
|
|
||||||
impl AttributeProvider for FruitsPerformanceAttributes {
|
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
|
||||||
DifficultyAttributes::Fruits(self.difficulty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "mania")]
|
|
||||||
impl AttributeProvider for ManiaPerformanceAttributes {
|
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
|
||||||
DifficultyAttributes::Mania(self.difficulty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "osu")]
|
|
||||||
impl AttributeProvider for OsuPerformanceAttributes {
|
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
|
||||||
DifficultyAttributes::Osu(self.difficulty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "taiko")]
|
|
||||||
impl AttributeProvider for TaikoPerformanceAttributes {
|
|
||||||
fn attributes(self) -> DifficultyAttributes {
|
|
||||||
DifficultyAttributes::Taiko(self.difficulty)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![cfg(feature = "taiko")]
|
|
||||||
|
|
||||||
mod difficulty_object;
|
mod difficulty_object;
|
||||||
mod gradual_difficulty;
|
mod gradual_difficulty;
|
||||||
mod gradual_performance;
|
mod gradual_performance;
|
||||||
|
|||||||
Reference in New Issue
Block a user