From 960d552dc1c1f2c34f6ac4cb79f652dce8fe70b5 Mon Sep 17 00:00:00 2001 From: Nathan Du Date: Fri, 28 Jun 2024 19:43:45 +0800 Subject: [PATCH] Initial implemention of the No Release mod This commit adds a new osu!mania mod No Release that relaxes tail judgements. The current implementation automatically awards Perfect (or Meh if the hold note is broken midway) for a hold note tail at the end of its Perfect window, as long as it is held by then. Tests are pending for the next commit. --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 1 + .../Mods/ManiaModNoRelease.cs | 35 +++++++++++++++++++ .../Objects/Drawables/DrawableHoldNoteTail.cs | 24 +++++++++++-- 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 40eb44944c..667002533d 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -241,6 +241,7 @@ namespace osu.Game.Rulesets.Mania new ManiaModEasy(), new ManiaModNoFail(), new MultiMod(new ManiaModHalfTime(), new ManiaModDaycore()), + new ManiaModNoRelease(), }; case ModType.DifficultyIncrease: diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs new file mode 100644 index 0000000000..f370ef15bd --- /dev/null +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNoRelease.cs @@ -0,0 +1,35 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; +using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mania.Mods +{ + public class ManiaModNoRelease : Mod, IApplicableToDrawableHitObject + { + public override string Name => "No Release"; + + public override string Acronym => "NR"; + + public override LocalisableString Description => "No more timing the end of hold notes."; + + public override double ScoreMultiplier => 0.9; + + public override ModType Type => ModType.DifficultyReduction; + + public void ApplyToDrawableHitObject(DrawableHitObject drawable) + { + if (drawable is DrawableHoldNote hold) + { + hold.HitObjectApplied += dho => + { + ((DrawableHoldNote)dho).Tail.LateReleaseResult = HitResult.Perfect; + }; + } + } + } +} diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs index 79002b3819..eb1637b0ea 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNoteTail.cs @@ -3,6 +3,7 @@ #nullable disable +using System.Diagnostics; using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Rulesets.Scoring; @@ -18,6 +19,11 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables protected internal DrawableHoldNote HoldNote => (DrawableHoldNote)ParentHitObject; + /// + /// The minimum uncapped result for a late release. + /// + public HitResult LateReleaseResult { get; set; } = HitResult.Miss; + public DrawableHoldNoteTail() : this(null) { @@ -32,9 +38,23 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables public void UpdateResult() => base.UpdateResult(true); - protected override void CheckForResult(bool userTriggered, double timeOffset) => + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + Debug.Assert(HitObject.HitWindows != null); + // Factor in the release lenience - base.CheckForResult(userTriggered, timeOffset / TailNote.RELEASE_WINDOW_LENIENCE); + double scaledTimeOffset = timeOffset / TailNote.RELEASE_WINDOW_LENIENCE; + + // Check for late release + if (HoldNote.HoldStartTime != null && scaledTimeOffset > HitObject.HitWindows.WindowFor(LateReleaseResult)) + { + ApplyResult(GetCappedResult(LateReleaseResult)); + } + else + { + base.CheckForResult(userTriggered, scaledTimeOffset); + } + } protected override HitResult GetCappedResult(HitResult result) {