From 8a1fc7c340070e1f04d5ffca1488cfde70a054be Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 26 Aug 2023 01:20:41 +0300 Subject: [PATCH 01/90] Basic stuff (not working for now) --- osu.Game/Overlays/Mods/ModMapInfoDisplay.cs | 203 ++++++++++++++++++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 18 ++ osu.Game/Screens/Select/BeatmapDetails.cs | 6 + .../Screens/Select/Details/AdvancedStats.cs | 21 ++ osu.Game/Screens/Select/SongSelect.cs | 3 + 5 files changed, 251 insertions(+) create mode 100644 osu.Game/Overlays/Mods/ModMapInfoDisplay.cs diff --git a/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs b/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs new file mode 100644 index 0000000000..20666391d6 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs @@ -0,0 +1,203 @@ +// 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.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Select.Details; +using osu.Game.Localisation; +using osuTK; + + +namespace osu.Game.Overlays.Mods +{ + public partial class ModMapInfoDisplay : Container, IHasCurrentValue + { + public const float HEIGHT = 42; + private const float transition_duration = 200; + + private readonly Box contentBackground; + private readonly Box labelBackground; + private readonly FillFlowContainer content; + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + /// + /// Text to display in the left area of the display. + /// + //protected abstract LocalisableString Label { get; } + protected LocalisableString Label => CommonStrings.Finish; + //protected string Label { get; } + + protected virtual float ValueAreaWidth => 56; + + protected virtual string CounterFormat => @"N0"; + + protected override Container Content => content; + + protected readonly RollingCounter Counter; + + public ModMapInfoDisplay() + { + Height = HEIGHT; + AutoSizeAxes = Axes.X; + + InternalChild = new InputBlockingContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + CornerRadius = ModSelectPanel.CORNER_RADIUS, + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + Children = new Drawable[] + { + contentBackground = new Box + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + RelativeSizeAxes = Axes.Y, + Width = ValueAreaWidth + ModSelectPanel.CORNER_RADIUS + }, + new GridContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, ValueAreaWidth) + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + CornerRadius = ModSelectPanel.CORNER_RADIUS, + Children = new Drawable[] + { + labelBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Horizontal = 18 }, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Text = Label, + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + } + } + }, + content = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Spacing = new Vector2(2, 0), + Child = Counter = new EffectCounter(CounterFormat) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = { BindTarget = Current.Value.StarRating } + } + } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + labelBackground.Colour = colourProvider.Background4; + } + + protected override void LoadComplete() + { + Current.BindValueChanged(e => + { + //var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); + setColours(e.NewValue.StarRating.Value); + }, true); + } + + /// + /// Fades colours of text and its background according to displayed value. + /// + /// random number. + private void setColours(double stars) + { + contentBackground.FadeColour(colours.ForStarDifficulty(stars), transition_duration, Easing.OutQuint); + } + + /// + /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. + /// + /// Value to convert. Will arrive from comparison between bindable once it changes and it's . + /// Effect of the value. + protected virtual ModEffect CalculateEffectForComparison(int comparison) + { + if (comparison == 0) + return ModEffect.NotChanged; + if (comparison < 0) + return ModEffect.DifficultyReduction; + + return ModEffect.DifficultyIncrease; + } + + protected enum ModEffect + { + NotChanged, + DifficultyReduction, + DifficultyIncrease + } + + private partial class EffectCounter : RollingCounter + { + private readonly string? format; + + public EffectCounter(string? format) + { + this.format = format; + } + + protected override double RollingDuration => 500; + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(format); + + protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText + { + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + }; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 9e92e9d959..ef5f6cf323 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -25,6 +25,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; +using osu.Game.Screens.Select.Details; using osu.Game.Utils; using osuTK; using osuTK.Input; @@ -123,6 +124,7 @@ namespace osu.Game.Overlays.Mods private Container aboveColumnsContent = null!; private DifficultyMultiplierDisplay? multiplierDisplay; + private ModMapInfoDisplay mapInfoDisplay = null!; protected ShearedButton BackButton { get; private set; } = null!; protected ShearedToggleButton? CustomisationButton { get; private set; } @@ -219,6 +221,12 @@ namespace osu.Game.Overlays.Mods }); } + aboveColumnsContent.Add(mapInfoDisplay = new ModMapInfoDisplay + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft + }); + FooterContent.Child = footerButtonFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -244,6 +252,10 @@ namespace osu.Game.Overlays.Mods globalAvailableMods.BindTo(game.AvailableMods); } + public void SetBindedMapStats(Bindable stats) + { + mapInfoDisplay.Current = stats; + } public override void Hide() { base.Hide(); @@ -399,6 +411,12 @@ namespace osu.Game.Overlays.Mods multiplierDisplay.Current.Value = multiplier; } + private void updateMapInfo() + { + if (mapInfoDisplay == null) + return; + } + private void updateCustomisation() { if (CustomisationButton == null) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 712b610515..c56411bef5 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -3,6 +3,7 @@ using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -273,6 +274,11 @@ namespace osu.Game.Screens.Select loading.Hide(); } + public Bindable GetBindedAdjustedMapStats() + { + return advanced.AdjustedMapStats.GetBoundCopy(); + } + private partial class DetailBox : Container { private readonly Container content; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index a383298faa..016e4ff2df 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -113,6 +113,8 @@ namespace osu.Game.Screens.Select.Details updateStatistics(); } + public Bindable AdjustedMapStats = new Bindable(); + private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; @@ -146,6 +148,14 @@ namespace osu.Game.Screens.Select.Details ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); updateStarDifficulty(); + + var temp = AdjustedMapStats.Value; + temp.CS.Value = FirstValue.Value.adjustedValue ?? 0; + temp.HP.Value = HpDrain.Value.adjustedValue ?? 0; + temp.OD.Value = Accuracy.Value.adjustedValue ?? 0; + temp.AR.Value = ApproachRate.Value.adjustedValue ?? 5; + AdjustedMapStats.Value = temp; + } private CancellationTokenSource starDifficultyCancellationSource; @@ -178,6 +188,11 @@ namespace osu.Game.Screens.Select.Details return; starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); + + var temp = AdjustedMapStats.Value; + temp.StarRating.Value = moddedDifficulty.Value.Stars; + AdjustedMapStats.Value = temp; + }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); }); @@ -296,4 +311,10 @@ namespace osu.Game.Screens.Select.Details } } } + public struct MapStats + { + public Bindable StarRating; + public Bindable MinBPM, MaxBPM, AvgBPM; + public Bindable CS, HP, AR, OD; + } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 58755878d0..17b1b1f870 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -305,6 +305,9 @@ namespace osu.Game.Screens.Select // therein it will be registered at the `OsuGame` level to properly function as a blocking overlay. LoadComponent(ModSelect = CreateModSelectOverlay()); + var bindedStats = BeatmapDetails.Details.GetBindedAdjustedMapStats(); + ModSelect.SetBindedMapStats(bindedStats); + if (Footer != null) { foreach (var (button, overlay) in CreateFooterButtons()) From e77581f64134f7c75b36776d223d7812bb9e4b42 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Aug 2023 01:25:50 +0300 Subject: [PATCH 02/90] `wholeSpins` -> `currentSpins` --- .../Objects/Drawables/DrawableSpinner.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 0ceda1d4b0..2d2a4a05ac 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -286,7 +286,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; - private int wholeSpins; + private int currentSpins; private void updateBonusScore() { @@ -295,14 +295,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables int spins = (int)(Result.RateAdjustedRotation / 360); - if (spins < wholeSpins) + if (spins < currentSpins) { // rewinding, silently handle - wholeSpins = spins; + currentSpins = spins; return; } - while (wholeSpins != spins) + while (currentSpins != spins) { var tick = ticks.FirstOrDefault(t => !t.Result.HasResult); @@ -315,7 +315,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables gainedBonus.Value = score_per_tick * (spins - HitObject.SpinsRequired); } - wholeSpins++; + currentSpins++; } } } From d614e745b8ff9e45bd46093741d810b18d255203 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Aug 2023 01:27:19 +0300 Subject: [PATCH 03/90] Calculate spinner ticks as "whole spins" without arbitrary factors --- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index b800b03c92..f32c6ae979 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -42,15 +42,10 @@ namespace osu.Game.Rulesets.Osu.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - // spinning doesn't match 1:1 with stable, so let's fudge them easier for the time being. - const double stable_matching_fudge = 0.6; - - // close to 477rpm - const double maximum_rotations_per_second = 8; + const double maximum_rotations_per_second = 477f / 60f; double secondsDuration = Duration / 1000; - - double minimumRotationsPerSecond = stable_matching_fudge * IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); + double minimumRotationsPerSecond = IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 1.5, 2.5, 3.75); SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration); From 3bcddf3cc8cb76f9ad154e4baeb39d30fc306e64 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 27 Aug 2023 01:27:48 +0300 Subject: [PATCH 04/90] Add gap of two whole spins before achieving bonus score --- .../Objects/Drawables/DrawableSpinner.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 2d2a4a05ac..20bbb9a1d1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -312,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tick.TriggerResult(true); if (tick is DrawableSpinnerBonusTick) - gainedBonus.Value = score_per_tick * (spins - HitObject.SpinsRequired); + gainedBonus.Value = score_per_tick * (spins - HitObject.FirstBonusSpin); } currentSpins++; diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index f32c6ae979..a26cb7b090 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -31,11 +31,21 @@ namespace osu.Game.Rulesets.Osu.Objects /// public int SpinsRequired { get; protected set; } = 1; + /// + /// The gap between spinner completion and the first bonus-awarding spin. + /// + private const int bonus_spins_gap = 2; + /// /// Number of spins available to give bonus, beyond . /// public int MaximumBonusSpins { get; protected set; } = 1; + /// + /// The first spin awarding bonus score. + /// + public int FirstBonusSpin => SpinsRequired + bonus_spins_gap; + public override Vector2 StackOffset => Vector2.Zero; protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) @@ -48,14 +58,14 @@ namespace osu.Game.Rulesets.Osu.Objects double minimumRotationsPerSecond = IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 1.5, 2.5, 3.75); SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); - MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration); + MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration) - bonus_spins_gap; } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) { base.CreateNestedHitObjects(cancellationToken); - int totalSpins = MaximumBonusSpins + SpinsRequired; + int totalSpins = MaximumBonusSpins + SpinsRequired + bonus_spins_gap; for (int i = 0; i < totalSpins; i++) { @@ -63,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects double startTime = StartTime + (float)(i + 1) / totalSpins * Duration; - AddNested(i < SpinsRequired + AddNested(i < FirstBonusSpin ? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration } : new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { CreateHitSampleInfo("spinnerbonus") } }); } From 50235cc2459c43802099b7b803396fbacf208af6 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Mon, 28 Aug 2023 23:16:33 +0300 Subject: [PATCH 05/90] somewhat working prototype --- osu.Game/Overlays/Mods/ModMapInfoContainer.cs | 58 ++++++++++++++++ osu.Game/Overlays/Mods/ModMapInfoDisplay.cs | 66 +++++++------------ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 17 +++-- osu.Game/Screens/Select/BeatmapDetailArea.cs | 4 ++ osu.Game/Screens/Select/BeatmapDetails.cs | 5 -- .../Screens/Select/Details/AdvancedStats.cs | 40 +++++------ osu.Game/Screens/Select/SongSelect.cs | 8 ++- 7 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 osu.Game/Overlays/Mods/ModMapInfoContainer.cs diff --git a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs b/osu.Game/Overlays/Mods/ModMapInfoContainer.cs new file mode 100644 index 0000000000..eb5291b0a8 --- /dev/null +++ b/osu.Game/Overlays/Mods/ModMapInfoContainer.cs @@ -0,0 +1,58 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Overlays.BeatmapSet; +using osu.Game.Resources.Localisation.Web; +using osu.Game.Screens.Select.Details; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Mods +{ + public partial class ModMapInfoContainer : Container + { + private ModMapInfoDisplay starRatingDisplay = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private Bindable adjustedInfo { get; set; } = null!; + private Bindable starRatingValue = new Bindable(); + + //public ModMapInfoContainer() + //{ + // + //} + + protected override void LoadComplete() + { + starRatingDisplay = new ModMapInfoDisplay("Star Rating", colours.ForStarDifficulty); + starRatingDisplay.Current.BindTo(starRatingValue); + + Content.Add(starRatingDisplay); + + adjustedInfo.BindValueChanged(e => { updateValues(); }, true); + } + + private void updateValues() + { + starRatingValue.Value = adjustedInfo.Value.StarRating; + } + } +} diff --git a/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs b/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs index 20666391d6..ca815984ce 100644 --- a/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs +++ b/osu.Game/Overlays/Mods/ModMapInfoDisplay.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 System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; @@ -12,14 +13,13 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Screens.Select.Details; using osu.Game.Localisation; using osuTK; namespace osu.Game.Overlays.Mods { - public partial class ModMapInfoDisplay : Container, IHasCurrentValue + public partial class ModMapInfoDisplay : Container, IHasCurrentValue { public const float HEIGHT = 42; private const float transition_duration = 200; @@ -28,36 +28,40 @@ namespace osu.Game.Overlays.Mods private readonly Box labelBackground; private readonly FillFlowContainer content; - public Bindable Current - { - get => current.Current; - set => current.Current = value; - } - private readonly BindableWithCurrent current = new BindableWithCurrent(); + //public Bindable Current + //{ + // get => current.Current; + // set => current.Current = value; + //} + //private readonly BindableWithCurrent current = new BindableWithCurrent(); - [Resolved] - private OsuColour colours { get; set; } = null!; + public Bindable Current { get; set; } = new BindableWithCurrent(); + + //[Resolved] + //private OsuColour colours { get; set; } = null!; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + protected Func GetColor; + /// /// Text to display in the left area of the display. /// - //protected abstract LocalisableString Label { get; } - protected LocalisableString Label => CommonStrings.Finish; - //protected string Label { get; } + protected LocalisableString Label; protected virtual float ValueAreaWidth => 56; - protected virtual string CounterFormat => @"N0"; + protected virtual string CounterFormat => @"0.00"; protected override Container Content => content; protected readonly RollingCounter Counter; - public ModMapInfoDisplay() + public ModMapInfoDisplay(LocalisableString label, Func colorFunc) { + Label = label; + GetColor = colorFunc; Height = HEIGHT; AutoSizeAxes = Axes.X; @@ -125,7 +129,7 @@ namespace osu.Game.Overlays.Mods { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Current = { BindTarget = Current.Value.StarRating } + Current = { BindTarget = Current } } } } @@ -146,39 +150,17 @@ namespace osu.Game.Overlays.Mods Current.BindValueChanged(e => { //var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); - setColours(e.NewValue.StarRating.Value); + setColours(e.NewValue); }, true); } /// /// Fades colours of text and its background according to displayed value. /// - /// random number. - private void setColours(double stars) + /// value + private void setColours(double value) { - contentBackground.FadeColour(colours.ForStarDifficulty(stars), transition_duration, Easing.OutQuint); - } - - /// - /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. - /// - /// Value to convert. Will arrive from comparison between bindable once it changes and it's . - /// Effect of the value. - protected virtual ModEffect CalculateEffectForComparison(int comparison) - { - if (comparison == 0) - return ModEffect.NotChanged; - if (comparison < 0) - return ModEffect.DifficultyReduction; - - return ModEffect.DifficultyIncrease; - } - - protected enum ModEffect - { - NotChanged, - DifficultyReduction, - DifficultyIncrease + contentBackground.FadeColour(GetColor(value), transition_duration, Easing.OutQuint); } private partial class EffectCounter : RollingCounter diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ef5f6cf323..a178f2e9dc 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -25,9 +25,9 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; -using osu.Game.Screens.Select.Details; using osu.Game.Utils; using osuTK; +using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Overlays.Mods @@ -124,7 +124,8 @@ namespace osu.Game.Overlays.Mods private Container aboveColumnsContent = null!; private DifficultyMultiplierDisplay? multiplierDisplay; - private ModMapInfoDisplay mapInfoDisplay = null!; + + private ModMapInfoContainer mapInfoContainer = null!; protected ShearedButton BackButton { get; private set; } = null!; protected ShearedToggleButton? CustomisationButton { get; private set; } @@ -221,7 +222,7 @@ namespace osu.Game.Overlays.Mods }); } - aboveColumnsContent.Add(mapInfoDisplay = new ModMapInfoDisplay + aboveColumnsContent.Add(mapInfoContainer = new ModMapInfoContainer { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft @@ -251,11 +252,6 @@ namespace osu.Game.Overlays.Mods globalAvailableMods.BindTo(game.AvailableMods); } - - public void SetBindedMapStats(Bindable stats) - { - mapInfoDisplay.Current = stats; - } public override void Hide() { base.Hide(); @@ -282,6 +278,7 @@ namespace osu.Game.Overlays.Mods SelectedMods.BindValueChanged(_ => { + updateMapInfo(); updateMultiplier(); updateFromExternalSelection(); updateCustomisation(); @@ -413,8 +410,10 @@ namespace osu.Game.Overlays.Mods private void updateMapInfo() { - if (mapInfoDisplay == null) + if (mapInfoContainer == null) return; + + //mapInfoDisplay.Current.Value = 5; } private void updateCustomisation() diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index 595b86924b..d43831e576 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -30,6 +31,9 @@ namespace osu.Game.Screens.Select public readonly BeatmapDetails Details; + //[Cached] + //public Bindable AdjustedInfo { get; private set; } = new Bindable(); + protected Bindable CurrentTab => tabControl.Current; protected Bindable CurrentModsFilter => tabControl.CurrentModsFilter; diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index c56411bef5..6ebdca1b8d 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -274,11 +274,6 @@ namespace osu.Game.Screens.Select loading.Hide(); } - public Bindable GetBindedAdjustedMapStats() - { - return advanced.AdjustedMapStats.GetBoundCopy(); - } - private partial class DetailBox : Container { private readonly Container content; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 016e4ff2df..8f609888df 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -46,6 +46,11 @@ namespace osu.Game.Screens.Select.Details private IBeatmapInfo beatmapInfo; +#nullable enable + [Resolved] + private Bindable? adjustedInfo { get; set; } = null; +#nullable disable + public IBeatmapInfo BeatmapInfo { get => beatmapInfo; @@ -99,6 +104,21 @@ namespace osu.Game.Screens.Select.Details private ModSettingChangeTracker modSettingChangeTracker; private ScheduledDelegate debouncedStatisticsUpdate; + private void updateBindedInfo() + { + if (adjustedInfo == null) return; + + BeatmapInfo adjusted = (BeatmapInfo)beatmapInfo; + adjusted.Difficulty.CircleSize = FirstValue.Value.adjustedValue ?? 0; + adjusted.Difficulty.DrainRate = HpDrain.Value.adjustedValue ?? 0; + adjusted.Difficulty.ApproachRate = ApproachRate.Value.adjustedValue ?? 5; + adjusted.Difficulty.OverallDifficulty = Accuracy.Value.adjustedValue ?? 0; + adjusted.StarRating = starDifficulty.Value.adjustedValue ?? 0; + + adjustedInfo.Value = adjusted; + adjustedInfo.TriggerChange(); + } + private void modsChanged(ValueChangedEvent> mods) { modSettingChangeTracker?.Dispose(); @@ -113,8 +133,6 @@ namespace osu.Game.Screens.Select.Details updateStatistics(); } - public Bindable AdjustedMapStats = new Bindable(); - private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; @@ -149,13 +167,6 @@ namespace osu.Game.Screens.Select.Details updateStarDifficulty(); - var temp = AdjustedMapStats.Value; - temp.CS.Value = FirstValue.Value.adjustedValue ?? 0; - temp.HP.Value = HpDrain.Value.adjustedValue ?? 0; - temp.OD.Value = Accuracy.Value.adjustedValue ?? 0; - temp.AR.Value = ApproachRate.Value.adjustedValue ?? 5; - AdjustedMapStats.Value = temp; - } private CancellationTokenSource starDifficultyCancellationSource; @@ -188,10 +199,7 @@ namespace osu.Game.Screens.Select.Details return; starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); - - var temp = AdjustedMapStats.Value; - temp.StarRating.Value = moddedDifficulty.Value.Stars; - AdjustedMapStats.Value = temp; + updateBindedInfo(); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); }); @@ -311,10 +319,4 @@ namespace osu.Game.Screens.Select.Details } } } - public struct MapStats - { - public Bindable StarRating; - public Bindable MinBPM, MaxBPM, AvgBPM; - public Bindable CS, HP, AR, OD; - } } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 17b1b1f870..4567869e8e 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -99,6 +99,9 @@ namespace osu.Game.Screens.Select [Resolved] private Bindable> selectedMods { get; set; } = null!; + [Cached] + private Bindable adjustedInfo { get; set; } = new Bindable(); + protected BeatmapCarousel Carousel { get; private set; } = null!; private ParallaxContainer wedgeBackground = null!; @@ -305,8 +308,8 @@ namespace osu.Game.Screens.Select // therein it will be registered at the `OsuGame` level to properly function as a blocking overlay. LoadComponent(ModSelect = CreateModSelectOverlay()); - var bindedStats = BeatmapDetails.Details.GetBindedAdjustedMapStats(); - ModSelect.SetBindedMapStats(bindedStats); + //var bindedStats = BeatmapDetails.Details.GetBindedAdjustedMapStats(); + //ModSelect.SetBindedMapStats(bindedStats); if (Footer != null) { @@ -583,6 +586,7 @@ namespace osu.Game.Screens.Select FilterControl.Activate(); ModSelect.SelectedMods.BindTo(selectedMods); + //BeatmapDetails.AdjustedInfo.BindTo(adjustedInfo); beginLooping(); } From b0398b62595bbf099ce957cafe9af3ea9769f16a Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 3 Sep 2023 02:09:01 +0300 Subject: [PATCH 06/90] functionality is done --- osu.Game/Beatmaps/BeatmapShortInfo.cs | 29 +++ osu.Game/Overlays/Mods/ModMapInfoContainer.cs | 171 +++++++++++++--- osu.Game/Overlays/Mods/ModMapInfoDisplay.cs | 185 ------------------ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 17 +- .../Overlays/Mods/VerticalAttributeDisplay.cs | 76 +++++++ osu.Game/Screens/Select/BeatmapDetailArea.cs | 4 - .../Screens/Select/Details/AdvancedStats.cs | 33 ++-- osu.Game/Screens/Select/SongSelect.cs | 2 +- 8 files changed, 287 insertions(+), 230 deletions(-) create mode 100644 osu.Game/Beatmaps/BeatmapShortInfo.cs delete mode 100644 osu.Game/Overlays/Mods/ModMapInfoDisplay.cs create mode 100644 osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs diff --git a/osu.Game/Beatmaps/BeatmapShortInfo.cs b/osu.Game/Beatmaps/BeatmapShortInfo.cs new file mode 100644 index 0000000000..633e12d1b2 --- /dev/null +++ b/osu.Game/Beatmaps/BeatmapShortInfo.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; + +namespace osu.Game.Beatmaps +{ + public class BeatmapShortInfo : IEquatable + { + public StarDifficulty StarDifficulty; + public float CircleSize; + public float DrainRate; + public float ApproachRate; + public float OverallDifficulty; + public double BPM; + public bool Equals(BeatmapShortInfo? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return StarDifficulty.Stars == other.StarDifficulty.Stars && + CircleSize.Equals(other.CircleSize) && + DrainRate.Equals(other.DrainRate) && + ApproachRate.Equals(other.ApproachRate) && + OverallDifficulty.Equals(other.OverallDifficulty) && + BPM.Equals(other.BPM); + } + } +} diff --git a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs b/osu.Game/Overlays/Mods/ModMapInfoContainer.cs index eb5291b0a8..378e6f6057 100644 --- a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs +++ b/osu.Game/Overlays/Mods/ModMapInfoContainer.cs @@ -1,24 +1,21 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; +#nullable disable + using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Online; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osu.Game.Overlays.BeatmapSet; -using osu.Game.Resources.Localisation.Web; -using osu.Game.Screens.Select.Details; using osuTK; using osuTK.Graphics; @@ -26,33 +23,161 @@ namespace osu.Game.Overlays.Mods { public partial class ModMapInfoContainer : Container { - private ModMapInfoDisplay starRatingDisplay = null!; + private Container content; + private Container innerContent; + + private Box background; + private Box innerBackground; + + private StarRatingDisplay starRatingDisplay; + private BPMDisplay bpmDisplay; + + private VerticalAttributeDisplay circleSizeDisplay; + private VerticalAttributeDisplay drainRateDisplay; + private VerticalAttributeDisplay approachRateDisplay; + private VerticalAttributeDisplay overallDifficultyDisplay; [Resolved] - private OsuColour colours { get; set; } = null!; + private OverlayColourProvider colourProvider { get; set; } [Resolved] - private Bindable adjustedInfo { get; set; } = null!; - private Bindable starRatingValue = new Bindable(); + private Bindable adjustedInfo { get; set; } - //public ModMapInfoContainer() - //{ - // - //} + public ModMapInfoContainer() + { + // values as ModSelectOverlay footer buttons + const float shear = ShearedOverlayContainer.SHEAR; + const float corner_radius = 7; + const float border_thickness = 2; + InternalChild = content = new InputBlockingContainer + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + AutoSizeAxes = Axes.X, + Height = 50, // as ModSelectOverlay footer buttons + Shear = new Vector2(shear, 0), + CornerRadius = corner_radius, + BorderThickness = border_thickness, + Masking = true, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer // divide inner and outer content + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + innerContent = new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + BorderThickness = border_thickness, + CornerRadius = corner_radius, + Masking = true, + Children = new Drawable[] + { + innerBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer // actual inner content + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Horizontal, + Margin = new MarginPadding { Horizontal = 15 }, + Children = new Drawable[] + { + new Container // wrap to reserve space for StarRatingDisplay + { + Width = 70, // can be up to 70px on extra high SR + Child = starRatingDisplay = new StarRatingDisplay(default, animated: true) + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Shear = new Vector2(-shear, 0), + } + }, + new Container // wrap to reserve space for BPM + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Width = 70, + Child = bpmDisplay = new BPMDisplay + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Shear = new Vector2(-shear, 0), + } + } + } + } + } + }, + new FillFlowContainer // outer content + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new[] + { + circleSizeDisplay = new VerticalAttributeDisplay("CS"), + drainRateDisplay = new VerticalAttributeDisplay("HP"), + approachRateDisplay = new VerticalAttributeDisplay("AR"), + overallDifficultyDisplay = new VerticalAttributeDisplay("OD"), + } + } + } + } + } + }; + } protected override void LoadComplete() { - starRatingDisplay = new ModMapInfoDisplay("Star Rating", colours.ForStarDifficulty); - starRatingDisplay.Current.BindTo(starRatingValue); + adjustedInfo.BindValueChanged(e => { UpdateValues(); }, true); - Content.Add(starRatingDisplay); + background.Colour = colourProvider.Background4; + innerBackground.Colour = colourProvider.Background3; + Color4 glow_colour = colourProvider.Background1; - adjustedInfo.BindValueChanged(e => { updateValues(); }, true); + content.BorderColour = ColourInfo.GradientVertical(background.Colour, glow_colour); + innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glow_colour); } - private void updateValues() + public void UpdateValues() { - starRatingValue.Value = adjustedInfo.Value.StarRating; + if (adjustedInfo.Value == null) return; + + starRatingDisplay.Current.Value = adjustedInfo.Value.StarDifficulty; + bpmDisplay.Current.Value = adjustedInfo.Value.BPM; + + circleSizeDisplay.Current.Value = adjustedInfo.Value.CircleSize; + drainRateDisplay.Current.Value = adjustedInfo.Value.DrainRate; + approachRateDisplay.Current.Value = adjustedInfo.Value.ApproachRate; + overallDifficultyDisplay.Current.Value = adjustedInfo.Value.OverallDifficulty; + } + + private partial class BPMDisplay : RollingCounter + { + protected override double RollingDuration => 500; + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0 BPM"); + + protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText + { + Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold) + }; } } } diff --git a/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs b/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs deleted file mode 100644 index ca815984ce..0000000000 --- a/osu.Game/Overlays/Mods/ModMapInfoDisplay.cs +++ /dev/null @@ -1,185 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Localisation; -using osuTK; - - -namespace osu.Game.Overlays.Mods -{ - public partial class ModMapInfoDisplay : Container, IHasCurrentValue - { - public const float HEIGHT = 42; - private const float transition_duration = 200; - - private readonly Box contentBackground; - private readonly Box labelBackground; - private readonly FillFlowContainer content; - - //public Bindable Current - //{ - // get => current.Current; - // set => current.Current = value; - //} - //private readonly BindableWithCurrent current = new BindableWithCurrent(); - - public Bindable Current { get; set; } = new BindableWithCurrent(); - - //[Resolved] - //private OsuColour colours { get; set; } = null!; - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - - protected Func GetColor; - - /// - /// Text to display in the left area of the display. - /// - protected LocalisableString Label; - - protected virtual float ValueAreaWidth => 56; - - protected virtual string CounterFormat => @"0.00"; - - protected override Container Content => content; - - protected readonly RollingCounter Counter; - - public ModMapInfoDisplay(LocalisableString label, Func colorFunc) - { - Label = label; - GetColor = colorFunc; - Height = HEIGHT; - AutoSizeAxes = Axes.X; - - InternalChild = new InputBlockingContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), - Children = new Drawable[] - { - contentBackground = new Box - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - Width = ValueAreaWidth + ModSelectPanel.CORNER_RADIUS - }, - new GridContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, ValueAreaWidth) - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Children = new Drawable[] - { - labelBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Horizontal = 18 }, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Text = Label, - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - } - } - }, - content = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Spacing = new Vector2(2, 0), - Child = Counter = new EffectCounter(CounterFormat) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = { BindTarget = Current } - } - } - } - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load() - { - labelBackground.Colour = colourProvider.Background4; - } - - protected override void LoadComplete() - { - Current.BindValueChanged(e => - { - //var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); - setColours(e.NewValue); - }, true); - } - - /// - /// Fades colours of text and its background according to displayed value. - /// - /// value - private void setColours(double value) - { - contentBackground.FadeColour(GetColor(value), transition_duration, Easing.OutQuint); - } - - private partial class EffectCounter : RollingCounter - { - private readonly string? format; - - public EffectCounter(string? format) - { - this.format = format; - } - - protected override double RollingDuration => 500; - - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(format); - - protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText - { - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - }; - } - } -} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a178f2e9dc..426c424541 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -222,13 +222,18 @@ namespace osu.Game.Overlays.Mods }); } - aboveColumnsContent.Add(mapInfoContainer = new ModMapInfoContainer + FooterContent.Add(mapInfoContainer = new ModMapInfoContainer { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding + { + Vertical = PADDING, + Horizontal = 70 + }, }); - FooterContent.Child = footerButtonFlow = new FillFlowContainer + FooterContent.Add(footerButtonFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -248,7 +253,7 @@ namespace osu.Game.Overlays.Mods DarkerColour = colours.Pink2, LighterColour = colours.Pink1 }) - }; + }); globalAvailableMods.BindTo(game.AvailableMods); } @@ -413,7 +418,7 @@ namespace osu.Game.Overlays.Mods if (mapInfoContainer == null) return; - //mapInfoDisplay.Current.Value = 5; + mapInfoContainer.UpdateValues(); } private void updateCustomisation() diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs new file mode 100644 index 0000000000..2ad420657c --- /dev/null +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -0,0 +1,76 @@ +// 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.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; + + +namespace osu.Game.Overlays.Mods +{ + public partial class VerticalAttributeDisplay : Container, IHasCurrentValue + { + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + /// + /// Text to display in the top area of the display. + /// + public LocalisableString Label { get; protected set; } + + public VerticalAttributeDisplay(LocalisableString label) + { + Label = label; + AutoSizeAxes = Axes.X; + Origin = Anchor = Anchor.CentreLeft; + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0); + InternalChild = new FillFlowContainer + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Text = Label, + Margin = new MarginPadding { Horizontal = 15 }, // to reserve space for 0.XX value + Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold) + }, + new EffectCounter() + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Current = { BindTarget = Current } + } + } + }; + } + + private partial class EffectCounter : RollingCounter + { + protected override double RollingDuration => 500; + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.##"); + + protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText + { + Font = OsuFont.Default.With(size: 18, weight: FontWeight.SemiBold) + }; + } + } +} diff --git a/osu.Game/Screens/Select/BeatmapDetailArea.cs b/osu.Game/Screens/Select/BeatmapDetailArea.cs index d43831e576..595b86924b 100644 --- a/osu.Game/Screens/Select/BeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/BeatmapDetailArea.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -31,9 +30,6 @@ namespace osu.Game.Screens.Select public readonly BeatmapDetails Details; - //[Cached] - //public Bindable AdjustedInfo { get; private set; } = new Bindable(); - protected Bindable CurrentTab => tabControl.Current; protected Bindable CurrentModsFilter => tabControl.CurrentModsFilter; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 8f609888df..31fac8b4a0 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -46,10 +46,8 @@ namespace osu.Game.Screens.Select.Details private IBeatmapInfo beatmapInfo; -#nullable enable - [Resolved] - private Bindable? adjustedInfo { get; set; } = null; -#nullable disable + [Resolved(canBeNull: true)] + private Bindable adjustedInfo { get; set; } = null; public IBeatmapInfo BeatmapInfo { @@ -104,19 +102,30 @@ namespace osu.Game.Screens.Select.Details private ModSettingChangeTracker modSettingChangeTracker; private ScheduledDelegate debouncedStatisticsUpdate; + private StarDifficulty latestStarDifficulty = new StarDifficulty(); private void updateBindedInfo() { if (adjustedInfo == null) return; - BeatmapInfo adjusted = (BeatmapInfo)beatmapInfo; - adjusted.Difficulty.CircleSize = FirstValue.Value.adjustedValue ?? 0; - adjusted.Difficulty.DrainRate = HpDrain.Value.adjustedValue ?? 0; - adjusted.Difficulty.ApproachRate = ApproachRate.Value.adjustedValue ?? 5; - adjusted.Difficulty.OverallDifficulty = Accuracy.Value.adjustedValue ?? 0; - adjusted.StarRating = starDifficulty.Value.adjustedValue ?? 0; + // sadly need to calculate this to prevent additional data transportation + double rate = 1; + foreach (var mod in mods.Value.OfType()) + rate = mod.ApplyToRate(0, rate); + + double bpm = 0; + if (beatmapInfo != null) bpm = beatmapInfo.BPM * rate; + + BeatmapShortInfo adjusted = new BeatmapShortInfo() + { + CircleSize = FirstValue.Value.adjustedValue ?? FirstValue.Value.baseValue, + DrainRate = HpDrain.Value.adjustedValue ?? HpDrain.Value.baseValue, + ApproachRate = ApproachRate.Value.adjustedValue ?? ApproachRate.Value.baseValue, + OverallDifficulty = Accuracy.Value.adjustedValue ?? Accuracy.Value.baseValue, + BPM = bpm, + StarDifficulty = latestStarDifficulty + }; adjustedInfo.Value = adjusted; - adjustedInfo.TriggerChange(); } private void modsChanged(ValueChangedEvent> mods) @@ -165,6 +174,7 @@ namespace osu.Game.Screens.Select.Details Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); + updateBindedInfo(); // to faster UI response (without SR calculation) updateStarDifficulty(); } @@ -199,6 +209,7 @@ namespace osu.Game.Screens.Select.Details return; starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); + latestStarDifficulty = moddedDifficulty ?? default; updateBindedInfo(); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 4567869e8e..ad98d1721b 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -100,7 +100,7 @@ namespace osu.Game.Screens.Select private Bindable> selectedMods { get; set; } = null!; [Cached] - private Bindable adjustedInfo { get; set; } = new Bindable(); + private Bindable adjustedInfo { get; set; } = new Bindable(); protected BeatmapCarousel Carousel { get; private set; } = null!; From 0779cd8f4f998c2d9b29fd71787681875bec9979 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 3 Sep 2023 02:17:04 +0300 Subject: [PATCH 07/90] minor design fixes --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 59 ++++++++++++---------- osu.Game/Screens/Select/SongSelect.cs | 4 -- 2 files changed, 31 insertions(+), 32 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 426c424541..8de4447b31 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -222,41 +222,44 @@ namespace osu.Game.Overlays.Mods }); } - FooterContent.Add(mapInfoContainer = new ModMapInfoContainer + FooterContent.Children = new Drawable[] { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Padding = new MarginPadding + mapInfoContainer = new ModMapInfoContainer { - Vertical = PADDING, - Horizontal = 70 + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Padding = new MarginPadding + { + Vertical = PADDING, + Horizontal = 70 + } }, - }); - - FooterContent.Add(footerButtonFlow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Padding = new MarginPadding + footerButtonFlow = new FillFlowContainer { - Vertical = PADDING, - Horizontal = 70 - }, - Spacing = new Vector2(10), - ChildrenEnumerable = CreateFooterButtons().Prepend(BackButton = new ShearedButton(BUTTON_WIDTH) - { - Text = CommonStrings.Back, - Action = Hide, - DarkerColour = colours.Pink2, - LighterColour = colours.Pink1 - }) - }); + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding + { + Vertical = PADDING, + Horizontal = 70 + }, + Spacing = new Vector2(10), + ChildrenEnumerable = CreateFooterButtons().Prepend(BackButton = new ShearedButton(BUTTON_WIDTH) + { + Text = CommonStrings.Back, + Action = Hide, + DarkerColour = colours.Pink2, + LighterColour = colours.Pink1 + }) + } + }; globalAvailableMods.BindTo(game.AvailableMods); } + public override void Hide() { base.Hide(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index ad98d1721b..7749e3937a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -308,9 +308,6 @@ namespace osu.Game.Screens.Select // therein it will be registered at the `OsuGame` level to properly function as a blocking overlay. LoadComponent(ModSelect = CreateModSelectOverlay()); - //var bindedStats = BeatmapDetails.Details.GetBindedAdjustedMapStats(); - //ModSelect.SetBindedMapStats(bindedStats); - if (Footer != null) { foreach (var (button, overlay) in CreateFooterButtons()) @@ -586,7 +583,6 @@ namespace osu.Game.Screens.Select FilterControl.Activate(); ModSelect.SelectedMods.BindTo(selectedMods); - //BeatmapDetails.AdjustedInfo.BindTo(adjustedInfo); beginLooping(); } From 5e5fe84a88d15af428ee6bff7ecd3e46b26bbf65 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 3 Sep 2023 02:19:02 +0300 Subject: [PATCH 08/90] Update AdvancedStats.cs --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 31fac8b4a0..a12323ab70 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -176,7 +176,6 @@ namespace osu.Game.Screens.Select.Details updateBindedInfo(); // to faster UI response (without SR calculation) updateStarDifficulty(); - } private CancellationTokenSource starDifficultyCancellationSource; From 079792644886a6c57fb03eee397d9594419e5847 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 3 Sep 2023 12:19:03 +0300 Subject: [PATCH 09/90] Update VerticalAttributeDisplay.cs --- osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 2ad420657c..95d979ebd2 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; - namespace osu.Game.Overlays.Mods { public partial class VerticalAttributeDisplay : Container, IHasCurrentValue From 8281ed5173af43556f8b2e5ba937a2e66eff7f3a Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 3 Sep 2023 14:51:53 +0300 Subject: [PATCH 10/90] Fixed "no animations" issue --- osu.Game/Overlays/Mods/ModMapInfoContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs b/osu.Game/Overlays/Mods/ModMapInfoContainer.cs index 378e6f6057..281fe8abe5 100644 --- a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs +++ b/osu.Game/Overlays/Mods/ModMapInfoContainer.cs @@ -50,6 +50,7 @@ namespace osu.Game.Overlays.Mods const float corner_radius = 7; const float border_thickness = 2; + AutoSizeAxes = Axes.Both; InternalChild = content = new InputBlockingContainer { Origin = Anchor.BottomRight, From 912c8b0901aba37b5d99eb06a4fa57d0e4267b83 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 6 Sep 2023 00:10:41 +0300 Subject: [PATCH 11/90] `currentSpins` -> `completedFullSpins` --- .../Objects/Drawables/DrawableSpinner.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 2d2a4a05ac..6611cca2bd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -286,7 +286,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; - private int currentSpins; + private int completedFullSpins; private void updateBonusScore() { @@ -295,14 +295,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables int spins = (int)(Result.RateAdjustedRotation / 360); - if (spins < currentSpins) + if (spins < completedFullSpins) { // rewinding, silently handle - currentSpins = spins; + completedFullSpins = spins; return; } - while (currentSpins != spins) + while (completedFullSpins != spins) { var tick = ticks.FirstOrDefault(t => !t.Result.HasResult); @@ -315,7 +315,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables gainedBonus.Value = score_per_tick * (spins - HitObject.SpinsRequired); } - currentSpins++; + completedFullSpins++; } } } From 88b9d3237aeac4809500788387da75c8db09c0f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Sep 2023 18:00:49 +0900 Subject: [PATCH 12/90] Fix osu!mania legacy skin configurations not working when notes are not skinned --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 83dcc3895d..90eb5fa013 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -19,7 +19,7 @@ namespace osu.Game.Skinning { public class LegacyBeatmapSkin : LegacySkin { - protected override bool AllowManiaSkin => false; + protected override bool AllowManiaConfigLookups => false; protected override bool UseCustomSampleBanks => true; /// diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index f286dfb776..1f485cbdf6 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -29,13 +29,7 @@ namespace osu.Game.Skinning { public class LegacySkin : Skin { - /// - /// Whether texture for the keys exists. - /// Used to determine if the mania ruleset is skinned. - /// - private readonly Lazy hasKeyTexture; - - protected virtual bool AllowManiaSkin => hasKeyTexture.Value; + protected virtual bool AllowManiaConfigLookups => true; /// /// Whether this skin can use samples with a custom bank (custom sample set in stable terminology). @@ -61,10 +55,6 @@ namespace osu.Game.Skinning protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage, string configurationFilename = @"skin.ini") : base(skin, resources, storage, configurationFilename) { - // todo: this shouldn't really be duplicated here (from ManiaLegacySkinTransformer). we need to come up with a better solution. - hasKeyTexture = new Lazy(() => this.GetAnimation( - lookupForMania(new LegacyManiaSkinConfigurationLookup(4, LegacyManiaSkinConfigurationLookups.KeyImage, 0))?.Value ?? "mania-key1", true, - true) != null); } protected override void ParseConfigurationStream(Stream stream) @@ -114,7 +104,7 @@ namespace osu.Game.Skinning return SkinUtils.As(getCustomColour(Configuration, customColour.Lookup.ToString() ?? string.Empty)); case LegacyManiaSkinConfigurationLookup maniaLookup: - if (!AllowManiaSkin) + if (!AllowManiaConfigLookups) break; var result = lookupForMania(maniaLookup); From 3db0d0d3415b423c28e46f20ea9f8b7501c64fea Mon Sep 17 00:00:00 2001 From: isakvik Date: Wed, 6 Sep 2023 23:26:34 +0200 Subject: [PATCH 13/90] extendable minimum AR value for osu!std diff adjust mod, set to -10 --- .../Mods/OsuModDifficultyAdjust.cs | 1 + osu.Game/Rulesets/Mods/DifficultyBindable.cs | 34 ++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 3a6b232f9f..5eb41ff189 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods Precision = 0.1f, MinValue = 0, MaxValue = 10, + ExtendedMinValue = -10, ExtendedMaxValue = 11, ReadCurrentFromDifficulty = diff => diff.ApproachRate, }; diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index c21ce756c9..4afeb6c8b2 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -34,9 +34,18 @@ namespace osu.Game.Rulesets.Mods set => CurrentNumber.Precision = value; } + private float minValue; + public float MinValue { - set => CurrentNumber.MinValue = value; + set + { + if (value == minValue) + return; + + minValue = value; + updateMinValue(); + } } private float maxValue; @@ -53,6 +62,23 @@ namespace osu.Game.Rulesets.Mods } } + private float? extendedMinValue; + + /// + /// The minimum value to be used when extended limits are applied. + /// + public float? ExtendedMinValue + { + set + { + if (value == extendedMinValue) + return; + + extendedMinValue = value; + updateMinValue(); + } + } + private float? extendedMaxValue; /// @@ -78,6 +104,7 @@ namespace osu.Game.Rulesets.Mods public DifficultyBindable(float? defaultValue = null) : base(defaultValue) { + ExtendedLimits.BindValueChanged(_ => updateMinValue()); ExtendedLimits.BindValueChanged(_ => updateMaxValue()); } @@ -94,6 +121,11 @@ namespace osu.Game.Rulesets.Mods } } + private void updateMinValue() + { + CurrentNumber.MinValue = ExtendedLimits.Value && extendedMinValue != null ? extendedMinValue.Value : minValue; + } + private void updateMaxValue() { CurrentNumber.MaxValue = ExtendedLimits.Value && extendedMaxValue != null ? extendedMaxValue.Value : maxValue; From 2ab11ab568e3869526682e647e4b24ae5ed0b5b0 Mon Sep 17 00:00:00 2001 From: isakvik Date: Thu, 7 Sep 2023 01:41:22 +0200 Subject: [PATCH 14/90] adds new diffsetting control that shows millisecond values for approach rate override --- .../Mods/OsuModDifficultyAdjust.cs | 2 +- .../Graphics/UserInterface/OsuSliderBar.cs | 2 +- ...oachRateDifficultyAdjustSettingsControl.cs | 36 +++++++++++++++++++ .../Mods/DifficultyAdjustSettingsControl.cs | 22 ++++++------ 4 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 5eb41ff189..c09e108b5a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods ReadCurrentFromDifficulty = diff => diff.CircleSize, }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(ApproachRateDifficultyAdjustSettingsControl))] public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { Precision = 0.1f, diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index e5f5f97eb7..cf3cb3b734 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -82,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface channel.Play(); } - private LocalisableString getTooltipText(T value) + protected LocalisableString getTooltipText(T value) { if (CurrentNumber.IsInteger) return value.ToInt32(NumberFormatInfo.InvariantInfo).ToString("N0"); diff --git a/osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs new file mode 100644 index 0000000000..21cde7d8e2 --- /dev/null +++ b/osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs @@ -0,0 +1,36 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics; +using osu.Framework.Localisation; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Rulesets.Mods +{ + public partial class ApproachRateDifficultyAdjustSettingsControl : DifficultyAdjustSettingsControl + { + protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent, + new ApproachRateSlider + { + RelativeSizeAxes = Axes.X, + Current = sliderDisplayCurrent, + KeyboardStep = 0.1f, + } + ); + + /// + /// A slider bar with more detailed approach rate info for its given value + /// + public partial class ApproachRateSlider : RoundedSliderBar + { + public override LocalisableString TooltipText => + $"{base.TooltipText} ({millisecondsFromApproachRate(Current.Value, 1.0f)} ms)"; + + private double millisecondsFromApproachRate(float value, float clockRate) + { + return Math.Round(1800 - Math.Min(value, 5) * 120 - (value >= 5 ? (value - 5) * 150 : 0) / clockRate); + } + } + } +} diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index a941c0a1db..62fc299c68 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -27,9 +27,16 @@ namespace osu.Game.Rulesets.Mods /// When the mod is overriding a default, this will match the value of . /// When there is no override (ie. is null), this value will match the beatmap provided default via . /// - private readonly BindableNumber sliderDisplayCurrent = new BindableNumber(); + protected readonly BindableNumber sliderDisplayCurrent = new BindableNumber(); - protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent); + protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent, + new RoundedSliderBar + { + RelativeSizeAxes = Axes.X, + Current = sliderDisplayCurrent, + KeyboardStep = 0.1f, + } + ); /// /// Guards against beatmap values displayed on slider bars being transferred to user override. @@ -88,7 +95,7 @@ namespace osu.Game.Rulesets.Mods isInternalChange = false; } - private partial class SliderControl : CompositeDrawable, IHasCurrentValue + protected partial class SliderControl : CompositeDrawable, IHasCurrentValue { // This is required as SettingsItem relies heavily on this bindable for internal use. // The actual update flow is done via the bindable provided in the constructor. @@ -100,16 +107,11 @@ namespace osu.Game.Rulesets.Mods set => current.Current = value; } - public SliderControl(BindableNumber currentNumber) + public SliderControl(BindableNumber currentNumber, RoundedSliderBar slider) { InternalChildren = new Drawable[] { - new RoundedSliderBar - { - RelativeSizeAxes = Axes.X, - Current = currentNumber, - KeyboardStep = 0.1f, - } + slider }; AutoSizeAxes = Axes.Y; From d2a62351350459d763f5fc43e60b6d2328c19613 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Sep 2023 14:19:15 +0900 Subject: [PATCH 15/90] Fix code quality issues --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 2 +- osu.Game/Rulesets/Mods/DifficultyBindable.cs | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index cf3cb3b734..e5f5f97eb7 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -82,7 +82,7 @@ namespace osu.Game.Graphics.UserInterface channel.Play(); } - protected LocalisableString getTooltipText(T value) + private LocalisableString getTooltipText(T value) { if (CurrentNumber.IsInteger) return value.ToInt32(NumberFormatInfo.InvariantInfo).ToString("N0"); diff --git a/osu.Game/Rulesets/Mods/DifficultyBindable.cs b/osu.Game/Rulesets/Mods/DifficultyBindable.cs index 4afeb6c8b2..a207048882 100644 --- a/osu.Game/Rulesets/Mods/DifficultyBindable.cs +++ b/osu.Game/Rulesets/Mods/DifficultyBindable.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mods return; minValue = value; - updateMinValue(); + updateExtents(); } } @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Mods return; maxValue = value; - updateMaxValue(); + updateExtents(); } } @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Mods return; extendedMinValue = value; - updateMinValue(); + updateExtents(); } } @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Mods return; extendedMaxValue = value; - updateMaxValue(); + updateExtents(); } } @@ -104,8 +104,7 @@ namespace osu.Game.Rulesets.Mods public DifficultyBindable(float? defaultValue = null) : base(defaultValue) { - ExtendedLimits.BindValueChanged(_ => updateMinValue()); - ExtendedLimits.BindValueChanged(_ => updateMaxValue()); + ExtendedLimits.BindValueChanged(_ => updateExtents()); } public override float? Value @@ -121,13 +120,9 @@ namespace osu.Game.Rulesets.Mods } } - private void updateMinValue() + private void updateExtents() { CurrentNumber.MinValue = ExtendedLimits.Value && extendedMinValue != null ? extendedMinValue.Value : minValue; - } - - private void updateMaxValue() - { CurrentNumber.MaxValue = ExtendedLimits.Value && extendedMaxValue != null ? extendedMaxValue.Value : maxValue; } From e05d7d5f8d3dbd29b861fbf4457ed3330be05935 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Sep 2023 14:20:19 +0900 Subject: [PATCH 16/90] Inline local control --- .../Mods/OsuModDifficultyAdjust.cs | 34 ++++++++++++++++-- ...oachRateDifficultyAdjustSettingsControl.cs | 36 ------------------- .../Mods/DifficultyAdjustSettingsControl.cs | 14 ++++---- 3 files changed, 39 insertions(+), 45 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index c09e108b5a..2b11546a4b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -1,14 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModDifficultyAdjust : ModDifficultyAdjust + public partial class OsuModDifficultyAdjust : ModDifficultyAdjust { [SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1, SettingControlType = typeof(DifficultyAdjustSettingsControl))] public DifficultyBindable CircleSize { get; } = new DifficultyBindable @@ -20,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods ReadCurrentFromDifficulty = diff => diff.CircleSize, }; - [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(ApproachRateDifficultyAdjustSettingsControl))] + [SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1, SettingControlType = typeof(ApproachRateSettingsControl))] public DifficultyBindable ApproachRate { get; } = new DifficultyBindable { Precision = 0.1f, @@ -54,5 +58,31 @@ namespace osu.Game.Rulesets.Osu.Mods if (CircleSize.Value != null) difficulty.CircleSize = CircleSize.Value.Value; if (ApproachRate.Value != null) difficulty.ApproachRate = ApproachRate.Value.Value; } + + private partial class ApproachRateSettingsControl : DifficultyAdjustSettingsControl + { + protected override Drawable CreateControl() => new SliderControl(SliderDisplayCurrent, + new ApproachRateSlider + { + RelativeSizeAxes = Axes.X, + Current = SliderDisplayCurrent, + KeyboardStep = 0.1f, + } + ); + + /// + /// A slider bar with more detailed approach rate info for its given value + /// + public partial class ApproachRateSlider : RoundedSliderBar + { + public override LocalisableString TooltipText => + $"{base.TooltipText} ({millisecondsFromApproachRate(Current.Value, 1.0f)} ms)"; + + private double millisecondsFromApproachRate(float value, float clockRate) + { + return Math.Round(1800 - Math.Min(value, 5) * 120 - (value >= 5 ? (value - 5) * 150 : 0) / clockRate); + } + } + } } } diff --git a/osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs deleted file mode 100644 index 21cde7d8e2..0000000000 --- a/osu.Game/Rulesets/Mods/ApproachRateDifficultyAdjustSettingsControl.cs +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Graphics; -using osu.Framework.Localisation; -using osu.Game.Graphics.UserInterface; - -namespace osu.Game.Rulesets.Mods -{ - public partial class ApproachRateDifficultyAdjustSettingsControl : DifficultyAdjustSettingsControl - { - protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent, - new ApproachRateSlider - { - RelativeSizeAxes = Axes.X, - Current = sliderDisplayCurrent, - KeyboardStep = 0.1f, - } - ); - - /// - /// A slider bar with more detailed approach rate info for its given value - /// - public partial class ApproachRateSlider : RoundedSliderBar - { - public override LocalisableString TooltipText => - $"{base.TooltipText} ({millisecondsFromApproachRate(Current.Value, 1.0f)} ms)"; - - private double millisecondsFromApproachRate(float value, float clockRate) - { - return Math.Round(1800 - Math.Min(value, 5) * 120 - (value >= 5 ? (value - 5) * 150 : 0) / clockRate); - } - } - } -} diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 62fc299c68..1e66b1966f 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -27,13 +27,13 @@ namespace osu.Game.Rulesets.Mods /// When the mod is overriding a default, this will match the value of . /// When there is no override (ie. is null), this value will match the beatmap provided default via . /// - protected readonly BindableNumber sliderDisplayCurrent = new BindableNumber(); + protected readonly BindableNumber SliderDisplayCurrent = new BindableNumber(); - protected override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent, + protected override Drawable CreateControl() => new SliderControl(SliderDisplayCurrent, new RoundedSliderBar { RelativeSizeAxes = Axes.X, - Current = sliderDisplayCurrent, + Current = SliderDisplayCurrent, KeyboardStep = 0.1f, } ); @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mods // Intercept and extract the internal number bindable from DifficultyBindable. // This will provide bounds and precision specifications for the slider bar. difficultyBindable = (DifficultyBindable)value.GetBoundCopy(); - sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); + SliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); base.Current = difficultyBindable; } @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Mods Current.BindValueChanged(_ => updateCurrentFromSlider()); beatmap.BindValueChanged(_ => updateCurrentFromSlider(), true); - sliderDisplayCurrent.BindValueChanged(number => + SliderDisplayCurrent.BindValueChanged(number => { // this handles the transfer of the slider value to the main bindable. // as such, should be skipped if the slider is being updated via updateFromDifficulty(). @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Mods if (Current.Value != null) { // a user override has been added or updated. - sliderDisplayCurrent.Value = Current.Value.Value; + SliderDisplayCurrent.Value = Current.Value.Value; return; } @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Mods return; isInternalChange = true; - sliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); + SliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); isInternalChange = false; } From 9c6f77b26eb7242c02fbc62c9b3a930035e2821f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Sep 2023 08:11:04 +0200 Subject: [PATCH 17/90] Fix too many things being exposed --- .../Mods/OsuModDifficultyAdjust.cs | 8 ++--- .../Mods/DifficultyAdjustSettingsControl.cs | 32 +++++++++---------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 2b11546a4b..933771321c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -61,14 +62,13 @@ namespace osu.Game.Rulesets.Osu.Mods private partial class ApproachRateSettingsControl : DifficultyAdjustSettingsControl { - protected override Drawable CreateControl() => new SliderControl(SliderDisplayCurrent, + protected override RoundedSliderBar CreateSlider(BindableNumber current) => new ApproachRateSlider { RelativeSizeAxes = Axes.X, - Current = SliderDisplayCurrent, + Current = current, KeyboardStep = 0.1f, - } - ); + }; /// /// A slider bar with more detailed approach rate info for its given value diff --git a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs index 1e66b1966f..d04d7636ec 100644 --- a/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs +++ b/osu.Game/Rulesets/Mods/DifficultyAdjustSettingsControl.cs @@ -27,16 +27,16 @@ namespace osu.Game.Rulesets.Mods /// When the mod is overriding a default, this will match the value of . /// When there is no override (ie. is null), this value will match the beatmap provided default via . /// - protected readonly BindableNumber SliderDisplayCurrent = new BindableNumber(); + private readonly BindableNumber sliderDisplayCurrent = new BindableNumber(); - protected override Drawable CreateControl() => new SliderControl(SliderDisplayCurrent, - new RoundedSliderBar - { - RelativeSizeAxes = Axes.X, - Current = SliderDisplayCurrent, - KeyboardStep = 0.1f, - } - ); + protected sealed override Drawable CreateControl() => new SliderControl(sliderDisplayCurrent, CreateSlider); + + protected virtual RoundedSliderBar CreateSlider(BindableNumber current) => new RoundedSliderBar + { + RelativeSizeAxes = Axes.X, + Current = current, + KeyboardStep = 0.1f, + }; /// /// Guards against beatmap values displayed on slider bars being transferred to user override. @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Mods // Intercept and extract the internal number bindable from DifficultyBindable. // This will provide bounds and precision specifications for the slider bar. difficultyBindable = (DifficultyBindable)value.GetBoundCopy(); - SliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); + sliderDisplayCurrent.BindTo(difficultyBindable.CurrentNumber); base.Current = difficultyBindable; } @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Mods Current.BindValueChanged(_ => updateCurrentFromSlider()); beatmap.BindValueChanged(_ => updateCurrentFromSlider(), true); - SliderDisplayCurrent.BindValueChanged(number => + sliderDisplayCurrent.BindValueChanged(number => { // this handles the transfer of the slider value to the main bindable. // as such, should be skipped if the slider is being updated via updateFromDifficulty(). @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Mods if (Current.Value != null) { // a user override has been added or updated. - SliderDisplayCurrent.Value = Current.Value.Value; + sliderDisplayCurrent.Value = Current.Value.Value; return; } @@ -91,11 +91,11 @@ namespace osu.Game.Rulesets.Mods return; isInternalChange = true; - SliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); + sliderDisplayCurrent.Value = difficultyBindable.ReadCurrentFromDifficulty(difficulty); isInternalChange = false; } - protected partial class SliderControl : CompositeDrawable, IHasCurrentValue + private partial class SliderControl : CompositeDrawable, IHasCurrentValue { // This is required as SettingsItem relies heavily on this bindable for internal use. // The actual update flow is done via the bindable provided in the constructor. @@ -107,11 +107,11 @@ namespace osu.Game.Rulesets.Mods set => current.Current = value; } - public SliderControl(BindableNumber currentNumber, RoundedSliderBar slider) + public SliderControl(BindableNumber currentNumber, Func, RoundedSliderBar> createSlider) { InternalChildren = new Drawable[] { - slider + createSlider(currentNumber) }; AutoSizeAxes = Axes.Y; From bd633889e79da9db86f1163cc24463f19a682602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Sep 2023 08:23:39 +0200 Subject: [PATCH 18/90] Remove local preempt time calculation And just use a hitcircle, and read the actual value. Comes with 100% less chance of forgetting to update either place in the future. --- osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 933771321c..be41355846 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -1,15 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu.Objects; namespace osu.Game.Rulesets.Osu.Mods { @@ -76,11 +77,13 @@ namespace osu.Game.Rulesets.Osu.Mods public partial class ApproachRateSlider : RoundedSliderBar { public override LocalisableString TooltipText => - $"{base.TooltipText} ({millisecondsFromApproachRate(Current.Value, 1.0f)} ms)"; + $"{base.TooltipText} ({getPreemptTime(Current.Value):0} ms)"; - private double millisecondsFromApproachRate(float value, float clockRate) + private double getPreemptTime(float approachRate) { - return Math.Round(1800 - Math.Min(value, 5) * 120 - (value >= 5 ? (value - 5) * 150 : 0) / clockRate); + var hitCircle = new HitCircle(); + hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { ApproachRate = approachRate }); + return hitCircle.TimePreempt; } } } From 323a4f4522040f92dc8023b65b5add32198a01ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Sep 2023 08:30:02 +0200 Subject: [PATCH 19/90] Only show preempt time when limits are extended --- osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index be41355846..f35b1abc42 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -77,7 +77,9 @@ namespace osu.Game.Rulesets.Osu.Mods public partial class ApproachRateSlider : RoundedSliderBar { public override LocalisableString TooltipText => - $"{base.TooltipText} ({getPreemptTime(Current.Value):0} ms)"; + (Current as BindableNumber)?.MinValue < 0 + ? $"{base.TooltipText} ({getPreemptTime(Current.Value):0} ms)" + : base.TooltipText; private double getPreemptTime(float approachRate) { From 59bd6d827e9897ad9a166593ffca850f356cd793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Sep 2023 10:01:32 +0200 Subject: [PATCH 20/90] Add failing test case for overlapping objects blocking each other when not intended --- .../TestSceneLegacyHitPolicy.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index 615044b642..c4413bf7ca 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -555,6 +555,54 @@ namespace osu.Game.Rulesets.Osu.Tests addClickActionAssert(0, ClickAction.Hit); } + [Test] + public void TestOverlappingObjectsDontBlockEachOtherWhenFullyFadedOut() + { + const double time_first_circle = 1000; + const double time_second_circle = 1200; + const double time_third_circle = 1400; + Vector2 positionFirstCircle = new Vector2(100); + Vector2 positionSecondCircle = new Vector2(200); + + var hitObjects = new List + { + new HitCircle + { + StartTime = time_first_circle, + Position = positionFirstCircle, + }, + new HitCircle + { + StartTime = time_second_circle, + Position = positionSecondCircle, + }, + new HitCircle + { + StartTime = time_third_circle, + Position = positionFirstCircle, + }, + }; + + performTest(hitObjects, new List + { + new OsuReplayFrame { Time = time_first_circle, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_first_circle + 50, Position = positionFirstCircle }, + new OsuReplayFrame { Time = time_second_circle, Position = positionSecondCircle, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_second_circle + 50, Position = positionSecondCircle }, + new OsuReplayFrame { Time = time_third_circle, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_third_circle + 50, Position = positionFirstCircle }, + }); + + addJudgementAssert(hitObjects[0], HitResult.Great); + addJudgementOffsetAssert(hitObjects[0], 0); + + addJudgementAssert(hitObjects[1], HitResult.Great); + addJudgementOffsetAssert(hitObjects[1], 0); + + addJudgementAssert(hitObjects[2], HitResult.Great); + addJudgementOffsetAssert(hitObjects[2], 0); + } + private void addJudgementAssert(OsuHitObject hitObject, HitResult result) { AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judgement is {result}", From 168a828f23407f036edf7b2105a83da2c92c3e51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Sep 2023 10:10:03 +0200 Subject: [PATCH 21/90] Revert "Block input to objects lying under already-hit hitcircles when classic note lock is active" This reverts commit 03c61a573ec9f8f1e83cd98193fd84bd18a75043. The goal here was to handle an edge case discovered during work on note lock, wherein it was determined that on stable hit circles would block input from reaching objects underneath them. However, the change mentioned above did that _too_ hard and caused overlaps to also be blocked even long past a hit circle has been faded out. Revert the change pending further (and more careful) investigation. --- .../TestSceneLegacyHitPolicy.cs | 3 +-- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 23 ------------------- .../Objects/Drawables/DrawableHitCircle.cs | 2 +- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index c4413bf7ca..8c311cce91 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -506,6 +506,7 @@ namespace osu.Game.Rulesets.Osu.Tests } [Test] + [Ignore("Currently broken, first attempt at fixing broke even harder. See https://github.com/ppy/osu/issues/24743.")] public void TestInputDoesNotFallThroughOverlappingSliders() { const double time_first_slider = 1000; @@ -549,8 +550,6 @@ namespace osu.Game.Rulesets.Osu.Tests addJudgementOffsetAssert("first slider head", () => ((Slider)hitObjects[0]).HeadCircle, 0); addJudgementAssert(hitObjects[1], HitResult.Miss); // the slider head of the first slider prevents the second slider's head from being hit, so the judgement offset should be very late. - // this is not strictly done by the hit policy implementation itself (see `OsuModClassic.blockInputToUnderlyingObjects()`), - // but we're testing this here anyways to just keep everything related to input handling and note lock in one place. addJudgementOffsetAssert("second slider head", () => ((Slider)hitObjects[1]).HeadCircle, referenceHitWindows.WindowFor(HitResult.Meh)); addClickActionAssert(0, ClickAction.Hit); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 1d95f833b0..5dbf23f7ea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -74,10 +74,6 @@ namespace osu.Game.Rulesets.Osu.Mods head.TrackFollowCircle = !NoSliderHeadMovement.Value; if (FadeHitCircleEarly.Value && !usingHiddenFading) applyEarlyFading(head); - - if (ClassicNoteLock.Value) - blockInputToUnderlyingObjects(head); - break; case DrawableSliderTail tail: @@ -87,29 +83,10 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableHitCircle circle: if (FadeHitCircleEarly.Value && !usingHiddenFading) applyEarlyFading(circle); - - if (ClassicNoteLock.Value) - blockInputToUnderlyingObjects(circle); - break; } } - /// - /// On stable, hitcircles that have already been hit block input from reaching objects that may be underneath them. - /// The purpose of this method is to restore that behaviour. - /// In order to avoid introducing yet another confusing config option, this behaviour is roped into the general notion of "note lock". - /// - private static void blockInputToUnderlyingObjects(DrawableHitCircle circle) - { - var oldHitAction = circle.HitArea.Hit; - circle.HitArea.Hit = () => - { - oldHitAction?.Invoke(); - return true; - }; - } - private void applyEarlyFading(DrawableHitCircle circle) { circle.ApplyCustomUpdateState += (dho, state) => diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 6beed0294d..932f6d3fff 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -261,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case OsuAction.RightButton: if (IsHovered && (Hit?.Invoke() ?? false)) { - HitAction ??= e.Action; + HitAction = e.Action; return true; } From e9b08219e934ecebdfa66b92ffbfccf1589dc1c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Sep 2023 11:08:25 +0200 Subject: [PATCH 22/90] Block input to objects lying under already-hit slider heads before slider is fully judged when classic note lock is active --- .../TestSceneLegacyHitPolicy.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 21 +++++++++++++++++++ .../Objects/Drawables/DrawableHitCircle.cs | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index 8c311cce91..0a3639b9ab 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -506,7 +506,6 @@ namespace osu.Game.Rulesets.Osu.Tests } [Test] - [Ignore("Currently broken, first attempt at fixing broke even harder. See https://github.com/ppy/osu/issues/24743.")] public void TestInputDoesNotFallThroughOverlappingSliders() { const double time_first_slider = 1000; @@ -550,6 +549,8 @@ namespace osu.Game.Rulesets.Osu.Tests addJudgementOffsetAssert("first slider head", () => ((Slider)hitObjects[0]).HeadCircle, 0); addJudgementAssert(hitObjects[1], HitResult.Miss); // the slider head of the first slider prevents the second slider's head from being hit, so the judgement offset should be very late. + // this is not strictly done by the hit policy implementation itself (see `OsuModClassic.blockInputToObjectsUnderSliderHead()`), + // but we're testing this here anyways to just keep everything related to input handling and note lock in one place. addJudgementOffsetAssert("second slider head", () => ((Slider)hitObjects[1]).HeadCircle, referenceHitWindows.WindowFor(HitResult.Meh)); addClickActionAssert(0, ClickAction.Hit); } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index 5dbf23f7ea..0148ec1987 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -74,6 +74,10 @@ namespace osu.Game.Rulesets.Osu.Mods head.TrackFollowCircle = !NoSliderHeadMovement.Value; if (FadeHitCircleEarly.Value && !usingHiddenFading) applyEarlyFading(head); + + if (ClassicNoteLock.Value) + blockInputToObjectsUnderSliderHead(head); + break; case DrawableSliderTail tail: @@ -83,10 +87,27 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableHitCircle circle: if (FadeHitCircleEarly.Value && !usingHiddenFading) applyEarlyFading(circle); + break; } } + /// + /// On stable, slider heads that have already been hit block input from reaching objects that may be underneath them + /// until the sliders they're part of have been fully judged. + /// The purpose of this method is to restore that behaviour. + /// In order to avoid introducing yet another confusing config option, this behaviour is roped into the general notion of "note lock". + /// + private static void blockInputToObjectsUnderSliderHead(DrawableSliderHead slider) + { + var oldHitAction = slider.HitArea.Hit; + slider.HitArea.Hit = () => + { + oldHitAction?.Invoke(); + return !slider.DrawableSlider.AllJudged; + }; + } + private void applyEarlyFading(DrawableHitCircle circle) { circle.ApplyCustomUpdateState += (dho, state) => diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index 932f6d3fff..6beed0294d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -261,7 +261,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case OsuAction.RightButton: if (IsHovered && (Hit?.Invoke() ?? false)) { - HitAction = e.Action; + HitAction ??= e.Action; return true; } From c68b76d9f421be0a5476c723ccaf25eae1ef5969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Sep 2023 13:53:13 +0200 Subject: [PATCH 23/90] Add more test cases for input handling with overlapping objects --- .../TestSceneLegacyHitPolicy.cs | 93 ++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index 0a3639b9ab..a2ef72fe57 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -556,7 +556,96 @@ namespace osu.Game.Rulesets.Osu.Tests } [Test] - public void TestOverlappingObjectsDontBlockEachOtherWhenFullyFadedOut() + public void TestOverlappingSlidersDontBlockEachOtherWhenFullyJudged() + { + const double time_first_slider = 1000; + const double time_second_slider = 1600; + Vector2 positionFirstSlider = new Vector2(100, 50); + Vector2 positionSecondSlider = new Vector2(100, 80); + var midpoint = (positionFirstSlider + positionSecondSlider) / 2; + + var hitObjects = new List + { + new Slider + { + StartTime = time_first_slider, + Position = positionFirstSlider, + Path = new SliderPath(PathType.Linear, new[] + { + Vector2.Zero, + new Vector2(25, 0), + }) + }, + new Slider + { + StartTime = time_second_slider, + Position = positionSecondSlider, + Path = new SliderPath(PathType.Linear, new[] + { + Vector2.Zero, + new Vector2(25, 0), + }) + } + }; + + performTest(hitObjects, new List + { + new OsuReplayFrame { Time = time_first_slider, Position = midpoint, Actions = { OsuAction.RightButton } }, + new OsuReplayFrame { Time = time_first_slider + 25, Position = midpoint }, + // this frame doesn't do anything on lazer, but is REQUIRED for correct playback on stable, + // because stable during replay playback only updates game state _when it encounters a replay frame_ + new OsuReplayFrame { Time = 1250, Position = midpoint }, + new OsuReplayFrame { Time = time_second_slider + 50, Position = midpoint, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_second_slider + 75, Position = midpoint }, + }); + + addJudgementAssert(hitObjects[0], HitResult.Ok); + addJudgementOffsetAssert("first slider head", () => ((Slider)hitObjects[0]).HeadCircle, 0); + addJudgementAssert(hitObjects[1], HitResult.Ok); + addJudgementOffsetAssert("second slider head", () => ((Slider)hitObjects[1]).HeadCircle, 50); + addClickActionAssert(0, ClickAction.Hit); + addClickActionAssert(1, ClickAction.Hit); + } + + [Test] + public void TestOverlappingHitCirclesDontBlockEachOtherWhenBothVisible() + { + const double time_first_circle = 1000; + const double time_second_circle = 1200; + Vector2 positionFirstCircle = new Vector2(100); + Vector2 positionSecondCircle = new Vector2(120); + var midpoint = (positionFirstCircle + positionSecondCircle) / 2; + + var hitObjects = new List + { + new HitCircle + { + StartTime = time_first_circle, + Position = positionFirstCircle, + }, + new HitCircle + { + StartTime = time_second_circle, + Position = positionSecondCircle, + }, + }; + + performTest(hitObjects, new List + { + new OsuReplayFrame { Time = time_first_circle, Position = midpoint, Actions = { OsuAction.LeftButton } }, + new OsuReplayFrame { Time = time_first_circle + 25, Position = midpoint }, + new OsuReplayFrame { Time = time_first_circle + 50, Position = midpoint, Actions = { OsuAction.RightButton } }, + }); + + addJudgementAssert(hitObjects[0], HitResult.Great); + addJudgementOffsetAssert(hitObjects[0], 0); + + addJudgementAssert(hitObjects[1], HitResult.Meh); + addJudgementOffsetAssert(hitObjects[1], -150); + } + + [Test] + public void TestOverlappingHitCirclesDontBlockEachOtherWhenFullyFadedOut() { const double time_first_circle = 1000; const double time_second_circle = 1200; @@ -587,8 +676,10 @@ namespace osu.Game.Rulesets.Osu.Tests { new OsuReplayFrame { Time = time_first_circle, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, new OsuReplayFrame { Time = time_first_circle + 50, Position = positionFirstCircle }, + new OsuReplayFrame { Time = time_second_circle - 50, Position = positionSecondCircle }, new OsuReplayFrame { Time = time_second_circle, Position = positionSecondCircle, Actions = { OsuAction.LeftButton } }, new OsuReplayFrame { Time = time_second_circle + 50, Position = positionSecondCircle }, + new OsuReplayFrame { Time = time_third_circle - 50, Position = positionFirstCircle }, new OsuReplayFrame { Time = time_third_circle, Position = positionFirstCircle, Actions = { OsuAction.LeftButton } }, new OsuReplayFrame { Time = time_third_circle + 50, Position = positionFirstCircle }, }); From 02e2b8546c4089598a60fe99e18a813b59e7ea1b Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 8 Sep 2023 20:32:55 +0300 Subject: [PATCH 24/90] fixed all stated problems --- osu.Game/Beatmaps/BeatmapShortInfo.cs | 29 ------- ...oContainer.cs => ModEffectPreviewPanel.cs} | 82 +++++++++++++++---- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 38 +++++---- osu.Game/Screens/Select/BeatmapDetails.cs | 1 - .../Screens/Select/Details/AdvancedStats.cs | 33 -------- osu.Game/Screens/Select/SongSelect.cs | 5 +- 6 files changed, 90 insertions(+), 98 deletions(-) delete mode 100644 osu.Game/Beatmaps/BeatmapShortInfo.cs rename osu.Game/Overlays/Mods/{ModMapInfoContainer.cs => ModEffectPreviewPanel.cs} (75%) diff --git a/osu.Game/Beatmaps/BeatmapShortInfo.cs b/osu.Game/Beatmaps/BeatmapShortInfo.cs deleted file mode 100644 index 633e12d1b2..0000000000 --- a/osu.Game/Beatmaps/BeatmapShortInfo.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; - -namespace osu.Game.Beatmaps -{ - public class BeatmapShortInfo : IEquatable - { - public StarDifficulty StarDifficulty; - public float CircleSize; - public float DrainRate; - public float ApproachRate; - public float OverallDifficulty; - public double BPM; - public bool Equals(BeatmapShortInfo? other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - - return StarDifficulty.Stars == other.StarDifficulty.Stars && - CircleSize.Equals(other.CircleSize) && - DrainRate.Equals(other.DrainRate) && - ApproachRate.Equals(other.ApproachRate) && - OverallDifficulty.Equals(other.OverallDifficulty) && - BPM.Equals(other.BPM); - } - } -} diff --git a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs similarity index 75% rename from osu.Game/Overlays/Mods/ModMapInfoContainer.cs rename to osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 281fe8abe5..b6a8dfe827 100644 --- a/osu.Game/Overlays/Mods/ModMapInfoContainer.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; @@ -16,12 +16,14 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; +using System.Threading; namespace osu.Game.Overlays.Mods { - public partial class ModMapInfoContainer : Container + public partial class ModEffectPreviewPanel : CompositeDrawable { private Container content; private Container innerContent; @@ -37,13 +39,37 @@ namespace osu.Game.Overlays.Mods private VerticalAttributeDisplay approachRateDisplay; private VerticalAttributeDisplay overallDifficultyDisplay; - [Resolved] - private OverlayColourProvider colourProvider { get; set; } + public const float HEIGHT = 50; // as ModSelectOverlay footer buttons + private const float transition_duration = 250; + + private IBeatmapInfo beatmapInfo = null!; + + public IBeatmapInfo BeatmapInfo + { + get => beatmapInfo; + set + { + if (value == beatmapInfo) return; + + beatmapInfo = value; + updateStarDifficultyBind(); + UpdateValues(); + } + } [Resolved] - private Bindable adjustedInfo { get; set; } + private Bindable> mods { get; set; } = null!; - public ModMapInfoContainer() + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + [Resolved] + private BeatmapDifficultyCache difficultyCache { get; set; } = null!; + + private CancellationTokenSource cancellationSource = null!; + private IBindable starDifficulty = null!; + + public ModEffectPreviewPanel() { // values as ModSelectOverlay footer buttons const float shear = ShearedOverlayContainer.SHEAR; @@ -56,7 +82,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, - Height = 50, // as ModSelectOverlay footer buttons + Height = HEIGHT, Shear = new Vector2(shear, 0), CornerRadius = corner_radius, BorderThickness = border_thickness, @@ -146,27 +172,47 @@ namespace osu.Game.Overlays.Mods } protected override void LoadComplete() { - adjustedInfo.BindValueChanged(e => { UpdateValues(); }, true); - background.Colour = colourProvider.Background4; innerBackground.Colour = colourProvider.Background3; Color4 glow_colour = colourProvider.Background1; content.BorderColour = ColourInfo.GradientVertical(background.Colour, glow_colour); innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glow_colour); - } + updateStarDifficultyBind(); + } + private void updateStarDifficultyBind() + { + if (cancellationSource != null) cancellationSource.Cancel(); + starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, (cancellationSource = new CancellationTokenSource()).Token); + starDifficulty.BindValueChanged(s => + { + starRatingDisplay.Current.Value = s.NewValue ?? default; + + if (!starRatingDisplay.IsPresent) + starRatingDisplay.FinishTransforms(true); + + starRatingDisplay.FadeIn(transition_duration); + }); + } public void UpdateValues() { - if (adjustedInfo.Value == null) return; + if (beatmapInfo == null) return; - starRatingDisplay.Current.Value = adjustedInfo.Value.StarDifficulty; - bpmDisplay.Current.Value = adjustedInfo.Value.BPM; + double rate = 1; + foreach (var mod in mods.Value.OfType()) + rate = mod.ApplyToRate(0, rate); - circleSizeDisplay.Current.Value = adjustedInfo.Value.CircleSize; - drainRateDisplay.Current.Value = adjustedInfo.Value.DrainRate; - approachRateDisplay.Current.Value = adjustedInfo.Value.ApproachRate; - overallDifficultyDisplay.Current.Value = adjustedInfo.Value.OverallDifficulty; + bpmDisplay.Current.Value = beatmapInfo.BPM * rate; + + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Difficulty); + foreach (var mod in mods.Value.OfType()) + mod.ApplyToDifficulty(adjustedDifficulty); + + circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize; + drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate; + approachRateDisplay.Current.Value = adjustedDifficulty.ApproachRate; + overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; } private partial class BPMDisplay : RollingCounter diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8de4447b31..4989c55d49 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -17,6 +17,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Audio; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -27,7 +28,6 @@ using osu.Game.Localisation; using osu.Game.Rulesets.Mods; using osu.Game.Utils; using osuTK; -using osuTK.Graphics; using osuTK.Input; namespace osu.Game.Overlays.Mods @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.Mods private Container aboveColumnsContent = null!; private DifficultyMultiplierDisplay? multiplierDisplay; - private ModMapInfoContainer mapInfoContainer = null!; + private ModEffectPreviewPanel modEffectPreviewPanel = null!; protected ShearedButton BackButton { get; private set; } = null!; protected ShearedToggleButton? CustomisationButton { get; private set; } @@ -133,6 +133,20 @@ namespace osu.Game.Overlays.Mods private Sample? columnAppearSample; + private WorkingBeatmap beatmap = null!; + + public WorkingBeatmap Beatmap + { + get => beatmap; + set + { + if (beatmap == value) return; + + beatmap = value; + modEffectPreviewPanel.BeatmapInfo = beatmap.BeatmapInfo; + } + } + protected ModSelectOverlay(OverlayColourScheme colourScheme = OverlayColourScheme.Green) : base(colourScheme) { @@ -224,11 +238,11 @@ namespace osu.Game.Overlays.Mods FooterContent.Children = new Drawable[] { - mapInfoContainer = new ModMapInfoContainer + modEffectPreviewPanel = new ModEffectPreviewPanel { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, - Padding = new MarginPadding + Margin = new MarginPadding { Vertical = PADDING, Horizontal = 70 @@ -286,7 +300,7 @@ namespace osu.Game.Overlays.Mods SelectedMods.BindValueChanged(_ => { - updateMapInfo(); + modEffectPreviewPanel.UpdateValues(); updateMultiplier(); updateFromExternalSelection(); updateCustomisation(); @@ -300,7 +314,11 @@ namespace osu.Game.Overlays.Mods // // See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988 modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); - modSettingChangeTracker.SettingChanged += _ => updateMultiplier(); + modSettingChangeTracker.SettingChanged += _ => + { + modEffectPreviewPanel.UpdateValues(); + updateMultiplier(); + }; } }, true); @@ -416,14 +434,6 @@ namespace osu.Game.Overlays.Mods multiplierDisplay.Current.Value = multiplier; } - private void updateMapInfo() - { - if (mapInfoContainer == null) - return; - - mapInfoContainer.UpdateValues(); - } - private void updateCustomisation() { if (CustomisationButton == null) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 6ebdca1b8d..712b610515 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -3,7 +3,6 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index a12323ab70..f77ed28dae 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -46,9 +46,6 @@ namespace osu.Game.Screens.Select.Details private IBeatmapInfo beatmapInfo; - [Resolved(canBeNull: true)] - private Bindable adjustedInfo { get; set; } = null; - public IBeatmapInfo BeatmapInfo { get => beatmapInfo; @@ -101,33 +98,6 @@ namespace osu.Game.Screens.Select.Details private ModSettingChangeTracker modSettingChangeTracker; private ScheduledDelegate debouncedStatisticsUpdate; - - private StarDifficulty latestStarDifficulty = new StarDifficulty(); - private void updateBindedInfo() - { - if (adjustedInfo == null) return; - - // sadly need to calculate this to prevent additional data transportation - double rate = 1; - foreach (var mod in mods.Value.OfType()) - rate = mod.ApplyToRate(0, rate); - - double bpm = 0; - if (beatmapInfo != null) bpm = beatmapInfo.BPM * rate; - - BeatmapShortInfo adjusted = new BeatmapShortInfo() - { - CircleSize = FirstValue.Value.adjustedValue ?? FirstValue.Value.baseValue, - DrainRate = HpDrain.Value.adjustedValue ?? HpDrain.Value.baseValue, - ApproachRate = ApproachRate.Value.adjustedValue ?? ApproachRate.Value.baseValue, - OverallDifficulty = Accuracy.Value.adjustedValue ?? Accuracy.Value.baseValue, - BPM = bpm, - StarDifficulty = latestStarDifficulty - }; - - adjustedInfo.Value = adjusted; - } - private void modsChanged(ValueChangedEvent> mods) { modSettingChangeTracker?.Dispose(); @@ -174,7 +144,6 @@ namespace osu.Game.Screens.Select.Details Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); - updateBindedInfo(); // to faster UI response (without SR calculation) updateStarDifficulty(); } @@ -208,8 +177,6 @@ namespace osu.Game.Screens.Select.Details return; starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); - latestStarDifficulty = moddedDifficulty ?? default; - updateBindedInfo(); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); }); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7749e3937a..f9d41f98ea 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -99,9 +99,6 @@ namespace osu.Game.Screens.Select [Resolved] private Bindable> selectedMods { get; set; } = null!; - [Cached] - private Bindable adjustedInfo { get; set; } = new Bindable(); - protected BeatmapCarousel Carousel { get; private set; } = null!; private ParallaxContainer wedgeBackground = null!; @@ -786,6 +783,8 @@ namespace osu.Game.Screens.Select BeatmapDetails.Beatmap = beatmap; + ModSelect.Beatmap = beatmap; + bool beatmapSelected = beatmap is not DummyWorkingBeatmap; if (beatmapSelected) From a01537c76301ff6de5c5d68080c7a90e68d23903 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 8 Sep 2023 20:37:07 +0300 Subject: [PATCH 25/90] minor format fix --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f77ed28dae..23298c694b 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -98,6 +98,7 @@ namespace osu.Game.Screens.Select.Details private ModSettingChangeTracker modSettingChangeTracker; private ScheduledDelegate debouncedStatisticsUpdate; + private void modsChanged(ValueChangedEvent> mods) { modSettingChangeTracker?.Dispose(); @@ -175,7 +176,6 @@ namespace osu.Game.Screens.Select.Details if (normalDifficulty == null || moddedDifficulty == null) return; - starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); From ed213dad6c369e66f9277869f3d41dd07135a8ce Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 8 Sep 2023 20:38:24 +0300 Subject: [PATCH 26/90] minor format fix (again) --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 23298c694b..a383298faa 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -176,8 +176,8 @@ namespace osu.Game.Screens.Select.Details if (normalDifficulty == null || moddedDifficulty == null) return; - starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); + starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); }); From 507f9642cc7db892326e4ca8a813063def813bba Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 8 Sep 2023 22:15:30 +0300 Subject: [PATCH 27/90] Better counter formatization Now CS and HP have 1 decimal point precision, while AR and OD is kept with 2 because of future support of rate-changed AR/OD --- osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs | 8 ++++---- osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs | 9 ++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index b6a8dfe827..6a101bd9f9 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -159,10 +159,10 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Horizontal, Children = new[] { - circleSizeDisplay = new VerticalAttributeDisplay("CS"), - drainRateDisplay = new VerticalAttributeDisplay("HP"), - approachRateDisplay = new VerticalAttributeDisplay("AR"), - overallDifficultyDisplay = new VerticalAttributeDisplay("OD"), + circleSizeDisplay = new VerticalAttributeDisplay("CS", "0.#"), + drainRateDisplay = new VerticalAttributeDisplay("HP", "0.#"), + approachRateDisplay = new VerticalAttributeDisplay("AR", "0.##"), + overallDifficultyDisplay = new VerticalAttributeDisplay("OD", "0.##"), } } } diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 95d979ebd2..503b07f3b9 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Mods /// public LocalisableString Label { get; protected set; } - public VerticalAttributeDisplay(LocalisableString label) + public VerticalAttributeDisplay(LocalisableString label, string format = "0.#") { Label = label; AutoSizeAxes = Axes.X; @@ -54,7 +54,8 @@ namespace osu.Game.Overlays.Mods { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Current = { BindTarget = Current } + Current = { BindTarget = Current }, + Format = format } } }; @@ -64,7 +65,9 @@ namespace osu.Game.Overlays.Mods { protected override double RollingDuration => 500; - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.##"); + public string Format = "0.#"; + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(Format); protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { From 23c4a038487c51fc654eba1a17a409d87856cb4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 08:26:05 +0200 Subject: [PATCH 28/90] Fix code inspections --- .../Overlays/Mods/ModEffectPreviewPanel.cs | 48 +++++++++++-------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 6a101bd9f9..79172a595c 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -25,26 +25,26 @@ namespace osu.Game.Overlays.Mods { public partial class ModEffectPreviewPanel : CompositeDrawable { - private Container content; - private Container innerContent; + private Container content = null!; + private Container innerContent = null!; - private Box background; - private Box innerBackground; + private Box background = null!; + private Box innerBackground = null!; - private StarRatingDisplay starRatingDisplay; - private BPMDisplay bpmDisplay; + private StarRatingDisplay starRatingDisplay = null!; + private BPMDisplay bpmDisplay = null!; - private VerticalAttributeDisplay circleSizeDisplay; - private VerticalAttributeDisplay drainRateDisplay; - private VerticalAttributeDisplay approachRateDisplay; - private VerticalAttributeDisplay overallDifficultyDisplay; + private VerticalAttributeDisplay circleSizeDisplay = null!; + private VerticalAttributeDisplay drainRateDisplay = null!; + private VerticalAttributeDisplay approachRateDisplay = null!; + private VerticalAttributeDisplay overallDifficultyDisplay = null!; public const float HEIGHT = 50; // as ModSelectOverlay footer buttons private const float transition_duration = 250; - private IBeatmapInfo beatmapInfo = null!; + private IBeatmapInfo? beatmapInfo; - public IBeatmapInfo BeatmapInfo + public IBeatmapInfo? BeatmapInfo { get => beatmapInfo; set @@ -66,10 +66,11 @@ namespace osu.Game.Overlays.Mods [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } = null!; - private CancellationTokenSource cancellationSource = null!; + private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; - public ModEffectPreviewPanel() + [BackgroundDependencyLoader] + private void load() { // values as ModSelectOverlay footer buttons const float shear = ShearedOverlayContainer.SHEAR; @@ -170,20 +171,25 @@ namespace osu.Game.Overlays.Mods } }; } + protected override void LoadComplete() { background.Colour = colourProvider.Background4; innerBackground.Colour = colourProvider.Background3; - Color4 glow_colour = colourProvider.Background1; + Color4 glowColour = colourProvider.Background1; - content.BorderColour = ColourInfo.GradientVertical(background.Colour, glow_colour); - innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glow_colour); + content.BorderColour = ColourInfo.GradientVertical(background.Colour, glowColour); + innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glowColour); updateStarDifficultyBind(); } + private void updateStarDifficultyBind() { - if (cancellationSource != null) cancellationSource.Cancel(); + if (beatmapInfo == null) + return; + + cancellationSource?.Cancel(); starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, (cancellationSource = new CancellationTokenSource()).Token); starDifficulty.BindValueChanged(s => { @@ -195,9 +201,11 @@ namespace osu.Game.Overlays.Mods starRatingDisplay.FadeIn(transition_duration); }); } + public void UpdateValues() { - if (beatmapInfo == null) return; + if (beatmapInfo == null) + return; double rate = 1; foreach (var mod in mods.Value.OfType()) @@ -205,7 +213,7 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = beatmapInfo.BPM * rate; - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Difficulty); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(beatmapInfo.Difficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(adjustedDifficulty); From c1a2b86f3f95142b5f0c73b2d2ae5acbd4006aa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 08:29:37 +0200 Subject: [PATCH 29/90] Extract constants properly --- osu.Game/Graphics/UserInterface/ShearedButton.cs | 14 ++++++++------ osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs | 13 +++++-------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShearedButton.cs b/osu.Game/Graphics/UserInterface/ShearedButton.cs index f1afacb2f4..b1e7066a01 100644 --- a/osu.Game/Graphics/UserInterface/ShearedButton.cs +++ b/osu.Game/Graphics/UserInterface/ShearedButton.cs @@ -17,6 +17,10 @@ namespace osu.Game.Graphics.UserInterface { public partial class ShearedButton : OsuClickableContainer { + public const float HEIGHT = 50; + public const float CORNER_RADIUS = 7; + public const float BORDER_THICKNESS = 2; + public LocalisableString Text { get => text.Text; @@ -83,12 +87,10 @@ namespace osu.Game.Graphics.UserInterface /// public ShearedButton(float? width = null) { - Height = 50; + Height = HEIGHT; Padding = new MarginPadding { Horizontal = shear * 50 }; - const float corner_radius = 7; - - Content.CornerRadius = corner_radius; + Content.CornerRadius = CORNER_RADIUS; Content.Shear = new Vector2(shear, 0); Content.Masking = true; Content.Anchor = Content.Origin = Anchor.Centre; @@ -98,9 +100,9 @@ namespace osu.Game.Graphics.UserInterface backgroundLayer = new Container { RelativeSizeAxes = Axes.Y, - CornerRadius = corner_radius, + CornerRadius = CORNER_RADIUS, Masking = true, - BorderThickness = 2, + BorderThickness = BORDER_THICKNESS, Children = new Drawable[] { background = new Box diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 79172a595c..aa7ddc47bc 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -39,7 +39,6 @@ namespace osu.Game.Overlays.Mods private VerticalAttributeDisplay approachRateDisplay = null!; private VerticalAttributeDisplay overallDifficultyDisplay = null!; - public const float HEIGHT = 50; // as ModSelectOverlay footer buttons private const float transition_duration = 250; private IBeatmapInfo? beatmapInfo; @@ -74,8 +73,6 @@ namespace osu.Game.Overlays.Mods { // values as ModSelectOverlay footer buttons const float shear = ShearedOverlayContainer.SHEAR; - const float corner_radius = 7; - const float border_thickness = 2; AutoSizeAxes = Axes.Both; InternalChild = content = new InputBlockingContainer @@ -83,10 +80,10 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, - Height = HEIGHT, + Height = ShearedButton.HEIGHT, Shear = new Vector2(shear, 0), - CornerRadius = corner_radius, - BorderThickness = border_thickness, + CornerRadius = ShearedButton.CORNER_RADIUS, + BorderThickness = ShearedButton.BORDER_THICKNESS, Masking = true, Children = new Drawable[] { @@ -107,8 +104,8 @@ namespace osu.Game.Overlays.Mods { AutoSizeAxes = Axes.X, RelativeSizeAxes = Axes.Y, - BorderThickness = border_thickness, - CornerRadius = corner_radius, + BorderThickness = ShearedButton.BORDER_THICKNESS, + CornerRadius = ShearedButton.CORNER_RADIUS, Masking = true, Children = new Drawable[] { From f591a30ea77eb11e0fb0a7a2a3c1f02f9871181a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 08:44:25 +0200 Subject: [PATCH 30/90] Refactor preview panel to be more self-contained --- .../Overlays/Mods/ModEffectPreviewPanel.cs | 47 ++++++++----------- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 9 +--- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index aa7ddc47bc..dde3761c4a 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; using System.Threading; +using osu.Game.Configuration; namespace osu.Game.Overlays.Mods { @@ -41,24 +42,13 @@ namespace osu.Game.Overlays.Mods private const float transition_duration = 250; - private IBeatmapInfo? beatmapInfo; - - public IBeatmapInfo? BeatmapInfo - { - get => beatmapInfo; - set - { - if (value == beatmapInfo) return; - - beatmapInfo = value; - updateStarDifficultyBind(); - UpdateValues(); - } - } + public Bindable BeatmapInfo { get; } = new Bindable(); [Resolved] private Bindable> mods { get; set; } = null!; + private ModSettingChangeTracker? modSettingChangeTracker; + [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; @@ -178,16 +168,25 @@ namespace osu.Game.Overlays.Mods content.BorderColour = ColourInfo.GradientVertical(background.Colour, glowColour); innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glowColour); - updateStarDifficultyBind(); + BeatmapInfo.BindValueChanged(_ => updateValues(), true); + + mods.BindValueChanged(_ => + { + modSettingChangeTracker?.Dispose(); + + modSettingChangeTracker = new ModSettingChangeTracker(mods.Value); + modSettingChangeTracker.SettingChanged += _ => updateValues(); + updateValues(); + }); } - private void updateStarDifficultyBind() + private void updateValues() => Scheduler.AddOnce(() => { - if (beatmapInfo == null) + if (BeatmapInfo.Value == null) return; cancellationSource?.Cancel(); - starDifficulty = difficultyCache.GetBindableDifficulty(beatmapInfo, (cancellationSource = new CancellationTokenSource()).Token); + starDifficulty = difficultyCache.GetBindableDifficulty(BeatmapInfo.Value, (cancellationSource = new CancellationTokenSource()).Token); starDifficulty.BindValueChanged(s => { starRatingDisplay.Current.Value = s.NewValue ?? default; @@ -197,20 +196,14 @@ namespace osu.Game.Overlays.Mods starRatingDisplay.FadeIn(transition_duration); }); - } - - public void UpdateValues() - { - if (beatmapInfo == null) - return; double rate = 1; foreach (var mod in mods.Value.OfType()) rate = mod.ApplyToRate(0, rate); - bpmDisplay.Current.Value = beatmapInfo.BPM * rate; + bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(beatmapInfo.Difficulty); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(adjustedDifficulty); @@ -218,7 +211,7 @@ namespace osu.Game.Overlays.Mods drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate; approachRateDisplay.Current.Value = adjustedDifficulty.ApproachRate; overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; - } + }); private partial class BPMDisplay : RollingCounter { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 4989c55d49..f2f2dc970c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Mods if (beatmap == value) return; beatmap = value; - modEffectPreviewPanel.BeatmapInfo = beatmap.BeatmapInfo; + modEffectPreviewPanel.BeatmapInfo.Value = beatmap.BeatmapInfo; } } @@ -300,7 +300,6 @@ namespace osu.Game.Overlays.Mods SelectedMods.BindValueChanged(_ => { - modEffectPreviewPanel.UpdateValues(); updateMultiplier(); updateFromExternalSelection(); updateCustomisation(); @@ -314,11 +313,7 @@ namespace osu.Game.Overlays.Mods // // See https://github.com/ppy/osu/pull/23284#issuecomment-1529056988 modSettingChangeTracker = new ModSettingChangeTracker(SelectedMods.Value); - modSettingChangeTracker.SettingChanged += _ => - { - modEffectPreviewPanel.UpdateValues(); - updateMultiplier(); - }; + modSettingChangeTracker.SettingChanged += _ => updateMultiplier(); } }, true); From a631fae6dfb2a0ab74ee8fd1e90a6224beb66f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 09:19:37 +0200 Subject: [PATCH 31/90] Add actual testing --- .../TestSceneModEffectPreviewPanel.cs | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs new file mode 100644 index 0000000000..e3afb874db --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs @@ -0,0 +1,128 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Tests.Visual.UserInterface +{ + [TestFixture] + public partial class TestSceneModEffectPreviewPanel : OsuTestScene + { + [Cached(typeof(BeatmapDifficultyCache))] + private TestBeatmapDifficultyCache difficultyCache = new TestBeatmapDifficultyCache(); + + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + + private Container content = null!; + private ModEffectPreviewPanel panel = null!; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("set up cache", () => Children = new Drawable[] + { + difficultyCache, + content = new Container + { + RelativeSizeAxes = Axes.Both + } + }); + } + + [Test] + public void TestDisplay() + { + OsuModDifficultyAdjust difficultyAdjust = new OsuModDifficultyAdjust(); + OsuModDoubleTime doubleTime = new OsuModDoubleTime(); + + AddStep("create display", () => content.Child = panel = new ModEffectPreviewPanel + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }); + + AddStep("set beatmap", () => + { + var beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo) + { + BeatmapInfo = + { + BPM = 120 + } + }; + + Ruleset.Value = beatmap.BeatmapInfo.Ruleset; + panel.BeatmapInfo.Value = beatmap.BeatmapInfo; + }); + + AddSliderStep("change star rating", 0, 10d, 5, stars => + { + if (panel.IsNotNull()) + previewStarRating(stars); + }); + AddStep("preview ridiculously high SR", () => previewStarRating(1234)); + + AddStep("add DA to mods", () => SelectedMods.Value = new[] { difficultyAdjust }); + + AddSliderStep("change AR", 0, 10f, 5, ar => + { + if (panel.IsNotNull()) + difficultyAdjust.ApproachRate.Value = ar; + }); + AddSliderStep("change CS", 0, 10f, 5, cs => + { + if (panel.IsNotNull()) + difficultyAdjust.CircleSize.Value = cs; + }); + AddSliderStep("change HP", 0, 10f, 5, hp => + { + if (panel.IsNotNull()) + difficultyAdjust.DrainRate.Value = hp; + }); + AddSliderStep("change OD", 0, 10f, 5, od => + { + if (panel.IsNotNull()) + difficultyAdjust.OverallDifficulty.Value = od; + }); + + AddStep("add DT to mods", () => SelectedMods.Value = new Mod[] { difficultyAdjust, doubleTime }); + AddSliderStep("change rate", 1.01d, 2d, 1.5d, rate => + { + if (panel.IsNotNull()) + doubleTime.SpeedChange.Value = rate; + }); + } + + private void previewStarRating(double stars) + { + difficultyCache.Difficulty = new StarDifficulty(stars, 0); + panel.BeatmapInfo.TriggerChange(); + } + + private partial class TestBeatmapDifficultyCache : BeatmapDifficultyCache + { + public StarDifficulty? Difficulty { get; set; } + + public override Task GetDifficultyAsync(IBeatmapInfo beatmapInfo, IRulesetInfo? rulesetInfo = null, IEnumerable? mods = null, + CancellationToken cancellationToken = default) + => Task.FromResult(Difficulty); + } + } +} From 552230af9d23f53e83cc57a06a425c0486e36c14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 09:37:25 +0200 Subject: [PATCH 32/90] Refactor layout of display --- .../Overlays/Mods/ModEffectPreviewPanel.cs | 48 ++++++++----------- .../Overlays/Mods/VerticalAttributeDisplay.cs | 10 ++-- 2 files changed, 23 insertions(+), 35 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index dde3761c4a..1a5d1e98c7 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -61,7 +61,6 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { - // values as ModSelectOverlay footer buttons const float shear = ShearedOverlayContainer.SHEAR; AutoSizeAxes = Axes.Both; @@ -103,36 +102,26 @@ namespace osu.Game.Overlays.Mods { RelativeSizeAxes = Axes.Both }, - new FillFlowContainer // actual inner content + new Container // actual inner content { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - AutoSizeAxes = Axes.X, - Direction = FillDirection.Horizontal, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Width = 140, + RelativeSizeAxes = Axes.Y, Margin = new MarginPadding { Horizontal = 15 }, Children = new Drawable[] { - new Container // wrap to reserve space for StarRatingDisplay - { - Width = 70, // can be up to 70px on extra high SR - Child = starRatingDisplay = new StarRatingDisplay(default, animated: true) - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Shear = new Vector2(-shear, 0), - } - }, - new Container // wrap to reserve space for BPM + starRatingDisplay = new StarRatingDisplay(default, animated: true) { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - Width = 70, - Child = bpmDisplay = new BPMDisplay - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Shear = new Vector2(-shear, 0), - } + Shear = new Vector2(-shear, 0), + }, + bpmDisplay = new BPMDisplay + { + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Shear = new Vector2(-shear, 0), } } } @@ -147,10 +136,10 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Horizontal, Children = new[] { - circleSizeDisplay = new VerticalAttributeDisplay("CS", "0.#"), - drainRateDisplay = new VerticalAttributeDisplay("HP", "0.#"), - approachRateDisplay = new VerticalAttributeDisplay("AR", "0.##"), - overallDifficultyDisplay = new VerticalAttributeDisplay("OD", "0.##"), + circleSizeDisplay = new VerticalAttributeDisplay("CS"), + drainRateDisplay = new VerticalAttributeDisplay("HP"), + approachRateDisplay = new VerticalAttributeDisplay("AR"), + overallDifficultyDisplay = new VerticalAttributeDisplay("OD"), } } } @@ -221,7 +210,8 @@ namespace osu.Game.Overlays.Mods protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { - Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold) + Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold), + UseFullGlyphHeight = false, }; } } diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 503b07f3b9..ba8efd1c2b 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -21,6 +21,7 @@ namespace osu.Game.Overlays.Mods get => current.Current; set => current.Current = value; } + private readonly BindableWithCurrent current = new BindableWithCurrent(); /// @@ -28,7 +29,7 @@ namespace osu.Game.Overlays.Mods /// public LocalisableString Label { get; protected set; } - public VerticalAttributeDisplay(LocalisableString label, string format = "0.#") + public VerticalAttributeDisplay(LocalisableString label) { Label = label; AutoSizeAxes = Axes.X; @@ -50,12 +51,11 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Horizontal = 15 }, // to reserve space for 0.XX value Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold) }, - new EffectCounter() + new EffectCounter { Origin = Anchor.Centre, Anchor = Anchor.Centre, Current = { BindTarget = Current }, - Format = format } } }; @@ -65,9 +65,7 @@ namespace osu.Game.Overlays.Mods { protected override double RollingDuration => 500; - public string Format = "0.#"; - - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(Format); + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.0"); protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { From 53c30dca64e7e7eb44f882ffe5f26d44a2adec54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 09:52:43 +0200 Subject: [PATCH 33/90] Fix data flow being sometimes incorrect --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 2 ++ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index ad79865ad9..029a7f8b9e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -51,6 +51,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("clear contents", Clear); AddStep("reset ruleset", () => Ruleset.Value = rulesetStore.GetRuleset(0)); AddStep("reset mods", () => SelectedMods.SetDefault()); + AddStep("set beatmap", () => Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo)); AddStep("set up presets", () => { Realm.Write(r => @@ -92,6 +93,7 @@ namespace osu.Game.Tests.Visual.UserInterface { RelativeSizeAxes = Axes.Both, State = { Value = Visibility.Visible }, + Beatmap = Beatmap.Value, SelectedMods = { BindTarget = SelectedMods } }); waitForColumnLoad(); diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index f2f2dc970c..1c5fc5abde 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -133,9 +133,9 @@ namespace osu.Game.Overlays.Mods private Sample? columnAppearSample; - private WorkingBeatmap beatmap = null!; + private WorkingBeatmap? beatmap; - public WorkingBeatmap Beatmap + public WorkingBeatmap? Beatmap { get => beatmap; set @@ -143,7 +143,8 @@ namespace osu.Game.Overlays.Mods if (beatmap == value) return; beatmap = value; - modEffectPreviewPanel.BeatmapInfo.Value = beatmap.BeatmapInfo; + if (IsLoaded) + modEffectPreviewPanel.BeatmapInfo.Value = beatmap?.BeatmapInfo; } } @@ -246,7 +247,8 @@ namespace osu.Game.Overlays.Mods { Vertical = PADDING, Horizontal = 70 - } + }, + BeatmapInfo = { Value = beatmap?.BeatmapInfo } }, footerButtonFlow = new FillFlowContainer { From 76bf82d900ccc46e1b89a1544f1748b25c74bbad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 09:55:45 +0200 Subject: [PATCH 34/90] Do not show mod effects on free mod select --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 56 +++++++++---------- .../OnlinePlay/FreeModSelectOverlay.cs | 2 +- 2 files changed, 28 insertions(+), 30 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 1c5fc5abde..80340d2253 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -78,9 +78,9 @@ namespace osu.Game.Overlays.Mods public ShearedSearchTextBox SearchTextBox { get; private set; } = null!; /// - /// Whether the total score multiplier calculated from the current selected set of mods should be shown. + /// Whether the effects (on score multiplier, on or beatmap difficulty) of the current selected set of mods should be shown. /// - protected virtual bool ShowTotalMultiplier => true; + protected virtual bool ShowModEffects => true; /// /// Whether per-mod customisation controls are visible. @@ -228,18 +228,15 @@ namespace osu.Game.Overlays.Mods } }); - if (ShowTotalMultiplier) + if (ShowModEffects) { aboveColumnsContent.Add(multiplierDisplay = new DifficultyMultiplierDisplay { Anchor = Anchor.TopRight, Origin = Anchor.TopRight }); - } - FooterContent.Children = new Drawable[] - { - modEffectPreviewPanel = new ModEffectPreviewPanel + FooterContent.Add(modEffectPreviewPanel = new ModEffectPreviewPanel { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, @@ -249,29 +246,30 @@ namespace osu.Game.Overlays.Mods Horizontal = 70 }, BeatmapInfo = { Value = beatmap?.BeatmapInfo } - }, - footerButtonFlow = new FillFlowContainer + }); + } + + FooterContent.Add(footerButtonFlow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Padding = new MarginPadding - { - Vertical = PADDING, - Horizontal = 70 - }, - Spacing = new Vector2(10), - ChildrenEnumerable = CreateFooterButtons().Prepend(BackButton = new ShearedButton(BUTTON_WIDTH) - { - Text = CommonStrings.Back, - Action = Hide, - DarkerColour = colours.Pink2, - LighterColour = colours.Pink1 - }) - } - }; + Vertical = PADDING, + Horizontal = 70 + }, + Spacing = new Vector2(10), + ChildrenEnumerable = CreateFooterButtons().Prepend(BackButton = new ShearedButton(BUTTON_WIDTH) + { + Text = CommonStrings.Back, + Action = Hide, + DarkerColour = colours.Pink2, + LighterColour = colours.Pink1 + }) + }); globalAvailableMods.BindTo(game.AvailableMods); } diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 4d5d724089..7f090aca57 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.OnlinePlay { public partial class FreeModSelectOverlay : ModSelectOverlay { - protected override bool ShowTotalMultiplier => false; + protected override bool ShowModEffects => false; protected override bool AllowCustomisation => false; From 97665e029b8fbcf6f3a034a9d06592ff2859f755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:00:44 +0200 Subject: [PATCH 35/90] Remove weird fade --- osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 1a5d1e98c7..1b08b88740 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -182,8 +182,6 @@ namespace osu.Game.Overlays.Mods if (!starRatingDisplay.IsPresent) starRatingDisplay.FinishTransforms(true); - - starRatingDisplay.FadeIn(transition_duration); }); double rate = 1; From ff6bf0bc2c3754a838be06d99937b2e6e917d51e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:16:09 +0200 Subject: [PATCH 36/90] Fix slightly wrong binding setup --- osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 1b08b88740..7ace402a24 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -157,8 +157,7 @@ namespace osu.Game.Overlays.Mods content.BorderColour = ColourInfo.GradientVertical(background.Colour, glowColour); innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glowColour); - BeatmapInfo.BindValueChanged(_ => updateValues(), true); - + BeatmapInfo.BindValueChanged(_ => updateValues()); mods.BindValueChanged(_ => { modSettingChangeTracker?.Dispose(); @@ -166,7 +165,7 @@ namespace osu.Game.Overlays.Mods modSettingChangeTracker = new ModSettingChangeTracker(mods.Value); modSettingChangeTracker.SettingChanged += _ => updateValues(); updateValues(); - }); + }, true); } private void updateValues() => Scheduler.AddOnce(() => From f9d4fbee56eebed60cbbe6a273062beeee30903f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:16:24 +0200 Subject: [PATCH 37/90] Add collapsed state to effect preview panel --- .../UserInterface/TestSceneModEffectPreviewPanel.cs | 2 ++ osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs index e3afb874db..f958b231f9 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs @@ -108,6 +108,8 @@ namespace osu.Game.Tests.Visual.UserInterface if (panel.IsNotNull()) doubleTime.SpeedChange.Value = rate; }); + + AddToggleStep("toggle collapsed", collapsed => panel.Collapsed.Value = collapsed); } private void previewStarRating(double stars) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 7ace402a24..e03beb8649 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -35,6 +35,7 @@ namespace osu.Game.Overlays.Mods private StarRatingDisplay starRatingDisplay = null!; private BPMDisplay bpmDisplay = null!; + private FillFlowContainer outerContent = null!; private VerticalAttributeDisplay circleSizeDisplay = null!; private VerticalAttributeDisplay drainRateDisplay = null!; private VerticalAttributeDisplay approachRateDisplay = null!; @@ -44,6 +45,8 @@ namespace osu.Game.Overlays.Mods public Bindable BeatmapInfo { get; } = new Bindable(); + public BindableBool Collapsed { get; } = new BindableBool(); + [Resolved] private Bindable> mods { get; set; } = null!; @@ -69,6 +72,8 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, + AutoSizeEasing = Easing.OutQuint, + AutoSizeDuration = transition_duration, Height = ShearedButton.HEIGHT, Shear = new Vector2(shear, 0), CornerRadius = ShearedButton.CORNER_RADIUS, @@ -127,7 +132,7 @@ namespace osu.Game.Overlays.Mods } } }, - new FillFlowContainer // outer content + outerContent = new FillFlowContainer { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, @@ -166,6 +171,8 @@ namespace osu.Game.Overlays.Mods modSettingChangeTracker.SettingChanged += _ => updateValues(); updateValues(); }, true); + + Collapsed.BindValueChanged(collapsed => outerContent.FadeTo(collapsed.NewValue ? 0 : 1, transition_duration, Easing.OutQuint), true); } private void updateValues() => Scheduler.AddOnce(() => From 0822e8b925b6bf9fd9d78c30fd71f740d96dd08a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:18:24 +0200 Subject: [PATCH 38/90] Fix preview panel not being marked nullable --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 80340d2253..1287c26c99 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -124,8 +124,7 @@ namespace osu.Game.Overlays.Mods private Container aboveColumnsContent = null!; private DifficultyMultiplierDisplay? multiplierDisplay; - - private ModEffectPreviewPanel modEffectPreviewPanel = null!; + private ModEffectPreviewPanel? modEffectPreviewPanel; protected ShearedButton BackButton { get; private set; } = null!; protected ShearedToggleButton? CustomisationButton { get; private set; } @@ -143,7 +142,7 @@ namespace osu.Game.Overlays.Mods if (beatmap == value) return; beatmap = value; - if (IsLoaded) + if (IsLoaded && modEffectPreviewPanel != null) modEffectPreviewPanel.BeatmapInfo.Value = beatmap?.BeatmapInfo; } } From 3deb6cb4ec3eb13135bb93def1b8b9d38d49094c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:19:02 +0200 Subject: [PATCH 39/90] Fix z-order of mod preview panel --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 42 +++++++++++----------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 1287c26c99..ca16b90852 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -227,27 +227,6 @@ namespace osu.Game.Overlays.Mods } }); - if (ShowModEffects) - { - aboveColumnsContent.Add(multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }); - - FooterContent.Add(modEffectPreviewPanel = new ModEffectPreviewPanel - { - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Margin = new MarginPadding - { - Vertical = PADDING, - Horizontal = 70 - }, - BeatmapInfo = { Value = beatmap?.BeatmapInfo } - }); - } - FooterContent.Add(footerButtonFlow = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -270,6 +249,27 @@ namespace osu.Game.Overlays.Mods }) }); + if (ShowModEffects) + { + aboveColumnsContent.Add(multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }); + + FooterContent.Add(modEffectPreviewPanel = new ModEffectPreviewPanel + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Margin = new MarginPadding + { + Vertical = PADDING, + Horizontal = 70 + }, + BeatmapInfo = { Value = beatmap?.BeatmapInfo } + }); + } + globalAvailableMods.BindTo(game.AvailableMods); } From d51396fdaf3838addb8ad7c0d268c7661d25aa95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:46:07 +0200 Subject: [PATCH 40/90] Collapse mod effect preview when it's not wide enough to fit --- osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs | 3 ++- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index e03beb8649..5eb1671570 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -45,7 +45,7 @@ namespace osu.Game.Overlays.Mods public Bindable BeatmapInfo { get; } = new Bindable(); - public BindableBool Collapsed { get; } = new BindableBool(); + public BindableBool Collapsed { get; } = new BindableBool(true); [Resolved] private Bindable> mods { get; set; } = null!; @@ -173,6 +173,7 @@ namespace osu.Game.Overlays.Mods }, true); Collapsed.BindValueChanged(collapsed => outerContent.FadeTo(collapsed.NewValue ? 0 : 1, transition_duration, Easing.OutQuint), true); + FinishTransforms(true); } private void updateValues() => Scheduler.AddOnce(() => diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ca16b90852..069fdf1af0 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -338,6 +338,15 @@ namespace osu.Game.Overlays.Mods base.Update(); SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? Resources.Localisation.Web.CommonStrings.InputSearch : ModSelectOverlayStrings.TabToSearch; + + if (modEffectPreviewPanel != null) + { + float rightEdgeOfLastButton = footerButtonFlow.Last().ScreenSpaceDrawQuad.TopRight.X; + // this is cheating a bit; the 375 value is hardcoded based on how wide the expanded panel _generally_ is. + // due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing. + float projectedLeftEdgeOfExpandedModEffectPreviewPanel = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(375 + 70, 0)).X; + modEffectPreviewPanel.Collapsed.Value = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedModEffectPreviewPanel; + } } /// From eebacfb982be3e1d0eaf834c2c78f15ec81686be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 10:59:19 +0200 Subject: [PATCH 41/90] Make mod effect preview uncollapse on hover --- .../Overlays/Mods/ModEffectPreviewPanel.cs | 26 +++++++++++++++++-- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 +++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index 5eb1671570..a6d7732cba 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; using System.Threading; +using osu.Framework.Input.Events; using osu.Game.Configuration; namespace osu.Game.Overlays.Mods @@ -67,7 +68,7 @@ namespace osu.Game.Overlays.Mods const float shear = ShearedOverlayContainer.SHEAR; AutoSizeAxes = Axes.Both; - InternalChild = content = new InputBlockingContainer + InternalChild = content = new Container { Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, @@ -172,10 +173,26 @@ namespace osu.Game.Overlays.Mods updateValues(); }, true); - Collapsed.BindValueChanged(collapsed => outerContent.FadeTo(collapsed.NewValue ? 0 : 1, transition_duration, Easing.OutQuint), true); + Collapsed.BindValueChanged(_ => updateCollapsedState(), true); FinishTransforms(true); } + protected override bool OnHover(HoverEvent e) + { + updateCollapsedState(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateCollapsedState(); + base.OnHoverLost(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; + private void updateValues() => Scheduler.AddOnce(() => { if (BeatmapInfo.Value == null) @@ -207,6 +224,11 @@ namespace osu.Game.Overlays.Mods overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; }); + private void updateCollapsedState() + { + outerContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); + } + private partial class BPMDisplay : RollingCounter { protected override double RollingDuration => 500; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 069fdf1af0..d52782cf11 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -924,6 +924,9 @@ namespace osu.Game.Overlays.Mods OnClicked?.Invoke(); return true; + case HoverEvent: + return false; + case MouseEvent: return true; } From 293b81106261b1a3aad47d9461a8b2a7f550c8b6 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 13:09:49 +0200 Subject: [PATCH 42/90] feat(OsuColours): add B6 --- osu.Game/Graphics/OsuColour.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 1b21f79c0a..481015de85 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -383,6 +383,7 @@ namespace osu.Game.Graphics // Content Background public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28"); + public readonly Color4 B6 = Color4Extensions.FromHex(@"1c1719"); // hsl(333,10%,10%) public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded"); public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787"); From f6031bc15f46a2a00103c8f8e63e3d1fa62e6b11 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 13:41:32 +0200 Subject: [PATCH 43/90] fix: prevent flickering when hovering out --- osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index b722fe92e0..953a14a241 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -84,6 +84,7 @@ namespace osu.Game.Overlays.Profile.Header.Components RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, + AlwaysPresent = true, Margin = new MarginPadding { Bottom = margin, Top = margin / 2f } } } From 200b3f87fff5ce4a7de5948f990cc85a70c89060 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 14:26:10 +0200 Subject: [PATCH 44/90] fix: adjust values according to osu-web --- .../Overlays/Profile/Header/Components/PreviousUsernames.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 953a14a241..18257e96c4 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -38,7 +38,7 @@ namespace osu.Game.Overlays.Profile.Header.Components AutoSizeAxes = Axes.Y; Width = width; Masking = true; - CornerRadius = 5; + CornerRadius = 6; AddRangeInternal(new Drawable[] { @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.Profile.Header.Components [BackgroundDependencyLoader] private void load(OsuColour colours) { - background.Colour = colours.GreySeaFoamDarker; + background.Colour = colours.B6; } protected override void LoadComplete() @@ -157,7 +157,7 @@ namespace osu.Game.Overlays.Profile.Header.Components { Margin = new MarginPadding { Top = 6, Left = margin, Right = margin * 2 }, Size = new Vector2(15), - Icon = FontAwesome.Solid.IdCard, + Icon = FontAwesome.Solid.AddressCard, }; } From 8c137353b62ac8a43e197d2d31a3efa2a9219357 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 15:02:19 +0200 Subject: [PATCH 45/90] feat: integrate previous usernames tooltip to profile overlay --- .../Online/TestSceneUserProfileHeader.cs | 26 +++++++++++++++++++ .../Profile/Header/TopHeaderContainer.cs | 14 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs index 640e895b6c..4f28baa849 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileHeader.cs @@ -110,5 +110,31 @@ namespace osu.Game.Tests.Visual.Online } }, new OsuRuleset().RulesetInfo)); } + + [Test] + public void TestPreviousUsernames() + { + AddStep("Show user w/ previous usernames", () => header.User.Value = new UserProfileData(new APIUser + { + Id = 727, + Username = "SomeoneIndecisive", + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c1.jpg", + Groups = new[] + { + new APIUserGroup { Colour = "#EB47D0", ShortName = "DEV", Name = "Developers" }, + }, + Statistics = new UserStatistics + { + IsRanked = false, + // web will sometimes return non-empty rank history even for unranked users. + RankHistory = new APIRankHistory + { + Mode = @"osu", + Data = Enumerable.Range(2345, 85).ToArray() + }, + }, + PreviousUsernames = new[] { "tsrk.", "quoicoubeh", "apagnan", "epita" } + }, new OsuRuleset().RulesetInfo)); + } } } diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index dc47ce6e30..f4f962cdfb 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -46,6 +46,7 @@ namespace osu.Game.Overlays.Profile.Header private OsuSpriteText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; private ToggleCoverButton coverToggle = null!; + private PreviousUsernames previousUsernames = null!; private Bindable coverExpanded = null!; @@ -143,6 +144,18 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, }, + new Container + { + Child = previousUsernames = new PreviousUsernames + { + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + }, + Anchor = Anchor.TopLeft, + Origin = Anchor.TopLeft, + Size = new Vector2(0), + Position = new Vector2(10) + } } }, titleText = new OsuSpriteText @@ -216,6 +229,7 @@ namespace osu.Game.Overlays.Profile.Header titleText.Text = user?.Title ?? string.Empty; titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff"); groupBadgeFlow.User.Value = user; + previousUsernames.User.Value = user; } private void updateCoverState() From 65f3feb044dc77518e0c767bd36da68ec71cb08b Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 15:21:51 +0200 Subject: [PATCH 46/90] refactor: remove container position --- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index f4f962cdfb..8aae13ebc5 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -154,7 +154,6 @@ namespace osu.Game.Overlays.Profile.Header Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, Size = new Vector2(0), - Position = new Vector2(10) } } }, From c6e8a77f08d13f9790c40a7d6697e089bc9f0fa8 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 15:22:23 +0200 Subject: [PATCH 47/90] refactor: slightly offset to make it look good --- .../Profile/Header/Components/PreviousUsernames.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 18257e96c4..686f6b2918 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -22,8 +22,9 @@ namespace osu.Game.Overlays.Profile.Header.Components { private const int duration = 200; private const int margin = 10; - private const int width = 310; + private const int width = 300; private const int move_offset = 15; + private const int base_y_offset = -3; // eye balled to make it look good public readonly Bindable User = new Bindable(); @@ -39,6 +40,7 @@ namespace osu.Game.Overlays.Profile.Header.Components Width = width; Masking = true; CornerRadius = 6; + Y = base_y_offset; AddRangeInternal(new Drawable[] { @@ -135,7 +137,7 @@ namespace osu.Game.Overlays.Profile.Header.Components text.FadeIn(duration, Easing.OutQuint); header.FadeIn(duration, Easing.OutQuint); background.FadeIn(duration, Easing.OutQuint); - this.MoveToY(-move_offset, duration, Easing.OutQuint); + this.MoveToY(base_y_offset - move_offset, duration, Easing.OutQuint); } private void hideContent() @@ -143,7 +145,7 @@ namespace osu.Game.Overlays.Profile.Header.Components text.FadeOut(duration, Easing.OutQuint); header.FadeOut(duration, Easing.OutQuint); background.FadeOut(duration, Easing.OutQuint); - this.MoveToY(0, duration, Easing.OutQuint); + this.MoveToY(base_y_offset, duration, Easing.OutQuint); } private partial class HoverIconContainer : Container From 8c989d77bdf020f2f34a84095ea95ac24f21bdd2 Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 16:00:46 +0200 Subject: [PATCH 48/90] revert: use `OverlayColourProvider` for B6 Refs: 293b81106261b1a3aad47d9461a8ba7f550c8b6 --- osu.Game/Graphics/OsuColour.cs | 1 - .../Overlays/Profile/Header/Components/PreviousUsernames.cs | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 481015de85..1b21f79c0a 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -383,7 +383,6 @@ namespace osu.Game.Graphics // Content Background public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28"); - public readonly Color4 B6 = Color4Extensions.FromHex(@"1c1719"); // hsl(333,10%,10%) public readonly Color4 RedLighter = Color4Extensions.FromHex(@"ffeded"); public readonly Color4 RedLight = Color4Extensions.FromHex(@"ed7787"); diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 686f6b2918..2ea175d1f0 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -99,9 +99,9 @@ namespace osu.Game.Overlays.Profile.Header.Components } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OverlayColourProvider colours) { - background.Colour = colours.B6; + background.Colour = colours.Background6; } protected override void LoadComplete() From f331f921518f1bf470480a255d7a6d4a448aad8d Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Mon, 11 Sep 2023 16:06:06 +0200 Subject: [PATCH 49/90] chore: explain `f6031bc1` --- .../Overlays/Profile/Header/Components/PreviousUsernames.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs index 2ea175d1f0..42f54ea43e 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs @@ -86,6 +86,9 @@ namespace osu.Game.Overlays.Profile.Header.Components RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, + // Prevents the tooltip of having a sudden size reduction + // and flickering when the text is being faded out. + // Also prevents a potential OnHover/HoverLost infinite loop AlwaysPresent = true, Margin = new MarginPadding { Bottom = margin, Top = margin / 2f } } From 824416067e0c95bdd4ca74f3da59f0cebfea9827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 11 Sep 2023 20:53:17 +0200 Subject: [PATCH 50/90] Fix test failure --- .../UserInterface/TestSceneModEffectPreviewPanel.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs index f958b231f9..adb12ad249 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -31,12 +30,14 @@ namespace osu.Game.Tests.Visual.UserInterface private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); private Container content = null!; + protected override Container Content => content; + private ModEffectPreviewPanel panel = null!; - [SetUpSteps] - public void SetUpSteps() + [BackgroundDependencyLoader] + private void load() { - AddStep("set up cache", () => Children = new Drawable[] + base.Content.AddRange(new Drawable[] { difficultyCache, content = new Container @@ -52,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface OsuModDifficultyAdjust difficultyAdjust = new OsuModDifficultyAdjust(); OsuModDoubleTime doubleTime = new OsuModDoubleTime(); - AddStep("create display", () => content.Child = panel = new ModEffectPreviewPanel + AddStep("create display", () => Child = panel = new ModEffectPreviewPanel { Anchor = Anchor.Centre, Origin = Anchor.Centre, From dbc07bc6dc50f5da76cf529cebcfdaa60105361e Mon Sep 17 00:00:00 2001 From: "tsrk." Date: Tue, 12 Sep 2023 01:34:17 +0200 Subject: [PATCH 51/90] fix: provide `OverlayColourProvider` as a dep in tests --- .../Online/TestSceneUserProfilePreviousUsernames.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs index 921738d331..4310710624 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs @@ -5,6 +5,7 @@ using System; using NUnit.Framework; using osu.Framework.Graphics; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Tests.Visual.Online @@ -13,12 +14,20 @@ namespace osu.Game.Tests.Visual.Online public partial class TestSceneUserProfilePreviousUsernames : OsuTestScene { private PreviousUsernames container = null!; + private OverlayColourProvider colourProvider = null!; [SetUp] public void SetUp() => Schedule(() => { - Child = container = new PreviousUsernames + colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); + Child = new DependencyProvidingContainer { + Child = container = new PreviousUsernames + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + CachedDependencies = new (Type, object)[] { (typeof(OverlayColourProvider), colourProvider) }, Anchor = Anchor.Centre, Origin = Anchor.Centre, }; From b4bedee49e0a8fc2104fbc5d98e2abb5d38edca4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 15:43:59 +0900 Subject: [PATCH 52/90] Adjust naming of component to give more context --- ...s.cs => TestSceneUserProfilePreviousUsernamesDisplay.cs} | 6 +++--- .../{PreviousUsernames.cs => PreviousUsernamesDisplay.cs} | 4 ++-- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneUserProfilePreviousUsernames.cs => TestSceneUserProfilePreviousUsernamesDisplay.cs} (92%) rename osu.Game/Overlays/Profile/Header/Components/{PreviousUsernames.cs => PreviousUsernamesDisplay.cs} (98%) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernamesDisplay.cs similarity index 92% rename from osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs rename to osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernamesDisplay.cs index 4310710624..c1140f60af 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernames.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfilePreviousUsernamesDisplay.cs @@ -11,9 +11,9 @@ using osu.Game.Overlays.Profile.Header.Components; namespace osu.Game.Tests.Visual.Online { [TestFixture] - public partial class TestSceneUserProfilePreviousUsernames : OsuTestScene + public partial class TestSceneUserProfilePreviousUsernamesDisplay : OsuTestScene { - private PreviousUsernames container = null!; + private PreviousUsernamesDisplay container = null!; private OverlayColourProvider colourProvider = null!; [SetUp] @@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Online colourProvider = new OverlayColourProvider(OverlayColourScheme.Pink); Child = new DependencyProvidingContainer { - Child = container = new PreviousUsernames + Child = container = new PreviousUsernamesDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs similarity index 98% rename from osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs rename to osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs index 42f54ea43e..634e6c5209 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernames.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs @@ -18,7 +18,7 @@ using osuTK; namespace osu.Game.Overlays.Profile.Header.Components { - public partial class PreviousUsernames : CompositeDrawable + public partial class PreviousUsernamesDisplay : CompositeDrawable { private const int duration = 200; private const int margin = 10; @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Header.Components private readonly Box background; private readonly SpriteText header; - public PreviousUsernames() + public PreviousUsernamesDisplay() { HoverIconContainer hoverIcon; diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 8aae13ebc5..146e38d389 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Profile.Header private OsuSpriteText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; private ToggleCoverButton coverToggle = null!; - private PreviousUsernames previousUsernames = null!; + private PreviousUsernamesDisplay previousUsernamesDisplay = null!; private Bindable coverExpanded = null!; @@ -146,7 +146,7 @@ namespace osu.Game.Overlays.Profile.Header }, new Container { - Child = previousUsernames = new PreviousUsernames + Child = previousUsernamesDisplay = new PreviousUsernamesDisplay { Anchor = Anchor.TopLeft, Origin = Anchor.TopLeft, @@ -228,7 +228,7 @@ namespace osu.Game.Overlays.Profile.Header titleText.Text = user?.Title ?? string.Empty; titleText.Colour = Color4Extensions.FromHex(user?.Colour ?? "fff"); groupBadgeFlow.User.Value = user; - previousUsernames.User.Value = user; + previousUsernamesDisplay.User.Value = user; } private void updateCoverState() From a6acd00908ffd82ce1b43b8e1f1d839ee3906ec7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 15:46:05 +0900 Subject: [PATCH 53/90] Reword inline comment --- .../Profile/Header/Components/PreviousUsernamesDisplay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs index 634e6c5209..dce5c84d12 100644 --- a/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs +++ b/osu.Game/Overlays/Profile/Header/Components/PreviousUsernamesDisplay.cs @@ -86,9 +86,8 @@ namespace osu.Game.Overlays.Profile.Header.Components RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Full, - // Prevents the tooltip of having a sudden size reduction - // and flickering when the text is being faded out. - // Also prevents a potential OnHover/HoverLost infinite loop + // Prevents the tooltip of having a sudden size reduction and flickering when the text is being faded out. + // Also prevents a potential OnHover/HoverLost feedback loop. AlwaysPresent = true, Margin = new MarginPadding { Bottom = margin, Top = margin / 2f } } From 23521ad394dbab69b8be597f800475dc06db4ddf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 15:53:27 +0900 Subject: [PATCH 54/90] Simplify container structure and mention why a zero-size container is being used --- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 146e38d389..36bd8a5af5 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -146,14 +146,8 @@ namespace osu.Game.Overlays.Profile.Header }, new Container { - Child = previousUsernamesDisplay = new PreviousUsernamesDisplay - { - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - }, - Anchor = Anchor.TopLeft, - Origin = Anchor.TopLeft, - Size = new Vector2(0), + // Intentionally use a zero-size container, else the fill flow will adjust to (and cancel) the upwards animation. + Child = previousUsernamesDisplay = new PreviousUsernamesDisplay(), } } }, From e40eaa73774804646f928989672054ce9d56f15a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 17:00:06 +0900 Subject: [PATCH 55/90] Improve collapse/expand animations Especially when on a screen resolution where it would start collapsed. --- .../Overlays/Mods/ModEffectPreviewPanel.cs | 23 +++++++++++++++---- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 +++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index a6d7732cba..b9511cc544 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -73,8 +73,6 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.BottomRight, Anchor = Anchor.BottomRight, AutoSizeAxes = Axes.X, - AutoSizeEasing = Easing.OutQuint, - AutoSizeDuration = transition_duration, Height = ShearedButton.HEIGHT, Shear = new Vector2(shear, 0), CornerRadius = ShearedButton.CORNER_RADIUS, @@ -135,6 +133,7 @@ namespace osu.Game.Overlays.Mods }, outerContent = new FillFlowContainer { + Alpha = 0, Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, AutoSizeAxes = Axes.X, @@ -163,7 +162,6 @@ namespace osu.Game.Overlays.Mods content.BorderColour = ColourInfo.GradientVertical(background.Colour, glowColour); innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glowColour); - BeatmapInfo.BindValueChanged(_ => updateValues()); mods.BindValueChanged(_ => { modSettingChangeTracker?.Dispose(); @@ -173,12 +171,20 @@ namespace osu.Game.Overlays.Mods updateValues(); }, true); - Collapsed.BindValueChanged(_ => updateCollapsedState(), true); - FinishTransforms(true); + Collapsed.BindValueChanged(_ => + { + // Only start autosize animations on first collapse toggle. This avoids an ugly initial presentation. + startAnimating(); + + updateCollapsedState(); + }); + + BeatmapInfo.BindValueChanged(_ => updateValues(), true); } protected override bool OnHover(HoverEvent e) { + startAnimating(); updateCollapsedState(); return true; } @@ -193,12 +199,19 @@ namespace osu.Game.Overlays.Mods protected override bool OnClick(ClickEvent e) => true; + private void startAnimating() + { + content.AutoSizeEasing = Easing.OutQuint; + content.AutoSizeDuration = transition_duration; + } + private void updateValues() => Scheduler.AddOnce(() => { if (BeatmapInfo.Value == null) return; cancellationSource?.Cancel(); + starDifficulty = difficultyCache.GetBindableDifficulty(BeatmapInfo.Value, (cancellationSource = new CancellationTokenSource()).Token); starDifficulty.BindValueChanged(s => { diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index d52782cf11..242f883068 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -339,9 +339,11 @@ namespace osu.Game.Overlays.Mods SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? Resources.Localisation.Web.CommonStrings.InputSearch : ModSelectOverlayStrings.TabToSearch; - if (modEffectPreviewPanel != null) + // only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be. + if (modEffectPreviewPanel != null && Alpha == 1) { float rightEdgeOfLastButton = footerButtonFlow.Last().ScreenSpaceDrawQuad.TopRight.X; + // this is cheating a bit; the 375 value is hardcoded based on how wide the expanded panel _generally_ is. // due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing. float projectedLeftEdgeOfExpandedModEffectPreviewPanel = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(375 + 70, 0)).X; From 3da30485b2766242f626798c206940cc52c9f90f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 17:06:42 +0900 Subject: [PATCH 56/90] Move shear spec to correct location --- .../Overlays/Mods/ModEffectPreviewPanel.cs | 20 +++++++++++++++---- .../Overlays/Mods/VerticalAttributeDisplay.cs | 8 +++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs index b9511cc544..c961235cb2 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs @@ -141,10 +141,22 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Horizontal, Children = new[] { - circleSizeDisplay = new VerticalAttributeDisplay("CS"), - drainRateDisplay = new VerticalAttributeDisplay("HP"), - approachRateDisplay = new VerticalAttributeDisplay("AR"), - overallDifficultyDisplay = new VerticalAttributeDisplay("OD"), + circleSizeDisplay = new VerticalAttributeDisplay("CS") + { + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + }, + drainRateDisplay = new VerticalAttributeDisplay("HP") + { + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + }, + approachRateDisplay = new VerticalAttributeDisplay("AR") + { + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + }, + overallDifficultyDisplay = new VerticalAttributeDisplay("OD") + { + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + }, } } } diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index ba8efd1c2b..60cc875dbb 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -10,7 +10,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osuTK; namespace osu.Game.Overlays.Mods { @@ -32,9 +31,12 @@ namespace osu.Game.Overlays.Mods public VerticalAttributeDisplay(LocalisableString label) { Label = label; + AutoSizeAxes = Axes.X; - Origin = Anchor = Anchor.CentreLeft; - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0); + + Origin = Anchor.CentreLeft; + Anchor = Anchor.CentreLeft; + InternalChild = new FillFlowContainer { Origin = Anchor.CentreLeft, From 7e3652284db7ab6bc14695ea4c4bb345edaf0bf1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 17:15:16 +0900 Subject: [PATCH 57/90] Adjust various class naming and add some xmldoc --- .../UserInterface/TestSceneModEffectPreviewPanel.cs | 4 ++-- .../Visual/UserInterface/TestSceneModsEffectDisplay.cs | 2 +- ...dEffectPreviewPanel.cs => BeatmapAttributesDisplay.cs} | 6 +++++- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 2 +- .../Mods/{ModsEffectDisplay.cs => ModCounterDisplay.cs} | 6 +++--- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- 6 files changed, 16 insertions(+), 12 deletions(-) rename osu.Game/Overlays/Mods/{ModEffectPreviewPanel.cs => BeatmapAttributesDisplay.cs} (97%) rename osu.Game/Overlays/Mods/{ModsEffectDisplay.cs => ModCounterDisplay.cs} (97%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs index adb12ad249..b3ad5a499e 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModEffectPreviewPanel.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.UserInterface private Container content = null!; protected override Container Content => content; - private ModEffectPreviewPanel panel = null!; + private BeatmapAttributesDisplay panel = null!; [BackgroundDependencyLoader] private void load() @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface OsuModDifficultyAdjust difficultyAdjust = new OsuModDifficultyAdjust(); OsuModDoubleTime doubleTime = new OsuModDoubleTime(); - AddStep("create display", () => Child = panel = new ModEffectPreviewPanel + AddStep("create display", () => Child = panel = new BeatmapAttributesDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs index a1c8bef1de..bf6a718167 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyIncrease)); } - private partial class TestDisplay : ModsEffectDisplay + private partial class TestDisplay : ModCounterDisplay { public Container Container => Content; diff --git a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs similarity index 97% rename from osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs rename to osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index c961235cb2..2aea53090a 100644 --- a/osu.Game/Overlays/Mods/ModEffectPreviewPanel.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -25,7 +25,11 @@ using osu.Game.Configuration; namespace osu.Game.Overlays.Mods { - public partial class ModEffectPreviewPanel : CompositeDrawable + /// + /// On the mod select overlay, this provides a local updating view of BPM, star rating and other + /// difficulty attributes so the user can have a better insight into what mods are changing. + /// + public partial class BeatmapAttributesDisplay : CompositeDrawable { private Container content = null!; private Container innerContent = null!; diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 9e16aa926f..9f4d187879 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -9,7 +9,7 @@ using osu.Game.Localisation; namespace osu.Game.Overlays.Mods { - public sealed partial class DifficultyMultiplierDisplay : ModsEffectDisplay + public sealed partial class DifficultyMultiplierDisplay : ModCounterDisplay { protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; diff --git a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs b/osu.Game/Overlays/Mods/ModCounterDisplay.cs similarity index 97% rename from osu.Game/Overlays/Mods/ModsEffectDisplay.cs rename to osu.Game/Overlays/Mods/ModCounterDisplay.cs index 3f31736ee1..3bec27d290 100644 --- a/osu.Game/Overlays/Mods/ModsEffectDisplay.cs +++ b/osu.Game/Overlays/Mods/ModCounterDisplay.cs @@ -19,9 +19,9 @@ using osuTK; namespace osu.Game.Overlays.Mods { /// - /// Base class for displays of mods effects. + /// Base class for displays of singular counters. Not to be confused with which aggregates multiple attributes. /// - public abstract partial class ModsEffectDisplay : Container, IHasCurrentValue + public abstract partial class ModCounterDisplay : Container, IHasCurrentValue { public const float HEIGHT = 42; private const float transition_duration = 200; @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.Mods protected readonly RollingCounter Counter; - protected ModsEffectDisplay() + protected ModCounterDisplay() { Height = HEIGHT; AutoSizeAxes = Axes.X; diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 242f883068..666e2849e8 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Mods private Container aboveColumnsContent = null!; private DifficultyMultiplierDisplay? multiplierDisplay; - private ModEffectPreviewPanel? modEffectPreviewPanel; + private BeatmapAttributesDisplay? modEffectPreviewPanel; protected ShearedButton BackButton { get; private set; } = null!; protected ShearedToggleButton? CustomisationButton { get; private set; } @@ -181,7 +181,7 @@ namespace osu.Game.Overlays.Mods aboveColumnsContent = new Container { RelativeSizeAxes = Axes.X, - Height = ModsEffectDisplay.HEIGHT, + Height = ModCounterDisplay.HEIGHT, Padding = new MarginPadding { Horizontal = 100 }, Child = SearchTextBox = new ShearedSearchTextBox { @@ -196,7 +196,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = ModsEffectDisplay.HEIGHT + PADDING, + Top = ModCounterDisplay.HEIGHT + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, @@ -257,7 +257,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.TopRight }); - FooterContent.Add(modEffectPreviewPanel = new ModEffectPreviewPanel + FooterContent.Add(modEffectPreviewPanel = new BeatmapAttributesDisplay { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, From 95d15a703eb33b37baa10d90d56fdc0ce4df550f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 11:36:41 +0200 Subject: [PATCH 58/90] Improve legibility of scoring test scene - Add black background to avoid clashes with background images. - Use sorta-tetradic colours for the four plots. --- .../Visual/Gameplay/TestSceneScoring.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index c722d67ac9..4cbc046877 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -36,6 +37,9 @@ namespace osu.Game.Tests.Visual.Gameplay private FillFlowContainer legend = null!; + [Resolved] + private OsuColour colours { get; set; } = null!; + [Test] public void TestBasic() { @@ -43,6 +47,11 @@ namespace osu.Game.Tests.Visual.Gameplay { Children = new Drawable[] { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Colour4.Black + }, new GridContainer { RelativeSizeAxes = Axes.Both, @@ -125,8 +134,8 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); - runForProcessor("lazer-standardised", Color4.YellowGreen, new OsuScoreProcessor(), ScoringMode.Standardised); - runForProcessor("lazer-classic", Color4.MediumPurple, new OsuScoreProcessor(), ScoringMode.Classic); + runForProcessor("lazer-standardised", colours.Green1, new OsuScoreProcessor(), ScoringMode.Standardised); + runForProcessor("lazer-classic", colours.Blue1, new OsuScoreProcessor(), ScoringMode.Classic); runScoreV1(); runScoreV2(); @@ -156,7 +165,7 @@ namespace osu.Game.Tests.Visual.Gameplay currentCombo++; } - runForAlgorithm("ScoreV1 (classic)", Color4.Purple, + runForAlgorithm("ScoreV1 (classic)", colours.Purple1, () => applyHitV1(base_great), () => applyHitV1(base_ok), () => applyHitV1(0), @@ -199,7 +208,7 @@ namespace osu.Game.Tests.Visual.Gameplay currentHits++; } - runForAlgorithm("ScoreV2", Color4.OrangeRed, + runForAlgorithm("ScoreV2", colours.Red1, () => applyHitV2(base_great), () => applyHitV2(base_ok), () => From 3cf8082aa7b2877a7f533074ad697b4329cf391f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 12:07:12 +0200 Subject: [PATCH 59/90] Add capability to toggle visibility of graph --- .../Visual/Gameplay/TestSceneScoring.cs | 92 ++++++++++++++++--- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 4cbc046877..d7974b63e7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; @@ -75,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay legend = new FillFlowContainer { Padding = new MarginPadding(20), - Direction = FillDirection.Full, + Direction = FillDirection.Vertical, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, }, @@ -263,7 +264,8 @@ namespace osu.Game.Tests.Visual.Gameplay results.Add(getTotalScore()); } - graphs.Add(new LineGraph + LineGraph graph; + graphs.Add(graph = new LineGraph { Name = name, RelativeSizeAxes = Axes.Both, @@ -271,20 +273,9 @@ namespace osu.Game.Tests.Visual.Gameplay Values = results }); - legend.Add(new OsuSpriteText + legend.Add(new LegendEntry(name, getTotalScore(), graph) { - Colour = colour, - RelativeSizeAxes = Axes.X, - Width = 0.5f, - Text = $"{FontAwesome.Solid.Circle.Icon} {name}" - }); - - legend.Add(new OsuSpriteText - { - Colour = colour, - RelativeSizeAxes = Axes.X, - Width = 0.5f, - Text = $"final score {getTotalScore():#,0}" + AccentColour = colour }); } } @@ -505,4 +496,75 @@ namespace osu.Game.Tests.Visual.Gameplay public void Move(Vector2 pos) => this.MoveTo(pos); } } + + public partial class LegendEntry : OsuClickableContainer, IHasAccentColour + { + public Color4 AccentColour { get; set; } + + public BindableBool Visible { get; } = new BindableBool(true); + + private readonly string description; + private readonly long finalScore; + private readonly LineGraph lineGraph; + + private OsuSpriteText descriptionText = null!; + private OsuSpriteText finalScoreText = null!; + + public LegendEntry(string description, long finalScore, LineGraph lineGraph) + { + this.description = description; + this.finalScore = finalScore; + this.lineGraph = lineGraph; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Content.RelativeSizeAxes = Axes.X; + AutoSizeAxes = Content.AutoSizeAxes = Axes.Y; + + Children = new Drawable[] + { + descriptionText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + finalScoreText = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Font = OsuFont.Default.With(fixedWidth: true) + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + Visible.BindValueChanged(_ => updateState(), true); + Action = Visible.Toggle; + } + + protected override bool OnHover(HoverEvent e) + { + updateState(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateState(); + base.OnHoverLost(e); + } + + private void updateState() + { + Colour = IsHovered ? AccentColour.Lighten(0.2f) : AccentColour; + + descriptionText.Text = $"{(Visible.Value ? FontAwesome.Solid.CheckCircle.Icon : FontAwesome.Solid.Circle.Icon)} {description}"; + finalScoreText.Text = finalScore.ToString("#,0"); + lineGraph.Alpha = Visible.Value ? 1 : 0; + } + } } From 607ceeccb98e8cfd269bdf17156cbf2432eaab98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 12:19:50 +0200 Subject: [PATCH 60/90] Persist visibility state of graphs --- .../Visual/Gameplay/TestSceneScoring.cs | 99 +++++++++++++------ 1 file changed, 67 insertions(+), 32 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index d7974b63e7..0162d57cd5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -38,6 +38,11 @@ namespace osu.Game.Tests.Visual.Gameplay private FillFlowContainer legend = null!; + private readonly BindableBool standardisedVisible = new BindableBool(true); + private readonly BindableBool classicVisible = new BindableBool(true); + private readonly BindableBool scoreV1Visible = new BindableBool(true); + private readonly BindableBool scoreV2Visible = new BindableBool(true); + [Resolved] private OsuColour colours { get; set; } = null!; @@ -135,8 +140,8 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); - runForProcessor("lazer-standardised", colours.Green1, new OsuScoreProcessor(), ScoringMode.Standardised); - runForProcessor("lazer-classic", colours.Blue1, new OsuScoreProcessor(), ScoringMode.Classic); + runForProcessor("lazer-standardised", colours.Green1, new OsuScoreProcessor(), ScoringMode.Standardised, standardisedVisible); + runForProcessor("lazer-classic", colours.Blue1, new OsuScoreProcessor(), ScoringMode.Classic, classicVisible); runScoreV1(); runScoreV2(); @@ -166,17 +171,22 @@ namespace osu.Game.Tests.Visual.Gameplay currentCombo++; } - runForAlgorithm("ScoreV1 (classic)", colours.Purple1, - () => applyHitV1(base_great), - () => applyHitV1(base_ok), - () => applyHitV1(0), - () => + runForAlgorithm(new ScoringAlgorithm + { + Name = "ScoreV1 (classic)", + Colour = colours.Purple1, + ApplyHit = () => applyHitV1(base_great), + ApplyNonPerfect = () => applyHitV1(base_ok), + ApplyMiss = () => applyHitV1(0), + GetTotalScore = () => { // Arbitrary value chosen towards the upper range. const double score_multiplier = 4; return (int)(totalScore * score_multiplier); - }); + }, + Visible = scoreV1Visible + }); } private void runScoreV2() @@ -209,15 +219,19 @@ namespace osu.Game.Tests.Visual.Gameplay currentHits++; } - runForAlgorithm("ScoreV2", colours.Red1, - () => applyHitV2(base_great), - () => applyHitV2(base_ok), - () => + runForAlgorithm(new ScoringAlgorithm + { + Name = "ScoreV2", + Colour = colours.Red1, + ApplyHit = () => applyHitV2(base_great), + ApplyNonPerfect = () => applyHitV2(base_ok), + ApplyMiss = () => { currentHits++; maxBaseScore += base_great; currentCombo = 0; - }, () => + }, + GetTotalScore = () => { double accuracy = currentBaseScore / maxBaseScore; @@ -226,10 +240,12 @@ namespace osu.Game.Tests.Visual.Gameplay 700000 * comboPortion / comboPortionMax + 300000 * Math.Pow(accuracy, 10) * ((double)currentHits / maxCombo) ); - }); + }, + Visible = scoreV2Visible + }); } - private void runForProcessor(string name, Color4 colour, ScoreProcessor processor, ScoringMode mode) + private void runForProcessor(string name, Color4 colour, ScoreProcessor processor, ScoringMode mode, BindableBool visibility) { int maxCombo = sliderMaxCombo.Current.Value; @@ -239,14 +255,19 @@ namespace osu.Game.Tests.Visual.Gameplay processor.ApplyBeatmap(beatmap); - runForAlgorithm(name, colour, - () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), - () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Ok }), - () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), - () => processor.GetDisplayScore(mode)); + runForAlgorithm(new ScoringAlgorithm + { + Name = name, + Colour = colour, + ApplyHit = () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Great }), + ApplyNonPerfect = () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Ok }), + ApplyMiss = () => processor.ApplyResult(new OsuJudgementResult(new HitCircle(), new OsuJudgement()) { Type = HitResult.Miss }), + GetTotalScore = () => processor.GetDisplayScore(mode), + Visible = visibility + }); } - private void runForAlgorithm(string name, Color4 colour, Action applyHit, Action applyNonPerfect, Action applyMiss, Func getTotalScore) + private void runForAlgorithm(ScoringAlgorithm scoringAlgorithm) { int maxCombo = sliderMaxCombo.Current.Value; @@ -255,31 +276,42 @@ namespace osu.Game.Tests.Visual.Gameplay for (int i = 0; i < maxCombo; i++) { if (graphs.MissLocations.Contains(i)) - applyMiss(); + scoringAlgorithm.ApplyMiss(); else if (graphs.NonPerfectLocations.Contains(i)) - applyNonPerfect(); + scoringAlgorithm.ApplyNonPerfect(); else - applyHit(); + scoringAlgorithm.ApplyHit(); - results.Add(getTotalScore()); + results.Add(scoringAlgorithm.GetTotalScore()); } LineGraph graph; graphs.Add(graph = new LineGraph { - Name = name, + Name = scoringAlgorithm.Name, RelativeSizeAxes = Axes.Both, - LineColour = colour, + LineColour = scoringAlgorithm.Colour, Values = results }); - legend.Add(new LegendEntry(name, getTotalScore(), graph) + legend.Add(new LegendEntry(scoringAlgorithm, graph) { - AccentColour = colour + AccentColour = scoringAlgorithm.Colour, }); } } + public class ScoringAlgorithm + { + public string Name { get; init; } = null!; + public Color4 Colour { get; init; } + public Action ApplyHit { get; init; } = () => { }; + public Action ApplyNonPerfect { get; init; } = () => { }; + public Action ApplyMiss { get; init; } = () => { }; + public Func GetTotalScore { get; init; } = null!; + public BindableBool Visible { get; init; } = null!; + } + public partial class GraphContainer : Container, IHasCustomTooltip> { public readonly BindableList MissLocations = new BindableList(); @@ -510,10 +542,13 @@ namespace osu.Game.Tests.Visual.Gameplay private OsuSpriteText descriptionText = null!; private OsuSpriteText finalScoreText = null!; - public LegendEntry(string description, long finalScore, LineGraph lineGraph) + public LegendEntry(ScoringAlgorithm scoringAlgorithm, LineGraph lineGraph) { - this.description = description; - this.finalScore = finalScore; + description = scoringAlgorithm.Name; + finalScore = scoringAlgorithm.GetTotalScore(); + AccentColour = scoringAlgorithm.Colour; + Visible.BindTo(scoringAlgorithm.Visible); + this.lineGraph = lineGraph; } From 15708ee46546562ffea09cd21e1af76491df17ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 13:27:40 +0200 Subject: [PATCH 61/90] Add toggle for showing relative/absolute score values --- .../Visual/Gameplay/TestSceneScoring.cs | 59 +++++++++++++++---- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 0162d57cd5..3cd79fa729 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -35,8 +35,9 @@ namespace osu.Game.Tests.Visual.Gameplay { private GraphContainer graphs = null!; private SettingsSlider sliderMaxCombo = null!; + private SettingsCheckbox scaleToMax = null!; - private FillFlowContainer legend = null!; + private FillFlowContainer legend = null!; private readonly BindableBool standardisedVisible = new BindableBool(true); private readonly BindableBool classicVisible = new BindableBool(true); @@ -78,7 +79,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, new Drawable[] { - legend = new FillFlowContainer + legend = new FillFlowContainer { Padding = new MarginPadding(20), Direction = FillDirection.Vertical, @@ -93,26 +94,31 @@ namespace osu.Game.Tests.Visual.Gameplay Padding = new MarginPadding(20), RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Direction = FillDirection.Full, + Direction = FillDirection.Vertical, + Spacing = new Vector2(10), Children = new Drawable[] { sliderMaxCombo = new SettingsSlider { - Width = 0.5f, TransferValueOnCommit = true, Current = new BindableInt(1024) { MinValue = 96, MaxValue = 8192, }, - LabelText = "max combo", + LabelText = "Max combo", + }, + scaleToMax = new SettingsCheckbox + { + LabelText = "Rescale plots to 100%", + Current = { Value = true, Default = true } }, new OsuTextFlowContainer { RelativeSizeAxes = Axes.X, - Width = 0.5f, AutoSizeAxes = Axes.Y, - Text = $"Left click to add miss\nRight click to add OK/{base_ok}" + Text = $"Left click to add miss\nRight click to add OK/{base_ok}", + Margin = new MarginPadding { Top = 20 } } } }, @@ -122,6 +128,12 @@ namespace osu.Game.Tests.Visual.Gameplay }; sliderMaxCombo.Current.BindValueChanged(_ => rerun()); + scaleToMax.Current.BindValueChanged(_ => rerun()); + + standardisedVisible.BindValueChanged(_ => rescalePlots()); + classicVisible.BindValueChanged(_ => rescalePlots()); + scoreV1Visible.BindValueChanged(_ => rescalePlots()); + scoreV2Visible.BindValueChanged(_ => rescalePlots()); graphs.MissLocations.BindCollectionChanged((_, __) => rerun()); graphs.NonPerfectLocations.BindCollectionChanged((_, __) => rerun()); @@ -145,6 +157,24 @@ namespace osu.Game.Tests.Visual.Gameplay runScoreV1(); runScoreV2(); + + rescalePlots(); + } + + private void rescalePlots() + { + if (!scaleToMax.Current.Value && legend.Any(entry => entry.Visible.Value)) + { + long maxScore = legend.Where(entry => entry.Visible.Value).Max(entry => entry.FinalScore); + + foreach (var graph in graphs) + graph.Height = graph.Values.Max() / maxScore; + } + else + { + foreach (var graph in graphs) + graph.Height = 1; + } } private void runScoreV1() @@ -289,6 +319,8 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Add(graph = new LineGraph { Name = scoringAlgorithm.Name, + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, LineColour = scoringAlgorithm.Colour, Values = results @@ -312,14 +344,14 @@ namespace osu.Game.Tests.Visual.Gameplay public BindableBool Visible { get; init; } = null!; } - public partial class GraphContainer : Container, IHasCustomTooltip> + public partial class GraphContainer : Container, IHasCustomTooltip> { public readonly BindableList MissLocations = new BindableList(); public readonly BindableList NonPerfectLocations = new BindableList(); public Bindable MaxCombo = new Bindable(); - protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; + protected override Container Content { get; } = new Container { RelativeSizeAxes = Axes.Both }; private readonly Box hoverLine; @@ -470,7 +502,7 @@ namespace osu.Game.Tests.Visual.Gameplay public ITooltip> GetCustomTooltip() => tooltip ??= new GraphTooltip(this); - public IEnumerable TooltipContent => Content.OfType(); + public IEnumerable TooltipContent => Content; public partial class GraphTooltip : CompositeDrawable, ITooltip> { @@ -535,8 +567,9 @@ namespace osu.Game.Tests.Visual.Gameplay public BindableBool Visible { get; } = new BindableBool(true); + public readonly long FinalScore; + private readonly string description; - private readonly long finalScore; private readonly LineGraph lineGraph; private OsuSpriteText descriptionText = null!; @@ -545,7 +578,7 @@ namespace osu.Game.Tests.Visual.Gameplay public LegendEntry(ScoringAlgorithm scoringAlgorithm, LineGraph lineGraph) { description = scoringAlgorithm.Name; - finalScore = scoringAlgorithm.GetTotalScore(); + FinalScore = scoringAlgorithm.GetTotalScore(); AccentColour = scoringAlgorithm.Colour; Visible.BindTo(scoringAlgorithm.Visible); @@ -598,7 +631,7 @@ namespace osu.Game.Tests.Visual.Gameplay Colour = IsHovered ? AccentColour.Lighten(0.2f) : AccentColour; descriptionText.Text = $"{(Visible.Value ? FontAwesome.Solid.CheckCircle.Icon : FontAwesome.Solid.Circle.Icon)} {description}"; - finalScoreText.Text = finalScore.ToString("#,0"); + finalScoreText.Text = FinalScore.ToString("#,0"); lineGraph.Alpha = Visible.Value ? 1 : 0; } } From 3981e2e957ab14d1d8f23b2e65d8f910c18765fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 13:29:02 +0200 Subject: [PATCH 62/90] Only show visible graph values in tooltip --- osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 3cd79fa729..0e64af9920 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -550,6 +550,8 @@ namespace osu.Game.Tests.Visual.Gameplay foreach (var graph in content) { + if (graph.Alpha == 0) continue; + float valueAtHover = graph.Values.ElementAt(relevantCombo); float ofTotal = valueAtHover / graph.Values.Last(); From e810abf1e61aa032ad9bc3d3a4a18cd366cacbc3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 12 Sep 2023 20:51:22 +0900 Subject: [PATCH 63/90] Rename variable --- .../Objects/Drawables/DrawableSpinner.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index c30ac6b88b..24446db92a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -312,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables tick.TriggerResult(true); if (tick is DrawableSpinnerBonusTick) - gainedBonus.Value = score_per_tick * (spins - HitObject.FirstBonusSpin); + gainedBonus.Value = score_per_tick * (spins - HitObject.SpinsRequiredForBonus); } completedFullSpins++; diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index a26cb7b090..5ef670c739 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -31,6 +31,11 @@ namespace osu.Game.Rulesets.Osu.Objects /// public int SpinsRequired { get; protected set; } = 1; + /// + /// The number of spins required to start receiving bonus score. The first bonus is awarded on this spin count. + /// + public int SpinsRequiredForBonus => SpinsRequired + bonus_spins_gap; + /// /// The gap between spinner completion and the first bonus-awarding spin. /// @@ -41,11 +46,6 @@ namespace osu.Game.Rulesets.Osu.Objects /// public int MaximumBonusSpins { get; protected set; } = 1; - /// - /// The first spin awarding bonus score. - /// - public int FirstBonusSpin => SpinsRequired + bonus_spins_gap; - public override Vector2 StackOffset => Vector2.Zero; protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects double startTime = StartTime + (float)(i + 1) / totalSpins * Duration; - AddNested(i < FirstBonusSpin + AddNested(i < SpinsRequiredForBonus ? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration } : new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { CreateHitSampleInfo("spinnerbonus") } }); } From 7c65a9de75e3342e46240e4d588291fda430c9f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 12 Sep 2023 20:31:49 +0200 Subject: [PATCH 64/90] Fix Score V1 simulation in scoring test scene incorrectly applying multiplier `TestSceneScoring` included a local simulation of stable's Score V1 algorithm. One of the parts of said algorithm is a mysterious "score multiplier", influenced by - among others - the beatmap's drain rate, overall difficulty, circle size, object count, drain length, and active mods. (An implementation of this already exists in lazer source, in `OsuLegacyScoreSimulator`, but more on this later.) However, `TestSceneScoring` had this multiplier in _two_ places, with _two_ distinct values, one of which being 1 (i.e. basically off). Unfortunately, the place that had 1 as the multiplier was the wrong one. Stable calculates the score increase for every hit in two stages; first, it takes the raw numerical value of the judgement, but then applies a combo-based bonus on top of it: scoreIncrease += (int)(Math.Max(0, ComboCounter.HitCombo - 1) * (scoreIncrease / 25 * ScoreMultiplier)); On the face of it, it may appear that the `ScoreMultiplier` factor can be factored out and applied at the end only when returning total score. However, once the above formula is rewritten as: scoreIncrease = scoreIncrease + (int)(Math.Max(0, ComboCounter.HitCombo - 1) * (scoreIncrease / 25 * ScoreMultiplier)); = scoreIncrease * (1 + (Math.Max(0, ComboCounter.HitCombo - 1) / 25 * ScoreMultiplier)) it becomes clear that that assumption is actually _incorrect_, and the `ScoreMultiplier` _must_ be applied to every score increase individually. The above was cross-checked experimentally against stable source on an example test map with 100 objects, and a replay hitting them perfectly. --- osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs index 0e64af9920..5db05ea67c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneScoring.cs @@ -190,7 +190,9 @@ namespace osu.Game.Tests.Visual.Gameplay return; } - const float score_multiplier = 1; + // this corresponds to stable's `ScoreMultiplier`. + // value is chosen arbitrarily, towards the upper range. + const float score_multiplier = 4; totalScore += baseScore; @@ -208,13 +210,7 @@ namespace osu.Game.Tests.Visual.Gameplay ApplyHit = () => applyHitV1(base_great), ApplyNonPerfect = () => applyHitV1(base_ok), ApplyMiss = () => applyHitV1(0), - GetTotalScore = () => - { - // Arbitrary value chosen towards the upper range. - const double score_multiplier = 4; - - return (int)(totalScore * score_multiplier); - }, + GetTotalScore = () => totalScore, Visible = scoreV1Visible }); } From d146da95466e5251f37376da8fbbf07701e6c1d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 16:43:06 +0900 Subject: [PATCH 65/90] Fix rate adjust mods not showing "x" prefix on customisation slider's tooltip --- .../Settings/MultiplierSettingsSlider.cs | 24 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs | 3 ++- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 3 ++- osu.Game/Rulesets/Mods/ModHalfTime.cs | 3 ++- osu.Game/Rulesets/Mods/ModTimeRamp.cs | 5 ++-- 5 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Overlays/Settings/MultiplierSettingsSlider.cs diff --git a/osu.Game/Overlays/Settings/MultiplierSettingsSlider.cs b/osu.Game/Overlays/Settings/MultiplierSettingsSlider.cs new file mode 100644 index 0000000000..bd5b8f92b6 --- /dev/null +++ b/osu.Game/Overlays/Settings/MultiplierSettingsSlider.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; +using osu.Game.Graphics.UserInterface; + +namespace osu.Game.Overlays.Settings +{ + public partial class MultiplierSettingsSlider : SettingsSlider + { + public MultiplierSettingsSlider() + { + KeyboardStep = 0.01f; + } + + /// + /// A slider bar which adds a "x" to the end of the tooltip string. + /// + public partial class MultiplierRoundedSliderBar : RoundedSliderBar + { + public override LocalisableString TooltipText => $"{base.TooltipText}x"; + } + } +} diff --git a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs index e7127abcf0..f62ba21827 100644 --- a/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs +++ b/osu.Game/Rulesets/Mods/ModAdaptiveSpeed.cs @@ -10,6 +10,7 @@ using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -35,7 +36,7 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => new[] { typeof(ModRateAdjust), typeof(ModTimeRamp), typeof(ModAutoplay) }; - [SettingSource("Initial rate", "The starting speed of the track")] + [SettingSource("Initial rate", "The starting speed of the track", SettingControlType = typeof(MultiplierSettingsSlider))] public BindableNumber InitialRate { get; } = new BindableDouble(1) { MinValue = 0.5, diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 733610c040..27e594edfe 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override LocalisableString Description => "Zoooooooooom..."; - [SettingSource("Speed increase", "The actual increase to apply")] + [SettingSource("Speed increase", "The actual increase to apply", SettingControlType = typeof(MultiplierSettingsSlider))] public override BindableNumber SpeedChange { get; } = new BindableDouble(1.5) { MinValue = 1.01, diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 06c7750035..7415c94cd8 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -6,6 +6,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override LocalisableString Description => "Less zoom..."; - [SettingSource("Speed decrease", "The actual decrease to apply")] + [SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))] public override BindableNumber SpeedChange { get; } = new BindableDouble(0.75) { MinValue = 0.5, diff --git a/osu.Game/Rulesets/Mods/ModTimeRamp.cs b/osu.Game/Rulesets/Mods/ModTimeRamp.cs index 7285315c3b..54ee0cd3bf 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.Bindables; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Mods @@ -20,10 +21,10 @@ namespace osu.Game.Rulesets.Mods public override double ScoreMultiplier => 0.5; - [SettingSource("Initial rate", "The starting speed of the track")] + [SettingSource("Initial rate", "The starting speed of the track", SettingControlType = typeof(MultiplierSettingsSlider))] public abstract BindableNumber InitialRate { get; } - [SettingSource("Final rate", "The final speed to ramp to")] + [SettingSource("Final rate", "The final speed to ramp to", SettingControlType = typeof(MultiplierSettingsSlider))] public abstract BindableNumber FinalRate { get; } [SettingSource("Adjust pitch", "Should pitch be adjusted with speed")] From f5cee22db79eca51c234da2fc1865ee1606ef1de Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 17:05:42 +0900 Subject: [PATCH 66/90] Collapse mod presets column slightly when not in use --- osu.Game/Overlays/Mods/ModPresetColumn.cs | 10 +++++++++- osu.Game/Overlays/Mods/ModSelectColumn.cs | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModPresetColumn.cs b/osu.Game/Overlays/Mods/ModPresetColumn.cs index 3b12eec195..0803389f45 100644 --- a/osu.Game/Overlays/Mods/ModPresetColumn.cs +++ b/osu.Game/Overlays/Mods/ModPresetColumn.cs @@ -26,6 +26,8 @@ namespace osu.Game.Overlays.Mods [Resolved] private IBindable ruleset { get; set; } = null!; + private const float contracted_width = WIDTH - 120; + [BackgroundDependencyLoader] private void load(OsuColour colours) { @@ -42,6 +44,8 @@ namespace osu.Game.Overlays.Mods base.LoadComplete(); ruleset.BindValueChanged(_ => rulesetChanged(), true); + + Width = contracted_width; } private IDisposable? presetSubscription; @@ -65,7 +69,11 @@ namespace osu.Game.Overlays.Mods { cancellationTokenSource?.Cancel(); - if (!presets.Any()) + bool hasPresets = presets.Any(); + + this.ResizeWidthTo(hasPresets ? WIDTH : contracted_width, 200, Easing.OutQuint); + + if (!hasPresets) { removeAndDisposePresetPanels(); return; diff --git a/osu.Game/Overlays/Mods/ModSelectColumn.cs b/osu.Game/Overlays/Mods/ModSelectColumn.cs index 338ebdaef4..1c56763bd9 100644 --- a/osu.Game/Overlays/Mods/ModSelectColumn.cs +++ b/osu.Game/Overlays/Mods/ModSelectColumn.cs @@ -61,9 +61,11 @@ namespace osu.Game.Overlays.Mods private const float header_height = 42; + protected const float WIDTH = 320; + protected ModSelectColumn() { - Width = 320; + Width = WIDTH; RelativeSizeAxes = Axes.Y; Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0); From be373391f8ef90c5b1da0ca63dca62a7fc76a9f9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 17:43:41 +0900 Subject: [PATCH 67/90] Simplify multiplier display implementation --- osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs | 1 + osu.Game/Overlays/Mods/ModCounterDisplay.cs | 9 +++------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 9f4d187879..7f92a4e644 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -19,6 +19,7 @@ namespace osu.Game.Overlays.Mods { Current.Default = 1d; Current.Value = 1d; + Add(new SpriteIcon { Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/Mods/ModCounterDisplay.cs b/osu.Game/Overlays/Mods/ModCounterDisplay.cs index 3bec27d290..de8f4433f4 100644 --- a/osu.Game/Overlays/Mods/ModCounterDisplay.cs +++ b/osu.Game/Overlays/Mods/ModCounterDisplay.cs @@ -49,8 +49,6 @@ namespace osu.Game.Overlays.Mods /// protected abstract LocalisableString Label { get; } - protected virtual float ValueAreaWidth => 56; - protected virtual string CounterFormat => @"N0"; protected override Container Content => content; @@ -59,7 +57,7 @@ namespace osu.Game.Overlays.Mods protected ModCounterDisplay() { - Height = HEIGHT; + Height = ShearedButton.HEIGHT; AutoSizeAxes = Axes.X; InternalChild = new InputBlockingContainer @@ -75,8 +73,7 @@ namespace osu.Game.Overlays.Mods { Anchor = Anchor.CentreRight, Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Y, - Width = ValueAreaWidth + ModSelectPanel.CORNER_RADIUS + RelativeSizeAxes = Axes.Both, }, new GridContainer { @@ -85,7 +82,7 @@ namespace osu.Game.Overlays.Mods ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, ValueAreaWidth) + new Dimension(GridSizeMode.Absolute, 56) }, Content = new[] { From 49114430ed9ac71858c2cb1adbde90d28683ab6d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 17:43:55 +0900 Subject: [PATCH 68/90] Move multiplier display to bottom of sceen to make visible while customising --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 47 +++++++++++++++------- 1 file changed, 33 insertions(+), 14 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 666e2849e8..fb28c41395 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -120,11 +120,12 @@ namespace osu.Game.Overlays.Mods private ColumnScrollContainer columnScroll = null!; private ColumnFlowContainer columnFlow = null!; private FillFlowContainer footerButtonFlow = null!; + private FillFlowContainer footerContentFlow = null!; private DeselectAllModsButton deselectAllModsButton = null!; private Container aboveColumnsContent = null!; private DifficultyMultiplierDisplay? multiplierDisplay; - private BeatmapAttributesDisplay? modEffectPreviewPanel; + private BeatmapAttributesDisplay? beatmapAttributesDisplay; protected ShearedButton BackButton { get; private set; } = null!; protected ShearedToggleButton? CustomisationButton { get; private set; } @@ -142,8 +143,8 @@ namespace osu.Game.Overlays.Mods if (beatmap == value) return; beatmap = value; - if (IsLoaded && modEffectPreviewPanel != null) - modEffectPreviewPanel.BeatmapInfo.Value = beatmap?.BeatmapInfo; + if (IsLoaded && beatmapAttributesDisplay != null) + beatmapAttributesDisplay.BeatmapInfo.Value = beatmap?.BeatmapInfo; } } @@ -251,22 +252,32 @@ namespace osu.Game.Overlays.Mods if (ShowModEffects) { - aboveColumnsContent.Add(multiplierDisplay = new DifficultyMultiplierDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight - }); - - FooterContent.Add(modEffectPreviewPanel = new BeatmapAttributesDisplay + FooterContent.Add(footerContentFlow = new FillFlowContainer { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(30, 10), Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight, Margin = new MarginPadding { Vertical = PADDING, - Horizontal = 70 + Horizontal = 20 }, - BeatmapInfo = { Value = beatmap?.BeatmapInfo } + Children = new Drawable[] + { + beatmapAttributesDisplay = new BeatmapAttributesDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + BeatmapInfo = { Value = beatmap?.BeatmapInfo } + }, + multiplierDisplay = new DifficultyMultiplierDisplay + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight + }, + } }); } @@ -340,14 +351,22 @@ namespace osu.Game.Overlays.Mods SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? Resources.Localisation.Web.CommonStrings.InputSearch : ModSelectOverlayStrings.TabToSearch; // only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be. - if (modEffectPreviewPanel != null && Alpha == 1) + if (beatmapAttributesDisplay != null && Alpha == 1) { float rightEdgeOfLastButton = footerButtonFlow.Last().ScreenSpaceDrawQuad.TopRight.X; // this is cheating a bit; the 375 value is hardcoded based on how wide the expanded panel _generally_ is. // due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing. float projectedLeftEdgeOfExpandedModEffectPreviewPanel = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(375 + 70, 0)).X; - modEffectPreviewPanel.Collapsed.Value = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedModEffectPreviewPanel; + + bool screenIsntWideEnough = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedModEffectPreviewPanel; + + beatmapAttributesDisplay.Collapsed.Value = screenIsntWideEnough; + footerContentFlow.Direction = screenIsntWideEnough ? FillDirection.Vertical : FillDirection.Horizontal; + + int layout = screenIsntWideEnough ? -1 : 1; + if (footerContentFlow.GetLayoutPosition(beatmapAttributesDisplay) != layout) + footerContentFlow.SetLayoutPosition(beatmapAttributesDisplay, layout); } } From e241e41f2a692351d426db65b9e84b003e3f4d47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 17:55:21 +0900 Subject: [PATCH 69/90] Fix layout decision to account for new multiplier addition --- 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 fb28c41395..95104eb29e 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -355,9 +355,9 @@ namespace osu.Game.Overlays.Mods { float rightEdgeOfLastButton = footerButtonFlow.Last().ScreenSpaceDrawQuad.TopRight.X; - // this is cheating a bit; the 375 value is hardcoded based on how wide the expanded panel _generally_ is. + // this is cheating a bit; the 640 value is hardcoded based on how wide the expanded panel _generally_ is. // due to the transition applied, the raw screenspace quad of the panel cannot be used, as it will trigger an ugly feedback cycle of expanding and collapsing. - float projectedLeftEdgeOfExpandedModEffectPreviewPanel = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(375 + 70, 0)).X; + float projectedLeftEdgeOfExpandedModEffectPreviewPanel = footerButtonFlow.ToScreenSpace(footerButtonFlow.DrawSize - new Vector2(640, 0)).X; bool screenIsntWideEnough = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedModEffectPreviewPanel; From 21252c1c2381e169586458e61314f4b30d59f854 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 17:55:25 +0900 Subject: [PATCH 70/90] Fix animations --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 95104eb29e..a18ee65612 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -266,16 +266,16 @@ namespace osu.Game.Overlays.Mods }, Children = new Drawable[] { - beatmapAttributesDisplay = new BeatmapAttributesDisplay - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - BeatmapInfo = { Value = beatmap?.BeatmapInfo } - }, multiplierDisplay = new DifficultyMultiplierDisplay { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight + }, + beatmapAttributesDisplay = new BeatmapAttributesDisplay + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + BeatmapInfo = { Value = beatmap?.BeatmapInfo } }, } }); @@ -350,8 +350,7 @@ namespace osu.Game.Overlays.Mods SearchTextBox.PlaceholderText = SearchTextBox.HasFocus ? Resources.Localisation.Web.CommonStrings.InputSearch : ModSelectOverlayStrings.TabToSearch; - // only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be. - if (beatmapAttributesDisplay != null && Alpha == 1) + if (beatmapAttributesDisplay != null) { float rightEdgeOfLastButton = footerButtonFlow.Last().ScreenSpaceDrawQuad.TopRight.X; @@ -361,12 +360,13 @@ namespace osu.Game.Overlays.Mods bool screenIsntWideEnough = rightEdgeOfLastButton > projectedLeftEdgeOfExpandedModEffectPreviewPanel; - beatmapAttributesDisplay.Collapsed.Value = screenIsntWideEnough; - footerContentFlow.Direction = screenIsntWideEnough ? FillDirection.Vertical : FillDirection.Horizontal; + // only update preview panel's collapsed state after we are fully visible, to ensure all the buttons are where we expect them to be. + if (Alpha == 1) + beatmapAttributesDisplay.Collapsed.Value = screenIsntWideEnough; - int layout = screenIsntWideEnough ? -1 : 1; - if (footerContentFlow.GetLayoutPosition(beatmapAttributesDisplay) != layout) - footerContentFlow.SetLayoutPosition(beatmapAttributesDisplay, layout); + footerContentFlow.LayoutDuration = 200; + footerContentFlow.LayoutEasing = Easing.OutQuint; + footerContentFlow.Direction = screenIsntWideEnough ? FillDirection.Vertical : FillDirection.Horizontal; } } From 923c2a16edb66b7af3f3f3cc1d7c228290cd9ae7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 18:11:16 +0900 Subject: [PATCH 71/90] Fix weird centering of multiplier "x" --- .../Overlays/Mods/DifficultyMultiplierDisplay.cs | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 7f92a4e644..a2d3e4b385 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -1,10 +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.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osuTK; using osu.Game.Localisation; namespace osu.Game.Overlays.Mods @@ -13,21 +10,12 @@ namespace osu.Game.Overlays.Mods { protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; - protected override string CounterFormat => @"N2"; + protected override string CounterFormat => @"0.0x"; public DifficultyMultiplierDisplay() { Current.Default = 1d; Current.Value = 1d; - - Add(new SpriteIcon - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Icon = FontAwesome.Solid.Times, - Size = new Vector2(7), - Margin = new MarginPadding { Top = 1 } - }); } protected override void LoadComplete() From 41b0619533f15f7aac4c5899acdc3d71c4b3afb4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 18:19:42 +0900 Subject: [PATCH 72/90] Fix shear being included on wrong side of `ModCounterDisplay` --- osu.Game/Overlays/Mods/ModCounterDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModCounterDisplay.cs b/osu.Game/Overlays/Mods/ModCounterDisplay.cs index de8f4433f4..2b6d1953ee 100644 --- a/osu.Game/Overlays/Mods/ModCounterDisplay.cs +++ b/osu.Game/Overlays/Mods/ModCounterDisplay.cs @@ -67,6 +67,8 @@ namespace osu.Game.Overlays.Mods Masking = true, CornerRadius = ModSelectPanel.CORNER_RADIUS, Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, Children = new Drawable[] { contentBackground = new Box From 491d94c5ac0d65d8d275f2622b7ab0f04e27b624 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 18:51:48 +0900 Subject: [PATCH 73/90] Use local shear definition --- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 2aea53090a..770e792736 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -72,6 +72,7 @@ namespace osu.Game.Overlays.Mods const float shear = ShearedOverlayContainer.SHEAR; AutoSizeAxes = Axes.Both; + InternalChild = content = new Container { Origin = Anchor.BottomRight, @@ -145,22 +146,10 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Horizontal, Children = new[] { - circleSizeDisplay = new VerticalAttributeDisplay("CS") - { - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - }, - drainRateDisplay = new VerticalAttributeDisplay("HP") - { - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - }, - approachRateDisplay = new VerticalAttributeDisplay("AR") - { - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - }, - overallDifficultyDisplay = new VerticalAttributeDisplay("OD") - { - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - }, + circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), }, + drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), }, + approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), }, + overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), }, } } } From 35840bf6719923053584d442594c0ba65737f40e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 18:51:31 +0900 Subject: [PATCH 74/90] Combine `ModCounterDisplay` and `DifficultyMultiplierDisplay` into one class --- .../TestSceneModsEffectDisplay.cs | 68 ------ .../Mods/DifficultyMultiplierDisplay.cs | 216 ++++++++++++++++- osu.Game/Overlays/Mods/ModCounterDisplay.cs | 222 ------------------ osu.Game/Overlays/Mods/ModSelectOverlay.cs | 4 +- 4 files changed, 213 insertions(+), 297 deletions(-) delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs delete mode 100644 osu.Game/Overlays/Mods/ModCounterDisplay.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs deleted file mode 100644 index bf6a718167..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModsEffectDisplay.cs +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Linq; -using NUnit.Framework; -using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Localisation; -using osu.Framework.Testing; -using osu.Game.Graphics; -using osu.Game.Overlays; -using osu.Game.Overlays.Mods; -using osu.Game.Rulesets.Mods; -using osuTK.Graphics; - -namespace osu.Game.Tests.Visual.UserInterface -{ - [TestFixture] - public partial class TestSceneModsEffectDisplay : OsuTestScene - { - [Cached] - private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); - - [Resolved] - private OsuColour colours { get; set; } = null!; - - [Test] - public void TestModsEffectDisplay() - { - TestDisplay testDisplay = null!; - Box background = null!; - - AddStep("add display", () => - { - Add(testDisplay = new TestDisplay - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre - }); - var boxes = testDisplay.ChildrenOfType(); - background = boxes.First(); - }); - - AddStep("set value to default", () => testDisplay.Current.Value = 50); - AddUntilStep("colours are correct", () => testDisplay.Container.Colour == Color4.White && background.Colour == colourProvider.Background3); - - AddStep("set value to less", () => testDisplay.Current.Value = 40); - AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyReduction)); - - AddStep("set value to bigger", () => testDisplay.Current.Value = 60); - AddUntilStep("colours are correct", () => testDisplay.Container.Colour == colourProvider.Background5 && background.Colour == colours.ForModType(ModType.DifficultyIncrease)); - } - - private partial class TestDisplay : ModCounterDisplay - { - public Container Container => Content; - - protected override LocalisableString Label => "Test display"; - - public TestDisplay() - { - Current.Default = 50; - } - } - } -} diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index a2d3e4b385..3c14e3aa60 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -1,30 +1,236 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// 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.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; +using osu.Game.Rulesets.Mods; +using osuTK; namespace osu.Game.Overlays.Mods { - public sealed partial class DifficultyMultiplierDisplay : ModCounterDisplay + /// + /// Base class for displays of singular counters. Not to be confused with which aggregates multiple attributes. + /// + public partial class DifficultyMultiplierDisplay : Container, IHasCurrentValue { - protected override LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; + public const float HEIGHT = 42; + private const float transition_duration = 200; - protected override string CounterFormat => @"0.0x"; + private readonly Box background; + private readonly Box labelBackground; + private readonly FillFlowContainer content; + + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private readonly BindableWithCurrent current = new BindableWithCurrent(); + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + /// + /// Text to display in the left area of the display. + /// + protected LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; + + protected virtual string CounterFormat => @"0.0x"; + + protected override Container Content => content; + + protected readonly RollingCounter Counter; + + private readonly InputBlockingContainer topContent; public DifficultyMultiplierDisplay() { Current.Default = 1d; Current.Value = 1d; + + const float shear = ShearedOverlayContainer.SHEAR; + + AutoSizeAxes = Axes.Both; + + InternalChild = topContent = new InputBlockingContainer + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + AutoSizeAxes = Axes.X, + Height = ShearedButton.HEIGHT, + Shear = new Vector2(shear, 0), + CornerRadius = ShearedButton.CORNER_RADIUS, + BorderThickness = ShearedButton.BORDER_THICKNESS, + Masking = true, + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + }, + new GridContainer + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.Absolute, 56) + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Masking = true, + CornerRadius = ModSelectPanel.CORNER_RADIUS, + Children = new Drawable[] + { + labelBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Horizontal = 18 }, + Shear = new Vector2(-shear, 0), + Text = Label, + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + } + } + }, + content = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Horizontal, + Shear = new Vector2(-shear, 0), + Spacing = new Vector2(2, 0), + Child = Counter = new EffectCounter(CounterFormat) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Current = { BindTarget = Current } + } + } + } + } + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + background.Colour = colourProvider.Background4; + topContent.BorderColour = ColourInfo.GradientVertical(background.Colour, colourProvider.Background1); + + labelBackground.Colour = colourProvider.Background4; } protected override void LoadComplete() { - base.LoadComplete(); + Current.BindValueChanged(e => + { + var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); + setColours(effect); + }, true); // required to prevent the counter initially rolling up from 0 to 1 // due to `Current.Value` having a nonstandard default value of 1. Counter.SetCountWithoutRolling(Current.Value); } + + /// + /// Fades colours of text and its background according to displayed value. + /// + /// Effect of the value. + private void setColours(ModEffect effect) + { + switch (effect) + { + case ModEffect.NotChanged: + background.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); + content.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + break; + + case ModEffect.DifficultyReduction: + background.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); + content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + break; + + case ModEffect.DifficultyIncrease: + background.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); + content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(effect)); + } + } + + /// + /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. + /// + /// Value to convert. Will arrive from comparison between bindable once it changes and it's . + /// Effect of the value. + protected virtual ModEffect CalculateEffectForComparison(int comparison) + { + if (comparison == 0) + return ModEffect.NotChanged; + if (comparison < 0) + return ModEffect.DifficultyReduction; + + return ModEffect.DifficultyIncrease; + } + + protected enum ModEffect + { + NotChanged, + DifficultyReduction, + DifficultyIncrease + } + + private partial class EffectCounter : RollingCounter + { + private readonly string? format; + + public EffectCounter(string? format) + { + this.format = format; + } + + protected override double RollingDuration => 500; + + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(format); + + protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText + { + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + }; + } } } diff --git a/osu.Game/Overlays/Mods/ModCounterDisplay.cs b/osu.Game/Overlays/Mods/ModCounterDisplay.cs deleted file mode 100644 index 2b6d1953ee..0000000000 --- a/osu.Game/Overlays/Mods/ModCounterDisplay.cs +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets.Mods; -using osuTK; - -namespace osu.Game.Overlays.Mods -{ - /// - /// Base class for displays of singular counters. Not to be confused with which aggregates multiple attributes. - /// - public abstract partial class ModCounterDisplay : Container, IHasCurrentValue - { - public const float HEIGHT = 42; - private const float transition_duration = 200; - - private readonly Box contentBackground; - private readonly Box labelBackground; - private readonly FillFlowContainer content; - - public Bindable Current - { - get => current.Current; - set => current.Current = value; - } - - private readonly BindableWithCurrent current = new BindableWithCurrent(); - - [Resolved] - private OsuColour colours { get; set; } = null!; - - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - - /// - /// Text to display in the left area of the display. - /// - protected abstract LocalisableString Label { get; } - - protected virtual string CounterFormat => @"N0"; - - protected override Container Content => content; - - protected readonly RollingCounter Counter; - - protected ModCounterDisplay() - { - Height = ShearedButton.HEIGHT; - AutoSizeAxes = Axes.X; - - InternalChild = new InputBlockingContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), - Anchor = Anchor.BottomRight, - Origin = Anchor.BottomRight, - Children = new Drawable[] - { - contentBackground = new Box - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 56) - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Children = new Drawable[] - { - labelBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Horizontal = 18 }, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Text = Label, - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - } - } - }, - content = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Spacing = new Vector2(2, 0), - Child = Counter = new EffectCounter(CounterFormat) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = { BindTarget = Current } - } - } - } - } - } - } - }; - } - - [BackgroundDependencyLoader] - private void load() - { - labelBackground.Colour = colourProvider.Background4; - } - - protected override void LoadComplete() - { - Current.BindValueChanged(e => - { - var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); - setColours(effect); - }, true); - } - - /// - /// Fades colours of text and its background according to displayed value. - /// - /// Effect of the value. - private void setColours(ModEffect effect) - { - switch (effect) - { - case ModEffect.NotChanged: - contentBackground.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); - content.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); - break; - - case ModEffect.DifficultyReduction: - contentBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); - content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - break; - - case ModEffect.DifficultyIncrease: - contentBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); - content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - break; - - default: - throw new ArgumentOutOfRangeException(nameof(effect)); - } - } - - /// - /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. - /// - /// Value to convert. Will arrive from comparison between bindable once it changes and it's . - /// Effect of the value. - protected virtual ModEffect CalculateEffectForComparison(int comparison) - { - if (comparison == 0) - return ModEffect.NotChanged; - if (comparison < 0) - return ModEffect.DifficultyReduction; - - return ModEffect.DifficultyIncrease; - } - - protected enum ModEffect - { - NotChanged, - DifficultyReduction, - DifficultyIncrease - } - - private partial class EffectCounter : RollingCounter - { - private readonly string? format; - - public EffectCounter(string? format) - { - this.format = format; - } - - protected override double RollingDuration => 500; - - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(format); - - protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText - { - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - }; - } - } -} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index a18ee65612..7e3082652a 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -182,7 +182,7 @@ namespace osu.Game.Overlays.Mods aboveColumnsContent = new Container { RelativeSizeAxes = Axes.X, - Height = ModCounterDisplay.HEIGHT, + Height = DifficultyMultiplierDisplay.HEIGHT, Padding = new MarginPadding { Horizontal = 100 }, Child = SearchTextBox = new ShearedSearchTextBox { @@ -197,7 +197,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = ModCounterDisplay.HEIGHT + PADDING, + Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, From 9a7d1ed98bb5ab10f41a84403876854d45980f48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 18:55:17 +0900 Subject: [PATCH 75/90] Tidy up `DifficultyMultiplierDisplay` --- .../Mods/DifficultyMultiplierDisplay.cs | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 3c14e3aa60..7d0f9f1b31 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -21,7 +21,7 @@ using osuTK; namespace osu.Game.Overlays.Mods { /// - /// Base class for displays of singular counters. Not to be confused with which aggregates multiple attributes. + /// On the mod select overlay, this provides a local updating view of the aggregate score multiplier coming from mods. /// public partial class DifficultyMultiplierDisplay : Container, IHasCurrentValue { @@ -46,16 +46,7 @@ namespace osu.Game.Overlays.Mods [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - /// - /// Text to display in the left area of the display. - /// - protected LocalisableString Label => DifficultyMultiplierDisplayStrings.DifficultyMultiplier; - - protected virtual string CounterFormat => @"0.0x"; - - protected override Container Content => content; - - protected readonly RollingCounter Counter; + private readonly RollingCounter counter; private readonly InputBlockingContainer topContent; @@ -115,7 +106,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.Centre, Margin = new MarginPadding { Horizontal = 18 }, Shear = new Vector2(-shear, 0), - Text = Label, + Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } } @@ -128,7 +119,7 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Horizontal, Shear = new Vector2(-shear, 0), Spacing = new Vector2(2, 0), - Child = Counter = new EffectCounter(CounterFormat) + Child = counter = new EffectCounter(@"0.0x") { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -155,13 +146,13 @@ namespace osu.Game.Overlays.Mods { Current.BindValueChanged(e => { - var effect = CalculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); + var effect = calculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); setColours(effect); }, true); // required to prevent the counter initially rolling up from 0 to 1 // due to `Current.Value` having a nonstandard default value of 1. - Counter.SetCountWithoutRolling(Current.Value); + counter.SetCountWithoutRolling(Current.Value); } /// @@ -197,7 +188,7 @@ namespace osu.Game.Overlays.Mods /// /// Value to convert. Will arrive from comparison between bindable once it changes and it's . /// Effect of the value. - protected virtual ModEffect CalculateEffectForComparison(int comparison) + private static ModEffect calculateEffectForComparison(int comparison) { if (comparison == 0) return ModEffect.NotChanged; From 46d5aa59bfa4e7ae6f28eaafe9dd195f7227116d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 19:33:30 +0900 Subject: [PATCH 76/90] Split base design out of `BeatmapAttributesDisplay` --- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 175 ++++-------------- .../Mods/ModFooterInformationDisplay.cs | 153 +++++++++++++++ 2 files changed, 184 insertions(+), 144 deletions(-) create mode 100644 osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 770e792736..8828e017fd 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -7,9 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; @@ -18,9 +15,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osuTK; -using osuTK.Graphics; using System.Threading; -using osu.Framework.Input.Events; using osu.Game.Configuration; namespace osu.Game.Overlays.Mods @@ -29,143 +24,69 @@ namespace osu.Game.Overlays.Mods /// On the mod select overlay, this provides a local updating view of BPM, star rating and other /// difficulty attributes so the user can have a better insight into what mods are changing. /// - public partial class BeatmapAttributesDisplay : CompositeDrawable + public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay { - private Container content = null!; - private Container innerContent = null!; - - private Box background = null!; - private Box innerBackground = null!; - private StarRatingDisplay starRatingDisplay = null!; private BPMDisplay bpmDisplay = null!; - private FillFlowContainer outerContent = null!; private VerticalAttributeDisplay circleSizeDisplay = null!; private VerticalAttributeDisplay drainRateDisplay = null!; private VerticalAttributeDisplay approachRateDisplay = null!; private VerticalAttributeDisplay overallDifficultyDisplay = null!; - private const float transition_duration = 250; - public Bindable BeatmapInfo { get; } = new Bindable(); - public BindableBool Collapsed { get; } = new BindableBool(true); - [Resolved] private Bindable> mods { get; set; } = null!; private ModSettingChangeTracker? modSettingChangeTracker; - [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; - [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } = null!; private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; + public BeatmapAttributesDisplay() + { + Collapsed.Value = true; + } + [BackgroundDependencyLoader] private void load() { const float shear = ShearedOverlayContainer.SHEAR; - AutoSizeAxes = Axes.Both; - - InternalChild = content = new Container + LeftContent.AddRange(new Drawable[] { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - AutoSizeAxes = Axes.X, - Height = ShearedButton.HEIGHT, - Shear = new Vector2(shear, 0), - CornerRadius = ShearedButton.CORNER_RADIUS, - BorderThickness = ShearedButton.BORDER_THICKNESS, - Masking = true, - Children = new Drawable[] + starRatingDisplay = new StarRatingDisplay(default, animated: true) { - background = new Box - { - RelativeSizeAxes = Axes.Both - }, - new FillFlowContainer // divide inner and outer content - { - Origin = Anchor.BottomLeft, - Anchor = Anchor.BottomLeft, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new Drawable[] - { - innerContent = new Container - { - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - BorderThickness = ShearedButton.BORDER_THICKNESS, - CornerRadius = ShearedButton.CORNER_RADIUS, - Masking = true, - Children = new Drawable[] - { - innerBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - new Container // actual inner content - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Width = 140, - RelativeSizeAxes = Axes.Y, - Margin = new MarginPadding { Horizontal = 15 }, - Children = new Drawable[] - { - starRatingDisplay = new StarRatingDisplay(default, animated: true) - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - Shear = new Vector2(-shear, 0), - }, - bpmDisplay = new BPMDisplay - { - Origin = Anchor.CentreRight, - Anchor = Anchor.CentreRight, - Shear = new Vector2(-shear, 0), - } - } - } - } - }, - outerContent = new FillFlowContainer - { - Alpha = 0, - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - AutoSizeAxes = Axes.X, - RelativeSizeAxes = Axes.Y, - Direction = FillDirection.Horizontal, - Children = new[] - { - circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), }, - drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), }, - approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), }, - overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), }, - } - } - } - } + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Shear = new Vector2(-shear, 0), + }, + bpmDisplay = new BPMDisplay + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Shear = new Vector2(-shear, 0), + AutoSizeAxes = Axes.Y, + Width = 75, } - }; + }); + + RightContent.AddRange(new Drawable[] + { + circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), }, + drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), }, + approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), }, + overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), }, + }); } protected override void LoadComplete() { - background.Colour = colourProvider.Background4; - innerBackground.Colour = colourProvider.Background3; - Color4 glowColour = colourProvider.Background1; - - content.BorderColour = ColourInfo.GradientVertical(background.Colour, glowColour); - innerContent.BorderColour = ColourInfo.GradientVertical(innerBackground.Colour, glowColour); + base.LoadComplete(); mods.BindValueChanged(_ => { @@ -176,40 +97,9 @@ namespace osu.Game.Overlays.Mods updateValues(); }, true); - Collapsed.BindValueChanged(_ => - { - // Only start autosize animations on first collapse toggle. This avoids an ugly initial presentation. - startAnimating(); - - updateCollapsedState(); - }); - BeatmapInfo.BindValueChanged(_ => updateValues(), true); } - protected override bool OnHover(HoverEvent e) - { - startAnimating(); - updateCollapsedState(); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - updateCollapsedState(); - base.OnHoverLost(e); - } - - protected override bool OnMouseDown(MouseDownEvent e) => true; - - protected override bool OnClick(ClickEvent e) => true; - - private void startAnimating() - { - content.AutoSizeEasing = Easing.OutQuint; - content.AutoSizeDuration = transition_duration; - } - private void updateValues() => Scheduler.AddOnce(() => { if (BeatmapInfo.Value == null) @@ -242,11 +132,6 @@ namespace osu.Game.Overlays.Mods overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; }); - private void updateCollapsedState() - { - outerContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); - } - private partial class BPMDisplay : RollingCounter { protected override double RollingDuration => 500; @@ -255,6 +140,8 @@ namespace osu.Game.Overlays.Mods protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, Font = OsuFont.Default.With(size: 20, weight: FontWeight.SemiBold), UseFullGlyphHeight = false, }; diff --git a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs new file mode 100644 index 0000000000..ca9c13e75f --- /dev/null +++ b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs @@ -0,0 +1,153 @@ +// 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.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; +using osu.Game.Graphics.UserInterface; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Overlays.Mods +{ + public abstract partial class ModFooterInformationDisplay : CompositeDrawable + { + protected FillFlowContainer LeftContent { get; private set; } = null!; + protected FillFlowContainer RightContent { get; private set; } = null!; + protected Container Content { get; private set; } = null!; + + public BindableBool Collapsed { get; } = new BindableBool(); + + private Container innerContent = null!; + + protected Box MainBackground { get; private set; } = null!; + protected Box FrontBackground { get; private set; } = null!; + + [Resolved] + private OverlayColourProvider colourProvider { get; set; } = null!; + + private const float transition_duration = 250; + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + InternalChild = Content = new Container + { + Origin = Anchor.BottomRight, + Anchor = Anchor.BottomRight, + AutoSizeAxes = Axes.X, + Height = ShearedButton.HEIGHT, + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + CornerRadius = ShearedButton.CORNER_RADIUS, + BorderThickness = ShearedButton.BORDER_THICKNESS, + Masking = true, + Children = new Drawable[] + { + MainBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + new FillFlowContainer // divide inner and outer content + { + Origin = Anchor.BottomLeft, + Anchor = Anchor.BottomLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Children = new Drawable[] + { + innerContent = new Container + { + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + BorderThickness = ShearedButton.BORDER_THICKNESS, + CornerRadius = ShearedButton.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + FrontBackground = new Box + { + RelativeSizeAxes = Axes.Both + }, + LeftContent = new FillFlowContainer // actual inner content + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Margin = new MarginPadding { Horizontal = 15 }, + Spacing = new Vector2(10), + } + } + }, + RightContent = new FillFlowContainer + { + Alpha = 0, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + AutoSizeAxes = Axes.X, + RelativeSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + } + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + MainBackground.Colour = colourProvider.Background4; + FrontBackground.Colour = colourProvider.Background3; + Color4 glowColour = colourProvider.Background1; + + Content.BorderColour = ColourInfo.GradientVertical(MainBackground.Colour, glowColour); + innerContent.BorderColour = ColourInfo.GradientVertical(FrontBackground.Colour, glowColour); + + Collapsed.BindValueChanged(_ => + { + // Only start autosize animations on first collapse toggle. This avoids an ugly initial presentation. + startAnimating(); + updateCollapsedState(); + }); + + updateCollapsedState(); + } + + protected override bool OnHover(HoverEvent e) + { + startAnimating(); + updateCollapsedState(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateCollapsedState(); + base.OnHoverLost(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; + + private void startAnimating() + { + Content.AutoSizeEasing = Easing.OutQuint; + Content.AutoSizeDuration = transition_duration; + } + + private void updateCollapsedState() + { + RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); + } + } +} From 824a3e4adc5eee76168dde693205067ce8632f61 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 19:33:39 +0900 Subject: [PATCH 77/90] Update `DifficultyMultiplierDisplay` to use new shared design --- .../Mods/DifficultyMultiplierDisplay.cs | 144 +++++------------- 1 file changed, 37 insertions(+), 107 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 7d0f9f1b31..98dc3fa62e 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -6,9 +6,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Colour; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; @@ -23,14 +20,9 @@ namespace osu.Game.Overlays.Mods /// /// On the mod select overlay, this provides a local updating view of the aggregate score multiplier coming from mods. /// - public partial class DifficultyMultiplierDisplay : Container, IHasCurrentValue + public partial class DifficultyMultiplierDisplay : ModFooterInformationDisplay, IHasCurrentValue { public const float HEIGHT = 42; - private const float transition_duration = 200; - - private readonly Box background; - private readonly Box labelBackground; - private readonly FillFlowContainer content; public Bindable Current { @@ -40,110 +32,53 @@ namespace osu.Game.Overlays.Mods private readonly BindableWithCurrent current = new BindableWithCurrent(); + private const float transition_duration = 200; + + private RollingCounter counter = null!; + [Resolved] private OsuColour colours { get; set; } = null!; [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - private readonly RollingCounter counter; - - private readonly InputBlockingContainer topContent; - public DifficultyMultiplierDisplay() { Current.Default = 1d; Current.Value = 1d; - - const float shear = ShearedOverlayContainer.SHEAR; - - AutoSizeAxes = Axes.Both; - - InternalChild = topContent = new InputBlockingContainer - { - Origin = Anchor.BottomRight, - Anchor = Anchor.BottomRight, - AutoSizeAxes = Axes.X, - Height = ShearedButton.HEIGHT, - Shear = new Vector2(shear, 0), - CornerRadius = ShearedButton.CORNER_RADIUS, - BorderThickness = ShearedButton.BORDER_THICKNESS, - Masking = true, - Children = new Drawable[] - { - background = new Box - { - RelativeSizeAxes = Axes.Both, - }, - new GridContainer - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - ColumnDimensions = new[] - { - new Dimension(GridSizeMode.AutoSize), - new Dimension(GridSizeMode.Absolute, 56) - }, - Content = new[] - { - new Drawable[] - { - new Container - { - RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, - Masking = true, - CornerRadius = ModSelectPanel.CORNER_RADIUS, - Children = new Drawable[] - { - labelBackground = new Box - { - RelativeSizeAxes = Axes.Both - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Horizontal = 18 }, - Shear = new Vector2(-shear, 0), - Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, - Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) - } - } - }, - content = new FillFlowContainer - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Horizontal, - Shear = new Vector2(-shear, 0), - Spacing = new Vector2(2, 0), - Child = counter = new EffectCounter(@"0.0x") - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Current = { BindTarget = Current } - } - } - } - } - } - } - }; } [BackgroundDependencyLoader] private void load() { - background.Colour = colourProvider.Background4; - topContent.BorderColour = ColourInfo.GradientVertical(background.Colour, colourProvider.Background1); + LeftContent.AddRange(new Drawable[] + { + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, + Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) + } + }); - labelBackground.Colour = colourProvider.Background4; + RightContent.Add(counter = new EffectCounter + { + Margin = new MarginPadding(10), + AutoSizeAxes = Axes.Y, + Width = 40, + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = Current } + }); } protected override void LoadComplete() { + base.LoadComplete(); + Current.BindValueChanged(e => { var effect = calculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); @@ -164,18 +99,18 @@ namespace osu.Game.Overlays.Mods switch (effect) { case ModEffect.NotChanged: - background.FadeColour(colourProvider.Background3, transition_duration, Easing.OutQuint); - content.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + MainBackground.FadeColour(colourProvider.Background4, transition_duration, Easing.OutQuint); + counter.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); break; case ModEffect.DifficultyReduction: - background.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); - content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + MainBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); + counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); break; case ModEffect.DifficultyIncrease: - background.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); - content.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + MainBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); + counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); break; default: @@ -207,19 +142,14 @@ namespace osu.Game.Overlays.Mods private partial class EffectCounter : RollingCounter { - private readonly string? format; - - public EffectCounter(string? format) - { - this.format = format; - } - protected override double RollingDuration => 500; - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(format); + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"0.00x"); protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) }; } From 3e1388c73f9230c19fb258ea1a750d5a7223b92f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 19:57:48 +0900 Subject: [PATCH 78/90] Add vertical animation when difficulty multiplier is changed --- .../Mods/DifficultyMultiplierDisplay.cs | 95 ++++++++----------- 1 file changed, 38 insertions(+), 57 deletions(-) diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs index 98dc3fa62e..879df73861 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; @@ -14,6 +15,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; using osuTK; +using osuTK.Graphics; namespace osu.Game.Overlays.Mods { @@ -63,15 +65,20 @@ namespace osu.Game.Overlays.Mods } }); - RightContent.Add(counter = new EffectCounter + RightContent.Add(new Container { - Margin = new MarginPadding(10), - AutoSizeAxes = Axes.Y, Width = 40, - Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + RelativeSizeAxes = Axes.Y, + Margin = new MarginPadding(10), Anchor = Anchor.Centre, Origin = Anchor.Centre, - Current = { BindTarget = Current } + Child = counter = new EffectCounter + { + Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Current = { BindTarget = Current } + } }); } @@ -81,8 +88,32 @@ namespace osu.Game.Overlays.Mods Current.BindValueChanged(e => { - var effect = calculateEffectForComparison(e.NewValue.CompareTo(Current.Default)); - setColours(effect); + if (e.NewValue > Current.Default) + { + MainBackground + .FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); + counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + } + else if (e.NewValue < Current.Default) + { + MainBackground + .FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); + counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + } + else + { + MainBackground.FadeColour(colourProvider.Background4, transition_duration, Easing.OutQuint); + counter.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); + } + + if (e.NewValue != Current.Default) + MainBackground.FlashColour(Color4.White, 500, Easing.OutQuint); + + const float move_amount = 4; + if (e.NewValue > e.OldValue) + counter.MoveToY(Math.Max(-move_amount * 2, counter.Y - move_amount)).Then().MoveToY(0, transition_duration * 2, Easing.OutQuint); + else + counter.MoveToY(Math.Min(move_amount * 2, counter.Y + move_amount)).Then().MoveToY(0, transition_duration * 2, Easing.OutQuint); }, true); // required to prevent the counter initially rolling up from 0 to 1 @@ -90,56 +121,6 @@ namespace osu.Game.Overlays.Mods counter.SetCountWithoutRolling(Current.Value); } - /// - /// Fades colours of text and its background according to displayed value. - /// - /// Effect of the value. - private void setColours(ModEffect effect) - { - switch (effect) - { - case ModEffect.NotChanged: - MainBackground.FadeColour(colourProvider.Background4, transition_duration, Easing.OutQuint); - counter.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); - break; - - case ModEffect.DifficultyReduction: - MainBackground.FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); - counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - break; - - case ModEffect.DifficultyIncrease: - MainBackground.FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); - counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); - break; - - default: - throw new ArgumentOutOfRangeException(nameof(effect)); - } - } - - /// - /// Converts signed integer into . Negative values are counted as difficulty reduction, positive as increase. - /// - /// Value to convert. Will arrive from comparison between bindable once it changes and it's . - /// Effect of the value. - private static ModEffect calculateEffectForComparison(int comparison) - { - if (comparison == 0) - return ModEffect.NotChanged; - if (comparison < 0) - return ModEffect.DifficultyReduction; - - return ModEffect.DifficultyIncrease; - } - - protected enum ModEffect - { - NotChanged, - DifficultyReduction, - DifficultyIncrease - } - private partial class EffectCounter : RollingCounter { protected override double RollingDuration => 500; From 32946413de83efceb75b51571802536e88dc708f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Sep 2023 20:00:06 +0900 Subject: [PATCH 79/90] Change display text from "difficulty" to "score" multiplier --- .../TestSceneModSelectOverlay.cs | 8 ++++---- ....cs => TestSceneScoreMultiplierDisplay.cs} | 13 ++++++------- .../DifficultyMultiplierDisplayStrings.cs | 19 ------------------- .../Localisation/ModSelectOverlayStrings.cs | 7 ++++++- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 ++++---- ...erDisplay.cs => ScoreMultiplierDisplay.cs} | 6 +++--- 6 files changed, 23 insertions(+), 38 deletions(-) rename osu.Game.Tests/Visual/UserInterface/{TestSceneDifficultyMultiplierDisplay.cs => TestSceneScoreMultiplierDisplay.cs} (79%) delete mode 100644 osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs rename osu.Game/Overlays/Mods/{DifficultyMultiplierDisplay.cs => ScoreMultiplierDisplay.cs} (95%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 029a7f8b9e..3728fb3f21 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); @@ -130,7 +130,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("mod multiplier correct", () => { double multiplier = SelectedMods.Value.Aggregate(1d, (m, mod) => m * mod.ScoreMultiplier); - return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); + return Precision.AlmostEquals(multiplier, modSelectOverlay.ChildrenOfType().Single().Current.Value); }); assertCustomisationToggleState(disabled: false, active: false); AddAssert("setting items created", () => modSelectOverlay.ChildrenOfType().Any()); @@ -787,7 +787,7 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.MoveMouseTo(this.ChildrenOfType().Single(preset => preset.Preset.Value.Name == "Half Time 0.5x")); InputManager.Click(MouseButton.Left); }); - AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.5)); + AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.5)); // this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation, // it is instrumental in the reproduction of the failure scenario that this test is supposed to cover. @@ -796,7 +796,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick()); AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType().Single() .ChildrenOfType>().Single().TriggerClick()); - AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.7)); + AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.7)); } private void waitForColumnLoad() => AddUntilStep("all column content loaded", diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDifficultyMultiplierDisplay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneScoreMultiplierDisplay.cs similarity index 79% rename from osu.Game.Tests/Visual/UserInterface/TestSceneDifficultyMultiplierDisplay.cs rename to osu.Game.Tests/Visual/UserInterface/TestSceneScoreMultiplierDisplay.cs index 890c7295b4..c2ddd814b7 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDifficultyMultiplierDisplay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneScoreMultiplierDisplay.cs @@ -1,10 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -12,17 +11,17 @@ using osu.Game.Overlays.Mods; namespace osu.Game.Tests.Visual.UserInterface { [TestFixture] - public partial class TestSceneDifficultyMultiplierDisplay : OsuTestScene + public partial class TestSceneScoreMultiplierDisplay : OsuTestScene { [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); [Test] - public void TestDifficultyMultiplierDisplay() + public void TestBasic() { - DifficultyMultiplierDisplay multiplierDisplay = null; + ScoreMultiplierDisplay multiplierDisplay = null!; - AddStep("create content", () => Child = multiplierDisplay = new DifficultyMultiplierDisplay + AddStep("create content", () => Child = multiplierDisplay = new ScoreMultiplierDisplay { Anchor = Anchor.Centre, Origin = Anchor.Centre @@ -34,7 +33,7 @@ namespace osu.Game.Tests.Visual.UserInterface AddSliderStep("set multiplier", 0, 2, 1d, multiplier => { - if (multiplierDisplay != null) + if (multiplierDisplay.IsNotNull()) multiplierDisplay.Current.Value = multiplier; }); } diff --git a/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs b/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs deleted file mode 100644 index 952ca22678..0000000000 --- a/osu.Game/Localisation/DifficultyMultiplierDisplayStrings.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Localisation; - -namespace osu.Game.Localisation -{ - public static class DifficultyMultiplierDisplayStrings - { - private const string prefix = @"osu.Game.Resources.Localisation.DifficultyMultiplierDisplay"; - - /// - /// "Difficulty Multiplier" - /// - public static LocalisableString DifficultyMultiplier => new TranslatableString(getKey(@"difficulty_multiplier"), @"Difficulty Multiplier"); - - private static string getKey(string key) => $@"{prefix}:{key}"; - } -} diff --git a/osu.Game/Localisation/ModSelectOverlayStrings.cs b/osu.Game/Localisation/ModSelectOverlayStrings.cs index 05dcf138d7..86ebebd293 100644 --- a/osu.Game/Localisation/ModSelectOverlayStrings.cs +++ b/osu.Game/Localisation/ModSelectOverlayStrings.cs @@ -44,6 +44,11 @@ namespace osu.Game.Localisation /// public static LocalisableString TabToSearch => new TranslatableString(getKey(@"tab_to_search"), @"tab to search..."); + /// + /// "Score Multiplier" + /// + public static LocalisableString ScoreMultiplier => new TranslatableString(getKey(@"score_multiplier"), @"Score Multiplier"); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} \ No newline at end of file +} diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 7e3082652a..bdea8a126c 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Mods private DeselectAllModsButton deselectAllModsButton = null!; private Container aboveColumnsContent = null!; - private DifficultyMultiplierDisplay? multiplierDisplay; + private ScoreMultiplierDisplay? multiplierDisplay; private BeatmapAttributesDisplay? beatmapAttributesDisplay; protected ShearedButton BackButton { get; private set; } = null!; @@ -182,7 +182,7 @@ namespace osu.Game.Overlays.Mods aboveColumnsContent = new Container { RelativeSizeAxes = Axes.X, - Height = DifficultyMultiplierDisplay.HEIGHT, + Height = ScoreMultiplierDisplay.HEIGHT, Padding = new MarginPadding { Horizontal = 100 }, Child = SearchTextBox = new ShearedSearchTextBox { @@ -197,7 +197,7 @@ namespace osu.Game.Overlays.Mods { Padding = new MarginPadding { - Top = DifficultyMultiplierDisplay.HEIGHT + PADDING, + Top = ScoreMultiplierDisplay.HEIGHT + PADDING, Bottom = PADDING }, RelativeSizeAxes = Axes.Both, @@ -266,7 +266,7 @@ namespace osu.Game.Overlays.Mods }, Children = new Drawable[] { - multiplierDisplay = new DifficultyMultiplierDisplay + multiplierDisplay = new ScoreMultiplierDisplay { Anchor = Anchor.BottomRight, Origin = Anchor.BottomRight diff --git a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs similarity index 95% rename from osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs rename to osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs index 879df73861..2b075442e4 100644 --- a/osu.Game/Overlays/Mods/DifficultyMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs @@ -22,7 +22,7 @@ namespace osu.Game.Overlays.Mods /// /// On the mod select overlay, this provides a local updating view of the aggregate score multiplier coming from mods. /// - public partial class DifficultyMultiplierDisplay : ModFooterInformationDisplay, IHasCurrentValue + public partial class ScoreMultiplierDisplay : ModFooterInformationDisplay, IHasCurrentValue { public const float HEIGHT = 42; @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Mods [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; - public DifficultyMultiplierDisplay() + public ScoreMultiplierDisplay() { Current.Default = 1d; Current.Value = 1d; @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.Mods Anchor = Anchor.Centre, Origin = Anchor.Centre, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), - Text = DifficultyMultiplierDisplayStrings.DifficultyMultiplier, + Text = ModSelectOverlayStrings.ScoreMultiplier, Font = OsuFont.Default.With(size: 17, weight: FontWeight.SemiBold) } }); From 753c1c877c48527c9a5b4d3f5ecc6608581d0e89 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Sep 2023 23:10:13 +0900 Subject: [PATCH 80/90] Revert masking SSBO changes --- .../UI/Cursor/CursorTrail.cs | 20 +++++-------------- .../Resources/Shaders/sh_TestVertex.vs | 2 +- osu.Game/Screens/Loader.cs | 5 ++++- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index 0774d34488..a29faac5a0 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -286,7 +286,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor if (time - part.Time >= 1) continue; - vertexBatch.Add(new TexturedTrailVertex(renderer) + vertexBatch.Add(new TexturedTrailVertex { Position = new Vector2(part.Position.X - size.X * originPosition.X, part.Position.Y + size.Y * (1 - originPosition.Y)), TexturePosition = textureRect.BottomLeft, @@ -295,7 +295,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Time = part.Time }); - vertexBatch.Add(new TexturedTrailVertex(renderer) + vertexBatch.Add(new TexturedTrailVertex { Position = new Vector2(part.Position.X + size.X * (1 - originPosition.X), part.Position.Y + size.Y * (1 - originPosition.Y)), TexturePosition = textureRect.BottomRight, @@ -304,7 +304,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Time = part.Time }); - vertexBatch.Add(new TexturedTrailVertex(renderer) + vertexBatch.Add(new TexturedTrailVertex { Position = new Vector2(part.Position.X + size.X * (1 - originPosition.X), part.Position.Y - size.Y * originPosition.Y), TexturePosition = textureRect.TopRight, @@ -313,7 +313,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor Time = part.Time }); - vertexBatch.Add(new TexturedTrailVertex(renderer) + vertexBatch.Add(new TexturedTrailVertex { Position = new Vector2(part.Position.X - size.X * originPosition.X, part.Position.Y - size.Y * originPosition.Y), TexturePosition = textureRect.TopLeft, @@ -362,22 +362,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [VertexMember(1, VertexAttribPointerType.Float)] public float Time; - [VertexMember(1, VertexAttribPointerType.Int)] - private readonly int maskingIndex; - - public TexturedTrailVertex(IRenderer renderer) - { - this = default; - maskingIndex = renderer.CurrentMaskingIndex; - } - public bool Equals(TexturedTrailVertex other) { return Position.Equals(other.Position) && TexturePosition.Equals(other.TexturePosition) && Colour.Equals(other.Colour) - && Time.Equals(other.Time) - && maskingIndex == other.maskingIndex; + && Time.Equals(other.Time); } } } diff --git a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs index 80ed686ba5..505554bb33 100644 --- a/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs +++ b/osu.Game.Tests/Resources/Shaders/sh_TestVertex.vs @@ -13,7 +13,7 @@ layout(location = 4) out mediump vec2 v_BlendRange; void main(void) { // Transform from screen space to masking space. - highp vec3 maskingPos = g_MaskingInfo.ToMaskingSpace * vec3(m_Position, 1.0); + highp vec3 maskingPos = g_ToMaskingSpace * vec3(m_Position, 1.0); v_MaskingPosition = maskingPos.xy / maskingPos.z; v_Colour = m_Colour; diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 962c7d9d14..372cfe748e 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -126,9 +126,12 @@ namespace osu.Game.Screens private void load(ShaderManager manager) { loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.TEXTURE)); - loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2_NO_MASKING, FragmentShaderDescriptor.BLUR)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, FragmentShaderDescriptor.BLUR)); + loadTargets.Add(manager.Load(@"CursorTrail", FragmentShaderDescriptor.TEXTURE)); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_2, "TriangleBorder")); + loadTargets.Add(manager.Load(VertexShaderDescriptor.TEXTURE_3, FragmentShaderDescriptor.TEXTURE)); } From b5902a873688d547dd114be1ede034b946e825b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Sep 2023 13:29:29 +0900 Subject: [PATCH 81/90] Avoid `MemoryStream` overhead for incoming non-`MemoryStream` in `ImportTask` --- osu.Game/Database/ImportTask.cs | 38 ++++++++++++--------------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs index def20bc1fb..bf5b377a07 100644 --- a/osu.Game/Database/ImportTask.cs +++ b/osu.Game/Database/ImportTask.cs @@ -46,9 +46,19 @@ namespace osu.Game.Database /// public ArchiveReader GetReader() { - return Stream != null - ? getReaderFrom(Stream) - : getReaderFrom(Path); + if (Stream == null) + return getReaderFromPath(Path); + + if (Stream is MemoryStream memoryStream) + { + if (ZipUtils.IsZipArchive(memoryStream)) + return new ZipArchiveReader(memoryStream, Path); + + return new LegacyByteArrayReader(memoryStream.ToArray(), Path); + } + + // This isn't used in any current path. May need to reconsider for performance reasons (ie. if we don't expect the incoming stream to be copied out). + return new LegacyByteArrayReader(Stream.ReadAllBytesToArray(), Path); } /// @@ -60,32 +70,12 @@ namespace osu.Game.Database File.Delete(Path); } - /// - /// Creates an from a stream. - /// - /// A seekable stream containing the archive content. - /// A reader giving access to the archive's content. - private ArchiveReader getReaderFrom(Stream stream) - { - if (!(stream is MemoryStream memoryStream)) - { - // This isn't used in any current path. May need to reconsider for performance reasons (ie. if we don't expect the incoming stream to be copied out). - memoryStream = new MemoryStream(stream.ReadAllBytesToArray()); - stream.Dispose(); - } - - if (ZipUtils.IsZipArchive(memoryStream)) - return new ZipArchiveReader(memoryStream, Path); - - return new LegacyByteArrayReader(memoryStream.ToArray(), Path); - } - /// /// Creates an from a valid storage path. /// /// A file or folder path resolving the archive content. /// A reader giving access to the archive's content. - private ArchiveReader getReaderFrom(string path) + private ArchiveReader getReaderFromPath(string path) { if (ZipUtils.IsZipArchive(path)) return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), System.IO.Path.GetFileName(path)); From 0657b551964986fc5504a202eaa5e699d1c72f00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Sep 2023 13:33:25 +0900 Subject: [PATCH 82/90] Avoid `MemoryStream.ToArray` overhead in `LegacyByteArrayReader` --- osu.Game/Database/ImportTask.cs | 2 +- .../IO/Archives/MemoryStreamArchiveReader.cs | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 osu.Game/IO/Archives/MemoryStreamArchiveReader.cs diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs index bf5b377a07..962eab9fa5 100644 --- a/osu.Game/Database/ImportTask.cs +++ b/osu.Game/Database/ImportTask.cs @@ -54,7 +54,7 @@ namespace osu.Game.Database if (ZipUtils.IsZipArchive(memoryStream)) return new ZipArchiveReader(memoryStream, Path); - return new LegacyByteArrayReader(memoryStream.ToArray(), Path); + return new MemoryStreamArchiveReader(memoryStream, Path); } // This isn't used in any current path. May need to reconsider for performance reasons (ie. if we don't expect the incoming stream to be copied out). diff --git a/osu.Game/IO/Archives/MemoryStreamArchiveReader.cs b/osu.Game/IO/Archives/MemoryStreamArchiveReader.cs new file mode 100644 index 0000000000..37ce1e508e --- /dev/null +++ b/osu.Game/IO/Archives/MemoryStreamArchiveReader.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; + +namespace osu.Game.IO.Archives +{ + /// + /// Allows reading a single file from the provided memory stream. + /// + public class MemoryStreamArchiveReader : ArchiveReader + { + private readonly MemoryStream stream; + + public MemoryStreamArchiveReader(MemoryStream stream, string filename) + : base(filename) + { + this.stream = stream; + } + + public override Stream GetStream(string name) => new MemoryStream(stream.GetBuffer(), 0, (int)stream.Length); + + public override void Dispose() + { + } + + public override IEnumerable Filenames => new[] { Name }; + } +} From 541cd972e1e19ba641b1e3397bb1b073d53121bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Sep 2023 13:36:07 +0900 Subject: [PATCH 83/90] Rename `ArchiveReader` implementations to read better --- osu.Game/Beatmaps/BeatmapImporter.cs | 2 +- osu.Game/Database/ImportTask.cs | 6 +++--- .../{LegacyByteArrayReader.cs => ByteArrayArchiveReader.cs} | 4 ++-- ...yDirectoryArchiveReader.cs => DirectoryArchiveReader.cs} | 6 +++--- ...egacyFileArchiveReader.cs => SingleFileArchiveReader.cs} | 6 +++--- osu.Game/Screens/Play/Player.cs | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) rename osu.Game/IO/Archives/{LegacyByteArrayReader.cs => ByteArrayArchiveReader.cs} (84%) rename osu.Game/IO/Archives/{LegacyDirectoryArchiveReader.cs => DirectoryArchiveReader.cs} (85%) rename osu.Game/IO/Archives/{LegacyFileArchiveReader.cs => SingleFileArchiveReader.cs} (83%) diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 14719da1bc..b497b8e8dc 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -319,7 +319,7 @@ namespace osu.Game.Beatmaps { DateTimeOffset dateAdded = DateTimeOffset.UtcNow; - if (reader is LegacyDirectoryArchiveReader legacyReader) + if (reader is DirectoryArchiveReader legacyReader) { var beatmaps = reader.Filenames.Where(f => f.EndsWith(".osu", StringComparison.OrdinalIgnoreCase)); diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs index 962eab9fa5..cbc5c9005c 100644 --- a/osu.Game/Database/ImportTask.cs +++ b/osu.Game/Database/ImportTask.cs @@ -58,7 +58,7 @@ namespace osu.Game.Database } // This isn't used in any current path. May need to reconsider for performance reasons (ie. if we don't expect the incoming stream to be copied out). - return new LegacyByteArrayReader(Stream.ReadAllBytesToArray(), Path); + return new ByteArrayArchiveReader(Stream.ReadAllBytesToArray(), Path); } /// @@ -80,9 +80,9 @@ namespace osu.Game.Database if (ZipUtils.IsZipArchive(path)) return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), System.IO.Path.GetFileName(path)); if (Directory.Exists(path)) - return new LegacyDirectoryArchiveReader(path); + return new DirectoryArchiveReader(path); if (File.Exists(path)) - return new LegacyFileArchiveReader(path); + return new SingleFileArchiveReader(path); throw new InvalidFormatException($"{path} is not a valid archive"); } diff --git a/osu.Game/IO/Archives/LegacyByteArrayReader.cs b/osu.Game/IO/Archives/ByteArrayArchiveReader.cs similarity index 84% rename from osu.Game/IO/Archives/LegacyByteArrayReader.cs rename to osu.Game/IO/Archives/ByteArrayArchiveReader.cs index 06db02b335..0e2dee3456 100644 --- a/osu.Game/IO/Archives/LegacyByteArrayReader.cs +++ b/osu.Game/IO/Archives/ByteArrayArchiveReader.cs @@ -9,11 +9,11 @@ namespace osu.Game.IO.Archives /// /// Allows reading a single file from the provided byte array. /// - public class LegacyByteArrayReader : ArchiveReader + public class ByteArrayArchiveReader : ArchiveReader { private readonly byte[] content; - public LegacyByteArrayReader(byte[] content, string filename) + public ByteArrayArchiveReader(byte[] content, string filename) : base(filename) { this.content = content; diff --git a/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs b/osu.Game/IO/Archives/DirectoryArchiveReader.cs similarity index 85% rename from osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs rename to osu.Game/IO/Archives/DirectoryArchiveReader.cs index 1503705022..f2012b7b49 100644 --- a/osu.Game/IO/Archives/LegacyDirectoryArchiveReader.cs +++ b/osu.Game/IO/Archives/DirectoryArchiveReader.cs @@ -8,13 +8,13 @@ using System.Linq; namespace osu.Game.IO.Archives { /// - /// Reads an archive from a directory on disk. + /// Reads an archive directly from a directory on disk. /// - public class LegacyDirectoryArchiveReader : ArchiveReader + public class DirectoryArchiveReader : ArchiveReader { private readonly string path; - public LegacyDirectoryArchiveReader(string path) + public DirectoryArchiveReader(string path) : base(Path.GetFileName(path)) { // re-get full path to standardise with Directory.GetFiles return values below. diff --git a/osu.Game/IO/Archives/LegacyFileArchiveReader.cs b/osu.Game/IO/Archives/SingleFileArchiveReader.cs similarity index 83% rename from osu.Game/IO/Archives/LegacyFileArchiveReader.cs rename to osu.Game/IO/Archives/SingleFileArchiveReader.cs index c317cae5fc..79d9c5de71 100644 --- a/osu.Game/IO/Archives/LegacyFileArchiveReader.cs +++ b/osu.Game/IO/Archives/SingleFileArchiveReader.cs @@ -7,14 +7,14 @@ using System.IO; namespace osu.Game.IO.Archives { /// - /// Reads a file on disk as an archive. + /// Reads a single file on disk as an archive. /// Note: In this case, the file is not an extractable archive, use instead. /// - public class LegacyFileArchiveReader : ArchiveReader + public class SingleFileArchiveReader : ArchiveReader { private readonly string path; - public LegacyFileArchiveReader(string path) + public SingleFileArchiveReader(string path) : base(Path.GetFileName(path)) { // re-get full path to standardise diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8c5828fc92..60806a73c5 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1144,14 +1144,14 @@ namespace osu.Game.Screens.Play if (DrawableRuleset.ReplayScore != null) return Task.CompletedTask; - LegacyByteArrayReader replayReader = null; + ByteArrayArchiveReader replayReader = null; if (score.ScoreInfo.Ruleset.IsLegacyRuleset()) { using (var stream = new MemoryStream()) { new LegacyScoreEncoder(score, GameplayState.Beatmap).Encode(stream); - replayReader = new LegacyByteArrayReader(stream.ToArray(), "replay.osr"); + replayReader = new ByteArrayArchiveReader(stream.ToArray(), "replay.osr"); } } From 364094fcf29e1a2e8e9f9af9dbd2dab55b28b2d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Sep 2023 13:38:23 +0900 Subject: [PATCH 84/90] Inline all archive reader pathing --- osu.Game/Database/ImportTask.cs | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/osu.Game/Database/ImportTask.cs b/osu.Game/Database/ImportTask.cs index cbc5c9005c..6d432aac25 100644 --- a/osu.Game/Database/ImportTask.cs +++ b/osu.Game/Database/ImportTask.cs @@ -47,7 +47,16 @@ namespace osu.Game.Database public ArchiveReader GetReader() { if (Stream == null) - return getReaderFromPath(Path); + { + if (ZipUtils.IsZipArchive(Path)) + return new ZipArchiveReader(File.Open(Path, FileMode.Open, FileAccess.Read, FileShare.Read), System.IO.Path.GetFileName(Path)); + if (Directory.Exists(Path)) + return new DirectoryArchiveReader(Path); + if (File.Exists(Path)) + return new SingleFileArchiveReader(Path); + + throw new InvalidFormatException($"{Path} is not a valid archive"); + } if (Stream is MemoryStream memoryStream) { @@ -70,23 +79,6 @@ namespace osu.Game.Database File.Delete(Path); } - /// - /// Creates an from a valid storage path. - /// - /// A file or folder path resolving the archive content. - /// A reader giving access to the archive's content. - private ArchiveReader getReaderFromPath(string path) - { - if (ZipUtils.IsZipArchive(path)) - return new ZipArchiveReader(File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read), System.IO.Path.GetFileName(path)); - if (Directory.Exists(path)) - return new DirectoryArchiveReader(path); - if (File.Exists(path)) - return new SingleFileArchiveReader(path); - - throw new InvalidFormatException($"{path} is not a valid archive"); - } - public override string ToString() => System.IO.Path.GetFileName(Path); } } From 7bf0a22112bbd2d48bdef9f2f8d41dd804a5ad62 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Sep 2023 13:57:56 +0900 Subject: [PATCH 85/90] Improve flash effect --- .../Mods/ModFooterInformationDisplay.cs | 8 ++-- .../Overlays/Mods/ScoreMultiplierDisplay.cs | 40 ++++++++++++++----- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs index ca9c13e75f..f4a277eb63 100644 --- a/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs +++ b/osu.Game/Overlays/Mods/ModFooterInformationDisplay.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Mods protected Box FrontBackground { get; private set; } = null!; [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; + protected OverlayColourProvider ColourProvider { get; private set; } = null!; private const float transition_duration = 250; @@ -105,9 +105,9 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - MainBackground.Colour = colourProvider.Background4; - FrontBackground.Colour = colourProvider.Background3; - Color4 glowColour = colourProvider.Background1; + MainBackground.Colour = ColourProvider.Background4; + FrontBackground.Colour = ColourProvider.Background3; + Color4 glowColour = ColourProvider.Background1; Content.BorderColour = ColourInfo.GradientVertical(MainBackground.Colour, glowColour); innerContent.BorderColour = ColourInfo.GradientVertical(FrontBackground.Colour, glowColour); diff --git a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs index 2b075442e4..c758632392 100644 --- a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; using osu.Game.Graphics; @@ -15,7 +16,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Mods { @@ -38,11 +38,10 @@ namespace osu.Game.Overlays.Mods private RollingCounter counter = null!; - [Resolved] - private OsuColour colours { get; set; } = null!; + private Box flashLayer = null!; [Resolved] - private OverlayColourProvider colourProvider { get; set; } = null!; + private OsuColour colours { get; set; } = null!; public ScoreMultiplierDisplay() { @@ -53,6 +52,27 @@ namespace osu.Game.Overlays.Mods [BackgroundDependencyLoader] private void load() { + // You would think that we could add this to `Content`, but borders don't mix well + // with additive blending children elements. + AddInternal(new Container + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + RelativeSizeAxes = Axes.Both, + Shear = new Vector2(ShearedOverlayContainer.SHEAR, 0), + CornerRadius = ShearedButton.CORNER_RADIUS, + Masking = true, + Children = new Drawable[] + { + flashLayer = new Box + { + Alpha = 0, + Blending = BlendingParameters.Additive, + RelativeSizeAxes = Axes.Both, + } + } + }); + LeftContent.AddRange(new Drawable[] { new OsuSpriteText @@ -92,22 +112,24 @@ namespace osu.Game.Overlays.Mods { MainBackground .FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); - counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + counter.FadeColour(ColourProvider.Background5, transition_duration, Easing.OutQuint); } else if (e.NewValue < Current.Default) { MainBackground .FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); - counter.FadeColour(colourProvider.Background5, transition_duration, Easing.OutQuint); + counter.FadeColour(ColourProvider.Background5, transition_duration, Easing.OutQuint); } else { - MainBackground.FadeColour(colourProvider.Background4, transition_duration, Easing.OutQuint); + MainBackground.FadeColour(ColourProvider.Background4, transition_duration, Easing.OutQuint); counter.FadeColour(Colour4.White, transition_duration, Easing.OutQuint); } - if (e.NewValue != Current.Default) - MainBackground.FlashColour(Color4.White, 500, Easing.OutQuint); + flashLayer + .FadeOutFromOne() + .FadeTo(0.15f, 60, Easing.OutQuint) + .Then().FadeOut(500, Easing.OutQuint); const float move_amount = 4; if (e.NewValue > e.OldValue) From 68323671ec689160ffc4790e8fd84a573979c3ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Sep 2023 08:24:18 +0200 Subject: [PATCH 86/90] Update framework and resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 4 ++-- osu.iOS.props | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2bfdce5ab8..10cee77b09 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - +