From 579d5b51eb25e90e7bc28f284b28c5edc14fc249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 20:34:41 +0900 Subject: [PATCH 001/195] Add and consume sample bank constants --- .../TestSceneAutoJuiceStream.cs | 2 +- .../Editor/TestSceneSliderSplitting.cs | 4 +- .../Formats/LegacyBeatmapDecoderTest.cs | 14 +++--- ...estSceneHitObjectSamplePointAdjustments.cs | 45 ++++++++++--------- .../TestSceneGameplaySampleTriggerSource.cs | 4 +- osu.Game/Audio/HitSampleInfo.cs | 9 ++++ .../ControlPoints/SampleControlPoint.cs | 4 +- .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 3 +- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 +-- 9 files changed, 51 insertions(+), 40 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 202228c9e7..e47a687f24 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests NewCombo = i % 8 == 0, Samples = new List(new[] { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL, volume: 100) }) }); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index 015952c59a..d642d6a5ed 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { if (slider is null) return; - slider.SampleControlPoint.SampleBank = "soft"; + slider.SampleControlPoint.SampleBank = HitSampleInfo.BANK_SOFT; slider.SampleControlPoint.SampleVolume = 70; sample = new HitSampleInfo("hitwhistle"); slider.Samples.Add(sample); @@ -207,7 +207,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("sliders have hitsounds", hasHitsounds); bool hasHitsounds() => sample is not null && - EditorBeatmap.HitObjects.All(o => o.SampleControlPoint.SampleBank == "soft" && + EditorBeatmap.HitObjects.All(o => o.SampleControlPoint.SampleBank == HitSampleInfo.BANK_SOFT && o.SampleControlPoint.SampleVolume == 70 && o.Samples.Contains(sample)); } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index fdd0167ed3..d9817802f3 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -206,17 +206,17 @@ namespace osu.Game.Tests.Beatmaps.Formats var soundPoint = controlPoints.SamplePointAt(0); Assert.AreEqual(956, soundPoint.Time); - Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(HitSampleInfo.BANK_SOFT, soundPoint.SampleBank); Assert.AreEqual(60, soundPoint.SampleVolume); soundPoint = controlPoints.SamplePointAt(53373); Assert.AreEqual(53373, soundPoint.Time); - Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(HitSampleInfo.BANK_SOFT, soundPoint.SampleBank); Assert.AreEqual(60, soundPoint.SampleVolume); soundPoint = controlPoints.SamplePointAt(119637); Assert.AreEqual(119637, soundPoint.Time); - Assert.AreEqual("soft", soundPoint.SampleBank); + Assert.AreEqual(HitSampleInfo.BANK_SOFT, soundPoint.SampleBank); Assert.AreEqual(80, soundPoint.SampleVolume); var effectPoint = controlPoints.EffectPointAt(0); @@ -261,10 +261,10 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(controlPoints.EffectPointAt(2500).KiaiMode, Is.False); Assert.That(controlPoints.EffectPointAt(3500).KiaiMode, Is.True); - Assert.That(controlPoints.SamplePointAt(500).SampleBank, Is.EqualTo("drum")); - Assert.That(controlPoints.SamplePointAt(1500).SampleBank, Is.EqualTo("drum")); - Assert.That(controlPoints.SamplePointAt(2500).SampleBank, Is.EqualTo("normal")); - Assert.That(controlPoints.SamplePointAt(3500).SampleBank, Is.EqualTo("drum")); + Assert.That(controlPoints.SamplePointAt(500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_DRUM)); + Assert.That(controlPoints.SamplePointAt(1500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_DRUM)); + Assert.That(controlPoints.SamplePointAt(2500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_NORMAL)); + Assert.That(controlPoints.SamplePointAt(3500).SampleBank, Is.EqualTo(HitSampleInfo.BANK_DRUM)); Assert.That(controlPoints.TimingPointAt(500).BeatLength, Is.EqualTo(500).Within(0.1)); Assert.That(controlPoints.TimingPointAt(1500).BeatLength, Is.EqualTo(500).Within(0.1)); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index 6313842dfd..31939f6971 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -7,6 +7,7 @@ using System.Linq; using Humanizer; using NUnit.Framework; using osu.Framework.Testing; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.UserInterface; @@ -41,7 +42,7 @@ namespace osu.Game.Tests.Visual.Editing Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2, SampleControlPoint = new SampleControlPoint { - SampleBank = "normal", + SampleBank = HitSampleInfo.BANK_NORMAL, SampleVolume = 80 } }); @@ -52,7 +53,7 @@ namespace osu.Game.Tests.Visual.Editing Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2, SampleControlPoint = new SampleControlPoint { - SampleBank = "soft", + SampleBank = HitSampleInfo.BANK_SOFT, SampleVolume = 60 } }); @@ -70,7 +71,7 @@ namespace osu.Game.Tests.Visual.Editing public void TestSingleSelection() { clickSamplePiece(0); - samplePopoverHasSingleBank("normal"); + samplePopoverHasSingleBank(HitSampleInfo.BANK_NORMAL); samplePopoverHasSingleVolume(80); dismissPopover(); @@ -80,14 +81,14 @@ namespace osu.Game.Tests.Visual.Editing AddStep("select first object", () => EditorBeatmap.SelectedHitObjects.Add(EditorBeatmap.HitObjects.First())); clickSamplePiece(1); - samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT); samplePopoverHasSingleVolume(60); setVolumeViaPopover(90); hitObjectHasSampleVolume(1, 90); - setBankViaPopover("drum"); - hitObjectHasSampleBank(1, "drum"); + setBankViaPopover(HitSampleInfo.BANK_DRUM); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM); } [Test] @@ -136,27 +137,27 @@ namespace osu.Game.Tests.Visual.Editing AddStep("unify sample bank", () => { foreach (var h in EditorBeatmap.HitObjects) - h.SampleControlPoint.SampleBank = "soft"; + h.SampleControlPoint.SampleBank = HitSampleInfo.BANK_SOFT; }); AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); clickSamplePiece(0); - samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT); dismissPopover(); clickSamplePiece(1); - samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT); setBankViaPopover(string.Empty); - hitObjectHasSampleBank(0, "soft"); - hitObjectHasSampleBank(1, "soft"); - samplePopoverHasSingleBank("soft"); + hitObjectHasSampleBank(0, HitSampleInfo.BANK_SOFT); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT); + samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT); - setBankViaPopover("drum"); - hitObjectHasSampleBank(0, "drum"); - hitObjectHasSampleBank(1, "drum"); - samplePopoverHasSingleBank("drum"); + setBankViaPopover(HitSampleInfo.BANK_DRUM); + hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM); + samplePopoverHasSingleBank(HitSampleInfo.BANK_DRUM); } [Test] @@ -172,14 +173,14 @@ namespace osu.Game.Tests.Visual.Editing samplePopoverHasIndeterminateBank(); setBankViaPopover(string.Empty); - hitObjectHasSampleBank(0, "normal"); - hitObjectHasSampleBank(1, "soft"); + hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT); samplePopoverHasIndeterminateBank(); - setBankViaPopover("normal"); - hitObjectHasSampleBank(0, "normal"); - hitObjectHasSampleBank(1, "normal"); - samplePopoverHasSingleBank("normal"); + setBankViaPopover(HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_NORMAL); + samplePopoverHasSingleBank(HitSampleInfo.BANK_NORMAL); } private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () => diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index b6da562bd0..6e53302624 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -57,13 +57,13 @@ namespace osu.Game.Tests.Visual.Gameplay { StartTime = t += spacing, Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }, - SampleControlPoint = new SampleControlPoint { SampleBank = "soft" }, + SampleControlPoint = new SampleControlPoint { SampleBank = HitSampleInfo.BANK_SOFT }, }, new HitCircle { StartTime = t + spacing, Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE) }, - SampleControlPoint = new SampleControlPoint { SampleBank = "soft" }, + SampleControlPoint = new SampleControlPoint { SampleBank = HitSampleInfo.BANK_SOFT }, }, }); diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs index efa5562cb8..81cfed23f5 100644 --- a/osu.Game/Audio/HitSampleInfo.cs +++ b/osu.Game/Audio/HitSampleInfo.cs @@ -19,11 +19,20 @@ namespace osu.Game.Audio public const string HIT_FINISH = @"hitfinish"; public const string HIT_CLAP = @"hitclap"; + public const string BANK_NORMAL = @"normal"; + public const string BANK_SOFT = @"soft"; + public const string BANK_DRUM = @"drum"; + /// /// All valid sample addition constants. /// public static IEnumerable AllAdditions => new[] { HIT_WHISTLE, HIT_FINISH, HIT_CLAP }; + /// + /// All valid bank constants. + /// + public static IEnumerable AllBanks => new[] { BANK_NORMAL, BANK_SOFT, BANK_DRUM }; + /// /// The name of the sample to load. /// diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index c454439c5c..e7b869cfa7 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -14,7 +14,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// public class SampleControlPoint : ControlPoint, IEquatable { - public const string DEFAULT_BANK = "normal"; + public const string DEFAULT_BANK = HitSampleInfo.BANK_NORMAL; public static readonly SampleControlPoint DEFAULT = new SampleControlPoint { @@ -30,7 +30,7 @@ namespace osu.Game.Beatmaps.ControlPoints public readonly Bindable SampleBankBindable = new Bindable(DEFAULT_BANK) { Default = DEFAULT_BANK }; /// - /// The speed multiplier at this control point. + /// The sample bank at this control point. /// public string SampleBank { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 75500fbc4e..b3e6f50366 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -10,6 +10,7 @@ using System.Linq; using osu.Framework.Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Logging; +using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Timing; @@ -412,7 +413,7 @@ namespace osu.Game.Beatmaps.Formats string stringSampleSet = sampleSet.ToString().ToLowerInvariant(); if (stringSampleSet == @"none") - stringSampleSet = @"normal"; + stringSampleSet = HitSampleInfo.HIT_NORMAL; if (timingChange) { diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 03c63ff4f2..52d1ea60a5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -547,13 +547,13 @@ namespace osu.Game.Beatmaps.Formats { switch (sampleBank?.ToLowerInvariant()) { - case "normal": + case HitSampleInfo.BANK_NORMAL: return LegacySampleBank.Normal; - case "soft": + case HitSampleInfo.BANK_SOFT: return LegacySampleBank.Soft; - case "drum": + case HitSampleInfo.BANK_DRUM: return LegacySampleBank.Drum; default: From 9222cb379f7c5cdb1ad7a25f73df28242e5b1406 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 20:53:18 +0900 Subject: [PATCH 002/195] Add sample bank suport to editor selection handler --- .../Components/EditorSelectionHandler.cs | 73 ++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 0bdfc5b0a0..1670328e58 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -48,11 +48,40 @@ namespace osu.Game.Screens.Edit.Compose.Components /// public readonly Dictionary> SelectionSampleStates = new Dictionary>(); + /// + /// The state of each sample bank type for all selected hitobjects. + /// + public readonly Dictionary> SelectionBankStates = new Dictionary>(); + /// /// Set up ternary state bindables and bind them to selection/hitobject changes (in both directions) /// private void createStateBindables() { + foreach (string bankName in HitSampleInfo.AllBanks) + { + var bindable = new Bindable + { + Description = bankName.Titleize() + }; + + bindable.ValueChanged += state => + { + switch (state.NewValue) + { + case TernaryState.False: + RemoveSampleBank(bankName); + break; + + case TernaryState.True: + AddSampleBank(bankName); + break; + } + }; + + SelectionBankStates[bankName] = bindable; + } + foreach (string sampleName in HitSampleInfo.AllAdditions) { var bindable = new Bindable @@ -104,12 +133,48 @@ namespace osu.Game.Screens.Edit.Compose.Components { bindable.Value = GetStateFromSelection(SelectedItems, h => h.Samples.Any(s => s.Name == sampleName)); } + + foreach ((string bankName, var bindable) in SelectionBankStates) + { + bindable.Value = GetStateFromSelection(SelectedItems, h => h.SampleControlPoint.SampleBank == bankName); + } } #endregion #region Ternary state changes + /// + /// Adds a sample bank to all selected s. + /// + /// The name of the sample bank. + public void AddSampleBank(string bankName) + { + EditorBeatmap.PerformOnSelection(h => + { + if (h.SampleControlPoint.SampleBank == bankName) + return; + + h.SampleControlPoint.SampleBank = bankName; + EditorBeatmap.Update(h); + }); + } + + /// + /// Removes a sample bank from all selected s. + /// + /// The name of the sample bank. + public void RemoveSampleBank(string bankName) + { + EditorBeatmap.PerformOnSelection(h => + { + if (h.SampleControlPoint.SampleBank == bankName) + h.SampleControlPoint.SampleBankBindable.SetDefault(); + + EditorBeatmap.Update(h); + }); + } + /// /// Adds a hit sample to all selected s. /// @@ -174,11 +239,17 @@ namespace osu.Game.Screens.Edit.Compose.Components yield return new TernaryStateToggleMenuItem("New combo") { State = { BindTarget = SelectionNewComboState } }; } - yield return new OsuMenuItem("Sound") + yield return new OsuMenuItem("Sample") { Items = SelectionSampleStates.Select(kvp => new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray() }; + + yield return new OsuMenuItem("Bank") + { + Items = SelectionBankStates.Select(kvp => + new TernaryStateToggleMenuItem(kvp.Value.Description) { State = { BindTarget = kvp.Value } }).ToArray() + }; } #endregion From 50e24ddd872bcd8e6c5c543d8fa6fcecac0f7b3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 21:35:08 +0900 Subject: [PATCH 003/195] Add icon and radio button logic --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 20 ++++++++- .../Components/ComposeBlueprintContainer.cs | 42 ++++++++++++++++++- .../Components/EditorSelectionHandler.cs | 35 +++++++++++++++- 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 3bed835854..8857bb2dae 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -71,6 +71,8 @@ namespace osu.Game.Rulesets.Edit private FillFlowContainer togglesCollection; + private FillFlowContainer sampleBankTogglesCollection; + private IBindable hasTiming; protected HitObjectComposer(Ruleset ruleset) @@ -146,6 +148,16 @@ namespace osu.Game.Rulesets.Edit Direction = FillDirection.Vertical, Spacing = new Vector2(0, 5), }, + }, + new EditorToolboxGroup("bank (Shift-Q~R)") + { + Child = sampleBankTogglesCollection = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + }, } } }, @@ -161,6 +173,8 @@ namespace osu.Game.Rulesets.Edit TernaryStates = CreateTernaryButtons().ToArray(); togglesCollection.AddRange(TernaryStates.Select(b => new DrawableTernaryButton(b))); + sampleBankTogglesCollection.AddRange(BlueprintContainer.SampleBankTernaryStates.Select(b => new DrawableTernaryButton(b))); + setSelectTool(); EditorBeatmap.SelectedHitObjects.CollectionChanged += selectionChanged; @@ -213,7 +227,7 @@ namespace osu.Game.Rulesets.Edit /// /// Create all ternary states required to be displayed to the user. /// - protected virtual IEnumerable CreateTernaryButtons() => BlueprintContainer.TernaryStates; + protected virtual IEnumerable CreateTernaryButtons() => BlueprintContainer.MainTernaryStates; /// /// Construct a relevant blueprint container. This will manage hitobject selection/placement input handling and display logic. @@ -255,7 +269,9 @@ namespace osu.Game.Rulesets.Edit if (checkRightToggleFromKey(e.Key, out int rightIndex)) { - var item = togglesCollection.ElementAtOrDefault(rightIndex); + var item = e.ShiftPressed + ? sampleBankTogglesCollection.ElementAtOrDefault(rightIndex) + : togglesCollection.ElementAtOrDefault(rightIndex); if (item is DrawableTernaryButton button) { diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index ec07da43a0..5adc60f6a7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -14,6 +14,8 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Game.Audio; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -55,7 +57,8 @@ namespace osu.Game.Screens.Edit.Compose.Components [BackgroundDependencyLoader] private void load() { - TernaryStates = CreateTernaryButtons().ToArray(); + MainTernaryStates = CreateTernaryButtons().ToArray(); + SampleBankTernaryStates = createSampleBankTernaryButtons().ToArray(); AddInternal(placementBlueprintContainer); } @@ -172,7 +175,9 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// A collection of states which will be displayed to the user in the toolbox. /// - public TernaryButton[] TernaryStates { get; private set; } + public TernaryButton[] MainTernaryStates { get; private set; } + + public TernaryButton[] SampleBankTernaryStates { get; private set; } /// /// Create all ternary states required to be displayed to the user. @@ -186,6 +191,39 @@ namespace osu.Game.Screens.Edit.Compose.Components yield return new TernaryButton(kvp.Value, kvp.Key.Replace("hit", string.Empty).Titleize(), () => getIconForSample(kvp.Key)); } + private IEnumerable createSampleBankTernaryButtons() + { + foreach (var kvp in SelectionHandler.SelectionBankStates) + yield return new TernaryButton(kvp.Value, kvp.Key.Titleize(), () => getIconForBank(kvp.Key)); + } + + private Drawable getIconForBank(string sampleName) + { + return new Container + { + Size = new Vector2(30, 20), + Children = new Drawable[] + { + new SpriteIcon + { + Size = new Vector2(8), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Icon = FontAwesome.Solid.VolumeOff + }, + new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + X = 10, + Y = -1, + Font = OsuFont.Default.With(weight: FontWeight.Bold, size: 20), + Text = $"{char.ToUpper(sampleName.First())}" + } + } + }; + } + private Drawable getIconForSample(string sampleName) { switch (sampleName) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 1670328e58..750dedac20 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.UserInterface; using osu.Game.Audio; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -70,11 +71,38 @@ namespace osu.Game.Screens.Edit.Compose.Components switch (state.NewValue) { case TernaryState.False: - RemoveSampleBank(bankName); + if (SelectedItems.Count == 0) + { + // Ensure that if this is the last selected bank, it should remain selected. + if (SelectionBankStates.Values.All(b => b.Value == TernaryState.False)) + bindable.Value = TernaryState.True; + } + else + { + // Never remove a sample bank. + // These are basically radio buttons, not toggles. + if (SelectedItems.All(h => h.SampleControlPoint.SampleBank == bankName)) + bindable.Value = TernaryState.True; + } + break; case TernaryState.True: - AddSampleBank(bankName); + if (SelectedItems.Count == 0) + { + // Ensure the user can't stack multiple bank selections when there's no hitobject selection. + // Note that in normal scenarios this is sorted out by the feedback from applying the bank to the selected objects. + foreach (var other in SelectionBankStates.Values) + { + if (other != bindable) + other.Value = TernaryState.False; + } + } + else + { + AddSampleBank(bankName); + } + break; } }; @@ -82,6 +110,9 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectionBankStates[bankName] = bindable; } + // start with normal selected. + SelectionBankStates[SampleControlPoint.DEFAULT_BANK].Value = TernaryState.True; + foreach (string sampleName in HitSampleInfo.AllAdditions) { var bindable = new Bindable From 372a655be1637d8823d89532d24a28f283798906 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 21:39:51 +0900 Subject: [PATCH 004/195] Ensure placement uses currently selected bank --- .../Components/ComposeBlueprintContainer.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 5adc60f6a7..1c9ac83630 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -77,9 +77,10 @@ namespace osu.Game.Screens.Edit.Compose.Components // we own SelectionHandler so don't need to worry about making bindable copies (for simplicity) foreach (var kvp in SelectionHandler.SelectionSampleStates) - { kvp.Value.BindValueChanged(_ => updatePlacementSamples()); - } + + foreach (var kvp in SelectionHandler.SelectionBankStates) + kvp.Value.BindValueChanged(_ => updatePlacementSamples()); } protected override void TransferBlueprintFor(HitObject hitObject, DrawableHitObject drawableObject) @@ -146,6 +147,9 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (var kvp in SelectionHandler.SelectionSampleStates) sampleChanged(kvp.Key, kvp.Value.Value); + + foreach (var kvp in SelectionHandler.SelectionBankStates) + bankChanged(kvp.Key, kvp.Value.Value); } private void sampleChanged(string sampleName, TernaryState state) @@ -170,6 +174,18 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private void bankChanged(string bankName, TernaryState state) + { + if (currentPlacement == null) return; + + switch (state) + { + case TernaryState.True: + currentPlacement.HitObject.SampleControlPoint.SampleBank = bankName; + break; + } + } + public readonly Bindable NewCombo = new Bindable { Description = "New Combo" }; /// From b9f41611a7777951b8eebbb83ab5c9a6b27e1e12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 21:48:18 +0900 Subject: [PATCH 005/195] Fix bank potentially being overwritten during placement --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index c8196b6865..132214a0bd 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -74,9 +74,13 @@ namespace osu.Game.Rulesets.Edit /// Whether this call is committing a value for HitObject.StartTime and continuing with further adjustments. protected void BeginPlacement(bool commitStart = false) { + // Store and copy the bank, since it is managed by the editor UI. + string bank = HitObject.SampleControlPoint.SampleBank; + var nearestSampleControlPoint = beatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.SampleControlPoint?.DeepClone() as SampleControlPoint; HitObject.SampleControlPoint = nearestSampleControlPoint ?? new SampleControlPoint(); + HitObject.SampleControlPoint.SampleBank = bank; placementHandler.BeginPlacement(HitObject); if (commitStart) From 677b8d09f8773626b36f0e686520fb871b53220d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 19 Oct 2022 23:54:12 +0900 Subject: [PATCH 006/195] Fix huge oversight causing test failures --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index b3e6f50366..ae57ee6f5a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -413,7 +413,7 @@ namespace osu.Game.Beatmaps.Formats string stringSampleSet = sampleSet.ToString().ToLowerInvariant(); if (stringSampleSet == @"none") - stringSampleSet = HitSampleInfo.HIT_NORMAL; + stringSampleSet = HitSampleInfo.BANK_NORMAL; if (timingChange) { From 849b50a38fe821720944fff0b560f33dceb25286 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 21 Oct 2022 00:16:33 +0900 Subject: [PATCH 007/195] Use `ToUpperInvariant` for added safety MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 1c9ac83630..eebc4c8e0e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -234,7 +234,7 @@ namespace osu.Game.Screens.Edit.Compose.Components X = 10, Y = -1, Font = OsuFont.Default.With(weight: FontWeight.Bold, size: 20), - Text = $"{char.ToUpper(sampleName.First())}" + Text = $"{char.ToUpperInvariant(sampleName.First())}" } } }; From 6e48860c79646d0f60e86e7e010954773d300730 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Feb 2023 17:13:55 +0900 Subject: [PATCH 008/195] Update in line with framework menu handling changes --- osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs index ad02e3b2ab..0adff11342 100644 --- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs @@ -77,10 +77,12 @@ namespace osu.Game.Graphics.UserInterface private void updateState() { - hoverClickSounds.Enabled.Value = !Item.Action.Disabled; - Alpha = Item.Action.Disabled ? 0.2f : 1; + bool enabledState = IsActionable || HasSubmenu; - if (IsHovered && !Item.Action.Disabled) + hoverClickSounds.Enabled.Value = enabledState; + Alpha = enabledState ? 1 : 0.2f; + + if (IsHovered && enabledState) { text.BoldText.FadeIn(transition_length, Easing.OutQuint); text.NormalText.FadeOut(transition_length, Easing.OutQuint); From 5aebbac6c5e8377f265404139c7630b9da834464 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 14 Mar 2023 18:15:18 +0900 Subject: [PATCH 009/195] Fix osu!mania hold note animations not correctly re-applying after rewind There's early exit logic in `OnPressed`/`OnReleased` for the sake of keeping order correct, but this doesn't account for the fact that `DrawableHitObject` resets all animations when the hit state changes. A bit of an ugly workaround, but seems to work as expected. --- .../Objects/Drawables/DrawableHoldNote.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 25d0573a82..3bf3aec9c3 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -323,7 +323,12 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // do not run any of this logic when rewinding, as it inverts order of presses/releases. if (Time.Elapsed < 0) + { + // Except for the IsHitting state, as this handles animations that need to be reapplied + // after rewind. + isHitting.Value = false; return; + } // Make sure a hold was started if (HoldStartTime == null) From bfd81b77fa58ce67acefe3264ba05fadb8abf97a Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Wed, 26 Apr 2023 17:40:25 +0900 Subject: [PATCH 010/195] provide more Accuracy Mode for `ModAccuracyChallenge` --- .../Rulesets/Mods/ModAccuracyChallenge.cs | 51 ++++++++++++------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index d4223a80c2..03149fd8c8 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Localisation.HUD; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Judgements; @@ -42,30 +43,44 @@ namespace osu.Game.Rulesets.Mods Value = 0.9, }; - private ScoreProcessor scoreProcessor = null!; + [SettingSource("Accuracy Mode", "The Accuracy mode that will be used to Judge.")] + public Bindable AccuracyJudgeMode { get; } = new Bindable(); - public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) => this.scoreProcessor = scoreProcessor; + private readonly Bindable currentAccuracy = new Bindable(); + + public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) + { + switch (AccuracyJudgeMode.Value) + { + case AccuracyMode.Standard: + currentAccuracy.BindTo(scoreProcessor.Accuracy); + break; + + case AccuracyMode.MaximumAchievable: + currentAccuracy.BindTo(scoreProcessor.MaximumAccuracy); + break; + } + + currentAccuracy.BindValueChanged(s => + { + if (s.NewValue < MinimumAccuracy.Value) + { + TriggerFailure(); + } + }); + } public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank; - protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) + protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) => false; + + public enum AccuracyMode { - if (!result.Type.AffectsAccuracy()) - return false; + [LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeStandard))] + Standard, - return getAccuracyWithImminentResultAdded(result) < MinimumAccuracy.Value; - } - - private double getAccuracyWithImminentResultAdded(JudgementResult result) - { - var score = new ScoreInfo { Ruleset = scoreProcessor.Ruleset.RulesetInfo }; - - // This is super ugly, but if we don't do it this way we will not have the most recent result added to the accuracy value. - // Hopefully we can improve this in the future. - scoreProcessor.PopulateScore(score); - score.Statistics[result.Type]++; - - return scoreProcessor.ComputeAccuracy(score); + [LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeMax))] + MaximumAchievable, } } } From 6a59ded1bad631e2b3ce508b4050b0a937126037 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 May 2023 15:18:37 +0900 Subject: [PATCH 011/195] Move logic to `Update` instead --- .../Objects/Drawables/DrawableHoldNote.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index b2c84b140b..20daab2447 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -219,6 +219,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Time.Current < releaseTime) releaseTime = null; + if (Time.Current < HoldStartTime) + endHold(); + // Pad the full size container so its contents (i.e. the masking container) reach under the tail. // This is required for the tail to not be masked away, since it lies outside the bounds of the hold note. sizingContainer.Padding = new MarginPadding @@ -322,15 +325,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (e.Action != Action.Value) return; - // do not run any of this logic when rewinding, as it inverts order of presses/releases. - if (Time.Elapsed < 0) - { - // Except for the IsHitting state, as this handles animations that need to be reapplied - // after rewind. - isHitting.Value = false; - return; - } - // Make sure a hold was started if (HoldStartTime == null) return; From 7d1e73e36dcf67a5052395aa84f9c241d2d6696b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 4 May 2023 21:24:31 +0200 Subject: [PATCH 012/195] Add workflow for automated osu-web mod definition updates --- .../workflows/update-web-mod-definitions.yml | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 .github/workflows/update-web-mod-definitions.yml diff --git a/.github/workflows/update-web-mod-definitions.yml b/.github/workflows/update-web-mod-definitions.yml new file mode 100644 index 0000000000..32d3d37ffe --- /dev/null +++ b/.github/workflows/update-web-mod-definitions.yml @@ -0,0 +1,53 @@ +name: Update osu-web mod definitions +on: + push: + tags: + - '*' + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + update-mod-definitions: + name: Update osu-web mod definitions + runs-on: ubuntu-latest + steps: + - name: Install .NET 6.0.x + uses: actions/setup-dotnet@v3 + with: + dotnet-version: "6.0.x" + + - name: Checkout ppy/osu + uses: actions/checkout@v3 + with: + path: osu + + - name: Checkout ppy/osu-tools + uses: actions/checkout@v3 + with: + repository: ppy/osu-tools + path: osu-tools + + - name: Checkout ppy/osu-web + uses: actions/checkout@v3 + with: + repository: ppy/osu-web + path: osu-web + + - name: Setup local game checkout for tools + run: ./UseLocalOsu.sh + working-directory: ./osu-tools + + - name: Regenerate mod definitions + run: dotnet run --project PerformanceCalculator -- mods > ../osu-web/database/mods.json + working-directory: ./osu-tools + + - name: Create pull request with changes + uses: peter-evans/create-pull-request@v5 + with: + title: Update mod definitions + body: "This PR has been auto-generated to update the mod definitions to match ppy/osu@${{ github.ref_name }}." + branch: update-mod-definitions + commit-message: Update mod definitions + path: osu-web + token: ${{ secrets.OSU_WEB_PULL_REQUEST_PAT }} From 85d0c56cd28d8a299e570b891f3f208b7832674c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 13:00:45 +0900 Subject: [PATCH 013/195] Fix incorrect special style description text Closes https://github.com/ppy/osu/issues/23428. --- osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs index 508733ad14..98635c10a3 100644 --- a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs +++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup specialStyle = new LabelledSwitchButton { Label = "Use special (N+1) style", - Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 5k (4+1) or 8key (7+1) configurations.", + Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6k (5+1) or 8k (7+1) configurations.", Current = { Value = Beatmap.BeatmapInfo.SpecialStyle } } }; From f7d44c3013f3bb11d6e2aa5476950e1d4036f3ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 May 2023 12:32:38 +0900 Subject: [PATCH 014/195] Rename `SliderMultiplier` to `BaseSliderVelocity` --- .../Editor/TestSceneJuiceStreamPlacementBlueprint.cs | 2 +- .../Editor/TestSceneJuiceStreamSelectionBlueprint.cs | 2 +- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneJuiceStream.cs | 2 +- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 4 ++-- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- .../Editor/TestSceneSliderStreamConversion.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Editor/TestSceneTaikoEditorSaving.cs | 4 ++-- .../Beatmaps/TaikoBeatmapConverter.cs | 6 +++--- .../Mods/TaikoModDifficultyAdjust.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../TestSceneHitObjectComposerDistanceSnapping.cs | 12 ++++++------ .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 2 +- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 4 ++-- .../Gameplay/TestSceneGameplaySampleTriggerSource.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 8 ++++---- osu.Game/Beatmaps/BeatmapImporter.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 7 ++++--- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- .../UI/Scrolling/DrawableScrollingRuleset.cs | 6 +++--- 30 files changed, 49 insertions(+), 48 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index 2426f8c886..e729a09a18 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor { var playable = base.GetPlayableBeatmap(); playable.Difficulty.SliderTickRate = 5; - playable.Difficulty.SliderMultiplier = velocity_factor * 10; + playable.Difficulty.BaseSliderVelocity = velocity_factor * 10; return playable; } diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index beba5811fe..634e1f5cbb 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor X = x, Path = sliderPath, }; - EditorBeatmap.Difficulty.SliderMultiplier = velocity; + EditorBeatmap.Difficulty.BaseSliderVelocity = velocity; EditorBeatmap.Add(hitObject); EditorBeatmap.Update(hitObject); Assert.That(hitObject.Velocity, Is.EqualTo(velocity)); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 40dc7d2403..d1f3f06971 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests { BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, + Difficulty = new BeatmapDifficulty { CircleSize = 6, BaseSliderVelocity = 3 }, Ruleset = ruleset } }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index c91f07891c..71cb8964c3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests { BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 }, + Difficulty = new BeatmapDifficulty { CircleSize = 5, BaseSliderVelocity = 2 }, Ruleset = ruleset }, HitObjects = new List diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 169e99c90c..301ecd6d66 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Catch.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - velocityFactor = base_scoring_distance * difficulty.SliderMultiplier / timingPoint.BeatLength; - tickDistanceFactor = base_scoring_distance * difficulty.SliderMultiplier / difficulty.SliderTickRate; + velocityFactor = base_scoring_distance * difficulty.BaseSliderVelocity / timingPoint.BeatLength; + tickDistanceFactor = base_scoring_distance * difficulty.BaseSliderVelocity / difficulty.SliderTickRate; } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 91b7be6e8f..ab6f5e8269 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy StartTime = (int)Math.Round(hitObject.StartTime); // This matches stable's calculation. - EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier); + EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.BaseSliderVelocity); SegmentDuration = (EndTime - StartTime) / SpanCount; } diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index af8758fb5e..cb38551a43 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Mania.UI { // Mania doesn't care about global velocity p.Velocity = 1; - p.BaseBeatLength *= Beatmap.Difficulty.SliderMultiplier; + p.BaseBeatLength *= Beatmap.Difficulty.BaseSliderVelocity; // For non-mania beatmap, speed changes should only happen through timing points if (!isForCurrentRuleset) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index 7579e8077b..b79d4efe1b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [SetUp] public void Setup() => Schedule(() => { - editorBeatmap.Difficulty.SliderMultiplier = 1; + editorBeatmap.Difficulty.BaseSliderVelocity = 1; editorBeatmap.ControlPointInfo.Clear(); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); snapProvider.DistanceSpacingMultiplier.Value = 1; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs index a162d9a491..938e093489 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("change to these specific circumstances", () => { - EditorBeatmap.Difficulty.SliderMultiplier = 1; + EditorBeatmap.Difficulty.BaseSliderVelocity = 1; var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(slider.StartTime); timingPoint.BeatLength = 352.941176470588; slider.Path.ControlPoints[^1].Position = new Vector2(-110, 16); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 4189f8ba1e..b86351e7cd 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * SliderVelocity; + double scoringDistance = BASE_SCORING_DISTANCE * difficulty.BaseSliderVelocity * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; TickDistance = GenerateTicks ? (scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier) : double.PositiveInfinity; diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs index 93b26624de..d79ca28c8f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor [Test] public void TestTaikoSliderMultiplier() { - AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = 2); + AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.BaseSliderVelocity = 2); SaveEditor(); @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor // therefore, ensure that we have that difficulty type by calling .CopyFrom(), which is a no-op if the type is already correct. var taikoDifficulty = new TaikoBeatmapConverter.TaikoMultiplierAppliedDifficulty(); taikoDifficulty.CopyFrom(EditorBeatmap.Difficulty); - return Precision.AlmostEquals(taikoDifficulty.SliderMultiplier, 2); + return Precision.AlmostEquals(taikoDifficulty.BaseSliderVelocity, 2); } } } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index e298e313df..932211695a 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -189,7 +189,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else beatLength = timingPoint.BeatLength; - double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.SliderMultiplier / beatmap.Difficulty.SliderTickRate; + double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.BaseSliderVelocity / beatmap.Difficulty.SliderTickRate; // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.Difficulty.SliderTickRate; @@ -239,14 +239,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { base.CopyTo(other); if (!(other is TaikoMultiplierAppliedDifficulty)) - other.SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + other.BaseSliderVelocity /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } public override void CopyFrom(IBeatmapDifficultyInfo other) { base.CopyFrom(other); if (!(other is TaikoMultiplierAppliedDifficulty)) - SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + BaseSliderVelocity *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } #endregion diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 99a064d35f..fe9c1f5815 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { base.ApplySettings(difficulty); - if (ScrollSpeed.Value != null) difficulty.SliderMultiplier *= ScrollSpeed.Value.Value; + if (ScrollSpeed.Value != null) difficulty.BaseSliderVelocity *= ScrollSpeed.Value.Value; } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 009f2854f8..5aeb5a87d7 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override void ApplyToDifficulty(BeatmapDifficulty difficulty) { base.ApplyToDifficulty(difficulty); - difficulty.SliderMultiplier *= slider_multiplier; + difficulty.BaseSliderVelocity *= slider_multiplier; } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index ba41175461..0f68750535 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override void ApplyToDifficulty(BeatmapDifficulty difficulty) { base.ApplyToDifficulty(difficulty); - difficulty.SliderMultiplier *= slider_multiplier; + difficulty.BaseSliderVelocity *= slider_multiplier; } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index b4a12fd314..014fec9319 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = base_distance * difficulty.SliderMultiplier * SliderVelocity; + double scoringDistance = base_distance * difficulty.BaseSliderVelocity * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; tickSpacing = timingPoint.BeatLength / TickRate; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 2c4f193327..98ad6fc18d 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.SliderMultiplier); + Assert.AreEqual(1.8, difficulty.BaseSliderVelocity); Assert.AreEqual(2, difficulty.SliderTickRate); } } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 3764467047..11c5f2a2fc 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.SliderMultiplier); + Assert.AreEqual(1.8, difficulty.BaseSliderVelocity); Assert.AreEqual(2, difficulty.SliderTickRate); } diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index 6399507aa0..ffbe16260b 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Editing BeatDivisor.Value = 1; - composer.EditorBeatmap.Difficulty.SliderMultiplier = 1; + composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 1; composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); }); @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Editing [TestCase(2)] public void TestSliderMultiplier(float multiplier) { - AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier); + AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = multiplier); assertSnapDistance(100 * multiplier, null, true); } @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Editing assertDurationToDistance(500, 50); assertDurationToDistance(1000, 100); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); assertDurationToDistance(500, 100); assertDurationToDistance(1000, 200); @@ -149,7 +149,7 @@ namespace osu.Game.Tests.Editing assertDistanceToDuration(50, 500); assertDistanceToDuration(100, 1000); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); assertDistanceToDuration(100, 500); assertDistanceToDuration(200, 1000); @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Editing assertSnappedDuration(200, 2000); assertSnappedDuration(250, 3000); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); assertSnappedDuration(0, 0); assertSnappedDuration(50, 0); @@ -206,7 +206,7 @@ namespace osu.Game.Tests.Editing assertSnappedDistance(200, 200); assertSnappedDistance(250, 200); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); assertSnappedDistance(50, 0); assertSnappedDistance(100, 0); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 21b925a257..a99bd72e40 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Editing } }); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); - editorBeatmap.Difficulty.SliderMultiplier = 1; + editorBeatmap.Difficulty.BaseSliderVelocity = 1; } [SetUp] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 287b7d43b4..735af9493c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.Difficulty.SliderMultiplier = 2; + beatmap.Difficulty.BaseSliderVelocity = 2; createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 5000); @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.Difficulty.SliderMultiplier = 2; + beatmap.Difficulty.BaseSliderVelocity = 2; createTest(beatmap); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 2000); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index 114c554d28..28cc890fce 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Gameplay { BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, + Difficulty = new BeatmapDifficulty { CircleSize = 6, BaseSliderVelocity = 3 }, Ruleset = ruleset }, ControlPointInfo = controlPointInfo diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index f4bc5e7b77..cbea30f3b0 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps public class BeatmapDifficulty : EmbeddedObject, IBeatmapDifficultyInfo { /// - /// The default value used for all difficulty settings except and . + /// The default value used for all difficulty settings except and . /// public const float DEFAULT_DIFFICULTY = 5; @@ -20,7 +20,7 @@ namespace osu.Game.Beatmaps public float OverallDifficulty { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; public float ApproachRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; - public double SliderMultiplier { get; set; } = 1; + public double BaseSliderVelocity { get; set; } = 1; public double SliderTickRate { get; set; } = 1; public BeatmapDifficulty() @@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps difficulty.CircleSize = CircleSize; difficulty.OverallDifficulty = OverallDifficulty; - difficulty.SliderMultiplier = SliderMultiplier; + difficulty.BaseSliderVelocity = BaseSliderVelocity; difficulty.SliderTickRate = SliderTickRate; } @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps CircleSize = other.CircleSize; OverallDifficulty = other.OverallDifficulty; - SliderMultiplier = other.SliderMultiplier; + BaseSliderVelocity = other.BaseSliderVelocity; SliderTickRate = other.SliderTickRate; } } diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 4731a70753..e96e38d249 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -353,7 +353,7 @@ namespace osu.Game.Beatmaps CircleSize = decodedDifficulty.CircleSize, OverallDifficulty = decodedDifficulty.OverallDifficulty, ApproachRate = decodedDifficulty.ApproachRate, - SliderMultiplier = decodedDifficulty.SliderMultiplier, + BaseSliderVelocity = decodedDifficulty.BaseSliderVelocity, SliderTickRate = decodedDifficulty.SliderTickRate, }; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 5e98025c9a..891e627435 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -384,7 +384,7 @@ namespace osu.Game.Beatmaps.Formats break; case @"SliderMultiplier": - difficulty.SliderMultiplier = Parsing.ParseDouble(pair.Value); + difficulty.BaseSliderVelocity = Parsing.ParseDouble(pair.Value); break; case @"SliderTickRate": diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7fbcca9adb..fffead3a6c 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -155,8 +155,8 @@ namespace osu.Game.Beatmaps.Formats // Taiko adjusts the slider multiplier (see: LEGACY_TAIKO_VELOCITY_MULTIPLIER) writer.WriteLine(onlineRulesetID == 1 - ? FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") - : FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier}")); + ? FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.BaseSliderVelocity / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") + : FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.BaseSliderVelocity}")); writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.Difficulty.SliderTickRate}")); } diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index dad9bbbd0b..bc2ee23da7 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -9,7 +9,7 @@ namespace osu.Game.Beatmaps public interface IBeatmapDifficultyInfo { /// - /// The default value used for all difficulty settings except and . + /// The default value used for all difficulty settings except and . /// const float DEFAULT_DIFFICULTY = 5; @@ -34,9 +34,10 @@ namespace osu.Game.Beatmaps float ApproachRate { get; } /// - /// The slider multiplier of the associated beatmap. + /// The base slider velocity of the associated beatmap. + /// This was known as "SliderMultiplier" in the .osu format and stable editor. /// - double SliderMultiplier { get; } + double BaseSliderVelocity { get; } /// /// The slider tick rate of the associated beatmap. diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index a8972775de..4adbb356da 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -240,7 +240,7 @@ namespace osu.Game.Rulesets.Edit public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true) { - return (float)(100 * (useReferenceSliderVelocity && referenceObject is IHasSliderVelocity hasSliderVelocity ? hasSliderVelocity.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 + return (float)(100 * (useReferenceSliderVelocity && referenceObject is IHasSliderVelocity hasSliderVelocity ? hasSliderVelocity.SliderVelocity : 1) * EditorBeatmap.Difficulty.BaseSliderVelocity * 1 / BeatSnapProvider.BeatDivisor); } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 7ddd372dc9..bdcf539033 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Legacy TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * SliderVelocity; + double scoringDistance = base_scoring_distance * difficulty.BaseSliderVelocity * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; } diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index 4c7564b791..da1110506a 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -123,7 +123,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here - baseBeatLength /= Beatmap.Difficulty.SliderMultiplier; + baseBeatLength /= Beatmap.Difficulty.BaseSliderVelocity; } // Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.UI.Scrolling return new MultiplierControlPoint(c.Time) { - Velocity = Beatmap.Difficulty.SliderMultiplier, + Velocity = Beatmap.Difficulty.BaseSliderVelocity, BaseBeatLength = baseBeatLength, TimingPoint = lastTimingPoint, EffectPoint = lastEffectPoint @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.UI.Scrolling ControlPoints.AddRange(timingChanges); if (ControlPoints.Count == 0) - ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.Difficulty.SliderMultiplier }); + ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.Difficulty.BaseSliderVelocity }); } protected override void LoadComplete() From b109ee74a631a32df7195b077cd237e70ead1530 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 May 2023 13:20:57 +0900 Subject: [PATCH 015/195] Add "base velocity" adjustment to difficulty setup screen --- osu.Game/Localisation/EditorSetupStrings.cs | 10 ++++++++++ osu.Game/Screens/Edit/Setup/DifficultySection.cs | 15 +++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/osu.Game/Localisation/EditorSetupStrings.cs b/osu.Game/Localisation/EditorSetupStrings.cs index 4ddacf2c5b..caea3dd130 100644 --- a/osu.Game/Localisation/EditorSetupStrings.cs +++ b/osu.Game/Localisation/EditorSetupStrings.cs @@ -126,6 +126,16 @@ namespace osu.Game.Localisation public static LocalisableString OverallDifficultyDescription => new TranslatableString(getKey(@"overall_difficulty_description"), @"The harshness of hit windows and difficulty of special objects (ie. spinners)"); + /// + /// "Base Velocity" + /// + public static LocalisableString BaseVelocity => new TranslatableString(getKey(@"base_velocity"), @"Base Velocity"); + + /// + /// "The base velocity of the beatmap, affecting things like slider velocity and scroll speed in some rulesets." + /// + public static LocalisableString BaseVelocityDescription => new TranslatableString(getKey(@"base_velocity_description"), @"The base velocity of the beatmap, affecting things like slider velocity and scroll speed in some rulesets."); + /// /// "Metadata" /// diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 7026bde681..7c4b0e72d7 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -19,6 +19,7 @@ namespace osu.Game.Screens.Edit.Setup private LabelledSliderBar healthDrainSlider = null!; private LabelledSliderBar approachRateSlider = null!; private LabelledSliderBar overallDifficultySlider = null!; + private LabelledSliderBar baseVelocitySlider = null!; public override LocalisableString Title => EditorSetupStrings.DifficultyHeader; @@ -79,6 +80,19 @@ namespace osu.Game.Screens.Edit.Setup Precision = 0.1f, } }, + baseVelocitySlider = new LabelledSliderBar + { + Label = EditorSetupStrings.BaseVelocity, + FixedLabelWidth = LABEL_WIDTH, + Description = EditorSetupStrings.BaseVelocityDescription, + Current = new BindableDouble(Beatmap.Difficulty.BaseSliderVelocity) + { + Default = 1, + MinValue = 0.01, + MaxValue = 10, + Precision = 0.1f, + } + }, }; foreach (var item in Children.OfType>()) @@ -93,6 +107,7 @@ namespace osu.Game.Screens.Edit.Setup Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value; Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value; Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value; + Beatmap.Difficulty.BaseSliderVelocity = baseVelocitySlider.Current.Value; Beatmap.UpdateAllHitObjects(); Beatmap.SaveState(); From 31de4de72031afdfa1e02ed201a5bbcacf9d8333 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 May 2023 13:21:39 +0900 Subject: [PATCH 016/195] Remove median/mode slider velocity display The intention was to give an idea of what the most common velocity of the beatmap is, but in hindsight, because the "base" velocity is being set elsewhere this doesn't make sense. It will/should be 1.0x. Showing this range is still valuable, though. --- .../Timeline/DifficultyPointPiece.cs | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index 13a1c30cfe..545e54254a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -159,20 +159,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline double[] sliderVelocities = EditorBeatmap.HitObjects.OfType().Select(sv => sv.SliderVelocity).OrderBy(v => v).ToArray(); - if (sliderVelocities.Length < 2) - return; + if (sliderVelocities.First() != sliderVelocities.Last()) + { + AddHeader("Used velocity range"); + AddValue($"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x"); + } - double? modeSliderVelocity = sliderVelocities.GroupBy(v => v).MaxBy(v => v.Count())?.Key; - double? medianSliderVelocity = sliderVelocities[sliderVelocities.Length / 2]; - - AddHeader("Average velocity"); - AddValue($"{medianSliderVelocity:#,0.00}x"); - - AddHeader("Most used velocity"); - AddValue($"{modeSliderVelocity:#,0.00}x"); - - AddHeader("Velocity range"); - AddValue($"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x"); } protected override void Dispose(bool isDisposing) From d9dd35c0202d10694721737c16675e4db236c45f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 May 2023 13:22:48 +0900 Subject: [PATCH 017/195] Show base velocity in slider adjustment popover --- .../Edit/Compose/Components/Timeline/DifficultyPointPiece.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index 545e54254a..a02a3be430 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -165,6 +165,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline AddValue($"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x"); } + AddHeader("Beatmap base velocity"); + AddValue($"{EditorBeatmap.Difficulty.BaseSliderVelocity:#,0.00}x"); } protected override void Dispose(bool isDisposing) From 451af9d1b5b04e58cf034f65a2480366c3b9a34b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 13:23:13 +0900 Subject: [PATCH 018/195] Fix beatmap values not being updated due to a varying data type --- osu.Game/Screens/Edit/Setup/DifficultySection.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 7c4b0e72d7..66bb8a9b84 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -96,10 +96,13 @@ namespace osu.Game.Screens.Edit.Setup }; foreach (var item in Children.OfType>()) - item.Current.ValueChanged += onValueChanged; + item.Current.ValueChanged += _ => updateValues(); + + foreach (var item in Children.OfType>()) + item.Current.ValueChanged += _ => updateValues(); } - private void onValueChanged(ValueChangedEvent args) + private void updateValues() { // for now, update these on commit rather than making BeatmapMetadata bindables. // after switching database engines we can reconsider if switching to bindables is a good direction. From a91edd68d976df038c654feea6c1714d03ac11bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 13:42:25 +0900 Subject: [PATCH 019/195] Show post-multiplied velocity in main hit object inspector --- osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs index 597925e3e2..60b9010aa8 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (selected is IHasSliderVelocity sliderVelocity) { AddHeader("Slider Velocity"); - AddValue($"{sliderVelocity.SliderVelocity:#,0.00}x"); + AddValue($"{sliderVelocity.SliderVelocity:#,0.00}x ({sliderVelocity.SliderVelocity * EditorBeatmap.Difficulty.BaseSliderVelocity:#,0.00}x)"); } if (selected is IHasRepeats repeats) From a6cb1f90e4c9ab1b2e06bda043321f2795a56b8e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 13:43:21 +0900 Subject: [PATCH 020/195] Change difficulty popover inspector display (yet again) I think this makes the most sense of the iterations I've tested so far, albeit maybe being a touch too verbose. --- .../Timeline/DifficultyPointPiece.cs | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index a02a3be430..ac37101060 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -96,7 +96,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline RelativeSizeAxes = Axes.X, Text = "Hold shift while dragging the end of an object to adjust velocity while snapping." }, - new SliderVelocityInspector(), + new SliderVelocityInspector(sliderVelocitySlider.Current), } } }; @@ -145,28 +145,48 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline internal partial class SliderVelocityInspector : EditorInspector { + private readonly Bindable current; + + public SliderVelocityInspector(Bindable current) + { + this.current = current; + } + [BackgroundDependencyLoader] private void load() { EditorBeatmap.TransactionBegan += updateInspectorText; EditorBeatmap.TransactionEnded += updateInspectorText; + EditorBeatmap.BeatmapReprocessed += updateInspectorText; + current.ValueChanged += _ => updateInspectorText(); + updateInspectorText(); } private void updateInspectorText() { + double beatmapVelocity = EditorBeatmap.Difficulty.BaseSliderVelocity; + InspectorText.Clear(); double[] sliderVelocities = EditorBeatmap.HitObjects.OfType().Select(sv => sv.SliderVelocity).OrderBy(v => v).ToArray(); + AddHeader("Base velocity (from beatmap setup)"); + AddValue($"{beatmapVelocity:#,0.00}x"); + + AddHeader("Final velocity"); + AddValue($"{beatmapVelocity * current.Value:#,0.00}x"); + if (sliderVelocities.First() != sliderVelocities.Last()) { - AddHeader("Used velocity range"); - AddValue($"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x"); - } + AddHeader("Beatmap velocity range"); - AddHeader("Beatmap base velocity"); - AddValue($"{EditorBeatmap.Difficulty.BaseSliderVelocity:#,0.00}x"); + string range = $"{sliderVelocities.First():#,0.00}x - {sliderVelocities.Last():#,0.00}x"; + if (beatmapVelocity != 1) + range += $" ({beatmapVelocity * sliderVelocities.First():#,0.00}x - {beatmapVelocity * sliderVelocities.Last():#,0.00}x)"; + + AddValue(range); + } } protected override void Dispose(bool isDisposing) From 8c44d528e9f640d56a4834cb81d99cbf81bc630a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 13:51:52 +0900 Subject: [PATCH 021/195] Use min/max values from stable --- osu.Game/Screens/Edit/Setup/DifficultySection.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 66bb8a9b84..6c7e96f91d 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -88,9 +88,9 @@ namespace osu.Game.Screens.Edit.Setup Current = new BindableDouble(Beatmap.Difficulty.BaseSliderVelocity) { Default = 1, - MinValue = 0.01, - MaxValue = 10, - Precision = 0.1f, + MinValue = 0.4, + MaxValue = 3.6, + Precision = 0.01f, } }, }; From f0020381377f77f58d5374007aebbe16e70e8776 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 15:29:04 +0900 Subject: [PATCH 022/195] Add test coverage of failing wiki return to page scenario --- .../Visual/Online/TestSceneWikiOverlay.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index b0e4303ca4..310e34262e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -1,8 +1,6 @@ // 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; using System.Linq; using System.Net; @@ -20,7 +18,7 @@ namespace osu.Game.Tests.Visual.Online { private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; - private WikiOverlay wiki; + private WikiOverlay wiki = null!; [SetUp] public void SetUp() => Schedule(() => Child = wiki = new WikiOverlay()); @@ -73,7 +71,23 @@ namespace osu.Game.Tests.Visual.Online AddUntilStep("Error message correct", () => wiki.ChildrenOfType().Any(text => text.Text == "\"This_page_will_error_out\".")); } - private void setUpWikiResponse(APIWikiPage r, string redirectionPath = null) + [Test] + public void TestReturnAfterErrorPage() + { + setUpWikiResponse(responseArticlePage); + + AddStep("Show article page", () => wiki.ShowPage("Article_styling_criteria/Formatting")); + AddUntilStep("Wait for non-error page", () => wiki.CurrentPath == "Article_styling_criteria/Formatting"); + + AddStep("Show nonexistent page", () => wiki.ShowPage("This_page_will_error_out")); + AddUntilStep("Wait for error page", () => wiki.CurrentPath == "error"); + + AddStep("Show article page", () => wiki.ShowPage("Article_styling_criteria/Formatting")); + AddUntilStep("Wait for non-error page", () => wiki.CurrentPath == "Article_styling_criteria/Formatting"); + AddUntilStep("Error message not displayed", () => wiki.ChildrenOfType().All(text => text.Text != "\"This_page_will_error_out\".")); + } + + private void setUpWikiResponse(APIWikiPage r, string? redirectionPath = null) => AddStep("set up response", () => { dummyAPI.HandleRequest = request => From 715b735131d8c1c1b3073222175ebe8753fc51ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 15:20:17 +0900 Subject: [PATCH 023/195] Fix "Return to main page" link not working on wiki after error --- osu.Game/Overlays/WikiOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 2444aa4fa2..c816eca776 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -157,7 +157,9 @@ namespace osu.Game.Overlays private void onFail(string originalPath) { + wikiData.Value = null; path.Value = "error"; + LoadDisplay(articlePage = new WikiArticlePage($@"{api.WebsiteRootUrl}/wiki/", $"Something went wrong when trying to fetch page \"{originalPath}\".\n\n[Return to the main page](Main_Page).")); } From 9160711470035a7fbe6fd8227debe1934c943742 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 15:32:00 +0900 Subject: [PATCH 024/195] Change "Show main page" test steps to actually load the main page --- osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 310e34262e..79c7e3a22e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -27,13 +27,13 @@ namespace osu.Game.Tests.Visual.Online public void TestMainPage() { setUpWikiResponse(responseMainPage); - AddStep("Show main page", () => wiki.Show()); + AddStep("Show main page", () => wiki.ShowPage()); } [Test] public void TestCancellationDoesntShowError() { - AddStep("Show main page", () => wiki.Show()); + AddStep("Show main page", () => wiki.ShowPage()); AddStep("Show another page", () => wiki.ShowPage("Article_styling_criteria/Formatting")); AddUntilStep("Current path is not error", () => wiki.CurrentPath != "error"); From 814f0b3fedf27661b721aecb17c1a9fbb7c5f000 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 15:43:11 +0900 Subject: [PATCH 025/195] Add back early return in `OnReleased` for safety --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 20daab2447..e5a6aa1049 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -329,6 +329,10 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (HoldStartTime == null) return; + // do not run any of this logic when rewinding, as it inverts order of presses/releases. + if (Time.Elapsed < 0) + return; + Tail.UpdateResult(); endHold(); From 1b7dd32eb1f006393fb73969f215a4b2421619c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 16:55:29 +0900 Subject: [PATCH 026/195] Apply nullability in related classes and remove unused variable --- .../TestSceneHoldNoteInput.cs | 12 ++++-------- .../UI/Scrolling/Algorithms/IScrollAlgorithm.cs | 2 -- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 42e2099e3f..ce6a118d9b 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -1,8 +1,6 @@ // 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 NUnit.Framework; @@ -40,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Tests private const double time_tail = 4000; private const double time_after_tail = 5250; - private List judgementResults; + private List judgementResults = new List(); /// /// -----[ ]----- @@ -521,9 +519,9 @@ namespace osu.Game.Rulesets.Mania.Tests private void assertLastTickJudgement(HitResult result) => AddAssert($"last tick judged as {result}", () => judgementResults.Last(j => j.HitObject is HoldNoteTick).Type, () => Is.EqualTo(result)); - private ScoreAccessibleReplayPlayer currentPlayer; + private ScoreAccessibleReplayPlayer currentPlayer = null!; - private void performTest(List frames, Beatmap beatmap = null) + private void performTest(List frames, Beatmap? beatmap = null) { if (beatmap == null) { @@ -569,15 +567,13 @@ namespace osu.Game.Rulesets.Mania.Tests AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); - AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor?.HasCompleted.Value == true); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } private partial class ScoreAccessibleReplayPlayer : ReplayPlayer { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; - public new GameplayClockContainer GameplayClockContainer => base.GameplayClockContainer; - protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs index f78509f919..437e5a5e38 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/IScrollAlgorithm.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - namespace osu.Game.Rulesets.UI.Scrolling.Algorithms { public interface IScrollAlgorithm From d6ce56e6b1d7eec37ea19a32d5034f467fb96226 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 16:55:48 +0900 Subject: [PATCH 027/195] Fix `GetMostCommonBeatLength` returning zero in case of not timing points --- osu.Game/Beatmaps/Beatmap.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 416d655cc3..4f81b26c3e 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -107,9 +107,12 @@ namespace osu.Game.Beatmaps // Aggregate durations into a set of (beatLength, duration) tuples for each beat length .GroupBy(t => Math.Round(t.beatLength * 1000) / 1000) .Select(g => (beatLength: g.Key, duration: g.Sum(t => t.duration))) - // Get the most common one, or 0 as a suitable default + // Get the most common one, or 0 as a suitable default (see handling below) .OrderByDescending(i => i.duration).FirstOrDefault(); + if (mostCommon.beatLength == 0) + return TimingControlPoint.DEFAULT_BEAT_LENGTH; + return mostCommon.beatLength; } From bcabe967141d8a55b42b4e3da42546c8f2520749 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 17:49:41 +0900 Subject: [PATCH 028/195] Add extra tests of hold note input in more standard scenarios --- .../TestSceneHoldNoteInput.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index ce6a118d9b..77db1b0bd8 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -59,6 +59,44 @@ namespace osu.Game.Rulesets.Mania.Tests assertNoteJudgement(HitResult.IgnoreMiss); } + /// + /// -----[ ]----- + /// x o + /// + [Test] + public void TestCorrectInput() + { + performTest(new List + { + new ManiaReplayFrame(time_head, ManiaAction.Key1), + new ManiaReplayFrame(time_tail), + }); + + assertHeadJudgement(HitResult.Perfect); + assertTickJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.Perfect); + assertNoteJudgement(HitResult.IgnoreHit); + } + + /// + /// -----[ ]----- + /// x o + /// + [Test] + public void TestLateRelease() + { + performTest(new List + { + new ManiaReplayFrame(time_head, ManiaAction.Key1), + new ManiaReplayFrame(time_after_tail), + }); + + assertHeadJudgement(HitResult.Perfect); + assertTickJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.Miss); + assertNoteJudgement(HitResult.IgnoreMiss); + } + /// /// -----[ ]----- /// x o From 27c10cbdb7082e219400873ae72de33029e131bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 18:14:07 +0900 Subject: [PATCH 029/195] Remove clamping of `sizingContainer` in `DrawableHoldNote` to fix head note alignment --- .../Objects/Drawables/DrawableHoldNote.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 372ef1e164..09976827fe 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -246,8 +245,8 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (Head.IsHit && releaseTime == null && DrawHeight > 0) { // How far past the hit target this hold note is. Always a positive value. - float yOffset = Math.Max(0, Direction.Value == ScrollingDirection.Up ? -Y : Y); - sizingContainer.Height = Math.Clamp(1 - yOffset / DrawHeight, 0, 1); + float yOffset = Direction.Value == ScrollingDirection.Up ? -Y : Y; + sizingContainer.Height = 1 - yOffset / DrawHeight; } } From 64498e95a46915843625de96156c9e6ec6e0c0aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 18:56:29 +0900 Subject: [PATCH 030/195] Add an `IAnimationTimeReference` to `DrawableHitObject` to synchronise all animations --- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 79fc778287..07c0d1f8a1 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -30,7 +30,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Objects.Drawables { [Cached(typeof(DrawableHitObject))] - public abstract partial class DrawableHitObject : PoolableDrawableWithLifetime + public abstract partial class DrawableHitObject : PoolableDrawableWithLifetime, IAnimationTimeReference { /// /// Invoked after this 's applied has had its defaults applied. @@ -425,11 +425,13 @@ namespace osu.Game.Rulesets.Objects.Drawables LifetimeEnd = double.MaxValue; - double transformTime = HitObject.StartTime - InitialLifetimeOffset; - clearExistingStateTransforms(); - using (BeginAbsoluteSequence(transformTime)) + double initialTransformsTime = HitObject.StartTime - InitialLifetimeOffset; + + AnimationStartTime.Value = initialTransformsTime; + + using (BeginAbsoluteSequence(initialTransformsTime)) UpdateInitialTransforms(); using (BeginAbsoluteSequence(StateUpdateTime)) @@ -721,6 +723,8 @@ namespace osu.Game.Rulesets.Objects.Drawables if (CurrentSkin != null) CurrentSkin.SourceChanged -= skinSourceChanged; } + + public Bindable AnimationStartTime { get; } = new BindableDouble(); } public abstract partial class DrawableHitObject : DrawableHitObject From 1ff17309484e02aceb14efa3ba2fea8ebce67b88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 May 2023 22:35:41 +0200 Subject: [PATCH 031/195] Remove no-longer-correct remark --- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 9ffd338d7c..ce34addeff 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables // 2. The head note will move along with the new "head position" in the container. if (Head.IsHit && releaseTime == null && DrawHeight > 0) { - // How far past the hit target this hold note is. Always a positive value. + // How far past the hit target this hold note is. float yOffset = Direction.Value == ScrollingDirection.Up ? -Y : Y; sizingContainer.Height = 1 - yOffset / DrawHeight; } From 0a47ffcbdd4bacdba6a0ab1739e7eaaeaedef35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 May 2023 07:03:13 +0200 Subject: [PATCH 032/195] Match generally used casing Co-authored-by: Joseph Madamba --- osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs index 98635c10a3..d5a9a311bc 100644 --- a/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs +++ b/osu.Game.Rulesets.Mania/Edit/Setup/ManiaSetupSection.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Setup specialStyle = new LabelledSwitchButton { Label = "Use special (N+1) style", - Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6k (5+1) or 8k (7+1) configurations.", + Description = "Changes one column to act as a classic \"scratch\" or \"special\" column, which can be moved around by the user's skin (to the left/right/centre). Generally used in 6K (5+1) or 8K (7+1) configurations.", Current = { Value = Beatmap.BeatmapInfo.SpecialStyle } } }; From 944da06c108b9240190c297b5825365e10ccf49b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 May 2023 16:58:19 +0900 Subject: [PATCH 033/195] Rename slider multiplier variable back for now --- .../Editor/TestSceneJuiceStreamPlacementBlueprint.cs | 2 +- .../Editor/TestSceneJuiceStreamSelectionBlueprint.cs | 2 +- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneJuiceStream.cs | 2 +- osu.Game.Rulesets.Catch/Objects/JuiceStream.cs | 4 ++-- .../Legacy/DistanceObjectPatternGenerator.cs | 2 +- osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs | 2 +- .../Editor/TestSceneOsuDistanceSnapGrid.cs | 2 +- .../Editor/TestSceneSliderStreamConversion.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Slider.cs | 2 +- .../Editor/TestSceneTaikoEditorSaving.cs | 4 ++-- .../Beatmaps/TaikoBeatmapConverter.cs | 6 +++--- .../Mods/TaikoModDifficultyAdjust.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Beatmaps/Formats/LegacyBeatmapDecoderTest.cs | 2 +- .../Beatmaps/Formats/OsuJsonDecoderTest.cs | 2 +- .../TestSceneHitObjectComposerDistanceSnapping.cs | 12 ++++++------ .../Visual/Editing/TestSceneDistanceSnapGrid.cs | 2 +- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 4 ++-- .../Gameplay/TestSceneGameplaySampleTriggerSource.cs | 2 +- osu.Game/Beatmaps/BeatmapDifficulty.cs | 8 ++++---- osu.Game/Beatmaps/BeatmapImporter.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 4 ++-- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 4 ++-- osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs | 2 +- osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs | 2 +- .../UI/Scrolling/DrawableScrollingRuleset.cs | 6 +++--- .../Edit/Compose/Components/HitObjectInspector.cs | 2 +- .../Components/Timeline/DifficultyPointPiece.cs | 2 +- osu.Game/Screens/Edit/Setup/DifficultySection.cs | 4 ++-- 33 files changed, 51 insertions(+), 51 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs index e729a09a18..2426f8c886 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamPlacementBlueprint.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor { var playable = base.GetPlayableBeatmap(); playable.Difficulty.SliderTickRate = 5; - playable.Difficulty.BaseSliderVelocity = velocity_factor * 10; + playable.Difficulty.SliderMultiplier = velocity_factor * 10; return playable; } diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 634e1f5cbb..beba5811fe 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor X = x, Path = sliderPath, }; - EditorBeatmap.Difficulty.BaseSliderVelocity = velocity; + EditorBeatmap.Difficulty.SliderMultiplier = velocity; EditorBeatmap.Add(hitObject); EditorBeatmap.Update(hitObject); Assert.That(hitObject.Velocity, Is.EqualTo(velocity)); diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index d1f3f06971..40dc7d2403 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Catch.Tests { BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty { CircleSize = 6, BaseSliderVelocity = 3 }, + Difficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, Ruleset = ruleset } }; diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index 71cb8964c3..c91f07891c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Catch.Tests { BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty { CircleSize = 5, BaseSliderVelocity = 2 }, + Difficulty = new BeatmapDifficulty { CircleSize = 5, SliderMultiplier = 2 }, Ruleset = ruleset }, HitObjects = new List diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 301ecd6d66..169e99c90c 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -64,8 +64,8 @@ namespace osu.Game.Rulesets.Catch.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - velocityFactor = base_scoring_distance * difficulty.BaseSliderVelocity / timingPoint.BeatLength; - tickDistanceFactor = base_scoring_distance * difficulty.BaseSliderVelocity / difficulty.SliderTickRate; + velocityFactor = base_scoring_distance * difficulty.SliderMultiplier / timingPoint.BeatLength; + tickDistanceFactor = base_scoring_distance * difficulty.SliderMultiplier / difficulty.SliderTickRate; } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index ab6f5e8269..91b7be6e8f 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy StartTime = (int)Math.Round(hitObject.StartTime); // This matches stable's calculation. - EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.BaseSliderVelocity); + EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier); SegmentDuration = (EndTime - StartTime) / SpanCount; } diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index cb38551a43..af8758fb5e 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -92,7 +92,7 @@ namespace osu.Game.Rulesets.Mania.UI { // Mania doesn't care about global velocity p.Velocity = 1; - p.BaseBeatLength *= Beatmap.Difficulty.BaseSliderVelocity; + p.BaseBeatLength *= Beatmap.Difficulty.SliderMultiplier; // For non-mania beatmap, speed changes should only happen through timing points if (!isForCurrentRuleset) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs index b79d4efe1b..7579e8077b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuDistanceSnapGrid.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor [SetUp] public void Setup() => Schedule(() => { - editorBeatmap.Difficulty.BaseSliderVelocity = 1; + editorBeatmap.Difficulty.SliderMultiplier = 1; editorBeatmap.ControlPointInfo.Clear(); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); snapProvider.DistanceSpacingMultiplier.Value = 1; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs index 938e093489..a162d9a491 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderStreamConversion.cs @@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("change to these specific circumstances", () => { - EditorBeatmap.Difficulty.BaseSliderVelocity = 1; + EditorBeatmap.Difficulty.SliderMultiplier = 1; var timingPoint = EditorBeatmap.ControlPointInfo.TimingPointAt(slider.StartTime); timingPoint.BeatLength = 352.941176470588; slider.Path.ControlPoints[^1].Position = new Vector2(-110, 16); diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index b86351e7cd..4189f8ba1e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = BASE_SCORING_DISTANCE * difficulty.BaseSliderVelocity * SliderVelocity; + double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; TickDistance = GenerateTicks ? (scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier) : double.PositiveInfinity; diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs index d79ca28c8f..93b26624de 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor [Test] public void TestTaikoSliderMultiplier() { - AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.BaseSliderVelocity = 2); + AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = 2); SaveEditor(); @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor // therefore, ensure that we have that difficulty type by calling .CopyFrom(), which is a no-op if the type is already correct. var taikoDifficulty = new TaikoBeatmapConverter.TaikoMultiplierAppliedDifficulty(); taikoDifficulty.CopyFrom(EditorBeatmap.Difficulty); - return Precision.AlmostEquals(taikoDifficulty.BaseSliderVelocity, 2); + return Precision.AlmostEquals(taikoDifficulty.SliderMultiplier, 2); } } } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 932211695a..e298e313df 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -189,7 +189,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else beatLength = timingPoint.BeatLength; - double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.BaseSliderVelocity / beatmap.Difficulty.SliderTickRate; + double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.SliderMultiplier / beatmap.Difficulty.SliderTickRate; // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.Difficulty.SliderTickRate; @@ -239,14 +239,14 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps { base.CopyTo(other); if (!(other is TaikoMultiplierAppliedDifficulty)) - other.BaseSliderVelocity /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + other.SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } public override void CopyFrom(IBeatmapDifficultyInfo other) { base.CopyFrom(other); if (!(other is TaikoMultiplierAppliedDifficulty)) - BaseSliderVelocity *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; } #endregion diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index fe9c1f5815..99a064d35f 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Mods { base.ApplySettings(difficulty); - if (ScrollSpeed.Value != null) difficulty.BaseSliderVelocity *= ScrollSpeed.Value.Value; + if (ScrollSpeed.Value != null) difficulty.SliderMultiplier *= ScrollSpeed.Value.Value; } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs index 5aeb5a87d7..009f2854f8 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModEasy.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override void ApplyToDifficulty(BeatmapDifficulty difficulty) { base.ApplyToDifficulty(difficulty); - difficulty.BaseSliderVelocity *= slider_multiplier; + difficulty.SliderMultiplier *= slider_multiplier; } } } diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index 0f68750535..ba41175461 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Mods public override void ApplyToDifficulty(BeatmapDifficulty difficulty) { base.ApplyToDifficulty(difficulty); - difficulty.BaseSliderVelocity *= slider_multiplier; + difficulty.SliderMultiplier *= slider_multiplier; } } } diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 014fec9319..b4a12fd314 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -68,7 +68,7 @@ namespace osu.Game.Rulesets.Taiko.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = base_distance * difficulty.BaseSliderVelocity * SliderVelocity; + double scoringDistance = base_distance * difficulty.SliderMultiplier * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; tickSpacing = timingPoint.BeatLength / TickRate; diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 98ad6fc18d..2c4f193327 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -136,7 +136,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.BaseSliderVelocity); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); Assert.AreEqual(2, difficulty.SliderTickRate); } } diff --git a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs index 11c5f2a2fc..3764467047 100644 --- a/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/OsuJsonDecoderTest.cs @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(4, difficulty.CircleSize); Assert.AreEqual(8, difficulty.OverallDifficulty); Assert.AreEqual(9, difficulty.ApproachRate); - Assert.AreEqual(1.8, difficulty.BaseSliderVelocity); + Assert.AreEqual(1.8, difficulty.SliderMultiplier); Assert.AreEqual(2, difficulty.SliderTickRate); } diff --git a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs index ffbe16260b..6399507aa0 100644 --- a/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs +++ b/osu.Game.Tests/Editing/TestSceneHitObjectComposerDistanceSnapping.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Editing BeatDivisor.Value = 1; - composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 1; + composer.EditorBeatmap.Difficulty.SliderMultiplier = 1; composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); }); @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Editing [TestCase(2)] public void TestSliderMultiplier(float multiplier) { - AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = multiplier); + AddStep($"set slider multiplier = {multiplier}", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = multiplier); assertSnapDistance(100 * multiplier, null, true); } @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Editing assertDurationToDistance(500, 50); assertDurationToDistance(1000, 100); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertDurationToDistance(500, 100); assertDurationToDistance(1000, 200); @@ -149,7 +149,7 @@ namespace osu.Game.Tests.Editing assertDistanceToDuration(50, 500); assertDistanceToDuration(100, 1000); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertDistanceToDuration(100, 500); assertDistanceToDuration(200, 1000); @@ -174,7 +174,7 @@ namespace osu.Game.Tests.Editing assertSnappedDuration(200, 2000); assertSnappedDuration(250, 3000); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertSnappedDuration(0, 0); assertSnappedDuration(50, 0); @@ -206,7 +206,7 @@ namespace osu.Game.Tests.Editing assertSnappedDistance(200, 200); assertSnappedDistance(250, 200); - AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.BaseSliderVelocity = 2); + AddStep("set slider multiplier = 2", () => composer.EditorBeatmap.Difficulty.SliderMultiplier = 2); assertSnappedDistance(50, 0); assertSnappedDistance(100, 0); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index a99bd72e40..21b925a257 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -45,7 +45,7 @@ namespace osu.Game.Tests.Visual.Editing } }); editorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = beat_length }); - editorBeatmap.Difficulty.BaseSliderVelocity = 1; + editorBeatmap.Difficulty.SliderMultiplier = 1; } [SetUp] diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 735af9493c..287b7d43b4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -184,7 +184,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.Difficulty.BaseSliderVelocity = 2; + beatmap.Difficulty.SliderMultiplier = 2; createTest(beatmap, d => d.RelativeScaleBeatLengthsOverride = true); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 5000); @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.Difficulty.BaseSliderVelocity = 2; + beatmap.Difficulty.SliderMultiplier = 2; createTest(beatmap); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 2000); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index 28cc890fce..114c554d28 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -46,7 +46,7 @@ namespace osu.Game.Tests.Visual.Gameplay { BeatmapInfo = new BeatmapInfo { - Difficulty = new BeatmapDifficulty { CircleSize = 6, BaseSliderVelocity = 3 }, + Difficulty = new BeatmapDifficulty { CircleSize = 6, SliderMultiplier = 3 }, Ruleset = ruleset }, ControlPointInfo = controlPointInfo diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index cbea30f3b0..f4bc5e7b77 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -11,7 +11,7 @@ namespace osu.Game.Beatmaps public class BeatmapDifficulty : EmbeddedObject, IBeatmapDifficultyInfo { /// - /// The default value used for all difficulty settings except and . + /// The default value used for all difficulty settings except and . /// public const float DEFAULT_DIFFICULTY = 5; @@ -20,7 +20,7 @@ namespace osu.Game.Beatmaps public float OverallDifficulty { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; public float ApproachRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; - public double BaseSliderVelocity { get; set; } = 1; + public double SliderMultiplier { get; set; } = 1; public double SliderTickRate { get; set; } = 1; public BeatmapDifficulty() @@ -44,7 +44,7 @@ namespace osu.Game.Beatmaps difficulty.CircleSize = CircleSize; difficulty.OverallDifficulty = OverallDifficulty; - difficulty.BaseSliderVelocity = BaseSliderVelocity; + difficulty.SliderMultiplier = SliderMultiplier; difficulty.SliderTickRate = SliderTickRate; } @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps CircleSize = other.CircleSize; OverallDifficulty = other.OverallDifficulty; - BaseSliderVelocity = other.BaseSliderVelocity; + SliderMultiplier = other.SliderMultiplier; SliderTickRate = other.SliderTickRate; } } diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index e96e38d249..4731a70753 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -353,7 +353,7 @@ namespace osu.Game.Beatmaps CircleSize = decodedDifficulty.CircleSize, OverallDifficulty = decodedDifficulty.OverallDifficulty, ApproachRate = decodedDifficulty.ApproachRate, - BaseSliderVelocity = decodedDifficulty.BaseSliderVelocity, + SliderMultiplier = decodedDifficulty.SliderMultiplier, SliderTickRate = decodedDifficulty.SliderTickRate, }; diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 891e627435..5e98025c9a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -384,7 +384,7 @@ namespace osu.Game.Beatmaps.Formats break; case @"SliderMultiplier": - difficulty.BaseSliderVelocity = Parsing.ParseDouble(pair.Value); + difficulty.SliderMultiplier = Parsing.ParseDouble(pair.Value); break; case @"SliderTickRate": diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index fffead3a6c..7fbcca9adb 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -155,8 +155,8 @@ namespace osu.Game.Beatmaps.Formats // Taiko adjusts the slider multiplier (see: LEGACY_TAIKO_VELOCITY_MULTIPLIER) writer.WriteLine(onlineRulesetID == 1 - ? FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.BaseSliderVelocity / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") - : FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.BaseSliderVelocity}")); + ? FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") + : FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier}")); writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.Difficulty.SliderTickRate}")); } diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index bc2ee23da7..78234a9dd9 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -9,7 +9,7 @@ namespace osu.Game.Beatmaps public interface IBeatmapDifficultyInfo { /// - /// The default value used for all difficulty settings except and . + /// The default value used for all difficulty settings except and . /// const float DEFAULT_DIFFICULTY = 5; @@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps /// The base slider velocity of the associated beatmap. /// This was known as "SliderMultiplier" in the .osu format and stable editor. /// - double BaseSliderVelocity { get; } + double SliderMultiplier { get; } /// /// The slider tick rate of the associated beatmap. diff --git a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs index 4adbb356da..a8972775de 100644 --- a/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/DistancedHitObjectComposer.cs @@ -240,7 +240,7 @@ namespace osu.Game.Rulesets.Edit public virtual float GetBeatSnapDistanceAt(HitObject referenceObject, bool useReferenceSliderVelocity = true) { - return (float)(100 * (useReferenceSliderVelocity && referenceObject is IHasSliderVelocity hasSliderVelocity ? hasSliderVelocity.SliderVelocity : 1) * EditorBeatmap.Difficulty.BaseSliderVelocity * 1 + return (float)(100 * (useReferenceSliderVelocity && referenceObject is IHasSliderVelocity hasSliderVelocity ? hasSliderVelocity.SliderVelocity : 1) * EditorBeatmap.Difficulty.SliderMultiplier * 1 / BeatSnapProvider.BeatDivisor); } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index bdcf539033..7ddd372dc9 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Objects.Legacy TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); - double scoringDistance = base_scoring_distance * difficulty.BaseSliderVelocity * SliderVelocity; + double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * SliderVelocity; Velocity = scoringDistance / timingPoint.BeatLength; } diff --git a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs index da1110506a..4c7564b791 100644 --- a/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs +++ b/osu.Game/Rulesets/UI/Scrolling/DrawableScrollingRuleset.cs @@ -123,7 +123,7 @@ namespace osu.Game.Rulesets.UI.Scrolling // The slider multiplier is post-multiplied to determine the final velocity, but for relative scale beat lengths // the multiplier should not affect the effective timing point (the longest in the beatmap), so it is factored out here - baseBeatLength /= Beatmap.Difficulty.BaseSliderVelocity; + baseBeatLength /= Beatmap.Difficulty.SliderMultiplier; } // Merge sequences of timing and difficulty control points to create the aggregate "multiplier" control point @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.UI.Scrolling return new MultiplierControlPoint(c.Time) { - Velocity = Beatmap.Difficulty.BaseSliderVelocity, + Velocity = Beatmap.Difficulty.SliderMultiplier, BaseBeatLength = baseBeatLength, TimingPoint = lastTimingPoint, EffectPoint = lastEffectPoint @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.UI.Scrolling ControlPoints.AddRange(timingChanges); if (ControlPoints.Count == 0) - ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.Difficulty.BaseSliderVelocity }); + ControlPoints.Add(new MultiplierControlPoint { Velocity = Beatmap.Difficulty.SliderMultiplier }); } protected override void LoadComplete() diff --git a/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs b/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs index 60b9010aa8..7beaf7d086 100644 --- a/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs +++ b/osu.Game/Screens/Edit/Compose/Components/HitObjectInspector.cs @@ -73,7 +73,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (selected is IHasSliderVelocity sliderVelocity) { AddHeader("Slider Velocity"); - AddValue($"{sliderVelocity.SliderVelocity:#,0.00}x ({sliderVelocity.SliderVelocity * EditorBeatmap.Difficulty.BaseSliderVelocity:#,0.00}x)"); + AddValue($"{sliderVelocity.SliderVelocity:#,0.00}x ({sliderVelocity.SliderVelocity * EditorBeatmap.Difficulty.SliderMultiplier:#,0.00}x)"); } if (selected is IHasRepeats repeats) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index ac37101060..76ad3e5b6a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline private void updateInspectorText() { - double beatmapVelocity = EditorBeatmap.Difficulty.BaseSliderVelocity; + double beatmapVelocity = EditorBeatmap.Difficulty.SliderMultiplier; InspectorText.Clear(); diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 6c7e96f91d..3a3fe7f747 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -85,7 +85,7 @@ namespace osu.Game.Screens.Edit.Setup Label = EditorSetupStrings.BaseVelocity, FixedLabelWidth = LABEL_WIDTH, Description = EditorSetupStrings.BaseVelocityDescription, - Current = new BindableDouble(Beatmap.Difficulty.BaseSliderVelocity) + Current = new BindableDouble(Beatmap.Difficulty.SliderMultiplier) { Default = 1, MinValue = 0.4, @@ -110,7 +110,7 @@ namespace osu.Game.Screens.Edit.Setup Beatmap.Difficulty.DrainRate = healthDrainSlider.Current.Value; Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value; Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value; - Beatmap.Difficulty.BaseSliderVelocity = baseVelocitySlider.Current.Value; + Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value; Beatmap.UpdateAllHitObjects(); Beatmap.SaveState(); From 2085833a842c023ddb26e85e6fd54a2375867119 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 May 2023 17:08:03 +0900 Subject: [PATCH 034/195] Fix missing delegate unsubscribe --- .../Edit/Compose/Components/Timeline/DifficultyPointPiece.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs index 76ad3e5b6a..173a665d5c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/DifficultyPointPiece.cs @@ -195,6 +195,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline EditorBeatmap.TransactionBegan -= updateInspectorText; EditorBeatmap.TransactionEnded -= updateInspectorText; + EditorBeatmap.BeatmapReprocessed -= updateInspectorText; } } } From ca5e8b290f30006c93116e7d1ef47fbdb663fc00 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 14:05:59 +0900 Subject: [PATCH 035/195] Add clamping to `SliderMultiplier` and `SliderTickRate` at parsing time --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 5e98025c9a..49594ca969 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -384,11 +384,11 @@ namespace osu.Game.Beatmaps.Formats break; case @"SliderMultiplier": - difficulty.SliderMultiplier = Parsing.ParseDouble(pair.Value); + difficulty.SliderMultiplier = Math.Clamp(Parsing.ParseDouble(pair.Value), 0.4, 3.6); break; case @"SliderTickRate": - difficulty.SliderTickRate = Parsing.ParseDouble(pair.Value); + difficulty.SliderTickRate = Math.Clamp(Parsing.ParseDouble(pair.Value), 0.5, 8); break; } } From bdf8a78b4245dc008e8661820ea3cbcff42b97e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 8 May 2023 14:14:54 +0900 Subject: [PATCH 036/195] Add the ability to adjust the beatmap tick rate in the editor --- osu.Game/Localisation/EditorSetupStrings.cs | 31 ++++++++++--------- .../Screens/Edit/Setup/DifficultySection.cs | 15 +++++++++ 2 files changed, 32 insertions(+), 14 deletions(-) diff --git a/osu.Game/Localisation/EditorSetupStrings.cs b/osu.Game/Localisation/EditorSetupStrings.cs index caea3dd130..401411365b 100644 --- a/osu.Game/Localisation/EditorSetupStrings.cs +++ b/osu.Game/Localisation/EditorSetupStrings.cs @@ -42,8 +42,7 @@ namespace osu.Game.Localisation /// /// "If enabled, an "Are you ready? 3, 2, 1, GO!" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so." /// - public static LocalisableString CountdownDescription => new TranslatableString(getKey(@"countdown_description"), - @"If enabled, an ""Are you ready? 3, 2, 1, GO!"" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so."); + public static LocalisableString CountdownDescription => new TranslatableString(getKey(@"countdown_description"), @"If enabled, an ""Are you ready? 3, 2, 1, GO!"" countdown will be inserted at the beginning of the beatmap, assuming there is enough time to do so."); /// /// "Countdown speed" @@ -53,8 +52,7 @@ namespace osu.Game.Localisation /// /// "If the countdown sounds off-time, use this to make it appear one or more beats early." /// - public static LocalisableString CountdownOffsetDescription => - new TranslatableString(getKey(@"countdown_offset_description"), @"If the countdown sounds off-time, use this to make it appear one or more beats early."); + public static LocalisableString CountdownOffsetDescription => new TranslatableString(getKey(@"countdown_offset_description"), @"If the countdown sounds off-time, use this to make it appear one or more beats early."); /// /// "Countdown offset" @@ -69,8 +67,7 @@ namespace osu.Game.Localisation /// /// "Allows storyboards to use the full screen space, rather than be confined to a 4:3 area." /// - public static LocalisableString WidescreenSupportDescription => - new TranslatableString(getKey(@"widescreen_support_description"), @"Allows storyboards to use the full screen space, rather than be confined to a 4:3 area."); + public static LocalisableString WidescreenSupportDescription => new TranslatableString(getKey(@"widescreen_support_description"), @"Allows storyboards to use the full screen space, rather than be confined to a 4:3 area."); /// /// "Epilepsy warning" @@ -80,8 +77,7 @@ namespace osu.Game.Localisation /// /// "Recommended if the storyboard or video contain scenes with rapidly flashing colours." /// - public static LocalisableString EpilepsyWarningDescription => - new TranslatableString(getKey(@"epilepsy_warning_description"), @"Recommended if the storyboard or video contain scenes with rapidly flashing colours."); + public static LocalisableString EpilepsyWarningDescription => new TranslatableString(getKey(@"epilepsy_warning_description"), @"Recommended if the storyboard or video contain scenes with rapidly flashing colours."); /// /// "Letterbox during breaks" @@ -91,8 +87,7 @@ namespace osu.Game.Localisation /// /// "Adds horizontal letterboxing to give a cinematic look during breaks." /// - public static LocalisableString LetterboxDuringBreaksDescription => - new TranslatableString(getKey(@"letterbox_during_breaks_description"), @"Adds horizontal letterboxing to give a cinematic look during breaks."); + public static LocalisableString LetterboxDuringBreaksDescription => new TranslatableString(getKey(@"letterbox_during_breaks_description"), @"Adds horizontal letterboxing to give a cinematic look during breaks."); /// /// "Samples match playback rate" @@ -102,8 +97,7 @@ namespace osu.Game.Localisation /// /// "When enabled, all samples will speed up or slow down when rate-changing mods are enabled." /// - public static LocalisableString SamplesMatchPlaybackRateDescription => new TranslatableString(getKey(@"samples_match_playback_rate_description"), - @"When enabled, all samples will speed up or slow down when rate-changing mods are enabled."); + public static LocalisableString SamplesMatchPlaybackRateDescription => new TranslatableString(getKey(@"samples_match_playback_rate_description"), @"When enabled, all samples will speed up or slow down when rate-changing mods are enabled."); /// /// "The size of all hit objects" @@ -123,8 +117,17 @@ namespace osu.Game.Localisation /// /// "The harshness of hit windows and difficulty of special objects (ie. spinners)" /// - public static LocalisableString OverallDifficultyDescription => - new TranslatableString(getKey(@"overall_difficulty_description"), @"The harshness of hit windows and difficulty of special objects (ie. spinners)"); + public static LocalisableString OverallDifficultyDescription => new TranslatableString(getKey(@"overall_difficulty_description"), @"The harshness of hit windows and difficulty of special objects (ie. spinners)"); + + /// + /// "Tick Rate" + /// + public static LocalisableString TickRate => new TranslatableString(getKey(@"tick_rate"), @"Tick Rate"); + + /// + /// "Determines how many "ticks" are generated within long hit objects. A tick rate of 1 will generate ticks on each beat, 2 would be twice per beat, etc." + /// + public static LocalisableString TickRateDescription => new TranslatableString(getKey(@"tick_rate_description"), @"Determines how many ""ticks"" are generated within long hit objects. A tick rate of 1 will generate ticks on each beat, 2 would be twice per beat, etc."); /// /// "Base Velocity" diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 3a3fe7f747..4c062b0cb7 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -20,6 +20,7 @@ namespace osu.Game.Screens.Edit.Setup private LabelledSliderBar approachRateSlider = null!; private LabelledSliderBar overallDifficultySlider = null!; private LabelledSliderBar baseVelocitySlider = null!; + private LabelledSliderBar tickRateSlider = null!; public override LocalisableString Title => EditorSetupStrings.DifficultyHeader; @@ -93,6 +94,19 @@ namespace osu.Game.Screens.Edit.Setup Precision = 0.01f, } }, + tickRateSlider = new LabelledSliderBar + { + Label = EditorSetupStrings.TickRate, + FixedLabelWidth = LABEL_WIDTH, + Description = EditorSetupStrings.TickRateDescription, + Current = new BindableDouble(Beatmap.Difficulty.SliderTickRate) + { + Default = 1, + MinValue = 1, + MaxValue = 4, + Precision = 1, + } + }, }; foreach (var item in Children.OfType>()) @@ -111,6 +125,7 @@ namespace osu.Game.Screens.Edit.Setup Beatmap.Difficulty.ApproachRate = approachRateSlider.Current.Value; Beatmap.Difficulty.OverallDifficulty = overallDifficultySlider.Current.Value; Beatmap.Difficulty.SliderMultiplier = baseVelocitySlider.Current.Value; + Beatmap.Difficulty.SliderTickRate = tickRateSlider.Current.Value; Beatmap.UpdateAllHitObjects(); Beatmap.SaveState(); From 84efddcbc7cb09db82ccb58d2a57bd80c1b9c1fd Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 9 May 2023 15:53:36 +0200 Subject: [PATCH 037/195] Fix default progress graph flipping with a hack --- osu.Game/Screens/Play/SquareGraph.cs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 57b7c84e89..8636f08b00 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -75,18 +75,28 @@ namespace osu.Game.Screens.Play private Vector2 previousDrawSize; + private Vector2 previousParentScale; + protected override void Update() { base.Update(); - if (graphNeedsUpdate || (values != null && DrawSize != previousDrawSize)) + bool hasFlipped = previousParentScale != Parent.Scale; + if (graphNeedsUpdate || (values != null && DrawSize != previousDrawSize) || hasFlipped) { - columns?.FadeOut(500, Easing.OutQuint).Expire(); - scheduledCreate?.Cancel(); - scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); + + if (!hasFlipped) + { + scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); + } + else + { + RecreateGraph(); + } previousDrawSize = DrawSize; + previousParentScale = Parent.Scale; graphNeedsUpdate = false; } } From ad80e2ff5119322dfadaadfa57622bd6aca5afbe Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 9 May 2023 17:15:54 +0200 Subject: [PATCH 038/195] More robust implementation --- osu.Game/Screens/Play/SquareGraph.cs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 8636f08b00..a498917504 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -81,20 +81,31 @@ namespace osu.Game.Screens.Play { base.Update(); - bool hasFlipped = previousParentScale != Parent.Scale; - if (graphNeedsUpdate || (values != null && DrawSize != previousDrawSize) || hasFlipped) + bool extraUpdateConditions = + DrawSize != previousDrawSize || + previousParentScale != Parent.Scale; + + if (graphNeedsUpdate || (values != null && extraUpdateConditions)) { - scheduledCreate?.Cancel(); + bool hasFlipped = + previousParentScale.X == -Parent.Scale.X || + previousParentScale.Y == -Parent.Scale.Y; if (!hasFlipped) { - scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); + columns?.FadeOut(500, Easing.OutQuint).Expire(); } else { - RecreateGraph(); + if (columns != null) + { + columns.Alpha = 0.0f; + } } + scheduledCreate?.Cancel(); + scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); + previousDrawSize = DrawSize; previousParentScale = Parent.Scale; graphNeedsUpdate = false; From 745341ca6b4b5c1ed40b362a2fece9af58a7af86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 May 2023 21:55:53 +0200 Subject: [PATCH 039/195] Fix iOS build workflow failing A new version of `Microsoft.iOS.Sdk` was released on 2023-05-09T17:28:41.7300000. This version requires Xcode 14.3, which is not currently available on `macos-latest` runners (which currently alias to `macos-12`). To fix, migrate to `macos-13`, which is currently in beta, and explicitly use Xcode 14.3 (because even on the `macos-13` image, Xcode 14.3 is not yet the default). --- .github/workflows/ci.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e60e0a39ae..a8167ec4db 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -121,12 +121,24 @@ jobs: build-only-ios: name: Build only (iOS) - runs-on: macos-latest + # `macos-13` is required, because Xcode 14.3 is required (see below). + # TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta) + runs-on: macos-13 timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@v3 + # newest Microsoft.iOS.Sdk versions require Xcode 14.3. + # 14.3 is currently not the default Xcode version (https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode), + # so set it manually. + # TODO: remove when 14.3 becomes the default Xcode version. + - name: Set Xcode version + shell: bash + run: | + sudo xcode-select -s "/Applications/Xcode_14.3.app" + echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.3.app" >> $GITHUB_ENV + - name: Install .NET 6.0.x uses: actions/setup-dotnet@v3 with: From 4aa241daeab7d12eb8a32f1bb1da78ed5749a5c8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 May 2023 15:19:43 +0900 Subject: [PATCH 040/195] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e9ecbaa10b..ff76e17184 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 7ab9810ab5..93f22a31d8 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index bfa0dc63bb..c5477f765e 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 3fc19553b4435211370cfd2655ed35992ae95aa7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 May 2023 15:19:46 +0900 Subject: [PATCH 041/195] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 93f22a31d8..4315f44e07 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From c1a85658b728614be2f25fbca7d8dda1916865f3 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 11 May 2023 14:58:17 +0100 Subject: [PATCH 042/195] feat(settings): add "import" as keywords for first run setup --- osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index c5274d6223..b12541b261 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Settings.Sections new SettingsButton { Text = GeneralSettingsStrings.RunSetupWizard, - Keywords = new[] { @"first run", @"initial", @"getting started" }, + Keywords = new[] { @"first run", @"initial", @"getting started", @"import" }, TooltipText = FirstRunSetupOverlayStrings.FirstRunSetupDescription, Action = () => firstRunSetupOverlay?.Show(), }, From 2d45d602a538532245f9fe4287700e27493e962b Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 11 May 2023 15:03:42 +0100 Subject: [PATCH 043/195] feat(settings): more keyword for first run setup --- osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index b12541b261..f4a79d65e6 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Settings.Sections new SettingsButton { Text = GeneralSettingsStrings.RunSetupWizard, - Keywords = new[] { @"first run", @"initial", @"getting started", @"import" }, + Keywords = new[] { @"first run", @"initial", @"getting started", @"import", @"tutorial", @"recommended beatmaps" }, TooltipText = FirstRunSetupOverlayStrings.FirstRunSetupDescription, Action = () => firstRunSetupOverlay?.Show(), }, From 4732c8a06c73c8e8e63673f6bbf21c7dd45c1b92 Mon Sep 17 00:00:00 2001 From: alix Date: Thu, 11 May 2023 13:46:07 -0400 Subject: [PATCH 044/195] fix angle sharpness slider value from not always scaling by 0.5 --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 307d731fd4..b8ce667398 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Osu.Mods { MinValue = 1, MaxValue = 10, - Precision = 0.1f + Precision = 0.5f }; private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; From 3bce7ac58931806ddaa81191b04e744ffbdef680 Mon Sep 17 00:00:00 2001 From: js1086 Date: Thu, 11 May 2023 19:07:22 +0100 Subject: [PATCH 045/195] Copy SliderVelocity to strict tracking sliders --- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index 7e4ffc7408..72031b4958 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -98,6 +98,7 @@ namespace osu.Game.Rulesets.Osu.Mods ComboOffset = original.ComboOffset; LegacyLastTickOffset = original.LegacyLastTickOffset; TickDistanceMultiplier = original.TickDistanceMultiplier; + SliderVelocity = original.SliderVelocity; } protected override void CreateNestedHitObjects(CancellationToken cancellationToken) From a4954e64acb288a3a3ca1d6213ef63f68d812a64 Mon Sep 17 00:00:00 2001 From: alix Date: Thu, 11 May 2023 23:45:41 -0400 Subject: [PATCH 046/195] fix precision from making mouse input also go 0.5 --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index b8ce667398..89767b29c1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,18 +22,27 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Mod that randomises the positions of the s /// + + public partial class AngleSharpnessSlider : SettingsSlider + { + public AngleSharpnessSlider() + { + KeyboardStep = 0.5f; + } + } + public class OsuModRandom : ModRandom, IApplicableToBeatmap { public override LocalisableString Description => "It never gets boring!"; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray(); - [SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(SettingsSlider))] + [SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(AngleSharpnessSlider))] public BindableFloat AngleSharpness { get; } = new BindableFloat(7) { MinValue = 1, MaxValue = 10, - Precision = 0.5f + Precision = 0.1f }; private static readonly float playfield_diagonal = OsuPlayfield.BASE_SIZE.LengthFast; From e50cab9e81196405011654019cd003a795018789 Mon Sep 17 00:00:00 2001 From: alix Date: Thu, 11 May 2023 23:48:31 -0400 Subject: [PATCH 047/195] move class to the bottom of the file --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 89767b29c1..3d19a4d6eb 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,15 +22,6 @@ namespace osu.Game.Rulesets.Osu.Mods /// /// Mod that randomises the positions of the s /// - - public partial class AngleSharpnessSlider : SettingsSlider - { - public AngleSharpnessSlider() - { - KeyboardStep = 0.5f; - } - } - public class OsuModRandom : ModRandom, IApplicableToBeatmap { public override LocalisableString Description => "It never gets boring!"; @@ -170,5 +161,13 @@ namespace osu.Game.Rulesets.Osu.Mods return previousObjectStartedCombo && random.NextDouble() < 0.6f; } + + public partial class AngleSharpnessSlider : SettingsSlider + { + public AngleSharpnessSlider() + { + KeyboardStep = 0.5f; + } + } } } From 53ab780796fdb4a83366e84e5c00b58be3ac1cb1 Mon Sep 17 00:00:00 2001 From: alix Date: Thu, 11 May 2023 23:50:45 -0400 Subject: [PATCH 048/195] fix indents --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 3d19a4d6eb..61fb9189bc 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -164,9 +164,9 @@ namespace osu.Game.Rulesets.Osu.Mods public partial class AngleSharpnessSlider : SettingsSlider { - public AngleSharpnessSlider() + public AngleSharpnessSlider() { - KeyboardStep = 0.5f; + KeyboardStep = 0.5f; } } } From b3a5e4d3052d4d97d4a97779f561359bf92c3217 Mon Sep 17 00:00:00 2001 From: alix Date: Fri, 12 May 2023 00:29:00 -0400 Subject: [PATCH 049/195] nest class outside of OsuModRandom --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 61fb9189bc..6affeb24d8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -161,13 +161,13 @@ namespace osu.Game.Rulesets.Osu.Mods return previousObjectStartedCombo && random.NextDouble() < 0.6f; } + } - public partial class AngleSharpnessSlider : SettingsSlider + public partial class AngleSharpnessSlider : SettingsSlider + { + public AngleSharpnessSlider() { - public AngleSharpnessSlider() - { - KeyboardStep = 0.5f; - } + KeyboardStep = 0.5f; } } } From c0f869e685970af218ef68640cabe755f2f93db5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 May 2023 14:57:54 +0900 Subject: [PATCH 050/195] Fix some tablet settings being hidden when searching using "area" keyword As discussed in https://github.com/ppy/osu/discussions/23504. --- osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs index 951cf3802f..4c9320c2a6 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/TabletSettings.cs @@ -3,6 +3,8 @@ #nullable disable +using System.Collections.Generic; +using System.Linq; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -23,6 +25,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input { public partial class TabletSettings : SettingsSubsection { + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "area" }); + public TabletAreaSelection AreaSelection { get; private set; } private readonly ITabletHandler tabletHandler; From f443cfb93e41c4c33e3f51bccd1583b382b5dd3a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 May 2023 16:00:40 +0900 Subject: [PATCH 051/195] Move blueprint validity conditions to allow more correct external usage of `EndPlacement` Until now, these were haphazardly enforce inline in blueprint implementations. The only thing stopping complete breakage is that `EndPlacement` wasn't called (too much) from outside the blueprint, leaving them responsible for their own placement. By moving this conditional out of the provided paramters to `EndPlacement`, it allows more flexible usage of that method externally. Coming in a future PR. --- .../Blueprints/BananaShowerPlacementBlueprint.cs | 4 +++- .../Blueprints/JuiceStreamPlacementBlueprint.cs | 4 +++- .../Edit/Blueprints/HoldNotePlacementBlueprint.cs | 4 +++- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 4 +++- .../Edit/Blueprints/TaikoSpanPlacementBlueprint.cs | 4 +++- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 13 +++++++++++-- 6 files changed, 26 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs index 5f22ef5c12..1e63d32c41 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private double placementStartTime; private double placementEndTime; + protected override bool IsValidForPlacement => HitObject.Duration > 0; + public BananaShowerPlacementBlueprint() { InternalChild = outline = new TimeSpanOutline(); @@ -49,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints case PlacementState.Active: if (e.Button != MouseButton.Right) break; - EndPlacement(HitObject.Duration > 0); + EndPlacement(true); return true; } diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 03ec674abb..9e50b5a80f 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private InputManager inputManager = null!; + protected override bool IsValidForPlacement => HitObject.Duration > 0; + public JuiceStreamPlacementBlueprint() { InternalChildren = new Drawable[] @@ -70,7 +72,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints return true; case MouseButton.Right: - EndPlacement(HitObject.Duration > 0); + EndPlacement(true); return true; } diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 21beee0769..381af8be7f 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } + protected override bool IsValidForPlacement => HitObject.Duration > 0; + public HoldNotePlacementBlueprint() : base(new HoldNote()) { @@ -75,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints return; base.OnMouseUp(e); - EndPlacement(HitObject.Duration > 0); + EndPlacement(true); } private double originalStartTime; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 50514865e1..28ceb80627 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -41,6 +41,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private IDistanceSnapProvider snapProvider { get; set; } + protected override bool IsValidForPlacement => HitObject.Path.HasValidLength; + public SliderPlacementBlueprint() : base(new Slider()) { @@ -150,7 +152,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void endCurve() { updateSlider(); - EndPlacement(HitObject.Path.HasValidLength); + EndPlacement(true); } protected override void Update() diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs index fcf2573d64..bc4129c982 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints private readonly IHasDuration spanPlacementObject; + protected override bool IsValidForPlacement => spanPlacementObject.Duration > 0; + public TaikoSpanPlacementBlueprint(HitObject hitObject) : base(hitObject) { @@ -73,7 +75,7 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints return; base.OnMouseUp(e); - EndPlacement(spanPlacementObject.Duration > 0); + EndPlacement(true); } public override void UpdateTimeAndPosition(SnapResult result) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index bdcb334738..253d59751d 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -47,6 +47,15 @@ namespace osu.Game.Rulesets.Edit [Resolved] private IPlacementHandler placementHandler { get; set; } + /// + /// Whether this blueprint is currently in a state that can be committed. + /// + /// + /// Override this with any preconditions that should be double-checked on committing. + /// If false is returned and a commit is attempted, the blueprint will be destroyed instead. + /// + protected virtual bool IsValidForPlacement => true; + protected PlacementBlueprint(HitObject hitObject) { HitObject = hitObject; @@ -88,7 +97,7 @@ namespace osu.Game.Rulesets.Edit /// Signals that the placement of has finished. /// This will destroy this , and add the HitObject.StartTime to the . /// - /// Whether the object should be committed. + /// Whether the object should be committed. Note that a commit may fail if is false. public void EndPlacement(bool commit) { switch (PlacementActive) @@ -102,7 +111,7 @@ namespace osu.Game.Rulesets.Edit break; } - placementHandler.EndPlacement(HitObject, commit); + placementHandler.EndPlacement(HitObject, IsValidForPlacement && commit); PlacementActive = PlacementState.Finished; } From 70e248b92790da42e5d74d4f966407af2c2e5e0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 May 2023 15:48:53 +0900 Subject: [PATCH 052/195] Force placement of in-progress object when changing tools in the editor --- .../Components/ComposeBlueprintContainer.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 453e4b9130..07fb450628 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -317,12 +317,16 @@ namespace osu.Game.Screens.Edit.Compose.Components } } + private void commitIfPlacementActive() + { + CurrentPlacement?.EndPlacement(CurrentPlacement.PlacementActive == PlacementBlueprint.PlacementState.Active); + removePlacement(); + } + private void removePlacement() { - if (CurrentPlacement == null) return; - - CurrentPlacement.EndPlacement(false); - CurrentPlacement.Expire(); + CurrentPlacement?.EndPlacement(false); + CurrentPlacement?.Expire(); CurrentPlacement = null; } @@ -342,7 +346,8 @@ namespace osu.Game.Screens.Edit.Compose.Components currentTool = value; - refreshTool(); + // As per stable editor, when changing tools, we should forcefully commit any pending placement. + commitIfPlacementActive(); } } } From 0c1959ef2ff93e2e743a6a1a32b74e18c4028d5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 12 May 2023 15:50:33 +0900 Subject: [PATCH 053/195] Allow commiting / undoing placement of blueprints using back / select bindings --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 28 +++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 253d59751d..12c0ea1807 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -9,10 +9,12 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; @@ -24,7 +26,7 @@ namespace osu.Game.Rulesets.Edit /// /// A blueprint which governs the creation of a new to actualisation. /// - public abstract partial class PlacementBlueprint : CompositeDrawable + public abstract partial class PlacementBlueprint : CompositeDrawable, IKeyBindingHandler { /// /// Whether the is currently mid-placement, but has not necessarily finished being placed. @@ -115,6 +117,30 @@ namespace osu.Game.Rulesets.Edit PlacementActive = PlacementState.Finished; } + public bool OnPressed(KeyBindingPressEvent e) + { + if (PlacementActive == PlacementState.Waiting) + return false; + + switch (e.Action) + { + case GlobalAction.Select: + EndPlacement(true); + return true; + + case GlobalAction.Back: + EndPlacement(false); + return true; + + default: + return false; + } + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + /// /// Updates the time and position of this based on the provided snap information. /// From 6180d0d620bcf6f49ec8e22168d23c5a7d7bb465 Mon Sep 17 00:00:00 2001 From: timiimit Date: Fri, 12 May 2023 15:00:46 +0200 Subject: [PATCH 054/195] Remove cached frame buffer --- osu.Game/Screens/Play/SquareGraph.cs | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 57b7c84e89..26283858a6 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -51,7 +51,8 @@ namespace osu.Game.Screens.Play if (value == values) return; values = value; - graphNeedsUpdate = true; + scheduledCreate?.Cancel(); + scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); } } @@ -70,27 +71,6 @@ namespace osu.Game.Screens.Play } private ScheduledDelegate scheduledCreate; - - private bool graphNeedsUpdate; - - private Vector2 previousDrawSize; - - protected override void Update() - { - base.Update(); - - if (graphNeedsUpdate || (values != null && DrawSize != previousDrawSize)) - { - columns?.FadeOut(500, Easing.OutQuint).Expire(); - - scheduledCreate?.Cancel(); - scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); - - previousDrawSize = DrawSize; - graphNeedsUpdate = false; - } - } - private CancellationTokenSource cts; /// @@ -98,7 +78,7 @@ namespace osu.Game.Screens.Play /// protected virtual void RecreateGraph() { - var newColumns = new BufferedContainer(cachedFrameBuffer: true) + var newColumns = new BufferedContainer { RedrawOnScale = false, RelativeSizeAxes = Axes.Both, From 159cacf9c7299dfa72cf091bb31dc99030879d4b Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 01:27:28 +0200 Subject: [PATCH 055/195] Fix logic in recalculateValues() --- osu.Game/Screens/Play/SquareGraph.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 26283858a6..d521bb5448 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -139,18 +139,19 @@ namespace osu.Game.Screens.Play if (values == null) { for (float i = 0; i < ColumnCount; i++) + { newValues.Add(0); - - return; } - + } + else + { int max = values.Max(); - float step = values.Length / (float)ColumnCount; for (float i = 0; i < values.Length; i += step) { newValues.Add((float)values[(int)i] / max); + } } calculatedValues = newValues.ToArray(); From 21d7c62f303c2064dd99ae305d19c409f87cd4c6 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 01:29:11 +0200 Subject: [PATCH 056/195] Add optimized UpdateGraph --- osu.Game/Screens/Play/SquareGraph.cs | 122 +++++++++++++++++++-------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index d521bb5448..dd964b8908 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -16,6 +16,7 @@ using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Allocation; using osu.Framework.Threading; +using osu.Framework.Layout; namespace osu.Game.Screens.Play { @@ -23,6 +24,8 @@ namespace osu.Game.Screens.Play { private BufferedContainer columns; + private readonly LayoutValue layout = new LayoutValue(Invalidation.DrawSize | Invalidation.DrawInfo); + public int ColumnCount => columns?.Children.Count ?? 0; private int progress; @@ -51,11 +54,13 @@ namespace osu.Game.Screens.Play if (value == values) return; values = value; - scheduledCreate?.Cancel(); - scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); + haveValuesChanged = true; + layout.Invalidate(); } } + bool haveValuesChanged; + private Color4 fillColour; public Color4 FillColour @@ -70,43 +75,93 @@ namespace osu.Game.Screens.Play } } - private ScheduledDelegate scheduledCreate; - private CancellationTokenSource cts; - - /// - /// Recreates the entire graph. - /// - protected virtual void RecreateGraph() + public SquareGraph() { - var newColumns = new BufferedContainer + AddLayout(layout); + } + + [BackgroundDependencyLoader] + private void load() + { + Child = columns = new BufferedContainer(cachedFrameBuffer: true) { RedrawOnScale = false, - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.Both }; + } - for (float x = 0; x < DrawWidth; x += Column.WIDTH) + + // private Vector2 parentScale; + + protected override void Update() + { + base.Update(); + + if (!layout.IsValid) { - newColumns.Add(new Column(DrawHeight) + UpdateGraph(); + layout.Validate(); + } + } + + /// + /// Updates the graph by either adding or removing columns based on DrawWidth. + /// Does nothing if correct number of columns already exists and/or if haven't changed. + /// + protected virtual void UpdateGraph() + { + int targetColumnCount = values == null ? 0 : (int)(DrawWidth / Column.WIDTH); + + // early exit the most frequent case + if (!haveValuesChanged && targetColumnCount == ColumnCount) + { + columns.ForceRedraw(); + return; + } + + ensureColumnCount(targetColumnCount); + + // fill graph data + recalculateValues(); + redrawFilled(); + redrawProgress(); + + haveValuesChanged = false; + } + + private void ensureColumnCount(int targetColumnCount) + { + // remove excess columns + while (targetColumnCount < ColumnCount) + { + columns.Remove(columns.Children[ColumnCount - 1], true); + } + + // update height of existing columns + foreach (var column in columns) + { + column.Height = DrawHeight; + } + + // add missing columns + float x = ColumnCount * Column.WIDTH; + while (targetColumnCount > ColumnCount) + { + var column = new Column() { + Height = DrawHeight, LitColour = fillColour, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Position = new Vector2(x, 0), State = ColumnState.Dimmed, - }); + }; + + LoadComponentAsync(column); + columns.Add(column); + + x += Column.WIDTH; } - - cts?.Cancel(); - - LoadComponentAsync(newColumns, c => - { - Child = columns = c; - columns.FadeInFromZero(500, Easing.OutQuint); - - recalculateValues(); - redrawFilled(); - redrawProgress(); - }, (cts = new CancellationTokenSource()).Token); } /// @@ -141,16 +196,16 @@ namespace osu.Game.Screens.Play for (float i = 0; i < ColumnCount; i++) { newValues.Add(0); - } + } } else { - int max = values.Max(); - float step = values.Length / (float)ColumnCount; + int max = values.Max(); + float step = values.Length / (float)ColumnCount; - for (float i = 0; i < values.Length; i += step) - { - newValues.Add((float)values[(int)i] / max); + for (float i = 0; i < values.Length; i += step) + { + newValues.Add((float)values[(int)i] / max); } } @@ -203,10 +258,9 @@ namespace osu.Game.Screens.Play } } - public Column(float height) + public Column() { Width = WIDTH; - Height = height; } [BackgroundDependencyLoader] From 04f7def798e365660a8e6e721c6309ba469f6a98 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 01:31:12 +0200 Subject: [PATCH 057/195] Remove useless comment --- osu.Game/Screens/Play/SquareGraph.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index dd964b8908..7e8a8952cc 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -90,9 +90,6 @@ namespace osu.Game.Screens.Play }; } - - // private Vector2 parentScale; - protected override void Update() { base.Update(); From d39d552660d79ea2652c15e9201f2d65034e6d10 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 01:35:48 +0200 Subject: [PATCH 058/195] Update test --- .../Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs index 66671a506f..4e57a255be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs @@ -63,10 +63,12 @@ namespace osu.Game.Tests.Visual.Gameplay { public int CreationCount { get; private set; } - protected override void RecreateGraph() + protected override void UpdateGraph() { - base.RecreateGraph(); - CreationCount++; + base.UpdateGraph(); + + if (base.ColumnCount > 0) + CreationCount++; } } } From 8cc0c5ad1ca7b27c13651a48f5ab5ebe9c673cc5 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 01:39:01 +0200 Subject: [PATCH 059/195] Fix code style --- osu.Game/Screens/Play/SquareGraph.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 7e8a8952cc..5ea17a7518 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using osu.Framework; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,7 +14,6 @@ using osuTK; using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Allocation; -using osu.Framework.Threading; using osu.Framework.Layout; namespace osu.Game.Screens.Play @@ -59,7 +57,7 @@ namespace osu.Game.Screens.Play } } - bool haveValuesChanged; + private bool haveValuesChanged; private Color4 fillColour; @@ -93,7 +91,7 @@ namespace osu.Game.Screens.Play protected override void Update() { base.Update(); - + if (!layout.IsValid) { UpdateGraph(); From 80b6e014f16747173aa4240aebb709fda1187b82 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 01:47:44 +0200 Subject: [PATCH 060/195] More code style --- .../Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs | 2 +- osu.Game/Screens/Play/SquareGraph.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs index 4e57a255be..8d0c26996c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs @@ -67,7 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay { base.UpdateGraph(); - if (base.ColumnCount > 0) + if (ColumnCount > 0) CreationCount++; } } diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 5ea17a7518..27c6cae2fc 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -140,9 +140,10 @@ namespace osu.Game.Screens.Play // add missing columns float x = ColumnCount * Column.WIDTH; + while (targetColumnCount > ColumnCount) { - var column = new Column() + var column = new Column { Height = DrawHeight, LitColour = fillColour, From 2cfc4eb515d50dcdd387020764bcad744c9bb9f5 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 10:12:46 +0200 Subject: [PATCH 061/195] Fix unconsidered height change --- osu.Game/Screens/Play/SquareGraph.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 27c6cae2fc..d5d2f1d3c2 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -110,6 +110,7 @@ namespace osu.Game.Screens.Play // early exit the most frequent case if (!haveValuesChanged && targetColumnCount == ColumnCount) { + updateColumnHeight(); columns.ForceRedraw(); return; } @@ -124,6 +125,14 @@ namespace osu.Game.Screens.Play haveValuesChanged = false; } + private void updateColumnHeight() + { + foreach (var column in columns) + { + column.Height = DrawHeight; + } + } + private void ensureColumnCount(int targetColumnCount) { // remove excess columns @@ -132,11 +141,7 @@ namespace osu.Game.Screens.Play columns.Remove(columns.Children[ColumnCount - 1], true); } - // update height of existing columns - foreach (var column in columns) - { - column.Height = DrawHeight; - } + updateColumnHeight(); // add missing columns float x = ColumnCount * Column.WIDTH; From 4b544903cb7bcd768aab655e34f40da0285785b6 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 10:13:09 +0200 Subject: [PATCH 062/195] Optimize recalculateValues --- osu.Game/Screens/Play/SquareGraph.cs | 33 +++++++++++++--------------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index d5d2f1d3c2..061d1f7103 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -190,27 +190,24 @@ namespace osu.Game.Screens.Play /// private void recalculateValues() { - var newValues = new List(); - - if (values == null) + int columnCount = ColumnCount; + if (values == null || values.Length == 0 || columnCount == 0) { - for (float i = 0; i < ColumnCount; i++) - { - newValues.Add(0); - } - } - else - { - int max = values.Max(); - float step = values.Length / (float)ColumnCount; - - for (float i = 0; i < values.Length; i += step) - { - newValues.Add((float)values[(int)i] / max); - } + calculatedValues = new float[0]; + return; } - calculatedValues = newValues.ToArray(); + float ratio = values.Length / (float)columnCount; + + if (calculatedValues.Length != columnCount) + calculatedValues = new float[columnCount]; + + float max = (float)values.Max(); + + for (int i = 0; i < calculatedValues.Length; i++) + { + calculatedValues[i] = values[(int)(i * ratio)] / max; + } } public partial class Column : Container, IStateful From d81cdacb0d30368cfc1ab092c0465936c5fabe56 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 11:28:05 +0200 Subject: [PATCH 063/195] Undo stupid changes --- .../TestSceneDefaultSongProgressGraph.cs | 8 +- osu.Game/Screens/Play/SquareGraph.cs | 126 +++++++----------- 2 files changed, 51 insertions(+), 83 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs index 8d0c26996c..66671a506f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDefaultSongProgressGraph.cs @@ -63,12 +63,10 @@ namespace osu.Game.Tests.Visual.Gameplay { public int CreationCount { get; private set; } - protected override void UpdateGraph() + protected override void RecreateGraph() { - base.UpdateGraph(); - - if (ColumnCount > 0) - CreationCount++; + base.RecreateGraph(); + CreationCount++; } } } diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 061d1f7103..037f2b5b78 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using osu.Framework; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -15,6 +16,7 @@ using osuTK.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Allocation; using osu.Framework.Layout; +using osu.Framework.Threading; namespace osu.Game.Screens.Play { @@ -22,8 +24,6 @@ namespace osu.Game.Screens.Play { private BufferedContainer columns; - private readonly LayoutValue layout = new LayoutValue(Invalidation.DrawSize | Invalidation.DrawInfo); - public int ColumnCount => columns?.Children.Count ?? 0; private int progress; @@ -52,13 +52,10 @@ namespace osu.Game.Screens.Play if (value == values) return; values = value; - haveValuesChanged = true; layout.Invalidate(); } } - private bool haveValuesChanged; - private Color4 fillColour; public Color4 FillColour @@ -73,96 +70,66 @@ namespace osu.Game.Screens.Play } } + private ScheduledDelegate scheduledCreate; + + private LayoutValue layout = new LayoutValue(Invalidation.DrawSize | Invalidation.DrawInfo); + public SquareGraph() { AddLayout(layout); } - [BackgroundDependencyLoader] - private void load() - { - Child = columns = new BufferedContainer(cachedFrameBuffer: true) - { - RedrawOnScale = false, - RelativeSizeAxes = Axes.Both - }; - } - protected override void Update() { base.Update(); if (!layout.IsValid) { - UpdateGraph(); + columns?.FadeOut(500, Easing.OutQuint).Expire(); + + scheduledCreate?.Cancel(); + scheduledCreate = Scheduler.AddDelayed(RecreateGraph, 500); + layout.Validate(); } } + private CancellationTokenSource cts; + /// - /// Updates the graph by either adding or removing columns based on DrawWidth. - /// Does nothing if correct number of columns already exists and/or if haven't changed. + /// Recreates the entire graph. /// - protected virtual void UpdateGraph() + protected virtual void RecreateGraph() { - int targetColumnCount = values == null ? 0 : (int)(DrawWidth / Column.WIDTH); - - // early exit the most frequent case - if (!haveValuesChanged && targetColumnCount == ColumnCount) + var newColumns = new BufferedContainer(cachedFrameBuffer: true) { - updateColumnHeight(); - columns.ForceRedraw(); - return; - } + RedrawOnScale = false, + RelativeSizeAxes = Axes.Both, + }; - ensureColumnCount(targetColumnCount); - - // fill graph data - recalculateValues(); - redrawFilled(); - redrawProgress(); - - haveValuesChanged = false; - } - - private void updateColumnHeight() - { - foreach (var column in columns) + for (float x = 0; x < DrawWidth; x += Column.WIDTH) { - column.Height = DrawHeight; - } - } - - private void ensureColumnCount(int targetColumnCount) - { - // remove excess columns - while (targetColumnCount < ColumnCount) - { - columns.Remove(columns.Children[ColumnCount - 1], true); - } - - updateColumnHeight(); - - // add missing columns - float x = ColumnCount * Column.WIDTH; - - while (targetColumnCount > ColumnCount) - { - var column = new Column + newColumns.Add(new Column(DrawHeight) { - Height = DrawHeight, LitColour = fillColour, Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, Position = new Vector2(x, 0), State = ColumnState.Dimmed, - }; - - LoadComponentAsync(column); - columns.Add(column); - - x += Column.WIDTH; + }); } + + cts?.Cancel(); + + LoadComponentAsync(newColumns, c => + { + Child = columns = c; + columns.FadeInFromZero(500, Easing.OutQuint); + + recalculateValues(); + redrawFilled(); + redrawProgress(); + }, (cts = new CancellationTokenSource()).Token); } /// @@ -190,24 +157,26 @@ namespace osu.Game.Screens.Play /// private void recalculateValues() { - int columnCount = ColumnCount; - if (values == null || values.Length == 0 || columnCount == 0) + var newValues = new List(); + + if (values == null) { - calculatedValues = new float[0]; + for (float i = 0; i < ColumnCount; i++) + newValues.Add(0); + return; } - float ratio = values.Length / (float)columnCount; + int max = values.Max(); - if (calculatedValues.Length != columnCount) - calculatedValues = new float[columnCount]; + float step = values.Length / (float)ColumnCount; - float max = (float)values.Max(); - - for (int i = 0; i < calculatedValues.Length; i++) + for (float i = 0; i < values.Length; i += step) { - calculatedValues[i] = values[(int)(i * ratio)] / max; + newValues.Add((float)values[(int)i] / max); } + + calculatedValues = newValues.ToArray(); } public partial class Column : Container, IStateful @@ -256,9 +225,10 @@ namespace osu.Game.Screens.Play } } - public Column() + public Column(float height) { Width = WIDTH; + Height = height; } [BackgroundDependencyLoader] From 18efdb0e4cb3d75fba4288fc7327a10a38214a82 Mon Sep 17 00:00:00 2001 From: timiimit Date: Sat, 13 May 2023 11:37:56 +0200 Subject: [PATCH 064/195] Make `layout` readonly --- osu.Game/Screens/Play/SquareGraph.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SquareGraph.cs b/osu.Game/Screens/Play/SquareGraph.cs index 037f2b5b78..b53e86a41b 100644 --- a/osu.Game/Screens/Play/SquareGraph.cs +++ b/osu.Game/Screens/Play/SquareGraph.cs @@ -72,7 +72,7 @@ namespace osu.Game.Screens.Play private ScheduledDelegate scheduledCreate; - private LayoutValue layout = new LayoutValue(Invalidation.DrawSize | Invalidation.DrawInfo); + private readonly LayoutValue layout = new LayoutValue(Invalidation.DrawSize | Invalidation.DrawInfo); public SquareGraph() { From 2e6fd4b321c6223ad1dccb3df1992c9ced5e4a0a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 May 2023 20:54:22 +0900 Subject: [PATCH 065/195] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ff76e17184..c73c643d4b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 4315f44e07..3ea4a57c2c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -36,7 +36,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index c5477f765e..a240dec963 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From 8c542c6c51134a7357a631f15a2d5fca0bfc5edc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 May 2023 21:12:21 +0900 Subject: [PATCH 066/195] Fix hold-for-right-click showing during gameplay --- osu.Game/Input/OsuUserInputManager.cs | 5 +++++ osu.Game/OsuGame.cs | 7 +++++++ osu.Game/Rulesets/UI/RulesetInputManager.cs | 2 ++ 3 files changed, 14 insertions(+) diff --git a/osu.Game/Input/OsuUserInputManager.cs b/osu.Game/Input/OsuUserInputManager.cs index ab43497156..c205636ab9 100644 --- a/osu.Game/Input/OsuUserInputManager.cs +++ b/osu.Game/Input/OsuUserInputManager.cs @@ -3,6 +3,7 @@ #nullable disable +using osu.Framework.Bindables; using osu.Framework.Input; using osuTK.Input; @@ -10,6 +11,10 @@ namespace osu.Game.Input { public partial class OsuUserInputManager : UserInputManager { + protected override bool AllowRightClickFromLongTouch => !LocalUserPlaying.Value; + + public readonly BindableBool LocalUserPlaying = new BindableBool(); + internal OsuUserInputManager() { } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7c9b03bd5b..fe6e479d19 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -269,6 +269,13 @@ namespace osu.Game if (hideToolbar) Toolbar.Hide(); } + protected override UserInputManager CreateUserInputManager() + { + var userInputManager = base.CreateUserInputManager(); + (userInputManager as OsuUserInputManager)?.LocalUserPlaying.BindTo(LocalUserPlaying); + return userInputManager; + } + private DependencyContainer dependencies; protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 2ae54a3afe..a24e22f22b 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.UI public abstract partial class RulesetInputManager : PassThroughInputManager, ICanAttachHUDPieces, IHasReplayHandler, IHasRecordingHandler where T : struct { + protected override bool AllowRightClickFromLongTouch => false; + public readonly KeyBindingContainer KeyBindingContainer; [Resolved(CanBeNull = true)] From 9a327d95b883323a21c649519f7cacae30e95832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 May 2023 11:24:24 +0200 Subject: [PATCH 067/195] Add test coverage --- .../Editing/TestScenePlacementBlueprint.cs | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs diff --git a/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs new file mode 100644 index 0000000000..58eff9ade7 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs @@ -0,0 +1,83 @@ +// 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.Screens; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Input.Bindings; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.UI; +using osu.Game.Screens.Edit.Compose.Components; +using osu.Game.Tests.Beatmaps; +using osuTK.Input; + +namespace osu.Game.Tests.Visual.Editing +{ + public partial class TestScenePlacementBlueprint : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new TestBeatmap(ruleset, false); + + private GlobalActionContainer globalActionContainer => this.ChildrenOfType().Single(); + + [Test] + public void TestCommitPlacementViaGlobalAction() + { + Playfield playfield = null!; + + AddStep("select slider placement tool", () => InputManager.Key(Key.Number3)); + AddStep("move mouse to top left of playfield", () => + { + playfield = this.ChildrenOfType().Single(); + var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4; + InputManager.MoveMouseTo(location); + }); + AddStep("begin placement", () => InputManager.Click(MouseButton.Left)); + AddStep("move mouse to bottom right of playfield", () => + { + var location = (playfield.ScreenSpaceDrawQuad.TopLeft + 3 * playfield.ScreenSpaceDrawQuad.BottomRight) / 4; + InputManager.MoveMouseTo(location); + }); + AddStep("confirm via global action", () => + { + globalActionContainer.TriggerPressed(GlobalAction.Select); + globalActionContainer.TriggerReleased(GlobalAction.Select); + }); + AddAssert("slider placed", () => EditorBeatmap.HitObjects.Count, () => Is.EqualTo(1)); + } + + [Test] + public void TestAbortPlacementViaGlobalAction() + { + Playfield playfield = null!; + + AddStep("select slider placement tool", () => InputManager.Key(Key.Number3)); + AddStep("move mouse to top left of playfield", () => + { + playfield = this.ChildrenOfType().Single(); + var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4; + InputManager.MoveMouseTo(location); + }); + AddStep("begin placement", () => InputManager.Click(MouseButton.Left)); + AddStep("move mouse to bottom right of playfield", () => + { + var location = (playfield.ScreenSpaceDrawQuad.TopLeft + 3 * playfield.ScreenSpaceDrawQuad.BottomRight) / 4; + InputManager.MoveMouseTo(location); + }); + AddStep("abort via global action", () => + { + globalActionContainer.TriggerPressed(GlobalAction.Back); + globalActionContainer.TriggerReleased(GlobalAction.Back); + }); + AddAssert("editor is still current", () => Editor.IsCurrentScreen()); + AddAssert("slider not placed", () => EditorBeatmap.HitObjects.Count, () => Is.EqualTo(0)); + AddAssert("no active placement", () => this.ChildrenOfType().Single().CurrentPlacement.PlacementActive, + () => Is.EqualTo(PlacementBlueprint.PlacementState.Waiting)); + } + } +} From a99bf0fc5a5befc1961e5e6d3eb175879a4973e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 May 2023 12:29:24 +0200 Subject: [PATCH 068/195] Add test coverage --- .../Editing/TestScenePlacementBlueprint.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs index 58eff9ade7..a5681bea4a 100644 --- a/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs +++ b/osu.Game.Tests/Visual/Editing/TestScenePlacementBlueprint.cs @@ -79,5 +79,28 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("no active placement", () => this.ChildrenOfType().Single().CurrentPlacement.PlacementActive, () => Is.EqualTo(PlacementBlueprint.PlacementState.Waiting)); } + + [Test] + public void TestCommitPlacementViaToolChange() + { + Playfield playfield = null!; + + AddStep("select slider placement tool", () => InputManager.Key(Key.Number3)); + AddStep("move mouse to top left of playfield", () => + { + playfield = this.ChildrenOfType().Single(); + var location = (3 * playfield.ScreenSpaceDrawQuad.TopLeft + playfield.ScreenSpaceDrawQuad.BottomRight) / 4; + InputManager.MoveMouseTo(location); + }); + AddStep("begin placement", () => InputManager.Click(MouseButton.Left)); + AddStep("move mouse to bottom right of playfield", () => + { + var location = (playfield.ScreenSpaceDrawQuad.TopLeft + 3 * playfield.ScreenSpaceDrawQuad.BottomRight) / 4; + InputManager.MoveMouseTo(location); + }); + + AddStep("change tool to circle", () => InputManager.Key(Key.Number2)); + AddAssert("slider placed", () => EditorBeatmap.HitObjects.Count, () => Is.EqualTo(1)); + } } } From d503312788f5e39173a0b2c94ba1b4f3af117f22 Mon Sep 17 00:00:00 2001 From: alix Date: Sun, 14 May 2023 13:37:44 -0400 Subject: [PATCH 069/195] updates and add changes --- osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index 6affeb24d8..3224ff9eaf 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -9,7 +9,6 @@ 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.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Beatmaps; @@ -28,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModTargetPractice)).ToArray(); - [SettingSource("Angle sharpness", "How sharp angles should be", SettingControlType = typeof(AngleSharpnessSlider))] + [SettingSource("Angle sharpness", "How sharp angles should be")] public BindableFloat AngleSharpness { get; } = new BindableFloat(7) { MinValue = 1, @@ -162,12 +161,4 @@ namespace osu.Game.Rulesets.Osu.Mods return previousObjectStartedCombo && random.NextDouble() < 0.6f; } } - - public partial class AngleSharpnessSlider : SettingsSlider - { - public AngleSharpnessSlider() - { - KeyboardStep = 0.5f; - } - } } From 26ed50d8fdf13996c749bb05eeb3e1d18239fd6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 May 2023 21:42:48 +0900 Subject: [PATCH 070/195] Add abbreviations found in framework functions Brings total inspections down to zero when using local framework checkout (in conjuncation with https://github.com/ppy/osu-framework/pull/5793). --- osu.sln.DotSettings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index d7486273fb..b54794cd6d 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -329,6 +329,7 @@ False AABB API + ARGB BPM EF FPS @@ -336,6 +337,7 @@ GL GLSL HID + HSL HSPA HSV HTML @@ -352,6 +354,7 @@ OS PM RGB + RGBA RNG SDL SHA From 10d6f9ea5a9e64eaa92aae278b6f8466485081a5 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 09:52:26 +0200 Subject: [PATCH 071/195] Create common online play pill --- .../Lounge/Components/OnlinePlayPill.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs new file mode 100644 index 0000000000..45a0fd2a1b --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs @@ -0,0 +1,37 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; + +namespace osu.Game.Screens.OnlinePlay.Lounge.Components +{ + public partial class OnlinePlayPill : OnlinePlayComposite + { + protected PillContainer pill; + protected OsuTextFlowContainer textFlow; + + public OnlinePlayPill() + { + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = pill = new PillContainer + { + Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) + { + AutoSizeAxes = Axes.Both, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft + } + }; + } + } +} From 04893064f06a673e60ce7e61bb23fb07571b41b7 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 09:53:11 +0200 Subject: [PATCH 072/195] Make used pills inherit from a common one --- .../Lounge/Components/MatchTypePill.cs | 23 +---------- .../Lounge/Components/PlaylistCountPill.cs | 33 +++------------ .../Lounge/Components/QueueModePill.cs | 27 +------------ .../Components/RoomSpecialCategoryPill.cs | 40 +++---------------- .../Lounge/Components/RoomStatusPill.cs | 32 ++------------- 5 files changed, 17 insertions(+), 138 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs index f96d547747..dd5f52155c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs @@ -13,29 +13,8 @@ using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class MatchTypePill : OnlinePlayComposite + public partial class MatchTypePill : OnlinePlayPill { - private OsuTextFlowContainer textFlow; - - public MatchTypePill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new PillContainer - { - Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 81ba48d135..c3118ab285 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -5,40 +5,17 @@ using System.Linq; using Humanizer; -using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Game.Graphics; -using osu.Game.Graphics.Containers; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// A pill that displays the playlist item count. /// - public partial class PlaylistCountPill : OnlinePlayComposite + public partial class PlaylistCountPill : OnlinePlayPill { - private OsuTextFlowContainer count; - - public PlaylistCountPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new PillContainer - { - Child = count = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -55,10 +32,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components ? Playlist.Count(i => !i.Expired) : PlaylistItemStats.Value.CountActive; - count.Clear(); - count.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); - count.AddText(" "); - count.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None)); + textFlow.Clear(); + textFlow.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + textFlow.AddText(" "); + textFlow.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None)); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs index 0175418a96..d37dede961 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs @@ -3,39 +3,14 @@ #nullable disable -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Online.Multiplayer; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class QueueModePill : OnlinePlayComposite + public partial class QueueModePill : OnlinePlayPill { - private OsuTextFlowContainer textFlow; - - public QueueModePill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = new PillContainer - { - Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) - { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index 5d67a18d1f..a01d14ab40 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -5,54 +5,26 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class RoomSpecialCategoryPill : OnlinePlayComposite + public partial class RoomSpecialCategoryPill : OnlinePlayPill { - private SpriteText text; - private PillContainer pill; - [Resolved] private OsuColour colours { get; set; } - public RoomSpecialCategoryPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = pill = new PillContainer - { - Background = - { - Colour = colours.Pink, - Alpha = 1 - }, - Child = text = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), - Colour = Color4.Black - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); + pill.Background.Colour = colours.Pink; + pill.Background.Alpha = 1; + Category.BindValueChanged(c => { - text.Text = c.NewValue.GetLocalisableDescription(); + textFlow.Clear(); + textFlow.AddText(c.NewValue.GetLocalisableDescription()); var backgroundColour = colours.ForRoomCategory(Category.Value); if (backgroundColour != null) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index 201314851e..a4fdd39236 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -6,46 +6,20 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { /// /// A pill that displays the room's current status. /// - public partial class RoomStatusPill : OnlinePlayComposite + public partial class RoomStatusPill : OnlinePlayPill { [Resolved] private OsuColour colours { get; set; } - private PillContainer pill; - private SpriteText statusText; - - public RoomStatusPill() - { - AutoSizeAxes = Axes.Both; - } - - [BackgroundDependencyLoader] - private void load() - { - InternalChild = pill = new PillContainer - { - Child = statusText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold, size: 12), - Colour = Color4.Black - } - }; - } - protected override void LoadComplete() { base.LoadComplete(); @@ -62,7 +36,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components pill.Background.Alpha = 1; pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); - statusText.Text = status.Message; + + textFlow.Clear(); + textFlow.AddText(status.Message); } private RoomStatus getDisplayStatus() From 40daa41a5272c78746f8c743ac0f72d0d0e917e4 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 10:06:48 +0200 Subject: [PATCH 073/195] Fix naming & namespaces --- .../OnlinePlay/Lounge/Components/MatchTypePill.cs | 8 ++------ .../OnlinePlay/Lounge/Components/OnlinePlayPill.cs | 8 ++++---- .../OnlinePlay/Lounge/Components/PlaylistCountPill.cs | 8 ++++---- .../OnlinePlay/Lounge/Components/QueueModePill.cs | 4 ++-- .../Lounge/Components/RoomSpecialCategoryPill.cs | 10 +++++----- .../OnlinePlay/Lounge/Components/RoomStatusPill.cs | 8 ++++---- 6 files changed, 21 insertions(+), 25 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs index dd5f52155c..e019de0f6f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs @@ -3,12 +3,8 @@ #nullable disable -using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; using osu.Game.Online.Rooms; namespace osu.Game.Screens.OnlinePlay.Lounge.Components @@ -24,8 +20,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onMatchTypeChanged(ValueChangedEvent type) { - textFlow.Clear(); - textFlow.AddText(type.NewValue.GetLocalisableDescription()); + TextFlow.Clear(); + TextFlow.AddText(type.NewValue.GetLocalisableDescription()); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs index 45a0fd2a1b..80e82c3856 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs @@ -12,8 +12,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public partial class OnlinePlayPill : OnlinePlayComposite { - protected PillContainer pill; - protected OsuTextFlowContainer textFlow; + protected PillContainer Pill; + protected OsuTextFlowContainer TextFlow; public OnlinePlayPill() { @@ -23,9 +23,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [BackgroundDependencyLoader] private void load() { - InternalChild = pill = new PillContainer + InternalChild = Pill = new PillContainer { - Child = textFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) + Child = TextFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) { AutoSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index c3118ab285..6687f30bc0 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -32,10 +32,10 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components ? Playlist.Count(i => !i.Expired) : PlaylistItemStats.Value.CountActive; - textFlow.Clear(); - textFlow.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); - textFlow.AddText(" "); - textFlow.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None)); + TextFlow.Clear(); + TextFlow.AddText(activeItems.ToLocalisableString(), s => s.Font = s.Font.With(weight: FontWeight.Bold)); + TextFlow.AddText(" "); + TextFlow.AddText("Beatmap".ToQuantity(activeItems, ShowQuantityAs.None)); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs index d37dede961..e402653c2f 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs @@ -20,8 +20,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onQueueModeChanged(ValueChangedEvent mode) { - textFlow.Clear(); - textFlow.AddText(mode.NewValue.GetLocalisableDescription()); + TextFlow.Clear(); + TextFlow.AddText(mode.NewValue.GetLocalisableDescription()); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index a01d14ab40..d266104cf5 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -18,17 +18,17 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - pill.Background.Colour = colours.Pink; - pill.Background.Alpha = 1; + Pill.Background.Colour = colours.Pink; + Pill.Background.Alpha = 1; Category.BindValueChanged(c => { - textFlow.Clear(); - textFlow.AddText(c.NewValue.GetLocalisableDescription()); + TextFlow.Clear(); + TextFlow.AddText(c.NewValue.GetLocalisableDescription()); var backgroundColour = colours.ForRoomCategory(Category.Value); if (backgroundColour != null) - pill.Background.Colour = backgroundColour.Value; + Pill.Background.Colour = backgroundColour.Value; }, true); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index a4fdd39236..fa5eba4b69 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -34,11 +34,11 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { RoomStatus status = getDisplayStatus(); - pill.Background.Alpha = 1; - pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); + Pill.Background.Alpha = 1; + Pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); - textFlow.Clear(); - textFlow.AddText(status.Message); + TextFlow.Clear(); + TextFlow.AddText(status.Message); } private RoomStatus getDisplayStatus() From a9991d9dd4bce7145fde56a8c4cae6c266313ae9 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 10:14:14 +0200 Subject: [PATCH 074/195] Remove another unused namespace --- .../Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs index 6687f30bc0..d1365c02f3 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/PlaylistCountPill.cs @@ -6,7 +6,6 @@ using System.Linq; using Humanizer; using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Graphics; using osu.Game.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components From 81b7737660260a2a5b3168afaf1c171278c1f5e9 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 10:20:16 +0200 Subject: [PATCH 075/195] Fix text colour of RoomSpecialCategoryPill --- .../OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index d266104cf5..b869ede9bd 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Game.Graphics; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { @@ -20,6 +21,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Pill.Background.Colour = colours.Pink; Pill.Background.Alpha = 1; + TextFlow.Colour = Color4.Black; Category.BindValueChanged(c => { From 4ef74eb3f960257d2693b23a33b6358d91a7f954 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 10:22:52 +0200 Subject: [PATCH 076/195] Fix `RoomStatusPill` text colour --- .../Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index fa5eba4b69..4206aaf167 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; +using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { @@ -28,6 +29,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components Status.BindValueChanged(_ => updateDisplay(), true); FinishTransforms(true); + + TextFlow.Colour = Colour4.Black; } private void updateDisplay() From cc6e4df93e3e1ec8ea06f14e434c1e4e0194b8d0 Mon Sep 17 00:00:00 2001 From: timiimit Date: Tue, 16 May 2023 10:27:46 +0200 Subject: [PATCH 077/195] Remove wrongly added back namepspace --- osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index 4206aaf167..f260d168a7 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -9,7 +9,6 @@ using osu.Framework.Graphics; using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; -using osuTK.Graphics; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { From 83dcd788262fb3cc51f94ddaa56f3fb1e202ef17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 May 2023 16:29:24 +0900 Subject: [PATCH 078/195] Make `HitSampleInfo.Bank` non-nullable --- .../Drawables/DrawablePippidonHitObject.cs | 3 +-- .../Drawables/DrawablePippidonHitObject.cs | 3 +-- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneSpinner.cs | 2 +- ...estSceneHitObjectSamplePointAdjustments.cs | 2 +- osu.Game/Audio/HitSampleInfo.cs | 7 ++++--- .../ControlPoints/SampleControlPoint.cs | 2 +- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 14 ++++++++----- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 +- .../Objects/Legacy/ConvertHitObjectParser.cs | 20 ++++++++++++++----- 10 files changed, 35 insertions(+), 22 deletions(-) diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index 29203f0a20..c5ada4288d 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osuTK; @@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables public override IEnumerable GetSamples() => new[] { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }; protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs index 554d03c79f..d198fa81cb 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon/Objects/Drawables/DrawablePippidonHitObject.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Game.Audio; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Pippidon.UI; using osu.Game.Rulesets.Scoring; @@ -44,7 +43,7 @@ namespace osu.Game.Rulesets.Pippidon.Objects.Drawables public override IEnumerable GetSamples() => new[] { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }; protected override void CheckForResult(bool userTriggered, double timeOffset) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 40dc7d2403..9a78df23cf 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests NewCombo = i % 8 == 0, Samples = new List(new[] { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 100) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: 100) }) }); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 74d0fb42a3..8cfd674f88 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Osu.Tests EndTime = Time.Current + delay + length, Samples = new List { - new HitSampleInfo("hitnormal") + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) } }; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs index 7a0418cfec..230333ec35 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs @@ -42,7 +42,7 @@ namespace osu.Game.Tests.Visual.Editing Position = (OsuPlayfield.BASE_SIZE - new Vector2(100, 0)) / 2, Samples = new List { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "normal", volume: 80) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: 80) } }); diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs index efa5562cb8..665ae3e1a7 100644 --- a/osu.Game/Audio/HitSampleInfo.cs +++ b/osu.Game/Audio/HitSampleInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Newtonsoft.Json; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Utils; namespace osu.Game.Audio @@ -32,7 +33,7 @@ namespace osu.Game.Audio /// /// The bank to load the sample from. /// - public readonly string? Bank; + public readonly string Bank; /// /// An optional suffix to provide priority lookup. Falls back to non-suffixed . @@ -44,7 +45,7 @@ namespace osu.Game.Audio /// public int Volume { get; } - public HitSampleInfo(string name, string? bank = null, string? suffix = null, int volume = 0) + public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 0) { Name = name; Bank = bank; @@ -76,7 +77,7 @@ namespace osu.Game.Audio /// An optional new volume. /// The new . public virtual HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) - => new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume)); + => new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank) ?? Bank, newSuffix.GetOr(Suffix), newVolume.GetOr(Volume)); public bool Equals(HitSampleInfo? other) => other != null && Name == other.Name && Bank == other.Bank && Suffix == other.Suffix; diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs index 6cd4d74a31..522a8b7892 100644 --- a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -69,7 +69,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// The . This will not be modified. /// The modified . This does not share a reference with . public virtual HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo) - => hitSampleInfo.With(newBank: hitSampleInfo.Bank ?? SampleBank, newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume); + => hitSampleInfo.With(newBank: hitSampleInfo.Bank, newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume); public override bool IsRedundant(ControlPoint? existing) => existing is SampleControlPoint existingSample diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 704756e3dd..23440b8a1d 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -226,12 +226,16 @@ namespace osu.Game.Beatmaps.Formats public override HitSampleInfo ApplyTo(HitSampleInfo hitSampleInfo) { - var baseInfo = base.ApplyTo(hitSampleInfo); + if (hitSampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy) + { + return legacy.With( + newCustomSampleBank: legacy.CustomSampleBank > 0 ? legacy.CustomSampleBank : CustomSampleBank, + newVolume: hitSampleInfo.Volume > 0 ? hitSampleInfo.Volume : SampleVolume, + newBank: legacy.BankSpecified ? legacy.Bank : SampleBank + ); + } - if (baseInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacy && legacy.CustomSampleBank == 0) - return legacy.With(newCustomSampleBank: CustomSampleBank); - - return baseInfo; + return base.ApplyTo(hitSampleInfo); } public override bool IsRedundant(ControlPoint? existing) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 12c0ea1807..eae9f922a4 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Edit HitObject = hitObject; // adding the default hit sample should be the case regardless of the ruleset. - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK, volume: 100)); + HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: 100)); RelativeSizeAxes = Axes.Both; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index ba5de6c14b..c0f02b0b5e 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -14,6 +14,7 @@ using System.Linq; using JetBrains.Annotations; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Utils; +using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Legacy; using osu.Game.Skinning; using osu.Game.Utils; @@ -446,9 +447,9 @@ namespace osu.Game.Rulesets.Objects.Legacy if (string.IsNullOrEmpty(bankInfo.Filename)) { soundTypes.Add(new LegacyHitSampleInfo(HitSampleInfo.HIT_NORMAL, bankInfo.BankForNormal, bankInfo.Volume, bankInfo.CustomSampleBank, - // if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample. - // None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds - type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal))); + // if the sound type doesn't have the Normal flag set, attach it anyway as a layered sample. + // None also counts as a normal non-layered sample: https://osu.ppy.sh/help/wiki/osu!_File_Formats/Osu_(file_format)#hitsounds + type != LegacyHitSoundType.None && !type.HasFlagFast(LegacyHitSoundType.Normal))); } else { @@ -479,12 +480,14 @@ namespace osu.Game.Rulesets.Objects.Legacy /// The bank identifier to use for the base ("hitnormal") sample. /// Transferred to when appropriate. /// + [CanBeNull] public string BankForNormal; /// /// The bank identifier to use for additions ("hitwhistle", "hitfinish", "hitclap"). /// Transferred to when appropriate. /// + [CanBeNull] public string BankForAdditions; /// @@ -518,10 +521,17 @@ namespace osu.Game.Rulesets.Objects.Legacy /// public readonly bool IsLayered; + /// + /// Whether a bank was specified locally to the relevant hitobject. + /// If false, a bank will be retrieved from the closest control point. + /// + public bool BankSpecified; + public LegacyHitSampleInfo(string name, string? bank = null, int volume = 0, int customSampleBank = 0, bool isLayered = false) - : base(name, bank, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume) + : base(name, bank ?? SampleControlPoint.DEFAULT_BANK, customSampleBank >= 2 ? customSampleBank.ToString() : null, volume) { CustomSampleBank = customSampleBank; + BankSpecified = !string.IsNullOrEmpty(bank); IsLayered = isLayered; } @@ -531,7 +541,7 @@ namespace osu.Game.Rulesets.Objects.Legacy public virtual LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, Optional newCustomSampleBank = default, Optional newIsLayered = default) - => new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered)); + => new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank) ?? Bank, newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered)); public bool Equals(LegacyHitSampleInfo? other) // The additions to equality checks here are *required* to ensure that pooling works correctly. From d9ae8229669b0f371810b5334ea563056642a735 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 May 2023 16:34:49 +0900 Subject: [PATCH 079/195] Fix bank not correctly being assigned when adding sample additions in editor --- osu.Game/Rulesets/Objects/HitObject.cs | 2 +- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 2 +- .../Screens/Edit/Compose/Components/EditorSelectionHandler.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index a4cb976d50..f5b9e9c8cd 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Objects /// /// The name of the sample. /// A populated . - protected HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) + public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) { var hitnormalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL); return hitnormalSample == null ? new HitSampleInfo(sampleName) : hitnormalSample.With(newName: sampleName); diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 07fb450628..73e3baf509 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -185,7 +185,7 @@ namespace osu.Game.Screens.Edit.Compose.Components case TernaryState.True: if (existingSample == null) - samples.Add(new HitSampleInfo(sampleName)); + samples.Add(CurrentPlacement.HitObject.GetSampleInfo(sampleName)); break; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 357cc940f2..694b24c567 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (h.Samples.Any(s => s.Name == sampleName)) return; - h.Samples.Add(new HitSampleInfo(sampleName)); + h.Samples.Add(h.GetSampleInfo(sampleName)); EditorBeatmap.Update(h); }); } From 31fff72eb6f8da11fb5f6d11d06775a1cd78f2ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 May 2023 16:35:58 +0900 Subject: [PATCH 080/195] Fix bank not correctly being assigned to some taiko hit cases --- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 787079bfee..d90c7016e9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Objects if (isRimType != rimSamples.Any()) { if (isRimType) - Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_CLAP)); + Samples.Add(GetSampleInfo(HitSampleInfo.HIT_CLAP)); else { foreach (var sample in rimSamples) From 8528fcaedc93bddd843be6fa6087cde82629f52e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 May 2023 18:31:10 +0900 Subject: [PATCH 081/195] Rename editor sample adjustment test scene to increase scope of tests --- ...intAdjustments.cs => TestSceneHitObjectSampleAdjustments.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Tests/Visual/Editing/{TestSceneHitObjectSamplePointAdjustments.cs => TestSceneHitObjectSampleAdjustments.cs} (99%) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs similarity index 99% rename from osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs rename to osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 230333ec35..6e3f403c19 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSamplePointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -24,7 +24,7 @@ using osuTK.Input; namespace osu.Game.Tests.Visual.Editing { - public partial class TestSceneHitObjectSamplePointAdjustments : EditorTestScene + public partial class TestSceneHitObjectSampleAdjustments : EditorTestScene { protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); From ebce39cd1c4ef3db38d6dbb92682252eb7c463ca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 May 2023 18:52:35 +0900 Subject: [PATCH 082/195] Add test coverage of failing sample bank transfer --- .../TestSceneHitObjectSampleAdjustments.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 6e3f403c19..530bd5eb20 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -58,6 +58,26 @@ namespace osu.Game.Tests.Visual.Editing }); } + [Test] + public void TestAddSampleAddition() + { + AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + + AddStep("add clap addition", () => InputManager.Key(Key.R)); + + hitObjectHasSampleBank(0, "normal"); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP); + hitObjectHasSampleBank(1, "soft"); + hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP); + + AddStep("remove clap addition", () => InputManager.Key(Key.R)); + + hitObjectHasSampleBank(0, "normal"); + hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); + hitObjectHasSampleBank(1, "soft"); + hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL); + } + [Test] public void TestPopoverHasFocus() { @@ -271,6 +291,12 @@ namespace osu.Game.Tests.Visual.Editing InputManager.Key(Key.Enter); }); + private void hitObjectHasSamples(int objectIndex, params string[] samples) => AddAssert($"{objectIndex.ToOrdinalWords()} has samples {string.Join(',', samples)}", () => + { + var h = EditorBeatmap.HitObjects.ElementAt(objectIndex); + return h.Samples.Select(s => s.Name).SequenceEqual(samples); + }); + private void hitObjectHasSampleBank(int objectIndex, string bank) => AddAssert($"{objectIndex.ToOrdinalWords()} has bank {bank}", () => { var h = EditorBeatmap.HitObjects.ElementAt(objectIndex); From ddfdd712bd61b32c1f00614a840156b234c2e070 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 17 May 2023 00:07:06 +0800 Subject: [PATCH 083/195] Update InspectCode to 2022.3.3 and enable C# 11 --- .config/dotnet-tools.json | 2 +- Directory.Build.props | 2 +- osu.Game/osu.Game.csproj | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 1f937e1837..d9f1e3985a 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.2.3", + "version": "2022.3.3", "commands": [ "jb" ] diff --git a/Directory.Build.props b/Directory.Build.props index 734374c840..fccac79a45 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 10.0 + 11.0 true enable diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3ea4a57c2c..bf0c1dde17 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,9 +1,8 @@ - + net6.0 Library true - 10 osu! From 84de463e2e9e11130fb8edd053796e1f722f15f6 Mon Sep 17 00:00:00 2001 From: Huo Yaoyuan Date: Wed, 17 May 2023 00:13:08 +0800 Subject: [PATCH 084/195] Turn off inspection of MSBuild unknown property --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index b54794cd6d..5039831f76 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -234,6 +234,7 @@ WARNING WARNING HINT + DO_NOT_SHOW WARNING HINT HINT From 88ce627c8ed5428b087e5c25c16a541cd1e66b11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 May 2023 20:40:12 +0200 Subject: [PATCH 085/195] Avoid bindable/event feedback when changing resolution --- .../Sections/Graphics/LayoutSettings.cs | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 2e26d15105..d82b8b1ba3 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -244,7 +244,8 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { Scheduler.AddOnce(d => { - displayDropdown.Items = d; + if (!displayDropdown.Items.SequenceEqual(d, DisplayListComparer.DEFAULT)) + displayDropdown.Items = d; updateDisplaySettingsVisibility(); }, displays); } @@ -376,5 +377,43 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics } } } + + /// + /// Contrary to , this comparer disregards the value of . + /// We want to just show a list of displays, and for the purposes of settings we don't care about their bounds when it comes to the list. + /// However, fires even if only the resolution of the current display was changed + /// (because it causes the bounds of all displays to also change). + /// We're not interested in those changes, so compare only the rest that we actually care about. + /// This helps to avoid a bindable/event feedback loop, in which a resolution change + /// would trigger a display "change", which would in turn reset resolution again. + /// + private class DisplayListComparer : IEqualityComparer + { + public static readonly DisplayListComparer DEFAULT = new DisplayListComparer(); + + public bool Equals(Display? x, Display? y) + { + if (ReferenceEquals(x, y)) return true; + if (ReferenceEquals(x, null)) return false; + if (ReferenceEquals(y, null)) return false; + + return x.Index == y.Index + && x.Name == y.Name + && x.DisplayModes.SequenceEqual(y.DisplayModes); + } + + public int GetHashCode(Display obj) + { + var hashCode = new HashCode(); + + hashCode.Add(obj.Index); + hashCode.Add(obj.Name); + hashCode.Add(obj.DisplayModes.Length); + foreach (var displayMode in obj.DisplayModes) + hashCode.Add(displayMode); + + return hashCode.ToHashCode(); + } + } } } From 0dcf1b540e0ee5a652e09982eeffd5174733891d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 May 2023 21:14:09 +0200 Subject: [PATCH 086/195] Attempt to preserve old resolution when switching displays --- .../Settings/Sections/Graphics/LayoutSettings.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index d82b8b1ba3..6a4ad63799 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -193,6 +193,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics currentDisplay.BindValueChanged(display => Schedule(() => { + // there is no easy way to perform an atomic swap on the `resolutions` list, which is bound to the dropdown via `ItemSource`. + // this means, if the old resolution isn't saved, the `RemoveRange()` call below will cause `resolutionDropdown.Current` to reset value, + // therefore making it impossible to select any dropdown value other than "Default". + // to circumvent this locally, store the old value here, so that we can attempt to restore it later. + var oldResolution = resolutionDropdown.Current.Value; + resolutions.RemoveRange(1, resolutions.Count - 1); if (display.NewValue != null) @@ -204,6 +210,9 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics .Distinct()); } + if (resolutions.Contains(oldResolution)) + resolutionDropdown.Current.Value = oldResolution; + updateDisplaySettingsVisibility(); }), true); From 2d7fe683110c18361d18ec979473e3ccb783e88e Mon Sep 17 00:00:00 2001 From: Susko3 Date: Tue, 16 May 2023 21:57:31 +0200 Subject: [PATCH 087/195] Prevent feedback by using atomic `.ReplaceRange()` `display.NewValue` will never be null, checked on SDL + osuTK/Android. On Android it's a 0x0 display, importantly not null. --- .../Settings/Sections/Graphics/LayoutSettings.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index 2e26d15105..f3ff2e87ba 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -193,16 +193,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics currentDisplay.BindValueChanged(display => Schedule(() => { - resolutions.RemoveRange(1, resolutions.Count - 1); - - if (display.NewValue != null) - { - resolutions.AddRange(display.NewValue.DisplayModes - .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) - .OrderByDescending(m => Math.Max(m.Size.Height, m.Size.Width)) - .Select(m => m.Size) - .Distinct()); - } + resolutions.ReplaceRange(1, resolutions.Count - 1, display.NewValue.DisplayModes + .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) + .OrderByDescending(m => Math.Max(m.Size.Height, m.Size.Width)) + .Select(m => m.Size) + .Distinct()); updateDisplaySettingsVisibility(); }), true); From 70426a504223eb089b1bc1af2bcdeaca6f760d35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 May 2023 22:42:25 +0200 Subject: [PATCH 088/195] Add reference to youtrack issue to suppression --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 5039831f76..f9aff99267 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -234,6 +234,7 @@ WARNING WARNING HINT + DO_NOT_SHOW WARNING HINT From c97b7a077e274493fec0dfbdd3e5193669c98fcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 May 2023 23:47:47 +0200 Subject: [PATCH 089/195] Use better issue reference --- osu.sln.DotSettings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index f9aff99267..9ca0fad2ab 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -234,7 +234,7 @@ WARNING WARNING HINT - + DO_NOT_SHOW WARNING HINT From d6fa44240d6009e3b902765f26feac6e648a96f3 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 16 May 2023 21:51:32 -0700 Subject: [PATCH 090/195] Fix storyboard video-only check being inverted --- osu.Game/Storyboards/Drawables/DrawableStoryboard.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs index aa264fa719..e674e7512c 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboard.cs @@ -69,7 +69,7 @@ namespace osu.Game.Storyboards.Drawables Size = new Vector2(640, 480); - bool onlyHasVideoElements = Storyboard.Layers.SelectMany(l => l.Elements).Any(e => !(e is StoryboardVideo)); + bool onlyHasVideoElements = Storyboard.Layers.SelectMany(l => l.Elements).All(e => e is StoryboardVideo); Width = Height * (storyboard.BeatmapInfo.WidescreenStoryboard || onlyHasVideoElements ? 16 / 9f : 4 / 3f); From a8bc337006918eb5579fb202c6537d0905075b17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 14:06:02 +0900 Subject: [PATCH 091/195] Change default volume when constructing a `HitSampleInfo` to non-zero --- osu.Game/Audio/HitSampleInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs index 665ae3e1a7..3cf8e84d56 100644 --- a/osu.Game/Audio/HitSampleInfo.cs +++ b/osu.Game/Audio/HitSampleInfo.cs @@ -45,7 +45,7 @@ namespace osu.Game.Audio /// public int Volume { get; } - public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 0) + public HitSampleInfo(string name, string bank = SampleControlPoint.DEFAULT_BANK, string? suffix = null, int volume = 100) { Name = name; Bank = bank; From dc51d5ecf32fb23e3c45048b9aa848116f913f31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 14:07:48 +0900 Subject: [PATCH 092/195] Rename `GetSampleInfo` to better describe what method does Also add full xmldoc --- osu.Game.Rulesets.Catch/Objects/BananaShower.cs | 2 +- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 2 +- .../Objects/TaikoStrongableHitObject.cs | 2 +- osu.Game/Rulesets/Objects/HitObject.cs | 14 ++++++++++---- .../Components/ComposeBlueprintContainer.cs | 2 +- .../Compose/Components/EditorSelectionHandler.cs | 2 +- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 5bd4ac86f5..b05c8e5f77 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Catch.Objects { StartTime = time, BananaIndex = i, - Samples = new List { new Banana.BananaHitSampleInfo(GetSampleInfo().Volume) } + Samples = new List { new Banana.BananaHitSampleInfo(CreateHitSampleInfo().Volume) } }); time += spacing; diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index df5898fd67..ba0981e781 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.cs @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Objects AddNested(i < SpinsRequired ? new SpinnerTick { StartTime = startTime, SpinnerDuration = Duration } - : new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { GetSampleInfo("spinnerbonus") } }); + : new SpinnerBonusTick { StartTime = startTime, SpinnerDuration = Duration, Samples = new[] { CreateHitSampleInfo("spinnerbonus") } }); } } diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index d90c7016e9..303447e672 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Taiko.Objects if (isRimType != rimSamples.Any()) { if (isRimType) - Samples.Add(GetSampleInfo(HitSampleInfo.HIT_CLAP)); + Samples.Add(CreateHitSampleInfo(HitSampleInfo.HIT_CLAP)); else { foreach (var sample in rimSamples) diff --git a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs index 0043f231d2..479ad8369a 100644 --- a/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/TaikoStrongableHitObject.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Taiko.Objects if (IsStrongBindable.Value != strongSamples.Any()) { if (IsStrongBindable.Value) - Samples.Add(GetSampleInfo(HitSampleInfo.HIT_FINISH)); + Samples.Add(CreateHitSampleInfo(HitSampleInfo.HIT_FINISH)); else { foreach (var sample in strongSamples) diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index f5b9e9c8cd..ed3d3a6eb2 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -206,14 +206,20 @@ namespace osu.Game.Rulesets.Objects } /// - /// Create a SampleInfo based on the sample settings of the hit normal sample in . + /// Create a based on the sample settings of the first sample in . + /// If no sample is available, sane default settings will be used instead. /// + /// + /// In the case an existing sample exists, all settings apart from the sample name will be inherited. This includes volume, bank and suffix. + /// /// The name of the sample. /// A populated . - public HitSampleInfo GetSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) + public HitSampleInfo CreateHitSampleInfo(string sampleName = HitSampleInfo.HIT_NORMAL) { - var hitnormalSample = Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL); - return hitnormalSample == null ? new HitSampleInfo(sampleName) : hitnormalSample.With(newName: sampleName); + if (Samples.FirstOrDefault(s => s.Name == HitSampleInfo.HIT_NORMAL) is HitSampleInfo existingSample) + return existingSample.With(newName: sampleName); + + return new HitSampleInfo(sampleName); } } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 73e3baf509..b42118b983 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -185,7 +185,7 @@ namespace osu.Game.Screens.Edit.Compose.Components case TernaryState.True: if (existingSample == null) - samples.Add(CurrentPlacement.HitObject.GetSampleInfo(sampleName)); + samples.Add(CurrentPlacement.HitObject.CreateHitSampleInfo(sampleName)); break; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 694b24c567..3b49b76e7e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -122,7 +122,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (h.Samples.Any(s => s.Name == sampleName)) return; - h.Samples.Add(h.GetSampleInfo(sampleName)); + h.Samples.Add(h.CreateHitSampleInfo(sampleName)); EditorBeatmap.Update(h); }); } From 510ebe1f234ffb9b1e8ca9c018e8ff2bb7839d3c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 14:11:14 +0900 Subject: [PATCH 093/195] Fix weird optional usage in `HitSampleInfo.With` --- osu.Game.Rulesets.Catch/Objects/Banana.cs | 2 +- osu.Game/Audio/HitSampleInfo.cs | 4 ++-- .../Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs index 4c66c054e1..b80527f379 100644 --- a/osu.Game.Rulesets.Catch/Objects/Banana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Banana.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Catch.Objects { } - public sealed override HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) + public sealed override HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) => new BananaHitSampleInfo(newVolume.GetOr(Volume)); public bool Equals(BananaHitSampleInfo? other) diff --git a/osu.Game/Audio/HitSampleInfo.cs b/osu.Game/Audio/HitSampleInfo.cs index 3cf8e84d56..5d1ce27c9f 100644 --- a/osu.Game/Audio/HitSampleInfo.cs +++ b/osu.Game/Audio/HitSampleInfo.cs @@ -76,8 +76,8 @@ namespace osu.Game.Audio /// An optional new lookup suffix. /// An optional new volume. /// The new . - public virtual HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) - => new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank) ?? Bank, newSuffix.GetOr(Suffix), newVolume.GetOr(Volume)); + public virtual HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) + => new HitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newSuffix.GetOr(Suffix), newVolume.GetOr(Volume)); public bool Equals(HitSampleInfo? other) => other != null && Name == other.Name && Bank == other.Bank && Suffix == other.Suffix; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index c0f02b0b5e..d9738ecd0a 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -535,13 +535,13 @@ namespace osu.Game.Rulesets.Objects.Legacy IsLayered = isLayered; } - public sealed override HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) + public sealed override HitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newSuffix = default, Optional newVolume = default) => With(newName, newBank, newVolume); - public virtual LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, + public virtual LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, Optional newCustomSampleBank = default, Optional newIsLayered = default) - => new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank) ?? Bank, newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered)); + => new LegacyHitSampleInfo(newName.GetOr(Name), newBank.GetOr(Bank), newVolume.GetOr(Volume), newCustomSampleBank.GetOr(CustomSampleBank), newIsLayered.GetOr(IsLayered)); public bool Equals(LegacyHitSampleInfo? other) // The additions to equality checks here are *required* to ensure that pooling works correctly. @@ -573,7 +573,7 @@ namespace osu.Game.Rulesets.Objects.Legacy Path.ChangeExtension(Filename, null) }; - public sealed override LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, + public sealed override LegacyHitSampleInfo With(Optional newName = default, Optional newBank = default, Optional newVolume = default, Optional newCustomSampleBank = default, Optional newIsLayered = default) => new FileHitSampleInfo(Filename, newVolume.GetOr(Volume)); From dc421bd2af6d093f9cd1f38a7080c3acde6fae35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 14:49:47 +0900 Subject: [PATCH 094/195] Revert "Merge pull request #23570 from huoyaoyuan/inspect-code-cs11" This reverts commit ab2bd123e7e29c098f29e92b547826f3844809bb, reversing changes made to 267e63320f5bfa1b83d15c85e9eea62106611a36. --- .config/dotnet-tools.json | 2 +- Directory.Build.props | 2 +- osu.Game/osu.Game.csproj | 3 ++- osu.sln.DotSettings | 2 -- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index d9f1e3985a..1f937e1837 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "jetbrains.resharper.globaltools": { - "version": "2022.3.3", + "version": "2022.2.3", "commands": [ "jb" ] diff --git a/Directory.Build.props b/Directory.Build.props index fccac79a45..734374c840 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@  - 11.0 + 10.0 true enable diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index bf0c1dde17..3ea4a57c2c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -1,8 +1,9 @@ - + net6.0 Library true + 10 osu! diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 9ca0fad2ab..b54794cd6d 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -234,8 +234,6 @@ WARNING WARNING HINT - - DO_NOT_SHOW WARNING HINT HINT From 94b184712d175e075ed805741f730fd610a3e2f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 14:38:58 +0900 Subject: [PATCH 095/195] Fix random button hover state not correctly being reset on right click --- osu.Game/Screens/Select/FooterButtonRandom.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index f413126e87..3a4bad43b3 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -119,14 +119,13 @@ namespace osu.Game.Screens.Select protected override void OnMouseUp(MouseUpEvent e) { + base.OnMouseUp(e); + if (e.Button == MouseButton.Right && IsHovered) { rewindSearch = true; TriggerClick(); - return; } - - base.OnMouseUp(e); } public override bool OnPressed(KeyBindingPressEvent e) From 764f0323f4727d412e77316a35c4d7f470158263 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 14:42:07 +0900 Subject: [PATCH 096/195] Show "rewind" text on random button when beginning a right mouse press --- osu.Game/Screens/Select/FooterButtonRandom.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/FooterButtonRandom.cs b/osu.Game/Screens/Select/FooterButtonRandom.cs index 3a4bad43b3..2d5d049133 100644 --- a/osu.Game/Screens/Select/FooterButtonRandom.cs +++ b/osu.Game/Screens/Select/FooterButtonRandom.cs @@ -93,16 +93,22 @@ namespace osu.Game.Screens.Select protected override bool OnKeyDown(KeyDownEvent e) { - updateText(e.ShiftPressed); + updateText(e); return base.OnKeyDown(e); } protected override void OnKeyUp(KeyUpEvent e) { - updateText(e.ShiftPressed); + updateText(e); base.OnKeyUp(e); } + protected override bool OnMouseDown(MouseDownEvent e) + { + updateText(e); + return base.OnMouseDown(e); + } + protected override bool OnClick(ClickEvent e) { try @@ -126,6 +132,8 @@ namespace osu.Game.Screens.Select rewindSearch = true; TriggerClick(); } + + updateText(e); } public override bool OnPressed(KeyBindingPressEvent e) @@ -150,10 +158,12 @@ namespace osu.Game.Screens.Select } } - private void updateText(bool rewind = false) + private void updateText(UIEvent e) { - randomSpriteText.Alpha = rewind ? 0 : 1; - rewindSpriteText.Alpha = rewind ? 1 : 0; + bool aboutToRewind = e.ShiftPressed || e.CurrentState.Mouse.IsPressed(MouseButton.Right); + + randomSpriteText.Alpha = aboutToRewind ? 0 : 1; + rewindSpriteText.Alpha = aboutToRewind ? 1 : 0; } } } From 9fe787acd89e04cdeb108129eb0055864343bbea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 15:03:59 +0900 Subject: [PATCH 097/195] Enable NRT on `AccuracyHeatmap` --- osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 0249b6d9b1..495fccf03a 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -1,8 +1,6 @@ // 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; using System.Diagnostics; using System.Linq; @@ -36,8 +34,8 @@ namespace osu.Game.Rulesets.Osu.Statistics private const float rotation = 45; - private BufferedContainer bufferedGrid; - private GridContainer pointGrid; + private BufferedContainer bufferedGrid = null!; + private GridContainer pointGrid = null!; private readonly ScoreInfo score; private readonly IBeatmap playableBeatmap; From 3054348c730db26c981cf5e06a36f2cc392da6ae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 15:22:48 +0900 Subject: [PATCH 098/195] Add text hints to accuracy heat map to better describe travel direction --- .../Statistics/AccuracyHeatmap.cs | 80 ++++++++++++++----- 1 file changed, 59 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 495fccf03a..9d9b3fb84a 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Scoring; using osuTK; @@ -64,34 +65,36 @@ namespace osu.Game.Rulesets.Osu.Statistics FillMode = FillMode.Fit, Children = new Drawable[] { - new CircularContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Size = new Vector2(inner_portion), - Masking = true, - BorderThickness = line_thickness, - BorderColour = Color4.White, - Child = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4Extensions.FromHex("#202624") - } - }, new Container { RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + new CircularContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(inner_portion), + Masking = true, + BorderThickness = line_thickness, + BorderColour = Color4.White, + Child = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = Color4Extensions.FromHex("#202624") + } + }, new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding(1), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Rotation = rotation, Child = new Container { RelativeSizeAxes = Axes.Both, - Masking = true, Children = new Drawable[] { new Box @@ -100,9 +103,9 @@ namespace osu.Game.Rulesets.Osu.Statistics Origin = Anchor.Centre, EdgeSmoothness = new Vector2(1), RelativeSizeAxes = Axes.Y, - Height = 2, // We're rotating along a diagonal - we don't really care how big this is. - Width = line_thickness / 2, - Rotation = -rotation, + Width = line_thickness / 2, // adjust for edgesmoothness + Height = MathF.Sqrt(2), + Rotation = -rotation * 2, Alpha = 0.3f, }, new Box @@ -111,9 +114,44 @@ namespace osu.Game.Rulesets.Osu.Statistics Origin = Anchor.Centre, EdgeSmoothness = new Vector2(1), RelativeSizeAxes = Axes.Y, - Height = 2, // We're rotating along a diagonal - we don't really care how big this is. Width = line_thickness / 2, // adjust for edgesmoothness - Rotation = rotation + Height = MathF.Sqrt(2), + }, + new OsuSpriteText + { + Text = "Next", + Anchor = Anchor.Centre, + Origin = Anchor.BottomRight, + Padding = new MarginPadding(3), + RelativePositionAxes = Axes.Both, + Y = -inner_portion / 2, + }, + new OsuSpriteText + { + Text = "object", + Anchor = Anchor.Centre, + Origin = Anchor.BottomLeft, + Padding = new MarginPadding(3), + RelativePositionAxes = Axes.Both, + Y = -inner_portion / 2, + }, + new OsuSpriteText + { + Text = "Last", + Anchor = Anchor.Centre, + Origin = Anchor.TopRight, + Padding = new MarginPadding(3), + RelativePositionAxes = Axes.Both, + Y = inner_portion / 2, + }, + new OsuSpriteText + { + Text = "object", + Anchor = Anchor.Centre, + Origin = Anchor.TopLeft, + Padding = new MarginPadding(3), + RelativePositionAxes = Axes.Both, + Y = inner_portion / 2, }, } }, From 79e1d978e7581f5303fc37270f2bc02aa9c1b60e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 15:42:38 +0900 Subject: [PATCH 099/195] Ensure scroll-to-top button is displayed above all overlay content --- osu.Game/Overlays/OnlineOverlay.cs | 8 ++++++++ osu.Game/Overlays/OverlayScrollContainer.cs | 2 +- osu.Game/Overlays/UserProfileOverlay.cs | 12 +++++++++++- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/OnlineOverlay.cs b/osu.Game/Overlays/OnlineOverlay.cs index 4d2c6bc9d0..8b7a82f899 100644 --- a/osu.Game/Overlays/OnlineOverlay.cs +++ b/osu.Game/Overlays/OnlineOverlay.cs @@ -77,6 +77,14 @@ namespace osu.Game.Overlays base.Content.Add(mainContent); } + protected override void LoadComplete() + { + base.LoadComplete(); + + // Ensure the scroll-to-top button is displayed above the fixed header. + AddInternal(ScrollFlow.Button.CreateProxy()); + } + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game/Overlays/OverlayScrollContainer.cs b/osu.Game/Overlays/OverlayScrollContainer.cs index 2a615f0e12..9ff0a65652 100644 --- a/osu.Game/Overlays/OverlayScrollContainer.cs +++ b/osu.Game/Overlays/OverlayScrollContainer.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays /// private const int button_scroll_position = 200; - protected ScrollBackButton Button; + public ScrollBackButton Button { get; private set; } private readonly Bindable lastScrollTarget = new Bindable(); diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index d1fe877e55..ab4f07b982 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -249,12 +249,14 @@ namespace osu.Game.Overlays private partial class ProfileSectionsContainer : SectionsContainer { + private OverlayScrollContainer scroll = null!; + public ProfileSectionsContainer() { RelativeSizeAxes = Axes.Both; } - protected override UserTrackingScrollContainer CreateScrollContainer() => new OverlayScrollContainer(); + protected override UserTrackingScrollContainer CreateScrollContainer() => scroll = new OverlayScrollContainer(); // Reverse child ID is required so expanding beatmap panels can appear above sections below them. // This can also be done by setting Depth when adding new sections above if using ReverseChildID turns out to have any issues. @@ -267,6 +269,14 @@ namespace osu.Game.Overlays Padding = new MarginPadding { Horizontal = 10 }, Margin = new MarginPadding { Bottom = 10 }, }; + + protected override void LoadComplete() + { + base.LoadComplete(); + + // Ensure the scroll-to-top button is displayed above the fixed header. + AddInternal(scroll.Button.CreateProxy()); + } } } } From e97d027230362277c9a12dab6581d387cc91f9a8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 16:13:03 +0900 Subject: [PATCH 100/195] Make `OnlinePlayPill` class `abstract` --- .../Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs index 80e82c3856..1235e5234c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs @@ -10,12 +10,12 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Screens.OnlinePlay.Lounge.Components { - public partial class OnlinePlayPill : OnlinePlayComposite + public abstract partial class OnlinePlayPill : OnlinePlayComposite { protected PillContainer Pill; protected OsuTextFlowContainer TextFlow; - public OnlinePlayPill() + protected OnlinePlayPill() { AutoSizeAxes = Axes.Both; } From 730bc3a9616e03d129224704373273fe93fc293f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 16:13:54 +0900 Subject: [PATCH 101/195] Apply NRT and simplify some remaining code --- .../OnlinePlay/Lounge/Components/MatchTypePill.cs | 3 +-- .../OnlinePlay/Lounge/Components/OnlinePlayPill.cs | 6 ++---- .../OnlinePlay/Lounge/Components/QueueModePill.cs | 3 +-- .../Lounge/Components/RoomSpecialCategoryPill.cs | 9 ++------- .../OnlinePlay/Lounge/Components/RoomStatusPill.cs | 6 ++---- 5 files changed, 8 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs index e019de0f6f..35e0482f2b 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/MatchTypePill.cs @@ -20,8 +20,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onMatchTypeChanged(ValueChangedEvent type) { - TextFlow.Clear(); - TextFlow.AddText(type.NewValue.GetLocalisableDescription()); + TextFlow.Text = type.NewValue.GetLocalisableDescription(); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs index 1235e5234c..983fab8525 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs @@ -1,8 +1,6 @@ // 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 osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Game.Graphics; @@ -12,8 +10,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { public abstract partial class OnlinePlayPill : OnlinePlayComposite { - protected PillContainer Pill; - protected OsuTextFlowContainer TextFlow; + protected PillContainer Pill { get; private set; } = null!; + protected OsuTextFlowContainer TextFlow { get; private set; } = null!; protected OnlinePlayPill() { diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs index e402653c2f..208c11c155 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/QueueModePill.cs @@ -20,8 +20,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components private void onQueueModeChanged(ValueChangedEvent mode) { - TextFlow.Clear(); - TextFlow.AddText(mode.NewValue.GetLocalisableDescription()); + TextFlow.Text = mode.NewValue.GetLocalisableDescription(); } } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index b869ede9bd..e88624f877 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -19,18 +19,13 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { base.LoadComplete(); - Pill.Background.Colour = colours.Pink; Pill.Background.Alpha = 1; TextFlow.Colour = Color4.Black; Category.BindValueChanged(c => { - TextFlow.Clear(); - TextFlow.AddText(c.NewValue.GetLocalisableDescription()); - - var backgroundColour = colours.ForRoomCategory(Category.Value); - if (backgroundColour != null) - Pill.Background.Colour = backgroundColour.Value; + TextFlow.Text = c.NewValue.GetLocalisableDescription(); + Pill.Background.Colour = colours.ForRoomCategory(c.NewValue) ?? colours.Pink; }, true); } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index f260d168a7..ab3d293db8 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -30,17 +30,15 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components FinishTransforms(true); TextFlow.Colour = Colour4.Black; + Pill.Background.Alpha = 1; } private void updateDisplay() { RoomStatus status = getDisplayStatus(); - Pill.Background.Alpha = 1; Pill.Background.FadeColour(status.GetAppropriateColour(colours), 100); - - TextFlow.Clear(); - TextFlow.AddText(status.Message); + TextFlow.Text = status.Message; } private RoomStatus getDisplayStatus() From ec2b9165d59a1ebdba6a2703cfa908f406ec931f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 16:33:58 +0900 Subject: [PATCH 102/195] Adjust `BeatDivisorControl` test to show control mmuch larger --- osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index 56b16301be..c3d5ecac5c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -42,7 +42,8 @@ namespace osu.Game.Tests.Visual.Editing { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(90, 90) + Size = new Vector2(90, 90), + Scale = new Vector2(3), } }; }); From 87ff28b0220c40691eab57038f98f366b2601986 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 16:34:10 +0900 Subject: [PATCH 103/195] Update beat divisor control to show ticks in more visually correct locations As proposed in https://github.com/ppy/osu/discussions/23527. --- .../Edit/Compose/Components/BeatDivisorControl.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 9f422d5aa9..3bfe81e6a7 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -410,6 +410,16 @@ namespace osu.Game.Screens.Edit.Compose.Components }); } + // Add a fake 1/1 at the end to give context. + AddInternal(new Tick(1) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.Centre, + Depth = float.MaxValue, + Alpha = 0.05f, + Colour = BindableBeatDivisor.GetColourFor(1, colours), + }); + AddInternal(marker = new Marker()); CurrentNumber.ValueChanged += moveMarker; CurrentNumber.TriggerChange(); @@ -483,7 +493,7 @@ namespace osu.Game.Screens.Edit.Compose.Components OnUserChange(Current.Value); } - private float getMappedPosition(float divisor) => MathF.Pow((divisor - 1) / (beatDivisor.ValidDivisors.Value.Presets.Last() - 1), 0.90f); + private float getMappedPosition(float divisor) => 1 - 1 / divisor; private partial class Tick : Circle { From 7d7d402d4e06459e6834db800018d7000b2fa61b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 16:58:23 +0900 Subject: [PATCH 104/195] Apply NRT to `PlacementBlueprint` --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 12c0ea1807..5eca4f8ccd 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -1,8 +1,6 @@ // 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.Linq; using System.Threading; using osu.Framework.Allocation; @@ -38,16 +36,16 @@ namespace osu.Game.Rulesets.Edit /// public readonly HitObject HitObject; - [Resolved(canBeNull: true)] - protected EditorClock EditorClock { get; private set; } + [Resolved] + protected EditorClock? EditorClock { get; private set; } [Resolved] - private EditorBeatmap beatmap { get; set; } + private EditorBeatmap beatmap { get; set; } = null!; - private Bindable startTimeBindable; + private Bindable startTimeBindable = null!; [Resolved] - private IPlacementHandler placementHandler { get; set; } + private IPlacementHandler placementHandler { get; set; } = null!; /// /// Whether this blueprint is currently in a state that can be committed. From e43f2c2c439bbc5c524606b20ee1bfb651cacb06 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 16:58:43 +0900 Subject: [PATCH 105/195] Improve previous hitobject lookup efficient and correctness --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 5eca4f8ccd..9ef208bcb5 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -44,6 +44,8 @@ namespace osu.Game.Rulesets.Edit private Bindable startTimeBindable = null!; + private HitObject? getPreviousHitObject() => beatmap.HitObjects.TakeWhile(h => h.StartTime <= startTimeBindable.Value).LastOrDefault(); + [Resolved] private IPlacementHandler placementHandler { get; set; } = null!; @@ -84,7 +86,7 @@ namespace osu.Game.Rulesets.Edit protected void BeginPlacement(bool commitStart = false) { // Take the hitnormal sample of the last hit object - var lastHitNormal = beatmap.HitObjects.LastOrDefault(h => h.GetEndTime() < HitObject.StartTime)?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL); + var lastHitNormal = getPreviousHitObject()?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL); if (lastHitNormal != null) HitObject.Samples[0] = lastHitNormal; From 8d925c8a8a5f06c33bddcf28b543a784a159b71a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 17:19:07 +0900 Subject: [PATCH 106/195] Move combo information updating to an interface level helper method --- osu.Game/Beatmaps/BeatmapProcessor.cs | 27 +++---------------- .../Objects/Types/IHasComboInformation.cs | 23 ++++++++++++++-- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index 8f3d0b7445..fb5313469f 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -1,8 +1,6 @@ // 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.Linq; using osu.Game.Rulesets.Objects.Types; @@ -22,34 +20,17 @@ namespace osu.Game.Beatmaps public virtual void PreProcess() { - IHasComboInformation lastObj = null; - - bool isFirst = true; + IHasComboInformation? lastObj = null; foreach (var obj in Beatmap.HitObjects.OfType()) { - if (isFirst) + if (lastObj == null) { - obj.NewCombo = true; - // first hitobject should always be marked as a new combo for sanity. - isFirst = false; - } - - obj.ComboIndex = lastObj?.ComboIndex ?? 0; - obj.ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; - obj.IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; - - if (obj.NewCombo) - { - obj.IndexInCurrentCombo = 0; - obj.ComboIndex++; - obj.ComboIndexWithOffsets += obj.ComboOffset + 1; - - if (lastObj != null) - lastObj.LastInCombo = true; + obj.NewCombo = true; } + obj.UpdateComboInformation(lastObj); lastObj = obj; } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index b45ea989f3..d34e71021f 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -1,8 +1,6 @@ // 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 osu.Framework.Bindables; using osu.Game.Skinning; using osuTK.Graphics; @@ -65,5 +63,26 @@ namespace osu.Game.Rulesets.Objects.Types { return skin.GetConfig(new SkinComboColourLookup(comboIndex, combo))?.Value ?? Color4.White; } + + /// + /// Given the previous object in the beatmap, update relevant combo information. + /// + /// The previous hitobject, or null if this is the first object in the beatmap. + void UpdateComboInformation(IHasComboInformation? lastObj) + { + ComboIndex = lastObj?.ComboIndex ?? 0; + ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; + IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; + + if (NewCombo || lastObj == null) + { + IndexInCurrentCombo = 0; + ComboIndex++; + ComboIndexWithOffsets += ComboOffset + 1; + + if (lastObj != null) + lastObj.LastInCombo = true; + } + } } } From 9563d4f73004ad5794e0031f2046fdfd1bd5dda9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 17:19:18 +0900 Subject: [PATCH 107/195] Fix weird purple tint on placement object in timeline --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index f93fb0679f..30f56814ba 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -83,7 +83,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline { placementBlueprint = CreateBlueprintFor(obj.NewValue).AsNonNull(); - placementBlueprint.Colour = Color4.MediumPurple; + placementBlueprint.Colour = OsuColour.Gray(0.9f); SelectionBlueprints.Add(placementBlueprint); } From 0b25818bd218d0d6388af0a5da2d7a0f0190c68d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 17:20:15 +0900 Subject: [PATCH 108/195] Update combo information on placement blueprint --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 9ef208bcb5..84e32d99f8 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Compose; using osuTK; @@ -148,7 +149,12 @@ namespace osu.Game.Rulesets.Edit public virtual void UpdateTimeAndPosition(SnapResult result) { if (PlacementActive == PlacementState.Waiting) + { HitObject.StartTime = result.Time ?? EditorClock?.CurrentTime ?? Time.Current; + + if (HitObject is IHasComboInformation comboInformation) + comboInformation.UpdateComboInformation(getPreviousHitObject() as IHasComboInformation); + } } /// From 214d7e07fa1ad2b4016a751b2ee6f495384b6ac5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 17:49:04 +0900 Subject: [PATCH 109/195] Add TODO regarding failing stack display code --- .../Compose/Components/Timeline/TimelineBlueprintContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs index 30f56814ba..b60e04afc1 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineBlueprintContainer.cs @@ -85,6 +85,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline placementBlueprint.Colour = OsuColour.Gray(0.9f); + // TODO: this is out of order, causing incorrect stacking height. SelectionBlueprints.Add(placementBlueprint); } } From b58ab28765974714537a893a60e5bfff39e71f29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 May 2023 20:54:17 +0900 Subject: [PATCH 110/195] Make `EditorClock` non-nullable in `PlacementBlueprint` --- .../Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs | 8 ++------ osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs index 73ee5df9dc..f59be0e0e9 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Spinners/SpinnerPlacementBlueprint.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. -#nullable disable - using System; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; @@ -24,9 +21,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Spinners private bool isPlacingEnd; - [Resolved(CanBeNull = true)] - [CanBeNull] - private IBeatSnapProvider beatSnapProvider { get; set; } + [Resolved] + private IBeatSnapProvider? beatSnapProvider { get; set; } public SpinnerPlacementBlueprint() : base(new Spinner { Position = OsuPlayfield.BASE_SIZE / 2 }) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 84e32d99f8..b10f1a2af5 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Edit public readonly HitObject HitObject; [Resolved] - protected EditorClock? EditorClock { get; private set; } + protected EditorClock EditorClock { get; private set; } = null!; [Resolved] private EditorBeatmap beatmap { get; set; } = null!; @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Edit { if (PlacementActive == PlacementState.Waiting) { - HitObject.StartTime = result.Time ?? EditorClock?.CurrentTime ?? Time.Current; + HitObject.StartTime = result.Time ?? EditorClock.CurrentTime; if (HitObject is IHasComboInformation comboInformation) comboInformation.UpdateComboInformation(getPreviousHitObject() as IHasComboInformation); From 7ac6688a0fd8c57f5d04c846ff0c04e069d61c89 Mon Sep 17 00:00:00 2001 From: Dimmitsaras Date: Wed, 17 May 2023 18:34:39 +0300 Subject: [PATCH 111/195] Chat message notifications always play on unfocused window --- osu.Game/Online/Chat/MessageNotifier.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index 9b2ad666b2..cb29fc4535 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -12,6 +12,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Framework.Platform; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Online.API; @@ -35,6 +36,9 @@ namespace osu.Game.Online.Chat [Resolved] private ChannelManager channelManager { get; set; } + [Resolved] + private GameHost host { get; set; } + private Bindable notifyOnUsername; private Bindable notifyOnPrivateMessage; @@ -89,8 +93,8 @@ namespace osu.Game.Online.Chat if (channel == null) return; - // Only send notifications, if ChatOverlay and the target channel aren't visible. - if (chatOverlay.IsPresent && channelManager.CurrentChannel.Value == channel) + // Only send notifications, if ChatOverlay and the target channel aren't visible, or if the window is unfocused + if (chatOverlay.IsPresent && channelManager.CurrentChannel.Value == channel && host.IsActive.Value) return; foreach (var message in messages.OrderByDescending(m => m.Id)) @@ -99,6 +103,7 @@ namespace osu.Game.Online.Chat if (message.Id <= channel.LastReadId) return; + // ignore notifications triggered by your own chat messages if (message.Sender.Id == localUser.Value.Id) continue; From caa79704acb69efd92dff2c40dc450988bf0230d Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 17 May 2023 20:23:37 -0700 Subject: [PATCH 112/195] Add test coverage for failing case --- .../Resources/storyboard_only_video.osu | 31 +++++++++++++++++++ .../Visual/Gameplay/TestSceneStoryboard.cs | 18 ++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Resources/storyboard_only_video.osu diff --git a/osu.Game.Tests/Resources/storyboard_only_video.osu b/osu.Game.Tests/Resources/storyboard_only_video.osu new file mode 100644 index 0000000000..25f1ff6361 --- /dev/null +++ b/osu.Game.Tests/Resources/storyboard_only_video.osu @@ -0,0 +1,31 @@ +osu file format v14 + +[Events] +//Background and Video events +0,0,"BG.jpg",0,0 +Video,0,"video.avi" +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +1674,333.333333333333,4,2,1,70,1,0 +1674,-100,4,2,1,70,0,0 +3340,-100,4,2,1,70,0,0 +3507,-100,4,2,1,70,0,0 +3673,-100,4,2,1,70,0,0 + +[Colours] +Combo1 : 240,80,80 +Combo2 : 171,252,203 +Combo3 : 128,128,255 +Combo4 : 249,254,186 + +[HitObjects] +148,303,1674,5,6,3:2:0:0: +378,252,1840,1,0,0:0:0:0: +389,270,2340,5,2,0:1:0:0: diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs index dbce62cbef..a6663f3086 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneStoryboard.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 NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -8,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Timing; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.IO; @@ -42,6 +44,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("Load storyboard with missing video", () => loadStoryboard("storyboard_no_video.osu")); } + [Test] + public void TestVideoSize() + { + AddStep("load storyboard with only video", () => + { + // LegacyStoryboardDecoder doesn't parse WidescreenStoryboard, so it is set manually + loadStoryboard("storyboard_only_video.osu", s => s.BeatmapInfo.WidescreenStoryboard = false); + }); + + AddAssert("storyboard is correct width", () => Precision.AlmostEquals(storyboard?.Width ?? 0f, 480 * 16 / 9f)); + } + [BackgroundDependencyLoader] private void load() { @@ -102,7 +116,7 @@ namespace osu.Game.Tests.Visual.Gameplay decoupledClock.ChangeSource(Beatmap.Value.Track); } - private void loadStoryboard(string filename) + private void loadStoryboard(string filename, Action? setUpStoryboard = null) { Storyboard loaded; @@ -113,6 +127,8 @@ namespace osu.Game.Tests.Visual.Gameplay loaded = decoder.Decode(bfr); } + setUpStoryboard?.Invoke(loaded); + loadStoryboard(loaded); } } From ee522253cb0373b8ca231557e8d56ec2d888945f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 May 2023 14:12:57 +0900 Subject: [PATCH 113/195] Remove a couple of unnecessary `volume` declarations --- osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs | 2 +- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 9a78df23cf..f21825668f 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Catch.Tests NewCombo = i % 8 == 0, Samples = new List(new[] { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: 100) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) }) }); } diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index eae9f922a4..919a3cdfdf 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Edit HitObject = hitObject; // adding the default hit sample should be the case regardless of the ruleset. - HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL, volume: 100)); + HitObject.Samples.Add(new HitSampleInfo(HitSampleInfo.HIT_NORMAL)); RelativeSizeAxes = Axes.Both; From e8cb19e40aead0744621afe052944f57fa81ea02 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 19 May 2023 17:49:26 +0900 Subject: [PATCH 114/195] Pin builds to .NET 6 As more things move to having the 7 SDK installed, let's pin for now. This helps with mobile build scenarios, which fall over on the new SDKs and require further attention. --- global.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 global.json diff --git a/global.json b/global.json new file mode 100644 index 0000000000..5dcd5f425a --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "6.0.100", + "rollForward": "latestFeature" + } +} + From 8e0a97ca4965a5d058fc16abb03ec9bc91ad0067 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 20 May 2023 18:02:12 +0200 Subject: [PATCH 115/195] Remove usage of `HasSubmenu` Property has been removed in the appropriate framework-side PR and instead folded into `IsActionable`. See: https://github.com/ppy/osu-framework/pull/5658#discussion_r1114834647 --- osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs index 0adff11342..eb046932e6 100644 --- a/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs +++ b/osu.Game/Graphics/UserInterface/DrawableOsuMenuItem.cs @@ -77,12 +77,10 @@ namespace osu.Game.Graphics.UserInterface private void updateState() { - bool enabledState = IsActionable || HasSubmenu; + hoverClickSounds.Enabled.Value = IsActionable; + Alpha = IsActionable ? 1 : 0.2f; - hoverClickSounds.Enabled.Value = enabledState; - Alpha = enabledState ? 1 : 0.2f; - - if (IsHovered && enabledState) + if (IsHovered && IsActionable) { text.BoldText.FadeIn(transition_length, Easing.OutQuint); text.NormalText.FadeOut(transition_length, Easing.OutQuint); From a677d87d39b49e1ba9f471f720d61c292d9c3547 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 20 May 2023 19:29:59 +0200 Subject: [PATCH 116/195] Touch up inline comments --- osu.Game/Online/Chat/MessageNotifier.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/MessageNotifier.cs b/osu.Game/Online/Chat/MessageNotifier.cs index cb29fc4535..52bdd36169 100644 --- a/osu.Game/Online/Chat/MessageNotifier.cs +++ b/osu.Game/Online/Chat/MessageNotifier.cs @@ -93,7 +93,7 @@ namespace osu.Game.Online.Chat if (channel == null) return; - // Only send notifications, if ChatOverlay and the target channel aren't visible, or if the window is unfocused + // Only send notifications if ChatOverlay or the target channel aren't visible, or if the window is unfocused if (chatOverlay.IsPresent && channelManager.CurrentChannel.Value == channel && host.IsActive.Value) return; @@ -103,7 +103,7 @@ namespace osu.Game.Online.Chat if (message.Id <= channel.LastReadId) return; - // ignore notifications triggered by your own chat messages + // ignore notifications triggered by local user's own chat messages if (message.Sender.Id == localUser.Value.Id) continue; From ec5f0bbf421828ee8d3ab42ddcec3ab2679baf1c Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 20 May 2023 16:29:49 -0700 Subject: [PATCH 117/195] Fix clicking area of news sidebar post links Side effect is that the hover color is yellow and pressing it opens an external dialog, but those are temporary (pending implementation of link underline to make `Light1` hover more readable and set at a higher level and news pages). --- .../Overlays/News/Sidebar/MonthSection.cs | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 30d29048ba..4dccc07eff 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -20,7 +20,7 @@ using System.Diagnostics; using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Extensions.LocalisationExtensions; -using osu.Framework.Platform; +using osu.Game.Online.Chat; namespace osu.Game.Overlays.News.Sidebar { @@ -123,35 +123,14 @@ namespace osu.Game.Overlays.News.Sidebar } } - private partial class PostButton : OsuHoverContainer + private partial class PostButton : LinkFlowContainer { - protected override IEnumerable EffectTargets => new[] { text }; - - private readonly TextFlowContainer text; - private readonly APINewsPost post; - public PostButton(APINewsPost post) + : base(t => t.Font = OsuFont.GetFont(size: 12)) { - this.post = post; - RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - Child = text = new TextFlowContainer(t => t.Font = OsuFont.GetFont(size: 12)) - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Text = post.Title - }; - } - - [BackgroundDependencyLoader] - private void load(OverlayColourProvider overlayColours, GameHost host) - { - IdleColour = overlayColours.Light2; - HoverColour = overlayColours.Light1; - - TooltipText = "view in browser"; - Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug); + AddLink(post.Title, LinkAction.External, "https://osu.ppy.sh/home/news/" + post.Slug, "view in browser"); } } From 8aefb62532aebf3430f19aae09bbcedce8fce7c7 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 20 May 2023 16:38:56 -0700 Subject: [PATCH 118/195] Rename `PostButton` to `PostLink` --- osu.Game/Overlays/News/Sidebar/MonthSection.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index 4dccc07eff..b586d156ad 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays.News.Sidebar new PostsContainer { Expanded = { BindTarget = Expanded }, - Children = posts.Select(p => new PostButton(p)).ToArray() + Children = posts.Select(p => new PostLink(p)).ToArray() } } }; @@ -123,9 +123,9 @@ namespace osu.Game.Overlays.News.Sidebar } } - private partial class PostButton : LinkFlowContainer + private partial class PostLink : LinkFlowContainer { - public PostButton(APINewsPost post) + public PostLink(APINewsPost post) : base(t => t.Font = OsuFont.GetFont(size: 12)) { RelativeSizeAxes = Axes.X; From 5229cf7343ea6d562598fbbdf67a6c2e19bb51b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 May 2023 16:47:16 +0200 Subject: [PATCH 119/195] Add failing test cases for drum roll/swell sample playback --- .../TestSceneDrumSampleTriggerSource.cs | 273 ++++++++++++++++++ .../UI/GameplaySampleTriggerSource.cs | 2 +- 2 files changed, 274 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs new file mode 100644 index 0000000000..0c8f18badd --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs @@ -0,0 +1,273 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Timing; +using osu.Game.Audio; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Game.Rulesets.Taiko.UI; +using osu.Game.Rulesets.UI; +using osu.Game.Rulesets.UI.Scrolling; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public partial class TestSceneDrumSampleTriggerSource : OsuTestScene + { + private readonly ManualClock manualClock = new ManualClock(); + + [Cached(typeof(IScrollingInfo))] + private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo + { + Direction = { Value = ScrollingDirection.Left }, + TimeRange = { Value = 200 }, + }; + + private ScrollingHitObjectContainer hitObjectContainer = null!; + private TestDrumSampleTriggerSource triggerSource = null!; + + [SetUp] + public void SetUp() => Schedule(() => + { + hitObjectContainer = new ScrollingHitObjectContainer(); + manualClock.CurrentTime = 0; + + Child = new Container + { + Clock = new FramedClock(manualClock), + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + hitObjectContainer, + triggerSource = new TestDrumSampleTriggerSource(hitObjectContainer) + } + }; + }); + + [Test] + public void TestNormalHit() + { + AddStep("add hit with normal samples", () => + { + var hit = new Hit + { + StartTime = 100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) + } + }; + hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableHit = new DrawableHit(hit); + hitObjectContainer.Add(drawableHit); + }); + + AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + + AddStep("seek past hit", () => manualClock.CurrentTime = 200); + AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + } + + [Test] + public void TestSoftHit() + { + AddStep("add hit with soft samples", () => + { + var hit = new Hit + { + StartTime = 100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft") + } + }; + hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableHit = new DrawableHit(hit); + hitObjectContainer.Add(drawableHit); + }); + + AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + + AddStep("seek past hit", () => manualClock.CurrentTime = 200); + AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + } + + [Test] + public void TestNormalDrumRoll() + { + AddStep("add drum roll with normal samples", () => + { + var drumRoll = new DrumRoll + { + StartTime = 100, + EndTime = 1100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) + } + }; + drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableDrumRoll = new DrawableDrumRoll(drumRoll); + hitObjectContainer.Add(drawableDrumRoll); + }); + + AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + + AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600); + AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + + AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200); + AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + } + + [Test] + public void TestSoftDrumRoll() + { + AddStep("add drum roll with soft samples", () => + { + var drumRoll = new DrumRoll + { + StartTime = 100, + EndTime = 1100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft") + } + }; + drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableDrumRoll = new DrawableDrumRoll(drumRoll); + hitObjectContainer.Add(drawableDrumRoll); + }); + + AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + + AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600); + AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + + AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200); + AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + } + + [Test] + public void TestNormalSwell() + { + AddStep("add swell with normal samples", () => + { + var swell = new Swell + { + StartTime = 100, + EndTime = 1100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) + } + }; + swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableSwell = new DrawableSwell(swell); + hitObjectContainer.Add(drawableSwell); + }); + + AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + + AddStep("seek to middle of swell", () => manualClock.CurrentTime = 600); + AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + + AddStep("seek past swell", () => manualClock.CurrentTime = 1200); + AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK); + } + + [Test] + public void TestDrumSwell() + { + AddStep("add swell with drum samples", () => + { + var swell = new Swell() + { + StartTime = 100, + EndTime = 1100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum") + } + }; + swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableSwell = new DrawableSwell(swell); + hitObjectContainer.Add(drawableSwell); + }); + + AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + + AddStep("seek to middle of swell", () => manualClock.CurrentTime = 600); + AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + + AddStep("seek past swell", () => manualClock.CurrentTime = 1200); + AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + } + + private void checkSound(HitType hitType, string expectedName, string expectedBank) + { + AddStep($"hit {hitType}", () => triggerSource.Play(hitType)); + AddAssert($"last played sample is {expectedName}", () => triggerSource.LastPlayedSamples!.OfType().Single().Name, () => Is.EqualTo(expectedName)); + AddAssert($"last played sample has {expectedBank} bank", () => triggerSource.LastPlayedSamples!.OfType().Single().Bank, () => Is.EqualTo(expectedBank)); + } + + private partial class TestDrumSampleTriggerSource : DrumSampleTriggerSource + { + public ISampleInfo[]? LastPlayedSamples { get; private set; } + + public TestDrumSampleTriggerSource(HitObjectContainer hitObjectContainer) + : base(hitObjectContainer) + { + } + + protected override void PlaySamples(ISampleInfo[] samples) + { + base.PlaySamples(samples); + LastPlayedSamples = samples; + } + + public new HitObject GetMostValidObject() => base.GetMostValidObject(); + } + } +} diff --git a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs index d4510a4519..fbb7a20a5d 100644 --- a/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs +++ b/osu.Game/Rulesets/UI/GameplaySampleTriggerSource.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.UI PlaySamples(samples); } - protected void PlaySamples(ISampleInfo[] samples) => Schedule(() => + protected virtual void PlaySamples(ISampleInfo[] samples) => Schedule(() => { var hitSound = getNextSample(); hitSound.Samples = samples; From 6d325651dcca275091d51bbfbad3e59fa4ce4dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 May 2023 16:49:17 +0200 Subject: [PATCH 120/195] Propagate samples to drum roll/swell ticks for correct playback In d97daee96be2c10f90709cc30beceb1f369ae225, `DrumSampleTriggerSource` was changed such that in order to play sounds for the user's inputs, the bank of the normal sound would always be used. The problem is that in the case of taiko objects which have nested objects (swells and drum rolls), the samples were not propagated fully (drum rolls, where only the finish sample was kept, for the purposes of determining strongability), or not propagated at all (swells) to ticks. As ticks of both objects are valid return values of `GetMostValidHitObject()`, this would lead to the drum making no sounds if the next object was a drum roll or swell, until that drum roll or swell was completed. To fix, propagate the full set of samples, so that `DrumSampleTriggerSource` can retrieve the normal sound to copy the bank from. Note that this may not necessarily reproduce prior behaviour. This is because it is not guaranteed that all realised samples for a given hitobject have the same bank - some may have been overriden locally on a given hitobject. Previously, the bank would have been retrieved from the sample control point, wherein there is only one possible bank to use; however, when deciding the sound to play on the basis of a constructed hitobject, it is possible that there are cases wherein the hitnormal sample was overridden on that given hitobject, and in such cases, this PR would make samples _play_, but not necessarily the _same_ samples as prior to #23308. If that turns out to be the case, this will have to be revisited. --- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 4 +--- osu.Game.Rulesets.Taiko/Objects/Swell.cs | 5 ++++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index b4a12fd314..ba68967fbe 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -3,11 +3,9 @@ #nullable disable -using System.Linq; using osu.Game.Rulesets.Objects.Types; using System.Threading; using osu.Framework.Bindables; -using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; @@ -98,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Objects TickSpacing = tickSpacing, StartTime = t, IsStrong = IsStrong, - Samples = Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToList() + Samples = Samples }); first = false; diff --git a/osu.Game.Rulesets.Taiko/Objects/Swell.cs b/osu.Game.Rulesets.Taiko/Objects/Swell.cs index cb91c46b4d..9ad783ba7e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Swell.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Swell.cs @@ -33,7 +33,10 @@ namespace osu.Game.Rulesets.Taiko.Objects for (int i = 0; i < RequiredHits; i++) { cancellationToken.ThrowIfCancellationRequested(); - AddNested(new SwellTick()); + AddNested(new SwellTick + { + Samples = Samples + }); } } From 812df9d652e73c55565a0667f09f8e0e480fcb29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 May 2023 17:14:24 +0200 Subject: [PATCH 121/195] Add failing test cases for strong object sample playback --- .../TestSceneDrumSampleTriggerSource.cs | 66 ++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs index 0c8f18badd..74da69e3eb 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs @@ -109,6 +109,35 @@ namespace osu.Game.Rulesets.Taiko.Tests checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); } + [Test] + public void TestDrumStrongHit() + { + AddStep("add strong hit with drum samples", () => + { + var hit = new Hit + { + StartTime = 100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum"), + new HitSampleInfo(HitSampleInfo.HIT_FINISH, "drum") // implies strong + } + }; + hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableHit = new DrawableHit(hit); + hitObjectContainer.Add(drawableHit); + }); + + AddAssert("most valid object is strong nested hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + + AddStep("seek past hit", () => manualClock.CurrentTime = 200); + AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + } + [Test] public void TestNormalDrumRoll() { @@ -177,6 +206,41 @@ namespace osu.Game.Rulesets.Taiko.Tests checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); } + [Test] + public void TestDrumStrongDrumRoll() + { + AddStep("add strong drum roll with drum samples", () => + { + var drumRoll = new DrumRoll + { + StartTime = 100, + EndTime = 1100, + Samples = new List + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum"), + new HitSampleInfo(HitSampleInfo.HIT_FINISH, "drum") // implies strong + } + }; + drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); + var drawableDrumRoll = new DrawableDrumRoll(drumRoll); + hitObjectContainer.Add(drawableDrumRoll); + }); + + AddAssert("most valid object is drum roll tick's nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + + AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600); + AddAssert("most valid object is drum roll tick's nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + + AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200); + AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum"); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum"); + } + [Test] public void TestNormalSwell() { @@ -216,7 +280,7 @@ namespace osu.Game.Rulesets.Taiko.Tests { AddStep("add swell with drum samples", () => { - var swell = new Swell() + var swell = new Swell { StartTime = 100, EndTime = 1100, From 4a7b011a53773d547b60401f609d7e394482a3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 May 2023 18:28:30 +0200 Subject: [PATCH 122/195] Propagate samples to strong nested hits too The rationale is the same as in 6d325651dcca275091d51bbfbad3e59fa4ce4dc8. Due to the recursive nature of `GameplaySampleTriggerSource.GetMostValidObject()`, in the case of nested hits, drum rolls and drum roll ticks, the nested strong hits would become the most valid object, and so without propagating the samples down to that level too, nothing would play. --- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 6 +++++- osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs | 6 +++++- osu.Game.Rulesets.Taiko/Objects/Hit.cs | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index ba68967fbe..aa5da6d710 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -107,7 +107,11 @@ namespace osu.Game.Rulesets.Taiko.Objects protected override HitWindows CreateHitWindows() => HitWindows.Empty; - protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit + { + StartTime = startTime, + Samples = Samples + }; public class StrongNestedHit : StrongNestedHitObject { diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs index 6bcb8674e6..f8203d793d 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRollTick.cs @@ -33,7 +33,11 @@ namespace osu.Game.Rulesets.Taiko.Objects public override double MaximumJudgementOffset => HitWindow; - protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit + { + StartTime = startTime, + Samples = Samples + }; public class StrongNestedHit : StrongNestedHitObject { diff --git a/osu.Game.Rulesets.Taiko/Objects/Hit.cs b/osu.Game.Rulesets.Taiko/Objects/Hit.cs index 303447e672..8935878f0e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Hit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Hit.cs @@ -72,7 +72,11 @@ namespace osu.Game.Rulesets.Taiko.Objects } } - protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime }; + protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit + { + StartTime = startTime, + Samples = Samples + }; public class StrongNestedHit : StrongNestedHitObject { From 9915fac2c825a5b06555bf440f61748b10cccd06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 May 2023 18:31:26 +0200 Subject: [PATCH 123/195] Fix sample silence being one level too low 4a7b011a53773d547b60401f609d7e394482a3ab inadvertently unearthed that nested strong hits could play samples of their own accord, rather than delegating to `DrumSampleTriggerSource` as they were supposed to. This was an unfortunate omission due to how the inheritance structure of `TaikoHitObject` looks like (some irrelevant classes omitted for brevity): DrawableTaikoHitObject DrawableTaikoHitObject <-- `GetSamples()` was overridden to empty here DrawableTaikoStrongableHitObject DrawableHit DrawableDrumRoll DrawableDrumRollTick DrawableSwell DrawableSwellTick DrawableStrongNestedHit <-- all strong nested hits are here => didn't receive `GetSamples()` override DrawableHit.StrongNestedHit DrawableDrumRoll.StrongNestedHit DrawableDrumRollTick.StrongNestedHit To fix, move the `GetSamples()` override one level higher, to the non-generic `DrawableTaikoHitObject`, to suppress the spurious sample playbacks. The stale reference in the comment was also updated to match current code. --- .../Objects/Drawables/DrawableTaikoHitObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs index f695c505a4..1b5d641612 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableTaikoHitObject.cs @@ -118,6 +118,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { public override bool RemoveWhenNotAlive => false; } + + // Most osu!taiko hitsounds are managed by the drum (see DrumSampleTriggerSource). + public override IEnumerable GetSamples() => Enumerable.Empty(); } public abstract partial class DrawableTaikoHitObject : DrawableTaikoHitObject @@ -157,9 +160,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables Content.Add(MainPiece = CreateMainPiece()); } - // Most osu!taiko hitsounds are managed by the drum (see DrumSampleMapping). - public override IEnumerable GetSamples() => Enumerable.Empty(); - protected abstract SkinnableDrawable CreateMainPiece(); } } From 88c112612f05c71cb75b5364753cc38bb310848f Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 21 May 2023 10:35:22 -0700 Subject: [PATCH 124/195] Remove hardcoded website url MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Overlays/News/Sidebar/MonthSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Sidebar/MonthSection.cs b/osu.Game/Overlays/News/Sidebar/MonthSection.cs index b586d156ad..9a748b2001 100644 --- a/osu.Game/Overlays/News/Sidebar/MonthSection.cs +++ b/osu.Game/Overlays/News/Sidebar/MonthSection.cs @@ -130,7 +130,7 @@ namespace osu.Game.Overlays.News.Sidebar { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - AddLink(post.Title, LinkAction.External, "https://osu.ppy.sh/home/news/" + post.Slug, "view in browser"); + AddLink(post.Title, LinkAction.External, @"/home/news/" + post.Slug, "view in browser"); } } From 19816ae0137d49709b53753dc55d137224be89a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 21 May 2023 20:38:27 +0200 Subject: [PATCH 125/195] Update framework --- 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 c73c643d4b..6aebae665d 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -11,7 +11,7 @@ manifestmerger.jar - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 3ea4a57c2c..0fd2b0c2c5 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -30,13 +30,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/osu.iOS.props b/osu.iOS.props index a240dec963..e4a169f8e5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -16,6 +16,6 @@ iossimulator-x64 - + From b3527b92b60670dd2f5a6d18af135987e0e08d60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 May 2023 09:25:17 +0900 Subject: [PATCH 126/195] Handle case in tests where current display becomes null --- .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index b76b9a40f9..a3290bc81c 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -193,6 +193,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics currentDisplay.BindValueChanged(display => Schedule(() => { + if (display.NewValue == null) + { + resolutions.Clear(); + return; + } + resolutions.ReplaceRange(1, resolutions.Count - 1, display.NewValue.DisplayModes .Where(m => m.Size.Width >= 800 && m.Size.Height >= 600) .OrderByDescending(m => Math.Max(m.Size.Height, m.Size.Width)) From 2279aad360a6f733068aa355855f17f383822782 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 21 May 2023 19:27:20 -0700 Subject: [PATCH 127/195] Apply NRT to `NewsCard` --- osu.Game/Overlays/News/NewsCard.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index e0be5cc4a9..18ca46a995 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -1,8 +1,6 @@ // 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; using System.Collections.Generic; using osu.Framework.Allocation; @@ -27,8 +25,8 @@ namespace osu.Game.Overlays.News private readonly APINewsPost post; - private Box background; - private TextFlowContainer main; + private Box background = null!; + private TextFlowContainer main = null!; public NewsCard(APINewsPost post) { From 7392109bcef1ff00170d26539cf13e29460790c4 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sun, 21 May 2023 19:28:12 -0700 Subject: [PATCH 128/195] Apply same behavioral changes to `NewsCard` --- osu.Game/Overlays/News/NewsCard.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index 18ca46a995..b12aa4509e 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; -using osu.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -39,12 +38,12 @@ namespace osu.Game.Overlays.News } [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider, GameHost host) + private void load(OverlayColourProvider colourProvider, OsuGame? game) { if (post.Slug != null) { TooltipText = "view in browser"; - Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug); + Action = () => game?.OpenUrlExternally(@"/home/news/" + post.Slug); } AddRange(new Drawable[] From 843d2903d237a12c92781ced262576f7a65c9ff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 May 2023 21:17:21 +0200 Subject: [PATCH 129/195] Add failing test case for slider velocity undo --- ...TestSceneHitObjectDifficultyPointAdjustments.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs index 3b998b4219..c874b39028 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectDifficultyPointAdjustments.cs @@ -92,6 +92,20 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasVelocity(1, 5); } + [Test] + public void TestUndo() + { + clickDifficultyPiece(1); + velocityPopoverHasSingleValue(2); + + setVelocityViaPopover(5); + hitObjectHasVelocity(1, 5); + dismissPopover(); + + AddStep("undo", () => Editor.Undo()); + hitObjectHasVelocity(1, 2); + } + [Test] public void TestMultipleSelectionWithSameSliderVelocity() { From f253d17a7f0af2d21985fbf92ca8835a510e9ee5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 May 2023 22:19:10 +0200 Subject: [PATCH 130/195] Fix slider velocity changes not being applied in patcher --- .../Screens/Edit/LegacyEditorBeatmapPatcher.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index b4647c2b64..33f8cd5c78 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -15,7 +15,9 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Legacy; using osu.Game.IO; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; using Decoder = osu.Game.Beatmaps.Formats.Decoder; @@ -42,6 +44,7 @@ namespace osu.Game.Screens.Edit editorBeatmap.BeginChange(); processHitObjects(result, () => newBeatmap ??= readBeatmap(newState)); processTimingPoints(() => newBeatmap ??= readBeatmap(newState)); + processSliderVelocity(() => newBeatmap ??= readBeatmap(newState)); editorBeatmap.EndChange(); } @@ -71,6 +74,17 @@ namespace osu.Game.Screens.Edit } } + private void processSliderVelocity(Func getNewBeatmap) + { + var legacyControlPoints = (LegacyControlPointInfo)getNewBeatmap().ControlPointInfo; + + foreach (var hitObject in editorBeatmap.HitObjects.Where(ho => ho is IHasSliderVelocity)) + { + var difficultyPoint = legacyControlPoints.DifficultyPointAt(hitObject.StartTime); + ((IHasSliderVelocity)hitObject).SliderVelocity = difficultyPoint.SliderVelocity; + } + } + private void processHitObjects(DiffResult result, Func getNewBeatmap) { findChangedIndices(result, LegacyDecoder.Section.HitObjects, out var removedIndices, out var addedIndices); From 2ce150ba2bbff1763a4be142ec802a5d942c96fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 May 2023 22:23:05 +0200 Subject: [PATCH 131/195] Add failing test case for sample undo --- .../TestSceneHitObjectSampleAdjustments.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 530bd5eb20..d812aed0f6 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -109,6 +109,21 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasSampleBank(1, "drum"); } + [Test] + public void TestUndo() + { + clickSamplePiece(1); + samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleVolume(60); + + setVolumeViaPopover(90); + hitObjectHasSampleVolume(1, 90); + dismissPopover(); + + AddStep("undo", () => Editor.Undo()); + hitObjectHasSampleVolume(1, 60); + } + [Test] public void TestMultipleSelectionWithSameSampleVolume() { From e0b7539c2a1338f079b8052bff0cf1eeb5e1de37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 May 2023 22:33:41 +0200 Subject: [PATCH 132/195] Fix sample changes not being applied in patcher --- .../Edit/LegacyEditorBeatmapPatcher.cs | 46 +++++++++++++------ 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index 33f8cd5c78..ae7105ee34 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Text; @@ -15,7 +16,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; -using osu.Game.Beatmaps.Legacy; +using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; @@ -44,7 +45,7 @@ namespace osu.Game.Screens.Edit editorBeatmap.BeginChange(); processHitObjects(result, () => newBeatmap ??= readBeatmap(newState)); processTimingPoints(() => newBeatmap ??= readBeatmap(newState)); - processSliderVelocity(() => newBeatmap ??= readBeatmap(newState)); + processHitObjectLocalData(() => newBeatmap ??= readBeatmap(newState)); editorBeatmap.EndChange(); } @@ -74,17 +75,6 @@ namespace osu.Game.Screens.Edit } } - private void processSliderVelocity(Func getNewBeatmap) - { - var legacyControlPoints = (LegacyControlPointInfo)getNewBeatmap().ControlPointInfo; - - foreach (var hitObject in editorBeatmap.HitObjects.Where(ho => ho is IHasSliderVelocity)) - { - var difficultyPoint = legacyControlPoints.DifficultyPointAt(hitObject.StartTime); - ((IHasSliderVelocity)hitObject).SliderVelocity = difficultyPoint.SliderVelocity; - } - } - private void processHitObjects(DiffResult result, Func getNewBeatmap) { findChangedIndices(result, LegacyDecoder.Section.HitObjects, out var removedIndices, out var addedIndices); @@ -101,6 +91,36 @@ namespace osu.Game.Screens.Edit } } + private void processHitObjectLocalData(Func getNewBeatmap) + { + // This method handles data that are stored in control points in the legacy format, + // but were moved to the hitobjects themselves in lazer. + // Specifically, the data being referred to here consists of: slider velocity and sample information. + + // For simplicity, this implementation relies on the editor beatmap already having the same hitobjects in sequence as the new beatmap. + // To guarantee that, `processHitObjects()` must be ran prior to this method for correct operation. + // This is done to avoid the necessity of reimplementing/reusing parts of LegacyBeatmapDecoder that already treat this data correctly. + + var oldObjects = editorBeatmap.HitObjects; + var newObjects = getNewBeatmap().HitObjects; + + Debug.Assert(oldObjects.Count == newObjects.Count); + + foreach (var (oldObject, newObject) in oldObjects.Zip(newObjects)) + { + if (oldObject is IHasSliderVelocity oldWithVelocity && newObject is IHasSliderVelocity newWithVelocity) + oldWithVelocity.SliderVelocity = newWithVelocity.SliderVelocity; + + oldObject.Samples = newObject.Samples; + + if (oldObject is IHasRepeats oldWithRepeats && newObject is IHasRepeats newWithRepeats) + { + oldWithRepeats.NodeSamples.Clear(); + oldWithRepeats.NodeSamples.AddRange(newWithRepeats.NodeSamples); + } + } + } + private void findChangedIndices(DiffResult result, LegacyDecoder.Section section, out List removedIndices, out List addedIndices) { removedIndices = new List(); From 38b4bd8aefd738af26e26c1bf2309995f7253aee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 May 2023 22:45:39 +0200 Subject: [PATCH 133/195] Fix undo not behaving as expected sometimes --- osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs index ae7105ee34..2cf823ca0c 100644 --- a/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs +++ b/osu.Game/Screens/Edit/LegacyEditorBeatmapPatcher.cs @@ -108,6 +108,11 @@ namespace osu.Game.Screens.Edit foreach (var (oldObject, newObject) in oldObjects.Zip(newObjects)) { + // if `oldObject` and `newObject` are the same, it means that `oldObject` was inserted into `editorBeatmap` by `processHitObjects()`. + // in that case, there is nothing to do (and some of the subsequent changes may even prove destructive). + if (ReferenceEquals(oldObject, newObject)) + continue; + if (oldObject is IHasSliderVelocity oldWithVelocity && newObject is IHasSliderVelocity newWithVelocity) oldWithVelocity.SliderVelocity = newWithVelocity.SliderVelocity; From c8303d55cd72c2e152ad40393325f5ec887ff4f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2023 16:21:56 +0900 Subject: [PATCH 134/195] Adjust text and alignment --- .../Statistics/AccuracyHeatmap.cs | 72 ++++++++----------- 1 file changed, 30 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs index 9d9b3fb84a..5d2f6a14c7 100644 --- a/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs +++ b/osu.Game.Rulesets.Osu/Statistics/AccuracyHeatmap.cs @@ -57,6 +57,8 @@ namespace osu.Game.Rulesets.Osu.Statistics [BackgroundDependencyLoader] private void load() { + const float line_extension = 0.2f; + InternalChild = new Container { Anchor = Anchor.Centre, @@ -97,81 +99,67 @@ namespace osu.Game.Rulesets.Osu.Statistics RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new Box + new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - EdgeSmoothness = new Vector2(1), RelativeSizeAxes = Axes.Y, - Width = line_thickness / 2, // adjust for edgesmoothness - Height = MathF.Sqrt(2), + Width = line_thickness, + Height = inner_portion + line_extension, Rotation = -rotation * 2, - Alpha = 0.3f, + Alpha = 0.6f, }, - new Box + new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - EdgeSmoothness = new Vector2(1), RelativeSizeAxes = Axes.Y, - Width = line_thickness / 2, // adjust for edgesmoothness - Height = MathF.Sqrt(2), + Width = line_thickness, + Height = inner_portion + line_extension, }, new OsuSpriteText { - Text = "Next", + Text = "Overshoot", Anchor = Anchor.Centre, - Origin = Anchor.BottomRight, + Origin = Anchor.BottomCentre, Padding = new MarginPadding(3), RelativePositionAxes = Axes.Both, - Y = -inner_portion / 2, + Y = -(inner_portion + line_extension) / 2, }, new OsuSpriteText { - Text = "object", + Text = "Undershoot", Anchor = Anchor.Centre, - Origin = Anchor.BottomLeft, + Origin = Anchor.TopCentre, Padding = new MarginPadding(3), RelativePositionAxes = Axes.Both, - Y = -inner_portion / 2, + Y = (inner_portion + line_extension) / 2, }, - new OsuSpriteText + new Circle { - Text = "Last", Anchor = Anchor.Centre, - Origin = Anchor.TopRight, - Padding = new MarginPadding(3), + Origin = Anchor.TopCentre, RelativePositionAxes = Axes.Both, - Y = inner_portion / 2, + Y = -(inner_portion + line_extension) / 2, + Margin = new MarginPadding(-line_thickness / 2), + Width = line_thickness, + Height = 10, + Rotation = 45, }, - new OsuSpriteText + new Circle { - Text = "object", Anchor = Anchor.Centre, - Origin = Anchor.TopLeft, - Padding = new MarginPadding(3), + Origin = Anchor.TopCentre, RelativePositionAxes = Axes.Both, - Y = inner_portion / 2, - }, + Y = -(inner_portion + line_extension) / 2, + Margin = new MarginPadding(-line_thickness / 2), + Width = line_thickness, + Height = 10, + Rotation = -45, + } } }, }, - new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - Width = 10, - EdgeSmoothness = new Vector2(1), - Height = line_thickness / 2, // adjust for edgesmoothness - }, - new Box - { - Anchor = Anchor.TopRight, - Origin = Anchor.TopRight, - EdgeSmoothness = new Vector2(1), - Width = line_thickness / 2, // adjust for edgesmoothness - Height = 10, - } } }, bufferedGrid = new BufferedContainer(cachedFrameBuffer: true) From adf9a596b5fd4cb5e71519e79c990b371dbb7132 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2023 17:58:10 +0900 Subject: [PATCH 135/195] Fix weird state when attempting to enter gameplay skin editor scene from multiplayer Closes https://github.com/ppy/osu/issues/23626. --- osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs index 61195d7175..9b021632cf 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs @@ -113,7 +113,7 @@ namespace osu.Game.Overlays.SkinEditor if (replayGeneratingMod != null) screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)))); - }, new[] { typeof(Player), typeof(SongSelect) }) + }, new[] { typeof(Player), typeof(PlaySongSelect) }) }, } }, From 7cf50b1e18a74b74fc34396a1c4ddaf444d6dc6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2023 18:06:04 +0900 Subject: [PATCH 136/195] Disallow game to check for updates while gameplay is active --- osu.Desktop/Updater/SquirrelUpdateManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Desktop/Updater/SquirrelUpdateManager.cs b/osu.Desktop/Updater/SquirrelUpdateManager.cs index 3d4db88471..941ab335e8 100644 --- a/osu.Desktop/Updater/SquirrelUpdateManager.cs +++ b/osu.Desktop/Updater/SquirrelUpdateManager.cs @@ -9,6 +9,7 @@ using osu.Framework.Logging; using osu.Game; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; +using osu.Game.Screens.Play; using Squirrel; using Squirrel.SimpleSplat; using LogLevel = Squirrel.SimpleSplat.LogLevel; @@ -36,6 +37,9 @@ namespace osu.Desktop.Updater [Resolved] private OsuGameBase game { get; set; } = null!; + [Resolved] + private ILocalUserPlayInfo? localUserInfo { get; set; } + [BackgroundDependencyLoader] private void load(INotificationOverlay notifications) { @@ -55,6 +59,10 @@ namespace osu.Desktop.Updater try { + // Avoid any kind of update checking while gameplay is running. + if (localUserInfo?.IsPlaying.Value == true) + return false; + updateManager ??= new GithubUpdateManager(@"https://github.com/ppy/osu", false, github_token, @"osulazer"); var info = await updateManager.CheckForUpdate(!useDeltaPatching).ConfigureAwait(false); From 6f4e2b37edfdf443f993caf679c061bb38909d38 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2023 18:47:56 +0900 Subject: [PATCH 137/195] Add shadow to notifications and settings overlays to better distinguish from other overlays --- osu.Game/Overlays/NotificationOverlay.cs | 13 +++++++++++++ osu.Game/Overlays/SettingsPanel.cs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 71a4c58afd..4a69fb6240 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -5,9 +5,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Framework.Logging; @@ -16,6 +18,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Overlays.Notifications; using osu.Game.Resources.Localisation.Web; using osuTK; +using osuTK.Graphics; using NotificationsStrings = osu.Game.Localisation.NotificationsStrings; namespace osu.Game.Overlays @@ -72,6 +75,14 @@ namespace osu.Game.Overlays mainContent = new Container { RelativeSizeAxes = Axes.Both, + Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Radius = 10, + Hollow = true, + }, Children = new Drawable[] { new Box @@ -199,6 +210,7 @@ namespace osu.Game.Overlays this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); mainContent.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + mainContent.FadeEdgeEffectTo(0.4f, WaveContainer.APPEAR_DURATION, Easing.Out); toastTray.FlushAllToasts(); } @@ -211,6 +223,7 @@ namespace osu.Game.Overlays this.MoveToX(WIDTH, TRANSITION_LENGTH, Easing.OutQuint); mainContent.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); + mainContent.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In); } private void notificationClosed() => Schedule(() => diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index aefaccdb5d..382423eb57 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -10,15 +10,18 @@ using System.Threading.Tasks; using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.Settings; +using osuTK.Graphics; namespace osu.Game.Overlays { @@ -105,6 +108,13 @@ namespace osu.Game.Overlays Add(SectionsContainer = new SettingsSectionsContainer { Masking = true, + EdgeEffect = new EdgeEffectParameters + { + Colour = Color4.Black.Opacity(0), + Type = EdgeEffectType.Shadow, + Hollow = true, + Radius = 10 + }, RelativeSizeAxes = Axes.Both, ExpandableHeader = CreateHeader(), SelectedSection = { BindTarget = CurrentSection }, @@ -156,6 +166,8 @@ namespace osu.Game.Overlays ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); + SectionsContainer.FadeEdgeEffectTo(0.4f, WaveContainer.APPEAR_DURATION, Easing.Out); + // delay load enough to ensure it doesn't overlap with the initial animation. // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress. // the eventual goal would be to remove the need for this by splitting up load into smaller work pieces, or fixing the remaining @@ -175,6 +187,7 @@ namespace osu.Game.Overlays { base.PopOut(); + SectionsContainer.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In); ContentContainer.MoveToX(-WIDTH + ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint); From 02d8e3a11e4d245f7b5587c59b4217418718d00a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 May 2023 18:48:10 +0900 Subject: [PATCH 138/195] Mark `FullscreenOverlay`'s shadow effect as `Hollow` to save on shader overhead --- osu.Game/Overlays/FullscreenOverlay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs index 2cc8354e50..007791387c 100644 --- a/osu.Game/Overlays/FullscreenOverlay.cs +++ b/osu.Game/Overlays/FullscreenOverlay.cs @@ -56,6 +56,7 @@ namespace osu.Game.Overlays { Colour = Color4.Black.Opacity(0), Type = EdgeEffectType.Shadow, + Hollow = true, Radius = 10 }; From ebda35c3c9a896861ec526bad90ebc2e154b3131 Mon Sep 17 00:00:00 2001 From: Johannes vd Berg Date: Tue, 23 May 2023 12:57:25 +0200 Subject: [PATCH 139/195] Add ghost ticks to exhibit current divisor on `BeatDivisorControl` --- .../Compose/Components/BeatDivisorControl.cs | 37 +++++++++++-------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 3bfe81e6a7..431fb2f659 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -398,28 +398,25 @@ namespace osu.Game.Screens.Edit.Compose.Components ClearInternal(); CurrentNumber.ValueChanged -= moveMarker; - foreach (int divisor in beatDivisor.ValidDivisors.Value.Presets) + int largestDivisor = beatDivisor.ValidDivisors.Value.Presets.Max(); + for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { - AddInternal(new Tick(divisor) + int divisor = largestDivisor; + foreach (int validDivisor in beatDivisor.ValidDivisors.Value.Presets) + { + if (divisor > validDivisor && (tickIndex * validDivisor) % largestDivisor == 0) + divisor = validDivisor; + } + bool solidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; + AddInternal(new Tick(solidTick, divisor) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, RelativePositionAxes = Axes.Both, Colour = BindableBeatDivisor.GetColourFor(divisor, colours), - X = getMappedPosition(divisor), + X = tickIndex / (float)largestDivisor, }); } - - // Add a fake 1/1 at the end to give context. - AddInternal(new Tick(1) - { - Anchor = Anchor.CentreRight, - Origin = Anchor.Centre, - Depth = float.MaxValue, - Alpha = 0.05f, - Colour = BindableBeatDivisor.GetColourFor(1, colours), - }); - AddInternal(marker = new Marker()); CurrentNumber.ValueChanged += moveMarker; CurrentNumber.TriggerChange(); @@ -428,6 +425,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void moveMarker(ValueChangedEvent divisor) { marker.MoveToX(getMappedPosition(divisor.NewValue), 100, Easing.OutQuint); + + foreach (Tick child in InternalChildren.OfType()) + { + float newAlpha = child.Solid ? 1f : divisor.NewValue % child.Divisor == 0 ? 0.2f : 0f; + child.FadeTo(newAlpha); + } } protected override void UpdateValue(float value) @@ -497,8 +500,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private partial class Tick : Circle { - public Tick(int divisor) + public bool Solid; + public int Divisor; + public Tick(bool solid, int divisor) { + Solid = solid; + Divisor = divisor; Size = new Vector2(6f, 12) * BindableBeatDivisor.GetSize(divisor); InternalChild = new Box { RelativeSizeAxes = Axes.Both }; } From 37a796306d65a5c940d8cd715021083bdef9990a Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 14:30:35 +0200 Subject: [PATCH 140/195] Small format & comment --- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 431fb2f659..04cc736e7d 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -402,13 +402,15 @@ namespace osu.Game.Screens.Edit.Compose.Components for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { int divisor = largestDivisor; + // Find lowest divisor that the tick fits into foreach (int validDivisor in beatDivisor.ValidDivisors.Value.Presets) { if (divisor > validDivisor && (tickIndex * validDivisor) % largestDivisor == 0) divisor = validDivisor; } - bool solidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; - AddInternal(new Tick(solidTick, divisor) + + bool isSolidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; + AddInternal(new Tick(isSolidTick, divisor) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, @@ -417,6 +419,7 @@ namespace osu.Game.Screens.Edit.Compose.Components X = tickIndex / (float)largestDivisor, }); } + AddInternal(marker = new Marker()); CurrentNumber.ValueChanged += moveMarker; CurrentNumber.TriggerChange(); From 1b32370c6a489ec618e7022b197f3c82518a183a Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 15:05:38 +0200 Subject: [PATCH 141/195] Remove duplicate code by making `GetDivisorForBeatIndex` method more general --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 7 +++++-- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 8 +------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index aa8e202e22..f1b97571bd 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -154,12 +154,15 @@ namespace osu.Game.Screens.Edit /// /// The 0-based beat index. /// The beat divisor. + /// The list of valid divisors which can be chosen from. Assumes ordered from low to high. /// The applicable divisor. - public static int GetDivisorForBeatIndex(int index, int beatDivisor) + public static int GetDivisorForBeatIndex(int index, int beatDivisor, int[] validDivisors = null) { + validDivisors ??= PREDEFINED_DIVISORS; + int beat = index % beatDivisor; - foreach (int divisor in PREDEFINED_DIVISORS) + foreach (int divisor in validDivisors) { if ((beat * divisor) % beatDivisor == 0) return divisor; diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 04cc736e7d..2dd5791943 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -401,13 +401,7 @@ namespace osu.Game.Screens.Edit.Compose.Components int largestDivisor = beatDivisor.ValidDivisors.Value.Presets.Max(); for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { - int divisor = largestDivisor; - // Find lowest divisor that the tick fits into - foreach (int validDivisor in beatDivisor.ValidDivisors.Value.Presets) - { - if (divisor > validDivisor && (tickIndex * validDivisor) % largestDivisor == 0) - divisor = validDivisor; - } + int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(tickIndex, largestDivisor, (int[])beatDivisor.ValidDivisors.Value.Presets); bool isSolidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; AddInternal(new Tick(isSolidTick, divisor) From 7b1e8ede54498bf66d54965769220dc626240c1f Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 15:11:27 +0200 Subject: [PATCH 142/195] Small format --- osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 2dd5791943..1af7d25dcd 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -402,8 +402,8 @@ namespace osu.Game.Screens.Edit.Compose.Components for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(tickIndex, largestDivisor, (int[])beatDivisor.ValidDivisors.Value.Presets); - bool isSolidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; + AddInternal(new Tick(isSolidTick, divisor) { Anchor = Anchor.CentreLeft, From 921d7e4d89351a50fe6ec4e65f1ec01b3d650a36 Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 16:46:08 +0200 Subject: [PATCH 143/195] More fitting tests for new layout --- .../Editing/TestSceneBeatDivisorControl.cs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index c3d5ecac5c..b74e9f436b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -65,17 +65,24 @@ namespace osu.Game.Tests.Visual.Editing InputManager.MoveMouseTo(tickMarkerHead.ScreenSpaceDrawQuad.Centre); InputManager.PressButton(MouseButton.Left); }); - AddStep("move to 8 and release", () => + AddStep("move to 1", () => InputManager.MoveMouseTo(getPositionForDivisor(1))); + AddStep("move to 16 and release", () => { - InputManager.MoveMouseTo(tickSliderBar.ScreenSpaceDrawQuad.Centre); + InputManager.MoveMouseTo(getPositionForDivisor(16)); InputManager.ReleaseButton(MouseButton.Left); }); - AddAssert("divisor is 8", () => bindableBeatDivisor.Value == 8); + AddAssert("divisor is 16", () => bindableBeatDivisor.Value == 16); AddStep("hold marker", () => InputManager.PressButton(MouseButton.Left)); - AddStep("move to 16", () => InputManager.MoveMouseTo(getPositionForDivisor(16))); - AddStep("move to ~10 and release", () => + AddStep("move to ~6 and release", () => + { + InputManager.MoveMouseTo(getPositionForDivisor(6)); + InputManager.ReleaseButton(MouseButton.Left); + }); + AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8); + AddStep("move to ~10 and click", () => { InputManager.MoveMouseTo(getPositionForDivisor(10)); + InputManager.PressButton(MouseButton.Left); InputManager.ReleaseButton(MouseButton.Left); }); AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8); From fa00f8b92a56db6669835efa70131e69308c9f67 Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 16:46:40 +0200 Subject: [PATCH 144/195] replace manual code with existing method --- .../Visual/Editing/TestSceneBeatDivisorControl.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index b74e9f436b..a4b36ef93a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -90,12 +90,11 @@ namespace osu.Game.Tests.Visual.Editing private Vector2 getPositionForDivisor(int divisor) { - float relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16; - var sliderDrawQuad = tickSliderBar.ScreenSpaceDrawQuad; - return new Vector2( - sliderDrawQuad.TopLeft.X + sliderDrawQuad.Width * relativePosition, - sliderDrawQuad.Centre.Y - ); + float localX = 1 - 1 / (float)divisor; + return tickSliderBar.ToScreenSpace(new Vector2( + localX, + 0.5f + )); } [Test] From b5f8093941873b6aa95bc62a61f7eb5d29e9ce52 Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 17:59:07 +0200 Subject: [PATCH 145/195] Use `RangePadding` to align mouse with slider --- osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs | 4 ++-- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs index a4b36ef93a..353acfa4ba 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneBeatDivisorControl.cs @@ -90,10 +90,10 @@ namespace osu.Game.Tests.Visual.Editing private Vector2 getPositionForDivisor(int divisor) { - float localX = 1 - 1 / (float)divisor; + float localX = (1 - 1 / (float)divisor) * tickSliderBar.UsableWidth + tickSliderBar.RangePadding; return tickSliderBar.ToScreenSpace(new Vector2( localX, - 0.5f + tickSliderBar.DrawHeight / 2 )); } diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 3bfe81e6a7..cec9806e94 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -383,7 +383,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { CurrentNumber.BindTo(this.beatDivisor = beatDivisor); - Padding = new MarginPadding { Horizontal = 5 }; + RangePadding = 5; + Padding = new MarginPadding { Horizontal = RangePadding }; } protected override void LoadComplete() From c5ef3ae1811f9216c2023c92de67ae2d548f4a66 Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 18:44:20 +0200 Subject: [PATCH 146/195] Code styling --- .../Screens/Edit/Compose/Components/BeatDivisorControl.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 1af7d25dcd..4c3c9872a0 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -425,7 +425,7 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach (Tick child in InternalChildren.OfType()) { - float newAlpha = child.Solid ? 1f : divisor.NewValue % child.Divisor == 0 ? 0.2f : 0f; + float newAlpha = child.IsSolid ? 1f : divisor.NewValue % child.Divisor == 0 ? 0.2f : 0f; child.FadeTo(newAlpha); } } @@ -497,11 +497,11 @@ namespace osu.Game.Screens.Edit.Compose.Components private partial class Tick : Circle { - public bool Solid; + public bool IsSolid; public int Divisor; - public Tick(bool solid, int divisor) + public Tick(bool isSolid, int divisor) { - Solid = solid; + IsSolid = isSolid; Divisor = divisor; Size = new Vector2(6f, 12) * BindableBeatDivisor.GetSize(divisor); InternalChild = new Box { RelativeSizeAxes = Axes.Both }; From 7fe19d1992633fc13e3cf68c35fb21e3734d39dd Mon Sep 17 00:00:00 2001 From: Gyoshi Date: Tue, 23 May 2023 18:45:49 +0200 Subject: [PATCH 147/195] `Last` instead of `Max` divisor to match code elsewhere --- osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 4c3c9872a0..4982ab9ef2 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -398,7 +398,7 @@ namespace osu.Game.Screens.Edit.Compose.Components ClearInternal(); CurrentNumber.ValueChanged -= moveMarker; - int largestDivisor = beatDivisor.ValidDivisors.Value.Presets.Max(); + int largestDivisor = beatDivisor.ValidDivisors.Value.Presets.Last(); for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(tickIndex, largestDivisor, (int[])beatDivisor.ValidDivisors.Value.Presets); From 0ea3eea8d6acad4049dc7da12a6961f3e6e8454b Mon Sep 17 00:00:00 2001 From: Robin Oger Date: Tue, 23 May 2023 19:21:44 +0200 Subject: [PATCH 148/195] Make GameplayMenuOverlay translatable This allows translator to translate the pause and failed in game menus --- .../GameplayMenuOverlayStrings.cs | 49 +++++++++++++++++++ osu.Game/Screens/Play/FailOverlay.cs | 10 ++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 7 +-- osu.Game/Screens/Play/PauseOverlay.cs | 12 +++-- 4 files changed, 66 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Localisation/GameplayMenuOverlayStrings.cs diff --git a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs new file mode 100644 index 0000000000..bba16e014a --- /dev/null +++ b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs @@ -0,0 +1,49 @@ +// 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 GameplayMenuOverlayStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.GameplayMenuOverlay"; + + /// + /// "Continue" + /// + public static LocalisableString Continue => new TranslatableString(getKey(@"continue"), @"Continue"); + + /// + /// "Retry" + /// + public static LocalisableString Retry => new TranslatableString(getKey(@"retry"), @"Retry"); + + /// + /// "Quit" + /// + public static LocalisableString Quit => new TranslatableString(getKey(@"quit"), @"Quit"); + + /// + /// "failed" + /// + public static LocalisableString FailedHeader => new TranslatableString(getKey(@"failed_header"), @"failed"); + + /// + /// "paused" + /// + public static LocalisableString PausedHeader => new TranslatableString(getKey(@"paused_header"), @"paused"); + + /// + /// "you're dead, try again?" + /// + public static LocalisableString FailedDescription => new TranslatableString(getKey(@"failed_description"), @"you're dead, try again?"); + + /// + /// "you're not going to do what i think you're going to do, are ya?" + /// + public static LocalisableString PausedDescription => new TranslatableString(getKey(@"paused_description"), @"you're not going to do what i think you're going to do, are ya?"); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 4fbc937b59..5b026a4c06 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -15,6 +15,8 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Screens.Play { @@ -22,14 +24,14 @@ namespace osu.Game.Screens.Play { public Func> SaveReplay; - public override string Header => "failed"; - public override string Description => "you're dead, try again?"; + public override LocalisableString Header => GameplayMenuOverlayStrings.FailedHeader; + public override LocalisableString Description => GameplayMenuOverlayStrings.FailedDescription; [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); - AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Retry, colours.YellowDark, () => OnRetry?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); // from #10339 maybe this is a better visual effect Add(new Container { diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 81146a4ea6..b83d17fdb2 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -51,9 +52,9 @@ namespace osu.Game.Screens.Play /// protected virtual Action SelectAction => () => InternalButtons.Selected?.TriggerClick(); - public abstract string Header { get; } + public abstract LocalisableString Header { get; } - public abstract string Description { get; } + public abstract LocalisableString Description { get; } protected SelectionCycleFillFlowContainer InternalButtons; public IReadOnlyList Buttons => InternalButtons; @@ -170,7 +171,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) => true; - protected void AddButton(string text, Color4 colour, Action action) + protected void AddButton(LocalisableString text, Color4 colour, Action action) { var button = new Button { diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index db42998c45..c3c1c493d4 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -9,9 +9,11 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osu.Game.Skinning; using osuTK.Graphics; @@ -23,8 +25,8 @@ namespace osu.Game.Screens.Play public override bool IsPresent => base.IsPresent || pauseLoop.IsPlaying; - public override string Header => "paused"; - public override string Description => "you're not going to do what i think you're going to do, are ya?"; + public override LocalisableString Header => GameplayMenuOverlayStrings.PausedHeader; + public override LocalisableString Description => GameplayMenuOverlayStrings.PausedDescription; private SkinnableSound pauseLoop; @@ -33,9 +35,9 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Continue", colours.Green, () => OnResume?.Invoke()); - AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); - AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Continue, colours.Green, () => OnResume?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Retry, colours.YellowDark, () => OnRetry?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); AddInternal(pauseLoop = new SkinnableSound(new SampleInfo("Gameplay/pause-loop")) { From a24da89908958d1879851b5a756e11f2b48402b4 Mon Sep 17 00:00:00 2001 From: Robin Oger Date: Tue, 23 May 2023 21:37:12 +0200 Subject: [PATCH 149/195] Change to sentence casing See: https://github.com/ppy/osu/pull/23640#discussion_r1202879352 --- osu.Game/Localisation/GameplayMenuOverlayStrings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs index bba16e014a..c89c35775b 100644 --- a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs +++ b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs @@ -35,14 +35,14 @@ namespace osu.Game.Localisation public static LocalisableString PausedHeader => new TranslatableString(getKey(@"paused_header"), @"paused"); /// - /// "you're dead, try again?" + /// "You're dead, try again?" /// - public static LocalisableString FailedDescription => new TranslatableString(getKey(@"failed_description"), @"you're dead, try again?"); + public static LocalisableString FailedDescription => new TranslatableString(getKey(@"failed_description"), @"You're dead, try again?"); /// - /// "you're not going to do what i think you're going to do, are ya?" + /// "You're not going to do what i think you're going to do, are ya?" /// - public static LocalisableString PausedDescription => new TranslatableString(getKey(@"paused_description"), @"you're not going to do what i think you're going to do, are ya?"); + public static LocalisableString PausedDescription => new TranslatableString(getKey(@"paused_description"), @"You're not going to do what i think you're going to do, are ya?"); private static string getKey(string key) => $@"{prefix}:{key}"; } From 067328233cf5715e8ce00936a6ce6bbe1b9795cd Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Tue, 23 May 2023 13:14:57 -0700 Subject: [PATCH 150/195] Remove `OsuScreen.ApplyLogoArrivingDefaults()` --- osu.Game/Screens/OsuScreen.cs | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index bc4cc2b00f..9c098794a6 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -233,7 +233,13 @@ namespace osu.Game.Screens /// protected virtual void LogoArriving(OsuLogo logo, bool resuming) { - ApplyLogoArrivingDefaults(logo); + logo.Action = null; + logo.FadeOut(300, Easing.OutQuint); + logo.Anchor = Anchor.TopLeft; + logo.Origin = Anchor.Centre; + logo.RelativePositionAxes = Axes.Both; + logo.Triangles = true; + logo.Ripple = true; } private void applyArrivingDefaults(bool isResuming) @@ -244,22 +250,6 @@ namespace osu.Game.Screens }, true); } - /// - /// Applies default animations to an arriving logo. - /// Todo: This should not exist. - /// - /// The logo to apply animations to. - public static void ApplyLogoArrivingDefaults(OsuLogo logo) - { - logo.Action = null; - logo.FadeOut(300, Easing.OutQuint); - logo.Anchor = Anchor.TopLeft; - logo.Origin = Anchor.Centre; - logo.RelativePositionAxes = Axes.Both; - logo.Triangles = true; - logo.Ripple = true; - } - private void onExitingLogo() { logo?.AppendAnimatingAction(() => LogoExiting(logo), false); From e5451d1d79c7cceb3479515b8611c0954706f95c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 12:38:27 +0900 Subject: [PATCH 151/195] Centralise definition of overlay shadow opacity and reduce slightly --- osu.Game/Graphics/Containers/WaveContainer.cs | 1 + osu.Game/Overlays/FullscreenOverlay.cs | 2 +- osu.Game/Overlays/NotificationOverlay.cs | 2 +- osu.Game/Overlays/SettingsPanel.cs | 3 ++- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 952ef3f182..05a666721a 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -17,6 +17,7 @@ namespace osu.Game.Graphics.Containers { public const float APPEAR_DURATION = 800; public const float DISAPPEAR_DURATION = 500; + public const float SHADOW_OPACITY = 0.2f; private const Easing easing_show = Easing.OutSine; private const Easing easing_hide = Easing.InSine; diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs index 007791387c..032821f215 100644 --- a/osu.Game/Overlays/FullscreenOverlay.cs +++ b/osu.Game/Overlays/FullscreenOverlay.cs @@ -102,7 +102,7 @@ namespace osu.Game.Overlays protected override void PopIn() { base.PopIn(); - FadeEdgeEffectTo(0.4f, WaveContainer.APPEAR_DURATION, Easing.Out); + FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); } protected override void PopOut() diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 4a69fb6240..f2eefb6e4b 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -210,7 +210,7 @@ namespace osu.Game.Overlays this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); mainContent.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); - mainContent.FadeEdgeEffectTo(0.4f, WaveContainer.APPEAR_DURATION, Easing.Out); + mainContent.FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); toastTray.FlushAllToasts(); } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 382423eb57..d571557993 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -115,6 +115,7 @@ namespace osu.Game.Overlays Hollow = true, Radius = 10 }, + MaskingSmoothness = 0, RelativeSizeAxes = Axes.Both, ExpandableHeader = CreateHeader(), SelectedSection = { BindTarget = CurrentSection }, @@ -166,7 +167,7 @@ namespace osu.Game.Overlays ContentContainer.MoveToX(ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); - SectionsContainer.FadeEdgeEffectTo(0.4f, WaveContainer.APPEAR_DURATION, Easing.Out); + SectionsContainer.FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); // delay load enough to ensure it doesn't overlap with the initial animation. // this is done as there is still a brief stutter during load completion which is more visible if the transition is in progress. From 561b759bf980216beca1ff2639da1018bc05c742 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 13:49:29 +0900 Subject: [PATCH 152/195] Tidy up implementation and ensure non-solid ticks start at zero alpha --- .../Compose/Components/BeatDivisorControl.cs | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs index 07330a6e10..432c5ea280 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BeatDivisorControl.cs @@ -400,12 +400,13 @@ namespace osu.Game.Screens.Edit.Compose.Components CurrentNumber.ValueChanged -= moveMarker; int largestDivisor = beatDivisor.ValidDivisors.Value.Presets.Last(); + for (int tickIndex = 0; tickIndex <= largestDivisor; tickIndex++) { int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(tickIndex, largestDivisor, (int[])beatDivisor.ValidDivisors.Value.Presets); bool isSolidTick = divisor * (largestDivisor - tickIndex) == largestDivisor; - AddInternal(new Tick(isSolidTick, divisor) + AddInternal(new Tick(divisor, isSolidTick) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, @@ -424,10 +425,9 @@ namespace osu.Game.Screens.Edit.Compose.Components { marker.MoveToX(getMappedPosition(divisor.NewValue), 100, Easing.OutQuint); - foreach (Tick child in InternalChildren.OfType()) + foreach (Tick tick in InternalChildren.OfType().Where(t => !t.AlwaysDisplayed)) { - float newAlpha = child.IsSolid ? 1f : divisor.NewValue % child.Divisor == 0 ? 0.2f : 0f; - child.FadeTo(newAlpha); + tick.FadeTo(divisor.NewValue % tick.Divisor == 0 ? 0.2f : 0f, 100, Easing.OutQuint); } } @@ -498,13 +498,18 @@ namespace osu.Game.Screens.Edit.Compose.Components private partial class Tick : Circle { - public bool IsSolid; - public int Divisor; - public Tick(bool isSolid, int divisor) + public readonly bool AlwaysDisplayed; + + public readonly int Divisor; + + public Tick(int divisor, bool alwaysDisplayed) { - IsSolid = isSolid; + AlwaysDisplayed = alwaysDisplayed; Divisor = divisor; + Size = new Vector2(6f, 12) * BindableBeatDivisor.GetSize(divisor); + Alpha = alwaysDisplayed ? 1 : 0; + InternalChild = new Box { RelativeSizeAxes = Axes.Both }; } } From e68ba6366c88554db3bee4332be5f1563e4257c1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 14:04:10 +0900 Subject: [PATCH 153/195] Update new usages of "soft" to use the new constant --- .../Editor/TestSceneSliderSplitting.cs | 2 +- .../TestSceneDrumSampleTriggerSource.cs | 24 +++++++++---------- .../TestSceneHitObjectSampleAdjustments.cs | 10 ++++---- .../TestSceneGameplaySampleTriggerSource.cs | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index a104433ea9..605771fb20 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { if (slider is null) return; - sample = new HitSampleInfo("hitwhistle", "soft", volume: 70); + sample = new HitSampleInfo("hitwhistle", HitSampleInfo.BANK_SOFT, volume: 70); slider.Samples.Add(sample.With()); }); diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs index 74da69e3eb..287d90b406 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneDrumSampleTriggerSource.cs @@ -91,7 +91,7 @@ namespace osu.Game.Rulesets.Taiko.Tests StartTime = 100, Samples = new List { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft") + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT) } }; hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -100,13 +100,13 @@ namespace osu.Game.Rulesets.Taiko.Tests }); AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); - checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); - checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT); AddStep("seek past hit", () => manualClock.CurrentTime = 200); AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf); - checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); - checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT); } [Test] @@ -183,7 +183,7 @@ namespace osu.Game.Rulesets.Taiko.Tests EndTime = 1100, Samples = new List { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft") + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT) } }; drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); @@ -192,18 +192,18 @@ namespace osu.Game.Rulesets.Taiko.Tests }); AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); - checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); - checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT); AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600); AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf); - checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); - checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT); AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200); AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf); - checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft"); - checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft"); + checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT); + checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT); } [Test] diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index 0581ff269c..eb39221211 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual.Editing Position = (OsuPlayfield.BASE_SIZE + new Vector2(100, 0)) / 2, Samples = new List { - new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft", volume: 60) + new HitSampleInfo(HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT, volume: 60) } }); }); @@ -67,14 +67,14 @@ namespace osu.Game.Tests.Visual.Editing hitObjectHasSampleBank(0, "normal"); hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP); - hitObjectHasSampleBank(1, "soft"); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT); hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_CLAP); AddStep("remove clap addition", () => InputManager.Key(Key.R)); hitObjectHasSampleBank(0, "normal"); hitObjectHasSamples(0, HitSampleInfo.HIT_NORMAL); - hitObjectHasSampleBank(1, "soft"); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT); hitObjectHasSamples(1, HitSampleInfo.HIT_NORMAL); } @@ -113,7 +113,7 @@ namespace osu.Game.Tests.Visual.Editing public void TestUndo() { clickSamplePiece(1); - samplePopoverHasSingleBank("soft"); + samplePopoverHasSingleBank(HitSampleInfo.BANK_SOFT); samplePopoverHasSingleVolume(60); setVolumeViaPopover(90); @@ -178,7 +178,7 @@ namespace osu.Game.Tests.Visual.Editing { for (int i = 0; i < h.Samples.Count; i++) { - h.Samples[i] = h.Samples[i].With(newBank: "soft"); + h.Samples[i] = h.Samples[i].With(newBank: HitSampleInfo.BANK_SOFT); } } }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index bf69da8c12..e52ec6f8cc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -83,7 +83,7 @@ namespace osu.Game.Tests.Visual.Gameplay { StartTime = t += spacing, Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }), - Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, "soft") }, + Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, HitSampleInfo.BANK_SOFT) }, }, }); From a9ba16a2be5d8355248d658ddb90867799dc86ba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 14:20:38 +0900 Subject: [PATCH 154/195] Update to support non-control-point sample changes --- .../Components/ComposeBlueprintContainer.cs | 4 ++-- .../Components/EditorSelectionHandler.cs | 23 ++++--------------- 2 files changed, 6 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index b4f2236847..3f3b4ad327 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -199,12 +199,12 @@ namespace osu.Game.Screens.Edit.Compose.Components private void bankChanged(string bankName, TernaryState state) { - if (currentPlacement == null) return; + if (CurrentPlacement == null) return; switch (state) { case TernaryState.True: - currentPlacement.HitObject.SampleControlPoint.SampleBank = bankName; + CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.With(newBank: bankName)).ToList(); break; } } diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index 5d2eb33d8d..fa3a4cddaa 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -81,7 +81,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { // Never remove a sample bank. // These are basically radio buttons, not toggles. - if (SelectedItems.All(h => h.SampleControlPoint.SampleBank == bankName)) + if (SelectedItems.All(h => h.Samples.All(s => s.Bank == bankName))) bindable.Value = TernaryState.True; } @@ -167,7 +167,7 @@ namespace osu.Game.Screens.Edit.Compose.Components foreach ((string bankName, var bindable) in SelectionBankStates) { - bindable.Value = GetStateFromSelection(SelectedItems, h => h.SampleControlPoint.SampleBank == bankName); + bindable.Value = GetStateFromSelection(SelectedItems, h => h.Samples.All(s => s.Bank == bankName)); } } @@ -183,25 +183,10 @@ namespace osu.Game.Screens.Edit.Compose.Components { EditorBeatmap.PerformOnSelection(h => { - if (h.SampleControlPoint.SampleBank == bankName) + if (h.Samples.All(s => s.Bank == bankName)) return; - h.SampleControlPoint.SampleBank = bankName; - EditorBeatmap.Update(h); - }); - } - - /// - /// Removes a sample bank from all selected s. - /// - /// The name of the sample bank. - public void RemoveSampleBank(string bankName) - { - EditorBeatmap.PerformOnSelection(h => - { - if (h.SampleControlPoint.SampleBank == bankName) - h.SampleControlPoint.SampleBankBindable.SetDefault(); - + h.Samples = h.Samples.Select(s => s.With(newBank: bankName)).ToList(); EditorBeatmap.Update(h); }); } From a22ad98cb79567018833c14e9cc845e02bf564cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 14:56:11 +0900 Subject: [PATCH 155/195] Fix hotkeys not actually working --- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 62c3211e85..106ffc8ee7 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -288,7 +288,7 @@ namespace osu.Game.Rulesets.Edit protected override bool OnKeyDown(KeyDownEvent e) { - if (e.ControlPressed || e.AltPressed || e.SuperPressed || e.ShiftPressed) + if (e.ControlPressed || e.AltPressed || e.SuperPressed) return false; if (checkLeftToggleFromKey(e.Key, out int leftIndex)) From fc22c754641b2b11c4f4ecaf714df70cf1fd6b0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 16:14:05 +0900 Subject: [PATCH 156/195] Don't use `switch` for single `case` statement --- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 3f3b4ad327..d2dd83eb1a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -201,12 +201,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (CurrentPlacement == null) return; - switch (state) - { - case TernaryState.True: - CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.With(newBank: bankName)).ToList(); - break; - } + if (state == TernaryState.True) + CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.With(newBank: bankName)).ToList(); } public readonly Bindable NewCombo = new Bindable { Description = "New Combo" }; From 3a05dffa506aa0babb6dec23356a8cc21c763d10 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 17:11:12 +0900 Subject: [PATCH 157/195] Add "auto" bank selection during placement --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 18 +++++++++++++----- .../Components/ComposeBlueprintContainer.cs | 2 ++ .../Components/EditorSelectionHandler.cs | 16 +++++++++++++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index 551e557599..a0a04c13d0 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -32,6 +32,11 @@ namespace osu.Game.Rulesets.Edit /// public PlacementState PlacementActive { get; private set; } + /// + /// Whether the sample bank should be taken from the previous hit object. + /// + public bool AutomaticBankAssignment; + /// /// The that is being placed. /// @@ -86,11 +91,6 @@ namespace osu.Game.Rulesets.Edit /// Whether this call is committing a value for HitObject.StartTime and continuing with further adjustments. protected void BeginPlacement(bool commitStart = false) { - // Take the hitnormal sample of the last hit object - var lastHitNormal = getPreviousHitObject()?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL); - if (lastHitNormal != null) - HitObject.Samples[0] = lastHitNormal; - placementHandler.BeginPlacement(HitObject); if (commitStart) PlacementActive = PlacementState.Active; @@ -155,6 +155,14 @@ namespace osu.Game.Rulesets.Edit if (HitObject is IHasComboInformation comboInformation) comboInformation.UpdateComboInformation(getPreviousHitObject() as IHasComboInformation); } + + if (AutomaticBankAssignment) + { + // Take the hitnormal sample of the last hit object + var lastHitNormal = getPreviousHitObject()?.Samples?.FirstOrDefault(o => o.Name == HitSampleInfo.HIT_NORMAL); + if (lastHitNormal != null) + HitObject.Samples[0] = lastHitNormal; + } } /// diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index d2dd83eb1a..25babae6ff 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -201,6 +201,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { if (CurrentPlacement == null) return; + if (bankName == EditorSelectionHandler.HIT_BANK_AUTO) + CurrentPlacement.AutomaticBankAssignment = state == TernaryState.True; if (state == TernaryState.True) CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.With(newBank: bankName)).ToList(); } diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index fa3a4cddaa..dc7e12ea93 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -21,6 +21,12 @@ namespace osu.Game.Screens.Edit.Compose.Components { public partial class EditorSelectionHandler : SelectionHandler { + /// + /// A special bank name that is only used in the editor UI. + /// When selected and in placement mode, the bank of the last hit object will always be used. + /// + public const string HIT_BANK_AUTO = "auto"; + [Resolved] protected EditorBeatmap EditorBeatmap { get; private set; } @@ -59,7 +65,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// private void createStateBindables() { - foreach (string bankName in HitSampleInfo.AllBanks) + foreach (string bankName in HitSampleInfo.AllBanks.Prepend(HIT_BANK_AUTO)) { var bindable = new Bindable { @@ -100,6 +106,14 @@ namespace osu.Game.Screens.Edit.Compose.Components } else { + // Auto should just not apply if there's a selection already made. + // Maybe we could make it a disabled button in the future, but right now the editor buttons don't support disabled state. + if (bankName == HIT_BANK_AUTO) + { + bindable.Value = TernaryState.False; + break; + } + AddSampleBank(bankName); } From 8e5ba2208d29451882378130e2be0854a7b142a6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 17:33:48 +0900 Subject: [PATCH 158/195] Add test coverage of new hotkeys --- .../TestSceneHitObjectSampleAdjustments.cs | 100 +++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs index eb39221211..b0b51a5dbd 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectSampleAdjustments.cs @@ -13,6 +13,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Rulesets; +using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.UI; @@ -170,7 +171,7 @@ namespace osu.Game.Tests.Visual.Editing } [Test] - public void TestMultipleSelectionWithSameSampleBank() + public void TestPopoverMultipleSelectionWithSameSampleBank() { AddStep("unify sample bank", () => { @@ -204,7 +205,7 @@ namespace osu.Game.Tests.Visual.Editing } [Test] - public void TestMultipleSelectionWithDifferentSampleBank() + public void TestPopoverMultipleSelectionWithDifferentSampleBank() { AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); clickSamplePiece(0); @@ -226,6 +227,101 @@ namespace osu.Game.Tests.Visual.Editing samplePopoverHasSingleBank(HitSampleInfo.BANK_NORMAL); } + [Test] + public void TestHotkeysMultipleSelectionWithSameSampleBank() + { + AddStep("unify sample bank", () => + { + foreach (var h in EditorBeatmap.HitObjects) + { + for (int i = 0; i < h.Samples.Count; i++) + { + h.Samples[i] = h.Samples[i].With(newBank: HitSampleInfo.BANK_SOFT); + } + } + }); + + AddStep("select both objects", () => EditorBeatmap.SelectedHitObjects.AddRange(EditorBeatmap.HitObjects)); + + hitObjectHasSampleBank(0, HitSampleInfo.BANK_SOFT); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_SOFT); + + AddStep("Press normal bank shortcut", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.W); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + hitObjectHasSampleBank(0, HitSampleInfo.BANK_NORMAL); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_NORMAL); + + AddStep("Press drum bank shortcut", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM); + + AddStep("Press auto bank shortcut", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.Q); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + // Should be a noop. + hitObjectHasSampleBank(0, HitSampleInfo.BANK_DRUM); + hitObjectHasSampleBank(1, HitSampleInfo.BANK_DRUM); + } + + [Test] + public void TestHotkeysDuringPlacement() + { + AddStep("Enter placement mode", () => InputManager.Key(Key.Number2)); + AddStep("Move mouse to centre", () => InputManager.MoveMouseTo(Editor.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre)); + + AddStep("Move between two objects", () => EditorClock.Seek(250)); + + AddStep("Press normal bank shortcut", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.W); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + checkPlacementSample(HitSampleInfo.BANK_NORMAL); + + AddStep("Press drum bank shortcut", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.R); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + checkPlacementSample(HitSampleInfo.BANK_DRUM); + + AddStep("Press auto bank shortcut", () => + { + InputManager.PressKey(Key.ShiftLeft); + InputManager.Key(Key.Q); + InputManager.ReleaseKey(Key.ShiftLeft); + }); + + checkPlacementSample(HitSampleInfo.BANK_NORMAL); + + AddStep("Move after second object", () => EditorClock.Seek(750)); + checkPlacementSample(HitSampleInfo.BANK_SOFT); + + AddStep("Move to first object", () => EditorClock.Seek(0)); + checkPlacementSample(HitSampleInfo.BANK_NORMAL); + + void checkPlacementSample(string expected) => AddAssert($"Placement sample is {expected}", () => EditorBeatmap.PlacementObject.Value.Samples.First().Bank, () => Is.EqualTo(expected)); + } + private void clickSamplePiece(int objectIndex) => AddStep($"click {objectIndex.ToOrdinalWords()} sample piece", () => { var samplePiece = this.ChildrenOfType().Single(piece => piece.HitObject == EditorBeatmap.HitObjects.ElementAt(objectIndex)); From 8ada8b1c8cee225a02b99232b77d4f10f42c6dca Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 17:48:34 +0900 Subject: [PATCH 159/195] Remove description line from pause/fail screen These were in the designs but read pretty bad / evil. I can't think of any text to go in their place that makes sense, so let's just nuke it. --- osu.Game/Screens/Play/FailOverlay.cs | 1 - osu.Game/Screens/Play/GameplayMenuOverlay.cs | 10 ---------- osu.Game/Screens/Play/PauseOverlay.cs | 1 - 3 files changed, 12 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index 4fbc937b59..f1dd2abc4a 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -23,7 +23,6 @@ namespace osu.Game.Screens.Play public Func> SaveReplay; public override string Header => "failed"; - public override string Description => "you're dead, try again?"; [BackgroundDependencyLoader] private void load(OsuColour colours) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 81146a4ea6..de0c4fe4f1 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -53,8 +53,6 @@ namespace osu.Game.Screens.Play public abstract string Header { get; } - public abstract string Description { get; } - protected SelectionCycleFillFlowContainer InternalButtons; public IReadOnlyList Buttons => InternalButtons; @@ -107,14 +105,6 @@ namespace osu.Game.Screens.Play Shadow = true, ShadowColour = new Color4(0, 0, 0, 0.25f) }, - new OsuSpriteText - { - Text = Description, - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f) - } } }, InternalButtons = new SelectionCycleFillFlowContainer diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index db42998c45..984f43d77a 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -24,7 +24,6 @@ namespace osu.Game.Screens.Play public override bool IsPresent => base.IsPresent || pauseLoop.IsPlaying; public override string Header => "paused"; - public override string Description => "you're not going to do what i think you're going to do, are ya?"; private SkinnableSound pauseLoop; From 456f3005d61292a714d43d4acb82d673a5e30677 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 17:58:48 +0900 Subject: [PATCH 160/195] Apply nullability to `GameplayMenuOverlay` and use `TextFlowContainer` for text --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 76 ++++++-------------- 1 file changed, 20 insertions(+), 56 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index de0c4fe4f1..f1e10912ac 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -1,12 +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 System; using System.Collections.Generic; using System.Linq; -using Humanizer; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -20,6 +17,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -38,8 +36,8 @@ namespace osu.Game.Screens.Play public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - public Action OnRetry; - public Action OnQuit; + public Action? OnRetry; + public Action? OnQuit; /// /// Action that is invoked when is triggered. @@ -53,10 +51,13 @@ namespace osu.Game.Screens.Play public abstract string Header { get; } - protected SelectionCycleFillFlowContainer InternalButtons; + protected SelectionCycleFillFlowContainer InternalButtons = null!; public IReadOnlyList Buttons => InternalButtons; - private FillFlowContainer retryCounterContainer; + private TextFlowContainer playInfoText = null!; + + [Resolved] + private GlobalActionContainer globalAction { get; set; } = null!; protected GameplayMenuOverlay() { @@ -84,28 +85,13 @@ namespace osu.Game.Screens.Play Anchor = Anchor.Centre, Children = new Drawable[] { - new FillFlowContainer + new OsuSpriteText { + Text = Header, + Font = OsuFont.GetFont(size: 48), Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 20), - Children = new Drawable[] - { - new OsuSpriteText - { - Text = Header, - Font = OsuFont.GetFont(size: 30), - Spacing = new Vector2(5, 0), - Origin = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Colour = colours.Yellow, - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f) - }, - } + Colour = colours.Yellow, }, InternalButtons = new SelectionCycleFillFlowContainer { @@ -122,10 +108,11 @@ namespace osu.Game.Screens.Play Radius = 50 }, }, - retryCounterContainer = new FillFlowContainer + playInfoText = new OsuTextFlowContainer(cp => cp.Font = OsuFont.GetFont(size: 18)) { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, + TextAnchor = Anchor.TopCentre, AutoSizeAxes = Axes.Both, } } @@ -147,7 +134,8 @@ namespace osu.Game.Screens.Play return; retries = value; - if (retryCounterContainer != null) + + if (IsLoaded) updateRetryCount(); } } @@ -160,7 +148,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) => true; - protected void AddButton(string text, Color4 colour, Action action) + protected void AddButton(string text, Color4 colour, Action? action) { var button = new Button { @@ -212,30 +200,9 @@ namespace osu.Game.Screens.Play // "You've retried 1,065 times in this session" // "You've retried 1 time in this session" - retryCounterContainer.Children = new Drawable[] - { - new OsuSpriteText - { - Text = "You've retried ", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - Font = OsuFont.GetFont(size: 18), - }, - new OsuSpriteText - { - Text = "time".ToQuantity(retries), - Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 18), - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - }, - new OsuSpriteText - { - Text = " in this session", - Shadow = true, - ShadowColour = new Color4(0, 0, 0, 0.25f), - Font = OsuFont.GetFont(size: 18), - } - }; + playInfoText.Clear(); + playInfoText.AddText("Retry count: "); + playInfoText.AddText(retries.ToString(), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); } private partial class Button : DialogButton @@ -250,9 +217,6 @@ namespace osu.Game.Screens.Play } } - [Resolved] - private GlobalActionContainer globalAction { get; set; } - protected override bool Handle(UIEvent e) { switch (e) From 79c9a48ff7b7e926b27be6321569b95baee44860 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 18:09:19 +0900 Subject: [PATCH 161/195] Show song progress at pause/fail screen --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 36 ++++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f1e10912ac..4e1913463c 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -121,7 +122,7 @@ namespace osu.Game.Screens.Play State.ValueChanged += _ => InternalButtons.Deselect(); - updateRetryCount(); + updateInfoText(); } private int retries; @@ -136,11 +137,16 @@ namespace osu.Game.Screens.Play retries = value; if (IsLoaded) - updateRetryCount(); + updateInfoText(); } } - protected override void PopIn() => this.FadeIn(TRANSITION_DURATION, Easing.In); + protected override void PopIn() + { + this.FadeIn(TRANSITION_DURATION, Easing.In); + updateInfoText(); + } + protected override void PopOut() => this.FadeOut(TRANSITION_DURATION, Easing.In); // Don't let mouse down events through the overlay or people can click circles while paused. @@ -195,14 +201,30 @@ namespace osu.Game.Screens.Play { } - private void updateRetryCount() - { - // "You've retried 1,065 times in this session" - // "You've retried 1 time in this session" + [Resolved] + private IGameplayClock? gameplayClock { get; set; } + [Resolved] + private DrawableRuleset? drawableRuleset { get; set; } + + private void updateInfoText() + { playInfoText.Clear(); playInfoText.AddText("Retry count: "); playInfoText.AddText(retries.ToString(), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); + + if (gameplayClock != null && drawableRuleset != null) + { + double firstHitTime = drawableRuleset.Objects.FirstOrDefault()?.StartTime ?? 0; + //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). + double lastHitTime = drawableRuleset.Objects.LastOrDefault()?.GetEndTime() ?? 0; + + double progress = Math.Clamp((gameplayClock.CurrentTime - firstHitTime) / (lastHitTime - firstHitTime), 0, 1); + + playInfoText.NewLine(); + playInfoText.AddText("Song progress: "); + playInfoText.AddText(progress.ToString("0%"), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); + } } private partial class Button : DialogButton From 3b9e1e8a9491c8db305320951c58360622f57846 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 19:16:51 +0900 Subject: [PATCH 162/195] Ensure editor selection buttons remain on screen when selection is near edge Addresses https://github.com/ppy/osu/discussions/23599. --- .../Edit/Compose/Components/SelectionBox.cs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 17790547ed..677b0cd76e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -22,6 +22,8 @@ namespace osu.Game.Screens.Edit.Compose.Components { public const float BORDER_RADIUS = 3; + private const float button_padding = 5; + public Func OnRotation; public Func OnScale; public Func OnFlip; @@ -182,6 +184,13 @@ namespace osu.Game.Screens.Edit.Compose.Components return base.OnKeyDown(e); } + protected override void Update() + { + base.Update(); + + ensureButtonsOnScreen(); + } + private void recreate() { if (LoadState < LoadState.Loading) @@ -234,11 +243,12 @@ namespace osu.Game.Screens.Edit.Compose.Components }, buttons = new FillFlowContainer { - Y = 20, - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.X, + Height = 30, Direction = FillDirection.Horizontal, + Margin = new MarginPadding(button_padding), Anchor = Anchor.BottomCentre, - Origin = Anchor.Centre + Origin = Anchor.TopCentre } }; @@ -352,5 +362,29 @@ namespace osu.Game.Screens.Edit.Compose.Components if (activeOperations++ == 0) OperationStarted?.Invoke(); } + + private void ensureButtonsOnScreen() + { + buttons.Position = Vector2.Zero; + + var buttonsQuad = buttons.ScreenSpaceDrawQuad; + var thisQuad = ScreenSpaceDrawQuad; + + // Shrink the parent quad to give a bit of padding so the buttons don't stick *right* on the border. + // AABBFloat assumes no rotation. one would hope the whole editor is not being rotated. + var parentQuad = Parent.ScreenSpaceDrawQuad.AABBFloat.Shrink(ToLocalSpace(thisQuad.TopLeft + new Vector2(button_padding * 2))); + + float leftExcess = buttonsQuad.TopLeft.X - parentQuad.TopLeft.X; + float rightExcess = parentQuad.TopRight.X - buttonsQuad.TopRight.X; + float bottomExcess = thisQuad.BottomLeft.Y + buttonsQuad.Height - parentQuad.BottomLeft.Y; + + if (leftExcess < 0) + buttons.X += ToLocalSpace(thisQuad.TopLeft - new Vector2(leftExcess)).X; + else if (rightExcess < 0) + buttons.X -= ToLocalSpace(thisQuad.TopLeft - new Vector2(rightExcess)).X; + + if (bottomExcess > 0) + buttons.Y += ToLocalSpace(thisQuad.TopLeft - new Vector2(bottomExcess)).X; + } } } From b14b1072c29a039c68d2cbe76d05023cd93b7e70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 19:24:14 +0900 Subject: [PATCH 163/195] Allow deselecting any selection in the editor using the `Back` binding (escape key) --- .../Compose/Components/BlueprintContainer.cs | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index cb7c083d87..0fcf84ec8e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -16,6 +16,7 @@ using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osu.Game.Rulesets.Edit; using osuTK; using osuTK.Input; @@ -26,7 +27,7 @@ namespace osu.Game.Screens.Edit.Compose.Components /// A container which provides a "blueprint" display of items. /// Includes selection and manipulation support via a . /// - public abstract partial class BlueprintContainer : CompositeDrawable, IKeyBindingHandler + public abstract partial class BlueprintContainer : CompositeDrawable, IKeyBindingHandler, IKeyBindingHandler where T : class { protected DragBox DragBox { get; private set; } @@ -279,6 +280,30 @@ namespace osu.Game.Screens.Edit.Compose.Components { } + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) + return false; + + switch (e.Action) + { + case GlobalAction.Back: + if (SelectedItems.Count > 0) + { + DeselectAll(); + return true; + } + + break; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + #region Blueprint Addition/Removal protected virtual void AddBlueprintFor(T item) From 663cec1ff6307a87e527b6ac6d76fa57d3d9b1e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 23:51:28 +0900 Subject: [PATCH 164/195] Combine editor navigation test scenes --- .../Editing/TestSceneEditorNavigation.cs | 57 ------------------- .../TestSceneBeatmapEditorNavigation.cs | 44 ++++++++++++++ 2 files changed, 44 insertions(+), 57 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs deleted file mode 100644 index 5914290d40..0000000000 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorNavigation.cs +++ /dev/null @@ -1,57 +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.Extensions; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Extensions.ObjectExtensions; -using osu.Game.Beatmaps; -using osu.Game.Database; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Osu; -using osu.Game.Screens.Edit; -using osu.Game.Screens.Edit.GameplayTest; -using osu.Game.Screens.Select; -using osu.Game.Tests.Resources; - -namespace osu.Game.Tests.Visual.Editing -{ - public partial class TestSceneEditorNavigation : OsuGameTestScene - { - [Test] - public void TestEditorGameplayTestAlwaysUsesOriginalRuleset() - { - BeatmapSetInfo beatmapSet = null!; - - AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); - AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); - - AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); - AddUntilStep("wait for song select", - () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) - && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect - && songSelect.IsLoaded); - AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo); - - AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); - AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); - AddStep("test gameplay", () => ((Editor)Game.ScreenStack.CurrentScreen).TestGameplay()); - - AddUntilStep("wait for player", () => - { - // notifications may fire at almost any inopportune time and cause annoying test failures. - // relentlessly attempt to dismiss any and all interfering overlays, which includes notifications. - // this is theoretically not foolproof, but it's the best that can be done here. - Game.CloseAllOverlays(); - return Game.ScreenStack.CurrentScreen is EditorPlayer editorPlayer && editorPlayer.IsLoaded; - }); - - AddAssert("current ruleset is osu!", () => Game.Ruleset.Value.Equals(new OsuRuleset().RulesetInfo)); - - AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield())); - AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); - AddAssert("previous ruleset restored", () => Game.Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo)); - } - } -} diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 603573058e..0307bc7ce5 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -3,15 +3,59 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.GameplayTest; using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osu.Game.Tests.Resources; namespace osu.Game.Tests.Visual.Navigation { public partial class TestSceneBeatmapEditorNavigation : OsuGameTestScene { + [Test] + public void TestEditorGameplayTestAlwaysUsesOriginalRuleset() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + AddStep("switch ruleset", () => Game.Ruleset.Value = new ManiaRuleset().RulesetInfo); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + AddStep("test gameplay", () => ((Editor)Game.ScreenStack.CurrentScreen).TestGameplay()); + + AddUntilStep("wait for player", () => + { + // notifications may fire at almost any inopportune time and cause annoying test failures. + // relentlessly attempt to dismiss any and all interfering overlays, which includes notifications. + // this is theoretically not foolproof, but it's the best that can be done here. + Game.CloseAllOverlays(); + return Game.ScreenStack.CurrentScreen is EditorPlayer editorPlayer && editorPlayer.IsLoaded; + }); + + AddAssert("current ruleset is osu!", () => Game.Ruleset.Value.Equals(new OsuRuleset().RulesetInfo)); + + AddStep("exit to song select", () => Game.PerformFromScreen(_ => { }, typeof(PlaySongSelect).Yield())); + AddUntilStep("wait for song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddAssert("previous ruleset restored", () => Game.Ruleset.Value.Equals(new ManiaRuleset().RulesetInfo)); + } + /// /// When entering the editor, a new beatmap is created as part of the asynchronous load process. /// This test ensures that in the case of an early exit from the editor (ie. while it's still loading) From 604432718116abaadf32778fa47bedc353f504d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 24 May 2023 23:57:37 +0900 Subject: [PATCH 165/195] Add test coverage for escape deselecting any active selection --- .../TestSceneBeatmapEditorNavigation.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 0307bc7ce5..5f026468c7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Database; using osu.Game.Rulesets.Mania; @@ -16,6 +17,7 @@ using osu.Game.Screens.Edit.GameplayTest; using osu.Game.Screens.Menu; using osu.Game.Screens.Select; using osu.Game.Tests.Resources; +using osuTK.Input; namespace osu.Game.Tests.Visual.Navigation { @@ -82,5 +84,63 @@ namespace osu.Game.Tests.Visual.Navigation BeatmapSetInfo[] allBeatmapSets() => Game.Realm.Run(realm => realm.All().Where(x => !x.DeletePending).ToArray()); } + + [Test] + public void TestExitEditorWithoutSelection() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + + AddStep("escape once", () => InputManager.Key(Key.Escape)); + + AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor); + } + + [Test] + public void TestExitEditorWithSelection() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import test beatmap", () => Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely()); + AddStep("retrieve beatmap", () => beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach()); + + AddStep("present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("wait for song select", + () => Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded); + + AddStep("open editor", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).Edit(beatmapSet.Beatmaps.First(beatmap => beatmap.Ruleset.OnlineID == 0))); + AddUntilStep("wait for editor open", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.ReadyForUse); + + AddStep("make selection", () => + { + var beatmap = getEditorBeatmap(); + beatmap.SelectedHitObjects.AddRange(beatmap.HitObjects.Take(5)); + }); + + AddAssert("selection exists", () => getEditorBeatmap().SelectedHitObjects, () => Has.Count.GreaterThan(0)); + + AddStep("escape once", () => InputManager.Key(Key.Escape)); + + AddAssert("selection exists", () => getEditorBeatmap().SelectedHitObjects, () => Has.Count.Zero); + + AddStep("escape again", () => InputManager.Key(Key.Escape)); + + AddUntilStep("wait for editor exit", () => Game.ScreenStack.CurrentScreen is not Editor); + } + + private EditorBeatmap getEditorBeatmap() => ((Editor)Game.ScreenStack.CurrentScreen).ChildrenOfType().Single(); } } From cd3602406b5999efe741b4d2fd3a3f97a2cf272c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 24 May 2023 18:54:48 +0200 Subject: [PATCH 166/195] Remove unused using directive --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index f1e10912ac..7f4979b840 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -17,7 +17,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; -using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; From 07b5874eeee3a5854898318eda111bd9a64870da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 24 May 2023 20:18:36 +0200 Subject: [PATCH 167/195] Fix test step name --- .../Visual/Navigation/TestSceneBeatmapEditorNavigation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index 5f026468c7..1b2bb57b84 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("escape once", () => InputManager.Key(Key.Escape)); - AddAssert("selection exists", () => getEditorBeatmap().SelectedHitObjects, () => Has.Count.Zero); + AddAssert("selection empty", () => getEditorBeatmap().SelectedHitObjects, () => Has.Count.Zero); AddStep("escape again", () => InputManager.Key(Key.Escape)); From 6ec4ecfdd734d7db0cd430055e6c01b5753540d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 24 May 2023 22:17:51 +0200 Subject: [PATCH 168/195] Mention fallback default in `GetDivisorForBeatIndex()` --- osu.Game/Screens/Edit/BindableBeatDivisor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/BindableBeatDivisor.cs b/osu.Game/Screens/Edit/BindableBeatDivisor.cs index f1b97571bd..1da224d850 100644 --- a/osu.Game/Screens/Edit/BindableBeatDivisor.cs +++ b/osu.Game/Screens/Edit/BindableBeatDivisor.cs @@ -154,7 +154,7 @@ namespace osu.Game.Screens.Edit /// /// The 0-based beat index. /// The beat divisor. - /// The list of valid divisors which can be chosen from. Assumes ordered from low to high. + /// The list of valid divisors which can be chosen from. Assumes ordered from low to high. Defaults to if omitted. /// The applicable divisor. public static int GetDivisorForBeatIndex(int index, int beatDivisor, int[] validDivisors = null) { From 058edb5d5fe4e5003b045f4aeeabb187a977661f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 17:15:31 +0900 Subject: [PATCH 169/195] Centralise beatmap playable duration and bounds lookups --- .../UserInterface/TestSceneSegmentedGraph.cs | 4 +- osu.Game/Beatmaps/BeatmapUpdater.cs | 18 +------- osu.Game/Beatmaps/IBeatmap.cs | 44 +++++++++++++++++++ .../Play/HUD/ArgonSongProgressGraph.cs | 4 +- .../Play/HUD/DefaultSongProgressGraph.cs | 4 +- osu.Game/Screens/Play/HUD/SongProgress.cs | 8 ++-- 6 files changed, 55 insertions(+), 27 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs index 1144b9053d..320ec48e07 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneSegmentedGraph.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; +using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; @@ -137,8 +138,7 @@ namespace osu.Game.Tests.Visual.UserInterface if (!objects.Any()) return; - double firstHit = objects.First().StartTime; - double lastHit = objects.Max(o => o.GetEndTime()); + (double firstHit, double lastHit) = BeatmapExtensions.CalculatePlayableBounds(objects); if (lastHit == 0) lastHit = objects.Last().StartTime; diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs index af9f32f834..046adb8327 100644 --- a/osu.Game/Beatmaps/BeatmapUpdater.cs +++ b/osu.Game/Beatmaps/BeatmapUpdater.cs @@ -3,7 +3,6 @@ using System; using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; @@ -11,7 +10,6 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Database; using osu.Game.Online.API; -using osu.Game.Rulesets.Objects; namespace osu.Game.Beatmaps { @@ -74,7 +72,7 @@ namespace osu.Game.Beatmaps var calculator = ruleset.CreateDifficultyCalculator(working); beatmap.StarRating = calculator.Calculate().StarRating; - beatmap.Length = calculateLength(working.Beatmap); + beatmap.Length = working.Beatmap.CalculatePlayableLength(); beatmap.BPM = 60000 / working.Beatmap.GetMostCommonBeatLength(); } @@ -82,20 +80,6 @@ namespace osu.Game.Beatmaps workingBeatmapCache.Invalidate(beatmapSet); }); - private double calculateLength(IBeatmap b) - { - if (!b.HitObjects.Any()) - return 0; - - var lastObject = b.HitObjects.Last(); - - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - double endTime = lastObject.GetEndTime(); - double startTime = b.HitObjects.First().StartTime; - - return endTime - startTime; - } - #region Implementation of IDisposable public void Dispose() diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index f6771f7adf..671f5ce8db 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -104,6 +104,19 @@ namespace osu.Game.Beatmaps } } + /// + /// Find the total milliseconds between the first and last hittable objects. + /// + /// + /// This is cached to , so using that is preferrable when available. + /// + public static double CalculatePlayableLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects); + + /// + /// Find the timestamps in milliseconds of the start and end of the playable region. + /// + public static (double start, double end) CalculatePlayableBounds(this IBeatmap beatmap) => CalculatePlayableBounds(beatmap.HitObjects); + /// /// Find the absolute end time of the latest in a beatmap. Will throw if beatmap contains no objects. /// @@ -114,5 +127,36 @@ namespace osu.Game.Beatmaps /// It's not super efficient so calls should be kept to a minimum. /// public static double GetLastObjectTime(this IBeatmap beatmap) => beatmap.HitObjects.Max(h => h.GetEndTime()); + + #region Helper methods + + /// + /// Find the total milliseconds between the first and last hittable objects. + /// + /// + /// This is cached to , so using that is preferrable when available. + /// + public static double CalculatePlayableLength(IEnumerable objects) + { + (double start, double end) = CalculatePlayableBounds(objects); + + return end - start; + } + + /// + /// Find the timestamps in milliseconds of the start and end of the playable region. + /// + public static (double start, double end) CalculatePlayableBounds(IEnumerable objects) + { + if (!objects.Any()) + return (0, 0); + + double lastObjectTime = objects.Max(o => o.GetEndTime()); + double firstObjectTime = objects.First().StartTime; + + return (firstObjectTime, lastObjectTime); + } + + #endregion } } diff --git a/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs b/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs index 0899476ed4..63ab9d15e0 100644 --- a/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs +++ b/osu.Game/Screens/Play/HUD/ArgonSongProgressGraph.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Graphics.UserInterface; @@ -26,8 +27,7 @@ namespace osu.Game.Screens.Play.HUD if (!objects.Any()) return; - double firstHit = objects.First().StartTime; - double lastHit = objects.Max(o => o.GetEndTime()); + (double firstHit, double lastHit) = BeatmapExtensions.CalculatePlayableBounds(objects); if (lastHit == 0) lastHit = objects.Last().StartTime; diff --git a/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs b/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs index bee5978817..047c64a4a4 100644 --- a/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs +++ b/osu.Game/Screens/Play/HUD/DefaultSongProgressGraph.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Collections.Generic; using System.Diagnostics; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; namespace osu.Game.Screens.Play.HUD @@ -26,8 +27,7 @@ namespace osu.Game.Screens.Play.HUD if (!objects.Any()) return; - double firstHit = objects.First().StartTime; - double lastHit = objects.Max(o => o.GetEndTime()); + (double firstHit, double lastHit) = BeatmapExtensions.CalculatePlayableBounds(objects); if (lastHit == 0) lastHit = objects.Last().StartTime; diff --git a/osu.Game/Screens/Play/HUD/SongProgress.cs b/osu.Game/Screens/Play/HUD/SongProgress.cs index ebe2fb83e6..4391193df8 100644 --- a/osu.Game/Screens/Play/HUD/SongProgress.cs +++ b/osu.Game/Screens/Play/HUD/SongProgress.cs @@ -2,12 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Timing; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Skinning; @@ -52,9 +52,9 @@ namespace osu.Game.Screens.Play.HUD set { objects = value; - FirstHitTime = objects.FirstOrDefault()?.StartTime ?? 0; - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - LastHitTime = objects.LastOrDefault()?.GetEndTime() ?? 0; + + (FirstHitTime, LastHitTime) = BeatmapExtensions.CalculatePlayableBounds(objects); + UpdateObjects(objects); } } From 37c6e632d0f325b36368e91ba749bde0634bdf46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 17:38:35 +0900 Subject: [PATCH 170/195] Switch to using new extension method for length calculations --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 4e1913463c..52251db898 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -12,13 +12,12 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +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.Input.Bindings; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.UI; using osuTK; using osuTK.Graphics; @@ -205,7 +204,7 @@ namespace osu.Game.Screens.Play private IGameplayClock? gameplayClock { get; set; } [Resolved] - private DrawableRuleset? drawableRuleset { get; set; } + private GameplayState? gameplayState { get; set; } private void updateInfoText() { @@ -213,11 +212,9 @@ namespace osu.Game.Screens.Play playInfoText.AddText("Retry count: "); playInfoText.AddText(retries.ToString(), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); - if (gameplayClock != null && drawableRuleset != null) + if (gameplayState != null && gameplayClock != null) { - double firstHitTime = drawableRuleset.Objects.FirstOrDefault()?.StartTime ?? 0; - //TODO: this isn't always correct (consider mania where a non-last object may last for longer than the last in the list). - double lastHitTime = drawableRuleset.Objects.LastOrDefault()?.GetEndTime() ?? 0; + (double firstHitTime, double lastHitTime) = gameplayState.Beatmap.CalculatePlayableBounds(); double progress = Math.Clamp((gameplayClock.CurrentTime - firstHitTime) / (lastHitTime - firstHitTime), 0, 1); From 1e14b024938a584dc61725e4ed5d423459a8f29c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 17:57:37 +0900 Subject: [PATCH 171/195] Fix bindable feedback loop --- .../Edit/Compose/Components/EditorSelectionHandler.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs index dc7e12ea93..d618541685 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorSelectionHandler.cs @@ -85,6 +85,11 @@ namespace osu.Game.Screens.Edit.Compose.Components } else { + // Auto should never apply when there is a selection made. + // This is also required to stop a bindable feedback loop when a HitObject has zero samples (and LINQ `All` below becomes true). + if (bankName == HIT_BANK_AUTO) + break; + // Never remove a sample bank. // These are basically radio buttons, not toggles. if (SelectedItems.All(h => h.Samples.All(s => s.Bank == bankName))) From 1049257b56b5dc0e2d71dc2db7d5a31c1062de78 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 18:46:31 +0900 Subject: [PATCH 172/195] Simplify `SelectionHandler`'s `DeselectAll` implementation We are already doing other operations in this class directly on `SelectedItems`, so might as well change this one to match --- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 1 - .../Screens/Edit/Compose/Components/SelectionHandler.cs | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 0fcf84ec8e..56a6b18433 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -92,7 +92,6 @@ namespace osu.Game.Screens.Edit.Compose.Components }; SelectionHandler = CreateSelectionHandler(); - SelectionHandler.DeselectAll = DeselectAll; SelectionHandler.SelectedItems.BindTo(SelectedItems); AddRangeInternal(new[] diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index 9e4fb26688..f73a25c339 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -197,9 +197,9 @@ namespace osu.Game.Screens.Edit.Compose.Components #region Selection Handling /// - /// Bind an action to deselect all selected blueprints. + /// Deselect all selected items. /// - internal Action DeselectAll { private get; set; } + protected void DeselectAll() => SelectedItems.Clear(); /// /// Handle a blueprint becoming selected. @@ -303,7 +303,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (blueprint.IsSelected) return false; - DeselectAll?.Invoke(); + DeselectAll(); blueprint.Select(); return true; } From 609c7227eee913dbb602b5eefdd628bb5c9c216d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 19:55:11 +0900 Subject: [PATCH 173/195] Fix changes to font weight in a couple of combined implementations --- .../Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs | 4 +++- .../OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs | 3 +++ .../Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs | 3 +++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs index 983fab8525..3e6d7a2e54 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/OnlinePlayPill.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -12,6 +13,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { protected PillContainer Pill { get; private set; } = null!; protected OsuTextFlowContainer TextFlow { get; private set; } = null!; + protected virtual FontUsage Font => OsuFont.GetFont(size: 12); protected OnlinePlayPill() { @@ -23,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components { InternalChild = Pill = new PillContainer { - Child = TextFlow = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12)) + Child = TextFlow = new OsuTextFlowContainer(s => s.Font = Font) { AutoSizeAxes = Axes.Both, Anchor = Anchor.CentreLeft, diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs index e88624f877..10f6e59260 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomSpecialCategoryPill.cs @@ -5,6 +5,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osuTK.Graphics; @@ -15,6 +16,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private OsuColour colours { get; set; } + protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold); + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs index ab3d293db8..ca9917ad00 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomStatusPill.cs @@ -6,6 +6,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Online.Rooms; using osu.Game.Online.Rooms.RoomStatuses; @@ -20,6 +21,8 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components [Resolved] private OsuColour colours { get; set; } + protected override FontUsage Font => base.Font.With(weight: FontWeight.SemiBold); + protected override void LoadComplete() { base.LoadComplete(); From 804671ca74995c1be6f429523b1b673dc00df184 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 21:41:19 +0900 Subject: [PATCH 174/195] Split out grid snapping modes into "relative" and "global" types --- .../Edit/CatchHitObjectComposer.cs | 2 +- .../Edit/OsuHitObjectComposer.cs | 7 +++++-- .../Editing/TestSceneDistanceSnapGrid.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/SnapType.cs | 20 +++++++++++++++++-- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index cd8894753f..611e69d614 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Catch.Edit result.ScreenSpacePosition.X = screenSpacePosition.X; - if (snapType.HasFlagFast(SnapType.Grids)) + if (snapType.HasFlagFast(SnapType.GlobalGrids)) { if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index ff1e208186..ad6af6d74e 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -143,7 +143,7 @@ namespace osu.Game.Rulesets.Osu.Edit // We want to ensure that in this particular case, the time-snapping component of distance snap is still applied. // The easiest way to ensure this is to attempt application of distance snap after a nearby object is found, and copy over // the time value if the proposed positions are roughly the same. - if (snapType.HasFlagFast(SnapType.Grids) && DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) + if (snapType.HasFlagFast(SnapType.RelativeGrids) && DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) { (Vector2 distanceSnappedPosition, double distanceSnappedTime) = distanceSnapGrid.GetSnappedPosition(distanceSnapGrid.ToLocalSpace(snapResult.ScreenSpacePosition)); if (Precision.AlmostEquals(distanceSnapGrid.ToScreenSpace(distanceSnappedPosition), snapResult.ScreenSpacePosition, 1)) @@ -155,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Edit SnapResult result = base.FindSnappedPositionAndTime(screenSpacePosition, snapType); - if (snapType.HasFlagFast(SnapType.Grids)) + if (snapType.HasFlagFast(SnapType.RelativeGrids)) { if (DistanceSnapToggle.Value == TernaryState.True && distanceSnapGrid != null) { @@ -164,7 +164,10 @@ namespace osu.Game.Rulesets.Osu.Edit result.ScreenSpacePosition = distanceSnapGrid.ToScreenSpace(pos); result.Time = time; } + } + if (snapType.HasFlagFast(SnapType.GlobalGrids)) + { if (rectangularGridSnapToggle.Value == TernaryState.True) { Vector2 pos = rectangularPositionSnapGrid.GetSnappedPosition(rectangularPositionSnapGrid.ToLocalSpace(result.ScreenSpacePosition)); diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs index 21b925a257..70e4420a45 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDistanceSnapGrid.cs @@ -187,7 +187,7 @@ namespace osu.Game.Tests.Visual.Editing private class SnapProvider : IDistanceSnapProvider { - public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.Grids) => new SnapResult(screenSpacePosition, 0); + public SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.AllGrids) => new SnapResult(screenSpacePosition, 0); public Bindable DistanceSpacingMultiplier { get; } = new BindableDouble(1); diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index e2dbd2acdc..9ce6d957fc 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -428,7 +428,7 @@ namespace osu.Game.Rulesets.Edit var playfield = PlayfieldAtScreenSpacePosition(screenSpacePosition); double? targetTime = null; - if (snapType.HasFlagFast(SnapType.Grids)) + if (snapType.HasFlagFast(SnapType.GlobalGrids)) { if (playfield is ScrollingPlayfield scrollingPlayfield) { diff --git a/osu.Game/Rulesets/Edit/SnapType.cs b/osu.Game/Rulesets/Edit/SnapType.cs index 6eb46457c8..f5f9ab0437 100644 --- a/osu.Game/Rulesets/Edit/SnapType.cs +++ b/osu.Game/Rulesets/Edit/SnapType.cs @@ -11,8 +11,24 @@ namespace osu.Game.Rulesets.Edit public enum SnapType { None = 0, + + /// + /// Snapping to visible nearby objects. + /// NearbyObjects = 1 << 0, - Grids = 1 << 1, - All = NearbyObjects | Grids, + + /// + /// Grids which are global to the playfield. + /// + GlobalGrids = 1 << 1, + + /// + /// Grids which are relative to other nearby hit objects. + /// + RelativeGrids = 1 << 2, + + AllGrids = RelativeGrids | GlobalGrids, + + All = NearbyObjects | GlobalGrids | RelativeGrids, } } From 1cd69220ef640424b79f19784c557ac183333c2d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 21:41:42 +0900 Subject: [PATCH 175/195] Fix slider path placement snapping non-head nodes to distanced snapping grid As discussed at https://github.com/ppy/osu/discussions/23531. --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 17d0fc457a..c56ffcb140 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -309,7 +309,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } else { - var result = snapProvider?.FindSnappedPositionAndTime(Parent.ToScreenSpace(e.MousePosition)); + var result = snapProvider?.FindSnappedPositionAndTime(Parent.ToScreenSpace(e.MousePosition), SnapType.GlobalGrids); Vector2 movementDelta = Parent.ToLocalSpace(result?.ScreenSpacePosition ?? Parent.ToScreenSpace(e.MousePosition)) - dragStartPositions[draggedControlPointIndex] - hitObject.Position; From beeca5a8dd1bc84f036f25caa5f4e00d4204d417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 May 2023 16:17:44 +0200 Subject: [PATCH 176/195] Use alternative layouting implementation --- .../Edit/Compose/Components/SelectionBox.cs | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index 677b0cd76e..fc57cbf17f 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -247,8 +247,6 @@ namespace osu.Game.Screens.Edit.Compose.Components Height = 30, Direction = FillDirection.Horizontal, Margin = new MarginPadding(button_padding), - Anchor = Anchor.BottomCentre, - Origin = Anchor.TopCentre } }; @@ -367,24 +365,30 @@ namespace osu.Game.Screens.Edit.Compose.Components { buttons.Position = Vector2.Zero; - var buttonsQuad = buttons.ScreenSpaceDrawQuad; var thisQuad = ScreenSpaceDrawQuad; // Shrink the parent quad to give a bit of padding so the buttons don't stick *right* on the border. // AABBFloat assumes no rotation. one would hope the whole editor is not being rotated. var parentQuad = Parent.ScreenSpaceDrawQuad.AABBFloat.Shrink(ToLocalSpace(thisQuad.TopLeft + new Vector2(button_padding * 2))); - float leftExcess = buttonsQuad.TopLeft.X - parentQuad.TopLeft.X; - float rightExcess = parentQuad.TopRight.X - buttonsQuad.TopRight.X; - float bottomExcess = thisQuad.BottomLeft.Y + buttonsQuad.Height - parentQuad.BottomLeft.Y; + float topExcess = thisQuad.TopLeft.Y - parentQuad.TopLeft.Y; + float bottomExcess = parentQuad.BottomLeft.Y - thisQuad.BottomLeft.Y; + float leftExcess = thisQuad.TopLeft.X - parentQuad.TopLeft.X; + float rightExcess = parentQuad.TopRight.X - thisQuad.TopRight.X; - if (leftExcess < 0) - buttons.X += ToLocalSpace(thisQuad.TopLeft - new Vector2(leftExcess)).X; - else if (rightExcess < 0) - buttons.X -= ToLocalSpace(thisQuad.TopLeft - new Vector2(rightExcess)).X; + if (topExcess > bottomExcess) + { + buttons.Anchor = Anchor.TopCentre; + buttons.Origin = Anchor.BottomCentre; + } + else + { + buttons.Anchor = Anchor.BottomCentre; + buttons.Origin = Anchor.TopCentre; + } - if (bottomExcess > 0) - buttons.Y += ToLocalSpace(thisQuad.TopLeft - new Vector2(bottomExcess)).X; + if (leftExcess < 0) buttons.X += ToLocalSpace(thisQuad.TopLeft - new Vector2(leftExcess)).X; + if (rightExcess < 0) buttons.X += ToLocalSpace(thisQuad.TopLeft + new Vector2(rightExcess)).X; } } } From 949de35664fa32e6abe115a1c7bea1d30843e1b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 25 May 2023 23:19:55 +0900 Subject: [PATCH 177/195] Ensure selection is reset after immediately deleting objects Closes https://github.com/ppy/osu/issues/23518. --- osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs index f73a25c339..5cedf1ca42 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionHandler.cs @@ -311,6 +311,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected void DeleteSelected() { DeleteItems(SelectedItems.ToArray()); + DeselectAll(); } #endregion From 3ad5f8b9c9973788780e540cb98e5b260367df98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 May 2023 16:39:54 +0200 Subject: [PATCH 178/195] Polish a few extreme edge cases --- .../Screens/Edit/Compose/Components/SelectionBox.cs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs index fc57cbf17f..1c5faed0e5 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBox.cs @@ -376,7 +376,12 @@ namespace osu.Game.Screens.Edit.Compose.Components float leftExcess = thisQuad.TopLeft.X - parentQuad.TopLeft.X; float rightExcess = parentQuad.TopRight.X - thisQuad.TopRight.X; - if (topExcess > bottomExcess) + if (topExcess + bottomExcess < buttons.Height + button_padding) + { + buttons.Anchor = Anchor.BottomCentre; + buttons.Origin = Anchor.BottomCentre; + } + else if (topExcess > bottomExcess) { buttons.Anchor = Anchor.TopCentre; buttons.Origin = Anchor.BottomCentre; @@ -387,8 +392,7 @@ namespace osu.Game.Screens.Edit.Compose.Components buttons.Origin = Anchor.TopCentre; } - if (leftExcess < 0) buttons.X += ToLocalSpace(thisQuad.TopLeft - new Vector2(leftExcess)).X; - if (rightExcess < 0) buttons.X += ToLocalSpace(thisQuad.TopLeft + new Vector2(rightExcess)).X; + buttons.X += ToLocalSpace(thisQuad.TopLeft - new Vector2(Math.Min(0, leftExcess)) + new Vector2(Math.Min(0, rightExcess))).X; } } } From 78f41f71095b25dd01a490aeddb3eb8e83aa47c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 May 2023 17:33:41 +0200 Subject: [PATCH 179/195] Fix spelling --- osu.Game/Beatmaps/IBeatmap.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/IBeatmap.cs b/osu.Game/Beatmaps/IBeatmap.cs index 671f5ce8db..9dc3084cb5 100644 --- a/osu.Game/Beatmaps/IBeatmap.cs +++ b/osu.Game/Beatmaps/IBeatmap.cs @@ -108,7 +108,7 @@ namespace osu.Game.Beatmaps /// Find the total milliseconds between the first and last hittable objects. /// /// - /// This is cached to , so using that is preferrable when available. + /// This is cached to , so using that is preferable when available. /// public static double CalculatePlayableLength(this IBeatmap beatmap) => CalculatePlayableLength(beatmap.HitObjects); @@ -134,7 +134,7 @@ namespace osu.Game.Beatmaps /// Find the total milliseconds between the first and last hittable objects. /// /// - /// This is cached to , so using that is preferrable when available. + /// This is cached to , so using that is preferable when available. /// public static double CalculatePlayableLength(IEnumerable objects) { From adee624a8fe86da21a3f1377aae10aa96eec9007 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 May 2023 21:32:19 +0200 Subject: [PATCH 180/195] Change `PlacementBlueprint.AutomaticBankAssignment` to property Mostly for consistency. --- osu.Game/Rulesets/Edit/PlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs index a0a04c13d0..717c026ded 100644 --- a/osu.Game/Rulesets/Edit/PlacementBlueprint.cs +++ b/osu.Game/Rulesets/Edit/PlacementBlueprint.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Edit /// /// Whether the sample bank should be taken from the previous hit object. /// - public bool AutomaticBankAssignment; + public bool AutomaticBankAssignment { get; set; } /// /// The that is being placed. From 7d8f08c0ea0e2e1f6d2952926f85557ff9447350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 25 May 2023 21:49:29 +0200 Subject: [PATCH 181/195] Fix `ComposeBlueprintContainer` briefly assigning `auto` bank Seems to have had no consequence due to the way `AutomaticBankAssignment` works (that flag is checked in `PlacementBlueprint.UpdateTimeAndPosition()`, which runs essentially every frame), but let's avoid putting it there at all ever. --- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index 25babae6ff..c8cfac454a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -203,7 +203,7 @@ namespace osu.Game.Screens.Edit.Compose.Components if (bankName == EditorSelectionHandler.HIT_BANK_AUTO) CurrentPlacement.AutomaticBankAssignment = state == TernaryState.True; - if (state == TernaryState.True) + else if (state == TernaryState.True) CurrentPlacement.HitObject.Samples = CurrentPlacement.HitObject.Samples.Select(s => s.With(newBank: bankName)).ToList(); } From 3c69956b55e29484888e9ec3564d1df30267aae4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 May 2023 10:41:29 +0900 Subject: [PATCH 182/195] Fix incorrect catch grid specification --- osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs index 611e69d614..8afeca3e51 100644 --- a/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs +++ b/osu.Game.Rulesets.Catch/Edit/CatchHitObjectComposer.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Catch.Edit result.ScreenSpacePosition.X = screenSpacePosition.X; - if (snapType.HasFlagFast(SnapType.GlobalGrids)) + if (snapType.HasFlagFast(SnapType.RelativeGrids)) { if (distanceSnapGrid.IsPresent && distanceSnapGrid.GetSnappedPosition(result.ScreenSpacePosition) is SnapResult snapResult && Vector2.Distance(snapResult.ScreenSpacePosition, result.ScreenSpacePosition) < distance_snap_radius) From cb468fa4eca95567cbbbad4c19ba2b507792182b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 May 2023 19:59:19 +0900 Subject: [PATCH 183/195] Fix `OverlappingScrollAlgorithm` returning incorrect results for `TimeAt` before first control point --- .../Algorithms/OverlappingScrollAlgorithm.cs | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs index 54079c7895..ead893c0af 100644 --- a/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs +++ b/osu.Game/Rulesets/UI/Scrolling/Algorithms/OverlappingScrollAlgorithm.cs @@ -1,9 +1,7 @@ // 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; +using System.Diagnostics; using System.Linq; using osu.Framework.Lists; using osu.Game.Beatmaps.ControlPoints; @@ -40,29 +38,16 @@ namespace osu.Game.Rulesets.UI.Scrolling.Algorithms public double TimeAt(float position, double currentTime, double timeRange, float scrollLength) { - // Find the control point relating to the position. + Debug.Assert(controlPoints.Count > 0); + + // Iterate over control points and find the most relevant for the provided position. // Note: Due to velocity adjustments, overlapping control points will provide multiple valid time values for a single position // As such, this operation provides unexpected results by using the latter of the control points. + var relevantControlPoint = controlPoints.LastOrDefault(cp => PositionAt(cp.Time, currentTime, timeRange, scrollLength) <= position) ?? controlPoints.First(); - int i = 0; - float pos = 0; + float positionAtControlPoint = PositionAt(relevantControlPoint.Time, currentTime, timeRange, scrollLength); - for (; i < controlPoints.Count; i++) - { - float lastPos = pos; - pos = PositionAt(controlPoints[i].Time, currentTime, timeRange, scrollLength); - - if (pos > position) - { - i--; - pos = lastPos; - break; - } - } - - i = Math.Clamp(i, 0, controlPoints.Count - 1); - - return controlPoints[i].Time + (position - pos) * timeRange / controlPoints[i].Multiplier / scrollLength; + return relevantControlPoint.Time + (position - positionAtControlPoint) * timeRange / relevantControlPoint.Multiplier / scrollLength; } public void Reset() From 6b0e215246021777389876701ef7114c64bb4e89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 26 May 2023 20:39:22 +0900 Subject: [PATCH 184/195] Add `(int)` flooring and handle potential `NaN` value --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 23 ++++++++++++++----- osu.Game/Screens/Play/HUD/SongProgressInfo.cs | 4 ++-- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 52251db898..3a2e381f97 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -212,18 +212,29 @@ namespace osu.Game.Screens.Play playInfoText.AddText("Retry count: "); playInfoText.AddText(retries.ToString(), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); - if (gameplayState != null && gameplayClock != null) + if (getSongProgress() is int progress) { - (double firstHitTime, double lastHitTime) = gameplayState.Beatmap.CalculatePlayableBounds(); - - double progress = Math.Clamp((gameplayClock.CurrentTime - firstHitTime) / (lastHitTime - firstHitTime), 0, 1); - playInfoText.NewLine(); playInfoText.AddText("Song progress: "); - playInfoText.AddText(progress.ToString("0%"), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); + playInfoText.AddText($"{progress}%", cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); } } + private int? getSongProgress() + { + if (gameplayClock == null || gameplayState == null) + return null; + + (double firstHitTime, double lastHitTime) = gameplayState.Beatmap.CalculatePlayableBounds(); + + double playableLength = (lastHitTime - firstHitTime); + + if (playableLength == 0) + return 0; + + return (int)Math.Clamp(((gameplayClock.CurrentTime - firstHitTime) / playableLength) * 100, 0, 100); + } + private partial class Button : DialogButton { // required to ensure keyboard navigation always starts from an extremity (unless the cursor is moved) diff --git a/osu.Game/Screens/Play/HUD/SongProgressInfo.cs b/osu.Game/Screens/Play/HUD/SongProgressInfo.cs index c04ecd671f..2f137f7e78 100644 --- a/osu.Game/Screens/Play/HUD/SongProgressInfo.cs +++ b/osu.Game/Screens/Play/HUD/SongProgressInfo.cs @@ -145,12 +145,12 @@ namespace osu.Game.Screens.Play.HUD double time = gameplayClock?.CurrentTime ?? Time.Current; double songCurrentTime = time - startTime; - int currentPercent = Math.Max(0, Math.Min(100, (int)(songCurrentTime / songLength * 100))); + int currentPercent = songLength == 0 ? 0 : Math.Max(0, Math.Min(100, (int)(songCurrentTime / songLength * 100))); int currentSecond = (int)Math.Floor(songCurrentTime / 1000.0); if (currentPercent != previousPercent) { - progress.Text = currentPercent + @"%"; + progress.Text = $@"{currentPercent}%"; previousPercent = currentPercent; } From e35201cb991aac8b27d68cb3dd49eb431d68d844 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 26 May 2023 19:51:20 +0200 Subject: [PATCH 185/195] Don't snap non-head slider nodes to distance grid during placement either 1cd69220ef640424b79f19784c557ac183333c2d only disabled snapping the aforementioned nodes to distance grid for already-placed sliders. `SliderPlacementBlueprint` has its own logic for placement, so the fix needs to be mirrored there too. --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 28ceb80627..966092c6fe 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Update the cursor position. - var result = snapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position); + var result = snapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.Body ? SnapType.GlobalGrids : SnapType.All); cursor.Position = ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position; } else if (cursor != null) From a01577cba528089335d6e52eb07d9f33357bd0d2 Mon Sep 17 00:00:00 2001 From: Robin Oger Date: Sat, 27 May 2023 12:29:14 +0200 Subject: [PATCH 186/195] Adapt changes to fit master --- osu.Game/Localisation/GameplayMenuOverlayStrings.cs | 10 ---------- osu.Game/Screens/Play/FailOverlay.cs | 8 +++++--- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 5 +++-- osu.Game/Screens/Play/PauseOverlay.cs | 10 ++++++---- 4 files changed, 14 insertions(+), 19 deletions(-) diff --git a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs index c89c35775b..597ee64347 100644 --- a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs +++ b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs @@ -34,16 +34,6 @@ namespace osu.Game.Localisation /// public static LocalisableString PausedHeader => new TranslatableString(getKey(@"paused_header"), @"paused"); - /// - /// "You're dead, try again?" - /// - public static LocalisableString FailedDescription => new TranslatableString(getKey(@"failed_description"), @"You're dead, try again?"); - - /// - /// "You're not going to do what i think you're going to do, are ya?" - /// - public static LocalisableString PausedDescription => new TranslatableString(getKey(@"paused_description"), @"You're not going to do what i think you're going to do, are ya?"); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index f1dd2abc4a..abfc401998 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -15,6 +15,8 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Localisation; +using osu.Game.Localisation; namespace osu.Game.Screens.Play { @@ -22,13 +24,13 @@ namespace osu.Game.Screens.Play { public Func> SaveReplay; - public override string Header => "failed"; + public override LocalisableString Header => GameplayMenuOverlayStrings.FailedHeader; [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); - AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Retry, colours.YellowDark, () => OnRetry?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); // from #10339 maybe this is a better visual effect Add(new Container { diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 3a2e381f97..a061b03e7e 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -49,7 +50,7 @@ namespace osu.Game.Screens.Play /// protected virtual Action SelectAction => () => InternalButtons.Selected?.TriggerClick(); - public abstract string Header { get; } + public abstract LocalisableString Header { get; } protected SelectionCycleFillFlowContainer InternalButtons = null!; public IReadOnlyList Buttons => InternalButtons; @@ -153,7 +154,7 @@ namespace osu.Game.Screens.Play protected override bool OnMouseMove(MouseMoveEvent e) => true; - protected void AddButton(string text, Color4 colour, Action? action) + protected void AddButton(LocalisableString text, Color4 colour, Action? action) { var button = new Button { diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 984f43d77a..2fbb4b3239 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -9,9 +9,11 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Audio; using osu.Game.Graphics; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osu.Game.Skinning; using osuTK.Graphics; @@ -23,7 +25,7 @@ namespace osu.Game.Screens.Play public override bool IsPresent => base.IsPresent || pauseLoop.IsPlaying; - public override string Header => "paused"; + public override LocalisableString Header => "paused"; private SkinnableSound pauseLoop; @@ -32,9 +34,9 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton("Continue", colours.Green, () => OnResume?.Invoke()); - AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke()); - AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Continue, colours.Green, () => OnResume?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Retry, colours.YellowDark, () => OnRetry?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); AddInternal(pauseLoop = new SkinnableSound(new SampleInfo("Gameplay/pause-loop")) { From cfa128002873f19df493a114971e7bd312aa1fed Mon Sep 17 00:00:00 2001 From: Robin Oger Date: Sat, 27 May 2023 12:47:05 +0200 Subject: [PATCH 187/195] GameplayMenuOverlay.cs: add translatable strings for `Retry count: ` and `Song progress: ` This makes the assumption that languages will prefer having the number on the right --- .../Localisation/GameplayMenuOverlayStrings.cs | 14 ++++++++++++-- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 5 +++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs index 597ee64347..f1a65ab430 100644 --- a/osu.Game/Localisation/GameplayMenuOverlayStrings.cs +++ b/osu.Game/Localisation/GameplayMenuOverlayStrings.cs @@ -1,4 +1,4 @@ -// 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 osu.Framework.Localisation; @@ -34,6 +34,16 @@ namespace osu.Game.Localisation /// public static LocalisableString PausedHeader => new TranslatableString(getKey(@"paused_header"), @"paused"); + /// + /// "Retry count: " + /// + public static LocalisableString RetryCount => new TranslatableString(getKey(@"retry_count"), @"Retry count: "); + + /// + /// "Song progress: " + /// + public static LocalisableString SongProgress => new TranslatableString(getKey(@"song_progress"), @"Song progress: "); + private static string getKey(string key) => $@"{prefix}:{key}"; } -} +} \ No newline at end of file diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index a061b03e7e..0680842891 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -21,6 +21,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; +using osu.Game.Localisation; namespace osu.Game.Screens.Play { @@ -210,13 +211,13 @@ namespace osu.Game.Screens.Play private void updateInfoText() { playInfoText.Clear(); - playInfoText.AddText("Retry count: "); + playInfoText.AddText(GameplayMenuOverlayStrings.RetryCount); playInfoText.AddText(retries.ToString(), cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); if (getSongProgress() is int progress) { playInfoText.NewLine(); - playInfoText.AddText("Song progress: "); + playInfoText.AddText(GameplayMenuOverlayStrings.SongProgress); playInfoText.AddText($"{progress}%", cp => cp.Font = cp.Font.With(weight: FontWeight.Bold)); } } From 318431a1b7550c1269ebf72c2033b9a793a69677 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 27 May 2023 21:27:32 +0900 Subject: [PATCH 188/195] make `MaximumAchievable` to default --- osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index 03149fd8c8..c951551af5 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -76,11 +76,11 @@ namespace osu.Game.Rulesets.Mods public enum AccuracyMode { - [LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeStandard))] - Standard, - [LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeMax))] MaximumAchievable, + + [LocalisableDescription(typeof(GameplayAccuracyCounterStrings), nameof(GameplayAccuracyCounterStrings.AccuracyDisplayModeStandard))] + Standard, } } } From ec61840e7d0d9ed5a60f246c44379542d07cb2bd Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 27 May 2023 23:25:01 +0900 Subject: [PATCH 189/195] Provides higher precision settings --- osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index c951551af5..5c0aa2ad00 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Mods public BindableNumber MinimumAccuracy { get; } = new BindableDouble { MinValue = 0.60, - MaxValue = 0.99, - Precision = 0.01, + MaxValue = 0.9999, + Precision = 0.0001, Default = 0.9, Value = 0.9, }; From 89c8ef3c9b28b6761a1ce2e4f3eceb1d74a9b07b Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 27 May 2023 23:35:09 +0900 Subject: [PATCH 190/195] Format percentage based on significant decimal digits --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 0e26029ffa..688a8ce8e6 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -89,14 +89,20 @@ namespace osu.Game.Graphics.UserInterface double floatValue = value.ToDouble(NumberFormatInfo.InvariantInfo); - if (DisplayAsPercentage) - return floatValue.ToString("0%"); - decimal decimalPrecision = normalise(CurrentNumber.Precision.ToDecimal(NumberFormatInfo.InvariantInfo), max_decimal_digits); // Find the number of significant digits (we could have less than 5 after normalize()) int significantDigits = FormatUtils.FindPrecision(decimalPrecision); + if (DisplayAsPercentage) + { + if (significantDigits <= 2) + return floatValue.ToString("0%"); + + string format = "0." + new string('0', significantDigits - 2) + "%"; + return floatValue.ToString(format); + } + string negativeSign = Math.Round(floatValue, significantDigits) < 0 ? "-" : string.Empty; return $"{negativeSign}{Math.Abs(floatValue).ToString($"N{significantDigits}")}"; From a05312c9a26b3f2178779e6220aa4f82ffc65137 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sat, 27 May 2023 23:44:50 +0900 Subject: [PATCH 191/195] simplify format method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Graphics/UserInterface/OsuSliderBar.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs index 688a8ce8e6..e5f5f97eb7 100644 --- a/osu.Game/Graphics/UserInterface/OsuSliderBar.cs +++ b/osu.Game/Graphics/UserInterface/OsuSliderBar.cs @@ -96,11 +96,7 @@ namespace osu.Game.Graphics.UserInterface if (DisplayAsPercentage) { - if (significantDigits <= 2) - return floatValue.ToString("0%"); - - string format = "0." + new string('0', significantDigits - 2) + "%"; - return floatValue.ToString(format); + return floatValue.ToString($@"P{Math.Max(0, significantDigits - 2)}"); } string negativeSign = Math.Round(floatValue, significantDigits) < 0 ? "-" : string.Empty; From 333e785f8bcebe7ca98712f70dfd27ec759a9f25 Mon Sep 17 00:00:00 2001 From: cdwcgt Date: Sun, 28 May 2023 09:50:11 +0900 Subject: [PATCH 192/195] Revert "Provides higher precision settings" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit ec61840e7d0d9ed5a60f246c44379542d07cb2bd. 😐 --- osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index 5c0aa2ad00..c951551af5 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -37,8 +37,8 @@ namespace osu.Game.Rulesets.Mods public BindableNumber MinimumAccuracy { get; } = new BindableDouble { MinValue = 0.60, - MaxValue = 0.9999, - Precision = 0.0001, + MaxValue = 0.99, + Precision = 0.01, Default = 0.9, Value = 0.9, }; From b0501c4e5cbca682254fff771ce7c6128e6f672e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 29 May 2023 10:24:59 +0900 Subject: [PATCH 193/195] Actually use paused header --- osu.Game/Screens/Play/PauseOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 2fbb4b3239..88561ada71 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Play public override bool IsPresent => base.IsPresent || pauseLoop.IsPlaying; - public override LocalisableString Header => "paused"; + public override LocalisableString Header => GameplayMenuOverlayStrings.PausedHeader; private SkinnableSound pauseLoop; From 314a0f80f319a05b97d3e2f5787c3ef3177b79a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 May 2023 22:12:37 +0200 Subject: [PATCH 194/195] Reword setting name & description --- osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs index c951551af5..0072c21053 100644 --- a/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs +++ b/osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Mods Value = 0.9, }; - [SettingSource("Accuracy Mode", "The Accuracy mode that will be used to Judge.")] + [SettingSource("Accuracy mode", "The mode of accuracy that will trigger failure.")] public Bindable AccuracyJudgeMode { get; } = new Bindable(); private readonly Bindable currentAccuracy = new Bindable(); From 1b57b0d31c71aaf1578d83293be11a665fc2cdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 29 May 2023 22:33:46 +0200 Subject: [PATCH 195/195] Add testing --- .../Mods/TestSceneModAccuracyChallenge.cs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs diff --git a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs new file mode 100644 index 0000000000..6bdb9132e1 --- /dev/null +++ b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs @@ -0,0 +1,69 @@ +// 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.Game.Beatmaps; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Objects; +using osuTK; + +namespace osu.Game.Tests.Visual.Mods +{ + public partial class TestSceneModAccuracyChallenge : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + protected override TestPlayer CreateModPlayer(Ruleset ruleset) + { + var player = base.CreateModPlayer(ruleset); + return player; + } + + protected override bool AllowFail => true; + + [Test] + public void TestMaximumAchievableAccuracy() => + CreateModTest(new ModTestData + { + Mod = new ModAccuracyChallenge + { + MinimumAccuracy = { Value = 0.6 } + }, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = Enumerable.Range(0, 5).Select(i => new HitCircle + { + StartTime = i * 250, + Position = new Vector2(i * 50) + }).Cast().ToList() + }, + PassCondition = () => Player.GameplayState.HasFailed && Player.ScoreProcessor.JudgedHits >= 3 + }); + + [Test] + public void TestStandardAccuracy() => + CreateModTest(new ModTestData + { + Mod = new ModAccuracyChallenge + { + MinimumAccuracy = { Value = 0.6 }, + AccuracyJudgeMode = { Value = ModAccuracyChallenge.AccuracyMode.Standard } + }, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = Enumerable.Range(0, 5).Select(i => new HitCircle + { + StartTime = i * 250, + Position = new Vector2(i * 50) + }).Cast().ToList() + }, + PassCondition = () => Player.GameplayState.HasFailed && Player.ScoreProcessor.JudgedHits >= 1 + }); + } +}