use crate::beatmap::Beatmap;
use crate::hitobject::{HitObject, HitObjectKind, SpinnerInfo};
use crate::timing::{
InheritedTimingInfo, Millis, TimingPoint, TimingPointKind,
UninheritedTimingInfo,
};
impl Beatmap {
#[allow(unused_variables, unused_assignments)]
pub fn max_combo(&self) -> u32 {
let mut res = 0;
let mut mpb = 0.0;
let mut sv = 0.0;
for (i, (obj, tp)) in self.double_iter().enumerate() {
let sl = match &obj.kind {
HitObjectKind::Circle | HitObjectKind::Spinner(_) => {
res += 1;
continue;
}
HitObjectKind::Slider(v) => v,
};
match &tp.kind {
TimingPointKind::Inherited(tp) => sv = tp.slider_velocity,
TimingPointKind::Uninherited(tp) => mpb = tp.mpb,
};
let slider_multiplier = self.difficulty.slider_multiplier;
let pixels_per_beat = slider_multiplier * 100.0 * sv;
let num_beats =
(sl.pixel_length * sl.num_repeats as f64) / pixels_per_beat;
let mut ticks = ((num_beats - 0.1) / sl.num_repeats as f64
* self.difficulty.slider_tick_rate)
.ceil() as i32;
ticks -= 1;
ticks *= sl.num_repeats as i32;
ticks += sl.num_repeats as i32 + 1;
res += ticks.max(0) as u32;
}
res
}
pub fn double_iter(&self) -> DoubleIter<'_> {
DoubleIter::new(self)
}
pub fn get_hitobject_end_time(&self, ho: &HitObject) -> Option<f64> {
match ho.kind {
HitObjectKind::Circle => Some(ho.start_time.as_seconds()),
HitObjectKind::Slider(_) => {
let duration = self.get_slider_duration(ho)?;
Some(ho.start_time.as_seconds() + duration)
}
HitObjectKind::Spinner(SpinnerInfo { end_time }) => {
Some(end_time.as_seconds())
}
}
}
pub fn get_slider_duration(&self, ho: &HitObject) -> Option<f64> {
let info = match &ho.kind {
HitObjectKind::Slider(info) => info,
_ => return None,
};
let slider_velocity = self.get_slider_velocity_at_time(ho.start_time);
let slider_multiplier = self.difficulty.slider_multiplier;
let pixels_per_beat = slider_multiplier * 100.0 * slider_velocity;
let beats_number =
info.pixel_length * info.num_repeats as f64 / pixels_per_beat;
let bpm = self.get_bpm_at_time(ho.start_time)?;
let beat_duration = 60.0 / bpm;
let duration = beats_number * beat_duration;
Some(duration)
}
pub fn get_slider_velocity_at_time(&self, time: Millis) -> f64 {
let mut current = 1.0;
for tp in self.timing_points.iter() {
if tp.time > time {
break;
}
match &tp.kind {
TimingPointKind::Uninherited(_) => {
current = 1.0;
}
TimingPointKind::Inherited(InheritedTimingInfo {
slider_velocity,
..
}) => {
current = *slider_velocity;
}
}
}
current
}
pub fn get_bpm_at_time(&self, time: Millis) -> Option<f64> {
let mut current = None;
for tp in self.timing_points.iter() {
if tp.time > time {
break;
}
if let TimingPointKind::Uninherited(UninheritedTimingInfo {
mpb, ..
}) = tp.kind
{
current = Some(60_000.0 / mpb as f64);
}
}
current
}
}
pub struct DoubleIter<'a> {
beatmap: &'a Beatmap,
ho_index: usize,
tp_index: usize,
}
impl<'a> DoubleIter<'a> {
fn new(beatmap: &'a Beatmap) -> Self {
DoubleIter {
beatmap,
ho_index: 0,
tp_index: 0,
}
}
}
impl<'a> Iterator for DoubleIter<'a> {
type Item = (&'a HitObject, &'a TimingPoint);
fn next(&mut self) -> Option<Self::Item> {
let ho = match self.beatmap.hit_objects.get(self.ho_index) {
Some(v) => v,
None => return None,
};
let tp = loop {
let this_tp = match self.beatmap.timing_points.get(self.tp_index) {
Some(v) => v,
None => return None,
};
if let Some(v) = self.beatmap.timing_points.get(self.tp_index + 1) {
if v.time <= ho.start_time {
self.tp_index += 1;
continue;
}
}
break this_tp;
};
self.ho_index += 1;
Some((ho, tp))
}
}