From 4500a908c229621551945afa483a06133d5d1abd Mon Sep 17 00:00:00 2001 From: Euro20179 Date: Mon, 11 Apr 2022 15:44:56 -0700 Subject: [PATCH 1/4] osu mod: single tap --- osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs | 102 ++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index 46b97dd23b99..fec63a626bee 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -22,7 +22,7 @@ public class OsuModAlternate : Mod, IApplicableToDrawableRuleset, public override string Acronym => @"AL"; public override string Description => @"Don't use the same key twice in a row!"; public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay) }; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModSingleTap) }; public override ModType Type => ModType.Conversion; public override IconUsage? Icon => FontAwesome.Solid.Keyboard; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs new file mode 100644 index 000000000000..3c9af821ef1f --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs @@ -0,0 +1,102 @@ +// 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 System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModSingleTap : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer + { + public override string Name => @"Single Tap"; + public override string Acronym => @"ST"; + public override string Description => @"Only use one key!"; + public override double ScoreMultiplier => 1.0; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAlternate), typeof(OsuModRelax) }; + public override ModType Type => ModType.Conversion; + private double firstObjectValidJudgementTime; + private IBindable isBreakTime; + private const double flash_duration = 1000; + private OsuAction? lastActionPressed; + private DrawableRuleset ruleset; + + private IFrameStableClock gameplayClock; + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + ruleset = drawableRuleset; + drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); + + var firstHitObject = ruleset.Objects.FirstOrDefault(); + firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); + + gameplayClock = drawableRuleset.FrameStableClock; + } + + public void ApplyToPlayer(Player player) + { + isBreakTime = player.IsBreakTime.GetBoundCopy(); + } + + private bool checkCorrectAction(OsuAction action) + { + if (isBreakTime.Value) + return true; + + if (gameplayClock.CurrentTime < firstObjectValidJudgementTime) + return true; + + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + break; + + // Any action which is not left or right button should be ignored. + default: + return true; + } + + if (lastActionPressed == null) + { + lastActionPressed = action; + return true; + } + else if (lastActionPressed == action) + { + return true; + } + + ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); + return false; + } + + private class InputInterceptor : Component, IKeyBindingHandler + { + private readonly OsuModSingleTap mod; + + public InputInterceptor(OsuModSingleTap mod) + { + this.mod = mod; + } + + public bool OnPressed(KeyBindingPressEvent e) + // if the pressed action is incorrect, block it from reaching gameplay. + => !mod.checkCorrectAction(e.Action); + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 207e7a4ab051..9595ad35c9fa 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -170,7 +170,7 @@ public override IEnumerable GetModsFor(ModType type) new OsuModClassic(), new OsuModRandom(), new OsuModMirror(), - new OsuModAlternate(), + new MultiMod(new OsuModAlternate(), new OsuModSingleTap()), }; case ModType.Automation: From 17207c171eb88cbf1a3ad3f4edb0f848cece83ea Mon Sep 17 00:00:00 2001 From: Euro20179 Date: Mon, 11 Apr 2022 21:56:37 -0700 Subject: [PATCH 2/4] abstract alternate/single tap into InputBlocker --- .../Mods/TestSceneOsuModTapBlocking.cs | 285 ++++++++++++++++++ osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 69 +---- osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs | 61 +--- osu.Game/Rulesets/Mods/ModAlternate.cs | 21 ++ osu.Game/Rulesets/Mods/ModInputBlocker.cs | 71 +++++ osu.Game/Rulesets/Mods/ModSingleTap.cs | 18 ++ 6 files changed, 400 insertions(+), 125 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs create mode 100644 osu.Game/Rulesets/Mods/ModAlternate.cs create mode 100644 osu.Game/Rulesets/Mods/ModInputBlocker.cs create mode 100644 osu.Game/Rulesets/Mods/ModSingleTap.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs new file mode 100644 index 000000000000..edd151f6d304 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs @@ -0,0 +1,285 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModTapBlocking : OsuModTestScene + { + [Test] + public void TestInputAtIntroAlternate() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 1000, + Position = new Vector2(100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(200)), + new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), + } + }); + [Test] + public void TestInputAtIntroSingleTap() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking { SingleTap = { Value = true }, Alternate = { Value = false }}, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 1000, + Position = new Vector2(100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(200)), + new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), + } + }); + + [Test] + public void TestInputAlternatingAlternate() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 4, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 1000, + Position = new Vector2(200, 100), + }, + new HitCircle + { + StartTime = 1500, + Position = new Vector2(300, 100), + }, + new HitCircle + { + StartTime = 2000, + Position = new Vector2(400, 100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton), + new OsuReplayFrame(1001, new Vector2(200, 100)), + new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton), + new OsuReplayFrame(1501, new Vector2(300, 100)), + new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton), + new OsuReplayFrame(2001, new Vector2(400, 100)), + } + }); + + [Test] + public void TestInputAlternatingSingleTap() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking { SingleTap = { Value = true}, Alternate = {Value = false}}, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 1000, + Position = new Vector2(200, 100), + }, + new HitCircle + { + StartTime = 1500, + Position = new Vector2(300, 100), + }, + new HitCircle + { + StartTime = 2000, + Position = new Vector2(400, 100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton), + new OsuReplayFrame(1001, new Vector2(200, 100)), + new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton), + new OsuReplayFrame(1501, new Vector2(300, 100)), + new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton), + new OsuReplayFrame(2001, new Vector2(400, 100)), + } + }); + [Test] + public void TestInputSingularAlternate() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 1000, + Position = new Vector2(200, 100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton), + } + }); + [Test] + public void TestInputSingularSingleTap() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking { SingleTap = { Value = true}, Alternate = {Value = false}}, + PassCondition = () => Player.ScoreProcessor.HighestCombo.Value == 2, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 1000, + Position = new Vector2(200, 100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton), + } + }); + + [Test] + public void TestInputSingularWithBreakAlternate() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + Breaks = new List + { + new BreakPeriod(500, 2250), + }, + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 2500, + Position = new Vector2(100), + } + } + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(2501, new Vector2(100)), + } + }); + [Test] + public void TestInputSingularWithBreakSingleTap() => CreateModTest(new ModTestData + { + Mod = new OsuModTapBlocking { SingleTap = { Value = true}, Alternate = {Value = false}}, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, + Autoplay = false, + Beatmap = new Beatmap + { + Breaks = new List + { + new BreakPeriod(500, 2250), + }, + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 2500, + Position = new Vector2(100), + } + } + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(2501, new Vector2(100)), + } + }); + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index fec63a626bee..041e677b1033 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -1,61 +1,16 @@ // 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 System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; + namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAlternate : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer + public class OsuModAlternate : ModAlternate { - public override string Name => @"Alternate"; - public override string Acronym => @"AL"; - public override string Description => @"Don't use the same key twice in a row!"; - public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModSingleTap) }; - public override ModType Type => ModType.Conversion; - public override IconUsage? Icon => FontAwesome.Solid.Keyboard; - - private double firstObjectValidJudgementTime; - private IBindable isBreakTime; - private const double flash_duration = 1000; - private OsuAction? lastActionPressed; - private DrawableRuleset ruleset; - - private IFrameStableClock gameplayClock; - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - ruleset = drawableRuleset; - drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); - - var firstHitObject = ruleset.Objects.FirstOrDefault(); - firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); - - gameplayClock = drawableRuleset.FrameStableClock; - } - - public void ApplyToPlayer(Player player) - { - isBreakTime = player.IsBreakTime.GetBoundCopy(); - isBreakTime.ValueChanged += e => - { - if (e.NewValue) - lastActionPressed = null; - }; - } - - private bool checkCorrectAction(OsuAction action) + public override bool checkCorrectAction(OsuAction action) { if (isBreakTime.Value) return true; @@ -84,23 +39,5 @@ private bool checkCorrectAction(OsuAction action) ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); return false; } - - private class InputInterceptor : Component, IKeyBindingHandler - { - private readonly OsuModAlternate mod; - - public InputInterceptor(OsuModAlternate mod) - { - this.mod = mod; - } - - public bool OnPressed(KeyBindingPressEvent e) - // if the pressed action is incorrect, block it from reaching gameplay. - => !mod.checkCorrectAction(e.Action); - - public void OnReleased(KeyBindingReleaseEvent e) - { - } - } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs index 3c9af821ef1f..f334ca07eafb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs @@ -1,54 +1,15 @@ // 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 System.Linq; -using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; -using osu.Framework.Input.Events; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; -using osu.Game.Rulesets.UI; -using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModSingleTap : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer + public class OsuModSingleTap : ModSingleTap { - public override string Name => @"Single Tap"; - public override string Acronym => @"ST"; - public override string Description => @"Only use one key!"; - public override double ScoreMultiplier => 1.0; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAlternate), typeof(OsuModRelax) }; - public override ModType Type => ModType.Conversion; - private double firstObjectValidJudgementTime; - private IBindable isBreakTime; - private const double flash_duration = 1000; - private OsuAction? lastActionPressed; - private DrawableRuleset ruleset; - - private IFrameStableClock gameplayClock; - - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) - { - ruleset = drawableRuleset; - drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); - - var firstHitObject = ruleset.Objects.FirstOrDefault(); - firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); - - gameplayClock = drawableRuleset.FrameStableClock; - } - - public void ApplyToPlayer(Player player) - { - isBreakTime = player.IsBreakTime.GetBoundCopy(); - } - - private bool checkCorrectAction(OsuAction action) + public override bool checkCorrectAction(OsuAction action) { if (isBreakTime.Value) return true; @@ -80,23 +41,5 @@ private bool checkCorrectAction(OsuAction action) ruleset.Cursor.FlashColour(Colour4.Red, flash_duration, Easing.OutQuint); return false; } - - private class InputInterceptor : Component, IKeyBindingHandler - { - private readonly OsuModSingleTap mod; - - public InputInterceptor(OsuModSingleTap mod) - { - this.mod = mod; - } - - public bool OnPressed(KeyBindingPressEvent e) - // if the pressed action is incorrect, block it from reaching gameplay. - => !mod.checkCorrectAction(e.Action); - - public void OnReleased(KeyBindingReleaseEvent e) - { - } - } } } diff --git a/osu.Game/Rulesets/Mods/ModAlternate.cs b/osu.Game/Rulesets/Mods/ModAlternate.cs new file mode 100644 index 000000000000..61e35577e382 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModAlternate.cs @@ -0,0 +1,21 @@ +// 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.Graphics.Sprites; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mods +{ + + public abstract class ModAlternate : ModInputBlocker + where TObject : HitObject + where TAction : struct + { + public override string Name => "Alternate"; + public override string Acronym => "AL"; + public override string Description => @"Don't use the same key twice in a row!"; + public override IconUsage? Icon => FontAwesome.Solid.Keyboard; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModSingleTap) }; + } +} diff --git a/osu.Game/Rulesets/Mods/ModInputBlocker.cs b/osu.Game/Rulesets/Mods/ModInputBlocker.cs new file mode 100644 index 000000000000..e27adfae13dc --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModInputBlocker.cs @@ -0,0 +1,71 @@ +// 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.Graphics; +using osu.Framework.Bindables; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.Play; + +namespace osu.Game.Rulesets.Mods +{ + + public abstract class ModInputBlocker : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer + where TObject : HitObject + where TAction : struct + { + public abstract bool checkCorrectAction(TAction action); + + public override double ScoreMultiplier => 1.0; + public override ModType Type => ModType.Conversion; + + protected IFrameStableClock gameplayClock; + protected IBindable isBreakTime; + protected double firstObjectValidJudgementTime; + protected const double flash_duration = 1000; + + protected TAction? lastActionPressed; + + protected DrawableRuleset ruleset; + + public void ApplyToPlayer(Player player) + { + isBreakTime = player.IsBreakTime.GetBoundCopy(); + isBreakTime.ValueChanged += e => + { + if (e.NewValue) + lastActionPressed = null; + }; + } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset){ + ruleset = drawableRuleset; + drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); + + var firstHitObject = ruleset.Objects.GetEnumerator().Current; + firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); + + gameplayClock = drawableRuleset.FrameStableClock; + } + + private class InputInterceptor : Component, IKeyBindingHandler + { + private readonly ModInputBlocker mod; + + public InputInterceptor(ModInputBlocker mod) + { + this.mod = mod; + } + + public bool OnPressed(KeyBindingPressEvent e) + => !mod.checkCorrectAction(e.Action); + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModSingleTap.cs b/osu.Game/Rulesets/Mods/ModSingleTap.cs new file mode 100644 index 000000000000..423abae2041a --- /dev/null +++ b/osu.Game/Rulesets/Mods/ModSingleTap.cs @@ -0,0 +1,18 @@ +// 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.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Mods +{ + public abstract class ModSingleTap : ModInputBlocker + where TObject : HitObject + where TAction : struct + { + public override string Name => "Single Tap"; + public override string Acronym => "SL"; + public override string Description => @"Alternate tapping!"; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModAlternate) }; + } +} From 3ce9a6388053f5b446bf74c16895cddddd6eb208 Mon Sep 17 00:00:00 2001 From: Euro20179 Date: Tue, 12 Apr 2022 07:18:29 -0700 Subject: [PATCH 3/4] fix start time always being 0 for InputBlocker mods --- .../Mods/TestSceneOsuModSingleTap.cs | 154 ++++++++++ .../Mods/TestSceneOsuModTapBlocking.cs | 285 ------------------ osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 1 + osu.Game/Rulesets/Mods/ModInputBlocker.cs | 32 +- osu.Game/Rulesets/Mods/ModSingleTap.cs | 2 +- 5 files changed, 178 insertions(+), 296 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs delete mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs new file mode 100644 index 000000000000..408ad2714e51 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSingleTap.cs @@ -0,0 +1,154 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModSingleTap : OsuModTestScene + { + [Test] + public void TestInputAtIntro() => CreateModTest(new ModTestData + { + Mod = new OsuModSingleTap(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 1000, + Position = new Vector2(100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(200)), + new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), + } + }); + + [Test] + public void TestInputAlternating() => CreateModTest(new ModTestData + { + Mod = new OsuModSingleTap(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 1000, + Position = new Vector2(200, 100), + }, + new HitCircle + { + StartTime = 1500, + Position = new Vector2(300, 100), + }, + new HitCircle + { + StartTime = 2000, + Position = new Vector2(400, 100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton), + new OsuReplayFrame(1001, new Vector2(200, 100)), + new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton), + new OsuReplayFrame(1501, new Vector2(300, 100)), + new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton), + new OsuReplayFrame(2001, new Vector2(400, 100)), + } + }); + + [Test] + public void TestInputSingular() => CreateModTest(new ModTestData + { + Mod = new OsuModSingleTap(), + PassCondition = () => Player.ScoreProcessor.HighestCombo.Value == 1, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 1000, + Position = new Vector2(200, 100), + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton), + } + }); + + [Test] + public void TestInputSingularWithBreak() => CreateModTest(new ModTestData + { + Mod = new OsuModSingleTap(), + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, + Autoplay = false, + Beatmap = new Beatmap + { + Breaks = new List + { + new BreakPeriod(500, 2250), + }, + HitObjects = new List + { + new HitCircle + { + StartTime = 500, + Position = new Vector2(100), + }, + new HitCircle + { + StartTime = 2500, + Position = new Vector2(100), + } + } + }, + ReplayFrames = new List + { + new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(100)), + new OsuReplayFrame(2500, new Vector2(100), OsuAction.RightButton), + new OsuReplayFrame(2501, new Vector2(100)), + } + }); + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs deleted file mode 100644 index edd151f6d304..000000000000 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTapBlocking.cs +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Collections.Generic; -using NUnit.Framework; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Mods; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Replays; -using osu.Game.Rulesets.Replays; -using osuTK; - -namespace osu.Game.Rulesets.Osu.Tests.Mods -{ - public class TestSceneOsuModTapBlocking : OsuModTestScene - { - [Test] - public void TestInputAtIntroAlternate() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 1000, - Position = new Vector2(100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(200)), - new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), - } - }); - [Test] - public void TestInputAtIntroSingleTap() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking { SingleTap = { Value = true }, Alternate = { Value = false }}, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 1000, - Position = new Vector2(100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(200), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(200)), - new OsuReplayFrame(1000, new Vector2(100), OsuAction.LeftButton), - } - }); - - [Test] - public void TestInputAlternatingAlternate() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 4, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 500, - Position = new Vector2(100), - }, - new HitCircle - { - StartTime = 1000, - Position = new Vector2(200, 100), - }, - new HitCircle - { - StartTime = 1500, - Position = new Vector2(300, 100), - }, - new HitCircle - { - StartTime = 2000, - Position = new Vector2(400, 100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(100)), - new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton), - new OsuReplayFrame(1001, new Vector2(200, 100)), - new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton), - new OsuReplayFrame(1501, new Vector2(300, 100)), - new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton), - new OsuReplayFrame(2001, new Vector2(400, 100)), - } - }); - - [Test] - public void TestInputAlternatingSingleTap() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking { SingleTap = { Value = true}, Alternate = {Value = false}}, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 500, - Position = new Vector2(100), - }, - new HitCircle - { - StartTime = 1000, - Position = new Vector2(200, 100), - }, - new HitCircle - { - StartTime = 1500, - Position = new Vector2(300, 100), - }, - new HitCircle - { - StartTime = 2000, - Position = new Vector2(400, 100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(100)), - new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.RightButton), - new OsuReplayFrame(1001, new Vector2(200, 100)), - new OsuReplayFrame(1500, new Vector2(300, 100), OsuAction.LeftButton), - new OsuReplayFrame(1501, new Vector2(300, 100)), - new OsuReplayFrame(2000, new Vector2(400, 100), OsuAction.RightButton), - new OsuReplayFrame(2001, new Vector2(400, 100)), - } - }); - [Test] - public void TestInputSingularAlternate() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 0 && Player.ScoreProcessor.HighestCombo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 500, - Position = new Vector2(100), - }, - new HitCircle - { - StartTime = 1000, - Position = new Vector2(200, 100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(100)), - new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton), - } - }); - [Test] - public void TestInputSingularSingleTap() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking { SingleTap = { Value = true}, Alternate = {Value = false}}, - PassCondition = () => Player.ScoreProcessor.HighestCombo.Value == 2, - Autoplay = false, - Beatmap = new Beatmap - { - HitObjects = new List - { - new HitCircle - { - StartTime = 500, - Position = new Vector2(100), - }, - new HitCircle - { - StartTime = 1000, - Position = new Vector2(200, 100), - }, - }, - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(100)), - new OsuReplayFrame(1000, new Vector2(200, 100), OsuAction.LeftButton), - } - }); - - [Test] - public void TestInputSingularWithBreakAlternate() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking(), - PassCondition = () => Player.ScoreProcessor.Combo.Value == 1, - Autoplay = false, - Beatmap = new Beatmap - { - Breaks = new List - { - new BreakPeriod(500, 2250), - }, - HitObjects = new List - { - new HitCircle - { - StartTime = 500, - Position = new Vector2(100), - }, - new HitCircle - { - StartTime = 2500, - Position = new Vector2(100), - } - } - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(100)), - new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(2501, new Vector2(100)), - } - }); - [Test] - public void TestInputSingularWithBreakSingleTap() => CreateModTest(new ModTestData - { - Mod = new OsuModTapBlocking { SingleTap = { Value = true}, Alternate = {Value = false}}, - PassCondition = () => Player.ScoreProcessor.Combo.Value == 2, - Autoplay = false, - Beatmap = new Beatmap - { - Breaks = new List - { - new BreakPeriod(500, 2250), - }, - HitObjects = new List - { - new HitCircle - { - StartTime = 500, - Position = new Vector2(100), - }, - new HitCircle - { - StartTime = 2500, - Position = new Vector2(100), - } - } - }, - ReplayFrames = new List - { - new OsuReplayFrame(500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(501, new Vector2(100)), - new OsuReplayFrame(2500, new Vector2(100), OsuAction.LeftButton), - new OsuReplayFrame(2501, new Vector2(100)), - } - }); - } -} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index 041e677b1033..92d95b1c7f1a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -10,6 +10,7 @@ namespace osu.Game.Rulesets.Osu.Mods { public class OsuModAlternate : ModAlternate { + public override bool checkCorrectAction(OsuAction action) { if (isBreakTime.Value) diff --git a/osu.Game/Rulesets/Mods/ModInputBlocker.cs b/osu.Game/Rulesets/Mods/ModInputBlocker.cs index e27adfae13dc..c6014666ae21 100644 --- a/osu.Game/Rulesets/Mods/ModInputBlocker.cs +++ b/osu.Game/Rulesets/Mods/ModInputBlocker.cs @@ -1,13 +1,14 @@ // 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.Graphics; using osu.Framework.Bindables; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Mods @@ -31,6 +32,26 @@ public abstract class ModInputBlocker : Mod, IApplicableToDraw protected DrawableRuleset ruleset; + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + ruleset = drawableRuleset; + drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); + + TObject firstHitObject; + try + { + firstHitObject = ruleset.Beatmap.HitObjects[0]; + } + catch(IndexOutOfRangeException) + { + firstHitObject = null; + } + + firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); + + gameplayClock = drawableRuleset.FrameStableClock; + } + public void ApplyToPlayer(Player player) { isBreakTime = player.IsBreakTime.GetBoundCopy(); @@ -41,15 +62,6 @@ public void ApplyToPlayer(Player player) }; } - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset){ - ruleset = drawableRuleset; - drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); - - var firstHitObject = ruleset.Objects.GetEnumerator().Current; - firstObjectValidJudgementTime = (firstHitObject?.StartTime ?? 0) - (firstHitObject?.HitWindows.WindowFor(HitResult.Meh) ?? 0); - - gameplayClock = drawableRuleset.FrameStableClock; - } private class InputInterceptor : Component, IKeyBindingHandler { diff --git a/osu.Game/Rulesets/Mods/ModSingleTap.cs b/osu.Game/Rulesets/Mods/ModSingleTap.cs index 423abae2041a..7a6bc5ecc03b 100644 --- a/osu.Game/Rulesets/Mods/ModSingleTap.cs +++ b/osu.Game/Rulesets/Mods/ModSingleTap.cs @@ -11,7 +11,7 @@ public abstract class ModSingleTap : ModInputBlocker "Single Tap"; - public override string Acronym => "SL"; + public override string Acronym => "ST"; public override string Description => @"Alternate tapping!"; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModAlternate) }; } From da6c290fe6c9bf9815795819b9311e40e41aed73 Mon Sep 17 00:00:00 2001 From: Euro20179 Date: Tue, 12 Apr 2022 16:34:02 -0700 Subject: [PATCH 4/4] move InputBlockingMod, and subclasses to osu! ruleset --- .../Mods/InputBlockingMod.cs | 48 +++++++++++++------ osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs | 29 ++++------- osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs | 24 +++------- osu.Game/Rulesets/Mods/ModAlternate.cs | 21 -------- osu.Game/Rulesets/Mods/ModSingleTap.cs | 18 ------- 5 files changed, 50 insertions(+), 90 deletions(-) rename osu.Game/Rulesets/Mods/ModInputBlocker.cs => osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs (58%) delete mode 100644 osu.Game/Rulesets/Mods/ModAlternate.cs delete mode 100644 osu.Game/Rulesets/Mods/ModSingleTap.cs diff --git a/osu.Game/Rulesets/Mods/ModInputBlocker.cs b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs similarity index 58% rename from osu.Game/Rulesets/Mods/ModInputBlocker.cs rename to osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs index c6014666ae21..ccf66827efb2 100644 --- a/osu.Game/Rulesets/Mods/ModInputBlocker.cs +++ b/osu.Game.Rulesets.Osu/Mods/InputBlockingMod.cs @@ -6,19 +6,17 @@ using osu.Framework.Bindables; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Screens.Play; -namespace osu.Game.Rulesets.Mods +namespace osu.Game.Rulesets.Osu.Mods { - public abstract class ModInputBlocker : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer - where TObject : HitObject - where TAction : struct + public abstract class InputBlockingMod : Mod, IApplicableToDrawableRuleset, IApplicableToPlayer { - public abstract bool checkCorrectAction(TAction action); public override double ScoreMultiplier => 1.0; public override ModType Type => ModType.Conversion; @@ -28,16 +26,36 @@ public abstract class ModInputBlocker : Mod, IApplicableToDraw protected double firstObjectValidJudgementTime; protected const double flash_duration = 1000; - protected TAction? lastActionPressed; + protected OsuAction? lastActionPressed; - protected DrawableRuleset ruleset; + protected DrawableRuleset ruleset; - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + public virtual bool checkCorrectAction(OsuAction action){ + if (isBreakTime.Value) + return true; + + if (gameplayClock.CurrentTime < firstObjectValidJudgementTime) + return true; + + switch (action) + { + case OsuAction.LeftButton: + case OsuAction.RightButton: + break; + + // Any action which is not left or right button should be ignored. + default: + return true; + } + return false; + } + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { ruleset = drawableRuleset; drawableRuleset.KeyBindingInputManager.Add(new InputInterceptor(this)); - TObject firstHitObject; + OsuHitObject firstHitObject; try { firstHitObject = ruleset.Beatmap.HitObjects[0]; @@ -63,19 +81,19 @@ public void ApplyToPlayer(Player player) } - private class InputInterceptor : Component, IKeyBindingHandler + private class InputInterceptor : Component, IKeyBindingHandler { - private readonly ModInputBlocker mod; + private readonly InputBlockingMod mod; - public InputInterceptor(ModInputBlocker mod) + public InputInterceptor(InputBlockingMod mod) { this.mod = mod; } - public bool OnPressed(KeyBindingPressEvent e) + public bool OnPressed(KeyBindingPressEvent e) => !mod.checkCorrectAction(e.Action); - public void OnReleased(KeyBindingReleaseEvent e) + public void OnReleased(KeyBindingReleaseEvent e) { } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs index 92d95b1c7f1a..2203f7020c63 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAlternate.cs @@ -1,35 +1,26 @@ // 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.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; - namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAlternate : ModAlternate + public class OsuModAlternate : InputBlockingMod { - + public override string Name => "Alternate"; + public override string Acronym => "AL"; + public override string Description => @"Don't use the same key twice in a row!"; + public override IconUsage? Icon => FontAwesome.Solid.Keyboard; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay) }; public override bool checkCorrectAction(OsuAction action) - { - if (isBreakTime.Value) - return true; - if (gameplayClock.CurrentTime < firstObjectValidJudgementTime) + { + if(base.checkCorrectAction(action)) return true; - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - break; - - // Any action which is not left or right button should be ignored. - default: - return true; - } - if (lastActionPressed != action) { // User alternated correctly. diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs index f334ca07eafb..a9c5491be6cb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSingleTap.cs @@ -1,33 +1,23 @@ // 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.Graphics; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModSingleTap : ModSingleTap + public class OsuModSingleTap : InputBlockingMod { + public override string Name => "Single Tap"; + public override string Acronym => "ST"; + public override string Description => @"Alternate tapping!"; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay) }; public override bool checkCorrectAction(OsuAction action) { - if (isBreakTime.Value) + if(base.checkCorrectAction(action)) return true; - if (gameplayClock.CurrentTime < firstObjectValidJudgementTime) - return true; - - switch (action) - { - case OsuAction.LeftButton: - case OsuAction.RightButton: - break; - - // Any action which is not left or right button should be ignored. - default: - return true; - } - if (lastActionPressed == null) { lastActionPressed = action; diff --git a/osu.Game/Rulesets/Mods/ModAlternate.cs b/osu.Game/Rulesets/Mods/ModAlternate.cs deleted file mode 100644 index 61e35577e382..000000000000 --- a/osu.Game/Rulesets/Mods/ModAlternate.cs +++ /dev/null @@ -1,21 +0,0 @@ -// 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.Graphics.Sprites; -using osu.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mods -{ - - public abstract class ModAlternate : ModInputBlocker - where TObject : HitObject - where TAction : struct - { - public override string Name => "Alternate"; - public override string Acronym => "AL"; - public override string Description => @"Don't use the same key twice in a row!"; - public override IconUsage? Icon => FontAwesome.Solid.Keyboard; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModSingleTap) }; - } -} diff --git a/osu.Game/Rulesets/Mods/ModSingleTap.cs b/osu.Game/Rulesets/Mods/ModSingleTap.cs deleted file mode 100644 index 7a6bc5ecc03b..000000000000 --- a/osu.Game/Rulesets/Mods/ModSingleTap.cs +++ /dev/null @@ -1,18 +0,0 @@ -// 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.Game.Rulesets.Objects; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModSingleTap : ModInputBlocker - where TObject : HitObject - where TAction : struct - { - public override string Name => "Single Tap"; - public override string Acronym => "ST"; - public override string Description => @"Alternate tapping!"; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModAlternate) }; - } -}