mod ext;
mod format;
#[cfg(feature = "pp-calc")]
#[cfg_attr(docsrs, doc(cfg(feature = "pp-calc")))]
pub mod pp_calc;
use crate::color::Color;
use crate::data::{GridSize, Mode};
use crate::events::Event;
use crate::hitobject::{HitObject, HitObjectKind};
use crate::hitsounds::SampleSet;
use crate::timing::{Millis, TimingPoint};
pub use self::ext::DoubleIter;
pub use self::format::*;
#[derive(Clone, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Difficulty {
pub hp_drain_rate: f32,
pub circle_size: f32,
pub overall_difficulty: f32,
pub approach_rate: f32,
pub slider_multiplier: f64,
pub slider_tick_rate: f64,
}
impl Difficulty {
pub fn circle_size_osupx(&self) -> f32 {
54.4 - 4.48 * self.circle_size
}
pub fn approach_preempt(&self) -> Millis {
Millis(if self.approach_rate < 5.0 {
1200 + (600.0 * (5.0 - self.approach_rate)) as i32 / 5
} else if self.approach_rate > 5.0 {
1200 - (750.0 * (self.approach_rate - 5.0)) as i32 / 5
} else {
1200
})
}
pub fn approach_fade_time(&self) -> Millis {
Millis(if self.approach_rate < 5.0 {
800 + (400.0 * (5.0 - self.approach_rate)) as i32 / 5
} else if self.approach_rate > 5.0 {
800 - (500.0 * (self.approach_rate - 5.0)) as i32 / 5
} else {
800
})
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Beatmap {
pub version: u32,
pub audio_filename: String,
pub audio_leadin: Millis,
pub preview_time: Millis,
pub countdown: bool,
pub sample_set: SampleSet,
pub stack_leniency: f64,
pub mode: Mode,
pub letterbox_in_breaks: bool,
pub widescreen_storyboard: bool,
pub difficulty: Difficulty,
pub bookmarks: Vec<i32>,
pub distance_spacing: f64,
pub beat_divisor: u8,
pub grid_size: GridSize,
pub timeline_zoom: f64,
pub title: String,
pub title_unicode: String,
pub artist: String,
pub artist_unicode: String,
pub creator: String,
pub difficulty_name: String,
pub source: String,
pub tags: Vec<String>,
pub beatmap_id: i32,
pub beatmap_set_id: i32,
pub events: Vec<Event>,
pub colors: Vec<Color>,
pub hit_objects: Vec<HitObject>,
pub timing_points: Vec<TimingPoint>,
}
impl Default for Beatmap {
fn default() -> Self {
Beatmap {
version: 0,
audio_filename: String::new(),
audio_leadin: Millis(0),
preview_time: Millis(0),
countdown: false,
sample_set: SampleSet::Default,
stack_leniency: 0.7,
mode: Mode::Osu,
letterbox_in_breaks: false,
widescreen_storyboard: false,
difficulty: Difficulty::default(),
bookmarks: Vec::new(),
distance_spacing: 0.0,
beat_divisor: 1,
grid_size: GridSize::Tiny,
timeline_zoom: 0.0,
title: String::new(),
title_unicode: String::new(),
artist: String::new(),
artist_unicode: String::new(),
creator: String::new(),
difficulty_name: String::new(),
source: String::new(),
tags: Vec::new(),
beatmap_id: 0,
beatmap_set_id: -1,
events: Vec::new(),
colors: Vec::new(),
hit_objects: Vec::new(),
timing_points: Vec::new(),
}
}
}
impl Beatmap {
pub fn locate_timing_point(
&self,
time: impl Into<Millis>,
) -> Option<TimingPoint> {
let mut tp = None;
let time = time.into();
for timing_point in self.timing_points.iter() {
if timing_point.time < time {
tp = Some(timing_point.clone());
}
}
tp
}
pub fn locate_hitobject(&self, time: impl Into<Millis>) -> Option<HitObject> {
let time = time.into();
for hit_object in self.hit_objects.iter() {
if hit_object.start_time == time {
return Some(hit_object.clone());
}
if let HitObjectKind::Slider { .. } = hit_object.kind {}
}
None
}
}