From bd2d6f3c7129fde5f98b19cf59513c5c2f7a906a Mon Sep 17 00:00:00 2001 From: Pants Date: Thu, 22 Feb 2024 18:34:09 -0500 Subject: [PATCH] Add "Whiplash" mod for osu! ruleset --- osu.Game.Rulesets.Osu/Mods/OsuModWhiplash.cs | 93 ++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModWhiplash.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWhiplash.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWhiplash.cs new file mode 100644 index 0000000000..b1c0e3042b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWhiplash.cs @@ -0,0 +1,93 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Framework.Localisation; +using osu.Game.Beatmaps; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModWhiplash : Mod, IApplicableToHitObject, IApplicableToBeatmapProcessor + { + public override string Name => "Whiplash"; + public override string Acronym => "WL"; + public override LocalisableString Description => "Am I rushing.. or am I dragging?"; + public override ModType Type => ModType.Conversion; + + private IBeatmap? currentBeatmap { get; set; } + + [SettingSource("Target BPM", "Target BPM to center the changes around.")] + public Bindable TargetBPM { get; } = new Bindable(TargetBPMStyle.MaxBPM); + + public override double ScoreMultiplier => TargetBPM.Value == TargetBPMStyle.MaxBPM ? 1.0 : 0.75; + + public void ApplyToHitObject(HitObject hitObject) + { + if (currentBeatmap == null || hitObject is not OsuHitObject hitCircle) + return; + + double currentBpm = currentBeatmap.ControlPointInfo.TimingPointAt(hitCircle.StartTime).BPM; + + if (currentBpm < 1) + { + updateTimePreempty(hitCircle, hitCircle.StartTime); + return; + } + + double scaleBpm = getBaseBPM(currentBeatmap); + double preempt = hitCircle.TimePreempt; + double newTimePreempt = (scaleBpm / Math.Max(currentBpm, 1)) * preempt; + + //don't allow AR to go over 11 + updateTimePreempty(hitCircle, Math.Max(newTimePreempt, 300)); + } + + private void updateTimePreempty(OsuHitObject hitCircle, double value) + { + hitCircle.TimePreempt = value; + + if (hitCircle is Slider slider) + { + slider.HeadCircle.TimePreempt = value; + } + } + + private double getBaseBPM(IBeatmap beatmap) + { + switch (TargetBPM.Value) + { + case TargetBPMStyle.MainBPM: + return beatmap.BeatmapInfo.BPM; + + case TargetBPMStyle.MaxBPM: + return beatmap.ControlPointInfo.BPMMaximum; + + case TargetBPMStyle.MinBPM: + return beatmap.ControlPointInfo.BPMMinimum; + + default: + throw new ArgumentOutOfRangeException(nameof(TargetBPM), TargetBPM, @"Unsupported BPM scale"); + } + } + + public void ApplyToBeatmapProcessor(IBeatmapProcessor beatmapProcessor) + { + if (currentBeatmap != beatmapProcessor.Beatmap) + { + currentBeatmap = beatmapProcessor.Beatmap; + } + } + + public enum TargetBPMStyle + { + MainBPM, + MinBPM, + MaxBPM, + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 6752712be1..260e9191c5 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -183,7 +183,8 @@ namespace osu.Game.Rulesets.Osu new OsuModClassic(), new OsuModRandom(), new OsuModMirror(), - new MultiMod(new OsuModAlternate(), new OsuModSingleTap()) + new MultiMod(new OsuModAlternate(), new OsuModSingleTap()), + new OsuModWhiplash() }; case ModType.Automation: