From 0c7e5a2e3b0cf886eae6f2828c33b793c2501cc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 6 Dec 2019 19:15:49 +0900 Subject: [PATCH 01/56] Add bindable adjustments for DT/HT rate --- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 14 +++++++++++++- osu.Game/Rulesets/Mods/ModHalfTime.cs | 14 +++++++++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index a5e76e32b1..1a6831d974 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -3,7 +3,9 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods @@ -19,6 +21,16 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray(); - protected override double RateAdjust => 1.5; + [SettingSource("Speed increase", "The actual increase to apply")] + public BindableNumber SpeedChange { get; } = new BindableDouble + { + MinValue = 1.01, + MaxValue = 2, + Default = 1.5, + Value = 1.5, + Precision = 0.01, + }; + + protected override double RateAdjust => SpeedChange.Value; } } diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 27369f4c30..4acf771fa8 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -3,7 +3,9 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods @@ -19,6 +21,16 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray(); - protected override double RateAdjust => 0.75; + [SettingSource("Speed decrease", "The actual decrease to apply")] + public BindableNumber SpeedChange { get; } = new BindableDouble + { + MinValue = 0.5, + MaxValue = 0.99, + Default = 0.75, + Value = 0.75, + Precision = 0.01, + }; + + protected override double RateAdjust => SpeedChange.Value; } } From 6b667daf0965ec6386632bca78415cfe8bca1365 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 19:41:18 +0900 Subject: [PATCH 02/56] Update bindable types in line with framework --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 14 +++++++++----- .../Tests/Visual/RateAdjustedBeatmapTestScene.cs | 2 +- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index 96275c1274..c15b5d4786 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -511,15 +511,17 @@ namespace osu.Game.Rulesets.UI public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - public void AddAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); - public void RemoveAdjustment(AdjustableProperty type, BindableDouble adjustBindable) => throw new NotImplementedException(); + public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotImplementedException(); - public BindableDouble Volume => throw new NotImplementedException(); + public BindableNumber Volume => throw new NotImplementedException(); - public BindableDouble Balance => throw new NotImplementedException(); + public BindableNumber Balance => throw new NotImplementedException(); - public BindableDouble Frequency => throw new NotImplementedException(); + public BindableNumber Frequency => throw new NotImplementedException(); + + public BindableNumber Tempo => throw new NotImplementedException(); public IBindable AggregateVolume => throw new NotImplementedException(); @@ -527,6 +529,8 @@ namespace osu.Game.Rulesets.UI public IBindable AggregateFrequency => throw new NotImplementedException(); + public IBindable AggregateTempo => throw new NotImplementedException(); + public int PlaybackConcurrency { get => throw new NotImplementedException(); diff --git a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs index 921a1d9789..ad24ffc7b8 100644 --- a/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs +++ b/osu.Game/Tests/Visual/RateAdjustedBeatmapTestScene.cs @@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual base.Update(); // note that this will override any mod rate application - Beatmap.Value.Track.TempoAdjust = Clock.Rate; + Beatmap.Value.Track.Tempo.Value = Clock.Rate; } } } From 7fd52c21f56cdf6fdc2f9004e6b9f449e26b9bba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 19:41:31 +0900 Subject: [PATCH 03/56] Update mods and user adjust to use adjustments --- osu.Game/Rulesets/Mods/ModDaycore.cs | 3 +- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 6 ++-- osu.Game/Rulesets/Mods/ModHalfTime.cs | 6 ++-- osu.Game/Rulesets/Mods/ModNightcore.cs | 3 +- .../{ModTimeAdjust.cs => ModRateAdjust.cs} | 8 +++-- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 35 +++++++++---------- .../Screens/Play/GameplayClockContainer.cs | 9 ++--- 7 files changed, 32 insertions(+), 38 deletions(-) rename osu.Game/Rulesets/Mods/{ModTimeAdjust.cs => ModRateAdjust.cs} (60%) diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index dcb3cb5597..f7da1e86db 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -1,6 +1,7 @@ // 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.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Sprites; @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Mods public override void ApplyToTrack(Track track) { - track.Frequency.Value *= RateAdjust; + track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); } } } diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index b9faad8cbd..7015460c51 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModDoubleTime : ModTimeAdjust + public abstract class ModDoubleTime : ModRateAdjust { public override string Name => "Double Time"; public override string Acronym => "DT"; @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray(); [SettingSource("Speed increase", "The actual increase to apply")] - public BindableNumber SpeedChange { get; } = new BindableDouble + public override BindableNumber SpeedChange { get; } = new BindableDouble { MinValue = 1.01, MaxValue = 2, @@ -30,7 +30,5 @@ namespace osu.Game.Rulesets.Mods Value = 1.5, Precision = 0.01, }; - - protected override double RateAdjust => SpeedChange.Value; } } diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 255c5b7065..15f7afa312 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -10,7 +10,7 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModHalfTime : ModTimeAdjust + public abstract class ModHalfTime : ModRateAdjust { public override string Name => "Half Time"; public override string Acronym => "HT"; @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray(); [SettingSource("Speed decrease", "The actual decrease to apply")] - public BindableNumber SpeedChange { get; } = new BindableDouble + public override BindableNumber SpeedChange { get; } = new BindableDouble { MinValue = 0.5, MaxValue = 0.99, @@ -30,7 +30,5 @@ namespace osu.Game.Rulesets.Mods Value = 0.75, Precision = 0.01, }; - - protected override double RateAdjust => SpeedChange.Value; } } diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index a4f1ef5a72..e41f1415ab 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -1,6 +1,7 @@ // 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.Audio; using osu.Framework.Audio.Track; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -16,7 +17,7 @@ namespace osu.Game.Rulesets.Mods public override void ApplyToTrack(Track track) { - track.Frequency.Value *= RateAdjust; + track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs similarity index 60% rename from osu.Game/Rulesets/Mods/ModTimeAdjust.cs rename to osu.Game/Rulesets/Mods/ModRateAdjust.cs index f137a75ed8..5aa3e09fee 100644 --- a/osu.Game/Rulesets/Mods/ModTimeAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -2,19 +2,21 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeAdjust : Mod, IApplicableToTrack + public abstract class ModRateAdjust : Mod, IApplicableToTrack { public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) }; - protected abstract double RateAdjust { get; } + public abstract BindableNumber SpeedChange { get; } public virtual void ApplyToTrack(Track track) { - track.TempoAdjust *= RateAdjust; + track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange); } } } diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index d95d354487..36de546707 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -3,36 +3,42 @@ using System; using System.Linq; +using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mods { - public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToTrack, IApplicableToBeatmap + public abstract class ModTimeRamp : Mod, IUpdatableByPlayfield, IApplicableToBeatmap, IApplicableToTrack { /// /// The point in the beatmap at which the final ramping rate should be reached. /// private const double final_rate_progress = 0.75f; - public override Type[] IncompatibleMods => new[] { typeof(ModTimeAdjust) }; + public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust) }; protected abstract double FinalRateAdjustment { get; } private double finalRateTime; private double beginRampTime; + + public BindableNumber SpeedChange { get; } = new BindableDouble + { + Default = 1, + Value = 1, + Precision = 0.01, + }; + private Track track; - public virtual void ApplyToTrack(Track track) + public void ApplyToTrack(Track track) { this.track = track; - - lastAdjust = 1; - - // for preview purposes. during gameplay, Update will overwrite this setting. - applyAdjustment(1); + track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); } public virtual void ApplyToBeatmap(IBeatmap beatmap) @@ -48,20 +54,11 @@ namespace osu.Game.Rulesets.Mods applyAdjustment((track.CurrentTime - beginRampTime) / finalRateTime); } - private double lastAdjust = 1; - /// /// Adjust the rate along the specified ramp /// /// The amount of adjustment to apply (from 0..1). - private void applyAdjustment(double amount) - { - double adjust = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); - - track.Tempo.Value /= lastAdjust; - track.Tempo.Value *= lastAdjust; - - lastAdjust = adjust; - } + private void applyAdjustment(double amount) => + SpeedChange.Value = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); } } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 2cc03ae453..9f46fddc5e 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -43,7 +43,7 @@ namespace osu.Game.Screens.Play private readonly double firstHitObjectTime; - public readonly Bindable UserPlaybackRate = new BindableDouble(1) + public readonly BindableNumber UserPlaybackRate = new BindableDouble(1) { Default = 1, MinValue = 0.5, @@ -73,7 +73,6 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both; track = beatmap.Track; - track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false }; @@ -120,7 +119,6 @@ namespace osu.Game.Screens.Play Seek(startTime); adjustableClock.ProcessFrame(); - UserPlaybackRate.ValueChanged += _ => updateRate(); } public void Restart() @@ -223,7 +221,8 @@ namespace osu.Game.Screens.Play speedAdjustmentsApplied = true; track.ResetSpeedAdjustments(); - track.Tempo.Value = UserPlaybackRate.Value; + track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); + track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate); foreach (var mod in mods.OfType()) mod.ApplyToTrack(track); @@ -244,8 +243,6 @@ namespace osu.Game.Screens.Play track.ResetSpeedAdjustments(); speedAdjustmentsApplied = false; } - - track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust); } } } From edc82205543106251bba5a9b8a2c92ea3b81e7ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 19:58:51 +0900 Subject: [PATCH 04/56] Add time ramp settings --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 8 ++++++-- osu.Game/Rulesets/Mods/ModWindDown.cs | 12 +++++++++++- osu.Game/Rulesets/Mods/ModWindUp.cs | 12 +++++++++++- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 36de546707..bffe4f7b70 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -7,6 +7,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.Objects; @@ -21,7 +22,8 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust) }; - protected abstract double FinalRateAdjustment { get; } + [SettingSource("Final rate", "The final speed to ramp to")] + public abstract BindableNumber FinalRate { get; } private double finalRateTime; private double beginRampTime; @@ -45,6 +47,8 @@ namespace osu.Game.Rulesets.Mods { HitObject lastObject = beatmap.HitObjects.LastOrDefault(); + SpeedChange.SetDefault(); + beginRampTime = beatmap.HitObjects.FirstOrDefault()?.StartTime ?? 0; finalRateTime = final_rate_progress * (lastObject?.GetEndTime() ?? 0); } @@ -59,6 +63,6 @@ namespace osu.Game.Rulesets.Mods /// /// The amount of adjustment to apply (from 0..1). private void applyAdjustment(double amount) => - SpeedChange.Value = 1 + (Math.Sign(FinalRateAdjustment) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRateAdjustment)); + SpeedChange.Value = 1 + (Math.Sign(FinalRate.Value) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRate.Value)); } } diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index b2e3abb59d..680c1a10fb 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -3,7 +3,9 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; namespace osu.Game.Rulesets.Mods { @@ -15,7 +17,15 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => FontAwesome.Solid.ChevronCircleDown; public override double ScoreMultiplier => 1.0; - protected override double FinalRateAdjustment => -0.25; + [SettingSource("Final rate", "The speed increase to ramp towards")] + public override BindableNumber FinalRate { get; } = new BindableDouble + { + MinValue = -0.5, + MaxValue = -0.01, + Default = -0.25, + Value = -0.25, + Precision = 0.01, + }; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindUp)).ToArray(); } diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index 8df35a1de2..ca9ce0ea3e 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -3,7 +3,9 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; +using osu.Game.Configuration; namespace osu.Game.Rulesets.Mods { @@ -15,7 +17,15 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => FontAwesome.Solid.ChevronCircleUp; public override double ScoreMultiplier => 1.0; - protected override double FinalRateAdjustment => 0.5; + [SettingSource("Final rate", "The speed increase to ramp towards")] + public override BindableNumber FinalRate { get; } = new BindableDouble + { + MinValue = 0.01, + MaxValue = 2, + Default = 1.5, + Value = 1.5, + Precision = 0.01, + }; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModWindDown)).ToArray(); } From 12bdb1dafd98d09db5af10de7961b8ee17c94a05 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 9 Dec 2019 20:40:38 +0900 Subject: [PATCH 05/56] Pin DC/NC pitch --- osu.Game/Rulesets/Mods/ModDaycore.cs | 13 ++++++++++++- osu.Game/Rulesets/Mods/ModNightcore.cs | 13 ++++++++++++- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index f7da1e86db..474e793dd1 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; namespace osu.Game.Rulesets.Mods @@ -14,9 +15,19 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => FontAwesome.Solid.Question; public override string Description => "Whoaaaaa..."; + private readonly BindableNumber tempoAdjust = new BindableDouble(1); + private readonly BindableNumber freqAdjust = new BindableDouble(1); + public override void ApplyToTrack(Track track) { - track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); + track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); + track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); + + SpeedChange.BindValueChanged(val => + { + freqAdjust.Value = SpeedChange.Default; + tempoAdjust.Value = val.NewValue / SpeedChange.Default; + }, true); } } } diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index e41f1415ab..401814d18b 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -3,6 +3,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; @@ -15,9 +16,19 @@ namespace osu.Game.Rulesets.Mods public override IconUsage Icon => OsuIcon.ModNightcore; public override string Description => "Uguuuuuuuu..."; + private readonly BindableNumber tempoAdjust = new BindableDouble(1); + private readonly BindableNumber freqAdjust = new BindableDouble(1); + public override void ApplyToTrack(Track track) { - track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); + track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); + track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); + + SpeedChange.BindValueChanged(val => + { + freqAdjust.Value = SpeedChange.Default; + tempoAdjust.Value = val.NewValue / SpeedChange.Default; + }, true); } } } From e9ec6591a907209bda53c4dc8fce87805b7d3e67 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 10 Dec 2019 11:20:08 +0900 Subject: [PATCH 06/56] Separate path connections from control points --- .../Components/PathControlPointConnection.cs | 72 +++++++++++++++++++ .../Components/PathControlPointPiece.cs | 43 ++--------- .../Components/PathControlPointVisualiser.cs | 22 ++++-- 3 files changed, 92 insertions(+), 45 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs new file mode 100644 index 0000000000..f57299c5a9 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs @@ -0,0 +1,72 @@ +// 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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Lines; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components +{ + public class PathControlPointConnection : CompositeDrawable + { + public PathControlPoint ControlPoint; + + private readonly Path path; + private readonly Slider slider; + + private IBindable sliderPosition; + private IBindable pathVersion; + + public PathControlPointConnection(Slider slider, PathControlPoint controlPoint) + { + this.slider = slider; + ControlPoint = controlPoint; + + Origin = Anchor.Centre; + AutoSizeAxes = Axes.Both; + + InternalChild = path = new SmoothPath + { + Anchor = Anchor.Centre, + PathRadius = 1 + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + sliderPosition = slider.PositionBindable.GetBoundCopy(); + sliderPosition.BindValueChanged(_ => updateConnectingPath()); + + pathVersion = slider.Path.Version.GetBoundCopy(); + pathVersion.BindValueChanged(_ => updateConnectingPath()); + + updateConnectingPath(); + } + + /// + /// Updates the path connecting this control point to the next one. + /// + private void updateConnectingPath() + { + Position = slider.StackedPosition + ControlPoint.Position.Value; + + path.ClearVertices(); + + int index = slider.Path.ControlPoints.IndexOf(ControlPoint) + 1; + + if (index == 0 || index == slider.Path.ControlPoints.Count) + return; + + path.AddVertex(Vector2.Zero); + path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value); + + path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index c2aefac587..42812ff934 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -6,7 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -28,7 +27,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public readonly PathControlPoint ControlPoint; private readonly Slider slider; - private readonly Path path; private readonly Container marker; private readonly Drawable markerRing; @@ -39,12 +37,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private OsuColour colours { get; set; } private IBindable sliderPosition; - private IBindable pathVersion; + private IBindable controlPointPosition; public PathControlPointPiece(Slider slider, PathControlPoint controlPoint) { this.slider = slider; - ControlPoint = controlPoint; Origin = Anchor.Centre; @@ -52,11 +49,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChildren = new Drawable[] { - path = new SmoothPath - { - Anchor = Anchor.Centre, - PathRadius = 1 - }, marker = new Container { Anchor = Anchor.Centre, @@ -96,20 +88,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components base.LoadComplete(); sliderPosition = slider.PositionBindable.GetBoundCopy(); - sliderPosition.BindValueChanged(_ => updateDisplay()); + sliderPosition.BindValueChanged(_ => updateMarkerDisplay()); - pathVersion = slider.Path.Version.GetBoundCopy(); - pathVersion.BindValueChanged(_ => updateDisplay()); + controlPointPosition = ControlPoint.Position.GetBoundCopy(); + controlPointPosition.BindValueChanged(_ => updateMarkerDisplay()); IsSelected.BindValueChanged(_ => updateMarkerDisplay()); - updateDisplay(); - } - - private void updateDisplay() - { updateMarkerDisplay(); - updateConnectingPath(); } // The connecting path is excluded from positional input @@ -189,26 +175,5 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components colour = Color4.White; marker.Colour = colour; } - - /// - /// Updates the path connecting this control point to the previous one. - /// - private void updateConnectingPath() - { - path.ClearVertices(); - - int index = slider.Path.ControlPoints.IndexOf(ControlPoint); - - if (index == -1) - return; - - if (++index != slider.Path.ControlPoints.Count) - { - path.AddVertex(Vector2.Zero); - path.AddVertex(slider.Path.ControlPoints[index].Position.Value - ControlPoint.Position.Value); - } - - path.OriginPosition = path.PositionInBoundingBox(Vector2.Zero); - } } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index eb6e3c01e1..e45dc1d47a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public class PathControlPointVisualiser : CompositeDrawable, IKeyBindingHandler, IHasContextMenu { internal readonly Container Pieces; + + private readonly Container connections; private readonly Slider slider; private readonly bool allowSelection; @@ -42,7 +44,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components RelativeSizeAxes = Axes.Both; - InternalChild = Pieces = new Container { RelativeSizeAxes = Axes.Both }; + InternalChildren = new Drawable[] + { + connections = new Container { RelativeSizeAxes = Axes.Both }, + Pieces = new Container { RelativeSizeAxes = Axes.Both } + }; } protected override void LoadComplete() @@ -62,19 +68,23 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { foreach (var point in controlPoints) { - var piece = new PathControlPointPiece(slider, point); + Pieces.Add(new PathControlPointPiece(slider, point).With(d => + { + if (allowSelection) + d.RequestSelection = selectPiece; + })); - if (allowSelection) - piece.RequestSelection = selectPiece; - - Pieces.Add(piece); + connections.Add(new PathControlPointConnection(slider, point)); } } private void removeControlPoints(IEnumerable controlPoints) { foreach (var point in controlPoints) + { Pieces.RemoveAll(p => p.ControlPoint == point); + connections.RemoveAll(c => c.ControlPoint == point); + } } protected override bool OnClick(ClickEvent e) From 404d3207ffc0c963dedde39d10b27d7de5f890fd Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Dec 2019 19:43:32 +0900 Subject: [PATCH 07/56] Refactor ModNightcore/ModDaycore --- osu.Game/Rulesets/Mods/ModDaycore.cs | 12 ++++++++---- osu.Game/Rulesets/Mods/ModNightcore.cs | 12 ++++++++---- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 474e793dd1..9445895fb0 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -18,16 +18,20 @@ namespace osu.Game.Rulesets.Mods private readonly BindableNumber tempoAdjust = new BindableDouble(1); private readonly BindableNumber freqAdjust = new BindableDouble(1); - public override void ApplyToTrack(Track track) + public ModDaycore() { - track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); - track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); - SpeedChange.BindValueChanged(val => { freqAdjust.Value = SpeedChange.Default; tempoAdjust.Value = val.NewValue / SpeedChange.Default; }, true); } + + public override void ApplyToTrack(Track track) + { + // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied) + track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); + track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); + } } } diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 401814d18b..9b27925693 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -19,16 +19,20 @@ namespace osu.Game.Rulesets.Mods private readonly BindableNumber tempoAdjust = new BindableDouble(1); private readonly BindableNumber freqAdjust = new BindableDouble(1); - public override void ApplyToTrack(Track track) + public ModNightcore() { - track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); - track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); - SpeedChange.BindValueChanged(val => { freqAdjust.Value = SpeedChange.Default; tempoAdjust.Value = val.NewValue / SpeedChange.Default; }, true); } + + public override void ApplyToTrack(Track track) + { + // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied) + track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); + track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); + } } } From c34b6b59eda2e6a62c5e1b1aca36b9e9c45f88c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Dec 2019 19:48:57 +0900 Subject: [PATCH 08/56] Remove time ramp and rate adjust mod incompatibility --- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 2 -- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 -- 2 files changed, 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 5aa3e09fee..9143836fb7 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -10,8 +10,6 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModRateAdjust : Mod, IApplicableToTrack { - public override Type[] IncompatibleMods => new[] { typeof(ModTimeRamp) }; - public abstract BindableNumber SpeedChange { get; } public virtual void ApplyToTrack(Track track) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index bffe4f7b70..e10afa7d7c 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Mods /// private const double final_rate_progress = 0.75f; - public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust) }; - [SettingSource("Final rate", "The final speed to ramp to")] public abstract BindableNumber FinalRate { get; } From 40f918dce6a65a77589bd6a5800f510f82d1ff53 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Dec 2019 19:49:32 +0900 Subject: [PATCH 09/56] Remove unused using --- osu.Game/Rulesets/Mods/ModRateAdjust.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModRateAdjust.cs b/osu.Game/Rulesets/Mods/ModRateAdjust.cs index 9143836fb7..1739524bcd 100644 --- a/osu.Game/Rulesets/Mods/ModRateAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModRateAdjust.cs @@ -1,7 +1,6 @@ // 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.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; From 47b6b0173913ad78ac3beaf702676b0e929c8515 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 20:11:48 +0900 Subject: [PATCH 10/56] Rename class to signify it is a drawable --- ...ointConnection.cs => PathControlPointConnectionPiece.cs} | 4 ++-- .../Sliders/Components/PathControlPointVisualiser.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/{PathControlPointConnection.cs => PathControlPointConnectionPiece.cs} (92%) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs similarity index 92% rename from osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs rename to osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index f57299c5a9..4dfe7834fd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnection.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { - public class PathControlPointConnection : CompositeDrawable + public class PathControlPointConnectionPiece : CompositeDrawable { public PathControlPoint ControlPoint; @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable sliderPosition; private IBindable pathVersion; - public PathControlPointConnection(Slider slider, PathControlPoint controlPoint) + public PathControlPointConnectionPiece(Slider slider, PathControlPoint controlPoint) { this.slider = slider; ControlPoint = controlPoint; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 64a8faa02a..a97c0b4a72 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { internal readonly Container Pieces; - private readonly Container connections; + private readonly Container connections; private readonly Slider slider; @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components InternalChildren = new Drawable[] { - connections = new Container { RelativeSizeAxes = Axes.Both }, + connections = new Container { RelativeSizeAxes = Axes.Both }, Pieces = new Container { RelativeSizeAxes = Axes.Both } }; } @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components d.RequestSelection = selectPiece; })); - connections.Add(new PathControlPointConnection(slider, point)); + connections.Add(new PathControlPointConnectionPiece(slider, point)); } } From 50377e728637b48c96769e1917cc628e4e70a1c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Dec 2019 20:14:16 +0900 Subject: [PATCH 11/56] Add summary xmldoc --- .../Sliders/Components/PathControlPointConnectionPiece.cs | 3 +++ .../Blueprints/Sliders/Components/PathControlPointPiece.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 4dfe7834fd..0fc441fec6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -11,6 +11,9 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { + /// + /// A visualisation of the line between two s. + /// public class PathControlPointConnectionPiece : CompositeDrawable { public PathControlPoint ControlPoint; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 42812ff934..6a0730db91 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -18,6 +18,9 @@ using osuTK.Input; namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { + /// + /// A visualisation of a single in a . + /// public class PathControlPointPiece : BlueprintPiece { public Action RequestSelection; From ad2528d4d2e03a1c355c84934dd73104b1afb81e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 15:05:29 +0900 Subject: [PATCH 12/56] Hide key counter along with other hud elements Also tidies up HUD hide logic and protects against incorrect hiding. --- osu.Game/Screens/Play/HUDOverlay.cs | 60 ++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 0f9edf5606..35157fca58 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; @@ -43,8 +44,15 @@ namespace osu.Game.Screens.Play private readonly DrawableRuleset drawableRuleset; private readonly IReadOnlyList mods; - private Bindable showHud; + /// + /// Whether the elements that can optionally be hidden should be visible. + /// + public Bindable ShowHud { get; } = new BindableBool(); + + private Bindable configShowHud; + private readonly Container visibilityContainer; + private readonly BindableBool replayLoaded = new BindableBool(); private static bool hasShownNotificationOnce; @@ -53,6 +61,8 @@ namespace osu.Game.Screens.Play private readonly Container topScoreContainer; + private IEnumerable hideTargets => new Drawable[] { visibilityContainer, KeyCounter }; + public HUDOverlay(ScoreProcessor scoreProcessor, DrawableRuleset drawableRuleset, IReadOnlyList mods) { this.scoreProcessor = scoreProcessor; @@ -73,8 +83,6 @@ namespace osu.Game.Screens.Play Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, - AutoSizeDuration = 200, - AutoSizeEasing = Easing.Out, Children = new Drawable[] { AccuracyCounter = CreateAccuracyCounter(), @@ -95,6 +103,8 @@ namespace osu.Game.Screens.Play Origin = Anchor.BottomRight, Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y), AutoSizeAxes = Axes.Both, + AutoSizeDuration = 150, + AutoSizeEasing = Easing.OutQuint, Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -118,8 +128,29 @@ namespace osu.Game.Screens.Play ModDisplay.Current.Value = mods; - showHud = config.GetBindable(OsuSetting.ShowInterface); - showHud.BindValueChanged(visible => visibilityContainer.FadeTo(visible.NewValue ? 1 : 0, duration, easing), true); + configShowHud = config.GetBindable(OsuSetting.ShowInterface); + + if (!configShowHud.Value && !hasShownNotificationOnce) + { + hasShownNotificationOnce = true; + + notificationOverlay?.Post(new SimpleNotification + { + Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." + }); + } + + // start all elements hidden + hideTargets.ForEach(d => d.Hide()); + } + + public override void Hide() => throw new InvalidOperationException($"{nameof(HUDOverlay)} should not be hidden as it will remove the ability of a user to quit. Use {nameof(ShowHud)} instead."); + + protected override void LoadComplete() + { + base.LoadComplete(); + + ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, duration, easing))); ShowHealthbar.BindValueChanged(healthBar => { @@ -135,20 +166,11 @@ namespace osu.Game.Screens.Play } }, true); - if (!showHud.Value && !hasShownNotificationOnce) + configShowHud.BindValueChanged(visible => { - hasShownNotificationOnce = true; - - notificationOverlay?.Post(new SimpleNotification - { - Text = @"The score overlay is currently disabled. You can toggle this by pressing Shift+Tab." - }); - } - } - - protected override void LoadComplete() - { - base.LoadComplete(); + if (!ShowHud.Disabled) + ShowHud.Value = visible.NewValue; + }, true); replayLoaded.BindValueChanged(replayLoadedValueChanged, true); } @@ -189,7 +211,7 @@ namespace osu.Game.Screens.Play switch (e.Key) { case Key.Tab: - showHud.Value = !showHud.Value; + configShowHud.Value = !configShowHud.Value; return true; } } From ffb5cdc6aef7056a8430cd76f79299d5ee087d84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 14:19:04 +0900 Subject: [PATCH 13/56] Hide settings overlay along with other HUD-hidden content --- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 35157fca58..64c3cddf2a 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -94,9 +94,9 @@ namespace osu.Game.Screens.Play Progress = CreateProgress(), ModDisplay = CreateModsContainer(), HitErrorDisplay = CreateHitErrorDisplayOverlay(), + PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), } }, - PlayerSettingsOverlay = CreatePlayerSettingsOverlay(), new FillFlowContainer { Anchor = Anchor.BottomRight, From 4c4199269c267c32ee651369e183c0e4d62a477a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 15:25:37 +0900 Subject: [PATCH 14/56] Use protected constructors --- osu.Game/Rulesets/Mods/ModDaycore.cs | 2 +- osu.Game/Rulesets/Mods/ModNightcore.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 9445895fb0..71a666414f 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods private readonly BindableNumber tempoAdjust = new BindableDouble(1); private readonly BindableNumber freqAdjust = new BindableDouble(1); - public ModDaycore() + protected ModDaycore() { SpeedChange.BindValueChanged(val => { diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index 9b27925693..c14e02e64d 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mods private readonly BindableNumber tempoAdjust = new BindableDouble(1); private readonly BindableNumber freqAdjust = new BindableDouble(1); - public ModNightcore() + protected ModNightcore() { SpeedChange.BindValueChanged(val => { From 3ccfee64f6ff44c82f3b66270f790b951c745da5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 16:09:42 +0900 Subject: [PATCH 15/56] Add HUDOverlay tests --- .../Visual/Gameplay/TestSceneHUDOverlay.cs | 81 +++++++++++++++++++ osu.Game/Screens/Play/HUD/HitErrorDisplay.cs | 7 +- osu.Game/Screens/Play/HUDOverlay.cs | 19 +++-- 3 files changed, 98 insertions(+), 9 deletions(-) create mode 100644 osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs new file mode 100644 index 0000000000..39c42980ab --- /dev/null +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneHUDOverlay.cs @@ -0,0 +1,81 @@ +// 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 NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Play; + +namespace osu.Game.Tests.Visual.Gameplay +{ + public class TestSceneHUDOverlay : ManualInputManagerTestScene + { + private HUDOverlay hudOverlay; + + private Drawable hideTarget => hudOverlay.KeyCounter; // best way of checking hideTargets without exposing. + + [Resolved] + private OsuConfigManager config { get; set; } + + [Test] + public void TestShownByDefault() + { + createNew(); + + AddAssert("showhud is set", () => hudOverlay.ShowHud.Value); + + AddAssert("hidetarget is visible", () => hideTarget.IsPresent); + AddAssert("pause button is visible", () => hudOverlay.HoldToQuit.IsPresent); + } + + [Test] + public void TestFadesInOnLoadComplete() + { + float? initialAlpha = null; + + createNew(h => h.OnLoadComplete += _ => initialAlpha = hideTarget.Alpha); + AddUntilStep("wait for load", () => hudOverlay.IsAlive); + AddAssert("initial alpha was less than 1", () => initialAlpha != null && initialAlpha < 1); + } + + [Test] + public void TestHideExternally() + { + createNew(); + + AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false); + + AddUntilStep("hidetarget is hidden", () => !hideTarget.IsPresent); + AddAssert("pause button is still visible", () => hudOverlay.HoldToQuit.IsPresent); + } + + [Test] + public void TestExternalHideDoesntAffectConfig() + { + bool originalConfigValue = false; + + createNew(); + + AddStep("get original config value", () => originalConfigValue = config.Get(OsuSetting.ShowInterface)); + + AddStep("set showhud false", () => hudOverlay.ShowHud.Value = false); + AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.ShowInterface)); + + AddStep("set showhud true", () => hudOverlay.ShowHud.Value = true); + AddAssert("config unchanged", () => originalConfigValue == config.Get(OsuSetting.ShowInterface)); + } + + private void createNew(Action action = null) + { + AddStep("create overlay", () => + { + Child = hudOverlay = new HUDOverlay(null, null, Array.Empty()); + + action?.Invoke(hudOverlay); + }); + } + } +} diff --git a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs index 54556f8648..6196ce4026 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorDisplay.cs @@ -31,7 +31,8 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = Axes.Both; - processor.NewJudgement += onNewJudgement; + if (processor != null) + processor.NewJudgement += onNewJudgement; } [BackgroundDependencyLoader] @@ -96,7 +97,9 @@ namespace osu.Game.Screens.Play.HUD protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - processor.NewJudgement -= onNewJudgement; + + if (processor != null) + processor.NewJudgement -= onNewJudgement; } } } diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 64c3cddf2a..7df780b678 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -118,13 +118,18 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader(true)] private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) { - BindProcessor(scoreProcessor); - BindDrawableRuleset(drawableRuleset); + if (scoreProcessor != null) + BindProcessor(scoreProcessor); - Progress.Objects = drawableRuleset.Objects; - Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value; - Progress.RequestSeek = time => RequestSeek(time); - Progress.ReferenceClock = drawableRuleset.FrameStableClock; + if (drawableRuleset != null) + { + BindDrawableRuleset(drawableRuleset); + + Progress.Objects = drawableRuleset.Objects; + Progress.AllowSeeking = drawableRuleset.HasReplayLoaded.Value; + Progress.RequestSeek = time => RequestSeek(time); + Progress.ReferenceClock = drawableRuleset.FrameStableClock; + } ModDisplay.Current.Value = mods; @@ -279,7 +284,7 @@ namespace osu.Game.Screens.Play Margin = new MarginPadding { Top = 20, Right = 10 }, }; - protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset.FirstAvailableHitWindows); + protected virtual HitErrorDisplay CreateHitErrorDisplayOverlay() => new HitErrorDisplay(scoreProcessor, drawableRuleset?.FirstAvailableHitWindows); protected virtual PlayerSettingsOverlay CreatePlayerSettingsOverlay() => new PlayerSettingsOverlay(); From 94f3dbb2f65afef1aaff9f63a7070efa11f0985b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 16:09:50 +0900 Subject: [PATCH 16/56] Adjust transitions slightly --- osu.Game/Screens/Play/HUDOverlay.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 7df780b678..dc32fc7cd5 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -24,8 +24,8 @@ namespace osu.Game.Screens.Play { public class HUDOverlay : Container { - private const int duration = 250; - private const Easing easing = Easing.OutQuint; + private const int fade_duration = 400; + private const Easing fade_easing = Easing.Out; public readonly KeyCounterDisplay KeyCounter; public readonly RollingCounter ComboCounter; @@ -103,8 +103,8 @@ namespace osu.Game.Screens.Play Origin = Anchor.BottomRight, Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y), AutoSizeAxes = Axes.Both, - AutoSizeDuration = 150, - AutoSizeEasing = Easing.OutQuint, + AutoSizeDuration = fade_duration, + AutoSizeEasing = fade_easing, Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -155,19 +155,19 @@ namespace osu.Game.Screens.Play { base.LoadComplete(); - ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, duration, easing))); + ShowHud.BindValueChanged(visible => hideTargets.ForEach(d => d.FadeTo(visible.NewValue ? 1 : 0, fade_duration, fade_easing))); ShowHealthbar.BindValueChanged(healthBar => { if (healthBar.NewValue) { - HealthDisplay.FadeIn(duration, easing); - topScoreContainer.MoveToY(30, duration, easing); + HealthDisplay.FadeIn(fade_duration, fade_easing); + topScoreContainer.MoveToY(30, fade_duration, fade_easing); } else { - HealthDisplay.FadeOut(duration, easing); - topScoreContainer.MoveToY(0, duration, easing); + HealthDisplay.FadeOut(fade_duration, fade_easing); + topScoreContainer.MoveToY(0, fade_duration, fade_easing); } }, true); From a0792f82e8bb8031cd97c22cdadd6109a0445e04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 17:05:47 +0900 Subject: [PATCH 17/56] Re-jig mod select logic to reduce event fires --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e8ea43e3f2..a6f9642a56 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -360,8 +360,8 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); + SelectedMods.BindValueChanged(selectedModsChanged); Ruleset.BindValueChanged(rulesetChanged, true); - SelectedMods.BindValueChanged(selectedModsChanged, true); } protected override void PopOut() From c4bc57484fdee19279ac616d690891494d67e0a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 17:12:01 +0900 Subject: [PATCH 18/56] Fix test logic and add regression test --- .../UserInterface/TestSceneModSettings.cs | 84 ++++++++++++++----- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index fc44c5f595..8117a4ad78 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -2,15 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; +using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; namespace osu.Game.Tests.Visual.UserInterface { @@ -18,28 +23,51 @@ namespace osu.Game.Tests.Visual.UserInterface { private TestModSelectOverlay modSelect; - [BackgroundDependencyLoader] - private void load() + Mod testCustomisableMod = new TestModCustomisable1(); + + [Test] + public void TestButtonShowsOnCustomisableMod() { - Add(modSelect = new TestModSelectOverlay - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - }); + createModSelect(); - var testMod = new TestModCustomisable1(); - - AddStep("open", modSelect.Show); + AddStep("open", () => modSelect.Show()); AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); - AddStep("select mod", () => modSelect.SelectMod(testMod)); + AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod)); AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); AddStep("open Customisation", () => modSelect.CustomiseButton.Click()); - AddStep("deselect mod", () => modSelect.SelectMod(testMod)); + AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod)); AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0); } + [Test] + public void TestButtonShowsOnModAlreadyAdded() + { + AddStep("set active mods", () => Mods.Value = new List { testCustomisableMod }); + + createModSelect(); + + AddAssert("mods still active", () => Mods.Value.Count == 1); + + AddStep("open", () => modSelect.Show()); + AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); + } + + private void createModSelect() + { + AddStep("create mod select", () => + { + Ruleset.Value = new TestRulesetInfo(); + + Child = modSelect = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }; + }); + } + private class TestModSelectOverlay : ModSelectOverlay { public new Container ModSettingsContainer => base.ModSettingsContainer; @@ -50,24 +78,36 @@ namespace osu.Game.Tests.Visual.UserInterface public void SelectMod(Mod mod) => ModSectionsContainer.Children.Single(s => s.ModType == mod.Type) .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); + } - protected override void LoadComplete() + public class TestRulesetInfo : RulesetInfo + { + public override Ruleset CreateInstance() => new TestCustomisableModRuleset(); + + public class TestCustomisableModRuleset : Ruleset { - base.LoadComplete(); - - foreach (var section in ModSectionsContainer) + public override IEnumerable GetModsFor(ModType type) { - if (section.ModType == ModType.Conversion) + if (type == ModType.Conversion) { - section.Mods = new Mod[] + return new Mod[] { new TestModCustomisable1(), new TestModCustomisable2() }; } - else - section.Mods = Array.Empty(); + + return Array.Empty(); } + + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => throw new NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + + public override string Description { get; } + public override string ShortName { get; } } } From 623ab1ef3be8f3ef21b71797622894690b21d735 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 17:28:31 +0900 Subject: [PATCH 19/56] Update time ramp preview on setting change --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index e10afa7d7c..5276c196f7 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -35,6 +35,12 @@ namespace osu.Game.Rulesets.Mods private Track track; + public ModTimeRamp() + { + // for preview purpose at song select. eventually we'll want to be able to update every frame. + FinalRate.BindValueChanged(val => applyAdjustment(1), true); + } + public void ApplyToTrack(Track track) { this.track = track; From 952bc96bbf3223b2415a486c3b8b922e57328ee4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:05:38 +0900 Subject: [PATCH 20/56] Use GameBase data sources for Beatmap/Mods/Ruleset Sourced in via OsuScreenDependencies for management --- osu.Game/Tests/Visual/OsuTestScene.cs | 40 ++++++++++----------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 4ca0ec0f7e..4a561f17a3 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -12,7 +12,6 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Textures; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Framework.Timing; @@ -21,6 +20,7 @@ using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Screens; using osu.Game.Storyboards; using osu.Game.Tests.Beatmaps; @@ -28,21 +28,13 @@ namespace osu.Game.Tests.Visual { public abstract class OsuTestScene : TestScene { - [Cached(typeof(Bindable))] - [Cached(typeof(IBindable))] - private NonNullableBindable beatmap; + protected Bindable Beatmap { get; private set; } - protected Bindable Beatmap => beatmap; + protected Bindable Ruleset; - [Cached] - [Cached(typeof(IBindable))] - protected readonly Bindable Ruleset = new Bindable(); + protected Bindable> Mods; - [Cached] - [Cached(Type = typeof(IBindable>))] - protected readonly Bindable> Mods = new Bindable>(Array.Empty()); - - protected new DependencyContainer Dependencies { get; private set; } + protected new OsuScreenDependencies Dependencies { get; private set; } private readonly Lazy localStorage; protected Storage LocalStorage => localStorage.Value; @@ -72,18 +64,16 @@ namespace osu.Game.Tests.Visual protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) { - // This is the earliest we can get OsuGameBase, which is used by the dummy working beatmap to find textures - var working = new DummyWorkingBeatmap(parent.Get(), parent.Get()); + Dependencies = new OsuScreenDependencies(false, base.CreateChildDependencies(parent)); - beatmap = new NonNullableBindable(working) { Default = working }; - beatmap.BindValueChanged(b => ScheduleAfterChildren(() => - { - // compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo) - if (b.OldValue?.TrackLoaded == true && b.OldValue?.Track != b.NewValue?.Track) - b.OldValue.RecycleTrack(); - })); + Beatmap = Dependencies.Beatmap; + Beatmap.SetDefault(); - Dependencies = new DependencyContainer(base.CreateChildDependencies(parent)); + Ruleset = Dependencies.Ruleset; + Ruleset.SetDefault(); + + Mods = Dependencies.Mods; + Mods.SetDefault(); if (!UseOnlineAPI) { @@ -135,8 +125,8 @@ namespace osu.Game.Tests.Visual { base.Dispose(isDisposing); - if (beatmap?.Value.TrackLoaded == true) - beatmap.Value.Track.Stop(); + if (Beatmap?.Value.TrackLoaded == true) + Beatmap.Value.Track.Stop(); if (contextFactory.IsValueCreated) contextFactory.Value.ResetDatabase(); From 395b058ff872a28cf74554088ec04f26eff319d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:05:54 +0900 Subject: [PATCH 21/56] Fix OsuScreenDependencies not caching non-leased versions --- osu.Game/Screens/OsuScreenDependencies.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game/Screens/OsuScreenDependencies.cs b/osu.Game/Screens/OsuScreenDependencies.cs index 115f4b7e1a..8d54829d49 100644 --- a/osu.Game/Screens/OsuScreenDependencies.cs +++ b/osu.Game/Screens/OsuScreenDependencies.cs @@ -26,16 +26,26 @@ namespace osu.Game.Screens Beatmap = parent.Get>()?.GetBoundCopy(); if (Beatmap == null) + { Cache(Beatmap = parent.Get>().BeginLease(false)); + CacheAs(Beatmap); + } Ruleset = parent.Get>()?.GetBoundCopy(); if (Ruleset == null) + { Cache(Ruleset = parent.Get>().BeginLease(true)); + CacheAs(Ruleset); + } Mods = parent.Get>>()?.GetBoundCopy(); + if (Mods == null) + { Cache(Mods = parent.Get>>().BeginLease(true)); + CacheAs(Mods); + } } else { From 3dc2b59d2a066e4827f52aff6c7951cd72cfddd4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:06:12 +0900 Subject: [PATCH 22/56] Move variable above common bindables --- osu.Game/Screens/OsuScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 94165fe4b7..6394fb8d23 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -87,12 +87,12 @@ namespace osu.Game.Screens public virtual float BackgroundParallaxAmount => 1; + public virtual bool AllowRateAdjustments => true; + public Bindable Beatmap { get; private set; } public Bindable Ruleset { get; private set; } - public virtual bool AllowRateAdjustments => true; - public Bindable> Mods { get; private set; } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) From 7033974733537e4d32984f4efeae2c578534fc04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:06:56 +0900 Subject: [PATCH 23/56] Fix test regressions (incorrect from the start) --- .../TestSceneDrawableHitObjectsHidden.cs | 6 ++++-- osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs | 5 +++-- osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs | 5 +++-- osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs | 5 +++-- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs index f6d26addaa..08107e01eb 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using NUnit.Framework; using osu.Game.Rulesets.Catch.Mods; namespace osu.Game.Rulesets.Catch.Tests @@ -12,9 +13,10 @@ namespace osu.Game.Rulesets.Catch.Tests { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(CatchModHidden) }).ToList(); - public TestSceneDrawableHitObjectsHidden() + [SetUp] + public void SetUp() => Schedule(() => { Mods.Value = new[] { new CatchModHidden() }; - } + }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs index 55c6b22146..eaba82a469 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs @@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - public TestSceneHitCircleHidden() + [SetUp] + public void SetUp() => Schedule(() => { Mods.Value = new[] { new OsuModHidden() }; - } + }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs index 2a9c1d167b..a1795fb877 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs @@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - public TestSceneSliderHidden() + [SetUp] + public void SetUp() => Schedule(() => { Mods.Value = new[] { new OsuModHidden() }; - } + }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs index a0ab1908d6..2976d4fdce 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs @@ -14,9 +14,10 @@ namespace osu.Game.Rulesets.Osu.Tests { public override IReadOnlyList RequiredTypes => base.RequiredTypes.Concat(new[] { typeof(OsuModHidden) }).ToList(); - public TestSceneSpinnerHidden() + [SetUp] + public void SetUp() => Schedule(() => { Mods.Value = new[] { new OsuModHidden() }; - } + }); } } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index be50200e1c..8cf2d29906 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -124,7 +124,7 @@ namespace osu.Game.Tests.Visual.UserInterface var easierMods = instance.GetModsFor(ModType.DifficultyReduction); var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); - AddStep("set mods externally", () => { modDisplay.Current.Value = new[] { noFailMod }; }); + AddStep("set mods externally", () => { Mods.Value = new[] { noFailMod }; }); changeRuleset(rulesetOsu); From a1f8ab1735b755bd98b27697dbf3f99828716f28 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:13:53 +0900 Subject: [PATCH 24/56] Fix unrequired type keyword --- osu.Game/OsuGameBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index f310da3883..207fb91aab 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -80,7 +80,7 @@ namespace osu.Game // todo: move this to SongSelect once Screen has the ability to unsuspend. [Cached] - [Cached(Type = typeof(IBindable>))] + [Cached(typeof(IBindable>))] protected readonly Bindable> Mods = new Bindable>(Array.Empty()); protected Bindable Beatmap { get; private set; } // cached via load() method From 0bbaf9b7fbe760d4b57ca66bf82c114e952cf824 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:21:35 +0900 Subject: [PATCH 25/56] Fix mod select overlay tests not running individually --- .../TestSceneModSelectOverlay.cs | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 8cf2d29906..5f77116c67 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -8,13 +8,16 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods.Sections; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.UI; using osu.Game.Screens.Play.HUD; @@ -48,42 +51,48 @@ namespace osu.Game.Tests.Visual.UserInterface private void load(RulesetStore rulesets) { this.rulesets = rulesets; + } - Add(modSelect = new TestModSelectOverlay + [SetUp] + public void SetUp() => Schedule(() => + { + Children = new Drawable[] { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - }); + modSelect = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }, - Add(modDisplay = new ModDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - AutoSizeAxes = Axes.Both, - Position = new Vector2(0, 25), - }); + modDisplay = new ModDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Position = new Vector2(0, 25), + Current = { BindTarget = modSelect.SelectedMods } + } + }; + }); - modDisplay.Current.UnbindBindings(); - modDisplay.Current.BindTo(modSelect.SelectedMods); - - AddStep("Show", modSelect.Show); - AddStep("Toggle", modSelect.ToggleVisibility); - AddStep("Toggle", modSelect.ToggleVisibility); + [SetUpSteps] + public void SetUpSteps() + { + AddStep("show", () => modSelect.Show()); } [Test] public void TestOsuMods() { - var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 0); - changeRuleset(ruleset); + changeRuleset(0); - var instance = ruleset.CreateInstance(); + var osu = new OsuRuleset(); - var easierMods = instance.GetModsFor(ModType.DifficultyReduction); - var harderMods = instance.GetModsFor(ModType.DifficultyIncrease); + var easierMods = osu.GetModsFor(ModType.DifficultyReduction); + var harderMods = osu.GetModsFor(ModType.DifficultyIncrease); - var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); + var noFailMod = osu.GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); var hiddenMod = harderMods.FirstOrDefault(m => m is OsuModHidden); var doubleTimeMod = harderMods.OfType().FirstOrDefault(m => m.Mods.Any(a => a is OsuModDoubleTime)); @@ -97,8 +106,8 @@ namespace osu.Game.Tests.Visual.UserInterface testMultiMod(doubleTimeMod); testIncompatibleMods(easy, hardRock); testDeselectAll(easierMods.Where(m => !(m is MultiMod))); - testMultiplierTextColour(noFailMod, modSelect.LowMultiplierColour); - testMultiplierTextColour(hiddenMod, modSelect.HighMultiplierColour); + testMultiplierTextColour(noFailMod, () => modSelect.LowMultiplierColour); + testMultiplierTextColour(hiddenMod, () => modSelect.HighMultiplierColour); testUnimplementedMod(spunOutMod); } @@ -106,37 +115,31 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestManiaMods() { - var ruleset = rulesets.AvailableRulesets.First(r => r.ID == 3); - changeRuleset(ruleset); + changeRuleset(3); - testRankedText(ruleset.CreateInstance().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom)); + testRankedText(new ManiaRuleset().GetModsFor(ModType.Conversion).First(m => m is ManiaModRandom)); } [Test] public void TestRulesetChanges() { - var rulesetOsu = rulesets.AvailableRulesets.First(r => r.ID == 0); - var rulesetMania = rulesets.AvailableRulesets.First(r => r.ID == 3); + changeRuleset(0); - changeRuleset(null); - - var instance = rulesetOsu.CreateInstance(); - var easierMods = instance.GetModsFor(ModType.DifficultyReduction); - var noFailMod = easierMods.FirstOrDefault(m => m is OsuModNoFail); + var noFailMod = new OsuRuleset().GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); AddStep("set mods externally", () => { Mods.Value = new[] { noFailMod }; }); - changeRuleset(rulesetOsu); + changeRuleset(0); AddAssert("ensure mods still selected", () => modDisplay.Current.Value.Single(m => m is OsuModNoFail) != null); - changeRuleset(rulesetMania); + changeRuleset(3); - AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any(m => m is OsuModNoFail)); + AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); - changeRuleset(rulesetOsu); + changeRuleset(0); - AddAssert("ensure mods not selected", () => !modDisplay.Current.Value.Any()); + AddAssert("ensure mods not selected", () => modDisplay.Current.Value.Count == 0); } private void testSingleMod(Mod mod) @@ -198,19 +201,19 @@ namespace osu.Game.Tests.Visual.UserInterface selectNext(mod); AddAssert("check for any selection", () => modSelect.SelectedMods.Value.Any()); - AddStep("deselect all", modSelect.DeselectAllButton.Action.Invoke); + AddStep("deselect all", () => modSelect.DeselectAllButton.Action.Invoke()); AddAssert("check for no selection", () => !modSelect.SelectedMods.Value.Any()); } - private void testMultiplierTextColour(Mod mod, Color4 colour) + private void testMultiplierTextColour(Mod mod, Func getCorrectColour) { - checkLabelColor(Color4.White); + checkLabelColor(() => Color4.White); selectNext(mod); AddWaitStep("wait for changing colour", 1); - checkLabelColor(colour); + checkLabelColor(getCorrectColour); selectPrevious(mod); AddWaitStep("wait for changing colour", 1); - checkLabelColor(Color4.White); + checkLabelColor(() => Color4.White); } private void testRankedText(Mod mod) @@ -235,9 +238,9 @@ namespace osu.Game.Tests.Visual.UserInterface }); } - private void changeRuleset(RulesetInfo ruleset) + private void changeRuleset(int? id) { - AddStep($"change ruleset to {ruleset}", () => { Ruleset.Value = ruleset; }); + AddStep($"change ruleset to {(id.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); }); waitForLoad(); } @@ -253,7 +256,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); } - private void checkLabelColor(Color4 color) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == color); + private void checkLabelColor(Func getColour) => AddAssert("check label has expected colour", () => modSelect.MultiplierLabel.Colour.AverageColour == getColour()); private class TestModSelectOverlay : ModSelectOverlay { From 34f67b9cadf7a01e8bae1a31dcc8e0b2435ca40f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:31:20 +0900 Subject: [PATCH 26/56] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 252570a150..8b266b08ba 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index a07348b57c..9ec833c9ac 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 544bba3963..1829cbe32a 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From f349e7ff78f9f3a7dec1851241c5478209dc8ca2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 20:35:34 +0900 Subject: [PATCH 27/56] Fix non-null ?? usage --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 5f77116c67..2738b5b8fc 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -240,7 +240,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void changeRuleset(int? id) { - AddStep($"change ruleset to {(id.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); }); + AddStep($"change ruleset to {(id?.ToString() ?? "none")}", () => { Ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(r => r.ID == id); }); waitForLoad(); } From 440a8470e146276a20899dcb8b8513a02b1fa622 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 18:53:25 +0900 Subject: [PATCH 28/56] Move available mods to global context This also tidies up ModSelectOverlay and setting creation flow in general. --- osu.Game/OsuGameBase.cs | 17 ++++++++ osu.Game/Overlays/Mods/ModControlSection.cs | 10 ++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 43 ++++++++------------- 3 files changed, 37 insertions(+), 33 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 207fb91aab..78a035c5d6 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -83,6 +83,11 @@ namespace osu.Game [Cached(typeof(IBindable>))] protected readonly Bindable> Mods = new Bindable>(Array.Empty()); + /// + /// Mods available for the current . + /// + public readonly Bindable>> AvailableMods = new Bindable>>(); + protected Bindable Beatmap { get; private set; } // cached via load() method private Bindable fpsDisplayVisible; @@ -233,6 +238,18 @@ namespace osu.Game PreviewTrackManager previewTrackManager; dependencies.Cache(previewTrackManager = new PreviewTrackManager()); Add(previewTrackManager); + + Ruleset.BindValueChanged(onRulesetChanged); + } + + private void onRulesetChanged(ValueChangedEvent r) + { + var dict = new Dictionary>(); + + foreach (ModType type in Enum.GetValues(typeof(ModType))) dict[type] = r.NewValue?.CreateInstance().GetModsFor(type).ToList(); + + SelectedMods.Value = Array.Empty(); + AvailableMods.Value = dict; } protected virtual Container CreateScalingContainer() => new DrawSizePreservingFillContainer(); diff --git a/osu.Game/Overlays/Mods/ModControlSection.cs b/osu.Game/Overlays/Mods/ModControlSection.cs index f4b588ddb3..10b3bc7c2b 100644 --- a/osu.Game/Overlays/Mods/ModControlSection.cs +++ b/osu.Game/Overlays/Mods/ModControlSection.cs @@ -1,10 +1,10 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; @@ -12,14 +12,13 @@ using osuTK; namespace osu.Game.Overlays.Mods { - public class ModControlSection : Container + public class ModControlSection : CompositeDrawable { protected FillFlowContainer FlowContent; - protected override Container Content => FlowContent; public readonly Mod Mod; - public ModControlSection(Mod mod) + public ModControlSection(Mod mod, IEnumerable modControls) { Mod = mod; @@ -33,9 +32,8 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Vertical, AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, + ChildrenEnumerable = modControls }; - - AddRange(Mod.CreateSettingsControls()); } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index e8ea43e3f2..247f4a2947 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -20,7 +20,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods.Sections; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Screens; using osuTK; @@ -50,7 +49,7 @@ namespace osu.Game.Overlays.Mods protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); - protected readonly IBindable Ruleset = new Bindable(); + private Bindable>> availableMods; protected Color4 LowMultiplierColour; protected Color4 HighMultiplierColour; @@ -322,14 +321,14 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader(true)] - private void load(OsuColour colours, IBindable ruleset, AudioManager audio, Bindable> mods) + private void load(OsuColour colours, AudioManager audio, Bindable> selectedMods, OsuGameBase osu) { LowMultiplierColour = colours.Red; HighMultiplierColour = colours.Green; UnrankedLabel.Colour = colours.Blue; - Ruleset.BindTo(ruleset); - if (mods != null) SelectedMods.BindTo(mods); + availableMods = osu.AvailableMods.GetBoundCopy(); + SelectedMods.BindTo(selectedMods); sampleOn = audio.Samples.Get(@"UI/check-on"); sampleOff = audio.Samples.Get(@"UI/check-off"); @@ -360,7 +359,7 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - Ruleset.BindValueChanged(rulesetChanged, true); + availableMods.BindValueChanged(availableModsChanged, true); SelectedMods.BindValueChanged(selectedModsChanged, true); } @@ -410,22 +409,12 @@ namespace osu.Game.Overlays.Mods return base.OnKeyDown(e); } - private void rulesetChanged(ValueChangedEvent e) + private void availableModsChanged(ValueChangedEvent>> mods) { - if (e.NewValue == null) return; - - var instance = e.NewValue.CreateInstance(); + if (mods.NewValue == null) return; foreach (var section in ModSectionsContainer.Children) - section.Mods = instance.GetModsFor(section.ModType); - - // attempt to re-select any already selected mods. - // this may be the first time we are receiving the ruleset, in which case they will still match. - selectedModsChanged(new ValueChangedEvent>(SelectedMods.Value, SelectedMods.Value)); - - // write the mods back to the SelectedMods bindable in the case a change was not applicable. - // this generally isn't required as the previous line will perform deselection; just here for safety. - refreshSelectedMods(); + section.Mods = mods.NewValue[section.ModType]; } private void selectedModsChanged(ValueChangedEvent> mods) @@ -462,17 +451,17 @@ namespace osu.Game.Overlays.Mods private void updateModSettings(ValueChangedEvent> selectedMods) { - foreach (var added in selectedMods.NewValue.Except(selectedMods.OldValue)) + ModSettingsContent.Clear(); + + foreach (var mod in selectedMods.NewValue) { - var controls = added.CreateSettingsControls().ToList(); - if (controls.Count > 0) - ModSettingsContent.Add(new ModControlSection(added) { Children = controls }); + var settings = mod.CreateSettingsControls().ToList(); + if (settings.Count > 0) + ModSettingsContent.Add(new ModControlSection(mod, settings)); } - foreach (var removed in selectedMods.OldValue.Except(selectedMods.NewValue)) - ModSettingsContent.RemoveAll(section => section.Mod == removed); + bool hasSettings = ModSettingsContent.Count > 0; - bool hasSettings = ModSettingsContent.Children.Count > 0; CustomiseButton.Enabled.Value = hasSettings; if (!hasSettings) @@ -502,7 +491,7 @@ namespace osu.Game.Overlays.Mods { base.Dispose(isDisposing); - Ruleset.UnbindAll(); + availableMods.UnbindAll(); SelectedMods.UnbindAll(); } From 7fdaf338f3af99870215902b66ff759228d53038 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 17:12:01 +0900 Subject: [PATCH 29/56] Fix test logic and add regression test --- .../UserInterface/TestSceneModSettings.cs | 84 ++++++++++++++----- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index fc44c5f595..8117a4ad78 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -2,15 +2,20 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using System.Linq; -using osu.Framework.Allocation; +using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Mods; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.UI; namespace osu.Game.Tests.Visual.UserInterface { @@ -18,28 +23,51 @@ namespace osu.Game.Tests.Visual.UserInterface { private TestModSelectOverlay modSelect; - [BackgroundDependencyLoader] - private void load() + Mod testCustomisableMod = new TestModCustomisable1(); + + [Test] + public void TestButtonShowsOnCustomisableMod() { - Add(modSelect = new TestModSelectOverlay - { - RelativeSizeAxes = Axes.X, - Origin = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - }); + createModSelect(); - var testMod = new TestModCustomisable1(); - - AddStep("open", modSelect.Show); + AddStep("open", () => modSelect.Show()); AddAssert("button disabled", () => !modSelect.CustomiseButton.Enabled.Value); AddUntilStep("wait for button load", () => modSelect.ButtonsLoaded); - AddStep("select mod", () => modSelect.SelectMod(testMod)); + AddStep("select mod", () => modSelect.SelectMod(testCustomisableMod)); AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); AddStep("open Customisation", () => modSelect.CustomiseButton.Click()); - AddStep("deselect mod", () => modSelect.SelectMod(testMod)); + AddStep("deselect mod", () => modSelect.SelectMod(testCustomisableMod)); AddAssert("controls hidden", () => modSelect.ModSettingsContainer.Alpha == 0); } + [Test] + public void TestButtonShowsOnModAlreadyAdded() + { + AddStep("set active mods", () => Mods.Value = new List { testCustomisableMod }); + + createModSelect(); + + AddAssert("mods still active", () => Mods.Value.Count == 1); + + AddStep("open", () => modSelect.Show()); + AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); + } + + private void createModSelect() + { + AddStep("create mod select", () => + { + Ruleset.Value = new TestRulesetInfo(); + + Child = modSelect = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.X, + Origin = Anchor.BottomCentre, + Anchor = Anchor.BottomCentre, + }; + }); + } + private class TestModSelectOverlay : ModSelectOverlay { public new Container ModSettingsContainer => base.ModSettingsContainer; @@ -50,24 +78,36 @@ namespace osu.Game.Tests.Visual.UserInterface public void SelectMod(Mod mod) => ModSectionsContainer.Children.Single(s => s.ModType == mod.Type) .ButtonsContainer.OfType().Single(b => b.Mods.Any(m => m.GetType() == mod.GetType())).SelectNext(1); + } - protected override void LoadComplete() + public class TestRulesetInfo : RulesetInfo + { + public override Ruleset CreateInstance() => new TestCustomisableModRuleset(); + + public class TestCustomisableModRuleset : Ruleset { - base.LoadComplete(); - - foreach (var section in ModSectionsContainer) + public override IEnumerable GetModsFor(ModType type) { - if (section.ModType == ModType.Conversion) + if (type == ModType.Conversion) { - section.Mods = new Mod[] + return new Mod[] { new TestModCustomisable1(), new TestModCustomisable2() }; } - else - section.Mods = Array.Empty(); + + return Array.Empty(); } + + public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => throw new NotImplementedException(); + + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); + + public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); + + public override string Description { get; } + public override string ShortName { get; } } } From 76aa4f9fb2ecd55d0e209a197d73ad13f5370bbe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 17:36:37 +0900 Subject: [PATCH 30/56] Fix code style issues --- osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index 8117a4ad78..d17408ff95 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -23,7 +23,7 @@ namespace osu.Game.Tests.Visual.UserInterface { private TestModSelectOverlay modSelect; - Mod testCustomisableMod = new TestModCustomisable1(); + private readonly Mod testCustomisableMod = new TestModCustomisable1(); [Test] public void TestButtonShowsOnCustomisableMod() @@ -106,8 +106,8 @@ namespace osu.Game.Tests.Visual.UserInterface public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => throw new NotImplementedException(); - public override string Description { get; } - public override string ShortName { get; } + public override string Description { get; } = "test"; + public override string ShortName { get; } = "tst"; } } From 8052aeb2383da8302aa677782acb4f2457edd200 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 00:23:32 +0900 Subject: [PATCH 31/56] Fix potential nullref in disposal logic --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 247f4a2947..7f07ce620c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -491,8 +491,8 @@ namespace osu.Game.Overlays.Mods { base.Dispose(isDisposing); - availableMods.UnbindAll(); - SelectedMods.UnbindAll(); + availableMods?.UnbindAll(); + SelectedMods?.UnbindAll(); } #endregion From bc311465609b5ee76dff71e4850feaa409863da6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 13 Dec 2019 21:45:38 +0900 Subject: [PATCH 32/56] Mods -> SelectedMods --- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneDrawableHitObjects.cs | 2 +- .../TestSceneDrawableHitObjectsHidden.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs | 2 +- .../TestSceneHitCircleHidden.cs | 2 +- .../TestSceneOsuFlashlight.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 2 +- .../TestSceneSpinnerHidden.cs | 2 +- .../TestSceneTaikoSuddenDeath.cs | 2 +- .../Visual/Background/TestSceneUserDimBackgrounds.cs | 2 +- osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs | 2 +- .../Visual/Gameplay/TestSceneFailAnimation.cs | 2 +- .../Visual/Gameplay/TestSceneFailJudgement.cs | 2 +- .../Visual/Gameplay/TestSceneGameplayRewinding.cs | 2 +- .../Visual/Gameplay/TestScenePlayerLoader.cs | 6 +++--- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 12 ++++++------ .../UserInterface/TestSceneModSelectOverlay.cs | 2 +- .../Visual/UserInterface/TestSceneModSettings.cs | 6 +++--- osu.Game/OsuGameBase.cs | 2 +- osu.Game/Tests/Visual/AllPlayersTestScene.cs | 2 +- osu.Game/Tests/Visual/OsuTestScene.cs | 6 +++--- osu.Game/Tests/Visual/PlayerTestScene.cs | 4 ++-- 24 files changed, 36 insertions(+), 36 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index ab3c040b4e..74a9c05bf9 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Tests protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); return base.CreatePlayer(ruleset); } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 02a017ce45..1eb913e900 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -151,7 +151,7 @@ namespace osu.Game.Rulesets.Catch.Tests private void addToPlayfield(DrawableCatchHitObject drawable) { - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); drawableRuleset.Playfield.Add(drawable); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs index 08107e01eb..8c3dfef39c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjectsHidden.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Tests [SetUp] public void SetUp() => Schedule(() => { - Mods.Value = new[] { new CatchModHidden() }; + SelectedMods.Value = new[] { new CatchModHidden() }; }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs index 64f353c4d9..098e277fff 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircle.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests var drawable = CreateDrawableHitCircle(circle, auto); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); return drawable; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs index eaba82a469..21ebce8c23 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleHidden.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests [SetUp] public void SetUp() => Schedule(() => { - Mods.Value = new[] { new OsuModHidden() }; + SelectedMods.Value = new[] { new OsuModHidden() }; }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs index 64e7632b1b..412effe176 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuFlashlight.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Osu.Tests { protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; + SelectedMods.Value = new Mod[] { new OsuModAutoplay(), new OsuModFlashlight(), }; return base.CreatePlayer(ruleset); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index a9d5c03517..e8386363be 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests var drawable = CreateDrawableSlider(slider); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); drawable.OnNewResult += onNewResult; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs index a1795fb877..d0ee1bddb5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderHidden.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests [SetUp] public void SetUp() => Schedule(() => { - Mods.Value = new[] { new OsuModHidden() }; + SelectedMods.Value = new[] { new OsuModHidden() }; }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 3ed3f3e981..f53b64c729 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests Depth = depthIndex++ }; - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); Add(drawable); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs index 2976d4fdce..dd863deed2 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerHidden.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Tests [SetUp] public void SetUp() => Schedule(() => { - Mods.Value = new[] { new OsuModHidden() }; + SelectedMods.Value = new[] { new OsuModHidden() }; }); } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs index d0db193738..140433a523 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoSuddenDeath.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Taiko.Tests protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Mods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray(); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { new TaikoModSuddenDeath() }).ToArray(); return new ScoreAccessiblePlayer(); } diff --git a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs index 2d2726bbd3..589ec7e8aa 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneUserDimBackgrounds.cs @@ -280,7 +280,7 @@ namespace osu.Game.Tests.Visual.Background AddUntilStep("Song select has selection", () => songSelect.Carousel.SelectedBeatmap != null); AddStep("Set default user settings", () => { - Mods.Value = Mods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { new OsuModNoFail() }).ToArray(); songSelect.DimLevel.Value = 0.7f; songSelect.BlurLevel.Value = 0.4f; }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs index 5ee109e3dd..069b965d9b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneAutoplay.cs @@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); return new ScoreAccessiblePlayer(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs index f4e8a68819..992c47f856 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailAnimation.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Array.Empty(); + SelectedMods.Value = Array.Empty(); return new FailPlayer(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs index cca6301b02..1580aac8c5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailJudgement.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.Gameplay { protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Array.Empty(); + SelectedMods.Value = Array.Empty(); return new FailPlayer(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs index b2b58a63fb..5336c720a1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplayRewinding.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected override Player CreatePlayer(Ruleset ruleset) { - Mods.Value = Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); + SelectedMods.Value = SelectedMods.Value.Concat(new[] { ruleset.GetAutoplayMod() }).ToArray(); return new RulesetExposingPlayer(); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index f02361e685..f68f4b8b83 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay beforeLoadAction?.Invoke(); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo); - foreach (var mod in Mods.Value.OfType()) + foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToTrack(Beatmap.Value.Track); InputManager.Child = container = new TestPlayerLoaderContainer( @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestEarlyExit() { - AddStep("load dummy beatmap", () => ResetPlayer(false, () => Mods.Value = new[] { new OsuModNightcore() })); + AddStep("load dummy beatmap", () => ResetPlayer(false, () => SelectedMods.Value = new[] { new OsuModNightcore() })); AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1); AddStep("exit loader", () => loader.Exit()); @@ -123,7 +123,7 @@ namespace osu.Game.Tests.Visual.Gameplay TestMod playerMod1 = null; TestMod playerMod2 = null; - AddStep("load player", () => { ResetPlayer(true, () => Mods.Value = new[] { gameMod = new TestMod() }); }); + AddStep("load player", () => { ResetPlayer(true, () => SelectedMods.Value = new[] { gameMod = new TestMod() }); }); AddUntilStep("wait for loader to become current", () => loader.IsCurrentScreen()); AddStep("mouse in centre", () => InputManager.MoveMouseTo(loader.ScreenSpaceDrawQuad.Centre)); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 5dd02c1ddd..00fa95bedc 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -256,17 +256,17 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("change ruleset", () => { - Mods.ValueChanged += onModChange; + SelectedMods.ValueChanged += onModChange; songSelect.Ruleset.ValueChanged += onRulesetChange; Ruleset.Value = new TaikoRuleset().RulesetInfo; - Mods.ValueChanged -= onModChange; + SelectedMods.ValueChanged -= onModChange; songSelect.Ruleset.ValueChanged -= onRulesetChange; }); AddAssert("mods changed before ruleset", () => modChangeIndex < rulesetChangeIndex); - AddAssert("empty mods", () => !Mods.Value.Any()); + AddAssert("empty mods", () => !SelectedMods.Value.Any()); void onModChange(ValueChangedEvent> e) => modChangeIndex = actionIndex++; void onRulesetChange(ValueChangedEvent e) => rulesetChangeIndex = actionIndex++; @@ -275,7 +275,7 @@ namespace osu.Game.Tests.Visual.SongSelect [Test] public void TestModsRetainedBetweenSongSelect() { - AddAssert("empty mods", () => !Mods.Value.Any()); + AddAssert("empty mods", () => !SelectedMods.Value.Any()); createSongSelect(); @@ -285,7 +285,7 @@ namespace osu.Game.Tests.Visual.SongSelect createSongSelect(); - AddAssert("mods retained", () => Mods.Value.Any()); + AddAssert("mods retained", () => SelectedMods.Value.Any()); } [Test] @@ -332,7 +332,7 @@ namespace osu.Game.Tests.Visual.SongSelect private void checkMusicPlaying(bool playing) => AddUntilStep($"music {(playing ? "" : "not ")}playing", () => music.IsPlaying == playing); - private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => Mods.Value = mods); + private void changeMods(params Mod[] mods) => AddStep($"change mods to {string.Join(", ", mods.Select(m => m.Acronym))}", () => SelectedMods.Value = mods); private void changeRuleset(int id) => AddStep($"change ruleset to {id}", () => Ruleset.Value = rulesets.AvailableRulesets.First(r => r.ID == id)); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 2738b5b8fc..12ee4ceb2e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.UserInterface var noFailMod = new OsuRuleset().GetModsFor(ModType.DifficultyReduction).FirstOrDefault(m => m is OsuModNoFail); - AddStep("set mods externally", () => { Mods.Value = new[] { noFailMod }; }); + AddStep("set mods externally", () => { SelectedMods.Value = new[] { noFailMod }; }); changeRuleset(0); diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index d17408ff95..edc749cbaa 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -43,11 +43,11 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestButtonShowsOnModAlreadyAdded() { - AddStep("set active mods", () => Mods.Value = new List { testCustomisableMod }); + AddStep("set active mods", () => SelectedMods.Value = new List { testCustomisableMod }); createModSelect(); - AddAssert("mods still active", () => Mods.Value.Count == 1); + AddAssert("mods still active", () => SelectedMods.Value.Count == 1); AddStep("open", () => modSelect.Show()); AddAssert("button enabled", () => modSelect.CustomiseButton.Enabled.Value); @@ -100,7 +100,7 @@ namespace osu.Game.Tests.Visual.UserInterface return Array.Empty(); } - public override DrawableRuleset CreateDrawableRulesetWith(IWorkingBeatmap beatmap, IReadOnlyList mods) => throw new NotImplementedException(); + public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList mods = null) => throw new NotImplementedException(); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => throw new NotImplementedException(); diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 78a035c5d6..834a64e597 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -81,7 +81,7 @@ namespace osu.Game // todo: move this to SongSelect once Screen has the ability to unsuspend. [Cached] [Cached(typeof(IBindable>))] - protected readonly Bindable> Mods = new Bindable>(Array.Empty()); + protected readonly Bindable> SelectedMods = new Bindable>(Array.Empty()); /// /// Mods available for the current . diff --git a/osu.Game/Tests/Visual/AllPlayersTestScene.cs b/osu.Game/Tests/Visual/AllPlayersTestScene.cs index b7d1979b0d..dd65c8c382 100644 --- a/osu.Game/Tests/Visual/AllPlayersTestScene.cs +++ b/osu.Game/Tests/Visual/AllPlayersTestScene.cs @@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual var working = CreateWorkingBeatmap(rulesetInfo); Beatmap.Value = working; - Mods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; + SelectedMods.Value = new[] { ruleset.GetAllMods().First(m => m is ModNoFail) }; Player?.Exit(); Player = null; diff --git a/osu.Game/Tests/Visual/OsuTestScene.cs b/osu.Game/Tests/Visual/OsuTestScene.cs index 4a561f17a3..18dbd212cc 100644 --- a/osu.Game/Tests/Visual/OsuTestScene.cs +++ b/osu.Game/Tests/Visual/OsuTestScene.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual protected Bindable Ruleset; - protected Bindable> Mods; + protected Bindable> SelectedMods; protected new OsuScreenDependencies Dependencies { get; private set; } @@ -72,8 +72,8 @@ namespace osu.Game.Tests.Visual Ruleset = Dependencies.Ruleset; Ruleset.SetDefault(); - Mods = Dependencies.Mods; - Mods.SetDefault(); + SelectedMods = Dependencies.Mods; + SelectedMods.SetDefault(); if (!UseOnlineAPI) { diff --git a/osu.Game/Tests/Visual/PlayerTestScene.cs b/osu.Game/Tests/Visual/PlayerTestScene.cs index 2c5a51ca02..3ed65bee61 100644 --- a/osu.Game/Tests/Visual/PlayerTestScene.cs +++ b/osu.Game/Tests/Visual/PlayerTestScene.cs @@ -53,14 +53,14 @@ namespace osu.Game.Tests.Visual { var noFailMod = ruleset.GetAllMods().FirstOrDefault(m => m is ModNoFail); if (noFailMod != null) - Mods.Value = new[] { noFailMod }; + SelectedMods.Value = new[] { noFailMod }; } if (Autoplay) { var mod = ruleset.GetAutoplayMod(); if (mod != null) - Mods.Value = Mods.Value.Concat(mod.Yield()).ToArray(); + SelectedMods.Value = SelectedMods.Value.Concat(mod.Yield()).ToArray(); } Player = CreatePlayer(ruleset); From af1566285698e8341a998d8d2f8bf7dea65697dd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Dec 2019 17:45:11 +0900 Subject: [PATCH 33/56] Fix WindUp applying too much change --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 4 +++- osu.Game/Rulesets/Mods/ModWindDown.cs | 8 ++++---- osu.Game/Rulesets/Mods/ModWindUp.cs | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 5276c196f7..4ce364ef1a 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -45,6 +45,8 @@ namespace osu.Game.Rulesets.Mods { this.track = track; track.AddAdjustment(AdjustableProperty.Frequency, SpeedChange); + + FinalRate.TriggerChange(); } public virtual void ApplyToBeatmap(IBeatmap beatmap) @@ -67,6 +69,6 @@ namespace osu.Game.Rulesets.Mods /// /// The amount of adjustment to apply (from 0..1). private void applyAdjustment(double amount) => - SpeedChange.Value = 1 + (Math.Sign(FinalRate.Value) * Math.Clamp(amount, 0, 1) * Math.Abs(FinalRate.Value)); + SpeedChange.Value = 1 + (FinalRate.Value - 1) * Math.Clamp(amount, 0, 1); } } diff --git a/osu.Game/Rulesets/Mods/ModWindDown.cs b/osu.Game/Rulesets/Mods/ModWindDown.cs index 680c1a10fb..5416f1ac22 100644 --- a/osu.Game/Rulesets/Mods/ModWindDown.cs +++ b/osu.Game/Rulesets/Mods/ModWindDown.cs @@ -20,10 +20,10 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Final rate", "The speed increase to ramp towards")] public override BindableNumber FinalRate { get; } = new BindableDouble { - MinValue = -0.5, - MaxValue = -0.01, - Default = -0.25, - Value = -0.25, + MinValue = 0.5, + MaxValue = 0.99, + Default = 0.75, + Value = 0.75, Precision = 0.01, }; diff --git a/osu.Game/Rulesets/Mods/ModWindUp.cs b/osu.Game/Rulesets/Mods/ModWindUp.cs index ca9ce0ea3e..3cf584f3dd 100644 --- a/osu.Game/Rulesets/Mods/ModWindUp.cs +++ b/osu.Game/Rulesets/Mods/ModWindUp.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods [SettingSource("Final rate", "The speed increase to ramp towards")] public override BindableNumber FinalRate { get; } = new BindableDouble { - MinValue = 0.01, + MinValue = 1.01, MaxValue = 2, Default = 1.5, Value = 1.5, From 72ea871bffd35b6cab8ac8b83a6015e628e22d80 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Dec 2019 23:33:18 +0900 Subject: [PATCH 34/56] Make constructor protected --- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 4ce364ef1a..133f9ceb39 100644 --- a/osu.Game/Rulesets/Mods/ModTimeRamp.cs +++ b/osu.Game/Rulesets/Mods/ModTimeRamp.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Mods private Track track; - public ModTimeRamp() + protected ModTimeRamp() { // for preview purpose at song select. eventually we'll want to be able to update every frame. FinalRate.BindValueChanged(val => applyAdjustment(1), true); From ef94df917ceb55dd7cdca2ce116495998d36ea7a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 13 Dec 2019 21:56:03 +0900 Subject: [PATCH 35/56] Add whitespace --- osu.Game/OsuGameBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 834a64e597..d9b569bf0e 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -246,7 +246,8 @@ namespace osu.Game { var dict = new Dictionary>(); - foreach (ModType type in Enum.GetValues(typeof(ModType))) dict[type] = r.NewValue?.CreateInstance().GetModsFor(type).ToList(); + foreach (ModType type in Enum.GetValues(typeof(ModType))) + dict[type] = r.NewValue?.CreateInstance().GetModsFor(type).ToList(); SelectedMods.Value = Array.Empty(); AvailableMods.Value = dict; From b94d5bf82eba5c96f0303e408caf170885a86c3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 14 Dec 2019 00:42:31 +0900 Subject: [PATCH 36/56] Fix error when entering multiplayer game in different ruleset --- osu.Game/OsuGameBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index d9b569bf0e..21cc4eaccc 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -249,7 +249,8 @@ namespace osu.Game foreach (ModType type in Enum.GetValues(typeof(ModType))) dict[type] = r.NewValue?.CreateInstance().GetModsFor(type).ToList(); - SelectedMods.Value = Array.Empty(); + if (!SelectedMods.Disabled) + SelectedMods.Value = Array.Empty(); AvailableMods.Value = dict; } From ddb2cfc46de5ee7881aaa66c9e752109e935e021 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 14 Dec 2019 18:02:56 +0800 Subject: [PATCH 37/56] Use GetEndTime in Taiko and Mania --- osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs | 4 ++-- osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs | 4 ++-- osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs | 3 +-- osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs | 5 +---- osu.Game/Rulesets/Objects/HitObject.cs | 2 +- 5 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs index 059cd39641..4f7ab87fad 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Individual.cs @@ -5,7 +5,7 @@ using System.Linq; using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; -using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Difficulty.Skills { @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var maniaCurrent = (ManiaDifficultyHitObject)current; - var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime; + var endTime = maniaCurrent.BaseObject.GetEndTime(); try { diff --git a/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs index ed25173d38..bbbb93fd8b 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/Skills/Overall.cs @@ -4,7 +4,7 @@ using osu.Game.Rulesets.Difficulty.Preprocessing; using osu.Game.Rulesets.Difficulty.Skills; using osu.Game.Rulesets.Mania.Difficulty.Preprocessing; -using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Mania.Difficulty.Skills { @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty.Skills protected override double StrainValueOf(DifficultyHitObject current) { var maniaCurrent = (ManiaDifficultyHitObject)current; - var endTime = (maniaCurrent.BaseObject as HoldNote)?.EndTime ?? maniaCurrent.BaseObject.StartTime; + var endTime = maniaCurrent.BaseObject.GetEndTime(); double holdFactor = 1.0; // Factor in case something else is held double holdAddition = 0; // Addition to the current note in case it's a hold and has to be released awkwardly diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs index 6f4fbd0651..c41727557b 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoHitObject.cs @@ -3,7 +3,6 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Scoring; @@ -38,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Objects base.CreateNestedHitObjects(); if (IsStrong) - AddNested(new StrongHitObject { StartTime = (this as IHasEndTime)?.EndTime ?? StartTime }); + AddNested(new StrongHitObject { StartTime = this.GetEndTime() }); } public override Judgement CreateJudgement() => new TaikoJudgement(); diff --git a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs index e61953aeb8..4b234b56d4 100644 --- a/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs +++ b/osu.Game.Rulesets.Taiko/Replays/TaikoAutoGenerator.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Replays; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Taiko.Beatmaps; @@ -39,9 +38,7 @@ namespace osu.Game.Rulesets.Taiko.Replays for (int i = 0; i < Beatmap.HitObjects.Count; i++) { TaikoHitObject h = Beatmap.HitObjects[i]; - - IHasEndTime endTimeData = h as IHasEndTime; - double endTime = endTimeData?.EndTime ?? h.StartTime; + double endTime = h.GetEndTime(); switch (h) { diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 1179efaa6e..bd96441ebb 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects ApplyDefaultsToSelf(controlPointInfo, difficulty); // This is done here since ApplyDefaultsToSelf may be used to determine the end time - SampleControlPoint = controlPointInfo.SamplePointAt(((this as IHasEndTime)?.EndTime ?? StartTime) + control_point_leniency); + SampleControlPoint = controlPointInfo.SamplePointAt(this.GetEndTime() + control_point_leniency); nestedHitObjects.Clear(); From 489d9dc7b5d1f56ca7429774fe44970106a0aed8 Mon Sep 17 00:00:00 2001 From: mcendu Date: Sat, 14 Dec 2019 18:33:56 +0800 Subject: [PATCH 38/56] Switch Expandtarget and "cursormiddle" --- osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs index 02152fa51e..e96bd29ad5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyCursor.cs @@ -28,18 +28,18 @@ namespace osu.Game.Rulesets.Osu.Skinning InternalChildren = new[] { + ExpandTarget = new NonPlayfieldSprite + { + Texture = skin.GetTexture("cursor"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, new NonPlayfieldSprite { Texture = skin.GetTexture("cursormiddle"), Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - ExpandTarget = new NonPlayfieldSprite - { - Texture = skin.GetTexture("cursor"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } }; } From 41d4609c9238e26b8f4fb73a68bb381eff1cf04e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Dec 2019 02:36:49 +0900 Subject: [PATCH 39/56] Fix crash on trying to retrieve mods from unavailable ruleset --- osu.Game/OsuGameBase.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 21cc4eaccc..22b8d9d012 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -246,8 +246,11 @@ namespace osu.Game { var dict = new Dictionary>(); - foreach (ModType type in Enum.GetValues(typeof(ModType))) - dict[type] = r.NewValue?.CreateInstance().GetModsFor(type).ToList(); + if (r.NewValue?.Available == true) + { + foreach (ModType type in Enum.GetValues(typeof(ModType))) + dict[type] = r.NewValue.CreateInstance().GetModsFor(type).ToList(); + } if (!SelectedMods.Disabled) SelectedMods.Value = Array.Empty(); From 244eb56455d5bbb7deb1afecdbbd6013f913294e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Dec 2019 03:01:37 +0900 Subject: [PATCH 40/56] Fix test ruleset availability --- osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs index edc749cbaa..8dcb7dcbf8 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSettings.cs @@ -84,6 +84,11 @@ namespace osu.Game.Tests.Visual.UserInterface { public override Ruleset CreateInstance() => new TestCustomisableModRuleset(); + public TestRulesetInfo() + { + Available = true; + } + public class TestCustomisableModRuleset : Ruleset { public override IEnumerable GetModsFor(ModType type) From 91bb851a7dbc1db8f470be81a7bd4506d9f45a17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 15 Dec 2019 03:32:50 +0900 Subject: [PATCH 41/56] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8b266b08ba..239e1c2d31 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -54,6 +54,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9ec833c9ac..56d65830f9 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -23,7 +23,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 1829cbe32a..a90e90db19 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -74,7 +74,7 @@ - + @@ -82,7 +82,7 @@ - + From a04f4b76bb93f9a94d06724cf803ac6a0b835a43 Mon Sep 17 00:00:00 2001 From: Joehu Date: Fri, 13 Dec 2019 22:27:14 -0800 Subject: [PATCH 42/56] Allow changing volume using alt when hovering scroll containers --- osu.Game/Graphics/Containers/OsuScrollContainer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index 2721ce55dc..df4c3a3324 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -83,6 +83,13 @@ namespace osu.Game.Graphics.Containers return base.OnDragEnd(e); } + protected override bool OnScroll(ScrollEvent e) + { + if (e.AltPressed) return false; + + return base.OnScroll(e); + } + protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction); protected class OsuScrollbar : ScrollbarContainer From 19a3c959923328c8b91e4653ee003b101f60c770 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 14 Dec 2019 21:27:26 +0800 Subject: [PATCH 43/56] Update InspectCode to 2019.3 --- build/InspectCode.cake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/InspectCode.cake b/build/InspectCode.cake index bd3fdf5f93..06c56dce87 100644 --- a/build/InspectCode.cake +++ b/build/InspectCode.cake @@ -1,5 +1,5 @@ #addin "nuget:?package=CodeFileSanity&version=0.0.33" -#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.2.1" +#addin "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2019.3.0" #tool "nuget:?package=NVika.MSBuild&version=1.0.1" var nVikaToolPath = GetFiles("./tools/NVika.MSBuild.*/tools/NVika.exe").First(); From ab70abe8bd2b577c049cad84283f0b74e1dcbc60 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 14 Dec 2019 21:28:13 +0800 Subject: [PATCH 44/56] Turn off unexpected new warnings. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 ++++ osu.sln.DotSettings | 1 + 2 files changed, 5 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index ec524043ee..4acc619753 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -587,13 +587,16 @@ namespace osu.Game.Screens.Select switch (d) { case DrawableCarouselBeatmapSet set: + { lastSet = set; set.MoveToX(set.Item.State.Value == CarouselItemState.Selected ? -100 : 0, 500, Easing.OutExpo); set.MoveToY(currentY, 750, Easing.OutExpo); break; + } case DrawableCarouselBeatmap beatmap: + { if (beatmap.Item.State.Value == CarouselItemState.Selected) scrollTarget = currentY + beatmap.DrawHeight / 2 - DrawHeight / 2; @@ -619,6 +622,7 @@ namespace osu.Game.Screens.Select } break; + } } } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 9b400de390..105d22fe3e 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -215,6 +215,7 @@ HINT HINT HINT + HINT DO_NOT_SHOW WARNING WARNING From 8b570233495c2c654b9493bb7d6415580c4c8174 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 14 Dec 2019 20:22:39 +0800 Subject: [PATCH 45/56] Require 3.1.100 SDK in global.json --- global.json | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/global.json b/global.json index 43bb34912a..6858d4044d 100644 --- a/global.json +++ b/global.json @@ -1,4 +1,9 @@ { + "sdk": { + "allowPrerelease": false, + "rollForward": "minor", + "version": "3.1.100" + }, "msbuild-sdks": { "Microsoft.Build.Traversal": "2.0.24" } From cea3a66d4a0f9e235aa7d5e3966281507229cfc1 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 14 Dec 2019 20:26:28 +0800 Subject: [PATCH 46/56] Use static local method fixed for roslyn 3.4 --- .../Visual/Online/TestSceneBeatmapSetOverlayDetails.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs index 2a45e68c0a..96c0c59695 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlayDetails.cs @@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual.Online AddStep("set second set", () => details.BeatmapSet = secondSet); AddAssert("ratings set", () => details.Ratings.Metrics == secondSet.Metrics); - BeatmapSetInfo createSet() => new BeatmapSetInfo + static BeatmapSetInfo createSet() => new BeatmapSetInfo { Metrics = new BeatmapSetMetrics { Ratings = Enumerable.Range(0, 11).Select(_ => RNG.Next(10)).ToArray() }, Beatmaps = new List From c457571da6d69f41ac7722bda6532fa99722c611 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Sat, 14 Dec 2019 20:54:22 +0800 Subject: [PATCH 47/56] Use index and range expressions --- .editorconfig | 4 ++-- osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs | 6 +++--- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 8 ++++---- .../Visual/UserInterface/TestSceneBeatSyncedContainer.cs | 4 ++-- osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs | 4 ++-- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 +- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 2 +- .../Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 4 ++-- osu.Game/Rulesets/Objects/SliderPath.cs | 6 +++--- osu.sln.DotSettings | 2 +- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/.editorconfig b/.editorconfig index b5333ad8e7..8cdb92d11c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -176,8 +176,8 @@ dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent #Style - C# 8 features csharp_prefer_static_local_function = true:warning csharp_prefer_simple_using_statement = true:silent -csharp_style_prefer_index_operator = false:none -csharp_style_prefer_range_operator = false:none +csharp_style_prefer_index_operator = true:warning +csharp_style_prefer_range_operator = true:warning csharp_style_prefer_switch_expression = false:none #Supressing roslyn built-in analyzers diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 9069c09ae4..6e4491de94 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps prevNoteTimes.RemoveAt(0); prevNoteTimes.Add(newNoteTime); - density = (prevNoteTimes[prevNoteTimes.Count - 1] - prevNoteTimes[0]) / prevNoteTimes.Count; + density = (prevNoteTimes[^1] - prevNoteTimes[0]) / prevNoteTimes.Count; } private double lastTime; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 5f75cbabec..b6fc9821a4 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -286,11 +286,11 @@ namespace osu.Game.Rulesets.Osu.Tests private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great; - private bool assertHeadMissTailTracked() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss; + private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss; - private bool assertMidSliderJudgements() => judgementResults[judgementResults.Count - 2].Type == HitResult.Great; + private bool assertMidSliderJudgements() => judgementResults[^2].Type == HitResult.Great; - private bool assertMidSliderJudgementFail() => judgementResults[judgementResults.Count - 2].Type == HitResult.Miss; + private bool assertMidSliderJudgementFail() => judgementResults[^2].Type == HitResult.Miss; private ScoreAccessibleReplayPlayer currentPlayer; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9b820261ab..2497e428fc 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDoubleClick(DoubleClickEvent e) { // Todo: This should all not occur on double click, but rather if the previous control point is hovered. - segmentStart = HitObject.Path.ControlPoints[HitObject.Path.ControlPoints.Count - 1]; + segmentStart = HitObject.Path.ControlPoints[^1]; segmentStart.Type.Value = PathType.Linear; currentSegmentLength = 1; diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index bd59e8a03f..2686ba4fd2 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -156,9 +156,9 @@ namespace osu.Game.Rulesets.Osu.Replays // TODO: Shouldn't the spinner always spin in the same direction? if (h is Spinner) { - calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[Frames.Count - 1]).Position, out startPosition, out spinnerDirection); + calcSpinnerStartPosAndDirection(((OsuReplayFrame)Frames[^1]).Position, out startPosition, out spinnerDirection); - Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[Frames.Count - 1]).Position; + Vector2 spinCentreOffset = SPINNER_CENTRE - ((OsuReplayFrame)Frames[^1]).Position; if (spinCentreOffset.Length > SPIN_RADIUS) { @@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Osu.Replays private void moveToHitObject(OsuHitObject h, Vector2 targetPos, Easing easing) { - OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[Frames.Count - 1]; + OsuReplayFrame lastFrame = (OsuReplayFrame)Frames[^1]; // Wait until Auto could "see and react" to the next note. double waitTime = h.StartTime - Math.Max(0.0, h.TimePreempt - reactionTime); @@ -363,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Replays } // We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed! - if (Frames[Frames.Count - 1].Time <= endFrame.Time) + if (Frames[^1].Time <= endFrame.Time) AddFrameToReplay(endFrame); } diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs index ed44d82bce..b0b673d6a4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneBeatSyncedContainer.cs @@ -157,7 +157,7 @@ namespace osu.Game.Tests.Visual.UserInterface private TimingControlPoint getNextTimingPoint(TimingControlPoint current) { - if (timingPoints[timingPoints.Count - 1] == current) + if (timingPoints[^1] == current) return current; int index = timingPoints.IndexOf(current); // -1 means that this is a "default beat" @@ -169,7 +169,7 @@ namespace osu.Game.Tests.Visual.UserInterface { if (timingPoints.Count == 0) return 0; - if (timingPoints[timingPoints.Count - 1] == current) + if (timingPoints[^1] == current) return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength); return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength); diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index ce2783004c..03496952e7 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -195,8 +195,8 @@ namespace osu.Game.Beatmaps.ControlPoints if (time < list[0].Time) return null; - if (time >= list[list.Count - 1].Time) - return list[list.Count - 1]; + if (time >= list[^1].Time) + return list[^1]; int l = 0; int r = list.Count - 2; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 2b914669cb..e401e3fb97 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -34,7 +34,7 @@ namespace osu.Game.Beatmaps.Formats if (line.StartsWith(@"[", StringComparison.Ordinal) && line.EndsWith(@"]", StringComparison.Ordinal)) { - if (!Enum.TryParse(line.Substring(1, line.Length - 2), out section)) + if (!Enum.TryParse(line[1..^1], out section)) { Logger.Log($"Unknown section \"{line}\" in \"{output}\""); section = Section.None; diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 2bbac92f7f..9735f6373d 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -37,7 +37,7 @@ namespace osu.Game.Graphics.Containers foreach (var link in links) { - AddText(text.Substring(previousLinkEnd, link.Index - previousLinkEnd)); + AddText(text[previousLinkEnd..link.Index]); AddLink(text.Substring(link.Index, link.Length), link.Action, link.Argument ?? link.Url); previousLinkEnd = link.Index + link.Length; } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index b5b1e26486..7fddb442d1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Objects.Legacy result = CreateSlider(pos, combo, comboOffset, convertControlPoints(points, pathType), length, repeatCount, nodeSamples); // The samples are played when the slider ends, which is the last node - result.Samples = nodeSamples[nodeSamples.Count - 1]; + result.Samples = nodeSamples[^1]; } else if (type.HasFlag(ConvertHitObjectType.Spinner)) { @@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Objects.Legacy { if (vertices[i] == vertices[i - 1]) { - points[points.Count - 1].Type.Value = type; + points[^1].Type.Value = type; continue; } diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 86deba3b93..293138097f 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Objects get { ensureValid(); - return cumulativeLength.Count == 0 ? 0 : cumulativeLength[cumulativeLength.Count - 1]; + return cumulativeLength.Count == 0 ? 0 : cumulativeLength[^1]; } } @@ -251,7 +251,7 @@ namespace osu.Game.Rulesets.Objects if (calculatedLength > expectedDistance) { // The path will be shortened further, in which case we should trim any more unnecessary lengths and their associated path segments - while (cumulativeLength.Count > 0 && cumulativeLength[cumulativeLength.Count - 1] >= expectedDistance) + while (cumulativeLength.Count > 0 && cumulativeLength[^1] >= expectedDistance) { cumulativeLength.RemoveAt(cumulativeLength.Count - 1); calculatedPath.RemoveAt(pathEndIndex--); @@ -269,7 +269,7 @@ namespace osu.Game.Rulesets.Objects // The direction of the segment to shorten or lengthen Vector2 dir = (calculatedPath[pathEndIndex] - calculatedPath[pathEndIndex - 1]).Normalized(); - calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[cumulativeLength.Count - 1]); + calculatedPath[pathEndIndex] = calculatedPath[pathEndIndex - 1] + dir * (float)(expectedDistance - cumulativeLength[^1]); cumulativeLength.Add(expectedDistance); } } diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 105d22fe3e..12571be31d 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -220,7 +220,7 @@ WARNING WARNING WARNING - DO_NOT_SHOW + WARNING WARNING True From 9062fe1935b114fd214b5d84f9319fbdb7cd2053 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 13:32:53 +0900 Subject: [PATCH 48/56] Fix crashes on custom skins due to extension-less file lookups --- osu.Game/Skinning/LegacySkinResourceStore.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinResourceStore.cs b/osu.Game/Skinning/LegacySkinResourceStore.cs index 72f3b9ed78..7c799d9c89 100644 --- a/osu.Game/Skinning/LegacySkinResourceStore.cs +++ b/osu.Game/Skinning/LegacySkinResourceStore.cs @@ -22,10 +22,8 @@ namespace osu.Game.Skinning if (source.Files == null) return null; - bool hasExtension = filename.Contains('.'); - var file = source.Files.Find(f => - string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), filename, StringComparison.InvariantCultureIgnoreCase)); + string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)); return file?.FileInfo.StoragePath; } From befb78f83b890f847b16cbcd2c52310bc7d15975 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 16 Dec 2019 14:01:08 +0900 Subject: [PATCH 49/56] Simplify LegacySkinResourceStore by deriving from ResourceStore --- osu.Game/Skinning/LegacySkinResourceStore.cs | 60 ++++---------------- 1 file changed, 12 insertions(+), 48 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinResourceStore.cs b/osu.Game/Skinning/LegacySkinResourceStore.cs index 7c799d9c89..79a4e2e932 100644 --- a/osu.Game/Skinning/LegacySkinResourceStore.cs +++ b/osu.Game/Skinning/LegacySkinResourceStore.cs @@ -3,75 +3,39 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Threading.Tasks; using osu.Framework.IO.Stores; using osu.Game.Database; namespace osu.Game.Skinning { - public class LegacySkinResourceStore : IResourceStore + public class LegacySkinResourceStore : ResourceStore where T : INamedFileInfo { private readonly IHasFiles source; - private readonly IResourceStore underlyingStore; - - private string getPathForFile(string filename) - { - if (source.Files == null) - return null; - - var file = source.Files.Find(f => - string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase)); - return file?.FileInfo.StoragePath; - } public LegacySkinResourceStore(IHasFiles source, IResourceStore underlyingStore) + : base(underlyingStore) { this.source = source; - this.underlyingStore = underlyingStore; } - public Stream GetStream(string name) + protected override IEnumerable GetFilenames(string name) { - string path = getPathForFile(name); - return path == null ? null : underlyingStore.GetStream(path); - } + if (source.Files == null) + yield break; - public IEnumerable GetAvailableResources() => source.Files.Select(f => f.Filename); - - byte[] IResourceStore.Get(string name) => GetAsync(name).Result; - - public Task GetAsync(string name) - { - string path = getPathForFile(name); - return path == null ? Task.FromResult(null) : underlyingStore.GetAsync(path); - } - - #region IDisposable Support - - private bool isDisposed; - - protected virtual void Dispose(bool disposing) - { - if (!isDisposed) + foreach (var filename in base.GetFilenames(name)) { - isDisposed = true; + var path = getPathForFile(filename); + if (path != null) + yield return path; } } - ~LegacySkinResourceStore() - { - Dispose(false); - } + private string getPathForFile(string filename) => + source.Files.Find(f => string.Equals(f.Filename, filename, StringComparison.InvariantCultureIgnoreCase))?.FileInfo.StoragePath; - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - #endregion + public override IEnumerable GetAvailableResources() => source.Files.Select(f => f.Filename); } } From db3dc4f3755e7254c4c039ea48f23c7a2edb1bb7 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 15:15:26 +0900 Subject: [PATCH 50/56] Optimise cursortrail with custom vertex logic --- .../UI/Cursor/CursorTrail.cs | 70 +++++++++++-------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 80291c002e..4d6db83d7a 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private void addPart(Vector2 screenSpacePosition) { parts[currentIndex].Position = screenSpacePosition; - parts[currentIndex].Time = time; + parts[currentIndex].Time = time + 1; ++parts[currentIndex].InvalidationID; currentIndex = (currentIndex + 1) % max_sprites; @@ -201,7 +201,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private readonly TrailPart[] parts = new TrailPart[max_sprites]; private Vector2 size; - private readonly TrailBatch vertexBatch = new TrailBatch(max_sprites, 1); + private readonly QuadBatch vertexBatch = new QuadBatch(max_sprites, 1); public TrailDrawNode(CursorTrail source) : base(source) @@ -227,23 +227,50 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor shader.Bind(); shader.GetUniform("g_FadeClock").UpdateValue(ref time); - for (int i = 0; i < parts.Length; ++i) + RectangleF textureRect = texture.GetTextureRect(); + + foreach (var part in parts) { - if (parts[i].InvalidationID == -1) + if (part.InvalidationID == -1) continue; - vertexBatch.DrawTime = parts[i].Time; + if (time - part.Time >= 1) + continue; - Vector2 pos = parts[i].Position; + vertexBatch.Add(new TexturedTrailVertex + { + Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y + size.Y / 2), + TexturePosition = textureRect.BottomLeft, + Colour = DrawColourInfo.Colour.BottomLeft.Linear, + Time = part.Time + }); - DrawQuad( - texture, - new Quad(pos.X - size.X / 2, pos.Y - size.Y / 2, size.X, size.Y), - DrawColourInfo.Colour, - null, - vertexBatch.AddAction); + vertexBatch.Add(new TexturedTrailVertex + { + Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y + size.Y / 2), + TexturePosition = textureRect.BottomRight, + Colour = DrawColourInfo.Colour.BottomRight.Linear, + Time = part.Time + }); + + vertexBatch.Add(new TexturedTrailVertex + { + Position = new Vector2(part.Position.X + size.X / 2, part.Position.Y - size.Y / 2), + TexturePosition = textureRect.TopRight, + Colour = DrawColourInfo.Colour.TopRight.Linear, + Time = part.Time + }); + + vertexBatch.Add(new TexturedTrailVertex + { + Position = new Vector2(part.Position.X - size.X / 2, part.Position.Y - size.Y / 2), + TexturePosition = textureRect.TopLeft, + Colour = DrawColourInfo.Colour.TopLeft.Linear, + Time = part.Time + }); } + vertexBatch.Draw(); shader.Unbind(); } @@ -253,25 +280,6 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor vertexBatch.Dispose(); } - - // Todo: This shouldn't exist, but is currently used to reduce allocations by caching variable-capturing closures. - private class TrailBatch : QuadBatch - { - public new readonly Action AddAction; - public float DrawTime; - - public TrailBatch(int size, int maxBuffers) - : base(size, maxBuffers) - { - AddAction = v => Add(new TexturedTrailVertex - { - Position = v.Position, - TexturePosition = v.TexturePosition, - Time = DrawTime + 1, - Colour = v.Colour, - }); - } - } } [StructLayout(LayoutKind.Sequential)] From a554ca728bf0b8e4f7db9034e08a908c7c549dc5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 15:27:54 +0900 Subject: [PATCH 51/56] Don't reuse the same control point references --- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index d5d99640af..a4ed966abb 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Catch.Objects if (value != null) { - path.ControlPoints.AddRange(value.ControlPoints); + path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value))); path.ExpectedDistance.Value = value.ExpectedDistance.Value; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 34e5a7f3cd..fe65ab78d1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects if (value != null) { - path.ControlPoints.AddRange(value.ControlPoints); + path.ControlPoints.AddRange(value.ControlPoints.Select(c => new PathControlPoint(c.Position.Value, c.Type.Value))); path.ExpectedDistance.Value = value.ExpectedDistance.Value; } } From fef1877095ff240dbb65bb6d020cc376d965e482 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2019 07:36:30 +0000 Subject: [PATCH 52/56] Bump ppy.osu.Game.Resources from 2019.1010.0 to 2019.1215.0 Bumps [ppy.osu.Game.Resources](https://github.com/ppy/osu-resources) from 2019.1010.0 to 2019.1215.0. - [Release notes](https://github.com/ppy/osu-resources/releases) - [Commits](https://github.com/ppy/osu-resources/compare/2019.1010.0...2019.1215.0) Signed-off-by: dependabot-preview[bot] --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 239e1c2d31..abb3cc8244 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -53,7 +53,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 56d65830f9..e5f34b1c7e 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -22,7 +22,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index a90e90db19..c84e617285 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -73,7 +73,7 @@ - + From add04e98e18d734e84fd88a1aad35fe71a5c90ba Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 18:10:44 +0900 Subject: [PATCH 53/56] Fix cursortrail texture not being bound --- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 4d6db83d7a..4e86662ec6 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -227,6 +227,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor shader.Bind(); shader.GetUniform("g_FadeClock").UpdateValue(ref time); + texture.TextureGL.Bind(); + RectangleF textureRect = texture.GetTextureRect(); foreach (var part in parts) From 83f77d9c3510cd4c68f1574c2a90993d04932779 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 18:41:02 +0900 Subject: [PATCH 54/56] Make the layout faster --- osu.Game/Screens/Play/HUDOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index dc32fc7cd5..840c1d37e3 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -24,7 +24,7 @@ namespace osu.Game.Screens.Play { public class HUDOverlay : Container { - private const int fade_duration = 400; + private const float fade_duration = 400; private const Easing fade_easing = Easing.Out; public readonly KeyCounterDisplay KeyCounter; @@ -103,8 +103,8 @@ namespace osu.Game.Screens.Play Origin = Anchor.BottomRight, Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y), AutoSizeAxes = Axes.Both, - AutoSizeDuration = fade_duration, - AutoSizeEasing = fade_easing, + LayoutDuration = fade_duration / 2, + LayoutEasing = fade_easing, Direction = FillDirection.Vertical, Children = new Drawable[] { From a276643a4bcc8a2a6d376ec43fee0ce816cddea2 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 16 Dec 2019 18:41:14 +0900 Subject: [PATCH 55/56] Reorder health display and score elements --- osu.Game/Screens/Play/HUDOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 840c1d37e3..e2f362780d 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -78,6 +78,7 @@ namespace osu.Game.Screens.Play RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + HealthDisplay = CreateHealthDisplay(), topScoreContainer = new Container { Anchor = Anchor.TopCentre, @@ -90,7 +91,6 @@ namespace osu.Game.Screens.Play ComboCounter = CreateComboCounter(), }, }, - HealthDisplay = CreateHealthDisplay(), Progress = CreateProgress(), ModDisplay = CreateModsContainer(), HitErrorDisplay = CreateHitErrorDisplayOverlay(), From a85653ebec2607ca2675a1b106ebcd814c75fa66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 17 Dec 2019 12:24:59 +0900 Subject: [PATCH 56/56] Add comment --- osu.Game/Graphics/Containers/OsuScrollContainer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index df4c3a3324..ab72276ad0 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -85,6 +85,8 @@ namespace osu.Game.Graphics.Containers protected override bool OnScroll(ScrollEvent e) { + // allow for controlling volume when alt is held. + // mostly for compatibility with osu-stable. if (e.AltPressed) return false; return base.OnScroll(e);