From 757b57bfa0098e582a94a370aa7309b0bb09c258 Mon Sep 17 00:00:00 2001 From: MaxOhn Date: Mon, 11 Nov 2024 12:07:40 +0100 Subject: [PATCH] feat: support horizontal flipping --- src/model/mods.rs | 46 ++++++++++++++++++++++++++++++++++- src/osu/convert.rs | 18 +++++++++----- src/osu/difficulty/gradual.rs | 2 +- src/osu/difficulty/mod.rs | 2 +- src/osu/object.rs | 40 ++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/model/mods.rs b/src/model/mods.rs index 3ca1512f..f32a38fc 100644 --- a/src/model/mods.rs +++ b/src/model/mods.rs @@ -179,7 +179,7 @@ impl_has_mod! { } impl GameMods { - pub fn no_slider_head_acc(&self, lazer: bool) -> bool { + pub(crate) fn no_slider_head_acc(&self, lazer: bool) -> bool { match self.inner { GameModsInner::Lazer(ref mods) => mods .iter() @@ -196,6 +196,42 @@ impl GameMods { GameModsInner::Legacy(_) => !lazer, } } + + pub(crate) fn reflection(&self) -> Reflection { + match self.inner { + GameModsInner::Lazer(ref mods) => { + if mods.contains_intermode(GameModIntermode::HardRock) { + return Reflection::Vertical; + } + + mods.iter() + .find_map(|m| match m { + GameMod::MirrorOsu(mirror) => Some(mirror), + _ => None, + }) + .map_or(Reflection::None, |mr| match mr.reflection.as_deref() { + Some("Horizontal") | None => Reflection::Horizontal, + Some("Vertical") => Reflection::Vertical, + Some("Both") => Reflection::Both, + Some(_) => Reflection::None, + }) + } + GameModsInner::Intermode(ref mods) => { + if mods.contains(GameModIntermode::HardRock) { + Reflection::Vertical + } else { + Reflection::None + } + } + GameModsInner::Legacy(mods) => { + if mods.contains(GameModsLegacy::HardRock) { + Reflection::Vertical + } else { + Reflection::None + } + } + } + } } impl Default for GameMods { @@ -244,3 +280,11 @@ impl From for GameMods { GameModsLegacy::from_bits(bits).into() } } + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Reflection { + None, + Vertical, + Horizontal, + Both, +} diff --git a/src/osu/convert.rs b/src/osu/convert.rs index c5269b10..30e33f5b 100644 --- a/src/osu/convert.rs +++ b/src/osu/convert.rs @@ -3,6 +3,7 @@ use rosu_map::section::{general::GameMode, hit_objects::CurveBuffers}; use crate::model::{ beatmap::{Beatmap, Converted}, mode::ConvertStatus, + mods::Reflection, }; use super::{ @@ -30,7 +31,7 @@ pub fn try_convert(map: &mut Beatmap) -> ConvertStatus { pub fn convert_objects( converted: &OsuBeatmap<'_>, scaling_factor: &ScalingFactor, - hr: bool, + reflection: Reflection, time_preempt: f64, mut take: usize, attrs: &mut OsuDifficultyAttributes, @@ -63,12 +64,17 @@ pub fn convert_objects( }) .collect(); - if hr { - osu_objects + match reflection { + Reflection::None => osu_objects.iter_mut().for_each(OsuObject::finalize_nested), + Reflection::Vertical => osu_objects .iter_mut() - .for_each(OsuObject::reflect_vertically); - } else { - osu_objects.iter_mut().for_each(OsuObject::finalize_nested); + .for_each(OsuObject::reflect_vertically), + Reflection::Horizontal => osu_objects + .iter_mut() + .for_each(OsuObject::reflect_horizontally), + Reflection::Both => osu_objects + .iter_mut() + .for_each(OsuObject::reflect_both_axes), } let stack_threshold = time_preempt * f64::from(converted.stack_leniency); diff --git a/src/osu/difficulty/gradual.rs b/src/osu/difficulty/gradual.rs index 3e3b7548..bf314ec8 100644 --- a/src/osu/difficulty/gradual.rs +++ b/src/osu/difficulty/gradual.rs @@ -84,7 +84,7 @@ impl OsuGradualDifficulty { let osu_objects = convert_objects( converted, &scaling_factor, - mods.hr(), + mods.reflection(), time_preempt, converted.hit_objects.len(), &mut attrs, diff --git a/src/osu/difficulty/mod.rs b/src/osu/difficulty/mod.rs index e358bba8..f8ec0ab2 100644 --- a/src/osu/difficulty/mod.rs +++ b/src/osu/difficulty/mod.rs @@ -114,7 +114,7 @@ impl DifficultyValues { let mut osu_objects = convert_objects( converted, &scaling_factor, - mods.hr(), + mods.reflection(), time_preempt, take, &mut attrs, diff --git a/src/osu/object.rs b/src/osu/object.rs index 6d929c51..085e9cf0 100644 --- a/src/osu/object.rs +++ b/src/osu/object.rs @@ -77,6 +77,46 @@ impl OsuObject { } } + pub fn reflect_horizontally(&mut self) { + fn reflect_x(x: &mut f32) { + *x = PLAYFIELD_BASE_SIZE.x - *x; + } + + reflect_x(&mut self.pos.x); + + if let OsuObjectKind::Slider(ref mut slider) = self.kind { + // Requires `stack_offset` so we can't add `h.pos` just yet + slider.lazy_end_pos.x = -slider.lazy_end_pos.x; + + for nested in slider.nested_objects.iter_mut() { + let mut nested_pos = self.pos; // already reflected at this point + nested_pos += Pos::new(-nested.pos.x, nested.pos.y); + nested.pos = nested_pos; + } + } + } + + pub fn reflect_both_axes(&mut self) { + fn reflect(pos: &mut Pos) { + pos.x = PLAYFIELD_BASE_SIZE.x - pos.x; + pos.y = PLAYFIELD_BASE_SIZE.y - pos.y; + } + + reflect(&mut self.pos); + + if let OsuObjectKind::Slider(ref mut slider) = self.kind { + // Requires `stack_offset` so we can't add `h.pos` just yet + slider.lazy_end_pos.x = -slider.lazy_end_pos.x; + slider.lazy_end_pos.y = -slider.lazy_end_pos.y; + + for nested in slider.nested_objects.iter_mut() { + let mut nested_pos = self.pos; // already reflected at this point + nested_pos += Pos::new(-nested.pos.x, -nested.pos.y); + nested.pos = nested_pos; + } + } + } + pub fn finalize_nested(&mut self) { if let OsuObjectKind::Slider(ref mut slider) = self.kind { for nested in slider.nested_objects.iter_mut() {