diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs new file mode 100644 index 0000000000..0d0fefe0ff --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModNoScope.cs @@ -0,0 +1,150 @@ +// 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 System.Linq; +using NUnit.Framework; +using osu.Framework.Utils; +using osu.Framework.Testing; +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.Objects.Drawables; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public class TestSceneOsuModNoScope : OsuModTestScene + { + [Test] + public void TestVisibleDuringBreak() + { + CreateModTest(new ModTestData + { + Mod = new OsuModNoScope + { + HiddenComboCount = { Value = 0 }, + }, + Autoplay = true, + PassCondition = () => true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 1000, + }, + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 5000, + } + }, + Breaks = new List + { + new BreakPeriod(2000, 4000), + } + } + }); + + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + AddUntilStep("wait for start of break", isBreak); + AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1)); + AddUntilStep("wait for end of break", () => !isBreak()); + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + } + + [Test] + public void TestVisibleDuringSpinner() + { + CreateModTest(new ModTestData + { + Mod = new OsuModNoScope + { + HiddenComboCount = { Value = 0 }, + }, + Autoplay = true, + PassCondition = () => true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 1000, + }, + new Spinner + { + Position = new Vector2(256, 192), + StartTime = 2000, + Duration = 2000, + }, + new HitCircle + { + Position = new Vector2(300, 192), + StartTime = 5000, + } + } + } + }); + + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + AddUntilStep("wait for start of spinner", isSpinning); + AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1)); + AddUntilStep("wait for end of spinner", () => !isSpinning()); + AddUntilStep("wait for cursor to hide", () => cursorAlphaAlmostEquals(0)); + } + + [Test] + public void TestVisibleAfterComboBreak() + { + CreateModTest(new ModTestData + { + Mod = new OsuModNoScope + { + HiddenComboCount = { Value = 2 }, + }, + Autoplay = true, + PassCondition = () => true, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HitCircle + { + Position = new Vector2(100, 192), + StartTime = 1000, + }, + new HitCircle + { + Position = new Vector2(150, 192), + StartTime = 3000, + }, + new HitCircle + { + Position = new Vector2(200, 192), + StartTime = 5000, + }, + } + } + }); + + AddAssert("cursor must start visible", () => cursorAlphaAlmostEquals(1)); + AddUntilStep("wait for combo", () => Player.ScoreProcessor.Combo.Value >= 2); + AddAssert("cursor must dim after combo", () => !cursorAlphaAlmostEquals(1)); + AddStep("break combo", () => Player.ScoreProcessor.Combo.Set(0)); + AddUntilStep("wait for cursor to show", () => cursorAlphaAlmostEquals(1)); + } + + private bool isSpinning() => Player.ChildrenOfType().SingleOrDefault()?.Progress > 0; + + private bool isBreak() => Player.IsBreakTime.Value; + + private bool cursorAlphaAlmostEquals(float alpha) => Precision.AlmostEquals(Player.DrawableRuleset.Cursor.Alpha, alpha); + } +} diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs index c48cbd9992..501c0a55bd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoScope.cs @@ -2,22 +2,27 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Game.Rulesets.UI; -using osu.Game.Rulesets.Mods; +using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; -using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Framework.Utils; -using osu.Game.Graphics.UserInterface; +using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Utils; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModNoScope : Mod, IUpdatableByPlayfield, IApplicableToScoreProcessor + public class OsuModNoScope : Mod, IUpdatableByPlayfield, IApplicableToScoreProcessor, IApplicableToPlayer, IApplicableToBeatmap { /// /// Slightly higher than the cutoff for . @@ -34,8 +39,10 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; private BindableNumber currentCombo; + private IBindable isBreakTime; + private PeriodTracker spinnerPeriods; - private float targetAlpha; + private float comboBasedAlpha; [SettingSource( "Hidden at combo", @@ -52,6 +59,16 @@ namespace osu.Game.Rulesets.Osu.Mods public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; + public void ApplyToPlayer(Player player) + { + isBreakTime = player.IsBreakTime.GetBoundCopy(); + } + + public void ApplyToBeatmap(IBeatmap beatmap) + { + spinnerPeriods = new PeriodTracker(beatmap.HitObjects.OfType().Select(b => new Period(b.StartTime - transition_duration, b.EndTime))); + } + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { if (HiddenComboCount.Value == 0) return; @@ -59,12 +76,14 @@ namespace osu.Game.Rulesets.Osu.Mods currentCombo = scoreProcessor.Combo.GetBoundCopy(); currentCombo.BindValueChanged(combo => { - targetAlpha = Math.Max(min_alpha, 1 - (float)combo.NewValue / HiddenComboCount.Value); + comboBasedAlpha = Math.Max(min_alpha, 1 - (float)combo.NewValue / HiddenComboCount.Value); }, true); } public virtual void Update(Playfield playfield) { + bool shouldAlwaysShowCursor = isBreakTime.Value || spinnerPeriods.IsInAny(playfield.Clock.CurrentTime); + float targetAlpha = shouldAlwaysShowCursor ? 1 : comboBasedAlpha; playfield.Cursor.Alpha = (float)Interpolation.Lerp(playfield.Cursor.Alpha, targetAlpha, Math.Clamp(playfield.Time.Elapsed / transition_duration, 0, 1)); } }