fixed taiko
This commit is contained in:
@@ -157,7 +157,7 @@ impl BeatmapAttributesBuilder {
|
||||
Self::TAIKO_MAX,
|
||||
);
|
||||
|
||||
diff_range.floor() / clock_rate
|
||||
diff_range / clock_rate
|
||||
}
|
||||
GameMode::Mania => {
|
||||
let mut value = if !self.converted {
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use crate::{
|
||||
curve::{Curve, CurveBuffers},
|
||||
parse::{HitObject, HitObjectKind},
|
||||
util::TandemSorter,
|
||||
Beatmap, GameMode,
|
||||
};
|
||||
|
||||
@@ -87,8 +86,10 @@ impl Beatmap {
|
||||
// We only convert STD to TKO so we don't need to remove objects
|
||||
// with the same timestamp that would appear only in MNA
|
||||
|
||||
map.hit_objects
|
||||
.sort_by(|p1, p2| p1.partial_cmp(p2).unwrap_or(Ordering::Equal));
|
||||
let mut sorter = TandemSorter::new(&map.hit_objects, true);
|
||||
sorter.sort(&mut map.hit_objects);
|
||||
sorter.toggle_marks();
|
||||
sorter.sort(&mut map.sounds);
|
||||
|
||||
map.mode = GameMode::Taiko;
|
||||
|
||||
|
||||
+1
-1
@@ -46,7 +46,7 @@ pub enum GradualDifficultyAttributes<'map> {
|
||||
/// Gradual osu!standard difficulty attributes.
|
||||
Osu(OsuGradualDifficultyAttributes),
|
||||
/// Gradual osu!taiko difficulty attributes.
|
||||
Taiko(TaikoGradualDifficultyAttributes<'map>),
|
||||
Taiko(TaikoGradualDifficultyAttributes),
|
||||
}
|
||||
|
||||
impl<'map> GradualDifficultyAttributes<'map> {
|
||||
|
||||
+22
-5
@@ -521,22 +521,39 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn custom() {
|
||||
let path = "F:/osu!/beatmaps/34881.osu";
|
||||
let path = "F:/osu!/beatmaps/1186086.osu";
|
||||
let map = Beatmap::from_path(path).unwrap();
|
||||
|
||||
let attrs = match OsuPP::new(&map).mode(GameMode::Mania).mods(0).calculate() {
|
||||
PerformanceAttributes::Mania(attrs) => attrs,
|
||||
let attrs = match OsuPP::new(&map).mode(GameMode::Taiko).mods(0).calculate() {
|
||||
PerformanceAttributes::Taiko(attrs) => attrs,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
println!(
|
||||
"difficulty:\n\
|
||||
stars={}\n\
|
||||
stamina={}\n\
|
||||
rhythm={}\n\
|
||||
colour={}\n\
|
||||
peak={}\n\
|
||||
hit_window={}\n\
|
||||
max_combo={}\n\
|
||||
stars={}\n\
|
||||
performance:\n\
|
||||
difficulty={}\n\
|
||||
acc={}\n\
|
||||
effective={}\n\
|
||||
pp={}\n",
|
||||
attrs.difficulty.stars, attrs.difficulty.max_combo, attrs.pp_difficulty, attrs.pp,
|
||||
attrs.difficulty.stamina,
|
||||
attrs.difficulty.rhythm,
|
||||
attrs.difficulty.colour,
|
||||
attrs.difficulty.peak,
|
||||
attrs.difficulty.hit_window,
|
||||
attrs.difficulty.max_combo,
|
||||
attrs.difficulty.stars,
|
||||
attrs.pp_difficulty,
|
||||
attrs.pp_acc,
|
||||
attrs.effective_miss_count,
|
||||
attrs.pp,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-13
@@ -600,19 +600,17 @@ macro_rules! parse_hitobjects_body {
|
||||
None => None,
|
||||
};
|
||||
|
||||
// Note: Edge sets are currently not considered, seems to be fine though.
|
||||
let edge_sounds_opt = split.next().map(|sounds| {
|
||||
sounds
|
||||
.split('|')
|
||||
.take(repeats + 2)
|
||||
.map(parse_custom_sound)
|
||||
.collect()
|
||||
});
|
||||
let mut edge_sounds = vec![sound; repeats + 2];
|
||||
|
||||
let edge_sounds = match edge_sounds_opt {
|
||||
None => Vec::new(),
|
||||
Some(sounds) => sounds,
|
||||
};
|
||||
split
|
||||
.next()
|
||||
.map(|sounds| sounds.split('|').map(parse_custom_sound))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.zip(edge_sounds.iter_mut())
|
||||
.for_each(|(parsed, sound)| *sound = parsed);
|
||||
|
||||
// Note: Edge sets are currently not considered, seems to be fine though.
|
||||
|
||||
match has_custom_sound_file(split.nth(1)) {
|
||||
Status::Ok(false) => {}
|
||||
@@ -686,7 +684,7 @@ macro_rules! parse_hitobjects_body {
|
||||
GameMode::Osu | GameMode::Taiko | GameMode::Catch if !unsorted => {}
|
||||
GameMode::Osu | GameMode::Taiko => {
|
||||
// Sort both hitobjects and hitsounds
|
||||
let mut sorter = TandemSorter::new(&$self.hit_objects);
|
||||
let mut sorter = TandemSorter::new(&$self.hit_objects, false);
|
||||
sorter.sort(&mut $self.hit_objects);
|
||||
sorter.toggle_marks();
|
||||
sorter.sort(&mut $self.sounds);
|
||||
|
||||
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::TaikoDifficultyObject;
|
||||
|
||||
use super::{mono_streak::MonoStreak, repeating_hit_patterns::RepeatingHitPatterns};
|
||||
|
||||
pub(crate) struct AlternatingMonoPattern<'o> {
|
||||
pub(crate) mono_streaks: Vec<Rc<RefCell<MonoStreak<'o>>>>,
|
||||
pub(crate) parent: Option<Weak<RefCell<RepeatingHitPatterns<'o>>>>,
|
||||
pub(crate) struct AlternatingMonoPattern {
|
||||
pub(crate) mono_streaks: Vec<Rc<RefCell<MonoStreak>>>,
|
||||
pub(crate) parent: Option<Weak<RefCell<RepeatingHitPatterns>>>,
|
||||
pub(crate) idx: usize,
|
||||
}
|
||||
|
||||
impl Debug for AlternatingMonoPattern<'_> {
|
||||
impl Debug for AlternatingMonoPattern {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(
|
||||
f,
|
||||
@@ -26,7 +26,7 @@ impl Debug for AlternatingMonoPattern<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'o> AlternatingMonoPattern<'o> {
|
||||
impl AlternatingMonoPattern {
|
||||
pub(crate) fn new() -> Rc<RefCell<Self>> {
|
||||
let this = Self {
|
||||
mono_streaks: Vec::new(),
|
||||
@@ -37,7 +37,7 @@ impl<'o> AlternatingMonoPattern<'o> {
|
||||
Rc::new(RefCell::new(this))
|
||||
}
|
||||
|
||||
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject>>> {
|
||||
self.mono_streaks
|
||||
.first()
|
||||
.and_then(|streak| streak.borrow().first_hit_object())
|
||||
|
||||
@@ -14,10 +14,10 @@ mod preprocessor;
|
||||
mod repeating_hit_patterns;
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct TaikoDifficultyColour<'o> {
|
||||
pub(crate) mono_streak: Option<Weak<RefCell<MonoStreak<'o>>>>,
|
||||
pub(crate) alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern<'o>>>>,
|
||||
pub(crate) repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns<'o>>>>,
|
||||
pub(crate) struct TaikoDifficultyColour {
|
||||
pub(crate) mono_streak: Option<Weak<RefCell<MonoStreak>>>,
|
||||
pub(crate) alternating_mono_pattern: Option<Weak<RefCell<AlternatingMonoPattern>>>,
|
||||
pub(crate) repeating_hit_patterns: Option<Rc<RefCell<RepeatingHitPatterns>>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
|
||||
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::{MonoIndex, TaikoDifficultyObject};
|
||||
|
||||
use super::{alternating_mono_pattern::AlternatingMonoPattern, HitKind};
|
||||
|
||||
pub(crate) struct MonoStreak<'o> {
|
||||
pub(crate) hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject<'o>>>>,
|
||||
pub(crate) parent: Option<Weak<RefCell<AlternatingMonoPattern<'o>>>>,
|
||||
pub(crate) struct MonoStreak {
|
||||
pub(crate) hit_objects: Vec<Weak<RefCell<TaikoDifficultyObject>>>,
|
||||
pub(crate) parent: Option<Weak<RefCell<AlternatingMonoPattern>>>,
|
||||
pub(crate) idx: usize,
|
||||
}
|
||||
|
||||
impl Debug for MonoStreak<'_> {
|
||||
impl Debug for MonoStreak {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(
|
||||
f,
|
||||
@@ -26,7 +26,7 @@ impl Debug for MonoStreak<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'o> MonoStreak<'o> {
|
||||
impl MonoStreak {
|
||||
pub(crate) fn new() -> Rc<RefCell<Self>> {
|
||||
let this = Self {
|
||||
hit_objects: Vec::new(),
|
||||
@@ -37,7 +37,7 @@ impl<'o> MonoStreak<'o> {
|
||||
Rc::new(RefCell::new(this))
|
||||
}
|
||||
|
||||
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject>>> {
|
||||
self.hit_objects.first().map(Weak::clone)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ use super::{
|
||||
repeating_hit_patterns::RepeatingHitPatterns,
|
||||
};
|
||||
|
||||
pub(crate) struct ColourDifficultyPreprocessor {}
|
||||
pub(crate) struct ColourDifficultyPreprocessor;
|
||||
|
||||
impl<'o> ColourDifficultyPreprocessor {
|
||||
pub(crate) fn process_and_assign(lists: &mut ObjectLists<'o>) {
|
||||
impl ColourDifficultyPreprocessor {
|
||||
pub(crate) fn process_and_assign(lists: &mut ObjectLists) {
|
||||
// * Assign indexing and encoding data to all relevant objects. Only the first note of each encoding type is
|
||||
// * assigned with the relevant encodings.
|
||||
for repeating_hit_pattern in Self::encode(lists) {
|
||||
@@ -78,14 +78,14 @@ impl<'o> ColourDifficultyPreprocessor {
|
||||
}
|
||||
}
|
||||
|
||||
fn encode(data: &mut ObjectLists<'o>) -> Vec<Rc<RefCell<RepeatingHitPatterns<'o>>>> {
|
||||
fn encode(data: &mut ObjectLists) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
|
||||
let mono_streaks = Self::encode_mono_streak(data);
|
||||
let alternating_mono_patterns = Self::encode_alternating_mono_pattern(mono_streaks);
|
||||
|
||||
Self::encode_repeating_hit_pattern(alternating_mono_patterns)
|
||||
}
|
||||
|
||||
fn encode_mono_streak(data: &mut ObjectLists<'o>) -> Vec<Rc<RefCell<MonoStreak<'o>>>> {
|
||||
fn encode_mono_streak(data: &mut ObjectLists) -> Vec<Rc<RefCell<MonoStreak>>> {
|
||||
let mut mono_streaks = vec![MonoStreak::new()];
|
||||
let mut curr_mono_streak = mono_streaks.last_mut();
|
||||
let mut data_iter = data.all.iter();
|
||||
@@ -100,9 +100,9 @@ impl<'o> ColourDifficultyPreprocessor {
|
||||
|
||||
// * If this is the first object in the list or the colour changed, create a new mono streak
|
||||
let condition = prev.filter(|prev| {
|
||||
!(taiko_obj.borrow().base.is_hit()
|
||||
&& prev.borrow().base.is_hit()
|
||||
&& (taiko_obj.borrow().base.is_rim() != prev.borrow().base.is_rim()))
|
||||
!(taiko_obj.borrow().base.is_hit
|
||||
&& prev.borrow().base.is_hit
|
||||
&& (taiko_obj.borrow().base.is_rim != prev.borrow().base.is_rim))
|
||||
});
|
||||
|
||||
if condition.is_none() {
|
||||
@@ -120,8 +120,8 @@ impl<'o> ColourDifficultyPreprocessor {
|
||||
}
|
||||
|
||||
fn encode_alternating_mono_pattern(
|
||||
data: Vec<Rc<RefCell<MonoStreak<'o>>>>,
|
||||
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern<'o>>>> {
|
||||
data: Vec<Rc<RefCell<MonoStreak>>>,
|
||||
) -> VecDeque<Rc<RefCell<AlternatingMonoPattern>>> {
|
||||
let mut mono_patterns = VecDeque::new();
|
||||
mono_patterns.push_back(AlternatingMonoPattern::new());
|
||||
let mut curr_mono_pattern = mono_patterns.back_mut();
|
||||
@@ -151,8 +151,8 @@ impl<'o> ColourDifficultyPreprocessor {
|
||||
}
|
||||
|
||||
fn encode_repeating_hit_pattern(
|
||||
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern<'o>>>>,
|
||||
) -> Vec<Rc<RefCell<RepeatingHitPatterns<'o>>>> {
|
||||
mut data: VecDeque<Rc<RefCell<AlternatingMonoPattern>>>,
|
||||
) -> Vec<Rc<RefCell<RepeatingHitPatterns>>> {
|
||||
let mut hit_patterns = Vec::new();
|
||||
let mut curr_hit_pattern: Option<Rc<std::cell::RefCell<_>>> = None;
|
||||
|
||||
|
||||
@@ -8,13 +8,13 @@ use crate::taiko::difficulty_object::TaikoDifficultyObject;
|
||||
|
||||
use super::alternating_mono_pattern::AlternatingMonoPattern;
|
||||
|
||||
pub(crate) struct RepeatingHitPatterns<'o> {
|
||||
pub(crate) alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern<'o>>>>,
|
||||
pub(crate) struct RepeatingHitPatterns {
|
||||
pub(crate) alternating_mono_patterns: Vec<Rc<RefCell<AlternatingMonoPattern>>>,
|
||||
pub(crate) prev: Option<Weak<RefCell<Self>>>,
|
||||
pub(crate) repetition_interval: usize,
|
||||
}
|
||||
|
||||
impl Debug for RepeatingHitPatterns<'_> {
|
||||
impl Debug for RepeatingHitPatterns {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(
|
||||
f,
|
||||
@@ -26,7 +26,7 @@ impl Debug for RepeatingHitPatterns<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'o> RepeatingHitPatterns<'o> {
|
||||
impl RepeatingHitPatterns {
|
||||
const MAX_REPETITION_INTERVAL: usize = 16;
|
||||
|
||||
pub(crate) fn new(prev: Option<Weak<RefCell<Self>>>) -> Rc<RefCell<Self>> {
|
||||
@@ -39,7 +39,7 @@ impl<'o> RepeatingHitPatterns<'o> {
|
||||
Rc::new(RefCell::new(this))
|
||||
}
|
||||
|
||||
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
pub(crate) fn first_hit_object(&self) -> Option<Weak<RefCell<TaikoDifficultyObject>>> {
|
||||
self.alternating_mono_patterns
|
||||
.first()
|
||||
.and_then(|pattern| pattern.borrow().first_hit_object())
|
||||
|
||||
+102
-101
@@ -1,135 +1,55 @@
|
||||
use std::{cell::RefCell, cmp::Ordering, rc::Rc};
|
||||
|
||||
use crate::parse::HitObject;
|
||||
|
||||
use super::{colours::TaikoDifficultyColour, rim::Rim, taiko_object::TaikoObject};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct ObjectLists<'o> {
|
||||
pub(crate) all: Vec<Rc<RefCell<TaikoDifficultyObject<'o>>>>,
|
||||
pub(crate) centres: Vec<usize>,
|
||||
pub(crate) rims: Vec<usize>,
|
||||
pub(crate) notes: Vec<usize>,
|
||||
}
|
||||
|
||||
impl<'o> ObjectLists<'o> {
|
||||
pub(crate) fn prev_mono(
|
||||
&self,
|
||||
curr: usize,
|
||||
backwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
let curr = &self.all[curr];
|
||||
|
||||
let prev = match curr.borrow().mono_idx {
|
||||
MonoIndex::Centre(idx) => idx
|
||||
.checked_sub(backwards_idx + 1)
|
||||
.and_then(|idx| self.centres.get(idx))?,
|
||||
MonoIndex::Rim(idx) => idx
|
||||
.checked_sub(backwards_idx + 1)
|
||||
.and_then(|idx| self.rims.get(idx))?,
|
||||
MonoIndex::None => return None,
|
||||
};
|
||||
|
||||
self.all.get(*prev)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn next_mono(
|
||||
&self,
|
||||
curr: usize,
|
||||
forwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
let curr = &self.all[curr];
|
||||
|
||||
let next = match curr.borrow().mono_idx {
|
||||
MonoIndex::Centre(idx) => self.centres.get(idx + (forwards_idx + 1))?,
|
||||
MonoIndex::Rim(idx) => self.rims.get(idx + (forwards_idx + 1))?,
|
||||
MonoIndex::None => return None,
|
||||
};
|
||||
|
||||
self.all.get(*next)
|
||||
}
|
||||
|
||||
pub(crate) fn prev_note(
|
||||
&self,
|
||||
curr: usize,
|
||||
backwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
let curr = &self.all[curr];
|
||||
let note_idx = curr.borrow().note_idx?;
|
||||
let idx = note_idx.checked_sub(backwards_idx + 1)?;
|
||||
let prev = self.notes.get(idx)?;
|
||||
|
||||
self.all.get(*prev)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn next_note(
|
||||
&self,
|
||||
curr: usize,
|
||||
forwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject<'o>>>> {
|
||||
let curr = &self.all[curr];
|
||||
let note_idx = curr.borrow().note_idx?;
|
||||
let idx = note_idx + (forwards_idx + 1);
|
||||
let prev = self.notes.get(idx)?;
|
||||
|
||||
self.all.get(*prev)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) enum MonoIndex {
|
||||
Centre(usize),
|
||||
Rim(usize),
|
||||
None,
|
||||
}
|
||||
use super::{colours::TaikoDifficultyColour, taiko_object::TaikoObject};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct TaikoDifficultyObject<'o> {
|
||||
pub(crate) base: TaikoObject<'o>,
|
||||
pub(crate) struct TaikoDifficultyObject {
|
||||
pub(crate) base: TaikoObject,
|
||||
pub(crate) prev_time: f64,
|
||||
pub(crate) colour: TaikoDifficultyColour<'o>,
|
||||
pub(crate) colour: TaikoDifficultyColour,
|
||||
pub(crate) rhythm: &'static HitObjectRhythm,
|
||||
pub(crate) mono_idx: MonoIndex,
|
||||
pub(crate) note_idx: Option<usize>,
|
||||
pub(crate) idx: usize,
|
||||
pub(crate) start_time: f64,
|
||||
pub(crate) delta: f64,
|
||||
}
|
||||
|
||||
impl<'o> TaikoDifficultyObject<'o> {
|
||||
impl TaikoDifficultyObject {
|
||||
pub(crate) fn new(
|
||||
base: TaikoObject<'o>,
|
||||
last: TaikoObject<'o>,
|
||||
last_last: TaikoObject<'o>,
|
||||
base: TaikoObject,
|
||||
base_start_time: f64,
|
||||
last_start_time: f64,
|
||||
last_last_start_time: f64,
|
||||
clock_rate: f64,
|
||||
lists: &ObjectLists<'o>,
|
||||
lists: &ObjectLists,
|
||||
idx: usize,
|
||||
) -> Self {
|
||||
// * Create the Colour object, its properties should be filled in by TaikoDifficultyPreprocessor
|
||||
let colour = TaikoDifficultyColour::default();
|
||||
|
||||
let delta = (base.h.start_time - last.h.start_time) / clock_rate;
|
||||
let rhythm = closest_rhythm(delta, last.h, last_last.h, clock_rate);
|
||||
let delta = (base_start_time - last_start_time) / clock_rate;
|
||||
let rhythm = closest_rhythm(delta, last_start_time, last_last_start_time, clock_rate);
|
||||
|
||||
let mono_idx = if !base.is_hit() {
|
||||
let mono_idx = if !base.is_hit {
|
||||
MonoIndex::None
|
||||
} else if base.sound.is_rim() {
|
||||
} else if base.is_rim {
|
||||
MonoIndex::Rim(lists.rims.len())
|
||||
} else {
|
||||
MonoIndex::Centre(lists.centres.len())
|
||||
};
|
||||
|
||||
let note_idx = base.is_hit().then_some(lists.notes.len());
|
||||
let note_idx = base.is_hit.then_some(lists.notes.len());
|
||||
|
||||
Self {
|
||||
base,
|
||||
prev_time: last.h.start_time / clock_rate,
|
||||
prev_time: last_start_time / clock_rate,
|
||||
colour,
|
||||
rhythm,
|
||||
mono_idx,
|
||||
note_idx,
|
||||
idx,
|
||||
start_time: base_start_time / clock_rate,
|
||||
delta,
|
||||
}
|
||||
}
|
||||
@@ -173,11 +93,11 @@ impl Eq for HitObjectRhythm {}
|
||||
|
||||
fn closest_rhythm(
|
||||
delta_time: f64,
|
||||
last: &HitObject,
|
||||
last_last: &HitObject,
|
||||
last_start_time: f64,
|
||||
last_last_start_time: f64,
|
||||
clock_rate: f64,
|
||||
) -> &'static HitObjectRhythm {
|
||||
let prev_len = (last.start_time - last_last.start_time) / clock_rate;
|
||||
let prev_len = (last_start_time - last_last_start_time) / clock_rate;
|
||||
let ratio = delta_time / prev_len;
|
||||
|
||||
COMMON_RHYTHMS
|
||||
@@ -190,3 +110,84 @@ fn closest_rhythm(
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub(crate) struct ObjectLists {
|
||||
pub(crate) all: Vec<Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
pub(crate) centres: Vec<usize>,
|
||||
pub(crate) rims: Vec<usize>,
|
||||
pub(crate) notes: Vec<usize>,
|
||||
}
|
||||
|
||||
impl ObjectLists {
|
||||
pub(crate) fn prev_mono(
|
||||
&self,
|
||||
curr: usize,
|
||||
backwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
let curr = &self.all[curr];
|
||||
|
||||
let prev = match curr.borrow().mono_idx {
|
||||
MonoIndex::Centre(idx) => idx
|
||||
.checked_sub(backwards_idx + 1)
|
||||
.and_then(|idx| self.centres.get(idx))?,
|
||||
MonoIndex::Rim(idx) => idx
|
||||
.checked_sub(backwards_idx + 1)
|
||||
.and_then(|idx| self.rims.get(idx))?,
|
||||
MonoIndex::None => return None,
|
||||
};
|
||||
|
||||
self.all.get(*prev)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn next_mono(
|
||||
&self,
|
||||
curr: usize,
|
||||
forwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
let curr = &self.all[curr];
|
||||
|
||||
let next = match curr.borrow().mono_idx {
|
||||
MonoIndex::Centre(idx) => self.centres.get(idx + (forwards_idx + 1))?,
|
||||
MonoIndex::Rim(idx) => self.rims.get(idx + (forwards_idx + 1))?,
|
||||
MonoIndex::None => return None,
|
||||
};
|
||||
|
||||
self.all.get(*next)
|
||||
}
|
||||
|
||||
pub(crate) fn prev_note(
|
||||
&self,
|
||||
curr: usize,
|
||||
backwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
let curr = &self.all[curr];
|
||||
let note_idx = curr.borrow().note_idx?;
|
||||
let idx = note_idx.checked_sub(backwards_idx + 1)?;
|
||||
let prev = self.notes.get(idx)?;
|
||||
|
||||
self.all.get(*prev)
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub(crate) fn next_note(
|
||||
&self,
|
||||
curr: usize,
|
||||
forwards_idx: usize,
|
||||
) -> Option<&'_ Rc<RefCell<TaikoDifficultyObject>>> {
|
||||
let curr = &self.all[curr];
|
||||
let note_idx = curr.borrow().note_idx?;
|
||||
let idx = note_idx + (forwards_idx + 1);
|
||||
let prev = self.notes.get(idx)?;
|
||||
|
||||
self.all.get(*prev)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) enum MonoIndex {
|
||||
Centre(usize),
|
||||
Rim(usize),
|
||||
None,
|
||||
}
|
||||
|
||||
@@ -41,17 +41,17 @@ use super::{
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TaikoGradualDifficultyAttributes<'map> {
|
||||
pub struct TaikoGradualDifficultyAttributes {
|
||||
pub(crate) idx: usize,
|
||||
attrs: TaikoDifficultyAttributes,
|
||||
hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject<'map>>>>,
|
||||
lists: ObjectLists<'map>,
|
||||
hit_objects: IntoIter<Rc<RefCell<TaikoDifficultyObject>>>,
|
||||
lists: ObjectLists,
|
||||
peaks: Peaks,
|
||||
}
|
||||
|
||||
impl<'map> TaikoGradualDifficultyAttributes<'map> {
|
||||
impl TaikoGradualDifficultyAttributes {
|
||||
/// Create a new difficulty attributes iterator for osu!taiko maps.
|
||||
pub fn new(map: &'map Beatmap, mods: u32) -> Self {
|
||||
pub fn new(map: &Beatmap, mods: u32) -> Self {
|
||||
let peaks = Peaks::new();
|
||||
let clock_rate = mods.clock_rate();
|
||||
|
||||
@@ -75,13 +75,20 @@ impl<'map> TaikoGradualDifficultyAttributes<'map> {
|
||||
.taiko_objects()
|
||||
.enumerate()
|
||||
.skip(2)
|
||||
.zip(map.taiko_objects().skip(1))
|
||||
.zip(map.taiko_objects())
|
||||
.zip(map.hit_objects.iter().skip(1))
|
||||
.zip(map.hit_objects.iter())
|
||||
.fold(
|
||||
ObjectLists::default(),
|
||||
|mut lists, (((idx, base), last), last_last)| {
|
||||
let diff_obj =
|
||||
TaikoDifficultyObject::new(base, last, last_last, clock_rate, &lists, idx);
|
||||
|mut lists, (((idx, (base, base_start_time)), last), last_last)| {
|
||||
let diff_obj = TaikoDifficultyObject::new(
|
||||
base,
|
||||
base_start_time,
|
||||
last.start_time,
|
||||
last_last.start_time,
|
||||
clock_rate,
|
||||
&lists,
|
||||
idx,
|
||||
);
|
||||
|
||||
match &diff_obj.mono_idx {
|
||||
MonoIndex::Centre(_) => lists.centres.push(idx),
|
||||
@@ -111,7 +118,7 @@ impl<'map> TaikoGradualDifficultyAttributes<'map> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for TaikoGradualDifficultyAttributes<'_> {
|
||||
impl Iterator for TaikoGradualDifficultyAttributes {
|
||||
type Item = TaikoDifficultyAttributes;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@@ -120,7 +127,7 @@ impl Iterator for TaikoGradualDifficultyAttributes<'_> {
|
||||
{
|
||||
let curr = curr.borrow();
|
||||
self.peaks.process(&curr, &self.lists);
|
||||
self.attrs.max_combo += curr.base.h.is_circle() as usize;
|
||||
self.attrs.max_combo += curr.base.is_hit as usize;
|
||||
}
|
||||
|
||||
let PeaksDifficultyValues {
|
||||
@@ -169,7 +176,7 @@ impl Iterator for TaikoGradualDifficultyAttributes<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for TaikoGradualDifficultyAttributes<'_> {
|
||||
impl ExactSizeIterator for TaikoGradualDifficultyAttributes {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.hit_objects.len()
|
||||
|
||||
@@ -134,7 +134,7 @@ impl TaikoScoreState {
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TaikoGradualPerformanceAttributes<'map> {
|
||||
difficulty: TaikoGradualDifficultyAttributes<'map>,
|
||||
difficulty: TaikoGradualDifficultyAttributes,
|
||||
performance: TaikoPP<'map>,
|
||||
}
|
||||
|
||||
|
||||
+14
-7
@@ -232,15 +232,22 @@ fn calculate_skills(params: TaikoStars<'_>) -> (Peaks, usize) {
|
||||
.taiko_objects()
|
||||
.take(take)
|
||||
.skip(2)
|
||||
.zip(map.taiko_objects().skip(1))
|
||||
.zip(map.taiko_objects())
|
||||
.zip(map.hit_objects.iter().skip(1))
|
||||
.zip(map.hit_objects.iter())
|
||||
.enumerate()
|
||||
.inspect(|(_, ((base, _), _))| max_combo += base.h.is_circle() as usize)
|
||||
.inspect(|(_, (((base, _), _), _))| max_combo += base.is_hit as usize)
|
||||
.fold(
|
||||
ObjectLists::default(),
|
||||
|mut lists, (idx, ((base, last), last_last))| {
|
||||
let diff_obj =
|
||||
TaikoDifficultyObject::new(base, last, last_last, clock_rate, &lists, idx);
|
||||
|mut lists, (idx, (((base, base_start_time), last), last_last))| {
|
||||
let diff_obj = TaikoDifficultyObject::new(
|
||||
base,
|
||||
base_start_time,
|
||||
last.start_time,
|
||||
last_last.start_time,
|
||||
clock_rate,
|
||||
&lists,
|
||||
idx,
|
||||
);
|
||||
|
||||
match &diff_obj.mono_idx {
|
||||
MonoIndex::Centre(_) => lists.centres.push(idx),
|
||||
@@ -272,7 +279,7 @@ fn rescale(stars: f64) -> f64 {
|
||||
if stars < 0.0 {
|
||||
stars
|
||||
} else {
|
||||
10.43 * (stars / 8.0).ln_1p()
|
||||
10.43 * (stars / 8.0 + 1.0).ln()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ impl Colour {
|
||||
}
|
||||
|
||||
impl Skill for Colour {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
|
||||
<Self as StrainSkill>::process(self, curr, hit_objects)
|
||||
}
|
||||
|
||||
@@ -52,15 +52,11 @@ impl StrainSkill for Colour {
|
||||
&mut self.curr_section_end
|
||||
}
|
||||
|
||||
fn strain_value_at(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64 {
|
||||
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
|
||||
<Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects)
|
||||
}
|
||||
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 {
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
<Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr)
|
||||
}
|
||||
}
|
||||
@@ -77,7 +73,7 @@ impl StrainDecaySkill for Colour {
|
||||
&mut self.curr_strain
|
||||
}
|
||||
|
||||
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject<'_>, _: &ObjectLists<'_>) -> f64 {
|
||||
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, _: &ObjectLists) -> f64 {
|
||||
ColourEvaluator::evaluate_diff_of(curr)
|
||||
}
|
||||
}
|
||||
@@ -91,7 +87,7 @@ impl ColourEvaluator {
|
||||
sigmoid * (height / 2.0) + middle
|
||||
}
|
||||
|
||||
fn evaluate_diff_of_mono_streak(mono_streak: Rc<RefCell<MonoStreak<'_>>>) -> f64 {
|
||||
fn evaluate_diff_of_mono_streak(mono_streak: Rc<RefCell<MonoStreak>>) -> f64 {
|
||||
let mono_streak = mono_streak.borrow();
|
||||
|
||||
let parent_eval = mono_streak
|
||||
@@ -104,7 +100,7 @@ impl ColourEvaluator {
|
||||
}
|
||||
|
||||
fn evaluate_diff_of_alternating_mono_pattern(
|
||||
alternating_mono_pattern: Rc<RefCell<AlternatingMonoPattern<'_>>>,
|
||||
alternating_mono_pattern: Rc<RefCell<AlternatingMonoPattern>>,
|
||||
) -> f64 {
|
||||
let alternating_mono_pattern = alternating_mono_pattern.borrow();
|
||||
|
||||
@@ -118,14 +114,14 @@ impl ColourEvaluator {
|
||||
}
|
||||
|
||||
fn evaluate_diff_of_repeating_hit_patterns(
|
||||
repeating_hit_patterns: Rc<RefCell<RepeatingHitPatterns<'_>>>,
|
||||
repeating_hit_patterns: Rc<RefCell<RepeatingHitPatterns>>,
|
||||
) -> f64 {
|
||||
let repetition_interval = repeating_hit_patterns.borrow().repetition_interval as f64;
|
||||
|
||||
2.0 * (1.0 - Self::sigmoid(repetition_interval, 2.0, 2.0, 0.5, 1.0))
|
||||
}
|
||||
|
||||
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject<'_>) -> f64 {
|
||||
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject) -> f64 {
|
||||
let colour = &hit_object.colour;
|
||||
let mut difficulty = 0.0;
|
||||
|
||||
|
||||
+10
-22
@@ -13,7 +13,7 @@ use super::{
|
||||
};
|
||||
|
||||
pub(crate) trait Skill: Sized {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>);
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists);
|
||||
fn difficulty_value(self) -> f64;
|
||||
}
|
||||
|
||||
@@ -24,22 +24,18 @@ pub(crate) trait StrainSkill: Skill {
|
||||
fn curr_section_peak(&mut self) -> &mut f64;
|
||||
fn curr_section_end(&mut self) -> &mut f64;
|
||||
|
||||
fn strain_value_at(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64;
|
||||
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64;
|
||||
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64;
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64;
|
||||
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
|
||||
// * The first object doesn't generate a strain, so we begin with an incremented section end
|
||||
if curr.idx == 0 {
|
||||
let section_len = SECTION_LEN as f64;
|
||||
*self.curr_section_end() = (curr.base.h.start_time / section_len).ceil() * section_len;
|
||||
*self.curr_section_end() = (curr.start_time / section_len).ceil() * section_len;
|
||||
}
|
||||
|
||||
while curr.base.h.start_time > *self.curr_section_end() {
|
||||
while curr.start_time > *self.curr_section_end() {
|
||||
self.save_curr_peak();
|
||||
|
||||
{
|
||||
@@ -60,7 +56,7 @@ pub(crate) trait StrainSkill: Skill {
|
||||
self.strain_peaks_mut().push(peak);
|
||||
}
|
||||
|
||||
fn start_new_section_from(&mut self, time: f64, curr: &TaikoDifficultyObject<'_>) {
|
||||
fn start_new_section_from(&mut self, time: f64, curr: &TaikoDifficultyObject) {
|
||||
// * The maximum strain of the new section is not zero by default
|
||||
// * This means we need to capture the strain level at the beginning of the new section,
|
||||
// * and use that as the initial peak level.
|
||||
@@ -104,21 +100,13 @@ pub(crate) trait StrainDecaySkill: StrainSkill {
|
||||
fn curr_strain(&self) -> f64;
|
||||
fn curr_strain_mut(&mut self) -> &mut f64;
|
||||
|
||||
fn strain_value_of(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64;
|
||||
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64;
|
||||
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 {
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
self.curr_strain() * self.strain_decay(time - curr.prev_time)
|
||||
}
|
||||
|
||||
fn strain_value_at(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64 {
|
||||
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
|
||||
*self.curr_strain_mut() *= self.strain_decay(curr.delta);
|
||||
*self.curr_strain_mut() += self.strain_value_of(curr, hit_objects) * Self::SKILL_MULTIPLIER;
|
||||
|
||||
|
||||
+12
-13
@@ -28,18 +28,17 @@ impl Peaks {
|
||||
|
||||
pub(crate) fn difficulty_values(self) -> PeaksDifficultyValues {
|
||||
let colour_rating =
|
||||
StrainSkill::difficulty_value(self.colour) * Self::COLOUR_SKILL_MULTIPLIER;
|
||||
// let rhythm_rating =
|
||||
// StrainSkill::difficulty_value(self.rhythm.clone()) * Self::RHYTHM_SKILL_MULTIPLIER;
|
||||
// let stamina_rating =
|
||||
// StrainSkill::difficulty_value(self.stamina.clone()) * Self::STAMINA_SKILL_MULTIPLIER;
|
||||
StrainSkill::difficulty_value(self.colour.clone()) * Self::COLOUR_SKILL_MULTIPLIER;
|
||||
let rhythm_rating =
|
||||
StrainSkill::difficulty_value(self.rhythm.clone()) * Self::RHYTHM_SKILL_MULTIPLIER;
|
||||
let stamina_rating =
|
||||
StrainSkill::difficulty_value(self.stamina.clone()) * Self::STAMINA_SKILL_MULTIPLIER;
|
||||
|
||||
PeaksDifficultyValues {
|
||||
colour_rating,
|
||||
rhythm_rating: 0.0,
|
||||
stamina_rating: 0.0,
|
||||
// combined_rating: self.difficulty_value(),
|
||||
combined_rating: 0.0,
|
||||
rhythm_rating,
|
||||
stamina_rating,
|
||||
combined_rating: self.difficulty_value(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +59,10 @@ impl Peaks {
|
||||
}
|
||||
|
||||
impl Skill for Peaks {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) {
|
||||
StrainSkill::process(&mut self.colour, curr, hit_objects);
|
||||
StrainSkill::process(&mut self.rhythm, curr, hit_objects);
|
||||
StrainSkill::process(&mut self.stamina, curr, hit_objects);
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
|
||||
<Colour as Skill>::process(&mut self.colour, curr, hit_objects);
|
||||
<Rhythm as Skill>::process(&mut self.rhythm, curr, hit_objects);
|
||||
<Stamina as Skill>::process(&mut self.stamina, curr, hit_objects);
|
||||
}
|
||||
|
||||
fn difficulty_value(self) -> f64 {
|
||||
|
||||
@@ -40,7 +40,7 @@ impl Rhythm {
|
||||
self.notes_since_rhythm_change = 0;
|
||||
}
|
||||
|
||||
fn repetition_penalties(&mut self, hit_object: &TaikoDifficultyObject<'_>) -> f64 {
|
||||
fn repetition_penalties(&mut self, hit_object: &TaikoDifficultyObject) -> f64 {
|
||||
let mut penalty = 1.0;
|
||||
|
||||
self.history.push(HistoryElement::new(hit_object));
|
||||
@@ -100,7 +100,7 @@ impl Rhythm {
|
||||
}
|
||||
|
||||
impl Skill for Rhythm {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
|
||||
<Self as StrainSkill>::process(self, curr, hit_objects)
|
||||
}
|
||||
|
||||
@@ -122,15 +122,11 @@ impl StrainSkill for Rhythm {
|
||||
&mut self.curr_section_end
|
||||
}
|
||||
|
||||
fn strain_value_at(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64 {
|
||||
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
|
||||
<Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects)
|
||||
}
|
||||
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 {
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
<Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr)
|
||||
}
|
||||
}
|
||||
@@ -147,8 +143,8 @@ impl StrainDecaySkill for Rhythm {
|
||||
&mut self.curr_decay_strain
|
||||
}
|
||||
|
||||
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject<'_>, _: &ObjectLists<'_>) -> f64 {
|
||||
let base_is_circle = curr.base.h.is_circle();
|
||||
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, _: &ObjectLists) -> f64 {
|
||||
let base_is_circle = curr.base.is_hit;
|
||||
|
||||
// * drum rolls and swells are exempt.
|
||||
if !base_is_circle {
|
||||
@@ -187,7 +183,7 @@ pub(crate) struct HistoryElement {
|
||||
}
|
||||
|
||||
impl HistoryElement {
|
||||
fn new(difficulty_object: &TaikoDifficultyObject<'_>) -> Self {
|
||||
fn new(difficulty_object: &TaikoDifficultyObject) -> Self {
|
||||
Self {
|
||||
idx: difficulty_object.idx,
|
||||
rhythm: difficulty_object.rhythm,
|
||||
|
||||
@@ -22,7 +22,7 @@ impl Stamina {
|
||||
}
|
||||
|
||||
impl Skill for Stamina {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject<'_>, hit_objects: &ObjectLists<'_>) {
|
||||
fn process(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) {
|
||||
<Self as StrainSkill>::process(self, curr, hit_objects)
|
||||
}
|
||||
|
||||
@@ -44,15 +44,11 @@ impl StrainSkill for Stamina {
|
||||
&mut self.curr_section_end
|
||||
}
|
||||
|
||||
fn strain_value_at(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64 {
|
||||
fn strain_value_at(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
|
||||
<Self as StrainDecaySkill>::strain_value_at(self, curr, hit_objects)
|
||||
}
|
||||
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject<'_>) -> f64 {
|
||||
fn calculate_initial_strain(&self, time: f64, curr: &TaikoDifficultyObject) -> f64 {
|
||||
<Self as StrainDecaySkill>::calculate_initial_strain(self, time, curr)
|
||||
}
|
||||
}
|
||||
@@ -69,11 +65,7 @@ impl StrainDecaySkill for Stamina {
|
||||
&mut self.curr_strain
|
||||
}
|
||||
|
||||
fn strain_value_of(
|
||||
&mut self,
|
||||
curr: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64 {
|
||||
fn strain_value_of(&mut self, curr: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
|
||||
StaminaEvaluator::evaluate_diff_of(curr, hit_objects)
|
||||
}
|
||||
}
|
||||
@@ -90,11 +82,8 @@ impl StaminaEvaluator {
|
||||
30.0 / interval
|
||||
}
|
||||
|
||||
fn evaluate_diff_of(
|
||||
hit_object: &TaikoDifficultyObject<'_>,
|
||||
hit_objects: &ObjectLists<'_>,
|
||||
) -> f64 {
|
||||
if !hit_object.base.is_hit() {
|
||||
fn evaluate_diff_of(hit_object: &TaikoDifficultyObject, hit_objects: &ObjectLists) -> f64 {
|
||||
if !hit_object.base.is_hit {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@@ -104,7 +93,7 @@ impl StaminaEvaluator {
|
||||
|
||||
if let Some(key_prev) = key_prev {
|
||||
// * Add a base strain to all objects
|
||||
0.5 + Self::speed_bonus(curr.base.h.start_time - key_prev.borrow().base.h.start_time)
|
||||
0.5 + Self::speed_bonus(curr.start_time - key_prev.borrow().start_time)
|
||||
} else {
|
||||
// * There is no previous hit object hit by the current key
|
||||
0.0
|
||||
|
||||
+14
-15
@@ -5,18 +5,17 @@ use crate::{parse::HitObject, Beatmap};
|
||||
use super::rim::Rim;
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct TaikoObject<'h> {
|
||||
pub(crate) h: &'h HitObject,
|
||||
pub(crate) sound: u8,
|
||||
pub(crate) struct TaikoObject {
|
||||
pub(crate) is_hit: bool,
|
||||
pub(crate) is_rim: bool,
|
||||
}
|
||||
|
||||
impl TaikoObject<'_> {
|
||||
pub(crate) fn is_rim(&self) -> bool {
|
||||
self.sound.is_rim()
|
||||
}
|
||||
|
||||
pub(crate) fn is_hit(&self) -> bool {
|
||||
self.h.is_circle()
|
||||
impl TaikoObject {
|
||||
pub(crate) fn new(h: &HitObject, sound: u8) -> Self {
|
||||
Self {
|
||||
is_hit: h.is_circle(),
|
||||
is_rim: sound.is_rim(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,14 +40,14 @@ impl IntoTaikoObjectIter for Beatmap {
|
||||
}
|
||||
|
||||
impl<'m> Iterator for TaikoObjectIter<'m> {
|
||||
type Item = TaikoObject<'m>;
|
||||
type Item = (TaikoObject, f64);
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(TaikoObject {
|
||||
h: self.hit_objects.next()?,
|
||||
sound: *self.sounds.next()?,
|
||||
})
|
||||
let h = self.hit_objects.next()?;
|
||||
let sound = self.sounds.next()?;
|
||||
|
||||
Some((TaikoObject::new(h, *sound), h.start_time))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
||||
+10
-4
@@ -9,14 +9,20 @@ pub(crate) struct TandemSorter {
|
||||
impl TandemSorter {
|
||||
/// Sort indices based on the given slice.
|
||||
/// Note that this does **not** sort the given slice.
|
||||
pub(crate) fn new<T>(slice: &[T]) -> Self
|
||||
pub(crate) fn new<T>(slice: &[T], stable: bool) -> Self
|
||||
where
|
||||
T: PartialOrd,
|
||||
{
|
||||
let mut indices: Vec<_> = (0..).take(slice.len()).collect();
|
||||
|
||||
indices
|
||||
.sort_unstable_by(|&i, &j| slice[i].partial_cmp(&slice[j]).unwrap_or(Ordering::Equal));
|
||||
let closure =
|
||||
|&i: &usize, &j: &usize| slice[i].partial_cmp(&slice[j]).unwrap_or(Ordering::Equal);
|
||||
|
||||
if stable {
|
||||
indices.sort_by(closure);
|
||||
} else {
|
||||
indices.sort_unstable_by(closure);
|
||||
}
|
||||
|
||||
Self { indices }
|
||||
}
|
||||
@@ -75,7 +81,7 @@ mod tests {
|
||||
#[test]
|
||||
fn sort() {
|
||||
let mut base = vec![9, 7, 8, 1, 4, 3, 5, 2];
|
||||
let mut sorter = TandemSorter::new(&base);
|
||||
let mut sorter = TandemSorter::new(&base, false);
|
||||
|
||||
sorter.sort(&mut base);
|
||||
assert_eq!(base, vec![1, 2, 3, 4, 5, 7, 8, 9]);
|
||||
|
||||
Reference in New Issue
Block a user